From c18bc982b714099827c036a84067c7de8be0208e Mon Sep 17 00:00:00 2001 From: Indrajith Madhumal Date: Tue, 9 Jan 2024 16:39:28 +0530 Subject: [PATCH 01/17] [Automated] Update the native jar versions --- ballerina-tests/Dependencies.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ballerina-tests/Dependencies.toml b/ballerina-tests/Dependencies.toml index 3848d2fc8..315c80918 100644 --- a/ballerina-tests/Dependencies.toml +++ b/ballerina-tests/Dependencies.toml @@ -119,7 +119,7 @@ modules = [ [[package]] org = "ballerina" name = "http" -version = "2.10.2" +version = "2.10.5" dependencies = [ {org = "ballerina", name = "auth"}, {org = "ballerina", name = "cache"}, @@ -383,7 +383,7 @@ modules = [ [[package]] org = "ballerina" name = "websocket" -version = "2.10.0" +version = "2.10.1" dependencies = [ {org = "ballerina", name = "auth"}, {org = "ballerina", name = "constraint"}, From 8256578b812ba1ee57032066d57a64e80bf70e8a Mon Sep 17 00:00:00 2001 From: Indrajith Madhumal Date: Tue, 9 Jan 2024 16:58:32 +0530 Subject: [PATCH 02/17] [Automated] Update the native jar versions --- ballerina/Dependencies.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index 150b38832..51b849305 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -97,7 +97,7 @@ modules = [ [[package]] org = "ballerina" name = "http" -version = "2.10.2" +version = "2.10.5" dependencies = [ {org = "ballerina", name = "auth"}, {org = "ballerina", name = "cache"}, @@ -376,7 +376,7 @@ modules = [ [[package]] org = "ballerina" name = "websocket" -version = "2.10.0" +version = "2.10.1" dependencies = [ {org = "ballerina", name = "auth"}, {org = "ballerina", name = "constraint"}, From 2f4f4b900939c2eb9822821913aef5c6b1594de8 Mon Sep 17 00:00:00 2001 From: Indrajith Madhumal Date: Tue, 9 Jan 2024 17:30:56 +0530 Subject: [PATCH 03/17] Add support to gen schema from local service obj --- .../compiler/SchemaGenerationTest.java | 7 ++ .../Ballerina.toml | 4 + .../service.bal | 14 +++ .../graphql/compiler/GraphqlCodeModifier.java | 2 + .../LocalLevelServiceObjectAnalysisTask.java | 86 +++++++++++++++++++ .../generator/GraphqlSourceModifier.java | 19 ++++ 6 files changed, 132 insertions(+) create mode 100644 compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/25_local_graphql_service_object/Ballerina.toml create mode 100644 compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/25_local_graphql_service_object/service.bal create mode 100644 compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/LocalLevelServiceObjectAnalysisTask.java diff --git a/compiler-plugin-tests/src/test/java/io/ballerina/stdlib/graphql/compiler/SchemaGenerationTest.java b/compiler-plugin-tests/src/test/java/io/ballerina/stdlib/graphql/compiler/SchemaGenerationTest.java index 67904c145..05d772425 100644 --- a/compiler-plugin-tests/src/test/java/io/ballerina/stdlib/graphql/compiler/SchemaGenerationTest.java +++ b/compiler-plugin-tests/src/test/java/io/ballerina/stdlib/graphql/compiler/SchemaGenerationTest.java @@ -207,6 +207,13 @@ public void testGraphqlDataLoader() { Assert.assertEquals(diagnosticResult.errorCount(), 0); } + @Test + public void testGraphqlLocalServiceObject() { + String packagePath = "25_local_graphql_service_object"; + DiagnosticResult diagnosticResult = getDiagnosticResult(packagePath); + Assert.assertEquals(diagnosticResult.errorCount(), 0); + } + private DiagnosticResult getDiagnosticResult(String path) { Path projectDirPath = RESOURCE_DIRECTORY.resolve(path); BuildProject project = BuildProject.load(getEnvironmentBuilder(), projectDirPath); diff --git a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/25_local_graphql_service_object/Ballerina.toml b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/25_local_graphql_service_object/Ballerina.toml new file mode 100644 index 000000000..b58c76324 --- /dev/null +++ b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/25_local_graphql_service_object/Ballerina.toml @@ -0,0 +1,4 @@ +[package] +org = "graphql_test" +name = "test_package" +version = "0.1.0" diff --git a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/25_local_graphql_service_object/service.bal b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/25_local_graphql_service_object/service.bal new file mode 100644 index 000000000..17ac2f4d2 --- /dev/null +++ b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/25_local_graphql_service_object/service.bal @@ -0,0 +1,14 @@ +import ballerina/lang.runtime; +import ballerina/graphql; + +public function main() returns error? { + graphql:Service gqlService = service object { + resource function get greeting() returns string { + return "Wubba lubba dub dub!!"; + } + }; + graphql:Listener l = check new(9090); + check l.attach(gqlService, "graphql"); + check l.'start(); + runtime:registerListener(l); +} \ No newline at end of file diff --git a/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/GraphqlCodeModifier.java b/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/GraphqlCodeModifier.java index 829d87897..333ce1342 100644 --- a/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/GraphqlCodeModifier.java +++ b/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/GraphqlCodeModifier.java @@ -46,6 +46,8 @@ public void init(CodeModifierContext modifierContext) { SyntaxKind.SERVICE_DECLARATION); modifierContext.addSyntaxNodeAnalysisTask( new ModuleLevelVariableDeclarationAnalysisTask(this.modifierContextMap), SyntaxKind.MODULE_VAR_DECL); + modifierContext.addSyntaxNodeAnalysisTask( + new LocalLevelServiceObjectAnalysisTask(this.modifierContextMap), SyntaxKind.OBJECT_CONSTRUCTOR); modifierContext.addSyntaxNodeAnalysisTask(new InterceptorAnalysisTask(), SyntaxKind.CLASS_DEFINITION); modifierContext.addSyntaxNodeAnalysisTask(new ListenerValidator(), Arrays.asList(SyntaxKind.IMPLICIT_NEW_EXPRESSION, diff --git a/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/LocalLevelServiceObjectAnalysisTask.java b/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/LocalLevelServiceObjectAnalysisTask.java new file mode 100644 index 000000000..4b1a41e09 --- /dev/null +++ b/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/LocalLevelServiceObjectAnalysisTask.java @@ -0,0 +1,86 @@ +package io.ballerina.stdlib.graphql.compiler; + +import io.ballerina.compiler.api.SemanticModel; +import io.ballerina.compiler.api.symbols.ModuleSymbol; +import io.ballerina.compiler.api.symbols.Symbol; +import io.ballerina.compiler.api.symbols.TypeReferenceTypeSymbol; +import io.ballerina.compiler.syntax.tree.ObjectConstructorExpressionNode; +import io.ballerina.compiler.syntax.tree.QualifiedNameReferenceNode; +import io.ballerina.compiler.syntax.tree.SyntaxKind; +import io.ballerina.compiler.syntax.tree.TypeDescriptorNode; +import io.ballerina.compiler.syntax.tree.TypedBindingPatternNode; +import io.ballerina.compiler.syntax.tree.VariableDeclarationNode; +import io.ballerina.projects.DocumentId; +import io.ballerina.projects.plugins.SyntaxNodeAnalysisContext; +import io.ballerina.stdlib.graphql.commons.types.Schema; +import io.ballerina.stdlib.graphql.compiler.schema.generator.GraphqlModifierContext; +import io.ballerina.stdlib.graphql.compiler.service.InterfaceEntityFinder; +import io.ballerina.stdlib.graphql.compiler.service.validator.ServiceValidator; + +import java.util.Map; +import java.util.Optional; + +import static io.ballerina.stdlib.graphql.compiler.Utils.hasCompilationErrors; + +public class LocalLevelServiceObjectAnalysisTask extends ServiceAnalysisTask { + + public LocalLevelServiceObjectAnalysisTask(Map nodeMap) { + super(nodeMap); + } + + private static final String PACKAGE_NAME = "graphql"; + private static final String SERVICE_NAME = "Service"; + + @Override + public void perform(SyntaxNodeAnalysisContext context) { + if (hasCompilationErrors(context)) { + return; + } + ObjectConstructorExpressionNode node = (ObjectConstructorExpressionNode) context.node(); + if (node.parent().kind() != SyntaxKind.LOCAL_VAR_DECL) { + return; + } + VariableDeclarationNode parentNode = (VariableDeclarationNode) node.parent(); + if (!isGraphQLServiceObjectDeclaration(context.semanticModel(), parentNode.typedBindingPattern())) { + return; + } + InterfaceEntityFinder interfaceEntityFinder = getInterfaceEntityFinder(context.semanticModel()); + ServiceValidator serviceValidator = getServiceValidator(context, node, interfaceEntityFinder); + if (serviceValidator.isErrorOccurred()) { + return; + } + Schema schema = generateSchema(context, interfaceEntityFinder, node, null); + DocumentId documentId = context.documentId(); + addToModifierContextMap(documentId, node, schema); + } + + public boolean isGraphQLServiceObjectDeclaration(SemanticModel semanticModel, + TypedBindingPatternNode typedBindingPatternNode) { + TypeDescriptorNode typeDescriptorNode = typedBindingPatternNode.typeDescriptor(); + if (typeDescriptorNode.kind() != SyntaxKind.QUALIFIED_NAME_REFERENCE) { + return false; + } + return isGraphqlServiceQualifiedNameReference(semanticModel, (QualifiedNameReferenceNode) typeDescriptorNode); + } + + private boolean isGraphqlServiceQualifiedNameReference(SemanticModel semanticModel, + QualifiedNameReferenceNode nameReferenceNode) { + Optional symbol = semanticModel.symbol(nameReferenceNode); + if (symbol.isEmpty()) { + return false; + } + TypeReferenceTypeSymbol typeSymbol = (TypeReferenceTypeSymbol) symbol.get(); + Optional moduleSymbol = typeSymbol.getModule(); + if (moduleSymbol.isEmpty()) { + return false; + } + if (!isPresentAndEquals(moduleSymbol.get().getName(), PACKAGE_NAME)) { + return false; + } + return isPresentAndEquals(typeSymbol.getName(), SERVICE_NAME); + } + + private boolean isPresentAndEquals(Optional value, String equals) { + return value.isPresent() && equals.equals(value.get()); + } +} diff --git a/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/schema/generator/GraphqlSourceModifier.java b/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/schema/generator/GraphqlSourceModifier.java index 4e206f05b..3d9f6353b 100644 --- a/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/schema/generator/GraphqlSourceModifier.java +++ b/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/schema/generator/GraphqlSourceModifier.java @@ -34,6 +34,7 @@ import io.ballerina.compiler.syntax.tree.NodeFactory; import io.ballerina.compiler.syntax.tree.NodeList; import io.ballerina.compiler.syntax.tree.NodeParser; +import io.ballerina.compiler.syntax.tree.NonTerminalNode; import io.ballerina.compiler.syntax.tree.ObjectConstructorExpressionNode; import io.ballerina.compiler.syntax.tree.QualifiedNameReferenceNode; import io.ballerina.compiler.syntax.tree.SeparatedNodeList; @@ -43,6 +44,7 @@ import io.ballerina.compiler.syntax.tree.SyntaxKind; import io.ballerina.compiler.syntax.tree.SyntaxTree; import io.ballerina.compiler.syntax.tree.Token; +import io.ballerina.compiler.syntax.tree.VariableDeclarationNode; import io.ballerina.projects.DocumentId; import io.ballerina.projects.Module; import io.ballerina.projects.plugins.ModifierTask; @@ -146,6 +148,23 @@ private ModulePartNode modifyDocument(SourceModifierContext context, ModulePartN nodeMap.put(targetNode, updatedNode); this.entityTypeNamesMap.put(targetNode, this.entityUnionTypeName); this.entityUnionSuffix++; + } else if (targetNode.kind() == SyntaxKind.OBJECT_CONSTRUCTOR) { + ObjectConstructorExpressionNode graphqlServiceVariableDeclaration + = (ObjectConstructorExpressionNode) targetNode; + ObjectConstructorExpressionNode updatedGraphqlServiceObject = modifyServiceObjectNode( + graphqlServiceVariableDeclaration, schemaString, prefix); + VariableDeclarationNode parentNode + = (VariableDeclarationNode) graphqlServiceVariableDeclaration.parent(); + VariableDeclarationNode updatedParentNode = parentNode.modify() + .withInitializer(updatedGraphqlServiceObject) + .apply(); + Node replacingNode = updatedParentNode; + NonTerminalNode iNode = parentNode; + while (iNode.parent() != null) { + replacingNode = iNode.parent().replace(iNode, replacingNode); + iNode = iNode.parent(); + } + rootNode = rootNode.replace(iNode, replacingNode); } } catch (IOException e) { updateContext(context, entry.getKey().location(), CompilationDiagnostic.SCHEMA_GENERATION_FAILED, From 8c502a2ef91cc2352e422946016378c79dded4cb Mon Sep 17 00:00:00 2001 From: Indrajith Madhumal Date: Tue, 9 Jan 2024 18:30:45 +0530 Subject: [PATCH 04/17] Update changelog --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index ee845543a..97f322670 100644 --- a/changelog.md +++ b/changelog.md @@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Added -[[#1586] Add Compile Time Schema Generation for Default Parameters](https://github.com/ballerina-platform/module-ballerina-graphql/pull/1586) +-[[#3317] Add Support to Generate GraphQL Schema for Local Service Variable Declaration](https://github.com/ballerina-platform/ballerina-library/issues/3317) ### Changed - [[#4634] Use Aliases in GraphQL Error Path](https://github.com/ballerina-platform/ballerina-standard-library/issues/4634) From 4667b026398171279d906be55da7fe6176bf6f24 Mon Sep 17 00:00:00 2001 From: Indrajith Madhumal Date: Tue, 9 Jan 2024 19:55:55 +0530 Subject: [PATCH 05/17] Add copyright and license --- .../LocalLevelServiceObjectAnalysisTask.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/LocalLevelServiceObjectAnalysisTask.java b/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/LocalLevelServiceObjectAnalysisTask.java index 4b1a41e09..4aded4ed3 100644 --- a/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/LocalLevelServiceObjectAnalysisTask.java +++ b/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/LocalLevelServiceObjectAnalysisTask.java @@ -1,3 +1,21 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + package io.ballerina.stdlib.graphql.compiler; import io.ballerina.compiler.api.SemanticModel; From cbd45498b829254a0f14282c2c655524ee0925a9 Mon Sep 17 00:00:00 2001 From: Indrajith Madhumal Date: Tue, 9 Jan 2024 21:41:32 +0530 Subject: [PATCH 06/17] Update license header --- .../service.bal | 18 ++++++++++- .../LocalLevelServiceObjectAnalysisTask.java | 32 +++++++++---------- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/25_local_graphql_service_object/service.bal b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/25_local_graphql_service_object/service.bal index 17ac2f4d2..b34ae1d8e 100644 --- a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/25_local_graphql_service_object/service.bal +++ b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/25_local_graphql_service_object/service.bal @@ -1,3 +1,19 @@ +// Copyright (c) 2024 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. + import ballerina/lang.runtime; import ballerina/graphql; @@ -11,4 +27,4 @@ public function main() returns error? { check l.attach(gqlService, "graphql"); check l.'start(); runtime:registerListener(l); -} \ No newline at end of file +} diff --git a/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/LocalLevelServiceObjectAnalysisTask.java b/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/LocalLevelServiceObjectAnalysisTask.java index 4aded4ed3..7b77153cc 100644 --- a/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/LocalLevelServiceObjectAnalysisTask.java +++ b/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/LocalLevelServiceObjectAnalysisTask.java @@ -1,20 +1,18 @@ -/* - * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ +// Copyright (c) 2024, 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. package io.ballerina.stdlib.graphql.compiler; From 642e4b2ba8d02158555caa0056d11fd7dda71d13 Mon Sep 17 00:00:00 2001 From: Indrajith Madhumal Date: Wed, 10 Jan 2024 08:56:15 +0530 Subject: [PATCH 07/17] Apply suggestions from code reviews --- .../compiler/LocalLevelServiceObjectAnalysisTask.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/LocalLevelServiceObjectAnalysisTask.java b/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/LocalLevelServiceObjectAnalysisTask.java index 7b77153cc..85c2e6cdd 100644 --- a/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/LocalLevelServiceObjectAnalysisTask.java +++ b/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/LocalLevelServiceObjectAnalysisTask.java @@ -71,7 +71,7 @@ public void perform(SyntaxNodeAnalysisContext context) { } public boolean isGraphQLServiceObjectDeclaration(SemanticModel semanticModel, - TypedBindingPatternNode typedBindingPatternNode) { + TypedBindingPatternNode typedBindingPatternNode) { TypeDescriptorNode typeDescriptorNode = typedBindingPatternNode.typeDescriptor(); if (typeDescriptorNode.kind() != SyntaxKind.QUALIFIED_NAME_REFERENCE) { return false; @@ -80,7 +80,7 @@ public boolean isGraphQLServiceObjectDeclaration(SemanticModel semanticModel, } private boolean isGraphqlServiceQualifiedNameReference(SemanticModel semanticModel, - QualifiedNameReferenceNode nameReferenceNode) { + QualifiedNameReferenceNode nameReferenceNode) { Optional symbol = semanticModel.symbol(nameReferenceNode); if (symbol.isEmpty()) { return false; @@ -96,7 +96,7 @@ private boolean isGraphqlServiceQualifiedNameReference(SemanticModel semanticMod return isPresentAndEquals(typeSymbol.getName(), SERVICE_NAME); } - private boolean isPresentAndEquals(Optional value, String equals) { - return value.isPresent() && equals.equals(value.get()); + private boolean isPresentAndEquals(Optional actualValue, String expectedValue) { + return actualValue.isPresent() && expectedValue.equals(actualValue.get()); } } From 2cb2a8844336cd8c9fb96317aa766596f6b16359 Mon Sep 17 00:00:00 2001 From: Indrajith Madhumal Date: Thu, 11 Jan 2024 10:22:02 +0530 Subject: [PATCH 08/17] [Automated] Update the native jar versions --- ballerina/Dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index 51b849305..35fc5bf76 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -25,7 +25,7 @@ modules = [ [[package]] org = "ballerina" name = "cache" -version = "3.7.0" +version = "3.7.1" dependencies = [ {org = "ballerina", name = "constraint"}, {org = "ballerina", name = "jballerina.java"}, From 2906fad7fdc74e7b3dbd8ab595af424fdb6c067e Mon Sep 17 00:00:00 2001 From: Indrajith Madhumal Date: Thu, 11 Jan 2024 11:31:51 +0530 Subject: [PATCH 09/17] [Automated] Update the native jar versions --- ballerina-tests/Dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ballerina-tests/Dependencies.toml b/ballerina-tests/Dependencies.toml index 315c80918..a1580ec99 100644 --- a/ballerina-tests/Dependencies.toml +++ b/ballerina-tests/Dependencies.toml @@ -22,7 +22,7 @@ dependencies = [ [[package]] org = "ballerina" name = "cache" -version = "3.7.0" +version = "3.7.1" dependencies = [ {org = "ballerina", name = "constraint"}, {org = "ballerina", name = "jballerina.java"}, From 4aa10e197b9030c4cbae8d0f17b61112f35052f0 Mon Sep 17 00:00:00 2001 From: Indrajith Madhumal Date: Fri, 12 Jan 2024 16:04:53 +0530 Subject: [PATCH 10/17] Use textRange to modify service declaration nodes --- .../compiler/SchemaGenerationTest.java | 26 ++++++- .../Ballerina.toml | 0 .../service.bal | 24 ++++-- .../Ballerina.toml | 4 + .../service.bal | 62 +++++++++++++++ .../Ballerina.toml | 4 + .../service.bal | 46 +++++++++++ .../Ballerina.toml | 4 + .../service.bal | 17 ++++ .../graphql/compiler/GraphqlCodeModifier.java | 2 +- ...ava => ObjectConstructorAnalysisTask.java} | 27 +++---- .../generator/GraphqlSourceModifier.java | 78 +++++++++++-------- 12 files changed, 235 insertions(+), 59 deletions(-) rename compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/{25_local_graphql_service_object => 25_local_service_object_declarations}/Ballerina.toml (100%) rename compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/{25_local_graphql_service_object => 25_local_service_object_declarations}/service.bal (56%) create mode 100644 compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/26_multiple_service_declarations/Ballerina.toml create mode 100644 compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/26_multiple_service_declarations/service.bal create mode 100644 compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/27_local_graphql_service_with_http_service/Ballerina.toml create mode 100644 compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/27_local_graphql_service_with_http_service/service.bal create mode 100644 compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/29_inline_service_declarations/Ballerina.toml create mode 100644 compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/29_inline_service_declarations/service.bal rename compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/{LocalLevelServiceObjectAnalysisTask.java => ObjectConstructorAnalysisTask.java} (80%) diff --git a/compiler-plugin-tests/src/test/java/io/ballerina/stdlib/graphql/compiler/SchemaGenerationTest.java b/compiler-plugin-tests/src/test/java/io/ballerina/stdlib/graphql/compiler/SchemaGenerationTest.java index 05d772425..a26944723 100644 --- a/compiler-plugin-tests/src/test/java/io/ballerina/stdlib/graphql/compiler/SchemaGenerationTest.java +++ b/compiler-plugin-tests/src/test/java/io/ballerina/stdlib/graphql/compiler/SchemaGenerationTest.java @@ -208,12 +208,34 @@ public void testGraphqlDataLoader() { } @Test - public void testGraphqlLocalServiceObject() { - String packagePath = "25_local_graphql_service_object"; + public void testGraphqlLocalServiceObjects() { + String packagePath = "25_local_service_object_declarations"; DiagnosticResult diagnosticResult = getDiagnosticResult(packagePath); Assert.assertEquals(diagnosticResult.errorCount(), 0); } + @Test + public void testGraphqlMultipleServiceDeclarations() { + String packagePath = "26_multiple_service_declarations"; + DiagnosticResult diagnosticResult = getDiagnosticResult(packagePath); + Assert.assertEquals(diagnosticResult.errorCount(), 0); + } + + @Test + public void testLocalGraphqlObjectDeclarationWithHttpService() { + String packagePath = "27_local_graphql_service_with_http_service"; + DiagnosticResult diagnosticResult = getDiagnosticResult(packagePath); + Assert.assertEquals(diagnosticResult.errorCount(), 0); + } + + @Test + public void testMultipleInlineServiceDeclarations() { + String packagePath = "29_inline_service_declarations"; + DiagnosticResult diagnosticResult = getDiagnosticResult(packagePath); + Assert.assertEquals(diagnosticResult.errorCount(), 0); + } + + private DiagnosticResult getDiagnosticResult(String path) { Path projectDirPath = RESOURCE_DIRECTORY.resolve(path); BuildProject project = BuildProject.load(getEnvironmentBuilder(), projectDirPath); diff --git a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/25_local_graphql_service_object/Ballerina.toml b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/25_local_service_object_declarations/Ballerina.toml similarity index 100% rename from compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/25_local_graphql_service_object/Ballerina.toml rename to compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/25_local_service_object_declarations/Ballerina.toml diff --git a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/25_local_graphql_service_object/service.bal b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/25_local_service_object_declarations/service.bal similarity index 56% rename from compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/25_local_graphql_service_object/service.bal rename to compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/25_local_service_object_declarations/service.bal index b34ae1d8e..c498dbf27 100644 --- a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/25_local_graphql_service_object/service.bal +++ b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/25_local_service_object_declarations/service.bal @@ -18,13 +18,23 @@ import ballerina/lang.runtime; import ballerina/graphql; public function main() returns error? { - graphql:Service gqlService = service object { + graphql:Service localService1 = service object { resource function get greeting() returns string { - return "Wubba lubba dub dub!!"; + return "Hello from local service 1"; } }; - graphql:Listener l = check new(9090); - check l.attach(gqlService, "graphql"); - check l.'start(); - runtime:registerListener(l); -} + + graphql:Service localService2 = service object { + resource function get greeting() returns string { + return "Hello from local service 2"; + } + }; + graphql:Listener localListener1 = check new(9090); + graphql:Listener localListener2 = check new(9091); + check localListener1.attach(localService1); + check localListener2.attach(localService2); + check localListener1.'start(); + check localListener2.'start(); + runtime:registerListener(localListener1); + runtime:registerListener(localListener2); +} \ No newline at end of file diff --git a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/26_multiple_service_declarations/Ballerina.toml b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/26_multiple_service_declarations/Ballerina.toml new file mode 100644 index 000000000..b58c76324 --- /dev/null +++ b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/26_multiple_service_declarations/Ballerina.toml @@ -0,0 +1,4 @@ +[package] +org = "graphql_test" +name = "test_package" +version = "0.1.0" diff --git a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/26_multiple_service_declarations/service.bal b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/26_multiple_service_declarations/service.bal new file mode 100644 index 000000000..b45f6ac14 --- /dev/null +++ b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/26_multiple_service_declarations/service.bal @@ -0,0 +1,62 @@ +// Copyright (c) 2024 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. + +import ballerina/lang.runtime; +import ballerina/graphql; + +service / on new graphql:Listener(9090) { + resource function get greeting() returns string { + return "Hello from global listener binding service"; + } +} + +graphql:Service globalService = service object { + resource function get greeting() returns string { + return "Hello from global service"; + } +}; + +service /too on new graphql:Listener(9091) { + resource function get greeting() returns string { + return "Hello from global listener binding service too"; + } +} + +public function main() returns error? { + graphql:Service localService1 = service object { + resource function get greeting() returns string { + return "Hello from local service 1"; + } + }; + + graphql:Service localService2 = service object { + resource function get greeting() returns string { + return "Hello from local service 2"; + } + }; + graphql:Listener localListener1 = check new(9092); + graphql:Listener localListener2 = check new(9093); + graphql:Listener globalListener = check new(9094); + check localListener1.attach(localService1); + check localListener2.attach(localService2); + check globalListener.attach(globalService); + check localListener1.'start(); + check localListener2.'start(); + check globalListener.'start(); + runtime:registerListener(localListener1); + runtime:registerListener(localListener2); + runtime:registerListener(globalListener); +} \ No newline at end of file diff --git a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/27_local_graphql_service_with_http_service/Ballerina.toml b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/27_local_graphql_service_with_http_service/Ballerina.toml new file mode 100644 index 000000000..b58c76324 --- /dev/null +++ b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/27_local_graphql_service_with_http_service/Ballerina.toml @@ -0,0 +1,4 @@ +[package] +org = "graphql_test" +name = "test_package" +version = "0.1.0" diff --git a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/27_local_graphql_service_with_http_service/service.bal b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/27_local_graphql_service_with_http_service/service.bal new file mode 100644 index 000000000..471f69c90 --- /dev/null +++ b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/27_local_graphql_service_with_http_service/service.bal @@ -0,0 +1,46 @@ +// Copyright (c) 2024 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. + +import ballerina/lang.runtime; +import ballerina/graphql; +import ballerina/http; + +service / on new http:Listener(9090) { + resource function get greet() returns string { + return "Hello from global http service"; + } +} + +public function main() returns error? { + graphql:Service gqlService = service object { + resource function get greet() returns string { + return "Hello from gql service"; + } + }; + http:Service httpService = service object { + resource function get greet() returns string { + return "Hello from local http service"; + } + }; + graphql:Listener gqlListener = check new(9091); + http:Listener httpListener = check new(9092); + check gqlListener.attach(gqlService); + check httpListener.attach(httpService); + check gqlListener.'start(); + check httpListener.'start(); + runtime:registerListener(gqlListener); + runtime:registerListener(httpListener); +} \ No newline at end of file diff --git a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/29_inline_service_declarations/Ballerina.toml b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/29_inline_service_declarations/Ballerina.toml new file mode 100644 index 000000000..b58c76324 --- /dev/null +++ b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/29_inline_service_declarations/Ballerina.toml @@ -0,0 +1,4 @@ +[package] +org = "graphql_test" +name = "test_package" +version = "0.1.0" diff --git a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/29_inline_service_declarations/service.bal b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/29_inline_service_declarations/service.bal new file mode 100644 index 000000000..82d3aa605 --- /dev/null +++ b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/29_inline_service_declarations/service.bal @@ -0,0 +1,17 @@ +// Copyright (c) 2024 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. + +import ballerina/lang.runtime; import ballerina/graphql; import ballerina/http; service / on new http:Listener(9090) { resource function get greet() returns string { return "Hello from global http service"; } } public function main() returns error? { graphql:Service gqlService = service object { resource function get greet() returns string { return "Hello from gql service"; } }; http:Service httpService = service object { resource function get greet() returns string { return "Hello from local http service"; } }; graphql:Listener gqlListener = check new(9091); http:Listener httpListener = check new(9092); check gqlListener.attach(gqlService); check httpListener.attach(httpService); check gqlListener.'start(); check httpListener.'start(); runtime:registerListener(gqlListener); runtime:registerListener(httpListener); } \ No newline at end of file diff --git a/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/GraphqlCodeModifier.java b/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/GraphqlCodeModifier.java index 333ce1342..e20da8d33 100644 --- a/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/GraphqlCodeModifier.java +++ b/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/GraphqlCodeModifier.java @@ -47,7 +47,7 @@ public void init(CodeModifierContext modifierContext) { modifierContext.addSyntaxNodeAnalysisTask( new ModuleLevelVariableDeclarationAnalysisTask(this.modifierContextMap), SyntaxKind.MODULE_VAR_DECL); modifierContext.addSyntaxNodeAnalysisTask( - new LocalLevelServiceObjectAnalysisTask(this.modifierContextMap), SyntaxKind.OBJECT_CONSTRUCTOR); + new ObjectConstructorAnalysisTask(this.modifierContextMap), SyntaxKind.OBJECT_CONSTRUCTOR); modifierContext.addSyntaxNodeAnalysisTask(new InterceptorAnalysisTask(), SyntaxKind.CLASS_DEFINITION); modifierContext.addSyntaxNodeAnalysisTask(new ListenerValidator(), Arrays.asList(SyntaxKind.IMPLICIT_NEW_EXPRESSION, diff --git a/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/LocalLevelServiceObjectAnalysisTask.java b/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/ObjectConstructorAnalysisTask.java similarity index 80% rename from compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/LocalLevelServiceObjectAnalysisTask.java rename to compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/ObjectConstructorAnalysisTask.java index 85c2e6cdd..6ffb77048 100644 --- a/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/LocalLevelServiceObjectAnalysisTask.java +++ b/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/ObjectConstructorAnalysisTask.java @@ -17,8 +17,6 @@ package io.ballerina.stdlib.graphql.compiler; import io.ballerina.compiler.api.SemanticModel; -import io.ballerina.compiler.api.symbols.ModuleSymbol; -import io.ballerina.compiler.api.symbols.Symbol; import io.ballerina.compiler.api.symbols.TypeReferenceTypeSymbol; import io.ballerina.compiler.syntax.tree.ObjectConstructorExpressionNode; import io.ballerina.compiler.syntax.tree.QualifiedNameReferenceNode; @@ -34,13 +32,12 @@ import io.ballerina.stdlib.graphql.compiler.service.validator.ServiceValidator; import java.util.Map; -import java.util.Optional; import static io.ballerina.stdlib.graphql.compiler.Utils.hasCompilationErrors; -public class LocalLevelServiceObjectAnalysisTask extends ServiceAnalysisTask { +public class ObjectConstructorAnalysisTask extends ServiceAnalysisTask { - public LocalLevelServiceObjectAnalysisTask(Map nodeMap) { + public ObjectConstructorAnalysisTask(Map nodeMap) { super(nodeMap); } @@ -67,7 +64,7 @@ public void perform(SyntaxNodeAnalysisContext context) { } Schema schema = generateSchema(context, interfaceEntityFinder, node, null); DocumentId documentId = context.documentId(); - addToModifierContextMap(documentId, node, schema); + addToModifierContextMap(documentId, node.parent(), schema); } public boolean isGraphQLServiceObjectDeclaration(SemanticModel semanticModel, @@ -81,22 +78,20 @@ public boolean isGraphQLServiceObjectDeclaration(SemanticModel semanticModel, private boolean isGraphqlServiceQualifiedNameReference(SemanticModel semanticModel, QualifiedNameReferenceNode nameReferenceNode) { - Optional symbol = semanticModel.symbol(nameReferenceNode); - if (symbol.isEmpty()) { + if (semanticModel.symbol(nameReferenceNode).isEmpty()) { return false; } - TypeReferenceTypeSymbol typeSymbol = (TypeReferenceTypeSymbol) symbol.get(); - Optional moduleSymbol = typeSymbol.getModule(); - if (moduleSymbol.isEmpty()) { + TypeReferenceTypeSymbol typeSymbol = (TypeReferenceTypeSymbol) semanticModel.symbol(nameReferenceNode).get(); + if (typeSymbol.getModule().isEmpty()) { return false; } - if (!isPresentAndEquals(moduleSymbol.get().getName(), PACKAGE_NAME)) { + if (!PACKAGE_NAME.equals(typeSymbol.getModule().get().getName().get())) { return false; } - return isPresentAndEquals(typeSymbol.getName(), SERVICE_NAME); + if (typeSymbol.getName().isEmpty()) { + return false; + } + return SERVICE_NAME.equals(typeSymbol.getName().get()); } - private boolean isPresentAndEquals(Optional actualValue, String expectedValue) { - return actualValue.isPresent() && expectedValue.equals(actualValue.get()); - } } diff --git a/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/schema/generator/GraphqlSourceModifier.java b/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/schema/generator/GraphqlSourceModifier.java index 3d9f6353b..cd0de385b 100644 --- a/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/schema/generator/GraphqlSourceModifier.java +++ b/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/schema/generator/GraphqlSourceModifier.java @@ -57,6 +57,7 @@ import io.ballerina.tools.diagnostics.DiagnosticInfo; import io.ballerina.tools.diagnostics.Location; import io.ballerina.tools.text.TextDocument; +import io.ballerina.tools.text.TextRange; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; @@ -123,7 +124,7 @@ public void modify(SourceModifierContext sourceModifierContext) { private ModulePartNode modifyDocument(SourceModifierContext context, ModulePartNode rootNode, GraphqlModifierContext modifierContext) { this.subgraphModulePrefix = getSubgraphModulePrefix(rootNode); - Map nodeMap = new HashMap<>(); + Map modifiedNodes = new HashMap<>(); Map nodeSchemaMap = modifierContext.getNodeSchemaMap(); for (Map.Entry entry : nodeSchemaMap.entrySet()) { Schema schema = entry.getValue(); @@ -137,7 +138,7 @@ private ModulePartNode modifyDocument(SourceModifierContext context, ModulePartN if (targetNode.kind() == SyntaxKind.SERVICE_DECLARATION) { ServiceDeclarationNode updatedNode = modifyServiceDeclarationNode( (ServiceDeclarationNode) targetNode, schemaString, prefix); - nodeMap.put(targetNode, updatedNode); + modifiedNodes.put((NonTerminalNode) targetNode, updatedNode); this.entityTypeNamesMap.put(targetNode, this.entityUnionTypeName); this.entityUnionSuffix++; } else if (targetNode.kind() == SyntaxKind.MODULE_VAR_DECL) { @@ -145,49 +146,60 @@ private ModulePartNode modifyDocument(SourceModifierContext context, ModulePartN = (ModuleVariableDeclarationNode) targetNode; ModuleVariableDeclarationNode updatedNode = modifyServiceVariableDeclarationNode( schemaString, graphqlServiceVariableDeclaration, prefix); - nodeMap.put(targetNode, updatedNode); + modifiedNodes.put((NonTerminalNode) targetNode, updatedNode); this.entityTypeNamesMap.put(targetNode, this.entityUnionTypeName); this.entityUnionSuffix++; - } else if (targetNode.kind() == SyntaxKind.OBJECT_CONSTRUCTOR) { - ObjectConstructorExpressionNode graphqlServiceVariableDeclaration - = (ObjectConstructorExpressionNode) targetNode; + } else if (targetNode.kind() == SyntaxKind.LOCAL_VAR_DECL) { + VariableDeclarationNode graphqlServiceVariableDeclaration = (VariableDeclarationNode) targetNode; + ObjectConstructorExpressionNode graphqlServiceObject + = (ObjectConstructorExpressionNode) graphqlServiceVariableDeclaration.initializer().get(); ObjectConstructorExpressionNode updatedGraphqlServiceObject = modifyServiceObjectNode( - graphqlServiceVariableDeclaration, schemaString, prefix); - VariableDeclarationNode parentNode - = (VariableDeclarationNode) graphqlServiceVariableDeclaration.parent(); - VariableDeclarationNode updatedParentNode = parentNode.modify() - .withInitializer(updatedGraphqlServiceObject) - .apply(); - Node replacingNode = updatedParentNode; - NonTerminalNode iNode = parentNode; - while (iNode.parent() != null) { - replacingNode = iNode.parent().replace(iNode, replacingNode); - iNode = iNode.parent(); - } - rootNode = rootNode.replace(iNode, replacingNode); + graphqlServiceObject, schemaString, prefix); + VariableDeclarationNode updatedNode = graphqlServiceVariableDeclaration + .modify() + .withInitializer(updatedGraphqlServiceObject) + .apply(); + modifiedNodes.put((NonTerminalNode) targetNode, updatedNode); + this.entityTypeNamesMap.put(targetNode, this.entityUnionTypeName); + this.entityUnionSuffix++; } } catch (IOException e) { updateContext(context, entry.getKey().location(), CompilationDiagnostic.SCHEMA_GENERATION_FAILED, e.getMessage()); } } - NodeList members = NodeFactory.createNodeList(); - for (ModuleMemberDeclarationNode member : rootNode.members()) { - if (member.kind() == SyntaxKind.SERVICE_DECLARATION || member.kind() == SyntaxKind.MODULE_VAR_DECL) { - if (nodeMap.containsKey(member)) { - this.entityUnionTypeName = this.entityTypeNamesMap.get(member); - Schema schema = nodeSchemaMap.get(member); - isSubgraph = schema.isSubgraph(); - if (schema.getEntities().size() > 0) { - this.entities = schema.getEntities().stream().map(Type::getName).collect(Collectors.toList()); - members = addEntityTypeDefinition(members); - } - members = members.add((ModuleMemberDeclarationNode) nodeMap.get(member)); - continue; + + ArrayList sortedNodesToBeModified = new ArrayList<>(modifiedNodes.keySet()); + sortedNodesToBeModified.sort((n1, n2) -> n1.textRange().startOffset() - n2.textRange().startOffset()); + NodeList entities = NodeFactory.createNodeList(); + for (NonTerminalNode targetNode : sortedNodesToBeModified) { + this.entityUnionTypeName = this.entityTypeNamesMap.get(targetNode); + Schema schema = nodeSchemaMap.get(targetNode); + isSubgraph = schema.isSubgraph(); + if (schema.getEntities().size() > 0) { + this.entities = schema.getEntities().stream().map(Type::getName).collect(Collectors.toList()); + if (isSubgraph) { + entities = addEntityTypeDefinition(entities); } } - members = members.add(member); } + + int prevModifiedNodeLength = 0; + int prevOriginalNodeLength = 0; + for (NonTerminalNode originalNode : sortedNodesToBeModified) { + int originalNodeStartOffset = originalNode.textRangeWithMinutiae().startOffset() + + prevModifiedNodeLength - prevOriginalNodeLength; + int originalNodeLength = originalNode.textRangeWithMinutiae().length(); + NonTerminalNode replacingNode = rootNode.findNode( + TextRange.from(originalNodeStartOffset, originalNodeLength), + true); + rootNode = rootNode.replace(replacingNode, modifiedNodes.get(originalNode)); + prevModifiedNodeLength += modifiedNodes.get(originalNode).textRangeWithMinutiae().length(); + prevOriginalNodeLength += originalNode.textRangeWithMinutiae().length(); + } + + NodeList members = rootNode.members() + .addAll(new ArrayList<>(entities.stream().toList())); return rootNode.modify(rootNode.imports(), members, rootNode.eofToken()); } From 04e8bc573bd9dc62301fce4a600332a67221ca55 Mon Sep 17 00:00:00 2001 From: Indrajith Madhumal Date: Sun, 14 Jan 2024 17:17:04 +0530 Subject: [PATCH 11/17] Add support to generate schema for object field --- .../compiler/SchemaGenerationTest.java | 14 +-- .../service.bal | 64 +++++++++- .../service.bal | 3 +- .../Ballerina.toml | 0 .../service.bal | 49 ++++++++ .../Ballerina.toml | 0 .../service.bal | 0 .../graphql/compiler/GraphqlCodeModifier.java | 2 - .../ObjectConstructorAnalysisTask.java | 56 ++++----- .../generator/GraphqlSourceModifier.java | 119 +++++++++++------- 10 files changed, 210 insertions(+), 97 deletions(-) rename compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/{26_multiple_service_declarations => 26_object_field_service_object_declarations}/Ballerina.toml (100%) create mode 100644 compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/26_object_field_service_object_declarations/service.bal rename compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/{27_local_graphql_service_with_http_service => 27_inline_service_declarations}/Ballerina.toml (100%) rename compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/{29_inline_service_declarations => 27_inline_service_declarations}/service.bal (100%) diff --git a/compiler-plugin-tests/src/test/java/io/ballerina/stdlib/graphql/compiler/SchemaGenerationTest.java b/compiler-plugin-tests/src/test/java/io/ballerina/stdlib/graphql/compiler/SchemaGenerationTest.java index a26944723..29d059eb5 100644 --- a/compiler-plugin-tests/src/test/java/io/ballerina/stdlib/graphql/compiler/SchemaGenerationTest.java +++ b/compiler-plugin-tests/src/test/java/io/ballerina/stdlib/graphql/compiler/SchemaGenerationTest.java @@ -215,27 +215,19 @@ public void testGraphqlLocalServiceObjects() { } @Test - public void testGraphqlMultipleServiceDeclarations() { - String packagePath = "26_multiple_service_declarations"; - DiagnosticResult diagnosticResult = getDiagnosticResult(packagePath); - Assert.assertEquals(diagnosticResult.errorCount(), 0); - } - - @Test - public void testLocalGraphqlObjectDeclarationWithHttpService() { - String packagePath = "27_local_graphql_service_with_http_service"; + public void testGraphqlObjectFieldServiceObjects() { + String packagePath = "26_object_field_service_object_declarations"; DiagnosticResult diagnosticResult = getDiagnosticResult(packagePath); Assert.assertEquals(diagnosticResult.errorCount(), 0); } @Test public void testMultipleInlineServiceDeclarations() { - String packagePath = "29_inline_service_declarations"; + String packagePath = "27_inline_service_declarations"; DiagnosticResult diagnosticResult = getDiagnosticResult(packagePath); Assert.assertEquals(diagnosticResult.errorCount(), 0); } - private DiagnosticResult getDiagnosticResult(String path) { Path projectDirPath = RESOURCE_DIRECTORY.resolve(path); BuildProject project = BuildProject.load(getEnvironmentBuilder(), projectDirPath); diff --git a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/22_graphql_service_with_http_service/service.bal b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/22_graphql_service_with_http_service/service.bal index 1682b5f07..4f4851531 100644 --- a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/22_graphql_service_with_http_service/service.bal +++ b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/22_graphql_service_with_http_service/service.bal @@ -16,6 +16,7 @@ import ballerina/http; import ballerina/graphql; +import ballerina/lang.runtime; service /greeting on new http:Listener(9090) { resource function get greeting() returns string { @@ -23,8 +24,63 @@ service /greeting on new http:Listener(9090) { } } -service graphql:Service /query on new graphql:Listener(8080) { - resource function get name() returns string { - return "Jack"; - } +service / on new graphql:Listener(9091) { + resource function get greeting() returns string { + return "Hello from global listener binding service"; + } +} + +service /greeting on new http:Listener(9092) { + resource function get greeting() returns string { + return "Hello, World!"; + } +} + +graphql:Service globalService = service object { + resource function get greeting() returns string { + return "Hello from global service"; + } +}; + +service /too on new graphql:Listener(9093) { + resource function get greeting() returns string { + return "Hello from global listener binding service too"; + } +} + +class TestService { + private graphql:Service fieldService = service object { + resource function get greeting() returns string { + return "Hello from object field service object"; + } + }; + + public function init() {} + + public function startService() returns error? { + graphql:Listener localListener = check new(9094); + check localListener.attach(self.fieldService); + check localListener.'start(); + runtime:registerListener(localListener); + } +} + +public function main() returns error? { + graphql:Service localService = service object { + resource function get greeting() returns string { + return "Hello from local service 2"; + } + }; + + TestService serviceClass = new (); + check serviceClass.startService(); + + graphql:Listener localListener = check new(9095); + graphql:Listener globalListener = check new(9096); + check localListener.attach(localService); + check globalListener.attach(globalService); + check localListener.'start(); + check globalListener.'start(); + runtime:registerListener(localListener); + runtime:registerListener(globalListener); } diff --git a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/25_local_service_object_declarations/service.bal b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/25_local_service_object_declarations/service.bal index c498dbf27..622e2bd92 100644 --- a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/25_local_service_object_declarations/service.bal +++ b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/25_local_service_object_declarations/service.bal @@ -23,10 +23,9 @@ public function main() returns error? { return "Hello from local service 1"; } }; - graphql:Service localService2 = service object { resource function get greeting() returns string { - return "Hello from local service 2"; + return "Hello from local service 1"; } }; graphql:Listener localListener1 = check new(9090); diff --git a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/26_multiple_service_declarations/Ballerina.toml b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/26_object_field_service_object_declarations/Ballerina.toml similarity index 100% rename from compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/26_multiple_service_declarations/Ballerina.toml rename to compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/26_object_field_service_object_declarations/Ballerina.toml diff --git a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/26_object_field_service_object_declarations/service.bal b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/26_object_field_service_object_declarations/service.bal new file mode 100644 index 000000000..eadf7fdb5 --- /dev/null +++ b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/26_object_field_service_object_declarations/service.bal @@ -0,0 +1,49 @@ +// Copyright (c) 2024 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. + +import ballerina/lang.runtime; +import ballerina/graphql; + +class TestService { + private graphql:Service fieldService1 = service object { + resource function get greeting() returns string { + return "Hello from object field service object 1"; + } + }; + private graphql:Service fieldService2 = service object { + resource function get greeting() returns string { + return "Hello from object field service object 2"; + } + }; + + public function init() {} + + public function startService() returns error? { + graphql:Listener localListener1 = check new(9090); + graphql:Listener localListener2 = check new(9091); + check localListener1.attach(self.fieldService1); + check localListener2.attach(self.fieldService2); + check localListener1.'start(); + check localListener2.'start(); + runtime:registerListener(localListener1); + runtime:registerListener(localListener2); + } +} + +public function main() returns error? { + TestService serviceClass = new (); + check serviceClass.startService(); +} \ No newline at end of file diff --git a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/27_local_graphql_service_with_http_service/Ballerina.toml b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/27_inline_service_declarations/Ballerina.toml similarity index 100% rename from compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/27_local_graphql_service_with_http_service/Ballerina.toml rename to compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/27_inline_service_declarations/Ballerina.toml diff --git a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/29_inline_service_declarations/service.bal b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/27_inline_service_declarations/service.bal similarity index 100% rename from compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/29_inline_service_declarations/service.bal rename to compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/27_inline_service_declarations/service.bal diff --git a/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/GraphqlCodeModifier.java b/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/GraphqlCodeModifier.java index e20da8d33..4b46039f7 100644 --- a/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/GraphqlCodeModifier.java +++ b/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/GraphqlCodeModifier.java @@ -44,8 +44,6 @@ public GraphqlCodeModifier() { public void init(CodeModifierContext modifierContext) { modifierContext.addSyntaxNodeAnalysisTask(new ServiceDeclarationAnalysisTask(this.modifierContextMap), SyntaxKind.SERVICE_DECLARATION); - modifierContext.addSyntaxNodeAnalysisTask( - new ModuleLevelVariableDeclarationAnalysisTask(this.modifierContextMap), SyntaxKind.MODULE_VAR_DECL); modifierContext.addSyntaxNodeAnalysisTask( new ObjectConstructorAnalysisTask(this.modifierContextMap), SyntaxKind.OBJECT_CONSTRUCTOR); modifierContext.addSyntaxNodeAnalysisTask(new InterceptorAnalysisTask(), SyntaxKind.CLASS_DEFINITION); diff --git a/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/ObjectConstructorAnalysisTask.java b/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/ObjectConstructorAnalysisTask.java index 6ffb77048..a95bbd7e7 100644 --- a/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/ObjectConstructorAnalysisTask.java +++ b/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/ObjectConstructorAnalysisTask.java @@ -17,12 +17,13 @@ package io.ballerina.stdlib.graphql.compiler; import io.ballerina.compiler.api.SemanticModel; -import io.ballerina.compiler.api.symbols.TypeReferenceTypeSymbol; +import io.ballerina.compiler.api.symbols.Symbol; +import io.ballerina.compiler.syntax.tree.ModuleVariableDeclarationNode; +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.NonTerminalNode; import io.ballerina.compiler.syntax.tree.ObjectConstructorExpressionNode; -import io.ballerina.compiler.syntax.tree.QualifiedNameReferenceNode; +import io.ballerina.compiler.syntax.tree.ObjectFieldNode; import io.ballerina.compiler.syntax.tree.SyntaxKind; -import io.ballerina.compiler.syntax.tree.TypeDescriptorNode; -import io.ballerina.compiler.syntax.tree.TypedBindingPatternNode; import io.ballerina.compiler.syntax.tree.VariableDeclarationNode; import io.ballerina.projects.DocumentId; import io.ballerina.projects.plugins.SyntaxNodeAnalysisContext; @@ -32,7 +33,9 @@ import io.ballerina.stdlib.graphql.compiler.service.validator.ServiceValidator; import java.util.Map; +import java.util.Optional; +import static io.ballerina.stdlib.graphql.commons.utils.Utils.isGraphqlModuleSymbol; import static io.ballerina.stdlib.graphql.compiler.Utils.hasCompilationErrors; public class ObjectConstructorAnalysisTask extends ServiceAnalysisTask { @@ -41,22 +44,16 @@ public ObjectConstructorAnalysisTask(Map nod super(nodeMap); } - private static final String PACKAGE_NAME = "graphql"; - private static final String SERVICE_NAME = "Service"; - @Override public void perform(SyntaxNodeAnalysisContext context) { if (hasCompilationErrors(context)) { return; } ObjectConstructorExpressionNode node = (ObjectConstructorExpressionNode) context.node(); - if (node.parent().kind() != SyntaxKind.LOCAL_VAR_DECL) { - return; - } - VariableDeclarationNode parentNode = (VariableDeclarationNode) node.parent(); - if (!isGraphQLServiceObjectDeclaration(context.semanticModel(), parentNode.typedBindingPattern())) { + if (!isGraphQLServiceObjectDeclaration(context.semanticModel(), (NonTerminalNode) context.node())) { return; } + InterfaceEntityFinder interfaceEntityFinder = getInterfaceEntityFinder(context.semanticModel()); ServiceValidator serviceValidator = getServiceValidator(context, node, interfaceEntityFinder); if (serviceValidator.isErrorOccurred()) { @@ -68,30 +65,21 @@ public void perform(SyntaxNodeAnalysisContext context) { } public boolean isGraphQLServiceObjectDeclaration(SemanticModel semanticModel, - TypedBindingPatternNode typedBindingPatternNode) { - TypeDescriptorNode typeDescriptorNode = typedBindingPatternNode.typeDescriptor(); - if (typeDescriptorNode.kind() != SyntaxKind.QUALIFIED_NAME_REFERENCE) { - return false; - } - return isGraphqlServiceQualifiedNameReference(semanticModel, (QualifiedNameReferenceNode) typeDescriptorNode); - } - - private boolean isGraphqlServiceQualifiedNameReference(SemanticModel semanticModel, - QualifiedNameReferenceNode nameReferenceNode) { - if (semanticModel.symbol(nameReferenceNode).isEmpty()) { - return false; - } - TypeReferenceTypeSymbol typeSymbol = (TypeReferenceTypeSymbol) semanticModel.symbol(nameReferenceNode).get(); - if (typeSymbol.getModule().isEmpty()) { - return false; - } - if (!PACKAGE_NAME.equals(typeSymbol.getModule().get().getName().get())) { - return false; - } - if (typeSymbol.getName().isEmpty()) { + NonTerminalNode node) { + Node typeReferenceNode; + if (node.parent().kind() == SyntaxKind.LOCAL_VAR_DECL) { + typeReferenceNode = ((VariableDeclarationNode) node.parent()).typedBindingPattern() + .typeDescriptor(); + } else if (node.parent().kind() == SyntaxKind.MODULE_VAR_DECL) { + typeReferenceNode = ((ModuleVariableDeclarationNode) node.parent()).typedBindingPattern() + .typeDescriptor(); + } else if (node.parent().kind() == SyntaxKind.OBJECT_FIELD) { + typeReferenceNode = ((ObjectFieldNode) node.parent()).typeName(); + } else { return false; } - return SERVICE_NAME.equals(typeSymbol.getName().get()); + Optional typeReferenceSymbol = semanticModel.symbol(typeReferenceNode); + return typeReferenceSymbol.isPresent() && isGraphqlModuleSymbol(typeReferenceSymbol.get()); } } diff --git a/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/schema/generator/GraphqlSourceModifier.java b/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/schema/generator/GraphqlSourceModifier.java index cd0de385b..2277c3f06 100644 --- a/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/schema/generator/GraphqlSourceModifier.java +++ b/compiler-plugin/src/main/java/io/ballerina/stdlib/graphql/compiler/schema/generator/GraphqlSourceModifier.java @@ -36,6 +36,7 @@ import io.ballerina.compiler.syntax.tree.NodeParser; import io.ballerina.compiler.syntax.tree.NonTerminalNode; import io.ballerina.compiler.syntax.tree.ObjectConstructorExpressionNode; +import io.ballerina.compiler.syntax.tree.ObjectFieldNode; import io.ballerina.compiler.syntax.tree.QualifiedNameReferenceNode; import io.ballerina.compiler.syntax.tree.SeparatedNodeList; import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; @@ -68,6 +69,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Base64; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -144,21 +146,22 @@ private ModulePartNode modifyDocument(SourceModifierContext context, ModulePartN } else if (targetNode.kind() == SyntaxKind.MODULE_VAR_DECL) { ModuleVariableDeclarationNode graphqlServiceVariableDeclaration = (ModuleVariableDeclarationNode) targetNode; - ModuleVariableDeclarationNode updatedNode = modifyServiceVariableDeclarationNode( + ModuleVariableDeclarationNode updatedNode = modifyModuleLevelServiceDeclarationNode( schemaString, graphqlServiceVariableDeclaration, prefix); modifiedNodes.put((NonTerminalNode) targetNode, updatedNode); this.entityTypeNamesMap.put(targetNode, this.entityUnionTypeName); this.entityUnionSuffix++; } else if (targetNode.kind() == SyntaxKind.LOCAL_VAR_DECL) { VariableDeclarationNode graphqlServiceVariableDeclaration = (VariableDeclarationNode) targetNode; - ObjectConstructorExpressionNode graphqlServiceObject - = (ObjectConstructorExpressionNode) graphqlServiceVariableDeclaration.initializer().get(); - ObjectConstructorExpressionNode updatedGraphqlServiceObject = modifyServiceObjectNode( - graphqlServiceObject, schemaString, prefix); - VariableDeclarationNode updatedNode = graphqlServiceVariableDeclaration - .modify() - .withInitializer(updatedGraphqlServiceObject) - .apply(); + VariableDeclarationNode updatedNode = modifyVariableServiceDeclarationNode( + schemaString, graphqlServiceVariableDeclaration, prefix); + modifiedNodes.put((NonTerminalNode) targetNode, updatedNode); + this.entityTypeNamesMap.put(targetNode, this.entityUnionTypeName); + this.entityUnionSuffix++; + } else if (targetNode.kind() == SyntaxKind.OBJECT_FIELD) { + ObjectFieldNode graphqlServiceFieldDeclaration = (ObjectFieldNode) targetNode; + ObjectFieldNode updatedNode = modifyObjectFieldServiceDeclarationNode( + schemaString, graphqlServiceFieldDeclaration, prefix); modifiedNodes.put((NonTerminalNode) targetNode, updatedNode); this.entityTypeNamesMap.put(targetNode, this.entityUnionTypeName); this.entityUnionSuffix++; @@ -169,38 +172,12 @@ private ModulePartNode modifyDocument(SourceModifierContext context, ModulePartN } } - ArrayList sortedNodesToBeModified = new ArrayList<>(modifiedNodes.keySet()); - sortedNodesToBeModified.sort((n1, n2) -> n1.textRange().startOffset() - n2.textRange().startOffset()); - NodeList entities = NodeFactory.createNodeList(); - for (NonTerminalNode targetNode : sortedNodesToBeModified) { - this.entityUnionTypeName = this.entityTypeNamesMap.get(targetNode); - Schema schema = nodeSchemaMap.get(targetNode); - isSubgraph = schema.isSubgraph(); - if (schema.getEntities().size() > 0) { - this.entities = schema.getEntities().stream().map(Type::getName).collect(Collectors.toList()); - if (isSubgraph) { - entities = addEntityTypeDefinition(entities); - } - } - } - - int prevModifiedNodeLength = 0; - int prevOriginalNodeLength = 0; - for (NonTerminalNode originalNode : sortedNodesToBeModified) { - int originalNodeStartOffset = originalNode.textRangeWithMinutiae().startOffset() - + prevModifiedNodeLength - prevOriginalNodeLength; - int originalNodeLength = originalNode.textRangeWithMinutiae().length(); - NonTerminalNode replacingNode = rootNode.findNode( - TextRange.from(originalNodeStartOffset, originalNodeLength), - true); - rootNode = rootNode.replace(replacingNode, modifiedNodes.get(originalNode)); - prevModifiedNodeLength += modifiedNodes.get(originalNode).textRangeWithMinutiae().length(); - prevOriginalNodeLength += originalNode.textRangeWithMinutiae().length(); - } - - NodeList members = rootNode.members() - .addAll(new ArrayList<>(entities.stream().toList())); - return rootNode.modify(rootNode.imports(), members, rootNode.eofToken()); + ArrayList nodesToBeModified = new ArrayList<>(modifiedNodes.keySet()); + nodesToBeModified.sort(Comparator.comparingInt(n -> n.textRange().startOffset())); + List entities = getEntityTypeDefinitions(nodeSchemaMap, nodesToBeModified); + ModulePartNode modifiedRootNode = addServiceDeclarationAnnotations(rootNode, nodesToBeModified, modifiedNodes); + NodeList modifiedMembers = modifiedRootNode.members().addAll(entities); + return modifiedRootNode.modify(modifiedRootNode.imports(), modifiedMembers, modifiedRootNode.eofToken()); } private String getSubgraphModulePrefix(ModulePartNode rootNode) { @@ -228,15 +205,49 @@ private NodeList addEntityTypeDefinition( return moduleMembers.add(typeDefinition); } + private ModulePartNode addServiceDeclarationAnnotations(ModulePartNode rootNode, + ArrayList nodesToBeModified, + Map modifiedNodes) { + int prevModifiedNodeLength = 0; + int prevOriginalNodeLength = 0; + for (NonTerminalNode originalNode : nodesToBeModified) { + int originalNodeStartOffset = originalNode.textRangeWithMinutiae().startOffset() + + prevModifiedNodeLength - prevOriginalNodeLength; + int originalNodeLength = originalNode.textRangeWithMinutiae().length(); + NonTerminalNode replacingNode = rootNode.findNode( + TextRange.from(originalNodeStartOffset, originalNodeLength), + true); + rootNode = rootNode.replace(replacingNode, modifiedNodes.get(originalNode)); + prevModifiedNodeLength += modifiedNodes.get(originalNode).textRangeWithMinutiae().length(); + prevOriginalNodeLength += originalNode.textRangeWithMinutiae().length(); + } + return rootNode; + } + + private List getEntityTypeDefinitions(Map nodeSchemaMap, + ArrayList serviceNodes) { + NodeList entities = NodeFactory.createNodeList(); + for (NonTerminalNode serviceNode : serviceNodes) { + this.entityUnionTypeName = this.entityTypeNamesMap.get(serviceNode); + Schema schema = nodeSchemaMap.get(serviceNode); + isSubgraph = schema.isSubgraph(); + if (schema.getEntities().size() > 0) { + this.entities = schema.getEntities().stream().map(Type::getName).collect(Collectors.toList()); + entities = addEntityTypeDefinition(entities); + } + } + return entities.stream().toList(); + } + private ModuleMemberDeclarationNode getEntityTypeDefinition() { String unionOfEntities = String.join("|", this.entities); return NodeParser.parseModuleMemberDeclaration( "type " + this.entityUnionTypeName + " " + unionOfEntities + ";"); } - private ModuleVariableDeclarationNode modifyServiceVariableDeclarationNode(String schemaString, - ModuleVariableDeclarationNode node, - String prefix) { + private ModuleVariableDeclarationNode modifyModuleLevelServiceDeclarationNode(String schemaString, + ModuleVariableDeclarationNode node, + String prefix) { // noinspection OptionalGetWithoutIsPresent ObjectConstructorExpressionNode graphqlServiceObject = (ObjectConstructorExpressionNode) node.initializer().get(); @@ -245,6 +256,26 @@ private ModuleVariableDeclarationNode modifyServiceVariableDeclarationNode(Strin return node.modify().withInitializer(updatedGraphqlServiceObject).apply(); } + private VariableDeclarationNode modifyVariableServiceDeclarationNode(String schemaString, + VariableDeclarationNode node, + String prefix) { + ObjectConstructorExpressionNode graphqlServiceObject + = (ObjectConstructorExpressionNode) node.initializer().get(); + ObjectConstructorExpressionNode updatedGraphqlServiceObject = modifyServiceObjectNode( + graphqlServiceObject, schemaString, prefix); + return node.modify().withInitializer(updatedGraphqlServiceObject).apply(); + } + + private ObjectFieldNode modifyObjectFieldServiceDeclarationNode(String schemaString, + ObjectFieldNode node, + String prefix) { + ObjectConstructorExpressionNode graphqlServiceObject + = (ObjectConstructorExpressionNode) node.expression().get(); + ObjectConstructorExpressionNode updatedGraphqlServiceObject = modifyServiceObjectNode( + graphqlServiceObject, schemaString, prefix); + return node.modify().withExpression(updatedGraphqlServiceObject).apply(); + } + private ObjectConstructorExpressionNode modifyServiceObjectNode(ObjectConstructorExpressionNode node, String schemaString, String prefix) { NodeList annotations = NodeFactory.createNodeList(); From ed29c33382b7f488ff69cba6bca61abecda1d608 Mon Sep 17 00:00:00 2001 From: Indrajith Madhumal Date: Sun, 14 Jan 2024 17:19:33 +0530 Subject: [PATCH 12/17] Remove unused test case resources --- .../service.bal | 46 ------------------- .../Ballerina.toml | 4 -- 2 files changed, 50 deletions(-) delete mode 100644 compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/27_local_graphql_service_with_http_service/service.bal delete mode 100644 compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/29_inline_service_declarations/Ballerina.toml diff --git a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/27_local_graphql_service_with_http_service/service.bal b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/27_local_graphql_service_with_http_service/service.bal deleted file mode 100644 index 471f69c90..000000000 --- a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/27_local_graphql_service_with_http_service/service.bal +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2024 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. - -import ballerina/lang.runtime; -import ballerina/graphql; -import ballerina/http; - -service / on new http:Listener(9090) { - resource function get greet() returns string { - return "Hello from global http service"; - } -} - -public function main() returns error? { - graphql:Service gqlService = service object { - resource function get greet() returns string { - return "Hello from gql service"; - } - }; - http:Service httpService = service object { - resource function get greet() returns string { - return "Hello from local http service"; - } - }; - graphql:Listener gqlListener = check new(9091); - http:Listener httpListener = check new(9092); - check gqlListener.attach(gqlService); - check httpListener.attach(httpService); - check gqlListener.'start(); - check httpListener.'start(); - runtime:registerListener(gqlListener); - runtime:registerListener(httpListener); -} \ No newline at end of file diff --git a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/29_inline_service_declarations/Ballerina.toml b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/29_inline_service_declarations/Ballerina.toml deleted file mode 100644 index b58c76324..000000000 --- a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/29_inline_service_declarations/Ballerina.toml +++ /dev/null @@ -1,4 +0,0 @@ -[package] -org = "graphql_test" -name = "test_package" -version = "0.1.0" From 54f7a2f905a065a016a341023888ad17bd54a3e0 Mon Sep 17 00:00:00 2001 From: Indrajith Madhumal Date: Tue, 16 Jan 2024 12:04:11 +0530 Subject: [PATCH 13/17] Add test case with distinct service class type --- .../service.bal | 62 ------------------- .../Ballerina.toml | 4 ++ .../service.bal | 53 ++++++++++++++++ 3 files changed, 57 insertions(+), 62 deletions(-) delete mode 100644 compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/26_multiple_service_declarations/service.bal create mode 100644 compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/28_with_distinct_service_class_type/Ballerina.toml create mode 100644 compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/28_with_distinct_service_class_type/service.bal diff --git a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/26_multiple_service_declarations/service.bal b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/26_multiple_service_declarations/service.bal deleted file mode 100644 index b45f6ac14..000000000 --- a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/26_multiple_service_declarations/service.bal +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) 2024 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. - -import ballerina/lang.runtime; -import ballerina/graphql; - -service / on new graphql:Listener(9090) { - resource function get greeting() returns string { - return "Hello from global listener binding service"; - } -} - -graphql:Service globalService = service object { - resource function get greeting() returns string { - return "Hello from global service"; - } -}; - -service /too on new graphql:Listener(9091) { - resource function get greeting() returns string { - return "Hello from global listener binding service too"; - } -} - -public function main() returns error? { - graphql:Service localService1 = service object { - resource function get greeting() returns string { - return "Hello from local service 1"; - } - }; - - graphql:Service localService2 = service object { - resource function get greeting() returns string { - return "Hello from local service 2"; - } - }; - graphql:Listener localListener1 = check new(9092); - graphql:Listener localListener2 = check new(9093); - graphql:Listener globalListener = check new(9094); - check localListener1.attach(localService1); - check localListener2.attach(localService2); - check globalListener.attach(globalService); - check localListener1.'start(); - check localListener2.'start(); - check globalListener.'start(); - runtime:registerListener(localListener1); - runtime:registerListener(localListener2); - runtime:registerListener(globalListener); -} \ No newline at end of file diff --git a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/28_with_distinct_service_class_type/Ballerina.toml b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/28_with_distinct_service_class_type/Ballerina.toml new file mode 100644 index 000000000..b58c76324 --- /dev/null +++ b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/28_with_distinct_service_class_type/Ballerina.toml @@ -0,0 +1,4 @@ +[package] +org = "graphql_test" +name = "test_package" +version = "0.1.0" diff --git a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/28_with_distinct_service_class_type/service.bal b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/28_with_distinct_service_class_type/service.bal new file mode 100644 index 000000000..77981c1a0 --- /dev/null +++ b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/28_with_distinct_service_class_type/service.bal @@ -0,0 +1,53 @@ +// Copyright (c) 2024 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. + +import ballerina/lang.runtime; +import ballerina/graphql; + +public distinct service isolated class Person { + private final string name; + + isolated function init(string name) { + self.name = name; + } + + isolated resource function get name() returns string { + return self.name; + } + +} + +class TestService { + private graphql:Service fieldService = service object { + resource function get greeting() returns Person { + return new Person("Rick"); + } + }; + + public function init() {} + + public function startService() returns error? { + graphql:Listener localListener = check new(9090); + check localListener.attach(self.fieldService); + check localListener.'start(); + runtime:registerListener(localListener); + } +} + +public function main() returns error? { + TestService serviceClass = new (); + check serviceClass.startService(); +} \ No newline at end of file From 7472df0d1045cb0ac02ddc1d1dab842afbca95d4 Mon Sep 17 00:00:00 2001 From: Indrajith Madhumal Date: Tue, 16 Jan 2024 13:45:28 +0530 Subject: [PATCH 14/17] Apply suggestions from code review --- .../25_local_service_object_declarations/service.bal | 2 +- .../26_object_field_service_object_declarations/service.bal | 2 +- .../generator_tests/27_inline_service_declarations/service.bal | 2 +- .../28_with_distinct_service_class_type/service.bal | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/25_local_service_object_declarations/service.bal b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/25_local_service_object_declarations/service.bal index 622e2bd92..dfe6b547f 100644 --- a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/25_local_service_object_declarations/service.bal +++ b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/25_local_service_object_declarations/service.bal @@ -36,4 +36,4 @@ public function main() returns error? { check localListener2.'start(); runtime:registerListener(localListener1); runtime:registerListener(localListener2); -} \ No newline at end of file +} diff --git a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/26_object_field_service_object_declarations/service.bal b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/26_object_field_service_object_declarations/service.bal index eadf7fdb5..0914ed7f9 100644 --- a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/26_object_field_service_object_declarations/service.bal +++ b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/26_object_field_service_object_declarations/service.bal @@ -46,4 +46,4 @@ class TestService { public function main() returns error? { TestService serviceClass = new (); check serviceClass.startService(); -} \ No newline at end of file +} diff --git a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/27_inline_service_declarations/service.bal b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/27_inline_service_declarations/service.bal index 82d3aa605..31d71e458 100644 --- a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/27_inline_service_declarations/service.bal +++ b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/27_inline_service_declarations/service.bal @@ -14,4 +14,4 @@ // specific language governing permissions and limitations // under the License. -import ballerina/lang.runtime; import ballerina/graphql; import ballerina/http; service / on new http:Listener(9090) { resource function get greet() returns string { return "Hello from global http service"; } } public function main() returns error? { graphql:Service gqlService = service object { resource function get greet() returns string { return "Hello from gql service"; } }; http:Service httpService = service object { resource function get greet() returns string { return "Hello from local http service"; } }; graphql:Listener gqlListener = check new(9091); http:Listener httpListener = check new(9092); check gqlListener.attach(gqlService); check httpListener.attach(httpService); check gqlListener.'start(); check httpListener.'start(); runtime:registerListener(gqlListener); runtime:registerListener(httpListener); } \ No newline at end of file +import ballerina/lang.runtime; import ballerina/graphql; import ballerina/http; service / on new http:Listener(9090) { resource function get greet() returns string { return "Hello from global http service"; } } public function main() returns error? { graphql:Service gqlService = service object { resource function get greet() returns string { return "Hello from gql service"; } }; http:Service httpService = service object { resource function get greet() returns string { return "Hello from local http service"; } }; graphql:Listener gqlListener = check new(9091); http:Listener httpListener = check new(9092); check gqlListener.attach(gqlService); check httpListener.attach(httpService); check gqlListener.'start(); check httpListener.'start(); runtime:registerListener(gqlListener); runtime:registerListener(httpListener); } diff --git a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/28_with_distinct_service_class_type/service.bal b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/28_with_distinct_service_class_type/service.bal index 77981c1a0..476cf33cc 100644 --- a/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/28_with_distinct_service_class_type/service.bal +++ b/compiler-plugin-tests/src/test/resources/ballerina_sources/generator_tests/28_with_distinct_service_class_type/service.bal @@ -50,4 +50,4 @@ class TestService { public function main() returns error? { TestService serviceClass = new (); check serviceClass.startService(); -} \ No newline at end of file +} From 808548d04bd9976e1c78107b89f9dfee30f65144 Mon Sep 17 00:00:00 2001 From: Indrajith Madhumal Date: Fri, 19 Jan 2024 10:36:32 +0530 Subject: [PATCH 15/17] [Automated] Update the native jar versions --- ballerina-tests/Dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ballerina-tests/Dependencies.toml b/ballerina-tests/Dependencies.toml index a1580ec99..93db6f16b 100644 --- a/ballerina-tests/Dependencies.toml +++ b/ballerina-tests/Dependencies.toml @@ -311,7 +311,7 @@ dependencies = [ [[package]] org = "ballerina" name = "observe" -version = "1.2.0" +version = "1.2.2" dependencies = [ {org = "ballerina", name = "jballerina.java"} ] From bdedebd00f446c4aa1ae888954a2756989021c5d Mon Sep 17 00:00:00 2001 From: Indrajith Madhumal Date: Fri, 19 Jan 2024 10:36:33 +0530 Subject: [PATCH 16/17] [Automated] Update the native jar versions --- ballerina/Dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index 35fc5bf76..e23833369 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -301,7 +301,7 @@ modules = [ [[package]] org = "ballerina" name = "observe" -version = "1.2.0" +version = "1.2.2" dependencies = [ {org = "ballerina", name = "jballerina.java"} ] From 44684e5c9416dad2e8cbb5dfc43d54f291d16dd4 Mon Sep 17 00:00:00 2001 From: Indrajith Madhumal Date: Fri, 19 Jan 2024 13:16:03 +0530 Subject: [PATCH 17/17] Add runtime tests --- ballerina-tests/tests/01_listener_test.bal | 42 ++++++++++++++++++++++ ballerina-tests/tests/test_services.bal | 16 +++++++++ 2 files changed, 58 insertions(+) diff --git a/ballerina-tests/tests/01_listener_test.bal b/ballerina-tests/tests/01_listener_test.bal index 2f2a5ccb7..cafa718ce 100644 --- a/ballerina-tests/tests/01_listener_test.bal +++ b/ballerina-tests/tests/01_listener_test.bal @@ -144,3 +144,45 @@ function testAttachServiceWithSubscriptionToHttp1BasedListener() returns error? check validateNextMessage(wsClient2, expectedMsgPayload, id = "2"); } } + +@test:Config { + groups: ["listener", "service_object"] +} +function testServiceDeclarationUsingLocalServiceObject() returns error? { + graphql:Service localService = service object { + resource function get greeting() returns string { + return "Hello world"; + } + }; + check basicListener.attach(localService, "/local_service_object"); + + string url = "http://localhost:9091/local_service_object"; + string document = string `query { greeting }`; + json actualPayload = check getJsonPayloadFromService(url, document); + json expectedPayload = { + data: { + greeting: "Hello world" + } + }; + check basicListener.detach(localService); + test:assertEquals(actualPayload, expectedPayload); +} + +@test:Config { + groups: ["listener", "service_object"] +} +function testServiceDeclarationUsingObjectField() returns error? { + graphql:Service localService = (new ServiceDeclarationOnObjectField()).getService(); + check basicListener.attach(localService, "/object_field_service_object"); + + string url = "http://localhost:9091/object_field_service_object"; + string document = string `query { greeting }`; + json actualPayload = check getJsonPayloadFromService(url, document); + json expectedPayload = { + data: { + greeting: "Hello world" + } + }; + check basicListener.detach(localService); + test:assertEquals(actualPayload, expectedPayload); +} diff --git a/ballerina-tests/tests/test_services.bal b/ballerina-tests/tests/test_services.bal index 3cdd84bd4..593309d4e 100644 --- a/ballerina-tests/tests/test_services.bal +++ b/ballerina-tests/tests/test_services.bal @@ -2475,3 +2475,19 @@ service /defaultParam on wrappedListener { resource function get nestedField() returns NestedField => new; } + +class ServiceDeclarationOnObjectField { + + private graphql:Service objectFieldService = service object { + resource function get greeting() returns string { + return "Hello world"; + } + }; + + public function init() {} + + public function getService() returns graphql:Service { + return self.objectFieldService; + } + +}