diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..271ca1a --- /dev/null +++ b/.gitattributes @@ -0,0 +1,12 @@ +# +# https://help.github.com/articles/dealing-with-line-endings/ +# +# Linux start script should use lf +/gradlew text eol=lf + +# These are Windows script files and should use crlf +*.bat text eol=crlf + +# Ensure all Java files use LF. +*.java eol=lf +*.bal eol=lf diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 7f015a5..7674faa 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -12,11 +12,11 @@ jobs: - name: Checkout Repository uses: actions/checkout@v3 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v3 with: distribution: 'temurin' - java-version: 11 + java-version: 17 - name: Build with Gradle env: diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 472353c..0ded125 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -1,9 +1,6 @@ name: Build -on: - pull_request: - push: - branches: [main] +on: pull_request jobs: ubuntu-build: @@ -17,11 +14,11 @@ jobs: - name: Checkout Repository uses: actions/checkout@v1 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v2 with: distribution: 'temurin' - java-version: 11 + java-version: 17 - name: Build the Package env: @@ -43,17 +40,17 @@ jobs: cancel-in-progress: true steps: - name: Checkout Repository - uses: actions/checkout@v1 + uses: actions/checkout@v3 - - name: Set up JDK 11 - uses: actions/setup-java@v2 + - name: Set up JDK 17 + uses: actions/setup-java@v3 with: distribution: 'temurin' - java-version: 11 + java-version: 17 - name: Build the Project env: packageUser: ${{ github.actor }} packagePAT: ${{ secrets.GITHUB_TOKEN }} JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF8 - run: ./gradlew.bat build -Pdisable=invalid_permission --no-daemon --scan + run: ./gradlew.bat build -Pdisable=invalid_permission --stacktrace --scan --console=plain --no-daemon diff --git a/README.md b/README.md index 7c41a1b..99c8302 100644 --- a/README.md +++ b/README.md @@ -1,95 +1,95 @@ -# GraphQL Federation Gateway -[![Build](https://github.com/Ishad-M-I-M/graphql-federation-gateway/actions/workflows/pull_request.yml/badge.svg)](https://github.com/Ishad-M-I-M/graphql-federation-gateway/actions/workflows/pull_request.yml/badge.svg) -[![codecov](https://codecov.io/gh/Ishad-M-I-M/graphql-federation-gateway/branch/main/graph/badge.svg?token=hLnziNmccQ)](https://codecov.io/gh/Ishad-M-I-M/graphql-federation-gateway) -[![GitHub Last Commit](https://img.shields.io/github/last-commit/Ishad-M-I-M/graphql-federation-gateway.svg)](https://github.com/Ishad-M-I-M/graphql-federation-gateway/commits/master) -[![Github issues](https://img.shields.io/github/issues/Ishad-M-I-M/graphql-federation-gateway.svg?label=Open%20Issues)](https://github.com/Ishad-M-I-M/graphql-federation-gateway) - -A Graphql Federation Gateway implemented using Ballerina as the underline technology. -This will generate a gateway executable for a given supergraph schema. - -## Using the Gateway - -### Prerequisites -1. Download and install Java SE Development Kit (JDK) version 11 (from one of the following locations). - - - [Oracle](https://www.oracle.com/java/technologies/javase-jdk11-downloads.html) - - - [OpenJDK](https://adoptopenjdk.net/) - - > **Note:** Set the JAVA_HOME environment variable to the path name of the directory into which you installed JDK. - -2. Download and install [Ballerina](https://ballerina.io/downloads/) - -### Steps to use the gateway. -1. Download [gateway.sh](https://github.com/Ishad-M-I-M/graphql-federation-gateway/releases/download/v0.1.0/gateway.sh) - -2. To start the gateway run the following command in the terminal. - -```bash -./gateway.sh -s -p -``` - -- `supergraphPath` is a mandatory argument. -- If the port is not provided the default port `9090` will be used. - -### Try out the example -1. Navigate into `examples/astronauts_missions_example` directory. There's two federated graphql services and a supergraph schema. -2. In terminal execute `./gateway.sh -s supergraph.graphql` to start the gateway. -3. In terminal execute `bal run` inside both `astronauts_service` and `missions_service` directories to start the subgraph services. -3. Navigate into `astronuats_service` directory and execute `bal run` in the terminal to start the `astronauts_service` subgraph service. -4. Navigate into `missions_service` directory and execute `bal run` in the terminal to start the `missions_service` subgraph service. -5. Try out the following query in the graphql client. - -```graphql -query { - astronauts { - id - name - missions { - id - designation - } - } -} -``` - -## Build from the source. - -### Setup the prerequisites -1. Download and install Java SE Development Kit (JDK) version 11 (from one of the following locations). - - - [Oracle](https://www.oracle.com/java/technologies/javase-jdk11-downloads.html) - - - [OpenJDK](https://adoptopenjdk.net/) - - > **Note:** Set the JAVA_HOME environment variable to the path name of the directory into which you installed JDK. - -2. Export your Github personal access token with the read package permissions as follows. - - export packageUser= - export packagePAT= - -### Build the source - -Execute the commands below to build from the source. -> **Note:** When running the build with test in Windows use `-Pdisable=invalid-permission` to skip the Windows incompatible test cases. - -1. To build the project: -```bash -./gradlew clean build -``` - -2. To run the tests -```bash -./gradlew clean test -``` - -3. To build the project without tests: -```bash -./gradlew clean build -x test -``` - -4. Publish `jar` artifact to the local `.m2` repository: -```bash -./gradlew clean build publishToMavenLocal -``` +# GraphQL Federation Gateway +[![Build](https://github.com/Ishad-M-I-M/graphql-federation-gateway/actions/workflows/pull_request.yml/badge.svg)](https://github.com/Ishad-M-I-M/graphql-federation-gateway/actions/workflows/pull_request.yml/badge.svg) +[![codecov](https://codecov.io/gh/Ishad-M-I-M/graphql-federation-gateway/branch/main/graph/badge.svg?token=hLnziNmccQ)](https://codecov.io/gh/Ishad-M-I-M/graphql-federation-gateway) +[![GitHub Last Commit](https://img.shields.io/github/last-commit/Ishad-M-I-M/graphql-federation-gateway.svg)](https://github.com/Ishad-M-I-M/graphql-federation-gateway/commits/master) +[![Github issues](https://img.shields.io/github/issues/Ishad-M-I-M/graphql-federation-gateway.svg?label=Open%20Issues)](https://github.com/Ishad-M-I-M/graphql-federation-gateway) + +A Graphql Federation Gateway implemented using Ballerina as the underline technology. +This will generate a gateway executable for a given supergraph schema. + +## Using the Gateway + +### Prerequisites +1. Download and install Java SE Development Kit (JDK) version 17 (from one of the following locations). + + - [Oracle](https://www.oracle.com/java/technologies/downloads/#java17) + + - [OpenJDK](https://adoptopenjdk.net/) + + > **Note:** Set the JAVA_HOME environment variable to the path name of the directory into which you installed JDK. + +2. Download and install [Ballerina](https://ballerina.io/downloads/) + +### Steps to use the gateway. +1. Download [gateway.sh](https://github.com/Ishad-M-I-M/graphql-federation-gateway/releases/download/v0.1.0/gateway.sh) + +2. To start the gateway run the following command in the terminal. + +```bash +./gateway.sh -s -p +``` + +- `supergraphPath` is a mandatory argument. +- If the port is not provided the default port `9090` will be used. + +### Try out the example +1. Navigate into `examples/astronauts_missions_example` directory. There's two federated graphql services and a supergraph schema. +2. In terminal execute `./gateway.sh -s supergraph.graphql` to start the gateway. +3. In terminal execute `bal run` inside both `astronauts_service` and `missions_service` directories to start the subgraph services. +3. Navigate into `astronuats_service` directory and execute `bal run` in the terminal to start the `astronauts_service` subgraph service. +4. Navigate into `missions_service` directory and execute `bal run` in the terminal to start the `missions_service` subgraph service. +5. Try out the following query in the graphql client. + +```graphql +query { + astronauts { + id + name + missions { + id + designation + } + } +} +``` + +## Build from the source. + +### Setup the prerequisites +1. Download and install Java SE Development Kit (JDK) version 17 (from one of the following locations). + + - [Oracle](https://www.oracle.com/java/technologies/downloads/#java17) + + - [OpenJDK](https://adoptopenjdk.net/) + + > **Note:** Set the JAVA_HOME environment variable to the path name of the directory into which you installed JDK. + +2. Export your Github personal access token with the read package permissions as follows. + + export packageUser= + export packagePAT= + +### Build the source + +Execute the commands below to build from the source. +> **Note:** When running the build with test in Windows use `-Pdisable=invalid-permission` to skip the Windows incompatible test cases. + +1. To build the project: +```bash +./gradlew clean build +``` + +2. To run the tests +```bash +./gradlew clean test +``` + +3. To build the project without tests: +```bash +./gradlew clean build -x test +``` + +4. Publish `jar` artifact to the local `.m2` repository: +```bash +./gradlew clean build publishToMavenLocal +``` diff --git a/ballerina/Ballerina.toml b/ballerina/Ballerina.toml index 3f0be25..2ef0aa5 100644 --- a/ballerina/Ballerina.toml +++ b/ballerina/Ballerina.toml @@ -7,40 +7,40 @@ export=["graphql_federation_gateway"] keywords = ["graphql", "federation", "gateway"] repository = "https://github.com/xlibb/graphql-federation-gateway" license = ["Apache-2.0"] -distribution = "2201.7.0" +distribution = "2201.8.6" -[[platform.java11.dependency]] +[[platform.java17.dependency]] groupId = "io.xlibb.gateway" artifactId = "gateway-native" version = "0.1.0" path = "../native/build/libs/gateway-native-0.1.0-SNAPSHOT.jar" -[[platform.java11.dependency]] +[[platform.java17.dependency]] groupId = "com.graphql-java" artifactId = "graphql-java" -version = "20.0" -path="./lib/graphql-java-20.0.jar" +version = "20.6" +path="./lib/graphql-java-20.6.jar" -[[platform.java11.dependency]] +[[platform.java17.dependency]] groupId = "com.graphql-java" artifactId = "graphql-java-extended-scalars" version = "20.0" path="./lib/graphql-java-extended-scalars-20.0.jar" -[[platform.java11.dependency]] +[[platform.java17.dependency]] groupId = "commons-io" artifactId = "commons-io" -version = "2.11.0" -path="./lib/commons-io-2.11.0.jar" +version = "2.12.0" +path="./lib/commons-io-2.12.0.jar" -[[platform.java11.dependency]] +[[platform.java17.dependency]] groupId = "org.ballerinalang" artifactId = "formatter-core" -version = "2201.7.0" -path="./lib/formatter-core-2201.7.0.jar" +version = "2201.8.6" +path="./lib/formatter-core-2201.8.6.jar" -[[platform.java11.dependency]] +[[platform.java17.dependency]] groupId = "org.ballerinalang" artifactId = "ballerina-parser" -version = "2201.7.0" -path="./lib/ballerina-parser-2201.7.0.jar" +version = "2201.8.6" +path="./lib/ballerina-parser-2201.8.6.jar" diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index d8fdd05..1958547 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -5,12 +5,12 @@ [ballerina] dependencies-toml-version = "2" -distribution-version = "2201.7.0" +distribution-version = "2201.8.6" [[package]] org = "ballerina" name = "file" -version = "1.8.1" +version = "1.9.0" dependencies = [ {org = "ballerina", name = "io"}, {org = "ballerina", name = "jballerina.java"}, @@ -24,7 +24,7 @@ modules = [ [[package]] org = "ballerina" name = "io" -version = "1.5.0" +version = "1.6.0" dependencies = [ {org = "ballerina", name = "jballerina.java"}, {org = "ballerina", name = "lang.value"} @@ -52,7 +52,7 @@ dependencies = [ [[package]] org = "ballerina" name = "os" -version = "1.7.0" +version = "1.8.0" dependencies = [ {org = "ballerina", name = "io"}, {org = "ballerina", name = "jballerina.java"} @@ -61,7 +61,7 @@ dependencies = [ [[package]] org = "ballerina" name = "time" -version = "2.3.0" +version = "2.4.0" dependencies = [ {org = "ballerina", name = "jballerina.java"} ] @@ -78,4 +78,4 @@ dependencies = [ modules = [ {org = "xlibb", packageName = "graphql_federation_gateway", moduleName = "graphql_federation_gateway"} ] - + diff --git a/ballerina/build.gradle b/ballerina/build.gradle index 43444fe..adff589 100644 --- a/ballerina/build.gradle +++ b/ballerina/build.gradle @@ -1,138 +1,139 @@ -/* - * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. - * - * Licensed 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 org.apache.tools.ant.taskdefs.condition.Os - -description = 'Ballerina - GraphQl Federation Gateway' - -def packageName = "graphql-federation-gateway" -def packageOrg = "Ishad-M-I-M" -def tomlVersion = stripBallerinaExtensionVersion("${project.version}") -def ballerinaTomlFilePlaceHolder = new File("${project.rootDir}/build-config/resources/Ballerina.toml") -def ballerinaTomlFile = new File("$project.projectDir/Ballerina.toml") -def distName = "ballerina-${ballerinaLangVersion}-swan-lake" -def distributionBinPath = "${project.rootDir}/build/${distName}/bin" - -def stripBallerinaExtensionVersion(String extVersion) { - if (extVersion.matches(project.ext.timestampedVersionRegex)) { - def splitVersion = extVersion.split('-') - if (splitVersion.length > 3) { - def strippedValues = splitVersion[0..-4] - return strippedValues.join('-') - } else { - return extVersion - } - } else { - return extVersion.replace("${project.ext.snapshotVersion}", "") - } -} - -task updateTomlFiles { - doLast { - ballerinaTomlFile.text = ballerinaTomlFilePlaceHolder.text - .replace("@project.version@", project.version) - .replace("@toml.version@", tomlVersion) - .replace("@graphql.java.version@", graphqlJavaVersion) - .replace("@graphql.java.extended.scalars.version@", graphqlJavaExtendedScalarVersion) - .replace("@commons.io.version@", commonsIoVersion) - .replace("@ballerina.lang.version@", ballerinaLangVersion) - } -} - -task commitTomlFiles { - doLast { - project.exec { - ignoreExitValue true - if (Os.isFamily(Os.FAMILY_WINDOWS)) { - commandLine 'cmd', '/c', "git commit -m \"[Automated] Update the native jar versions\" Ballerina.toml Dependencies.toml" - } else { - commandLine 'sh', '-c', "git commit -m '[Automated] Update the native jar versions' Ballerina.toml Dependencies.toml" - } - } - } -} - - -task ballerinaBuild { - dependsOn(updateTomlFiles) - finalizedBy(commitTomlFiles) - doLast { - exec { - workingDir project.projectDir - environment "JAVA_OPTS", "-DBALLERINA_DEV_COMPILE_BALLERINA_ORG=true" - if (Os.isFamily(Os.FAMILY_WINDOWS)) { - commandLine 'cmd', '/c', "${distributionBinPath}/bal.bat build && exit %%ERRORLEVEL%%" - } else { - commandLine 'sh', '-c', "${distributionBinPath}/bal build" - } - } - } -} - -publishing { - publications { - maven(MavenPublication) { - artifact source: "${projectDir}/target/bin/graphql_federation_gateway.jar", extension: 'jar' - } - } - repositories { - maven { - name = "GitHubPackages" - url = uri("https://maven.pkg.github.com/${packageOrg}/${packageName}") - credentials { - username = System.getenv("publishUser") - password = System.getenv("publishPAT") - } - } - } -} - -configurations { - externalJars -} - -dependencies { - externalJars "com.graphql-java:graphql-java:${graphqlJavaVersion}" - externalJars "com.graphql-java:graphql-java-extended-scalars:${graphqlJavaExtendedScalarVersion}" - externalJars "commons-io:commons-io:${commonsIoVersion}" - externalJars "org.ballerinalang:formatter-core:${ballerinaLangVersion}" - externalJars "org.ballerinalang:ballerina-lang:${ballerinaLangVersion}" - externalJars "org.ballerinalang:ballerina-tools-api:${ballerinaLangVersion}" - externalJars "org.ballerinalang:ballerina-parser:${ballerinaLangVersion}" -} - -task copyToLib(type: Copy) { - if (project.configurations.find { it.name == "externalJars" }) { - into "${projectDir}/lib" - from project.configurations.externalJars - } else { - println "No external jars found" - } -} - -task build { - dependsOn(ballerinaBuild) -} - -task clean { - delete("${projectDir}/target") -} - -ballerinaBuild.dependsOn ":gateway-native:build" -ballerinaBuild.dependsOn copyToLib -publishToMavenLocal.dependsOn build -publish.dependsOn build +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). + * + * Licensed 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 org.apache.tools.ant.taskdefs.condition.Os + +description = 'Ballerina - GraphQl Federation Gateway' + +def packageName = "graphql-federation-gateway" +def packageOrg = "Ishad-M-I-M" +def tomlVersion = stripBallerinaExtensionVersion("${project.version}") +def ballerinaTomlFilePlaceHolder = new File("${project.rootDir}/build-config/resources/Ballerina.toml") +def ballerinaTomlFile = new File("$project.projectDir/Ballerina.toml") +def distName = "ballerina-${ballerinaLangVersion}-swan-lake" +def distributionBinPath = "${project.rootDir}/build/${distName}/bin" + +def stripBallerinaExtensionVersion(String extVersion) { + if (extVersion.matches(project.ext.timestampedVersionRegex)) { + def splitVersion = extVersion.split('-') + if (splitVersion.length > 3) { + def strippedValues = splitVersion[0..-4] + return strippedValues.join('-') + } else { + return extVersion + } + } else { + return extVersion.replace("${project.ext.snapshotVersion}", "") + } +} + +task updateTomlFiles { + doLast { + ballerinaTomlFile.text = ballerinaTomlFilePlaceHolder.text + .replace("@project.version@", project.version) + .replace("@toml.version@", tomlVersion) + .replace("@graphql.java.version@", graphqlJavaVersion) + .replace("@graphql.java.extended.scalars.version@", graphqlJavaExtendedScalarVersion) + .replace("@commons.io.version@", commonsIoVersion) + .replace("@ballerina.lang.version@", ballerinaLangVersion) + } +} + +task commitTomlFiles { + doLast { + project.exec { + ignoreExitValue true + if (Os.isFamily(Os.FAMILY_WINDOWS)) { + commandLine 'cmd', '/c', "git commit -m \"[Automated] Update the native jar versions\" Ballerina.toml Dependencies.toml" + } else { + commandLine 'sh', '-c', "git commit -m '[Automated] Update the native jar versions' Ballerina.toml Dependencies.toml" + } + } + } +} + + +task ballerinaBuild { + dependsOn(updateTomlFiles) + finalizedBy(commitTomlFiles) + doLast { + exec { + workingDir project.projectDir + environment "JAVA_OPTS", "-DBALLERINA_DEV_COMPILE_BALLERINA_ORG=true" + def balHomePath = "${project(':gateway-native').ext.balHomePath}/bin" + if (Os.isFamily(Os.FAMILY_WINDOWS)) { + commandLine 'cmd', '/c', "\"${balHomePath}/bal.bat\" build && exit %%ERRORLEVEL%%" + } else { + commandLine 'sh', '-c', "${balHomePath}/bal build" + } + } + } +} + +publishing { + publications { + maven(MavenPublication) { + artifact source: "${projectDir}/target/bin/graphql_federation_gateway.jar", extension: 'jar' + } + } + repositories { + maven { + name = "GitHubPackages" + url = uri("https://maven.pkg.github.com/${packageOrg}/${packageName}") + credentials { + username = System.getenv("publishUser") + password = System.getenv("publishPAT") + } + } + } +} + +configurations { + externalJars +} + +dependencies { + externalJars "com.graphql-java:graphql-java:${graphqlJavaVersion}" + externalJars "com.graphql-java:graphql-java-extended-scalars:${graphqlJavaExtendedScalarVersion}" + externalJars "commons-io:commons-io:${commonsIoVersion}" + externalJars "org.ballerinalang:formatter-core:${ballerinaLangVersion}" + externalJars "org.ballerinalang:ballerina-lang:${ballerinaLangVersion}" + externalJars "org.ballerinalang:ballerina-tools-api:${ballerinaLangVersion}" + externalJars "org.ballerinalang:ballerina-parser:${ballerinaLangVersion}" +} + +task copyToLib(type: Copy) { + if (project.configurations.find { it.name == "externalJars" }) { + into "${projectDir}/lib" + from project.configurations.externalJars + } else { + println "No external jars found" + } +} + +task build { + dependsOn(ballerinaBuild) +} + +task clean { + delete("${projectDir}/target") +} + +ballerinaBuild.dependsOn ":gateway-native:build" +ballerinaBuild.dependsOn copyToLib +publishToMavenLocal.dependsOn build +publish.dependsOn build diff --git a/build-config/checkstyle/build.gradle b/build-config/checkstyle/build.gradle index c3725db..71d2123 100644 --- a/build-config/checkstyle/build.gradle +++ b/build-config/checkstyle/build.gradle @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,8 +23,8 @@ apply plugin: 'java' task downloadCheckstyleRuleFiles(type: Download) { src([ - 'https://raw.githubusercontent.com/wso2/code-quality-tools/v1.3/checkstyle/checkstyle.xml', - 'https://raw.githubusercontent.com/wso2/code-quality-tools/v1.3/checkstyle/suppressions.xml' + 'https://raw.githubusercontent.com/wso2/code-quality-tools/v1.4/checkstyle/jdk-17/checkstyle.xml', + 'https://raw.githubusercontent.com/wso2/code-quality-tools/v1.4/checkstyle/jdk-17/suppressions.xml' ]) overwrite false onlyIfNewer true diff --git a/build-config/resources/Ballerina.toml b/build-config/resources/Ballerina.toml index 688dbfc..15ce134 100644 --- a/build-config/resources/Ballerina.toml +++ b/build-config/resources/Ballerina.toml @@ -9,37 +9,37 @@ repository = "https://github.com/xlibb/graphql-federation-gateway" license = ["Apache-2.0"] distribution = "@ballerina.lang.version@" -[[platform.java11.dependency]] +[[platform.java17.dependency]] groupId = "io.xlibb.gateway" artifactId = "gateway-native" version = "@toml.version@" path = "../native/build/libs/gateway-native-@project.version@.jar" -[[platform.java11.dependency]] +[[platform.java17.dependency]] groupId = "com.graphql-java" artifactId = "graphql-java" version = "@graphql.java.version@" path="./lib/graphql-java-@graphql.java.version@.jar" -[[platform.java11.dependency]] +[[platform.java17.dependency]] groupId = "com.graphql-java" artifactId = "graphql-java-extended-scalars" version = "@graphql.java.extended.scalars.version@" path="./lib/graphql-java-extended-scalars-@graphql.java.extended.scalars.version@.jar" -[[platform.java11.dependency]] +[[platform.java17.dependency]] groupId = "commons-io" artifactId = "commons-io" version = "@commons.io.version@" path="./lib/commons-io-@commons.io.version@.jar" -[[platform.java11.dependency]] +[[platform.java17.dependency]] groupId = "org.ballerinalang" artifactId = "formatter-core" version = "@ballerina.lang.version@" path="./lib/formatter-core-@ballerina.lang.version@.jar" -[[platform.java11.dependency]] +[[platform.java17.dependency]] groupId = "org.ballerinalang" artifactId = "ballerina-parser" version = "@ballerina.lang.version@" diff --git a/build.gradle b/build.gradle index 0160e37..6061715 100644 --- a/build.gradle +++ b/build.gradle @@ -1,74 +1,74 @@ -/* - * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. - * - * Licensed 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. - * - */ - -plugins { - id 'com.github.spotbugs-base' - id 'com.github.johnrengelman.shadow' - id 'de.undercouch.download' - id 'net.researchgate.release' - id 'jacoco' -} - -allprojects { - group = project.group - version = project.version - - apply plugin: 'maven-publish' - - repositories { - mavenLocal() - maven { - url = 'https://mvnrepository.com/artifact/org.testng/testng' - } - - maven { - url = 'https://repo.maven.apache.org/maven2' - } - - maven { - url = 'https://maven.pkg.github.com/ballerina-platform/*' - credentials { - username System.getenv("packageUser") - password System.getenv("packagePAT") - } - } - } - - ext { - snapshotVersion= '-SNAPSHOT' - timestampedVersionRegex = '.*-\\d{8}-\\d{6}-\\w.*\$' - } -} - -def moduleVersion = project.version.replace("-SNAPSHOT", "") - - -task build { - dependsOn(':gateway-native:build') - dependsOn(':gateway-ballerina:build') -} - -release { - buildTasks = ['build'] - failOnSnapshotDependencies = true - versionPropertyFile = 'gradle.properties' - tagTemplate = 'v${version}' - git { - requireBranch = "release-${moduleVersion}" - pushToRemote = 'origin' - } -} +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). + * + * Licensed 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. + * + */ + +plugins { + id 'com.github.spotbugs-base' + id 'com.github.johnrengelman.shadow' + id 'de.undercouch.download' + id 'net.researchgate.release' + id 'jacoco' +} + +allprojects { + group = project.group + version = project.version + + apply plugin: 'maven-publish' + + repositories { + mavenLocal() + maven { + url = 'https://mvnrepository.com/artifact/org.testng/testng' + } + + maven { + url = 'https://repo.maven.apache.org/maven2' + } + + maven { + url = 'https://maven.pkg.github.com/ballerina-platform/*' + credentials { + username System.getenv("packageUser") + password System.getenv("packagePAT") + } + } + } + + ext { + snapshotVersion= '-SNAPSHOT' + timestampedVersionRegex = '.*-\\d{8}-\\d{6}-\\w.*\$' + } +} + +def moduleVersion = project.version.replace("-SNAPSHOT", "") + + +task build { + dependsOn(':gateway-native:build') + dependsOn(':gateway-ballerina:build') +} + +release { + buildTasks = ['build'] + failOnSnapshotDependencies = true + versionPropertyFile = 'gradle.properties' + tagTemplate = 'v${version}' + git { + requireBranch = "release-${moduleVersion}" + pushToRemote = 'origin' + } +} diff --git a/gradle.properties b/gradle.properties index c811c35..79545ad 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,29 +1,28 @@ -org.gradle.caching=true group=io.xlibb version=0.1.0-SNAPSHOT #dependency -ballerinaLangVersion=2201.7.0 +ballerinaLangVersion=2201.8.6 githubJohnrengelmanShadowVersion=5.2.0 underCouchDownloadVersion=4.0.4 researchgateReleaseVersion=2.8.0 slf4jVersion=1.7.30 commonsLang3Version=3.9 commonsLoggingVersion=1.2 -commonsIoVersion=2.11.0 +commonsIoVersion=2.12.0 commonsCompressVersion=1.21 -graphqlJavaVersion=20.0 +graphqlJavaVersion=20.6 graphqlJavaExtendedScalarVersion=20.0 org.gradle.jvmargs=-Xmx4096M -checkstylePluginVersion=8.18 -spotbugsPluginVersion=4.5.1 -shadowJarPluginVersion=5.2.0 -downloadPluginVersion=4.0.4 -releasePluginVersion=2.6.0 -testngVersion=7.4.0 +checkstylePluginVersion=10.12.0 +spotbugsPluginVersion=5.0.14 +shadowJarPluginVersion=8.1.1 +downloadPluginVersion=5.4.0 +releasePluginVersion=2.8.0 +testngVersion=7.6.1 eclipseLsp4jVersion=0.12.0 -ballerinaGradlePluginVersion=1.0.3 -jacocoVersion=0.8.8 +ballerinaGradlePluginVersion=2.0.1 +jacocoVersion=0.8.10 okhttpVersion=3.14.0 okioVersion=2.2.2 kotlinVersion=1.2.60 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 05679dc..be6b5cb 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Wed Jan 17 14:13:37 IST 2024 distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip -zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/native/build.gradle b/native/build.gradle index 0292d7c..9b54f31 100644 --- a/native/build.gradle +++ b/native/build.gradle @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -61,6 +61,7 @@ checkstyle { } checkstyleMain.dependsOn(":checkstyle:downloadCheckstyleRuleFiles") +checkstyleTest.dependsOn(":checkstyle:downloadCheckstyleRuleFiles") spotbugsMain { effort "max" @@ -142,6 +143,7 @@ task getBalHomePath { getBalHomePath.ext.balHomePath = "${System.getenv("BALLERINA_HOME")}/distributions/ballerina-${ballerinaLangVersion}" } + project.ext.balHomePath = getBalHomePath.ext.balHomePath } } diff --git a/native/src/main/java/io/xlibb/gateway/GatewayProject.java b/native/src/main/java/io/xlibb/gateway/GatewayProject.java index fc470bb..c75d6f4 100644 --- a/native/src/main/java/io/xlibb/gateway/GatewayProject.java +++ b/native/src/main/java/io/xlibb/gateway/GatewayProject.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except diff --git a/native/src/main/java/io/xlibb/gateway/exception/GatewayGenerationException.java b/native/src/main/java/io/xlibb/gateway/exception/GatewayGenerationException.java index 66ede75..db1d86f 100644 --- a/native/src/main/java/io/xlibb/gateway/exception/GatewayGenerationException.java +++ b/native/src/main/java/io/xlibb/gateway/exception/GatewayGenerationException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except diff --git a/native/src/main/java/io/xlibb/gateway/exception/ValidationException.java b/native/src/main/java/io/xlibb/gateway/exception/ValidationException.java index 1df3ba0..4036478 100644 --- a/native/src/main/java/io/xlibb/gateway/exception/ValidationException.java +++ b/native/src/main/java/io/xlibb/gateway/exception/ValidationException.java @@ -1,29 +1,29 @@ -/* - * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). All Rights Reserved. - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package io.xlibb.gateway.exception; - -/** - * Exception class to represent validation errors. - */ -public class ValidationException extends Exception { - - public ValidationException(String message) { - super(message); - } -} +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). + * + * 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.xlibb.gateway.exception; + +/** + * Exception class to represent validation errors. + */ +public class ValidationException extends Exception { + + public ValidationException(String message) { + super(message); + } +} diff --git a/native/src/main/java/io/xlibb/gateway/generator/CommonUtils.java b/native/src/main/java/io/xlibb/gateway/generator/CommonUtils.java index b72974e..e83eddd 100644 --- a/native/src/main/java/io/xlibb/gateway/generator/CommonUtils.java +++ b/native/src/main/java/io/xlibb/gateway/generator/CommonUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except @@ -72,6 +72,9 @@ public class CommonUtils { public static final String GRAHQL_TYPE_FLOAT = "Float"; public static final String GRAHQL_TYPE_BOOLEAN = "Boolean"; + public static final String GRAPHQL_DEPRECATED_DIRECTIVE = "deprecated"; + public static final String GRAPHQL_DEPRECATED_DIRECTIVE_DEFAULT_REASON = "No longer supported"; + public static final String BALLERINA_TYPE_INT = "int"; public static final String BALLERINA_TYPE_FLOAT = "float"; public static final String BALLERINA_TYPE_BOOLEAN = "boolean"; diff --git a/native/src/main/java/io/xlibb/gateway/generator/GatewayCodeGenerator.java b/native/src/main/java/io/xlibb/gateway/generator/GatewayCodeGenerator.java index 73f2b59..4c0f0d9 100644 --- a/native/src/main/java/io/xlibb/gateway/generator/GatewayCodeGenerator.java +++ b/native/src/main/java/io/xlibb/gateway/generator/GatewayCodeGenerator.java @@ -1,146 +1,146 @@ -/* - * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). All Rights Reserved. - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package io.xlibb.gateway.generator; - -import graphql.schema.GraphQLSchema; -import io.ballerina.runtime.api.utils.StringUtils; -import io.ballerina.runtime.api.values.BString; -import io.xlibb.gateway.GatewayProject; -import io.xlibb.gateway.exception.GatewayGenerationException; -import io.xlibb.gateway.exception.ValidationException; -import org.apache.commons.io.IOUtils; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.PrintWriter; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.NoSuchFileException; -import java.nio.file.Path; -import java.nio.file.Paths; - -/** - * Class to generated source code for the gateway. - */ -public class GatewayCodeGenerator { - public static final String SERVICE_FILE_NAME = "service.bal"; - public static final String QUERY_PLAN_FILE_NAME = "query_plan.bal"; - public static final String TYPES_FILE_NAME = "types.bal"; - public static final String GATEWAY_PROJECT_TEMPLATE_DIRECTORY = "gateway"; - private static final String[] GATEWAY_PROJECT_TEMPLATE_FILES = { - "Ballerina.toml", - "resolver.bal", - "utils.bal", - "records.bal", - "query_field_classifier.bal" - }; - - public static final String ERROR_INVALID_SUPERGRAPH_FILE_PATH = "Given supergraph file path is invalid"; - public static final String ERROR_INVALID_OUTPUT_PATH = "Given output path is invalid"; - public static final String ERROR_OUTPUT_PATH_NOT_WRITABLE = "Given out path is not writable"; - public static final String ERROR_INVALID_SCHEMA = "Error occurred while parsing the GraphQL schema"; - - public static BString generateGateway(BString supergraphPath, BString outPath, BString port) { - try { - Path path = Paths.get(supergraphPath.getValue()); - Path outputPath = Paths.get(outPath.getValue()); - File outputDest = new File(outputPath.toString()); - Path fileName = path.getFileName(); - if (fileName == null) { - return StringUtils.fromString(ERROR_INVALID_SUPERGRAPH_FILE_PATH); - } - if (!outputDest.exists()) { - return StringUtils.fromString(ERROR_INVALID_OUTPUT_PATH); - } - if (!outputDest.canWrite()) { - return StringUtils.fromString(ERROR_OUTPUT_PATH_NOT_WRITABLE); - } - GatewayProject project = new GatewayProject(fileName.toString().replace(".graphql", ""), - path.toString(), outputPath.toString(), Integer.parseInt(port.getValue())); - generateGatewayProject(project); - return StringUtils.fromString("success"); - } catch (NoSuchFileException e) { - return StringUtils.fromString(ERROR_INVALID_SUPERGRAPH_FILE_PATH); - } catch (GatewayGenerationException | IOException | ValidationException e) { - return StringUtils.fromString(e.getMessage()); - } - } - - public static void generateGatewayProject(GatewayProject project) throws GatewayGenerationException { - try { - copyTemplateFiles(project.getOutputPath()); - generateBalSources(project, project.getOutputPath()); - deletePartialFiles(project.getOutputPath()); - } catch (GatewayGenerationException | IOException | ValidationException e) { - throw new GatewayGenerationException(e.getMessage()); - } - } - - public static void copyTemplateFiles(Path targetPath) throws GatewayGenerationException, IOException { - ClassLoader classLoader = GatewayCodeGenerator.class.getClassLoader(); - for (String fileName : GATEWAY_PROJECT_TEMPLATE_FILES) { - InputStream inputStream = classLoader.getResourceAsStream( - GATEWAY_PROJECT_TEMPLATE_DIRECTORY + "/" + fileName); - - checkInputStream(inputStream); - String resource = IOUtils.toString(inputStream, StandardCharsets.UTF_8); - Path path = targetPath.resolve(fileName); - try (PrintWriter writer = new PrintWriter(path.toString(), StandardCharsets.UTF_8)) { - writer.print(resource); - } catch (IOException e) { - throw new GatewayGenerationException("Error while copying the template files."); - } - } - } - - private static void checkInputStream(InputStream inputStream) throws GatewayGenerationException { - if (inputStream == null) { - throw new GatewayGenerationException("Error while copying the template files."); - } - } - - private static void deletePartialFiles(Path directoryPath) { - try { - for (Path path : Files.walk(directoryPath) - .filter(path -> path.toString().endsWith(".partial")).toArray(Path[]::new)) { - Files.delete(path); - } - } catch (IOException ignore) { } - } - - private static void generateBalSources(GatewayProject project, Path outputPath) - throws GatewayGenerationException, IOException, ValidationException { - GraphQLSchema graphQLSchema = project.getSchema(); - - writeSourceToFile(new GatewayTypeGenerator(graphQLSchema).generateSrc(), TYPES_FILE_NAME, outputPath); - writeSourceToFile(new GatewayQueryPlanGenerator(graphQLSchema).generateSrc(), QUERY_PLAN_FILE_NAME, outputPath); - writeSourceToFile(new GatewayServiceGenerator(project).generateSrc(), SERVICE_FILE_NAME, outputPath); - } - - private static void writeSourceToFile(String content, String filename, Path targetPath) throws IOException { - Path path = targetPath.resolve(filename); - try (PrintWriter writer = new PrintWriter(path.toString(), StandardCharsets.UTF_8)) { - writer.print(content); - } catch (IOException e) { - throw new IOException("Error while writing the generated source to the file."); - } - } - -} +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). + * + * 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.xlibb.gateway.generator; + +import graphql.schema.GraphQLSchema; +import io.ballerina.runtime.api.utils.StringUtils; +import io.ballerina.runtime.api.values.BString; +import io.xlibb.gateway.GatewayProject; +import io.xlibb.gateway.exception.GatewayGenerationException; +import io.xlibb.gateway.exception.ValidationException; +import org.apache.commons.io.IOUtils; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.nio.file.Paths; + +/** + * Class to generated source code for the gateway. + */ +public class GatewayCodeGenerator { + public static final String SERVICE_FILE_NAME = "service.bal"; + public static final String QUERY_PLAN_FILE_NAME = "query_plan.bal"; + public static final String TYPES_FILE_NAME = "types.bal"; + public static final String GATEWAY_PROJECT_TEMPLATE_DIRECTORY = "gateway"; + private static final String[] GATEWAY_PROJECT_TEMPLATE_FILES = { + "Ballerina.toml", + "resolver.bal", + "utils.bal", + "records.bal", + "query_field_classifier.bal" + }; + + public static final String ERROR_INVALID_SUPERGRAPH_FILE_PATH = "Given supergraph file path is invalid"; + public static final String ERROR_INVALID_OUTPUT_PATH = "Given output path is invalid"; + public static final String ERROR_OUTPUT_PATH_NOT_WRITABLE = "Given out path is not writable"; + public static final String ERROR_INVALID_SCHEMA = "Error occurred while parsing the GraphQL schema"; + + public static BString generateGateway(BString supergraphPath, BString outPath, BString port) { + try { + Path path = Paths.get(supergraphPath.getValue()); + Path outputPath = Paths.get(outPath.getValue()); + File outputDest = new File(outputPath.toString()); + Path fileName = path.getFileName(); + if (fileName == null) { + return StringUtils.fromString(ERROR_INVALID_SUPERGRAPH_FILE_PATH); + } + if (!outputDest.exists()) { + return StringUtils.fromString(ERROR_INVALID_OUTPUT_PATH); + } + if (!outputDest.canWrite()) { + return StringUtils.fromString(ERROR_OUTPUT_PATH_NOT_WRITABLE); + } + GatewayProject project = new GatewayProject(fileName.toString().replace(".graphql", ""), + path.toString(), outputPath.toString(), Integer.parseInt(port.getValue())); + generateGatewayProject(project); + return StringUtils.fromString("success"); + } catch (NoSuchFileException e) { + return StringUtils.fromString(ERROR_INVALID_SUPERGRAPH_FILE_PATH); + } catch (GatewayGenerationException | IOException | ValidationException e) { + return StringUtils.fromString(e.getMessage()); + } + } + + public static void generateGatewayProject(GatewayProject project) throws GatewayGenerationException { + try { + copyTemplateFiles(project.getOutputPath()); + generateBalSources(project, project.getOutputPath()); + deletePartialFiles(project.getOutputPath()); + } catch (GatewayGenerationException | IOException | ValidationException e) { + throw new GatewayGenerationException(e.getMessage()); + } + } + + public static void copyTemplateFiles(Path targetPath) throws GatewayGenerationException, IOException { + ClassLoader classLoader = GatewayCodeGenerator.class.getClassLoader(); + for (String fileName : GATEWAY_PROJECT_TEMPLATE_FILES) { + InputStream inputStream = classLoader.getResourceAsStream( + GATEWAY_PROJECT_TEMPLATE_DIRECTORY + "/" + fileName); + + checkInputStream(inputStream); + String resource = IOUtils.toString(inputStream, StandardCharsets.UTF_8); + Path path = targetPath.resolve(fileName); + try (PrintWriter writer = new PrintWriter(path.toString(), StandardCharsets.UTF_8)) { + writer.print(resource); + } catch (IOException e) { + throw new GatewayGenerationException("Error while copying the template files."); + } + } + } + + private static void checkInputStream(InputStream inputStream) throws GatewayGenerationException { + if (inputStream == null) { + throw new GatewayGenerationException("Error while copying the template files."); + } + } + + private static void deletePartialFiles(Path directoryPath) { + try { + for (Path path : Files.walk(directoryPath) + .filter(path -> path.toString().endsWith(".partial")).toArray(Path[]::new)) { + Files.delete(path); + } + } catch (IOException ignore) { } + } + + private static void generateBalSources(GatewayProject project, Path outputPath) + throws GatewayGenerationException, IOException, ValidationException { + GraphQLSchema graphQLSchema = project.getSchema(); + + writeSourceToFile(new GatewayTypeGenerator(graphQLSchema).generateSrc(), TYPES_FILE_NAME, outputPath); + writeSourceToFile(new GatewayQueryPlanGenerator(graphQLSchema).generateSrc(), QUERY_PLAN_FILE_NAME, outputPath); + writeSourceToFile(new GatewayServiceGenerator(project).generateSrc(), SERVICE_FILE_NAME, outputPath); + } + + private static void writeSourceToFile(String content, String filename, Path targetPath) throws IOException { + Path path = targetPath.resolve(filename); + try (PrintWriter writer = new PrintWriter(path.toString(), StandardCharsets.UTF_8)) { + writer.print(content); + } catch (IOException e) { + throw new IOException("Error while writing the generated source to the file."); + } + } + +} diff --git a/native/src/main/java/io/xlibb/gateway/generator/GatewayQueryPlanGenerator.java b/native/src/main/java/io/xlibb/gateway/generator/GatewayQueryPlanGenerator.java index 62c5e43..ed859e4 100644 --- a/native/src/main/java/io/xlibb/gateway/generator/GatewayQueryPlanGenerator.java +++ b/native/src/main/java/io/xlibb/gateway/generator/GatewayQueryPlanGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except @@ -49,7 +49,6 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Objects; import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createEmptyMinutiaeList; import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createEmptyNodeList; @@ -331,20 +330,22 @@ private String getGraphOfJoinTypeArgument(GraphQLAppliedDirective directive) private String getKeyOfJoinTypeArgument(String name, GraphQLAppliedDirective directive) throws GatewayGenerationException { - try { - for (GraphQLAppliedDirectiveArgument argument : directive.getArguments()) { + for (GraphQLAppliedDirectiveArgument argument : directive.getArguments()) { if (argument.getName().equals(ARGUMENT_KEY)) { - return ((StringValue) Objects.requireNonNull(argument.getArgumentValue().getValue())).getValue(); + Object argumentValue = argument.getArgumentValue().getValue(); + if (argumentValue == null) { + break; + } + return ((StringValue) argumentValue).getValue(); } - } - } catch (NullPointerException e) { - for (FieldData field : - schemaTypes.getFieldsOfType(name)) { + } + + for (FieldData field : schemaTypes.getFieldsOfType(name)) { if (field.isID()) { - return field.getFieldName(); + return field.getFieldName(); } - } } + throw new GatewayGenerationException("No key argument found in @join__type directive"); } diff --git a/native/src/main/java/io/xlibb/gateway/generator/GatewayServiceGenerator.java b/native/src/main/java/io/xlibb/gateway/generator/GatewayServiceGenerator.java index 46c25d8..f39d3db 100644 --- a/native/src/main/java/io/xlibb/gateway/generator/GatewayServiceGenerator.java +++ b/native/src/main/java/io/xlibb/gateway/generator/GatewayServiceGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except @@ -20,7 +20,9 @@ import graphql.language.EnumValue; import graphql.language.InputValueDefinition; +import graphql.language.StringValue; import graphql.schema.GraphQLAppliedDirective; +import graphql.schema.GraphQLAppliedDirectiveArgument; import graphql.schema.GraphQLArgument; import graphql.schema.GraphQLFieldDefinition; import graphql.schema.GraphQLSchemaElement; @@ -58,6 +60,8 @@ import static io.xlibb.gateway.generator.CommonUtils.CLIENT_NAME_VALUE_PLACEHOLDER; import static io.xlibb.gateway.generator.CommonUtils.DIRECTIVE_JOIN_FIELD; import static io.xlibb.gateway.generator.CommonUtils.DIRECTIVE_JOIN_TYPE; +import static io.xlibb.gateway.generator.CommonUtils.GRAPHQL_DEPRECATED_DIRECTIVE; +import static io.xlibb.gateway.generator.CommonUtils.GRAPHQL_DEPRECATED_DIRECTIVE_DEFAULT_REASON; import static io.xlibb.gateway.generator.CommonUtils.TYPE_MUTATION; import static io.xlibb.gateway.generator.CommonUtils.TYPE_QUERY; import static io.xlibb.gateway.generator.CommonUtils.getJoinGraphs; @@ -72,6 +76,7 @@ enum FunctionType { * Class to generate service code for the gateway. */ public class GatewayServiceGenerator { + public static final String DEPRECATED_PLACEHOLDER = "@\\{deprecatedDirective}"; public static final String QUERY_PLACEHOLDER = "@\\{query}"; public static final String FUNCTION_PARAM_PLACEHOLDER = "@\\{params}"; public static final String RESPONSE_TYPE_PLACEHOLDER = "@\\{responseType}"; @@ -111,7 +116,7 @@ public GatewayServiceGenerator(GatewayProject project) throws ValidationExceptio public String generateSrc() throws GatewayGenerationException { try { SyntaxTree syntaxTree = generateSyntaxTree(); - return Formatter.format(syntaxTree).toString(); + return Formatter.format(syntaxTree).toSourceCode(); } catch (IOException | FormatterException e) { throw new GatewayGenerationException("Error while generating the gateway services"); } @@ -205,19 +210,17 @@ private String getServiceFunction(FunctionType functionType, GraphQLSchemaElemen "mergeToResultJson(result, >response.data.@{query}.toJson());"); } - return template.replaceAll(QUERY_PLACEHOLDER, - ((GraphQLFieldDefinition) graphQLSchemaElement).getName()) - .replaceAll(FUNCTION_PARAM_PLACEHOLDER, - getArgumentString(graphQLSchemaElement)) + GraphQLFieldDefinition graphQLFieldDefinition = (GraphQLFieldDefinition) graphQLSchemaElement; + return template.replaceAll(QUERY_PLACEHOLDER, graphQLFieldDefinition.getName()) + .replaceAll(FUNCTION_PARAM_PLACEHOLDER, getArgumentString(graphQLSchemaElement)) .replaceAll(RESPONSE_TYPE_PLACEHOLDER, - CommonUtils.getTypeFromGraphQLType( - ((GraphQLFieldDefinition) graphQLSchemaElement).getType())) + CommonUtils.getTypeFromGraphQLType(graphQLFieldDefinition.getType())) .replaceAll(BASIC_RESPONSE_TYPE_PLACEHOLDER, - CommonUtils.getBasicTypeNameFromGraphQLType( - ((GraphQLFieldDefinition) graphQLSchemaElement).getType())) + CommonUtils.getBasicTypeNameFromGraphQLType(graphQLFieldDefinition.getType())) .replaceAll(CLIENT_NAME_PLACEHOLDER, - getClientNameFromFieldDefinition((GraphQLFieldDefinition) graphQLSchemaElement, type)) - .replaceAll(QUERY_ARGS_PLACEHOLDER, getQueryArguments(graphQLSchemaElement)); + getClientNameFromFieldDefinition(graphQLFieldDefinition, type)) + .replaceAll(QUERY_ARGS_PLACEHOLDER, getQueryArguments(graphQLSchemaElement)) + .replaceAll(DEPRECATED_PLACEHOLDER, getDeprecationStatus(graphQLFieldDefinition)); } private ModuleMemberDeclarationNode getGetClientFunction() @@ -254,11 +257,27 @@ private List getClientDeclarations() { } return nodes; } + + private String getDeprecationStatus(GraphQLFieldDefinition fieldDefinition) { + if (!fieldDefinition.getAllAppliedDirectivesByName().containsKey(GRAPHQL_DEPRECATED_DIRECTIVE)) { + return ""; + } + GraphQLAppliedDirective deprecatedDirective = fieldDefinition.getAppliedDirectives(GRAPHQL_DEPRECATED_DIRECTIVE) + .get(0); + Object reasonArgumentValue = deprecatedDirective.getArgument("reason").getArgumentValue().getValue(); + String reason = reasonArgumentValue == null ? GRAPHQL_DEPRECATED_DIRECTIVE_DEFAULT_REASON : + ((StringValue) reasonArgumentValue).getValue(); + return String.format("# # Deprecated%n# %s%n@%s%n", reason, GRAPHQL_DEPRECATED_DIRECTIVE); + } private String getClientNameFromFieldDefinition(GraphQLFieldDefinition graphQLFieldDefinition, String parentType) throws GatewayGenerationException { for (GraphQLAppliedDirective directive : graphQLFieldDefinition.getAppliedDirectives()) { - Object value = directive.getArgument(ARGUMENT_GRAPH).getArgumentValue().getValue(); + GraphQLAppliedDirectiveArgument appliedDirectiveArgument = directive.getArgument(ARGUMENT_GRAPH); + if (appliedDirectiveArgument == null) { + continue; + } + Object value = appliedDirectiveArgument.getArgumentValue().getValue(); if (directive.getName().equals(DIRECTIVE_JOIN_FIELD) && value instanceof EnumValue) { return ((EnumValue) value).getName(); } @@ -267,7 +286,11 @@ private String getClientNameFromFieldDefinition(GraphQLFieldDefinition graphQLFi List appliedDirectivesOnParent = SpecReader.getObjectTypeDirectives(project.getSchema(), parentType); for (GraphQLAppliedDirective directive : appliedDirectivesOnParent) { - Object value = directive.getArgument(ARGUMENT_GRAPH).getArgumentValue().getValue(); + GraphQLAppliedDirectiveArgument appliedDirectiveArgument = directive.getArgument(ARGUMENT_GRAPH); + if (appliedDirectiveArgument == null) { + continue; + } + Object value = appliedDirectiveArgument.getArgumentValue().getValue(); if (directive.getName().equals(DIRECTIVE_JOIN_TYPE) && value instanceof EnumValue) { return ((EnumValue) value).getName(); } diff --git a/native/src/main/java/io/xlibb/gateway/generator/GatewayTypeGenerator.java b/native/src/main/java/io/xlibb/gateway/generator/GatewayTypeGenerator.java index 0986348..883c7eb 100644 --- a/native/src/main/java/io/xlibb/gateway/generator/GatewayTypeGenerator.java +++ b/native/src/main/java/io/xlibb/gateway/generator/GatewayTypeGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except diff --git a/native/src/main/java/io/xlibb/gateway/graphql/SpecReader.java b/native/src/main/java/io/xlibb/gateway/graphql/SpecReader.java index 9509cdf..094cb4f 100644 --- a/native/src/main/java/io/xlibb/gateway/graphql/SpecReader.java +++ b/native/src/main/java/io/xlibb/gateway/graphql/SpecReader.java @@ -1,230 +1,230 @@ -package io.xlibb.gateway.graphql; -/* - * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). All Rights Reserved. - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import graphql.language.FieldDefinition; -import graphql.language.InputValueDefinition; -import graphql.language.ListType; -import graphql.language.NonNullType; -import graphql.language.Type; -import graphql.language.TypeName; -import graphql.schema.GraphQLAppliedDirective; -import graphql.schema.GraphQLEnumType; -import graphql.schema.GraphQLFieldDefinition; -import graphql.schema.GraphQLInputObjectField; -import graphql.schema.GraphQLInputObjectType; -import graphql.schema.GraphQLNamedType; -import graphql.schema.GraphQLObjectType; -import graphql.schema.GraphQLScalarType; -import graphql.schema.GraphQLSchema; -import io.xlibb.gateway.exception.ValidationException; -import io.xlibb.gateway.graphql.components.FieldType; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static io.xlibb.gateway.graphql.Utils.getBallerinaTypeName; - -/** - * Class includes the methods to read the GraphQL schema (SDL). - */ -public class SpecReader { - /** - * Get the object type names from the GraphQL schema. - * - * @param graphQLSchema the instance of the Graphql schema file - * @return the list of the object type names - */ - public static List getObjectTypeNames(GraphQLSchema graphQLSchema) { - List objectTypeNames = new ArrayList<>(); - for (GraphQLNamedType graphQLNamedType : graphQLSchema.getAllTypesAsList()) { - if (graphQLNamedType instanceof GraphQLObjectType && !graphQLNamedType.getName().startsWith("__")) { - objectTypeNames.add(graphQLNamedType.getName()); - } - } - return objectTypeNames; - } - - /** - * Get the directives applied on the object type name from the GraphQL schema. - * - * @param graphQLSchema the instance of the Graphql schema file - * @param objectTypeName the object type name - * @return the object type directives - */ - public static List getObjectTypeDirectives(GraphQLSchema graphQLSchema, - String objectTypeName) { - List objectTypeDirectives = new ArrayList<>(); - if (graphQLSchema.getType(objectTypeName) instanceof GraphQLObjectType) { - GraphQLObjectType objectType = - ((GraphQLObjectType) graphQLSchema.getType(objectTypeName)); - if (objectType != null) { - objectTypeDirectives = objectType.getAppliedDirectives(); - } - } - return objectTypeDirectives; - } - - /** - * Gets the representation of Ballerina field type for a given GraphQL field type. - * - * @param graphQLSchema the object instance of the GraphQL schema (SDL) - * @param type the field type - * @return the string representation of Ballerina type for a given GraphQL field type - */ - public static FieldType getFieldType(GraphQLSchema graphQLSchema, Type type) { - FieldType fieldType = new FieldType(); - if (type instanceof TypeName) { - fieldType.setName(getBallerinaTypeName(graphQLSchema, ((TypeName) type).getName())); - fieldType.setTokens("?"); - } - if (type instanceof NonNullType) { - if (((NonNullType) type).getType() instanceof TypeName) { - fieldType.setName(getBallerinaTypeName(graphQLSchema, - ((TypeName) ((NonNullType) type).getType()).getName())); - fieldType.setTokens(""); - } - if (((NonNullType) type).getType() instanceof ListType) { - if (((ListType) ((NonNullType) type).getType()).getType() instanceof TypeName) { - fieldType.setName(getBallerinaTypeName(graphQLSchema, - ((TypeName) ((ListType) ((NonNullType) type).getType()).getType()).getName())); - fieldType.setTokens("?[]"); - } - if (((ListType) ((NonNullType) type).getType()).getType() instanceof NonNullType) { - if (((NonNullType) ((ListType) ((NonNullType) type).getType()).getType()) - .getType() instanceof TypeName) { - fieldType.setName(getBallerinaTypeName(graphQLSchema, - ((TypeName) ((NonNullType) ((ListType) ((NonNullType) type).getType()).getType()) - .getType()).getName())); - fieldType.setTokens("[]"); - } - } - } - } - if (type instanceof ListType) { - if (((ListType) type).getType() instanceof TypeName) { - fieldType.setName(getBallerinaTypeName(graphQLSchema, - ((TypeName) ((ListType) type).getType()).getName())); - fieldType.setTokens("?[]?"); - } - if (((ListType) type).getType() instanceof NonNullType) { - if (((NonNullType) ((ListType) type).getType()).getType() instanceof TypeName) { - fieldType.setName(getBallerinaTypeName(graphQLSchema, - ((TypeName) ((NonNullType) ((ListType) type).getType()).getType()).getName())); - fieldType.setTokens("[]?"); - } - } - } - return fieldType; - } - - /** - * Get the custom scalar type names from the GraphQL schema. - * - * @param graphQLSchema the instance of the Graphql schema file - * @return the list of the custom scalar type names - */ - public static List getCustomScalarTypeNames(GraphQLSchema graphQLSchema) { - List scalarTypeNames = new ArrayList<>(); - for (GraphQLNamedType graphQLNamedType : graphQLSchema.getAllTypesAsList()) { - if (graphQLNamedType instanceof GraphQLScalarType && !graphQLNamedType.getName().startsWith("__") - && !Utils.isPrimitiveScalarType(graphQLNamedType.getName())) { - scalarTypeNames.add(graphQLNamedType.getName()); - } - } - return scalarTypeNames; - } - - public static Map getObjectTypeFieldDefinitionMap(GraphQLSchema graphQLSchema, - String objectTypeName) { - Map objectTypeFieldsMap = new HashMap<>(); - if (graphQLSchema.getType(objectTypeName) instanceof GraphQLObjectType) { - GraphQLObjectType objectType = - ((GraphQLObjectType) graphQLSchema.getType(objectTypeName)); - if (objectType != null) { - for (GraphQLFieldDefinition field : objectType.getFields()) { - objectTypeFieldsMap.put(Utils.escapeIdentifier(field.getName()), - field.getDefinition()); - } - } - } - return objectTypeFieldsMap; - } - - /** - * Get the enum type names from the GraphQL schema. - * - * @param graphQLSchema the instance of the Graphql schema file - * @return the list of the enum type names - */ - public static List getEnumTypeNames(GraphQLSchema graphQLSchema) { - List enumTypeNames = new ArrayList<>(); - for (GraphQLNamedType graphQLNamedType : graphQLSchema.getAllTypesAsList()) { - if (graphQLNamedType instanceof GraphQLEnumType && !graphQLNamedType.getName().startsWith("__")) { - enumTypeNames.add(graphQLNamedType.getName()); - } - } - return enumTypeNames; - } - - /** - * Get the input object type names from the GraphQL schema. - * - * @param graphQLSchema the instance of the Graphql schema file - * @return the list of the input object type names - */ - public static List getInputObjectTypeNames(GraphQLSchema graphQLSchema) { - List inputObjectTypeNames = new ArrayList<>(); - for (GraphQLNamedType graphQLNamedType : graphQLSchema.getAllTypesAsList()) { - if (graphQLNamedType instanceof GraphQLInputObjectType) { - inputObjectTypeNames.add(graphQLNamedType.getName()); - } - } - return inputObjectTypeNames; - } - - /** - * Get the input object type fields map based on the input object type name from the GraphQL schema. - * - * @param graphQLSchema the instance of the Graphql schema file - * @param inputObjectTypeName the input object type name - * @return the input object type fields map - */ - public static Map getInputTypeFieldsMap(GraphQLSchema graphQLSchema, - String inputObjectTypeName) throws ValidationException { - Map inputTypeFieldsMap = new HashMap<>(); - if (graphQLSchema.getType(inputObjectTypeName) instanceof GraphQLInputObjectType) { - GraphQLInputObjectType inputObjectType = - ((GraphQLInputObjectType) graphQLSchema.getType(inputObjectTypeName)); - if (inputObjectType != null) { - for (GraphQLInputObjectField field : inputObjectType.getFields()) { - InputValueDefinition inputValueDefinition = field.getDefinition(); - if (inputValueDefinition == null) { - throw new ValidationException("Field definition cannot be null"); - } - inputTypeFieldsMap.put(Utils.escapeIdentifier(field.getName()), - SpecReader.getFieldType(graphQLSchema, inputValueDefinition.getType())); - - } - } - } - return inputTypeFieldsMap; - } -} +package io.xlibb.gateway.graphql; +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). + * + * 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 graphql.language.FieldDefinition; +import graphql.language.InputValueDefinition; +import graphql.language.ListType; +import graphql.language.NonNullType; +import graphql.language.Type; +import graphql.language.TypeName; +import graphql.schema.GraphQLAppliedDirective; +import graphql.schema.GraphQLEnumType; +import graphql.schema.GraphQLFieldDefinition; +import graphql.schema.GraphQLInputObjectField; +import graphql.schema.GraphQLInputObjectType; +import graphql.schema.GraphQLNamedType; +import graphql.schema.GraphQLObjectType; +import graphql.schema.GraphQLScalarType; +import graphql.schema.GraphQLSchema; +import io.xlibb.gateway.exception.ValidationException; +import io.xlibb.gateway.graphql.components.FieldType; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static io.xlibb.gateway.graphql.Utils.getBallerinaTypeName; + +/** + * Class includes the methods to read the GraphQL schema (SDL). + */ +public class SpecReader { + /** + * Get the object type names from the GraphQL schema. + * + * @param graphQLSchema the instance of the Graphql schema file + * @return the list of the object type names + */ + public static List getObjectTypeNames(GraphQLSchema graphQLSchema) { + List objectTypeNames = new ArrayList<>(); + for (GraphQLNamedType graphQLNamedType : graphQLSchema.getAllTypesAsList()) { + if (graphQLNamedType instanceof GraphQLObjectType && !graphQLNamedType.getName().startsWith("__")) { + objectTypeNames.add(graphQLNamedType.getName()); + } + } + return objectTypeNames; + } + + /** + * Get the directives applied on the object type name from the GraphQL schema. + * + * @param graphQLSchema the instance of the Graphql schema file + * @param objectTypeName the object type name + * @return the object type directives + */ + public static List getObjectTypeDirectives(GraphQLSchema graphQLSchema, + String objectTypeName) { + List objectTypeDirectives = new ArrayList<>(); + if (graphQLSchema.getType(objectTypeName) instanceof GraphQLObjectType) { + GraphQLObjectType objectType = + ((GraphQLObjectType) graphQLSchema.getType(objectTypeName)); + if (objectType != null) { + objectTypeDirectives = objectType.getAppliedDirectives(); + } + } + return objectTypeDirectives; + } + + /** + * Gets the representation of Ballerina field type for a given GraphQL field type. + * + * @param graphQLSchema the object instance of the GraphQL schema (SDL) + * @param type the field type + * @return the string representation of Ballerina type for a given GraphQL field type + */ + public static FieldType getFieldType(GraphQLSchema graphQLSchema, Type type) { + FieldType fieldType = new FieldType(); + if (type instanceof TypeName) { + fieldType.setName(getBallerinaTypeName(graphQLSchema, ((TypeName) type).getName())); + fieldType.setTokens("?"); + } + if (type instanceof NonNullType) { + if (((NonNullType) type).getType() instanceof TypeName) { + fieldType.setName(getBallerinaTypeName(graphQLSchema, + ((TypeName) ((NonNullType) type).getType()).getName())); + fieldType.setTokens(""); + } + if (((NonNullType) type).getType() instanceof ListType) { + if (((ListType) ((NonNullType) type).getType()).getType() instanceof TypeName) { + fieldType.setName(getBallerinaTypeName(graphQLSchema, + ((TypeName) ((ListType) ((NonNullType) type).getType()).getType()).getName())); + fieldType.setTokens("?[]"); + } + if (((ListType) ((NonNullType) type).getType()).getType() instanceof NonNullType) { + if (((NonNullType) ((ListType) ((NonNullType) type).getType()).getType()) + .getType() instanceof TypeName) { + fieldType.setName(getBallerinaTypeName(graphQLSchema, + ((TypeName) ((NonNullType) ((ListType) ((NonNullType) type).getType()).getType()) + .getType()).getName())); + fieldType.setTokens("[]"); + } + } + } + } + if (type instanceof ListType) { + if (((ListType) type).getType() instanceof TypeName) { + fieldType.setName(getBallerinaTypeName(graphQLSchema, + ((TypeName) ((ListType) type).getType()).getName())); + fieldType.setTokens("?[]?"); + } + if (((ListType) type).getType() instanceof NonNullType) { + if (((NonNullType) ((ListType) type).getType()).getType() instanceof TypeName) { + fieldType.setName(getBallerinaTypeName(graphQLSchema, + ((TypeName) ((NonNullType) ((ListType) type).getType()).getType()).getName())); + fieldType.setTokens("[]?"); + } + } + } + return fieldType; + } + + /** + * Get the custom scalar type names from the GraphQL schema. + * + * @param graphQLSchema the instance of the Graphql schema file + * @return the list of the custom scalar type names + */ + public static List getCustomScalarTypeNames(GraphQLSchema graphQLSchema) { + List scalarTypeNames = new ArrayList<>(); + for (GraphQLNamedType graphQLNamedType : graphQLSchema.getAllTypesAsList()) { + if (graphQLNamedType instanceof GraphQLScalarType && !graphQLNamedType.getName().startsWith("__") + && !Utils.isPrimitiveScalarType(graphQLNamedType.getName())) { + scalarTypeNames.add(graphQLNamedType.getName()); + } + } + return scalarTypeNames; + } + + public static Map getObjectTypeFieldDefinitionMap(GraphQLSchema graphQLSchema, + String objectTypeName) { + Map objectTypeFieldsMap = new HashMap<>(); + if (graphQLSchema.getType(objectTypeName) instanceof GraphQLObjectType) { + GraphQLObjectType objectType = + ((GraphQLObjectType) graphQLSchema.getType(objectTypeName)); + if (objectType != null) { + for (GraphQLFieldDefinition field : objectType.getFields()) { + objectTypeFieldsMap.put(Utils.escapeIdentifier(field.getName()), + field.getDefinition()); + } + } + } + return objectTypeFieldsMap; + } + + /** + * Get the enum type names from the GraphQL schema. + * + * @param graphQLSchema the instance of the Graphql schema file + * @return the list of the enum type names + */ + public static List getEnumTypeNames(GraphQLSchema graphQLSchema) { + List enumTypeNames = new ArrayList<>(); + for (GraphQLNamedType graphQLNamedType : graphQLSchema.getAllTypesAsList()) { + if (graphQLNamedType instanceof GraphQLEnumType && !graphQLNamedType.getName().startsWith("__")) { + enumTypeNames.add(graphQLNamedType.getName()); + } + } + return enumTypeNames; + } + + /** + * Get the input object type names from the GraphQL schema. + * + * @param graphQLSchema the instance of the Graphql schema file + * @return the list of the input object type names + */ + public static List getInputObjectTypeNames(GraphQLSchema graphQLSchema) { + List inputObjectTypeNames = new ArrayList<>(); + for (GraphQLNamedType graphQLNamedType : graphQLSchema.getAllTypesAsList()) { + if (graphQLNamedType instanceof GraphQLInputObjectType) { + inputObjectTypeNames.add(graphQLNamedType.getName()); + } + } + return inputObjectTypeNames; + } + + /** + * Get the input object type fields map based on the input object type name from the GraphQL schema. + * + * @param graphQLSchema the instance of the Graphql schema file + * @param inputObjectTypeName the input object type name + * @return the input object type fields map + */ + public static Map getInputTypeFieldsMap(GraphQLSchema graphQLSchema, + String inputObjectTypeName) throws ValidationException { + Map inputTypeFieldsMap = new HashMap<>(); + if (graphQLSchema.getType(inputObjectTypeName) instanceof GraphQLInputObjectType) { + GraphQLInputObjectType inputObjectType = + ((GraphQLInputObjectType) graphQLSchema.getType(inputObjectTypeName)); + if (inputObjectType != null) { + for (GraphQLInputObjectField field : inputObjectType.getFields()) { + InputValueDefinition inputValueDefinition = field.getDefinition(); + if (inputValueDefinition == null) { + throw new ValidationException("Field definition cannot be null"); + } + inputTypeFieldsMap.put(Utils.escapeIdentifier(field.getName()), + SpecReader.getFieldType(graphQLSchema, inputValueDefinition.getType())); + + } + } + } + return inputTypeFieldsMap; + } +} diff --git a/native/src/main/java/io/xlibb/gateway/graphql/Utils.java b/native/src/main/java/io/xlibb/gateway/graphql/Utils.java index 7791d13..c346d34 100644 --- a/native/src/main/java/io/xlibb/gateway/graphql/Utils.java +++ b/native/src/main/java/io/xlibb/gateway/graphql/Utils.java @@ -1,210 +1,210 @@ -/* - * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). All Rights Reserved. - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package io.xlibb.gateway.graphql; - -import graphql.Scalars; -import graphql.scalars.ExtendedScalars; -import graphql.schema.GraphQLScalarType; -import graphql.schema.GraphQLSchema; -import graphql.schema.idl.RuntimeWiring; -import graphql.schema.idl.SchemaGenerator; -import graphql.schema.idl.SchemaParser; -import graphql.schema.idl.TypeDefinitionRegistry; -import graphql.schema.idl.errors.SchemaProblem; -import io.xlibb.gateway.exception.ValidationException; -import io.xlibb.gateway.generator.GatewayCodeGenerator; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -/** - * Class to hold the utility methods. - * */ -public class Utils { - - private static final String[] KEYWORDS = new String[] {"abort", "aborted", "abstract", "all", "annotation", - "any", "anydata", "boolean", "break", "byte", "catch", "channel", "check", "checkpanic", "client", - "committed", "const", "continue", "decimal", "else", "error", "external", "fail", "final", "finally", - "float", "flush", "fork", "function", "future", "handle", "if", "import", "in", "int", "is", "join", - "json", "listener", "lock", "match", "new", "object", "OBJECT_INIT", "onretry", "parameter", "panic", - "private", "public", "record", "remote", "resource", "retries", "retry", "return", "returns", "service", - "source", "start", "stream", "string", "table", "transaction", "try", "type", "typedesc", "typeof", - "trap", "throw", "wait", "while", "with", "worker", "var", "version", "xml", "xmlns", "BOOLEAN_LITERAL", - "NULL_LITERAL", "ascending", "descending", "foreach", "map", "group", "from", "default", "field", - "limit", "as", "on", "isolated", "readonly", "distinct", "where", "select", "do", "transactional" - , "commit", "enum", "base16", "base64", "rollback", "configurable", "class", "module", "never", - "outer", "order", "null", "key", "let", "by", "equals"}; - - private static final String[] TYPES = new String[] {"int", "any", "anydata", "boolean", "byte", "float", "int", - "json", "string", "table", "var", "xml"}; - - public static final List BAL_KEYWORDS = Collections.unmodifiableList(Arrays.asList(KEYWORDS)); - public static final List BAL_TYPES = Collections.unmodifiableList(Arrays.asList(TYPES)); - public static final String GRAPHQL_ID_TYPE = "ID"; - public static final String GRAPHQL_STRING_TYPE = "String"; - public static final String GRAPHQL_INT_TYPE = "Int"; - public static final String GRAPHQL_FLOAT_TYPE = "Float"; - public static final String GRAPHQL_BOOLEAN_TYPE = "Boolean"; - public static final String BALLERINA_STRING_TYPE = "string"; - public static final String BALLERINA_INT_TYPE = "int"; - public static final String BALLERINA_FLOAT_TYPE = "float"; - public static final String BALLERINA_BOOLEAN_TYPE = "boolean"; - public static final String BALLERINA_ANYDATA_TYPE = "anydata"; - - public static final String ESCAPE_PATTERN = "([\\[\\]\\\\?!<>@#&~`*\\-=^+();:\\/\\_{}\\s|.$])"; - - public static GraphQLSchema getGraphqlSchema(String schema) throws ValidationException { - try { - SchemaParser schemaParser = new SchemaParser(); - TypeDefinitionRegistry typeRegistry; - typeRegistry = schemaParser.parse(schema); - - // TODO: Find an alternative way for define custom scalar types - GraphQLScalarType joinFieldSet = ExtendedScalars.newAliasedScalar("join__FieldSet") - .aliasedScalar(Scalars.GraphQLString).build(); - GraphQLScalarType linkImport = ExtendedScalars.newAliasedScalar("link__Import") - .aliasedScalar(Scalars.GraphQLString).build(); - - return new SchemaGenerator().makeExecutableSchema(typeRegistry, - RuntimeWiring.newRuntimeWiring().scalar(joinFieldSet).scalar(linkImport).build()); - } catch (SchemaProblem e) { - throw new ValidationException(GatewayCodeGenerator.ERROR_INVALID_SCHEMA); - } - } - - /** - * This method will escape special characters used in method names and identifiers. - * - * @param identifier identifier or method name - * @return escaped string - */ - public static String escapeIdentifier(String identifier) { - - if (identifier.matches("\\b[0-9]*\\b")) { - return "'" + identifier; - } else if (!identifier.matches("\\b[_a-zA-Z][_a-zA-Z0-9]*\\b") || - BAL_KEYWORDS.stream().anyMatch(identifier::equals)) { - - // TODO: Remove this `if`. Refer - https://github.com/ballerina-platform/ballerina-lang/issues/23045 - if (identifier.equals("error")) { - identifier = "_error"; - } else { - identifier = identifier.replaceAll(ESCAPE_PATTERN, "\\\\$1"); - if (identifier.endsWith("?")) { - if (identifier.charAt(identifier.length() - 2) == '\\') { - StringBuilder stringBuilder = new StringBuilder(identifier); - stringBuilder.deleteCharAt(identifier.length() - 2); - identifier = stringBuilder.toString(); - } - if (BAL_KEYWORDS.stream().anyMatch( - Optional.ofNullable(identifier).filter(sStr -> sStr.length() != 0) - .map(sStr -> sStr.substring(0, sStr.length() - 1)).orElse(identifier)::equals)) { - identifier = "'" + identifier; - } else { - return identifier; - } - } else { - identifier = "'" + identifier; - } - } - } - return identifier; - } - - /** - * Checks whether a given GraphQL scalar type name is a primitive scalar type. - * - * @param graphqlTypeName the GraphQL scalar type name - * @return whether a given GraphQL scalar type name is a primitive scalar type - */ - public static Boolean isPrimitiveScalarType(String graphqlTypeName) { - boolean isPrimitiveScalarType; - switch (graphqlTypeName) { - case GRAPHQL_ID_TYPE: - case GRAPHQL_STRING_TYPE: - case GRAPHQL_INT_TYPE: - case GRAPHQL_FLOAT_TYPE: - case GRAPHQL_BOOLEAN_TYPE: - isPrimitiveScalarType = true; - break; - default: - isPrimitiveScalarType = false; - } - return isPrimitiveScalarType; - } - - /** - * Checks whether a given GraphQL type name is a custom scalar type. - * - * @param graphQLSchema the object instance of the GraphQL schema (SDL) - * @param graphqlTypeName the GraphQL scalar type name - * @return whether a given GraphQL scalar type name is a primitive scalar type - */ - public static Boolean isCustomScalarType(GraphQLSchema graphQLSchema, String graphqlTypeName) { - return SpecReader.getCustomScalarTypeNames(graphQLSchema).contains(graphqlTypeName); - } - - /** - * Gets the Ballerina type name for a given GraphQL type name. - * - * @param graphQLSchema the object instance of the GraphQL schema (SDL) - * @param graphqlTypeName the GraphQL scalar type name - * @return the Ballerina type name for a given GraphQL scalar type name - */ - public static String getBallerinaTypeName(GraphQLSchema graphQLSchema, String graphqlTypeName) { - String ballerinaTypeName; - if (isCustomScalarType(graphQLSchema, graphqlTypeName)) { - ballerinaTypeName = BALLERINA_ANYDATA_TYPE; - } else if (isEnumType(graphQLSchema, graphqlTypeName)) { - ballerinaTypeName = BALLERINA_STRING_TYPE; - } else { - switch (graphqlTypeName) { - case GRAPHQL_ID_TYPE: - case GRAPHQL_STRING_TYPE: - ballerinaTypeName = BALLERINA_STRING_TYPE; - break; - case GRAPHQL_INT_TYPE: - ballerinaTypeName = BALLERINA_INT_TYPE; - break; - case GRAPHQL_FLOAT_TYPE: - ballerinaTypeName = BALLERINA_FLOAT_TYPE; - break; - case GRAPHQL_BOOLEAN_TYPE: - ballerinaTypeName = BALLERINA_BOOLEAN_TYPE; - break; - default: - ballerinaTypeName = graphqlTypeName; - } - } - return ballerinaTypeName; - } - - /** - * Checks whether a given GraphQL type name is an enum type. - * - * @param graphQLSchema the object instance of the GraphQL schema (SDL) - * @param graphqlTypeName the GraphQL scalar type name - * @return whether a given GraphQL scalar type name is a primitive scalar type - */ - public static Boolean isEnumType(GraphQLSchema graphQLSchema, String graphqlTypeName) { - return SpecReader.getEnumTypeNames(graphQLSchema).contains(graphqlTypeName); - } -} +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). + * + * 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.xlibb.gateway.graphql; + +import graphql.Scalars; +import graphql.scalars.ExtendedScalars; +import graphql.schema.GraphQLScalarType; +import graphql.schema.GraphQLSchema; +import graphql.schema.idl.RuntimeWiring; +import graphql.schema.idl.SchemaGenerator; +import graphql.schema.idl.SchemaParser; +import graphql.schema.idl.TypeDefinitionRegistry; +import graphql.schema.idl.errors.SchemaProblem; +import io.xlibb.gateway.exception.ValidationException; +import io.xlibb.gateway.generator.GatewayCodeGenerator; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +/** + * Class to hold the utility methods. + * */ +public class Utils { + + private static final String[] KEYWORDS = new String[] {"abort", "aborted", "abstract", "all", "annotation", + "any", "anydata", "boolean", "break", "byte", "catch", "channel", "check", "checkpanic", "client", + "committed", "const", "continue", "decimal", "else", "error", "external", "fail", "final", "finally", + "float", "flush", "fork", "function", "future", "handle", "if", "import", "in", "int", "is", "join", + "json", "listener", "lock", "match", "new", "object", "OBJECT_INIT", "onretry", "parameter", "panic", + "private", "public", "record", "remote", "resource", "retries", "retry", "return", "returns", "service", + "source", "start", "stream", "string", "table", "transaction", "try", "type", "typedesc", "typeof", + "trap", "throw", "wait", "while", "with", "worker", "var", "version", "xml", "xmlns", "BOOLEAN_LITERAL", + "NULL_LITERAL", "ascending", "descending", "foreach", "map", "group", "from", "default", "field", + "limit", "as", "on", "isolated", "readonly", "distinct", "where", "select", "do", "transactional" + , "commit", "enum", "base16", "base64", "rollback", "configurable", "class", "module", "never", + "outer", "order", "null", "key", "let", "by", "equals"}; + + private static final String[] TYPES = new String[] {"int", "any", "anydata", "boolean", "byte", "float", "int", + "json", "string", "table", "var", "xml"}; + + public static final List BAL_KEYWORDS = Collections.unmodifiableList(Arrays.asList(KEYWORDS)); + public static final List BAL_TYPES = Collections.unmodifiableList(Arrays.asList(TYPES)); + public static final String GRAPHQL_ID_TYPE = "ID"; + public static final String GRAPHQL_STRING_TYPE = "String"; + public static final String GRAPHQL_INT_TYPE = "Int"; + public static final String GRAPHQL_FLOAT_TYPE = "Float"; + public static final String GRAPHQL_BOOLEAN_TYPE = "Boolean"; + public static final String BALLERINA_STRING_TYPE = "string"; + public static final String BALLERINA_INT_TYPE = "int"; + public static final String BALLERINA_FLOAT_TYPE = "float"; + public static final String BALLERINA_BOOLEAN_TYPE = "boolean"; + public static final String BALLERINA_ANYDATA_TYPE = "anydata"; + + public static final String ESCAPE_PATTERN = "([\\[\\]\\\\?!<>@#&~`*\\-=^+();:\\/\\_{}\\s|.$])"; + + public static GraphQLSchema getGraphqlSchema(String schema) throws ValidationException { + try { + SchemaParser schemaParser = new SchemaParser(); + TypeDefinitionRegistry typeRegistry; + typeRegistry = schemaParser.parse(schema); + + // TODO: Find an alternative way for define custom scalar types + GraphQLScalarType joinFieldSet = ExtendedScalars.newAliasedScalar("join__FieldSet") + .aliasedScalar(Scalars.GraphQLString).build(); + GraphQLScalarType linkImport = ExtendedScalars.newAliasedScalar("link__Import") + .aliasedScalar(Scalars.GraphQLString).build(); + + return new SchemaGenerator().makeExecutableSchema(typeRegistry, + RuntimeWiring.newRuntimeWiring().scalar(joinFieldSet).scalar(linkImport).build()); + } catch (SchemaProblem e) { + throw new ValidationException(GatewayCodeGenerator.ERROR_INVALID_SCHEMA); + } + } + + /** + * This method will escape special characters used in method names and identifiers. + * + * @param identifier identifier or method name + * @return escaped string + */ + public static String escapeIdentifier(String identifier) { + + if (identifier.matches("\\b[0-9]*\\b")) { + return "'" + identifier; + } else if (!identifier.matches("\\b[_a-zA-Z][_a-zA-Z0-9]*\\b") || + BAL_KEYWORDS.stream().anyMatch(identifier::equals)) { + + // TODO: Remove this `if`. Refer - https://github.com/ballerina-platform/ballerina-lang/issues/23045 + if (identifier.equals("error")) { + identifier = "_error"; + } else { + identifier = identifier.replaceAll(ESCAPE_PATTERN, "\\\\$1"); + if (identifier.endsWith("?")) { + if (identifier.charAt(identifier.length() - 2) == '\\') { + StringBuilder stringBuilder = new StringBuilder(identifier); + stringBuilder.deleteCharAt(identifier.length() - 2); + identifier = stringBuilder.toString(); + } + if (BAL_KEYWORDS.stream().anyMatch( + Optional.ofNullable(identifier).filter(sStr -> sStr.length() != 0) + .map(sStr -> sStr.substring(0, sStr.length() - 1)).orElse(identifier)::equals)) { + identifier = "'" + identifier; + } else { + return identifier; + } + } else { + identifier = "'" + identifier; + } + } + } + return identifier; + } + + /** + * Checks whether a given GraphQL scalar type name is a primitive scalar type. + * + * @param graphqlTypeName the GraphQL scalar type name + * @return whether a given GraphQL scalar type name is a primitive scalar type + */ + public static Boolean isPrimitiveScalarType(String graphqlTypeName) { + boolean isPrimitiveScalarType; + switch (graphqlTypeName) { + case GRAPHQL_ID_TYPE: + case GRAPHQL_STRING_TYPE: + case GRAPHQL_INT_TYPE: + case GRAPHQL_FLOAT_TYPE: + case GRAPHQL_BOOLEAN_TYPE: + isPrimitiveScalarType = true; + break; + default: + isPrimitiveScalarType = false; + } + return isPrimitiveScalarType; + } + + /** + * Checks whether a given GraphQL type name is a custom scalar type. + * + * @param graphQLSchema the object instance of the GraphQL schema (SDL) + * @param graphqlTypeName the GraphQL scalar type name + * @return whether a given GraphQL scalar type name is a primitive scalar type + */ + public static Boolean isCustomScalarType(GraphQLSchema graphQLSchema, String graphqlTypeName) { + return SpecReader.getCustomScalarTypeNames(graphQLSchema).contains(graphqlTypeName); + } + + /** + * Gets the Ballerina type name for a given GraphQL type name. + * + * @param graphQLSchema the object instance of the GraphQL schema (SDL) + * @param graphqlTypeName the GraphQL scalar type name + * @return the Ballerina type name for a given GraphQL scalar type name + */ + public static String getBallerinaTypeName(GraphQLSchema graphQLSchema, String graphqlTypeName) { + String ballerinaTypeName; + if (isCustomScalarType(graphQLSchema, graphqlTypeName)) { + ballerinaTypeName = BALLERINA_ANYDATA_TYPE; + } else if (isEnumType(graphQLSchema, graphqlTypeName)) { + ballerinaTypeName = BALLERINA_STRING_TYPE; + } else { + switch (graphqlTypeName) { + case GRAPHQL_ID_TYPE: + case GRAPHQL_STRING_TYPE: + ballerinaTypeName = BALLERINA_STRING_TYPE; + break; + case GRAPHQL_INT_TYPE: + ballerinaTypeName = BALLERINA_INT_TYPE; + break; + case GRAPHQL_FLOAT_TYPE: + ballerinaTypeName = BALLERINA_FLOAT_TYPE; + break; + case GRAPHQL_BOOLEAN_TYPE: + ballerinaTypeName = BALLERINA_BOOLEAN_TYPE; + break; + default: + ballerinaTypeName = graphqlTypeName; + } + } + return ballerinaTypeName; + } + + /** + * Checks whether a given GraphQL type name is an enum type. + * + * @param graphQLSchema the object instance of the GraphQL schema (SDL) + * @param graphqlTypeName the GraphQL scalar type name + * @return whether a given GraphQL scalar type name is a primitive scalar type + */ + public static Boolean isEnumType(GraphQLSchema graphQLSchema, String graphqlTypeName) { + return SpecReader.getEnumTypeNames(graphQLSchema).contains(graphqlTypeName); + } +} diff --git a/native/src/main/java/io/xlibb/gateway/graphql/components/FieldData.java b/native/src/main/java/io/xlibb/gateway/graphql/components/FieldData.java index 176bd18..480ff73 100644 --- a/native/src/main/java/io/xlibb/gateway/graphql/components/FieldData.java +++ b/native/src/main/java/io/xlibb/gateway/graphql/components/FieldData.java @@ -1,63 +1,63 @@ -/* - * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). All Rights Reserved. - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package io.xlibb.gateway.graphql.components; - -import graphql.language.FieldDefinition; -import graphql.schema.GraphQLAppliedDirective; -import io.xlibb.gateway.exception.GatewayGenerationException; - -import java.util.List; - -import static io.xlibb.gateway.generator.CommonUtils.getClientFromFieldDefinition; -import static io.xlibb.gateway.generator.CommonUtils.getTypeFromFieldDefinition; - -/** - * Class to hold data related to a graphql type field. - */ -public class FieldData { - private final String fieldName; - private final String type; - private final String client; - - - FieldData(String fieldName, FieldDefinition fieldDefinition, - List joinTypeDirectivesOnParent) - throws GatewayGenerationException { - this.fieldName = fieldName; - this.type = getTypeFromFieldDefinition(fieldDefinition); - this.client = getClientFromFieldDefinition(fieldDefinition, joinTypeDirectivesOnParent); - } - - public String getFieldName() { - return fieldName; - } - - public String getType() { - return type; - } - - public String getClient() { - return client; - } - - public boolean isID() { - return this.type.equals("ID"); - } - -} +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). + * + * 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.xlibb.gateway.graphql.components; + +import graphql.language.FieldDefinition; +import graphql.schema.GraphQLAppliedDirective; +import io.xlibb.gateway.exception.GatewayGenerationException; + +import java.util.List; + +import static io.xlibb.gateway.generator.CommonUtils.getClientFromFieldDefinition; +import static io.xlibb.gateway.generator.CommonUtils.getTypeFromFieldDefinition; + +/** + * Class to hold data related to a graphql type field. + */ +public class FieldData { + private final String fieldName; + private final String type; + private final String client; + + + FieldData(String fieldName, FieldDefinition fieldDefinition, + List joinTypeDirectivesOnParent) + throws GatewayGenerationException { + this.fieldName = fieldName; + this.type = getTypeFromFieldDefinition(fieldDefinition); + this.client = getClientFromFieldDefinition(fieldDefinition, joinTypeDirectivesOnParent); + } + + public String getFieldName() { + return fieldName; + } + + public String getType() { + return type; + } + + public String getClient() { + return client; + } + + public boolean isID() { + return this.type.equals("ID"); + } + +} diff --git a/native/src/main/java/io/xlibb/gateway/graphql/components/FieldType.java b/native/src/main/java/io/xlibb/gateway/graphql/components/FieldType.java index 814706b..2d9c649 100644 --- a/native/src/main/java/io/xlibb/gateway/graphql/components/FieldType.java +++ b/native/src/main/java/io/xlibb/gateway/graphql/components/FieldType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/native/src/main/java/io/xlibb/gateway/graphql/components/JoinGraph.java b/native/src/main/java/io/xlibb/gateway/graphql/components/JoinGraph.java index 836e67a..20b0996 100644 --- a/native/src/main/java/io/xlibb/gateway/graphql/components/JoinGraph.java +++ b/native/src/main/java/io/xlibb/gateway/graphql/components/JoinGraph.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except diff --git a/native/src/main/java/io/xlibb/gateway/graphql/components/SchemaTypes.java b/native/src/main/java/io/xlibb/gateway/graphql/components/SchemaTypes.java index 5c39ae6..48168ae 100644 --- a/native/src/main/java/io/xlibb/gateway/graphql/components/SchemaTypes.java +++ b/native/src/main/java/io/xlibb/gateway/graphql/components/SchemaTypes.java @@ -1,112 +1,112 @@ -/* - * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). All Rights Reserved. - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package io.xlibb.gateway.graphql.components; - -import graphql.language.FieldDefinition; -import graphql.schema.GraphQLAppliedDirective; -import graphql.schema.GraphQLFieldDefinition; -import graphql.schema.GraphQLObjectType; -import graphql.schema.GraphQLSchema; -import io.xlibb.gateway.exception.GatewayGenerationException; -import io.xlibb.gateway.generator.CommonUtils; -import io.xlibb.gateway.graphql.SpecReader; -import io.xlibb.gateway.graphql.Utils; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * Class to hold data related to graphql types in a given graphql schema. - */ -public class SchemaTypes { - private final Map> fieldDataMap; - - public SchemaTypes(GraphQLSchema graphQLSchema) throws GatewayGenerationException { - List names = CommonUtils.getCustomDefinedObjectTypeNames(graphQLSchema); - - this.fieldDataMap = new HashMap<>(); - for (String name : names) { - fieldDataMap.put(name, getFieldsOfType(name, graphQLSchema)); - } - } - - /** - * Return the list of fields of the given type. - * - * @param name Type name - * @return List of fields - */ - public List getFieldsOfType(String name) { - return fieldDataMap.get(name); - } - - /** - * Return the list of fields of the given type. - * - * @param typeName Type name - * @param graphQLSchema GraphQL schema - * @return List of fields - */ - private List getFieldsOfType(String typeName, GraphQLSchema graphQLSchema) - throws GatewayGenerationException { - List fields = new ArrayList<>(); - - List joinTypeDirectives = - SpecReader.getObjectTypeDirectives(graphQLSchema, typeName).stream().filter( - directive -> directive.getName().equals(CommonUtils.DIRECTIVE_JOIN_TYPE) - ).collect(Collectors.toList()); - for (Map.Entry entry : - SpecReader.getObjectTypeFieldDefinitionMap(graphQLSchema, typeName).entrySet()) { - FieldData field = new FieldData(entry.getKey(), entry.getValue(), joinTypeDirectives); - if (field.getClient() != null) { - fields.add(field); - } - } - return fields; - } - - /** - * Get the object type fields map based on the input object type name from the GraphQL schema. - * - * @param graphQLSchema the instance of the Graphql schema file - * @param objectTypeName the object type name - * @return the object type fields map - */ - public static Map getObjectTypeFieldsMap(GraphQLSchema graphQLSchema, String objectTypeName) { - Map objectTypeFieldsMap = new HashMap<>(); - if (graphQLSchema.getType(objectTypeName) instanceof GraphQLObjectType) { - GraphQLObjectType objectType = - ((GraphQLObjectType) graphQLSchema.getType(objectTypeName)); - if (objectType != null) { - for (GraphQLFieldDefinition field : objectType.getFields()) { - FieldDefinition fieldDefinition = field.getDefinition(); - if (fieldDefinition == null) { - continue; - } - objectTypeFieldsMap.put(Utils.escapeIdentifier(field.getName()), - SpecReader.getFieldType(graphQLSchema, fieldDefinition.getType())); - } - } - } - return objectTypeFieldsMap; - } -} +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). + * + * 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.xlibb.gateway.graphql.components; + +import graphql.language.FieldDefinition; +import graphql.schema.GraphQLAppliedDirective; +import graphql.schema.GraphQLFieldDefinition; +import graphql.schema.GraphQLObjectType; +import graphql.schema.GraphQLSchema; +import io.xlibb.gateway.exception.GatewayGenerationException; +import io.xlibb.gateway.generator.CommonUtils; +import io.xlibb.gateway.graphql.SpecReader; +import io.xlibb.gateway.graphql.Utils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Class to hold data related to graphql types in a given graphql schema. + */ +public class SchemaTypes { + private final Map> fieldDataMap; + + public SchemaTypes(GraphQLSchema graphQLSchema) throws GatewayGenerationException { + List names = CommonUtils.getCustomDefinedObjectTypeNames(graphQLSchema); + + this.fieldDataMap = new HashMap<>(); + for (String name : names) { + fieldDataMap.put(name, getFieldsOfType(name, graphQLSchema)); + } + } + + /** + * Return the list of fields of the given type. + * + * @param name Type name + * @return List of fields + */ + public List getFieldsOfType(String name) { + return fieldDataMap.get(name); + } + + /** + * Return the list of fields of the given type. + * + * @param typeName Type name + * @param graphQLSchema GraphQL schema + * @return List of fields + */ + private List getFieldsOfType(String typeName, GraphQLSchema graphQLSchema) + throws GatewayGenerationException { + List fields = new ArrayList<>(); + + List joinTypeDirectives = + SpecReader.getObjectTypeDirectives(graphQLSchema, typeName).stream().filter( + directive -> directive.getName().equals(CommonUtils.DIRECTIVE_JOIN_TYPE) + ).collect(Collectors.toList()); + for (Map.Entry entry : + SpecReader.getObjectTypeFieldDefinitionMap(graphQLSchema, typeName).entrySet()) { + FieldData field = new FieldData(entry.getKey(), entry.getValue(), joinTypeDirectives); + if (field.getClient() != null) { + fields.add(field); + } + } + return fields; + } + + /** + * Get the object type fields map based on the input object type name from the GraphQL schema. + * + * @param graphQLSchema the instance of the Graphql schema file + * @param objectTypeName the object type name + * @return the object type fields map + */ + public static Map getObjectTypeFieldsMap(GraphQLSchema graphQLSchema, String objectTypeName) { + Map objectTypeFieldsMap = new HashMap<>(); + if (graphQLSchema.getType(objectTypeName) instanceof GraphQLObjectType) { + GraphQLObjectType objectType = + ((GraphQLObjectType) graphQLSchema.getType(objectTypeName)); + if (objectType != null) { + for (GraphQLFieldDefinition field : objectType.getFields()) { + FieldDefinition fieldDefinition = field.getDefinition(); + if (fieldDefinition == null) { + continue; + } + objectTypeFieldsMap.put(Utils.escapeIdentifier(field.getName()), + SpecReader.getFieldType(graphQLSchema, fieldDefinition.getType())); + } + } + } + return objectTypeFieldsMap; + } +} diff --git a/native/src/main/java/module-info.java b/native/src/main/java/module-info.java index 2b6b17e..4eedb5b 100644 --- a/native/src/main/java/module-info.java +++ b/native/src/main/java/module-info.java @@ -1,4 +1,4 @@ -// Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. +// Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). // // WSO2 Inc. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except diff --git a/native/src/main/resources/gateway_templates/remote_function.bal.partial b/native/src/main/resources/gateway_templates/remote_function.bal.partial index 4d5df0e..1bc7e0d 100644 --- a/native/src/main/resources/gateway_templates/remote_function.bal.partial +++ b/native/src/main/resources/gateway_templates/remote_function.bal.partial @@ -1,4 +1,4 @@ -isolated remote function @{query}(graphql:Field 'field, graphql:Context context @{params}) returns @{responseType}|error{ +@{deprecatedDirective}isolated remote function @{query}(graphql:Field 'field, graphql:Context context @{params}) returns @{responseType}|error{ QueryFieldClassifier classifier = new ('field, queryPlan, @{clientName}); string fieldString = classifier.getFieldString(); UnresolvableField[] propertiesNotResolved = classifier.getUnresolvableFields(); diff --git a/native/src/main/resources/gateway_templates/resource_function.bal.partial b/native/src/main/resources/gateway_templates/resource_function.bal.partial index 87fb919..1cac5ec 100644 --- a/native/src/main/resources/gateway_templates/resource_function.bal.partial +++ b/native/src/main/resources/gateway_templates/resource_function.bal.partial @@ -1,4 +1,4 @@ -isolated resource function get @{query}(graphql:Field 'field, graphql:Context context @{params}) returns @{responseType}|error{ +@{deprecatedDirective}isolated resource function get @{query}(graphql:Field 'field, graphql:Context context @{params}) returns @{responseType}|error{ QueryFieldClassifier classifier = new ('field, queryPlan, @{clientName}); string fieldString = classifier.getFieldString(); UnresolvableField[] propertiesNotResolved = classifier.getUnresolvableFields(); diff --git a/native/src/main/resources/gateway_templates/scalar_return_type_remote_function.bal.partial b/native/src/main/resources/gateway_templates/scalar_return_type_remote_function.bal.partial index 50446f5..8761ca9 100644 --- a/native/src/main/resources/gateway_templates/scalar_return_type_remote_function.bal.partial +++ b/native/src/main/resources/gateway_templates/scalar_return_type_remote_function.bal.partial @@ -1,4 +1,4 @@ -isolated remote function @{query}(graphql:Field 'field, graphql:Context context @{params}) returns @{responseType}|error { +@{deprecatedDirective}isolated remote function @{query}(graphql:Field 'field, graphql:Context context @{params}) returns @{responseType}|error { string queryString = wrapwithMutation("@{query}", () @{queryArgs}); @{query}Response|graphql:ClientError response = @{clientName}_CLIENT->execute(queryString); if response is graphql:ClientError { diff --git a/native/src/main/resources/gateway_templates/scalar_return_type_resource_function.bal.partial b/native/src/main/resources/gateway_templates/scalar_return_type_resource_function.bal.partial index 17be026..f9d3cc7 100644 --- a/native/src/main/resources/gateway_templates/scalar_return_type_resource_function.bal.partial +++ b/native/src/main/resources/gateway_templates/scalar_return_type_resource_function.bal.partial @@ -1,4 +1,4 @@ -isolated resource function get @{query}(graphql:Field 'field, graphql:Context context @{params}) returns @{responseType}|error { +@{deprecatedDirective}isolated resource function get @{query}(graphql:Field 'field, graphql:Context context @{params}) returns @{responseType}|error { string queryString = wrapwithQuery("@{query}", () @{queryArgs}); @{query}Response|graphql:ClientError response = @{clientName}_CLIENT->execute(queryString); if response is graphql:ClientError { diff --git a/native/src/test/java/io/xlibb/gateway/generator/GatewayCodeGenerationTest.java b/native/src/test/java/io/xlibb/gateway/generator/GatewayCodeGenerationTest.java index 4cb2942..8184b6c 100644 --- a/native/src/test/java/io/xlibb/gateway/generator/GatewayCodeGenerationTest.java +++ b/native/src/test/java/io/xlibb/gateway/generator/GatewayCodeGenerationTest.java @@ -1,133 +1,134 @@ -/* - * Copyright (c) 2023, WSO2 LLC. (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.xlibb.gateway.generator; - -import graphql.schema.GraphQLSchema; -import io.ballerina.runtime.api.utils.StringUtils; -import io.ballerina.runtime.api.values.BString; -import io.xlibb.gateway.GatewayProject; -import io.xlibb.gateway.exception.GatewayGenerationException; -import io.xlibb.gateway.exception.ValidationException; -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; - -import static io.xlibb.gateway.generator.GatewayCodeGenerator.generateGateway; - -/** - * Class to test code generation related to gateway. - */ -public class GatewayCodeGenerationTest extends GraphqlTest { - private final Path expectedResources = this.resourceDir.resolve(Paths.get( - "results")); - - @Test(description = "Test query plan generation for gateway", dataProvider = - "GatewayGenerationDataProvider") - public void testQueryPlanGeneration(String supergraphFileName) - throws ValidationException, IOException, GatewayGenerationException { - GraphQLSchema graphQLSchema = GatewayTestUtils.getGatewayProject(supergraphFileName, tmpDir).getSchema(); - String generatedSrc = (new GatewayQueryPlanGenerator(graphQLSchema)).generateSrc(); - String expectedSrc = Files.readString(expectedResources.resolve( - Paths.get(supergraphFileName, "query_plan.bal"))); - Assert.assertEquals(generatedSrc, expectedSrc); - } - - @Test(description = "Test service generation for gateway", dataProvider = "GatewayGenerationDataProvider") - public void testGatewayServiceGeneration(String supergraphFileName) - throws ValidationException, IOException, GatewayGenerationException { - - GatewayProject project = GatewayTestUtils.getGatewayProject(supergraphFileName, tmpDir); - String generatedSrc = (new GatewayServiceGenerator(project)).generateSrc(); - String expectedSrc = Files.readString(expectedResources.resolve( - Paths.get(supergraphFileName, "service.bal"))); - Assert.assertEquals(generatedSrc, expectedSrc); - } - - @Test(description = "Test gateway types generation", dataProvider = "GatewayGenerationDataProvider") - public void testGatewayTypeGeneration(String supergraphFileName) - throws IOException, ValidationException, GatewayGenerationException { - GatewayProject project = GatewayTestUtils.getGatewayProject(supergraphFileName, tmpDir); - GraphQLSchema graphQLSchema = project.getSchema(); - String generatedSrc = (new GatewayTypeGenerator(graphQLSchema)).generateSrc(); - String expectedSrc = Files.readString(expectedResources.resolve( - Paths.get(supergraphFileName, "types.bal"))); - Assert.assertEquals(generatedSrc, expectedSrc); - } - - @Test(description = "Test generate gateway function", dataProvider = "GatewayGenerationDataProvider") - public void testGenerateGatewayFunction(String supergraphFileName) { - String schemaPath = GatewayTestUtils.SCHEMA_RESOURCE_DIR.resolve(supergraphFileName + ".graphql") - .toAbsolutePath().toString(); - BString gatewayFilePath = generateGateway(StringUtils.fromString(schemaPath), - StringUtils.fromString(tmpDir.toString()), StringUtils.fromString("9000")); - Assert.assertEquals(gatewayFilePath.getValue(), "success"); - } - - @Test(description = "Test generate gateway function with invalid arguments", dataProvider = - "InvalidArgumentsDataProvider") - public void testGenerateGatewayFunctionWithInvalidArguments(String supergraphFileName, String outputPath, - String expected) { - String schemaPath = GatewayTestUtils.SCHEMA_RESOURCE_DIR.resolve(supergraphFileName + ".graphql") - .toAbsolutePath().toString(); - BString gatewayFilePath = generateGateway(StringUtils.fromString(schemaPath), - StringUtils.fromString(outputPath), StringUtils.fromString("9000")); - Assert.assertEquals(gatewayFilePath.getValue(), expected); - } - - @DataProvider(name = "GatewayGenerationDataProvider") - public Object[][] getGatewayTypeGenerationTestData() { - return new Object[][]{ - {"two_entities"}, - {"two_entities_with_id_type_fields"}, - {"three_entities"} - }; - } - - @DataProvider(name = "InvalidArgumentsDataProvider") - public Object[][] getInvalidArgumentsTestData() { - String tempPath = tmpDir.toAbsolutePath().toString(); - return new Object[][]{ - {"invalid_schema_path", tempPath, GatewayCodeGenerator.ERROR_INVALID_SUPERGRAPH_FILE_PATH}, - {"two_entities", "invalid_output_path", GatewayCodeGenerator.ERROR_INVALID_OUTPUT_PATH}, - {"invalid/missing_directive_definitions", tempPath, GatewayCodeGenerator.ERROR_INVALID_SCHEMA}, - {"invalid/missing_query_type", tempPath, GatewayCodeGenerator.ERROR_INVALID_SCHEMA} - }; - } - - @Test(groups = {"invalid_permission"}, description = "Test ouput path is not writable") - public void testReadOnlyOutputPath() throws IOException { - String supergraph = "two_entities"; - String schemaPath = GatewayTestUtils.SCHEMA_RESOURCE_DIR.resolve(supergraph + ".graphql") - .toAbsolutePath().toString(); - Path readOnlyPath = tmpDir.toAbsolutePath().resolve("readonly_folder"); - Files.createDirectory(readOnlyPath); - File file = new File(readOnlyPath.toString()); - file.setReadOnly(); - BString output = generateGateway(StringUtils.fromString(schemaPath), - StringUtils.fromString(readOnlyPath.toString()), StringUtils.fromString("9000")); - Assert.assertEquals(output.getValue(), GatewayCodeGenerator.ERROR_OUTPUT_PATH_NOT_WRITABLE); - } - -} +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). + * + * 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.xlibb.gateway.generator; + +import graphql.schema.GraphQLSchema; +import io.ballerina.runtime.api.utils.StringUtils; +import io.ballerina.runtime.api.values.BString; +import io.xlibb.gateway.GatewayProject; +import io.xlibb.gateway.exception.GatewayGenerationException; +import io.xlibb.gateway.exception.ValidationException; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static io.xlibb.gateway.generator.GatewayCodeGenerator.generateGateway; + +/** + * Class to test code generation related to gateway. + */ +public class GatewayCodeGenerationTest extends GraphqlTest { + private final Path expectedResources = this.resourceDir.resolve(Paths.get( + "results")); + + @Test(description = "Test query plan generation for gateway", dataProvider = + "GatewayGenerationDataProvider") + public void testQueryPlanGeneration(String supergraphFileName) + throws ValidationException, IOException, GatewayGenerationException { + GraphQLSchema graphQLSchema = GatewayTestUtils.getGatewayProject(supergraphFileName, tmpDir).getSchema(); + String generatedSrc = (new GatewayQueryPlanGenerator(graphQLSchema)).generateSrc(); + String expectedSrc = GatewayTestUtils.readWithLF(expectedResources.resolve( + Paths.get(supergraphFileName, "query_plan.bal"))); + Assert.assertEquals(generatedSrc, expectedSrc); + } + + @Test(description = "Test service generation for gateway", dataProvider = "GatewayGenerationDataProvider") + public void testGatewayServiceGeneration(String supergraphFileName) + throws ValidationException, IOException, GatewayGenerationException { + + GatewayProject project = GatewayTestUtils.getGatewayProject(supergraphFileName, tmpDir); + String generatedSrc = (new GatewayServiceGenerator(project)).generateSrc(); + String expectedSrc = GatewayTestUtils.readWithLF(expectedResources.resolve( + Paths.get(supergraphFileName, "service.bal"))); + Assert.assertEquals(generatedSrc, expectedSrc); + } + + @Test(description = "Test gateway types generation", dataProvider = "GatewayGenerationDataProvider") + public void testGatewayTypeGeneration(String supergraphFileName) + throws IOException, ValidationException, GatewayGenerationException { + GatewayProject project = GatewayTestUtils.getGatewayProject(supergraphFileName, tmpDir); + GraphQLSchema graphQLSchema = project.getSchema(); + String generatedSrc = (new GatewayTypeGenerator(graphQLSchema)).generateSrc(); + String expectedSrc = GatewayTestUtils.readWithLF(expectedResources.resolve( + Paths.get(supergraphFileName, "types.bal"))); + Assert.assertEquals(generatedSrc, expectedSrc); + } + + @Test(description = "Test generate gateway function", dataProvider = "GatewayGenerationDataProvider") + public void testGenerateGatewayFunction(String supergraphFileName) { + String schemaPath = GatewayTestUtils.SCHEMA_RESOURCE_DIR.resolve(supergraphFileName + ".graphql") + .toAbsolutePath().toString(); + BString gatewayFilePath = generateGateway(StringUtils.fromString(schemaPath), + StringUtils.fromString(tmpDir.toString()), StringUtils.fromString("9000")); + Assert.assertEquals(gatewayFilePath.getValue(), "success"); + } + + @Test(description = "Test generate gateway function with invalid arguments", dataProvider = + "InvalidArgumentsDataProvider") + public void testGenerateGatewayFunctionWithInvalidArguments(String supergraphFileName, String outputPath, + String expected) { + String schemaPath = GatewayTestUtils.SCHEMA_RESOURCE_DIR.resolve(supergraphFileName + ".graphql") + .toAbsolutePath().toString(); + BString gatewayFilePath = generateGateway(StringUtils.fromString(schemaPath), + StringUtils.fromString(outputPath), StringUtils.fromString("9000")); + Assert.assertEquals(gatewayFilePath.getValue(), expected); + } + + @DataProvider(name = "GatewayGenerationDataProvider") + public Object[][] getGatewayTypeGenerationTestData() { + return new Object[][]{ + {"two_entities"}, + {"two_entities_with_id_type_fields"}, + {"three_entities"}, + {"deprecated_directive"} + }; + } + + @DataProvider(name = "InvalidArgumentsDataProvider") + public Object[][] getInvalidArgumentsTestData() { + String tempPath = tmpDir.toAbsolutePath().toString(); + return new Object[][]{ + {"invalid_schema_path", tempPath, GatewayCodeGenerator.ERROR_INVALID_SUPERGRAPH_FILE_PATH}, + {"two_entities", "invalid_output_path", GatewayCodeGenerator.ERROR_INVALID_OUTPUT_PATH}, + {"invalid/missing_directive_definitions", tempPath, GatewayCodeGenerator.ERROR_INVALID_SCHEMA}, + {"invalid/missing_query_type", tempPath, GatewayCodeGenerator.ERROR_INVALID_SCHEMA} + }; + } + + @Test(groups = {"invalid_permission"}, description = "Test output path is not writable", enabled = false) + public void testReadOnlyOutputPath() throws IOException { + String supergraph = "two_entities"; + String schemaPath = GatewayTestUtils.SCHEMA_RESOURCE_DIR.resolve(supergraph + ".graphql") + .toAbsolutePath().toString(); + Path readOnlyPath = tmpDir.toAbsolutePath().resolve("readonly_folder"); + Files.createDirectory(readOnlyPath); + File file = new File(readOnlyPath.toString()); + file.setReadOnly(); + BString output = generateGateway(StringUtils.fromString(schemaPath), + StringUtils.fromString(readOnlyPath.toString()), StringUtils.fromString("9000")); + Assert.assertEquals(output.getValue(), GatewayCodeGenerator.ERROR_OUTPUT_PATH_NOT_WRITABLE); + } + +} diff --git a/native/src/test/java/io/xlibb/gateway/generator/GatewayExecutionTest.java b/native/src/test/java/io/xlibb/gateway/generator/GatewayExecutionTest.java index de63ebb..df4a310 100644 --- a/native/src/test/java/io/xlibb/gateway/generator/GatewayExecutionTest.java +++ b/native/src/test/java/io/xlibb/gateway/generator/GatewayExecutionTest.java @@ -1,6 +1,5 @@ /* - * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. - * + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). * 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. diff --git a/native/src/test/java/io/xlibb/gateway/generator/GatewayTestUtils.java b/native/src/test/java/io/xlibb/gateway/generator/GatewayTestUtils.java index b9ffe1d..4d8b9bc 100644 --- a/native/src/test/java/io/xlibb/gateway/generator/GatewayTestUtils.java +++ b/native/src/test/java/io/xlibb/gateway/generator/GatewayTestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except @@ -175,7 +175,7 @@ public static File getCompiledBallerinaProject(Path projectPath, Path targetPath BuildProject buildProject = BuildProject.load(projectPath, buildOptions); checkDiagnosticResultsForErrors(buildProject.currentPackage().runCodeGenAndModifyPlugins()); PackageCompilation packageCompilation = buildProject.currentPackage().getCompilation(); - JBallerinaBackend jBallerinaBackend = JBallerinaBackend.from(packageCompilation, JvmTarget.JAVA_11); + JBallerinaBackend jBallerinaBackend = JBallerinaBackend.from(packageCompilation, JvmTarget.JAVA_17); checkDiagnosticResultsForErrors(jBallerinaBackend.diagnosticResult()); Path executablePath = targetPath.resolve(executableName + ".jar"); jBallerinaBackend.emit(JBallerinaBackend.OutputType.EXEC, executablePath); @@ -188,4 +188,9 @@ private static void checkDiagnosticResultsForErrors(DiagnosticResult diagnosticR throw new GatewayGenerationException("Error while generating the executable."); } } + + public static String readWithLF(Path filePath) throws IOException { + String codeSrc = Files.readString(filePath); + return codeSrc.replace("\n", System.lineSeparator()); + } } diff --git a/native/src/test/java/io/xlibb/gateway/generator/GraphqlTest.java b/native/src/test/java/io/xlibb/gateway/generator/GraphqlTest.java index 53bb9e5..a953b42 100644 --- a/native/src/test/java/io/xlibb/gateway/generator/GraphqlTest.java +++ b/native/src/test/java/io/xlibb/gateway/generator/GraphqlTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org). * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except diff --git a/native/src/test/java/io/xlibb/gateway/generator/common/CommonUtilTest.java b/native/src/test/java/io/xlibb/gateway/generator/common/CommonUtilTest.java index 86c8c93..0c458af 100644 --- a/native/src/test/java/io/xlibb/gateway/generator/common/CommonUtilTest.java +++ b/native/src/test/java/io/xlibb/gateway/generator/common/CommonUtilTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except diff --git a/native/src/test/java/io/xlibb/gateway/generator/common/SchemaTypesTest.java b/native/src/test/java/io/xlibb/gateway/generator/common/SchemaTypesTest.java index c7cf5d2..2be0e40 100644 --- a/native/src/test/java/io/xlibb/gateway/generator/common/SchemaTypesTest.java +++ b/native/src/test/java/io/xlibb/gateway/generator/common/SchemaTypesTest.java @@ -1,92 +1,92 @@ -/* - * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). All Rights Reserved. - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package io.xlibb.gateway.generator.common; - -import graphql.schema.GraphQLSchema; -import io.xlibb.gateway.exception.GatewayGenerationException; -import io.xlibb.gateway.exception.ValidationException; -import io.xlibb.gateway.generator.GatewayTestUtils; -import io.xlibb.gateway.generator.GraphqlTest; -import io.xlibb.gateway.graphql.components.FieldData; -import io.xlibb.gateway.graphql.components.SchemaTypes; -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - -import java.io.IOException; -import java.util.List; -import java.util.Map; - -/** - * Test class for SchemaTypes. - * */ -public class SchemaTypesTest extends GraphqlTest { - @Test(description = "Test schema types on a given graphql schema", dataProvider = "SchemaTypesDataProvider") - public void testSchemaTypes(String graphQLSchemaFileName, String[] typeNames) - throws GatewayGenerationException, ValidationException, IOException { - GraphQLSchema graphQLSchema = GatewayTestUtils.getGatewayProject(graphQLSchemaFileName, tmpDir) - .getSchema(); - SchemaTypes schemaTypes = new SchemaTypes(graphQLSchema); - for (String typeName : typeNames) { - Assert.assertNotNull(schemaTypes.getFieldsOfType(typeName)); - } - } - - @DataProvider(name = "SchemaTypesDataProvider") - public Object[][] schemaTypesDataProvider() { - return new Object[][] { - {"two_entities", new String[] {"Astronaut", "Mission"}}, - {"two_entities_with_id_type_fields", new String[] {"Astronaut", "Mission"}}, - {"three_entities", new String[] {"Product", "Category", "Review"}} - }; - } - - @Test(description = "Test field names of a given type on a graphql schema", dataProvider = "FieldNameProvider") - public void testFieldData(String graphQLSchemaFileName, String typeName, Map fieldData) - throws GatewayGenerationException, ValidationException, IOException { - GraphQLSchema graphQLSchema = GatewayTestUtils.getGatewayProject(graphQLSchemaFileName, tmpDir) - .getSchema(); - SchemaTypes schemaTypes = new SchemaTypes(graphQLSchema); - List fieldDataList = schemaTypes.getFieldsOfType(typeName); - for (FieldData data : fieldDataList) { - Object[] expected = fieldData.get(data.getFieldName()); - Assert.assertEquals(data.getType(), expected[0]); - Assert.assertEquals(data.getClient(), expected[1]); - Assert.assertEquals(data.isID(), expected[2]); - } - } - - @DataProvider(name = "FieldNameProvider") - public Object[][] getFieldData() { - return new Object[][] { - {"two_entities", "Astronaut", Map.ofEntries( - Map.entry("id", new Object[] {"ID", "ASTRONAUTS", true}), - Map.entry("name", new Object[] {"String", "ASTRONAUTS", false}), - Map.entry("missions", new Object[] {"Mission", "MISSIONS", false}) - )}, - {"two_entities", "Mission", Map.ofEntries( - Map.entry("id", new Object[] {"Int", "MISSIONS", false}), - Map.entry("designation", new Object[] {"String", "MISSIONS", false}), - Map.entry("startDate", new Object[] {"String", "MISSIONS", false}), - Map.entry("endDate", new Object[] {"String", "MISSIONS", false}), - Map.entry("crew", new Object[] {"Astronaut", "MISSIONS", false}) - )} - }; - } -} +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org). + * + * 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.xlibb.gateway.generator.common; + +import graphql.schema.GraphQLSchema; +import io.xlibb.gateway.exception.GatewayGenerationException; +import io.xlibb.gateway.exception.ValidationException; +import io.xlibb.gateway.generator.GatewayTestUtils; +import io.xlibb.gateway.generator.GraphqlTest; +import io.xlibb.gateway.graphql.components.FieldData; +import io.xlibb.gateway.graphql.components.SchemaTypes; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +/** + * Test class for SchemaTypes. + * */ +public class SchemaTypesTest extends GraphqlTest { + @Test(description = "Test schema types on a given graphql schema", dataProvider = "SchemaTypesDataProvider") + public void testSchemaTypes(String graphQLSchemaFileName, String[] typeNames) + throws GatewayGenerationException, ValidationException, IOException { + GraphQLSchema graphQLSchema = GatewayTestUtils.getGatewayProject(graphQLSchemaFileName, tmpDir) + .getSchema(); + SchemaTypes schemaTypes = new SchemaTypes(graphQLSchema); + for (String typeName : typeNames) { + Assert.assertNotNull(schemaTypes.getFieldsOfType(typeName)); + } + } + + @DataProvider(name = "SchemaTypesDataProvider") + public Object[][] schemaTypesDataProvider() { + return new Object[][] { + {"two_entities", new String[] {"Astronaut", "Mission"}}, + {"two_entities_with_id_type_fields", new String[] {"Astronaut", "Mission"}}, + {"three_entities", new String[] {"Product", "Category", "Review"}} + }; + } + + @Test(description = "Test field names of a given type on a graphql schema", dataProvider = "FieldNameProvider") + public void testFieldData(String graphQLSchemaFileName, String typeName, Map fieldData) + throws GatewayGenerationException, ValidationException, IOException { + GraphQLSchema graphQLSchema = GatewayTestUtils.getGatewayProject(graphQLSchemaFileName, tmpDir) + .getSchema(); + SchemaTypes schemaTypes = new SchemaTypes(graphQLSchema); + List fieldDataList = schemaTypes.getFieldsOfType(typeName); + for (FieldData data : fieldDataList) { + Object[] expected = fieldData.get(data.getFieldName()); + Assert.assertEquals(data.getType(), expected[0]); + Assert.assertEquals(data.getClient(), expected[1]); + Assert.assertEquals(data.isID(), expected[2]); + } + } + + @DataProvider(name = "FieldNameProvider") + public Object[][] getFieldData() { + return new Object[][] { + {"two_entities", "Astronaut", Map.ofEntries( + Map.entry("id", new Object[] {"ID", "ASTRONAUTS", true}), + Map.entry("name", new Object[] {"String", "ASTRONAUTS", false}), + Map.entry("missions", new Object[] {"Mission", "MISSIONS", false}) + )}, + {"two_entities", "Mission", Map.ofEntries( + Map.entry("id", new Object[] {"Int", "MISSIONS", false}), + Map.entry("designation", new Object[] {"String", "MISSIONS", false}), + Map.entry("startDate", new Object[] {"String", "MISSIONS", false}), + Map.entry("endDate", new Object[] {"String", "MISSIONS", false}), + Map.entry("crew", new Object[] {"Astronaut", "MISSIONS", false}) + )} + }; + } +} diff --git a/native/src/test/resources/results/deprecated_directive/query_plan.bal b/native/src/test/resources/results/deprecated_directive/query_plan.bal new file mode 100644 index 0000000..90018c5 --- /dev/null +++ b/native/src/test/resources/results/deprecated_directive/query_plan.bal @@ -0,0 +1,6 @@ +public const string PRODUCTS = "products"; +public final readonly & table key(typename) queryPlan = table [{typename: "Product", keys: {"products": "id"}, fields: table [ + {name: "price", 'type: "Float", 'client: PRODUCTS}, + {name: "name", 'type: "String", 'client: PRODUCTS}, + {name: "description", 'type: "String", 'client: PRODUCTS} + ]}]; diff --git a/native/src/test/resources/results/deprecated_directive/service.bal b/native/src/test/resources/results/deprecated_directive/service.bal new file mode 100644 index 0000000..227136c --- /dev/null +++ b/native/src/test/resources/results/deprecated_directive/service.bal @@ -0,0 +1,110 @@ +import ballerina/graphql; +import ballerina/log; + +final graphql:Client PRODUCTS_CLIENT = check new graphql:Client("http://localhost:9091"); + +isolated function getClient(string clientName) returns graphql:Client { + match clientName { + "products" => { + return PRODUCTS_CLIENT; + } + _ => { + panic error("Client not found"); + } + } +} + +configurable int PORT = 9000; + +isolated service on new graphql:Listener(PORT) { + isolated function init() { + log:printInfo(string `💃 Server ready at port: ${PORT}`); + } + + # # Deprecated + # No longer supported + @deprecated + isolated resource function get products(graphql:Field 'field, graphql:Context context) returns Product[]|error { + QueryFieldClassifier classifier = new ('field, queryPlan, PRODUCTS); + string fieldString = classifier.getFieldString(); + UnresolvableField[] propertiesNotResolved = classifier.getUnresolvableFields(); + string queryString = wrapwithQuery("products", fieldString); + productsResponse|graphql:ClientError response = PRODUCTS_CLIENT->execute(queryString); + Product[]? result = null; + graphql:ErrorDetail[] errors = []; + if response is graphql:ClientError { + appendUnableToResolveErrorDetail(errors, 'field); + } else { + result = response.data.products; + appendErrorDetailsFromResponse(errors, response?.errors); + } + Resolver resolver = new (queryPlan, result.toJson(), "Product", propertiesNotResolved, ["products"], errors); + json finalResult = resolver.getResult(); + addErrorsToGraphqlContext(context, errors); + return finalResult.cloneWithType(); + } + + isolated resource function get product(graphql:Field 'field, graphql:Context context, string id) returns Product?|error { + QueryFieldClassifier classifier = new ('field, queryPlan, PRODUCTS); + string fieldString = classifier.getFieldString(); + UnresolvableField[] propertiesNotResolved = classifier.getUnresolvableFields(); + string queryString = wrapwithQuery("product", fieldString, {"id": getParamAsString(id)}); + productResponse|graphql:ClientError response = PRODUCTS_CLIENT->execute(queryString); + map result = {"id": id}; + graphql:ErrorDetail[] errors = []; + if response is graphql:ClientError { + appendUnableToResolveErrorDetail(errors, 'field); + } else { + mergeToResultJson(result, >response.data.product.toJson()); + appendErrorDetailsFromResponse(errors, response?.errors); + } + Resolver resolver = new (queryPlan, result.toJson(), "Product", propertiesNotResolved, ["product"], errors); + json finalResult = resolver.getResult(); + addErrorsToGraphqlContext(context, errors); + return finalResult.cloneWithType(); + } + + # # Deprecated + # `person` will be removed in the future + @deprecated + isolated resource function get person(graphql:Field 'field, graphql:Context context, string? id) returns string?|error { + string queryString = wrapwithQuery("person", (), {"id": getParamAsString(id)}); + personResponse|graphql:ClientError response = PRODUCTS_CLIENT->execute(queryString); + if response is graphql:ClientError { + return error("Unable to resolve : person"); + } + return response.data.person; + } + # # Deprecated + # No longer supported + @deprecated + isolated remote function addProduct(graphql:Field 'field, graphql:Context context, string? name) returns Product?|error { + QueryFieldClassifier classifier = new ('field, queryPlan, PRODUCTS); + string fieldString = classifier.getFieldString(); + UnresolvableField[] propertiesNotResolved = classifier.getUnresolvableFields(); + string queryString = wrapwithMutation("addProduct", fieldString, {"name": getParamAsString(name)}); + addProductResponse|graphql:ClientError response = PRODUCTS_CLIENT->execute(queryString); + if response is graphql:ClientError { + return error("Unable to perform the operation"); + } + Product? result = response.data.addProduct; + graphql:ErrorDetail[] errors = []; + appendErrorDetailsFromResponse(errors, response?.errors); + Resolver resolver = new (queryPlan, result.toJson(), "Product", propertiesNotResolved, ["addProduct"], errors); + json finalResult = resolver.getResult(); + addErrorsToGraphqlContext(context, errors); + return finalResult.cloneWithType(); + } + + # # Deprecated + # `addPerson` will be removed in the future + @deprecated + isolated remote function addPerson(graphql:Field 'field, graphql:Context context, string? id) returns string|error { + string queryString = wrapwithMutation("addPerson", (), {"id": getParamAsString(id)}); + addPersonResponse|graphql:ClientError response = PRODUCTS_CLIENT->execute(queryString); + if response is graphql:ClientError { + return error("Unable to resolve : addPerson"); + } + return response.data.addPerson; + } +} diff --git a/native/src/test/resources/results/deprecated_directive/types.bal b/native/src/test/resources/results/deprecated_directive/types.bal new file mode 100644 index 0000000..b2f5779 --- /dev/null +++ b/native/src/test/resources/results/deprecated_directive/types.bal @@ -0,0 +1,33 @@ +import ballerina/graphql; + +public type Product record {| + float price?; + string name?; + string description?; + string id?; +|}; + +public type productsResponse record { + graphql:ErrorDetail[] errors?; + record {|Product[] products;|} data; +}; + +public type productResponse record { + graphql:ErrorDetail[] errors?; + record {|Product product;|} data; +}; + +public type personResponse record { + graphql:ErrorDetail[] errors?; + record {|string person;|} data; +}; + +public type addProductResponse record { + graphql:ErrorDetail[] errors?; + record {|Product addProduct;|} data; +}; + +public type addPersonResponse record { + graphql:ErrorDetail[] errors?; + record {|string addPerson;|} data; +}; diff --git a/native/src/test/resources/supergraph_schemas/deprecated_directive.graphql b/native/src/test/resources/supergraph_schemas/deprecated_directive.graphql new file mode 100644 index 0000000..d094673 --- /dev/null +++ b/native/src/test/resources/supergraph_schemas/deprecated_directive.graphql @@ -0,0 +1,83 @@ +schema + @link(url: "https://specs.apollo.dev/link/v1.0") + @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION) { + query: Query + mutation: Mutation +} + +directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE + +directive @join__field( + graph: join__Graph + requires: join__FieldSet + provides: join__FieldSet + type: String + external: Boolean + override: String + usedOverridden: Boolean +) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION + +directive @join__graph(name: String!, url: String!) on ENUM_VALUE + +directive @join__implements( + graph: join__Graph! + interface: String! +) repeatable on OBJECT | INTERFACE + +directive @join__type( + graph: join__Graph! + key: join__FieldSet + extension: Boolean! = false + resolvable: Boolean! = true + isInterfaceObject: Boolean! = false +) repeatable on OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT | SCALAR + +directive @join__unionMember( + graph: join__Graph! + member: String! +) repeatable on UNION + +directive @link( + url: String + as: String + for: link__Purpose + import: [link__Import] +) repeatable on SCHEMA + +scalar join__FieldSet + +enum join__Graph { + PRODUCTS @join__graph(name: "products", url: "http://localhost:9091") +} + +scalar link__Import + +enum link__Purpose { + """ + `SECURITY` features provide metadata necessary to securely resolve fields. + """ + SECURITY + + """ + `EXECUTION` features provide metadata necessary for operation execution. + """ + EXECUTION +} + +type Product @join__type(graph: PRODUCTS, key: "id") { + id: ID! + name: String! + description: String! + price: Float! +} + +type Query @join__type(graph: PRODUCTS) { + products: [Product!]! @deprecated + product(id: ID!): Product + person(id: ID): String @deprecated(reason: "`person` will be removed in the future") +} + +type Mutation @join__type(graph: PRODUCTS) { + addProduct(name: String): Product @join__field(graph: PRODUCTS) @deprecated + addPerson(id: ID): String! @join__field(graph: PRODUCTS) @deprecated(reason: "`addPerson` will be removed in the future") +} diff --git a/native/src/test/resources/testng.xml b/native/src/test/resources/testng.xml index c93a77b..aa82f44 100644 --- a/native/src/test/resources/testng.xml +++ b/native/src/test/resources/testng.xml @@ -1,32 +1,32 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + +