diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 2c48305b..00000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,11 +0,0 @@ -version: 2 -updates: - - package-ecosystem: "github-actions" - directory: "/" - groups: - github-actions: - patterns: ["*"] - schedule: - interval: "weekly" - cooldown: - default-days: 7 diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 745d3932..a0b933a3 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -3,29 +3,31 @@ name: Linux on: [push, pull_request] jobs: - gcc10: + Linux: strategy: fail-fast: false matrix: - config: [Debug, Release] + compiler: [gcc-14, clang-18] + config: [debug, release] - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/checkout@v4.1.3 with: submodules: true - name: Install Dependencies run: | + sudo add-apt-repository -y universe sudo apt-get update - sudo apt-get install -yq libgtest-dev libboost-program-options-dev rapidjson-dev ninja-build gcc-10 g++-10 + sudo apt-get install -yq libgtest-dev libboost-program-options-dev rapidjson-dev ninja-build - name: Build GTest run: | cmake -E make_directory gtest cd gtest - cmake -DCMAKE_BUILD_TYPE=${{ matrix.config }} -G Ninja /usr/src/gtest + cmake -DCMAKE_BUILD_TYPE=${{ matrix.config == 'debug' && 'Debug' || 'Release' }} -G Ninja /usr/src/gtest cmake --build . -j -v sudo cmake --install . @@ -33,20 +35,10 @@ jobs: run: cmake -E make_directory build - name: Configure CMake - shell: pwsh - env: - CC: gcc-10 - CXX: g++-10 - working-directory: build/ - run: | - $cmakeBuildType = '${{ matrix.config }}' - - cmake "-DCMAKE_BUILD_TYPE=$cmakeBuildType" -G Ninja ${{ github.workspace }} + run: cmake --preset ${{ matrix.compiler }}-${{ matrix.config }} -DGRAPHQL_USE_TAOCPP_JSON=OFF -DCMAKE_TOOLCHAIN_FILE= - name: Build - working-directory: build/ - run: cmake --build . -j -v + run: cmake --build --preset ${{ matrix.compiler }}-${{ matrix.config }} -j -v - name: Test - working-directory: build/ - run: ctest --output-on-failure + run: ctest --preset ${{ matrix.compiler }}-${{ matrix.config }} --output-on-failure diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 29bb3f8c..0f04dd34 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -9,19 +9,20 @@ jobs: matrix: config: [Debug, Release] - runs-on: macos-13 + runs-on: macos-latest steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/checkout@v4.1.3 with: submodules: true + fetch-depth: 0 - name: Cache vcpkg - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + uses: actions/cache@v4.0.2 id: cache-vcpkg with: path: build/vcpkg_cache/ - key: vcpkg-binaries-x64-osx + key: vcpkg-binaries-osx-${{ hashFiles('vcpkg.json') }} - name: Create Build Environment if: ${{ !steps.cache-vcpkg.outputs.cache-hit }} @@ -29,18 +30,27 @@ jobs: cmake -E make_directory build cmake -E make_directory build/vcpkg_cache + - name: Boostrap vcpkg + shell: pwsh + working-directory: vcpkg/ + run: | + ./bootstrap-vcpkg.sh + ./vcpkg integrate install + - name: Configure shell: pwsh + env: + VCPKG_ROOT: ${{ github.workspace }}/vcpkg working-directory: build/ run: | - $vcpkgToolchain = Join-Path $env:VCPKG_INSTALLATION_ROOT './scripts/buildsystems/vcpkg.cmake' -Resolve + $vcpkgToolchain = Join-Path $env:VCPKG_ROOT './scripts/buildsystems/vcpkg.cmake' -Resolve $cmakeBuildType = '${{ matrix.config }}' $cachedBinaries = Join-Path $(Get-Location) './vcpkg_cache/' -Resolve $cacheAccess = $(if ('${{ steps.cache-vcpkg.outputs.cache-hit }}' -eq 'true') { 'read' } else { 'write' }) $env:VCPKG_BINARY_SOURCES = "clear;files,$cachedBinaries,$cacheAccess" - cmake "-DCMAKE_TOOLCHAIN_FILE=$vcpkgToolchain" "-DCMAKE_BUILD_TYPE=$cmakeBuildType" ${{ github.workspace }} + cmake "-DCMAKE_TOOLCHAIN_FILE=$vcpkgToolchain" "-DCMAKE_BUILD_TYPE=$cmakeBuildType" "-DGRAPHQL_BUILD_MODULES=OFF" ${{ github.workspace }} - name: Build working-directory: build/ diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 55465a54..cd638c24 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -11,12 +11,13 @@ jobs: libs: ['shared', 'static'] config: ['Debug', 'Release'] - runs-on: windows-2022 + runs-on: windows-latest steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/checkout@v4.1.3 with: submodules: true + fetch-depth: 0 - name: Set target triplet id: set-variables @@ -24,11 +25,11 @@ jobs: run: echo "vcpkg_triplet=${{ matrix.arch }}-windows$(if ('${{ matrix.libs }}' -eq 'static') { '-static' })" >> $env:GITHUB_OUTPUT - name: Cache vcpkg - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + uses: actions/cache@v4.0.2 id: cache-vcpkg with: path: build/vcpkg_cache/ - key: vcpkg-binaries-${{ steps.set-variables.outputs.vcpkg_triplet }} + key: vcpkg-binaries-${{ steps.set-variables.outputs.vcpkg_triplet }}-${{ hashFiles('vcpkg.json') }} - name: Create Build Environment if: ${{ !steps.cache-vcpkg.outputs.cache-hit }} @@ -36,11 +37,20 @@ jobs: cmake -E make_directory build cmake -E make_directory build/vcpkg_cache + - name: Boostrap vcpkg + shell: pwsh + working-directory: vcpkg/ + run: | + ./bootstrap-vcpkg.bat + ./vcpkg integrate install + - name: Configure shell: pwsh + env: + VCPKG_ROOT: ${{ github.workspace }}\vcpkg working-directory: build/ run: | - $vcpkgToolchain = Join-Path $env:VCPKG_INSTALLATION_ROOT '.\scripts\buildsystems\vcpkg.cmake' -Resolve + $vcpkgToolchain = Join-Path $env:VCPKG_ROOT './scripts/buildsystems/vcpkg.cmake' -Resolve $vcpkgTriplet = '${{ steps.set-variables.outputs.vcpkg_triplet }}' $cmakeSharedLibs = $(if ('${{ matrix.libs }}' -eq 'shared') { 'ON' } else { 'OFF' }) $msbuildArch = $(if ('${{ matrix.arch }}' -eq 'x64') { 'X64' } else { 'Win32' }) diff --git a/.gitignore b/.gitignore index 0f77c1a4..08458f10 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ CMakeSettings.json CMakeCache.txt CTestCostData.txt CTestTestfile.cmake +CMakeUserPresets.json DartConfiguration.tcl install_manifest.txt LastTest.log @@ -40,3 +41,4 @@ settings.json build/ install/ isenseconfig/ +vc140.pdb diff --git a/.gitmodules b/.gitmodules index 63c116a4..425cae62 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "PEGTL"] path = PEGTL url = https://github.com/taocpp/PEGTL.git +[submodule "vcpkg"] + path = vcpkg + url = https://github.com/microsoft/vcpkg.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f25e927..0b1f7a31 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,13 +1,12 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.28) -# Enable CMAKE_MSVC_RUNTIME_LIBRARY on Windows: https://cmake.org/cmake/help/latest/policy/CMP0091.html -cmake_policy(SET CMP0091 NEW) - -# Do not set default MSVC warning flags: https://cmake.org/cmake/help/latest/policy/CMP0092.html -cmake_policy(SET CMP0092 NEW) +if(POLICY CMP0167) + # Prefer the upstream BoostConfig.cmake file instead of the builtin FindBoost module: https://cmake.org/cmake/help/latest/policy/CMP0167.html + cmake_policy(SET CMP0167 NEW) +endif() # Export compile commands for other tools, e.g. SonarLint. set(CMAKE_EXPORT_COMPILE_COMMANDS ON) @@ -34,10 +33,11 @@ if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/.git") endif() endif() +option(GRAPHQL_BUILD_MODULES "Build the C++20 module interface libraries." ON) option(GRAPHQL_BUILD_SCHEMAGEN "Build the schemagen tool." ON) option(GRAPHQL_BUILD_CLIENTGEN "Build the clientgen tool." ON) option(GRAPHQL_BUILD_TESTS "Build the tests and sample schema library." ON) -option(GRAPHQL_USE_RAPIDJSON "Use RapidJSON for JSON serialization." ON) +option(GRAPHQL_USE_TAOCPP_JSON "Use taocpp-json for JSON serialization." ON) if(GRAPHQL_BUILD_SCHEMAGEN) list(APPEND VCPKG_MANIFEST_FEATURES "schemagen") @@ -51,8 +51,13 @@ if(GRAPHQL_BUILD_TESTS) list(APPEND VCPKG_MANIFEST_FEATURES "tests") endif() -if(GRAPHQL_USE_RAPIDJSON) - list(APPEND VCPKG_MANIFEST_FEATURES "rapidjson") +if(GRAPHQL_USE_TAOCPP_JSON) + list(APPEND VCPKG_MANIFEST_FEATURES "taocpp-json") +else() + option(GRAPHQL_USE_RAPIDJSON "Use RapidJSON for JSON serialization." ON) + if(GRAPHQL_USE_RAPIDJSON) + list(APPEND VCPKG_MANIFEST_FEATURES "rapidjson") + endif() endif() if(GRAPHQL_BUILD_SCHEMAGEN AND GRAPHQL_BUILD_CLIENTGEN) diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 00000000..942ec260 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,120 @@ +{ + "version": 8, + "configurePresets": [ + { + "hidden": true, + "name": "ninja-generator", + "binaryDir": "build/${presetName}", + "toolchainFile": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake", + "generator": "Ninja" + }, + { + "name": "debug", + "inherits": [ "ninja-generator" ], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "release", + "inherits": [ "ninja-generator" ], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "gcc-14-debug", + "inherits": [ "debug" ], + "condition": { + "type": "notEquals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + }, + "cacheVariables": { + "CMAKE_C_COMPILER": "/usr/bin/gcc-14", + "CMAKE_CXX_COMPILER": "/usr/bin/g++-14", + "GRAPHQL_BUILD_MODULES": false + } + }, + { + "name": "gcc-14-release", + "inherits": [ "gcc-14-debug" ], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "RelWithDebInfo" + } + }, + { + "name": "clang-18-debug", + "inherits": [ "debug" ], + "condition": { + "type": "notEquals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + }, + "cacheVariables": { + "CMAKE_C_COMPILER": "/usr/bin/clang-18", + "CMAKE_CXX_COMPILER": "/usr/bin/clang++-18", + "CMAKE_CXX_COMPILER_CLANG_SCAN_DEPS": "/usr/bin/clang-scan-deps-18" + } + }, + { + "name": "clang-18-release", + "inherits": [ "clang-18-debug" ], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + } + ], + "buildPresets": [ + { + "name": "debug", + "configurePreset": "debug" + }, + { + "name": "release", + "configurePreset": "release" + }, + { + "name": "gcc-14-debug", + "configurePreset": "gcc-14-debug" + }, + { + "name": "gcc-14-release", + "configurePreset": "gcc-14-release" + }, + { + "name": "clang-18-debug", + "configurePreset": "clang-18-debug" + }, + { + "name": "clang-18-release", + "configurePreset": "clang-18-release" + } + ], + "testPresets": [ + { + "name": "debug", + "configurePreset": "debug" + }, + { + "name": "release", + "configurePreset": "release" + }, + { + "name": "gcc-14-debug", + "configurePreset": "gcc-14-debug" + }, + { + "name": "gcc-14-release", + "configurePreset": "gcc-14-release" + }, + { + "name": "clang-18-debug", + "configurePreset": "clang-18-debug" + }, + { + "name": "clang-18-release", + "configurePreset": "clang-18-release" + } + ] +} \ No newline at end of file diff --git a/PEGTL b/PEGTL index cf639f7f..be527327 160000 --- a/PEGTL +++ b/PEGTL @@ -1 +1 @@ -Subproject commit cf639f7f4ee125f68e1ccfba8d99ebc0de57b9fe +Subproject commit be527327653e94b02e711f7eff59285ad13e1db0 diff --git a/README.md b/README.md index a3a0b6e8..52c0e004 100644 --- a/README.md +++ b/README.md @@ -230,6 +230,7 @@ There are some more targeted documents in the [doc](./doc) directory: * [Query Responses](./doc/responses.md) * [JSON Representation](./doc/json.md) * [Field Resolvers](./doc/resolvers.md) +* [Custom Scalar Payloads](./doc/scalars.md) * [Field Parameters](./doc/fieldparams.md) * [Directives](./doc/directives.md) * [Subscriptions](./doc/subscriptions.md) diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index 1db60cea..1fdaf4a5 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -1,7 +1,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.28) # Enable version checks in find_package include(CMakePackageConfigHelpers) diff --git a/cmake/Version.h.in b/cmake/Version.h.in index ce86ef57..711f874c 100644 --- a/cmake/Version.h.in +++ b/cmake/Version.h.in @@ -6,15 +6,20 @@ #ifndef GRAPHQLVERSION_H #define GRAPHQLVERSION_H +#include #include namespace graphql::internal { +inline namespace version { + constexpr std::string_view FullVersion { "@PROJECT_VERSION@" }; -constexpr size_t MajorVersion = @PROJECT_VERSION_MAJOR@; -constexpr size_t MinorVersion = @PROJECT_VERSION_MINOR@; -constexpr size_t PatchVersion = @PROJECT_VERSION_PATCH@; +constexpr std::size_t MajorVersion = @PROJECT_VERSION_MAJOR@; +constexpr std::size_t MinorVersion = @PROJECT_VERSION_MINOR@; +constexpr std::size_t PatchVersion = @PROJECT_VERSION_PATCH@; + +} // namespace version } // namespace graphql::internal diff --git a/cmake/cppgraphqlgen-functions.cmake b/cmake/cppgraphqlgen-functions.cmake index 1ad5c95d..fad90e0c 100644 --- a/cmake/cppgraphqlgen-functions.cmake +++ b/cmake/cppgraphqlgen-functions.cmake @@ -36,18 +36,37 @@ function(update_graphql_schema_files SCHEMA_TARGET SCHEMA_GRAPHQL SCHEMA_PREFIX DEPENDS ${SCHEMA_GRAPHQL} ${GRAPHQL_UPDATE_SCHEMA_FILES_SCRIPT} cppgraphqlgen::schemagen COMMENT "Generating ${SCHEMA_TARGET} GraphQL schema" VERBATIM) -endfunction() -function(add_graphql_schema_target SCHEMA_TARGET) add_custom_target(${SCHEMA_TARGET}_update_schema ALL DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${SCHEMA_TARGET}_schema_files) + file(REAL_PATH ${SCHEMA_GRAPHQL} SCHEMA_GRAPHQL BASE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + set_target_properties(${SCHEMA_TARGET}_update_schema PROPERTIES + SCHEMA_GRAPHQL ${SCHEMA_GRAPHQL} + SCHEMA_PREFIX ${SCHEMA_PREFIX} + SCHEMA_NAMESPACE ${SCHEMA_NAMESPACE}) +endfunction() +function(add_graphql_schema_target SCHEMA_TARGET) if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${SCHEMA_TARGET}_schema_files) file(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/${SCHEMA_TARGET}_schema_files SCHEMA_FILES) add_library(${SCHEMA_TARGET}_schema STATIC ${SCHEMA_FILES}) - add_dependencies(${SCHEMA_TARGET}_schema ${SCHEMA_TARGET}_update_schema) + if(TARGET ${SCHEMA_TARGET}_update_schema) + add_dependencies(${SCHEMA_TARGET}_schema ${SCHEMA_TARGET}_update_schema) + endif() + target_compile_features(${SCHEMA_TARGET}_schema PUBLIC cxx_std_20) target_include_directories(${SCHEMA_TARGET}_schema PUBLIC $) target_link_libraries(${SCHEMA_TARGET}_schema PUBLIC cppgraphqlgen::graphqlservice) + file(GLOB SCHEMA_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/*.h) + target_sources(${SCHEMA_TARGET}_schema PUBLIC FILE_SET HEADERS + BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} + FILES ${SCHEMA_HEADERS}) + get_target_property(GRAPHQL_BUILD_MODULES cppgraphqlgen::graphqlservice INTERFACE_CXX_MODULE_SETS) + if(GRAPHQL_BUILD_MODULES) + file(GLOB SCHEMA_MODULES ${CMAKE_CURRENT_SOURCE_DIR}/*.ixx) + target_sources(${SCHEMA_TARGET}_schema PUBLIC FILE_SET CXX_MODULES + BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} + FILES ${SCHEMA_MODULES}) + endif() endif() endfunction() @@ -78,17 +97,73 @@ function(update_graphql_client_files CLIENT_TARGET SCHEMA_GRAPHQL REQUEST_GRAPHQ DEPENDS ${SCHEMA_GRAPHQL} ${REQUEST_GRAPHQL} ${GRAPHQL_UPDATE_CLIENT_FILES_SCRIPT} cppgraphqlgen::clientgen COMMENT "Generating ${CLIENT_TARGET} client" VERBATIM) + + add_custom_target(${CLIENT_TARGET}_update_client ALL + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${CLIENT_TARGET}_client_files) endfunction() -function(add_graphql_client_target CLIENT_TARGET) +function(update_graphql_shared_client_files CLIENT_TARGET SCHEMA_TARGET REQUEST_GRAPHQL) + set_property(DIRECTORY APPEND + PROPERTY CMAKE_CONFIGURE_DEPENDS ${CLIENT_TARGET}_client_files) + get_target_property(SCHEMA_GRAPHQL ${SCHEMA_TARGET}_update_schema SCHEMA_GRAPHQL) + file(RELATIVE_PATH SCHEMA_GRAPHQL ${CMAKE_CURRENT_SOURCE_DIR} ${SCHEMA_GRAPHQL}) + get_target_property(SCHEMA_PREFIX ${SCHEMA_TARGET}_update_schema SCHEMA_PREFIX) + get_target_property(SCHEMA_NAMESPACE ${SCHEMA_TARGET}_update_schema SCHEMA_NAMESPACE) + + # Collect optional arguments + set(ADDITIONAL_CLIENTGEN_ARGS "--shared-types") + if(ARGC GREATER 4) + math(EXPR LAST_ARG "${ARGC} - 1") + foreach(ARGN RANGE 4 ${LAST_ARG}) + set(NEXT_ARG "${ARGV${ARGN}}") + list(APPEND ADDITIONAL_CLIENTGEN_ARGS "${NEXT_ARG}") + endforeach() + endif() + + add_custom_command( + OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/${CLIENT_TARGET}_client_files + COMMAND + ${CMAKE_COMMAND} "-DCLIENT_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}" + "-DCLIENT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}" + "-DCLIENTGEN_PROGRAM=$" "-DCLIENT_TARGET=${CLIENT_TARGET}" + "-DSCHEMA_GRAPHQL=${SCHEMA_GRAPHQL}" "-DREQUEST_GRAPHQL=${REQUEST_GRAPHQL}" + "-DCLIENT_PREFIX=${SCHEMA_PREFIX}" "-DCLIENT_NAMESPACE=${SCHEMA_NAMESPACE}" + "-DADDITIONAL_CLIENTGEN_ARGS=${ADDITIONAL_CLIENTGEN_ARGS}" + -P ${GRAPHQL_UPDATE_CLIENT_FILES_SCRIPT} + DEPENDS ${SCHEMA_GRAPHQL} ${REQUEST_GRAPHQL} ${GRAPHQL_UPDATE_CLIENT_FILES_SCRIPT} cppgraphqlgen::clientgen + COMMENT "Generating ${CLIENT_TARGET} client" + VERBATIM) + add_custom_target(${CLIENT_TARGET}_update_client ALL DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${CLIENT_TARGET}_client_files) + set_target_properties(${CLIENT_TARGET}_update_client PROPERTIES + SCHEMA_TARGET ${SCHEMA_TARGET}) +endfunction() +function(add_graphql_client_target CLIENT_TARGET) if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${CLIENT_TARGET}_client_files) file(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/${CLIENT_TARGET}_client_files CLIENT_FILES) add_library(${CLIENT_TARGET}_client STATIC ${CLIENT_FILES}) - add_dependencies(${CLIENT_TARGET}_client ${CLIENT_TARGET}_update_client) + if(TARGET ${CLIENT_TARGET}_update_client) + add_dependencies(${CLIENT_TARGET}_client ${CLIENT_TARGET}_update_client) + endif() + target_compile_features(${CLIENT_TARGET}_client PUBLIC cxx_std_20) target_include_directories(${CLIENT_TARGET}_client PUBLIC $) target_link_libraries(${CLIENT_TARGET}_client PUBLIC cppgraphqlgen::graphqlclient) + get_target_property(SCHEMA_TARGET ${CLIENT_TARGET}_update_client SCHEMA_TARGET) + if(SCHEMA_TARGET) + target_link_libraries(${CLIENT_TARGET}_client PUBLIC ${SCHEMA_TARGET}_schema) + endif() + file(GLOB CLIENT_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/*.h) + target_sources(${CLIENT_TARGET}_client PUBLIC FILE_SET HEADERS + BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} + FILES ${CLIENT_HEADERS}) + get_target_property(GRAPHQL_BUILD_MODULES cppgraphqlgen::graphqlclient INTERFACE_CXX_MODULE_SETS) + if(GRAPHQL_BUILD_MODULES) + file(GLOB CLIENT_MODULES ${CMAKE_CURRENT_SOURCE_DIR}/*.ixx) + target_sources(${CLIENT_TARGET}_client PUBLIC FILE_SET CXX_MODULES + BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} + FILES ${CLIENT_MODULES}) + endif() endif() endfunction() diff --git a/cmake/cppgraphqlgen-update-client-files.cmake b/cmake/cppgraphqlgen-update-client-files.cmake index 1ab6fcb1..c9d5934d 100644 --- a/cmake/cppgraphqlgen-update-client-files.cmake +++ b/cmake/cppgraphqlgen-update-client-files.cmake @@ -11,7 +11,7 @@ get_filename_component(REQUEST_GRAPHQL "${CLIENT_SOURCE_DIR}/${REQUEST_GRAPHQL}" file(MAKE_DIRECTORY ${CLIENT_BINARY_DIR}) # Cleanup all of the stale files in the binary directory -file(GLOB PREVIOUS_FILES ${CLIENT_BINARY_DIR}/*.h ${CLIENT_BINARY_DIR}/*.cpp +file(GLOB PREVIOUS_FILES ${CLIENT_BINARY_DIR}/*.h ${CLIENT_BINARY_DIR}/*.ixx ${CLIENT_BINARY_DIR}/*.cpp ${CLIENT_BINARY_DIR}/${CLIENT_TARGET}_client_files) foreach(PREVIOUS_FILE ${PREVIOUS_FILES}) file(REMOVE ${PREVIOUS_FILE}) @@ -30,7 +30,7 @@ execute_process( # Get the up-to-date list of files in the binary directory set(FILE_NAMES "") -file(GLOB NEW_FILES ${CLIENT_BINARY_DIR}/*.h ${CLIENT_BINARY_DIR}/*.cpp) +file(GLOB NEW_FILES ${CLIENT_BINARY_DIR}/*.h ${CLIENT_BINARY_DIR}/*.ixx ${CLIENT_BINARY_DIR}/*.cpp) foreach(NEW_FILE ${NEW_FILES}) get_filename_component(NEW_FILE ${NEW_FILE} NAME) list(APPEND FILE_NAMES "${NEW_FILE}") @@ -45,11 +45,11 @@ endif() cmake_policy(SET CMP0057 NEW) # Remove stale files in the source directory -file(GLOB OLD_FILES ${CLIENT_SOURCE_DIR}/*.h ${CLIENT_SOURCE_DIR}/*.cpp) +file(GLOB OLD_FILES ${CLIENT_SOURCE_DIR}/*.h ${CLIENT_SOURCE_DIR}/*.ixx ${CLIENT_SOURCE_DIR}/*.cpp) foreach(OLD_FILE ${OLD_FILES}) get_filename_component(OLD_FILE ${OLD_FILE} NAME) if(NOT OLD_FILE IN_LIST FILE_NAMES) - if(OLD_FILE MATCHES "Client\\.h$" OR OLD_FILE MATCHES "Client\\.cpp$") + if(OLD_FILE MATCHES "Client\\.h$" OR OLD_FILE MATCHES "Client\\.ixx$" OR OLD_FILE MATCHES "Client\\.cpp$") file(REMOVE "${CLIENT_SOURCE_DIR}/${OLD_FILE}") else() message(WARNING "Unexpected file in ${CLIENT_TARGET} client sources: ${OLD_FILE}") diff --git a/cmake/cppgraphqlgen-update-schema-files.cmake b/cmake/cppgraphqlgen-update-schema-files.cmake index 00d6fe06..44a840c2 100644 --- a/cmake/cppgraphqlgen-update-schema-files.cmake +++ b/cmake/cppgraphqlgen-update-schema-files.cmake @@ -10,7 +10,7 @@ get_filename_component(SCHEMA_GRAPHQL "${SCHEMA_SOURCE_DIR}/${SCHEMA_GRAPHQL}" A file(MAKE_DIRECTORY ${SCHEMA_BINARY_DIR}) # Cleanup all of the stale files in the binary directory -file(GLOB PREVIOUS_FILES ${SCHEMA_BINARY_DIR}/*.h ${SCHEMA_BINARY_DIR}/*.cpp +file(GLOB PREVIOUS_FILES ${SCHEMA_BINARY_DIR}/*.h ${SCHEMA_BINARY_DIR}/*.ixx ${SCHEMA_BINARY_DIR}/*.cpp ${SCHEMA_BINARY_DIR}/${SCHEMA_TARGET}_schema_files) foreach(PREVIOUS_FILE ${PREVIOUS_FILES}) file(REMOVE ${PREVIOUS_FILE}) @@ -29,7 +29,7 @@ execute_process( # Get the up-to-date list of files in the binary directory set(FILE_NAMES "") -file(GLOB NEW_FILES ${SCHEMA_BINARY_DIR}/*.h ${SCHEMA_BINARY_DIR}/*.cpp) +file(GLOB NEW_FILES ${SCHEMA_BINARY_DIR}/*.h ${SCHEMA_BINARY_DIR}/*.ixx ${SCHEMA_BINARY_DIR}/*.cpp) foreach(NEW_FILE ${NEW_FILES}) get_filename_component(NEW_FILE ${NEW_FILE} NAME) list(APPEND FILE_NAMES "${NEW_FILE}") @@ -44,14 +44,18 @@ endif() cmake_policy(SET CMP0057 NEW) # Remove stale files in the source directory -file(GLOB OLD_FILES ${SCHEMA_SOURCE_DIR}/*.h ${SCHEMA_SOURCE_DIR}/*.cpp) +file(GLOB OLD_FILES ${SCHEMA_SOURCE_DIR}/*.h ${SCHEMA_SOURCE_DIR}/*.ixx ${SCHEMA_SOURCE_DIR}/*.cpp) foreach(OLD_FILE ${OLD_FILES}) get_filename_component(OLD_FILE ${OLD_FILE} NAME) if(NOT OLD_FILE IN_LIST FILE_NAMES) - if(OLD_FILE MATCHES "Object\\.h$" OR OLD_FILE MATCHES "Object\\.cpp$") + if(OLD_FILE MATCHES "Object\\.h$" OR OLD_FILE MATCHES "Object\\.ixx$" OR OLD_FILE MATCHES "Object\\.cpp$") file(REMOVE "${SCHEMA_SOURCE_DIR}/${OLD_FILE}") elseif(NOT OLD_FILE STREQUAL "${SCHEMA_PREFIX}Schema.h" AND - NOT OLD_FILE STREQUAL "${SCHEMA_PREFIX}Schema.cpp") + NOT OLD_FILE STREQUAL "${SCHEMA_PREFIX}Schema.ixx" AND + NOT OLD_FILE STREQUAL "${SCHEMA_PREFIX}Schema.cpp" AND + NOT OLD_FILE STREQUAL "${SCHEMA_PREFIX}SharedTypes.h" AND + NOT OLD_FILE STREQUAL "${SCHEMA_PREFIX}SharedTypes.ixx" AND + NOT OLD_FILE STREQUAL "${SCHEMA_PREFIX}SharedTypes.cpp") message(WARNING "Unexpected file in ${SCHEMA_TARGET} GraphQL schema sources: ${OLD_FILE}") endif() endif() diff --git a/cmake/test_boost_beast.cpp b/cmake/test_boost_beast.cpp deleted file mode 100644 index b59b338a..00000000 --- a/cmake/test_boost_beast.cpp +++ /dev/null @@ -1,15 +0,0 @@ -// This is a dummy program that just needs to compile to tell us if Boost.Asio -// supports co_await and Boost.Beast is installed. - -#include - -#include - -int main() -{ -#ifdef BOOST_ASIO_HAS_CO_AWAIT - return 0; -#else - #error BOOST_ASIO_HAS_CO_AWAIT is undefined -#endif -} \ No newline at end of file diff --git a/cmake/test_coroutine.cpp.in b/cmake/test_coroutine.cpp.in deleted file mode 100644 index 9ea6c5ac..00000000 --- a/cmake/test_coroutine.cpp.in +++ /dev/null @@ -1,59 +0,0 @@ -// This is a dummy program that just needs to compile and link to tell us if -// the C++20 coroutine API is available. Use CMake's configure_file command -// to replace the COROUTINE_HEADER and COROUTINE_NAMESPACE tokens for each -// combination of headers and namespaces which we want to pass to the CMake -// try_compile command. - -#include <@COROUTINE_HEADER@> -#include - -struct task -{ - struct promise_type - { - task get_return_object() noexcept - { - return {}; - } - - @COROUTINE_NAMESPACE@::suspend_never initial_suspend() noexcept - { - return {}; - } - - @COROUTINE_NAMESPACE@::suspend_never final_suspend() noexcept - { - return {}; - } - - void return_void() noexcept - { - promise.set_value(); - } - - void unhandled_exception() - { - promise.set_exception(std::current_exception()); - } - - std::promise promise; - }; - - constexpr bool await_ready() const noexcept - { - return true; - } - - std::future future; -}; - -task test_co_return() -{ - co_return; -} - -int main() -{ - test_co_return().future.get(); - return 0; -} \ No newline at end of file diff --git a/cmake/test_filesystem.cpp b/cmake/test_filesystem.cpp deleted file mode 100644 index 240e7d6b..00000000 --- a/cmake/test_filesystem.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// This is a dummy program that just needs to compile and link to tell us if -// the C++17 std::filesystem API requires any additional libraries. - -#include - -int main() -{ - try - { - throw std::filesystem::filesystem_error("instantiate one to make sure it links", - std::make_error_code(std::errc::function_not_supported)); - } - catch (const std::filesystem::filesystem_error& error) - { - return -1; - } - - return !std::filesystem::temp_directory_path().is_absolute(); -} \ No newline at end of file diff --git a/cmake/version.txt b/cmake/version.txt index 94bc0a3e..28cbf7c0 100644 --- a/cmake/version.txt +++ b/cmake/version.txt @@ -1 +1 @@ -4.5.9 \ No newline at end of file +5.0.0 \ No newline at end of file diff --git a/doc/awaitable.md b/doc/awaitable.md index e22a0454..4d13a6bc 100644 --- a/doc/awaitable.md +++ b/doc/awaitable.md @@ -16,7 +16,7 @@ private: virtual ~Concept() = default; [[nodiscard("unexpected call")]] virtual bool await_ready() const = 0; - virtual void await_suspend(coro::coroutine_handle<> h) const = 0; + virtual void await_suspend(std::coroutine_handle<> h) const = 0; virtual void await_resume() const = 0; }; ... @@ -32,7 +32,7 @@ public: // Default to immediate synchronous execution. await_async() : _pimpl { std::static_pointer_cast( - std::make_shared>(std::make_shared())) } + std::make_shared>(std::make_shared())) } { } @@ -41,8 +41,8 @@ public: : _pimpl { ((launch & std::launch::async) == std::launch::async) ? std::static_pointer_cast(std::make_shared>( std::make_shared())) - : std::static_pointer_cast(std::make_shared>( - std::make_shared())) } + : std::static_pointer_cast(std::make_shared>( + std::make_shared())) } { } ... @@ -51,12 +51,12 @@ public: For convenience, it will use `graphql::service::await_worker_thread` if you specify `std::launch::async`, which should have the same behavior as calling `std::async(std::launch::async, ...)` did before. -If you specify any other flags for `std::launch`, it does not honor them. It will use `coro::suspend_never` +If you specify any other flags for `std::launch`, it does not honor them. It will use `std::suspend_never` (an alias for `std::suspend_never` or `std::experimental::suspend_never`), which as the name suggests, continues executing the coroutine without suspending. In other words, `std::launch::deferred` will no longer defer execution as in previous versions, it will execute immediately. -There is also a default constructor which also uses `coro::suspend_never`, so that is the default +There is also a default constructor which also uses `std::suspend_never`, so that is the default behavior anywhere that `await_async` is default-initialized with `{}`. Other than simplification, the big advantage this brings is in the type-erased template constructor. @@ -68,7 +68,7 @@ coroutine when and where it likes. ## Awaitable Results Many APIs which used to return some sort of `std::future` now return an alias for -`graphql::internal::Awaitable<...>`. This template is defined in [Awaitable.h](../include/graphqlservice/internal/Awaitable.h): +`graphql::internal::Awaitable<...>`. This template is defined in [Awaitable.ixx](../include/graphqlservice/internal/Awaitable.ixx): ```cpp template class [[nodiscard("unnecessary construction")]] Awaitable @@ -110,7 +110,7 @@ public: return true; } - void await_suspend(coro::coroutine_handle<> h) const + void await_suspend(std::coroutine_handle<> h) const { h.resume(); } @@ -189,7 +189,7 @@ public: bool await_ready() const noexcept { ... } - void await_suspend(coro::coroutine_handle<> h) const { ... } + void await_suspend(std::coroutine_handle<> h) const { ... } T await_resume() { @@ -245,7 +245,7 @@ public: bool await_ready() const noexcept { ... } - void await_suspend(coro::coroutine_handle<> h) const { ... } + void await_suspend(std::coroutine_handle<> h) const { ... } T await_resume() { ... } diff --git a/doc/getting_started.md b/doc/getting_started.md deleted file mode 100644 index 0fc1b4b9..00000000 --- a/doc/getting_started.md +++ /dev/null @@ -1,266 +0,0 @@ -# Getting Started - -# About -This simple tutorial aims at getting you started using `cppgraphqlgen`, even though it will not cover all the feature provided by both GraphQL and cppgraphqlgen; it will get you started, allowing you to understand the basic usage and concepts behind this library. - -The final goal is to write a basic system, which given an input *query string* will return an *output in Json format*. - -# The Schema -The first step is to create a GraphQL schema; said schema will be provided to *schemagen* to produce the equivalent *C++ Schema Representation*, more details about how to use this will be shown later through the tutorial. - -For this getting started guide the schema provided will be fairly simple, it will contain only a query which will return a list of strings and a list of object, given a certain optional input parameter. - -Below the schema in question: - -``` GraphQL Schema -type Thing { - description: String! - id: Int! -} - -type Query { - names: [String!]! - stuff(id: String = null): [Thing!]! -} -``` - -## Generating the C++ Schema Code -In order to generate the schema code you need to use the previously mentioned schemagen. This can be done either by leveraging the CMake helper function or by invoking schemagen from the command line directly, for example: - -``` -schemagen mySchema.graphql prefix gsm -``` - -*Arguments (invoke schemagen with `--help` for a full list of instructions)*: - 1. File name of the GraphQL Schema (code above) - 2. String to prefix on the name of the generated files - 3. Custom namespace to add *(it will be wrapped between `graphql::` and `::object`)* - -If you provide the same schema as this tutorial, the generated files will be (each with a `.h` and `.cpp`): -- *prefixSchema* -- *QueryObject* -- *ThingObject* - -*Note: you will not need to edit these files, as indicated by the warning at the top of each generated file; however this tutorial will guide you towards reading them to understand how to implement your code.* - -With the C++ Schema code generated, you can move on to the next section. - -# Implementation -The basic premise of cppgraphqlgen is to provide an `std::shared_ptr` containing a custom class that you will be writing, as an input to the various classes generated by schemagen. - -In order for your classes to be *"compatible"* they need to contain certain methods which have to satisfy a specific function signature in terms of name, return value and arguments. - -The code generated by schemagen will provide a compile time error (unless the `--stubs` option was provided to schemagen, in which case they will be runtime exceptions) in case it doesn't find the correct function signature by stating that the *"`Function XYZ is not implemented`"*. - -The best way to check the required function signature is to open the *header file* of the corresponding object generated by schemagen and search for the word `static_assert`, each match will be referred to a function that needs to be implemented in a certain way, but more on that later. - -## The Query Object & the Thing Object -The query object is the entry point of your GraphQL query, every query starts from the query object you defined, therefore that will also be the entry point that cppgraphqlgen will leverage to execute your query. - -This object will be provided to the `Operations` object, which once initialized will return a service to execute your queries and return an output, more on that later. - -Your implementation of the query object itself, like for other objects, *is a class which does not inherit the schemagen code nor does it implement some virtual functions*. -Strictly speaking, the only requirements are the function that the generated schemagen code checks for, therefore, in the case of our sample schema, it will be two methods: one to retrieve *names* and one to retrieve *stuff*. - -By exploring the schemagen `QueryObject.h` file and searching `static_assert` you can notice how there will be two matches, reported below: - -``` cpp -[[nodiscard("unnecessary call")]] service::AwaitableScalar> getNames(service::FieldParams&& params) const override -{ - if constexpr (methods::QueryHas::getNamesWithParams) - { - return { _pimpl->getNames(std::move(params)) }; - } - else - { - static_assert(methods::QueryHas::getNames, R"msg(Query::getNames is not implemented)msg"); - return { _pimpl->getNames() }; - } -} -[[nodiscard("unnecessary call")]] service::AwaitableObject>> getStuff(service::FieldParams&& params, std::optional&& idArg) const override -{ - if constexpr (methods::QueryHas::getStuffWithParams) - { - return { _pimpl->getStuff(std::move(params), std::move(idArg)) }; - } - else - { - static_assert(methods::QueryHas::getStuff, R"msg(Query::getStuff is not implemented)msg"); - return { _pimpl->getStuff(std::move(idArg)) }; - } -} -``` - -these two blocks of code are responsible for checking whether you class, of which instance you will be providing to this object, satisfies the requirement of having certain methods. - -For example, in this case you can see how two methods are required, one is `getStuff` and one is `getNames`, and they belong to the class `Query`. -The name `Query` comes from the fact that it's a query, while `getNames` and `getStuff` are made by prefixing `get` to `name` and `stuff`. -*This can be useful to remember, but it's not important since the compiler error, and this code, will remind you.* - -Other useful information are the *arguments* and *return type.* - -***Get Names Signature*** -If we take as an example `getNames` we can see how the return type is indicated in the function signature, it returns a `service::AwaitableScalar>`, meaning that our code will have to return a `std::vector`; which makes sense, considering how schema returns an array of string that is *mandatory*, as in, the query *must return the field with something*. -The method itself is called like this `_pimpl->getNames()`, without any parameters; which makes sense since we didn't specify any on our original GraphQL schema. - -***Get Stuff Signature*** -Another, slightly different example is `getStuff`, from our own specification this GraphQL field: - - Can take an *optional string argument called id* - - *Must return a vector of Things* -As we can see our requirements have been reflected in the function signature. -The return value is `service::AwaitableObject>>`, this means that we *must* to return a `>`; once again it's a vector since the GraphQL schema declared a list. -You can also note how this non scalar value (a custom class) is wrapped in a shared pointer. - -Next we can notice how an argument is passed to the function call itself: `_pimpl->getStuff(std::move(idArg))`; if we read the definition of `idArg` (which takes it's name by the `id` declaration on GraphQL) we can see it as follows: `std::optional&& idArg`. -Notice how it's a string wrapped in an `optional` object, since the parameter itself was declared as optional on the GraphQL schema. - -***Full Class Implementation*** -In order to therefore satisfy the cppgraphqlgen Query requirements, we need to provide a class with the methods specified above; the implementation itself is yours to decide depends on your need. - -Here is a sample class declaration and implementation -``` cpp -#include "QueryObject.h" - -namespace mod_graphql::mock ... -using namespace graphql; -// Declaration -class Query { -public: - explicit Query() noexcept; - - std::vector getNames() const noexcept; - std::vector> - getStuff(std::optional &&idArg) const noexcept; -}; - -// Definition -Query::Query() noexcept {} - -std::vector Query::getNames() const noexcept { - // Some mock code - auto names = std::vector(); - names.push_back("Name 1"); - names.push_back("Name 2"); - names.push_back("Name 3"); - return names; -} - -std::vector> -Query::getStuff(std::optional &&idArg) const noexcept { - // Some mock code - auto stuff = std::vector>(); - auto thing_ptr = std::make_shared(0, "Sample Description!"); - stuff.push_back(std::make_shared<::graphql::gsm::object::Thing>(thing_ptr)); - return stuff; -} -``` - -*Note: this is more very basic code to show you the bare minimum to implement a compatible class, for more examples you can check the `samples/` directory.* - -As you may have noticed, the *getStuff* method returns an instance of `object::Thing`, which is the class defined by cppgraphqlgen; and by the method definition you can see how `object::Thing` is initialized by passing a `shared_ptr` of type `Thing` (*the class we will be defining shortly*). -This is basically the logic behind the Query class we have just implemented, and you can see the same `static_assert` guidance on the relative header file. - -In this case, the `Thing` class has been implemented as such: -``` cpp -#include "ThingObject.h" - -namespace mod_graphql::mock ... -using namespace graphql; -// Declaration -class Thing { -public: - explicit Thing(int id, std::string description) noexcept; - - const int getId() const noexcept; - const std::string getDescription() const noexcept; - -private: - const int id; - const std::string description; -}; - -// Definition -Thing::Thing(int id, std::string description) noexcept - : id{id}, description{description} {}; - -const int Thing::getId() const noexcept { return id; } - -const std::string Thing::getDescription() const noexcept { return description; } -``` - -As you can imagine, `getId` and `getDescription` match the requirements of `ThingObject.h`; though in this case they can just return a *string* and an *int* since they do not return a complex class. - -## Initializing the Service -Once you have defined all the necessary classes you can create a service. - -To do this you need to provide a `shared_ptr` to an instance of your custom `Query` class to `Operations`, for example: -```cpp -#include "gsmSchema.h" -#include -#include - -auto query = std::make_shared(); -auto service = std::make_shared<::graphql::gsm::Operations>(query); -``` - -The `Operations` class may require more parameters, depending for example, on whether you specified a mutation on the schema or not. -The best way to check the required input is by reading constructor signature in the `gsmSchema.h` equivalent file. - -## Executing Queries -Once the service is ready you can execute queries. - -A query however needs to be parsed by the `graphql::peg::parseString` (or equivalent file, etc. functions) function into a `graphql::peg::ast` object. - -This object can then be given to the service `resolve` method, which will return an object that can be parsed by the `graphql::response::toJSON` method, to provide a final result string. - -These methods may throw an exception, therefore you need to wrap them in a `try/catch` block. -Below an example: - -```cpp -// Previous service init code... - -std::string final_output = "Empty Result!"; -try { - ::graphql::peg::ast query_ast = ::graphql::peg::parseString(query_input); - final_output = - ::graphql::response::toJSON(service->resolve({query_ast, ""}).get()); -} catch (std::exception &e) { - std::cerr << e.what() << std::endl; -} -return std::string(final_output); -``` - -In this case `final_output` will be the final `GraphQL` response which can be given to a client. - -If you followed this tutorial you should be able to provide as input the following query: -``` GraphQL -query GetShit{ - names - stuff(id: "0") { - description - } -} -``` - -and get the following result inside `final_output` -```cpp -{ - "data": - { - "names": - [ - "Name 1", - "Name 2", - "Name 3" - ], - "stuff": - [ - { "description" : "Sample Description!" } - ] - } -} -``` -which reflects the test data and hardcoded strings we placed in the code. - -## Next Steps -With a now clearer image of the cppgraphqlgen library, you can try exploring the `samples` directory for a practical implementation of more features, such as mutations. diff --git a/doc/json.md b/doc/json.md index 4227683a..49cd7827 100644 --- a/doc/json.md +++ b/doc/json.md @@ -58,7 +58,7 @@ private: virtual void end_object() const = 0; virtual void start_array() const = 0; - virtual void end_arrary() const = 0; + virtual void end_array() const = 0; virtual void write_null() const = 0; virtual void write_string(const std::string& value) const = 0; diff --git a/doc/responses.md b/doc/responses.md index 079d18c6..d328f5cf 100644 --- a/doc/responses.md +++ b/doc/responses.md @@ -41,8 +41,8 @@ specializations. ## Map and List -`Map` and `List` types enable collection methods like `reserve(size_t)`, +`Map` and `List` types enable collection methods like `reserve(std::size_t)`, `size()`, and `emplace_back(...)`. `Map` additionally implements `begin()` and `end()` for range-based for loops and `find(const std::string&)` and `operator[](const std::string&)` for key-based lookups. `List` has an -`operator[](size_t)` for index-based instead of key-based lookups. \ No newline at end of file +`operator[](std::size_t)` for index-based instead of key-based lookups. \ No newline at end of file diff --git a/doc/scalars.md b/doc/scalars.md new file mode 100644 index 00000000..d4169f7c --- /dev/null +++ b/doc/scalars.md @@ -0,0 +1,68 @@ +# Custom Scalar Payloads with `AnyScalar` + +A custom `scalar` type in GraphQL IDL (e.g. `scalar DateTime`) is represented by +`schemagen` as a plain `graphql::response::Value`, and its generated resolver +signature never changes. Sometimes, though, the natural C++ representation of a +custom scalar can't be expressed with any of `response::Value`'s built-in +alternatives — for example a `BigInt` backed by `std::int64_t`, which is +too large for the 32-bit `response::IntType`. + +`response::AnyScalar` lets a hand-written resolver embed an arbitrary +`std::any` payload as the value of a scalar field, along with a type-erased +serializer callback that knows how to turn that payload into calls on a +`response::ValueVisitor`: + +```c++ +struct [[nodiscard("unnecessary construction")]] AnyScalar +{ + using Serializer = + std::function&)>; + + std::any value; + Serializer serialize; +}; +``` + +This is purely a runtime extension: it does not touch the schema IDL, +`schemagen`, or any GraphQL directive, and the wire format is still ordinary +JSON produced by whatever the serializer calls on the visitor +(`add_string`, `add_int`, `add_bool`, `start_object`, ...). + +## Opting In + +An implementer constructs a `response::Value` from an `AnyScalar`. The payload +and serializer are stored on the value; when the response is serialized, the +token stream calls the serializer with the visitor for the active JSON backend: + +```c++ +struct Int64Scalar +{ + std::int64_t value = 0; +}; + +response::Value getBigInt() +{ + return response::Value { response::AnyScalar { + std::any { Int64Scalar { 9223372036854775807LL } }, + [](const std::any& payload, + const std::shared_ptr& visitor) { + const auto& scalar = std::any_cast(payload); + // Serialize on the wire as an ordinary JSON string. + visitor->add_string(std::to_string(scalar.value)); + }, + } }; +} +``` + +`response::toJSON(getBigInt())` yields `"9223372036854775807"`. + +## Accessors + +* `bool Value::isAny() const` returns `true` if the value is a `Type::Scalar` + holding an `AnyScalar` payload. +* `SharedAnyScalar Value::releaseAny()` moves the shared payload out of the + value. + +Copying a `Value` that holds an `AnyScalar` shares ownership of the same +payload (no deep copy), and equality compares payloads by shared-pointer +identity. diff --git a/include/ClientGenerator.h b/include/ClientGenerator.h index 6083e526..a6109108 100644 --- a/include/ClientGenerator.h +++ b/include/ClientGenerator.h @@ -9,6 +9,8 @@ #include "RequestLoader.h" #include "SchemaLoader.h" +#include + namespace graphql::generator::client { struct [[nodiscard("unnecessary construction")]] GeneratorPaths @@ -27,9 +29,8 @@ class [[nodiscard("unnecessary construction")]] Generator { public: // Initialize the generator with the introspection client or a custom GraphQL client. - explicit Generator(SchemaOptions && schemaOptions, - RequestOptions && requestOptions, - GeneratorOptions && options); + explicit Generator( + SchemaOptions&& schemaOptions, RequestOptions&& requestOptions, GeneratorOptions&& options); // Run the generator and return a list of filenames that were output. [[nodiscard("unnecessary memory copy")]] std::vector Build() const noexcept; @@ -38,31 +39,80 @@ class [[nodiscard("unnecessary construction")]] Generator [[nodiscard("unnecessary memory copy")]] std::string getHeaderDir() const noexcept; [[nodiscard("unnecessary memory copy")]] std::string getSourceDir() const noexcept; [[nodiscard("unnecessary memory copy")]] std::string getHeaderPath() const noexcept; + [[nodiscard("unnecessary memory copy")]] std::string getModulePath() const noexcept; [[nodiscard("unnecessary memory copy")]] std::string getSourcePath() const noexcept; [[nodiscard("unnecessary call")]] const std::string& getClientNamespace() const noexcept; [[nodiscard("unnecessary call")]] const std::string& getOperationNamespace( const Operation& operation) const noexcept; [[nodiscard("unnecessary memory copy")]] std::string getResponseFieldCppType( - const ResponseField& responseField, - std::string_view currentScope = {}) const noexcept; + const ResponseField& responseField, std::string_view currentScope = {}) const noexcept; [[nodiscard("unnecessary call")]] bool outputHeader() const noexcept; - void outputRequestComment(std::ostream & headerFile) const noexcept; - void outputGetRequestDeclaration(std::ostream & headerFile) const noexcept; - void outputGetOperationNameDeclaration(std::ostream & headerFile) const noexcept; - [[nodiscard("unnecessary call")]] bool outputResponseFieldType(std::ostream & headerFile, - const ResponseField& responseField, - size_t indent = 0) const noexcept; + void outputRequestComment(std::ostream& headerFile) const noexcept; + void outputGetRequestDeclaration(std::ostream& headerFile) const noexcept; + void outputGetOperationNameDeclaration(std::ostream& headerFile) const noexcept; + [[nodiscard("unnecessary call")]] bool outputResponseFieldType(std::ostream& headerFile, + const ResponseField& responseField, std::size_t indent = 0) const noexcept; + + [[nodiscard("unnecessary call")]] bool outputModule() const noexcept; [[nodiscard("unnecessary call")]] bool outputSource() const noexcept; - void outputGetRequestImplementation(std::ostream & sourceFile) const noexcept; - void outputGetOperationNameImplementation(std::ostream & sourceFile, const Operation& operation) - const noexcept; - bool outputModifiedResponseImplementation(std::ostream & sourceFile, - const std::string& outerScope, - const ResponseField& responseField) const noexcept; + void outputGetRequestImplementation(std::ostream& sourceFile) const noexcept; + void outputGetOperationNameImplementation( + std::ostream& sourceFile, const Operation& operation) const noexcept; + bool outputModifiedResponseImplementation(std::ostream& sourceFile, + const std::string& outerScope, const ResponseField& responseField) const noexcept; [[nodiscard("unnecessary memory copy")]] static std::string getTypeModifierList( const TypeModifierStack& modifiers) noexcept; + void outputResponseFieldVisitorStates(std::ostream& sourceFile, + const ResponseField& responseField, std::string_view parent = {}) const noexcept; + void outputResponseFieldVisitorAddValue(std::ostream& sourceFile, + const ResponseField& responseField, bool arrayElement = false, + std::string_view parentState = {}, std::string_view parentAccessor = {}, + std::string_view parentCppType = {}) const noexcept; + void outputResponseFieldVisitorReserve(std::ostream& sourceFile, + const ResponseField& responseField, std::string_view parentState = {}, + std::string_view parentAccessor = {}, std::string_view parentCppType = {}) const noexcept; + void outputResponseFieldVisitorStartObject(std::ostream& sourceFile, + const ResponseField& responseField, std::string_view parentState = {}, + std::string_view parentAccessor = {}, std::string_view parentCppType = {}) const noexcept; + void outputResponseFieldVisitorAddMember(std::ostream& sourceFile, + const ResponseFieldList& children, bool arrayElement = false, + std::string_view parentState = {}) const noexcept; + void outputResponseFieldVisitorEndObject(std::ostream& sourceFile, + const ResponseField& responseField, bool arrayElement = false, + std::string_view parentState = {}) const noexcept; + void outputResponseFieldVisitorStartArray(std::ostream& sourceFile, + const ResponseField& responseField, std::string_view parentState = {}, + std::string_view parentAccessor = {}, std::string_view parentCppType = {}) const noexcept; + void outputResponseFieldVisitorEndArray(std::ostream& sourceFilearrayElement, + const ResponseField& responseField, bool arrayElement = false, + std::string_view parentState = {}) const noexcept; + void outputResponseFieldVisitorAddNull(std::ostream& sourceFilearrayElement, + const ResponseField& responseField, bool arrayElement = false, + std::string_view parentState = {}, std::string_view parentAccessor = {}) const noexcept; + void outputResponseFieldVisitorAddMovedValue(std::ostream& sourceFile, + const ResponseField& responseField, std::string_view movedCppType, + bool arrayElement = false, std::string_view parentState = {}, + std::string_view parentAccessor = {}) const noexcept; + void outputResponseFieldVisitorAddString( + std::ostream& sourceFile, const ResponseField& responseField) const noexcept; + void outputResponseFieldVisitorAddEnum(std::ostream& sourceFile, + const ResponseField& responseField, bool arrayElement = false, + std::string_view parentState = {}, std::string_view parentAccessor = {}, + std::string_view parentCppType = {}) const noexcept; + void outputResponseFieldVisitorAddId( + std::ostream& sourceFile, const ResponseField& responseField) const noexcept; + void outputResponseFieldVisitorAddCopiedValue(std::ostream& sourceFile, + const ResponseField& responseField, std::string_view copiedCppType, + bool arrayElement = false, std::string_view parentState = {}, + std::string_view parentAccessor = {}) const noexcept; + void outputResponseFieldVisitorAddBool( + std::ostream& sourceFile, const ResponseField& responseField) const noexcept; + void outputResponseFieldVisitorAddInt( + std::ostream& sourceFile, const ResponseField& responseField) const noexcept; + void outputResponseFieldVisitorAddFloat( + std::ostream& sourceFile, const ResponseField& responseField) const noexcept; const SchemaLoader _schemaLoader; const RequestLoader _requestLoader; @@ -70,6 +120,7 @@ class [[nodiscard("unnecessary construction")]] Generator const std::string _headerDir; const std::string _sourceDir; const std::string _headerPath; + const std::string _modulePath; const std::string _sourcePath; }; diff --git a/include/RequestLoader.h b/include/RequestLoader.h index 8ec05d3c..96911e9b 100644 --- a/include/RequestLoader.h +++ b/include/RequestLoader.h @@ -84,6 +84,7 @@ struct [[nodiscard("unnecessary construction")]] RequestOptions const std::string requestFilename; const std::optional operationName; const bool noIntrospection = false; + const bool sharedTypes = false; }; class SchemaLoader; @@ -91,16 +92,16 @@ class SchemaLoader; class [[nodiscard("unnecessary construction")]] RequestLoader { public: - explicit RequestLoader(RequestOptions && requestOptions, const SchemaLoader& schemaLoader); + explicit RequestLoader(RequestOptions&& requestOptions, const SchemaLoader& schemaLoader); [[nodiscard("unnecessary call")]] std::string_view getRequestFilename() const noexcept; [[nodiscard("unnecessary call")]] const OperationList& getOperations() const noexcept; [[nodiscard("unnecessary call")]] std::string_view getOperationDisplayName( const Operation& operation) const noexcept; - [[nodiscard("unnecessary call")]] std::string getOperationNamespace(const Operation& operation) - const noexcept; - [[nodiscard("unnecessary call")]] std::string_view getOperationType(const Operation& operation) - const noexcept; + [[nodiscard("unnecessary call")]] std::string getOperationNamespace( + const Operation& operation) const noexcept; + [[nodiscard("unnecessary call")]] std::string_view getOperationType( + const Operation& operation) const noexcept; [[nodiscard("unnecessary call")]] std::string_view getRequestText() const noexcept; [[nodiscard("unnecessary call")]] const ResponseType& getResponseType( @@ -108,6 +109,7 @@ class [[nodiscard("unnecessary construction")]] RequestLoader [[nodiscard("unnecessary call")]] const RequestVariableList& getVariables( const Operation& operation) const noexcept; + [[nodiscard("unnecessary call")]] bool useSharedTypes() const noexcept; [[nodiscard("unnecessary call")]] const RequestInputTypeList& getReferencedInputTypes( const Operation& operation) const noexcept; [[nodiscard("unnecessary call")]] const RequestSchemaTypeList& getReferencedEnums( @@ -116,20 +118,18 @@ class [[nodiscard("unnecessary construction")]] RequestLoader [[nodiscard("unnecessary call")]] std::string getInputCppType( const RequestSchemaType& wrappedInputType) const noexcept; [[nodiscard("unnecessary call")]] std::string getInputCppType( - const RequestSchemaType& inputType, - const TypeModifierStack& modifiers) const noexcept; + const RequestSchemaType& inputType, const TypeModifierStack& modifiers) const noexcept; [[nodiscard("unnecessary call")]] static std::string getOutputCppType( - std::string_view outputCppType, - const TypeModifierStack& modifiers) noexcept; + std::string_view outputCppType, const TypeModifierStack& modifiers) noexcept; [[nodiscard("unnecessary call")]] static std::pair - unwrapSchemaType(RequestSchemaType && type) noexcept; + unwrapSchemaType(RequestSchemaType&& type) noexcept; private: void buildSchema(); void addTypesToSchema(); - [[nodiscard("unnecessary call")]] RequestSchemaType getSchemaType(std::string_view type, - const TypeModifierStack& modifiers) const noexcept; + [[nodiscard("unnecessary call")]] RequestSchemaType getSchemaType( + std::string_view type, const TypeModifierStack& modifiers) const noexcept; void validateRequest() const; [[nodiscard("unnecessary call")]] static std::string_view trimWhitespace( @@ -137,11 +137,11 @@ class [[nodiscard("unnecessary construction")]] RequestLoader void findOperation(); void collectFragments() noexcept; - void collectVariables(Operation & operation) noexcept; - void collectInputTypes(Operation & operation, const RequestSchemaType& variableType) noexcept; - void reorderInputTypeDependencies(Operation & operation); - void collectEnums(Operation & operation, const RequestSchemaType& variableType) noexcept; - void collectEnums(Operation & operation, const ResponseField& responseField) noexcept; + void collectVariables(Operation& operation) noexcept; + void collectInputTypes(Operation& operation, const RequestSchemaType& variableType) noexcept; + void reorderInputTypeDependencies(Operation& operation); + void collectEnums(Operation& operation, const RequestSchemaType& variableType) noexcept; + void collectEnums(Operation& operation, const ResponseField& responseField) noexcept; using FragmentDefinitionMap = std::map; @@ -150,8 +150,7 @@ class [[nodiscard("unnecessary construction")]] RequestLoader { public: explicit SelectionVisitor(const SchemaLoader& schemaLoader, - const FragmentDefinitionMap& fragments, - const std::shared_ptr& schema, + const FragmentDefinitionMap& fragments, const std::shared_ptr& schema, const RequestSchemaType& type); void visit(const peg::ast_node& selection); @@ -163,7 +162,7 @@ class [[nodiscard("unnecessary construction")]] RequestLoader void visitFragmentSpread(const peg::ast_node& fragmentSpread); void visitInlineFragment(const peg::ast_node& inlineFragment); - void mergeFragmentFields(ResponseFieldList && fragmentFields) noexcept; + void mergeFragmentFields(ResponseFieldList&& fragmentFields) noexcept; const SchemaLoader& _schemaLoader; const FragmentDefinitionMap& _fragments; diff --git a/include/SchemaGenerator.h b/include/SchemaGenerator.h index f0ba1bf0..7edabf18 100644 --- a/include/SchemaGenerator.h +++ b/include/SchemaGenerator.h @@ -8,6 +8,8 @@ #include "SchemaLoader.h" +#include + namespace graphql::generator::schema { struct [[nodiscard("unnecessary construction")]] GeneratorPaths @@ -22,13 +24,14 @@ struct [[nodiscard("unnecessary construction")]] GeneratorOptions const bool verbose = false; const bool stubs = false; const bool noIntrospection = false; + const bool prefixedHeaders = false; }; class [[nodiscard("unnecessary construction")]] Generator { public: // Initialize the generator with the introspection schema or a custom GraphQL schema. - explicit Generator(SchemaOptions && schemaOptions, GeneratorOptions && options); + explicit Generator(SchemaOptions&& schemaOptions, GeneratorOptions&& options); // Run the generator and return a list of filenames that were output. [[nodiscard("unnecessary construction")]] std::vector Build() const noexcept; @@ -36,16 +39,24 @@ class [[nodiscard("unnecessary construction")]] Generator private: [[nodiscard("unnecessary memory copy")]] std::string getHeaderDir() const noexcept; [[nodiscard("unnecessary memory copy")]] std::string getSourceDir() const noexcept; - [[nodiscard("unnecessary memory copy")]] std::string getHeaderPath() const noexcept; - [[nodiscard("unnecessary memory copy")]] std::string getSourcePath() const noexcept; - - [[nodiscard("unnecessary call")]] bool outputHeader() const noexcept; - void outputInterfaceDeclaration(std::ostream & headerFile, std::string_view cppType) const; - void outputObjectImplements(std::ostream & headerFile, const ObjectType& objectType) const; - void outputObjectStubs(std::ostream & headerFile, const ObjectType& objectType) const; - void outputObjectDeclaration(std::ostream & headerFile, - const ObjectType& objectType, - bool isQueryType) const; + [[nodiscard("unnecessary memory copy")]] std::string getSchemaHeaderPath() const noexcept; + [[nodiscard("unnecessary memory copy")]] std::string getSchemaModulePath() const noexcept; + [[nodiscard("unnecessary memory copy")]] std::string getSchemaSourcePath() const noexcept; + [[nodiscard("unnecessary memory copy")]] std::string getSharedTypesHeaderPath() const noexcept; + [[nodiscard("unnecessary memory copy")]] std::string getSharedTypesModulePath() const noexcept; + [[nodiscard("unnecessary memory copy")]] std::string getSharedTypesSourcePath() const noexcept; + + [[nodiscard("unnecessary call")]] bool outputSchemaHeader() const noexcept; + [[nodiscard("unnecessary call")]] bool outputSchemaModule() const noexcept; + [[nodiscard("unnecessary call")]] bool outputSharedTypesHeader() const noexcept; + [[nodiscard("unnecessary call")]] bool outputSharedTypesModule() const noexcept; + void outputInterfaceDeclaration(std::ostream& headerFile, std::string_view cppType) const; + void outputObjectModule( + std::ostream& moduleFile, std::string_view objectNamespace, std::string_view cppType) const; + void outputObjectImplements(std::ostream& headerFile, const ObjectType& objectType) const; + void outputObjectStubs(std::ostream& headerFile, const ObjectType& objectType) const; + void outputObjectDeclaration(std::ostream& headerFile, const ObjectType& objectType, + bool isQueryType, bool isSubscriptionType) const; [[nodiscard("unnecessary memory copy")]] std::string getFieldDeclaration( const InputField& inputField) const noexcept; [[nodiscard("unnecessary memory copy")]] std::string getFieldDeclaration( @@ -53,27 +64,23 @@ class [[nodiscard("unnecessary construction")]] Generator [[nodiscard("unnecessary memory copy")]] std::string getResolverDeclaration( const OutputField& outputField) const noexcept; - [[nodiscard("unnecessary call")]] bool outputSource() const noexcept; - void outputInterfaceImplementation(std::ostream & sourceFile, std::string_view cppType) const; - void outputInterfaceIntrospection(std::ostream & sourceFile, const InterfaceType& interfaceType) - const; - void outputUnionIntrospection(std::ostream & sourceFile, const UnionType& unionType) const; - void outputObjectImplementation(std::ostream & sourceFile, - const ObjectType& objectType, - bool isQueryType) const; - void outputObjectIntrospection(std::ostream & sourceFile, const ObjectType& objectType) const; - void outputIntrospectionInterfaces(std::ostream & sourceFile, - std::string_view cppType, + [[nodiscard("unnecessary call")]] bool outputSchemaSource() const noexcept; + [[nodiscard("unnecessary call")]] bool outputSharedTypesSource() const noexcept; + void outputInterfaceImplementation(std::ostream& sourceFile, std::string_view cppType) const; + void outputInterfaceIntrospection( + std::ostream& sourceFile, const InterfaceType& interfaceType) const; + void outputUnionIntrospection(std::ostream& sourceFile, const UnionType& unionType) const; + void outputObjectImplementation( + std::ostream& sourceFile, const ObjectType& objectType, bool isQueryType) const; + void outputObjectIntrospection(std::ostream& sourceFile, const ObjectType& objectType) const; + void outputIntrospectionInterfaces(std::ostream& sourceFile, std::string_view cppType, const std::vector& interfaces) const; - void outputIntrospectionFields(std::ostream & sourceFile, - std::string_view cppType, - const OutputFieldList& fields) const; - [[nodiscard("unnecessary memory copy")]] std::string getArgumentDefaultValue(size_t level, - const response::Value& defaultValue) const noexcept; + void outputIntrospectionFields( + std::ostream& sourceFile, std::string_view cppType, const OutputFieldList& fields) const; + [[nodiscard("unnecessary memory copy")]] std::string getArgumentDefaultValue( + std::size_t level, const response::Value& defaultValue) const noexcept; [[nodiscard("unnecessary memory copy")]] std::string getArgumentDeclaration( - const InputField& argument, - const char* prefixToken, - const char* argumentsToken, + const InputField& argument, const char* prefixToken, const char* argumentsToken, const char* defaultToken) const noexcept; [[nodiscard("unnecessary memory copy")]] std::string getArgumentAccessType( const InputField& argument) const noexcept; @@ -81,8 +88,8 @@ class [[nodiscard("unnecessary construction")]] Generator const OutputField& result) const noexcept; [[nodiscard("unnecessary memory copy")]] std::string getTypeModifiers( const TypeModifierStack& modifiers) const noexcept; - [[nodiscard("unnecessary memory copy")]] std::string getIntrospectionType(std::string_view type, - const TypeModifierStack& modifiers) const noexcept; + [[nodiscard("unnecessary memory copy")]] std::string getIntrospectionType( + std::string_view type, const TypeModifierStack& modifiers) const noexcept; [[nodiscard("unnecessary memory copy")]] std::vector outputSeparateFiles() const noexcept; @@ -92,9 +99,14 @@ class [[nodiscard("unnecessary construction")]] Generator SchemaLoader _loader; const GeneratorOptions _options; const std::string _headerDir; + const std::string _moduleDir; const std::string _sourceDir; - const std::string _headerPath; - const std::string _sourcePath; + const std::string _schemaHeaderPath; + const std::string _schemaModulePath; + const std::string _schemaSourcePath; + const std::string _sharedTypesHeaderPath; + const std::string _sharedTypesModulePath; + const std::string _sharedTypesSourcePath; }; } // namespace graphql::generator::schema diff --git a/include/SchemaLoader.h b/include/SchemaLoader.h index 9aa73444..181de219 100644 --- a/include/SchemaLoader.h +++ b/include/SchemaLoader.h @@ -14,6 +14,7 @@ #include "graphqlservice/internal/Grammar.h" #include +#include #include #include @@ -31,14 +32,14 @@ enum class [[nodiscard("unnecessary conversion")]] BuiltinType { using BuiltinTypeMap = std::map; // These are the C++ types we'll use for them. -using CppTypeMap = std::array(BuiltinType::ID) + 1>; +using CppTypeMap = std::array(BuiltinType::ID) + 1>; // Keep track of the positions of each type declaration in the file. using PositionMap = std::unordered_map; // For all of the named types we track, we want to keep them in order in a vector but // be able to lookup their offset quickly by name. -using TypeNameMap = std::unordered_map; +using TypeNameMap = std::unordered_map; // Scalar types are opaque to the generator, it's up to the service implementation // to handle parsing, validating, and serializing them. We just need to track which @@ -216,7 +217,7 @@ class [[nodiscard("unnecessary construction")]] SchemaLoader { public: // Initialize the loader with the introspection schema or a custom GraphQL schema. - explicit SchemaLoader(SchemaOptions && schemaOptions); + explicit SchemaLoader(SchemaOptions&& schemaOptions); [[nodiscard("unnecessary call")]] bool isIntrospection() const noexcept; [[nodiscard("unnecessary call")]] std::string_view getSchemaDescription() const noexcept; @@ -232,22 +233,22 @@ class [[nodiscard("unnecessary construction")]] SchemaLoader [[nodiscard("unnecessary call")]] const tao::graphqlpeg::position& getTypePosition( std::string_view type) const; - [[nodiscard("unnecessary call")]] size_t getScalarIndex(std::string_view type) const; + [[nodiscard("unnecessary call")]] std::size_t getScalarIndex(std::string_view type) const; [[nodiscard("unnecessary call")]] const ScalarTypeList& getScalarTypes() const noexcept; - [[nodiscard("unnecessary call")]] size_t getEnumIndex(std::string_view type) const; + [[nodiscard("unnecessary call")]] std::size_t getEnumIndex(std::string_view type) const; [[nodiscard("unnecessary call")]] const EnumTypeList& getEnumTypes() const noexcept; - [[nodiscard("unnecessary call")]] size_t getInputIndex(std::string_view type) const; + [[nodiscard("unnecessary call")]] std::size_t getInputIndex(std::string_view type) const; [[nodiscard("unnecessary call")]] const InputTypeList& getInputTypes() const noexcept; - [[nodiscard("unnecessary call")]] size_t getUnionIndex(std::string_view type) const; + [[nodiscard("unnecessary call")]] std::size_t getUnionIndex(std::string_view type) const; [[nodiscard("unnecessary call")]] const UnionTypeList& getUnionTypes() const noexcept; - [[nodiscard("unnecessary call")]] size_t getInterfaceIndex(std::string_view type) const; + [[nodiscard("unnecessary call")]] std::size_t getInterfaceIndex(std::string_view type) const; [[nodiscard("unnecessary call")]] const InterfaceTypeList& getInterfaceTypes() const noexcept; - [[nodiscard("unnecessary call")]] size_t getObjectIndex(std::string_view type) const; + [[nodiscard("unnecessary call")]] std::size_t getObjectIndex(std::string_view type) const; [[nodiscard("unnecessary call")]] const ObjectTypeList& getObjectTypes() const noexcept; [[nodiscard("unnecessary call")]] const DirectiveList& getDirectives() const noexcept; @@ -259,12 +260,12 @@ class [[nodiscard("unnecessary construction")]] SchemaLoader [[nodiscard("unnecessary call")]] static std::string_view getSafeCppName( std::string_view type) noexcept; - [[nodiscard("unnecessary call")]] std::string_view getCppType(std::string_view type) - const noexcept; - [[nodiscard("unnecessary memory copy")]] std::string getInputCppType(const InputField& field) - const noexcept; - [[nodiscard("unnecessary memory copy")]] std::string getOutputCppType(const OutputField& field) - const noexcept; + [[nodiscard("unnecessary call")]] std::string_view getCppType( + std::string_view type) const noexcept; + [[nodiscard("unnecessary memory copy")]] std::string getInputCppType( + const InputField& field) const noexcept; + [[nodiscard("unnecessary memory copy")]] std::string getOutputCppType( + const OutputField& field) const noexcept; [[nodiscard("unnecessary memory copy")]] static std::string getOutputCppAccessor( const OutputField& field) noexcept; @@ -296,32 +297,29 @@ class [[nodiscard("unnecessary construction")]] SchemaLoader void visitObjectTypeExtension(const peg::ast_node& objectTypeExtension); void visitDirectiveDefinition(const peg::ast_node& directiveDefinition); - static void blockReservedName(std::string_view name, - std::optional position = std::nullopt); + static void blockReservedName( + std::string_view name, std::optional position = std::nullopt); [[nodiscard("unnecessary memory copy")]] static OutputFieldList getOutputFields( const peg::ast_node::children_t& fields); [[nodiscard("unnecessary memory copy")]] static InputFieldList getInputFields( const peg::ast_node::children_t& fields); void validateSchema(); - void fixupOutputFieldList(OutputFieldList & fields, + void fixupOutputFieldList(OutputFieldList& fields, const std::optional>& interfaceFields, const std::optional& accessor); - void fixupInputFieldList(InputFieldList & fields); + void fixupInputFieldList(InputFieldList& fields); void reorderInputTypeDependencies(); void validateImplementedInterfaces() const; [[nodiscard("unnecessary call")]] const InterfaceType& findInterfaceType( - std::string_view typeName, - std::string_view interfaceName) const; - void validateInterfaceFields(std::string_view typeName, - std::string_view interfaceName, + std::string_view typeName, std::string_view interfaceName) const; + void validateInterfaceFields(std::string_view typeName, std::string_view interfaceName, const OutputFieldList& typeFields) const; - void validateTransitiveInterfaces(std::string_view typeName, - const std::vector& interfaces) const; + void validateTransitiveInterfaces( + std::string_view typeName, const std::vector& interfaces) const; [[nodiscard("unnecessary memory copy")]] static std::string getJoinedCppName( - std::string_view prefix, - std::string_view fieldName) noexcept; + std::string_view prefix, std::string_view fieldName) noexcept; static const std::string_view s_introspectionNamespace; static const BuiltinTypeMap s_builtinTypes; diff --git a/include/Validation.h b/include/Validation.h index 5b11057c..1dd48c77 100644 --- a/include/Validation.h +++ b/include/Validation.h @@ -10,6 +10,8 @@ #include "graphqlservice/internal/Schema.h" +#include + namespace graphql::service { using ValidateType = std::optional>; @@ -257,8 +259,8 @@ class [[nodiscard("unnecessary construction")]] ValidateExecutableVisitor VariableDefinitions _variableDefinitions; VariableSet _referencedVariables; FragmentSet _fragmentStack; - size_t _fieldCount = 0; - size_t _introspectionFieldCount = 0; + std::size_t _fieldCount = 0; + std::size_t _introspectionFieldCount = 0; TypeFields _typeFields; InputTypeFields _inputTypeFields; ValidateType _scopedType; diff --git a/include/graphqlservice/Client.ixx b/include/graphqlservice/Client.ixx new file mode 100644 index 00000000..9740d304 --- /dev/null +++ b/include/graphqlservice/Client.ixx @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +module; + +#include "GraphQLClient.h" + +export module GraphQL.Client; + +export import GraphQL.Response; + +export namespace graphql::client { + +// clang-format off +using client::ErrorLocation; +using client::ErrorPathSegment; +using client::Error; +using client::ServiceResponse; +using client::parseServiceResponse; +using client::TypeModifier; +using client::Variable; + +using modified_variable::ModifiedVariable; +using modified_variable::IntVariable; +using modified_variable::FloatVariable; +using modified_variable::StringVariable; +using modified_variable::BooleanVariable; +using modified_variable::IdVariable; +using modified_variable::ScalarVariable; + +using client::Response; + +using modified_response::ModifiedResponse; +using modified_response::IntResponse; +using modified_response::FloatResponse; +using modified_response::StringResponse; +using modified_response::BooleanResponse; +using modified_response::IdResponse; +using modified_response::ScalarResponse; +// clang-format on + +} // namespace graphql::client diff --git a/include/graphqlservice/GraphQLClient.h b/include/graphqlservice/GraphQLClient.h index 1321c1b4..8db4830e 100644 --- a/include/graphqlservice/GraphQLClient.h +++ b/include/graphqlservice/GraphQLClient.h @@ -6,27 +6,15 @@ #ifndef GRAPHQLCLIENT_H #define GRAPHQLCLIENT_H -// clang-format off -#ifdef GRAPHQL_DLLEXPORTS - #ifdef IMPL_GRAPHQLCLIENT_DLL - #define GRAPHQLCLIENT_EXPORT __declspec(dllexport) - #else // !IMPL_GRAPHQLCLIENT_DLL - #define GRAPHQLCLIENT_EXPORT __declspec(dllimport) - #endif // !IMPL_GRAPHQLCLIENT_DLL -#else // !GRAPHQL_DLLEXPORTS - #define GRAPHQLCLIENT_EXPORT -#endif // !GRAPHQL_DLLEXPORTS -// clang-format on - -#include "graphqlservice/GraphQLResponse.h" +#include "GraphQLResponse.h" -#include "graphqlservice/internal/Version.h" +#include "internal/DllExports.h" #include #include #include +#include #include -#include #include namespace graphql::client { @@ -96,7 +84,7 @@ template <> GRAPHQLCLIENT_EXPORT response::Value Variable::serialize(response::Value&& value); #endif // GRAPHQL_DLLEXPORTS -namespace { +inline namespace modified_variable { // These types are used as scalar variables even though they are represented with a class. template @@ -180,9 +168,19 @@ struct ModifiedVariable response::Value result { response::Type::List }; result.reserve(listValue.size()); - std::for_each(listValue.begin(), listValue.end(), [&result](auto& value) { - result.emplace_back(serialize(std::move(value))); - }); + if constexpr (std::is_same_v && OnlyNoneModifiers) + { + std::ranges::for_each(listValue, [&result](bool value) { + result.emplace_back(response::Value { value }); + }); + } + else + { + std::ranges::for_each(listValue, [&result](auto& value) { + result.emplace_back(serialize(std::move(value))); + }); + } + listValue.clear(); return result; @@ -231,7 +229,14 @@ struct ModifiedVariable { typename VariableTraits::type result(listValue.size()); - std::transform(listValue.cbegin(), listValue.cend(), result.begin(), duplicate); + if constexpr (std::is_same_v && OnlyNoneModifiers) + { + std::copy(listValue.begin(), listValue.end(), result.begin()); + } + else + { + std::ranges::transform(listValue, result.begin(), duplicate); + } return result; } @@ -247,7 +252,7 @@ using BooleanVariable = ModifiedVariable; using IdVariable = ModifiedVariable; using ScalarVariable = ModifiedVariable; -} // namespace +} // namespace modified_variable // Parse a single response output value. This is the inverse of Variable for output types instead of // input types. @@ -274,7 +279,7 @@ template <> GRAPHQLCLIENT_EXPORT response::Value Response::parse(response::Value&& response); #endif // GRAPHQL_DLLEXPORTS -namespace { +inline namespace modified_response { // Parse response output values with chained type modifiers that add nullable or list wrappers. // This is the inverse of ModifiedVariable for output types instead of input types. @@ -335,8 +340,7 @@ struct ModifiedResponse auto listValue = response.release(); result.reserve(listValue.size()); - std::transform(listValue.begin(), - listValue.end(), + std::ranges::transform(listValue, std::back_inserter(result), [](response::Value& value) { return parse(std::move(value)); @@ -357,7 +361,7 @@ using BooleanResponse = ModifiedResponse; using IdResponse = ModifiedResponse; using ScalarResponse = ModifiedResponse; -} // namespace +} // namespace modified_response } // namespace graphql::client #endif // GRAPHQLCLIENT_H diff --git a/include/graphqlservice/GraphQLParse.h b/include/graphqlservice/GraphQLParse.h index 627446dc..40cc8749 100644 --- a/include/graphqlservice/GraphQLParse.h +++ b/include/graphqlservice/GraphQLParse.h @@ -6,18 +6,9 @@ #ifndef GRAPHQLPARSE_H #define GRAPHQLPARSE_H -// clang-format off -#ifdef GRAPHQL_DLLEXPORTS - #ifdef IMPL_GRAPHQLPEG_DLL - #define GRAPHQLPEG_EXPORT __declspec(dllexport) - #else // !IMPL_GRAPHQLPEG_DLL - #define GRAPHQLPEG_EXPORT __declspec(dllimport) - #endif // !IMPL_GRAPHQLPEG_DLL -#else // !GRAPHQL_DLLEXPORTS - #define GRAPHQLPEG_EXPORT -#endif // !GRAPHQL_DLLEXPORTS -// clang-format on +#include "internal/DllExports.h" +#include #include #include @@ -34,24 +25,28 @@ struct [[nodiscard("unnecessary parse")]] ast bool validated = false; }; +inline namespace constants { + // By default, we want to limit the depth of nested nodes. You can override this with // another value for the depthLimit parameter in these parse functions. -constexpr size_t c_defaultDepthLimit = 25; +constexpr std::size_t c_defaultDepthLimit = 25; + +} // namespace constants [[nodiscard("unnecessary parse")]] GRAPHQLPEG_EXPORT ast parseSchemaString( - std::string_view input, size_t depthLimit = c_defaultDepthLimit); + std::string_view input, std::size_t depthLimit = c_defaultDepthLimit); [[nodiscard("unnecessary parse")]] GRAPHQLPEG_EXPORT ast parseSchemaFile( - std::string_view filename, size_t depthLimit = c_defaultDepthLimit); + std::string_view filename, std::size_t depthLimit = c_defaultDepthLimit); [[nodiscard("unnecessary parse")]] GRAPHQLPEG_EXPORT ast parseString( - std::string_view input, size_t depthLimit = c_defaultDepthLimit); + std::string_view input, std::size_t depthLimit = c_defaultDepthLimit); [[nodiscard("unnecessary parse")]] GRAPHQLPEG_EXPORT ast parseFile( - std::string_view filename, size_t depthLimit = c_defaultDepthLimit); + std::string_view filename, std::size_t depthLimit = c_defaultDepthLimit); } // namespace peg -[[nodiscard("unnecessary parse")]] GRAPHQLPEG_EXPORT peg::ast operator""_graphql( - const char* text, size_t size); +[[nodiscard("unnecessary parse")]] GRAPHQLPEG_EXPORT peg::ast operator"" _graphql( + const char* text, std::size_t size); } // namespace graphql diff --git a/include/graphqlservice/GraphQLResponse.h b/include/graphqlservice/GraphQLResponse.h index 5ff8cb07..97bcea87 100644 --- a/include/graphqlservice/GraphQLResponse.h +++ b/include/graphqlservice/GraphQLResponse.h @@ -6,22 +6,16 @@ #ifndef GRAPHQLRESPONSE_H #define GRAPHQLRESPONSE_H -// clang-format off -#ifdef GRAPHQL_DLLEXPORTS - #ifdef IMPL_GRAPHQLRESPONSE_DLL - #define GRAPHQLRESPONSE_EXPORT __declspec(dllexport) - #else // !IMPL_GRAPHQLRESPONSE_DLL - #define GRAPHQLRESPONSE_EXPORT __declspec(dllimport) - #endif // !IMPL_GRAPHQLRESPONSE_DLL -#else // !GRAPHQL_DLLEXPORTS - #define GRAPHQLRESPONSE_EXPORT -#endif // !GRAPHQL_DLLEXPORTS -// clang-format on - -#include "graphqlservice/internal/Awaitable.h" +#include "internal/Awaitable.h" +#include "internal/DllExports.h" +#include +#include #include +#include #include +#include +#include #include #include #include @@ -47,6 +41,7 @@ enum class [[nodiscard("unnecessary conversion")]] Type : std::uint8_t { }; struct Value; +class ValueVisitor; using MapType = std::vector>; using ListType = std::vector; @@ -66,7 +61,7 @@ struct [[nodiscard("unnecessary conversion")]] IdType GRAPHQLRESPONSE_EXPORT ~IdType(); // Implicit ByteData constructors - GRAPHQLRESPONSE_EXPORT IdType(size_t count, typename ByteData::value_type value = 0); + GRAPHQLRESPONSE_EXPORT IdType(std::size_t count, typename ByteData::value_type value = 0); GRAPHQLRESPONSE_EXPORT IdType(std::initializer_list values); template IdType(InputIt begin, InputIt end); @@ -101,20 +96,21 @@ struct [[nodiscard("unnecessary conversion")]] IdType // Shared accessors [[nodiscard("unnecessary call")]] GRAPHQLRESPONSE_EXPORT bool empty() const noexcept; - [[nodiscard("unnecessary call")]] GRAPHQLRESPONSE_EXPORT size_t size() const noexcept; - [[nodiscard("unnecessary call")]] GRAPHQLRESPONSE_EXPORT size_t max_size() const noexcept; - GRAPHQLRESPONSE_EXPORT void reserve(size_t new_cap); - [[nodiscard("unnecessary call")]] GRAPHQLRESPONSE_EXPORT size_t capacity() const noexcept; + [[nodiscard("unnecessary call")]] GRAPHQLRESPONSE_EXPORT std::size_t size() const noexcept; + [[nodiscard("unnecessary call")]] GRAPHQLRESPONSE_EXPORT std::size_t max_size() const noexcept; + GRAPHQLRESPONSE_EXPORT void reserve(std::size_t new_cap); + [[nodiscard("unnecessary call")]] GRAPHQLRESPONSE_EXPORT std::size_t capacity() const noexcept; GRAPHQLRESPONSE_EXPORT void shrink_to_fit(); GRAPHQLRESPONSE_EXPORT void clear() noexcept; // ByteData accessors [[nodiscard("unnecessary call")]] GRAPHQLRESPONSE_EXPORT const std::uint8_t& at( - size_t pos) const; - [[nodiscard("unnecessary call")]] GRAPHQLRESPONSE_EXPORT std::uint8_t& at(size_t pos); + std::size_t pos) const; + [[nodiscard("unnecessary call")]] GRAPHQLRESPONSE_EXPORT std::uint8_t& at(std::size_t pos); [[nodiscard("unnecessary call")]] GRAPHQLRESPONSE_EXPORT const std::uint8_t& operator[]( - size_t pos) const; - [[nodiscard("unnecessary call")]] GRAPHQLRESPONSE_EXPORT std::uint8_t& operator[](size_t pos); + std::size_t pos) const; + [[nodiscard("unnecessary call")]] GRAPHQLRESPONSE_EXPORT std::uint8_t& operator[]( + std::size_t pos); [[nodiscard("unnecessary call")]] GRAPHQLRESPONSE_EXPORT const std::uint8_t& front() const; [[nodiscard("unnecessary call")]] GRAPHQLRESPONSE_EXPORT std::uint8_t& front(); [[nodiscard("unnecessary call")]] GRAPHQLRESPONSE_EXPORT const std::uint8_t& back() const; @@ -169,6 +165,22 @@ template <> GRAPHQLRESPONSE_EXPORT IdType::OpaqueString IdType::release(); #endif // GRAPHQL_DLLEXPORTS +// A type-erased payload for scalar values whose C++ representation can't be expressed with any +// of Value's other alternatives (e.g. a BigInt backed by std::int64_t). The implementer supplies +// both the std::any payload and a serializer that knows how to drive a ValueVisitor with it, so +// this stays a private implementation detail of a hand-written resolver -- it's never exposed to +// the schema definition (no IDL/directive changes) or to the client (the wire format is still +// ordinary JSON, produced by whatever the serializer calls on the visitor). +struct [[nodiscard("unnecessary construction")]] AnyScalar +{ + using Serializer = std::function&)>; + + std::any value; + Serializer serialize; +}; + +using SharedAnyScalar = std::shared_ptr; + template struct ValueTypeTraits { @@ -232,6 +244,7 @@ struct [[nodiscard("unnecessary conversion")]] Value GRAPHQLRESPONSE_EXPORT explicit Value(IntType value); GRAPHQLRESPONSE_EXPORT explicit Value(FloatType value); GRAPHQLRESPONSE_EXPORT explicit Value(IdType&& value); + GRAPHQLRESPONSE_EXPORT explicit Value(AnyScalar&& value); GRAPHQLRESPONSE_EXPORT Value(Value&& other) noexcept; GRAPHQLRESPONSE_EXPORT explicit Value(const Value& other); @@ -248,6 +261,10 @@ struct [[nodiscard("unnecessary conversion")]] Value // Check the Type [[nodiscard("unnecessary call")]] GRAPHQLRESPONSE_EXPORT Type type() const noexcept; + // Check for and release a type-erased AnyScalar payload attached to a Type::Scalar value. + [[nodiscard("unnecessary call")]] GRAPHQLRESPONSE_EXPORT bool isAny() const noexcept; + GRAPHQLRESPONSE_EXPORT SharedAnyScalar releaseAny(); + // JSON doesn't distinguish between Type::String, Type::EnumValue, and Type::ID, so if this // value comes from JSON and it's a string we need to track the fact that it can be interpreted // as any of those types. @@ -261,8 +278,8 @@ struct [[nodiscard("unnecessary conversion")]] Value [[nodiscard("unnecessary call")]] GRAPHQLRESPONSE_EXPORT bool maybe_id() const noexcept; // Valid for Type::Map or Type::List - GRAPHQLRESPONSE_EXPORT void reserve(size_t count); - [[nodiscard("unnecessary call")]] GRAPHQLRESPONSE_EXPORT size_t size() const; + GRAPHQLRESPONSE_EXPORT void reserve(std::size_t count); + [[nodiscard("unnecessary call")]] GRAPHQLRESPONSE_EXPORT std::size_t size() const; // Valid for Type::Map GRAPHQLRESPONSE_EXPORT bool emplace_back(std::string&& name, Value&& value); @@ -276,7 +293,7 @@ struct [[nodiscard("unnecessary conversion")]] Value // Valid for Type::List GRAPHQLRESPONSE_EXPORT void emplace_back(Value&& value); [[nodiscard("unnecessary call")]] GRAPHQLRESPONSE_EXPORT const Value& operator[]( - size_t index) const; + std::size_t index) const; // Specialized for all single-value Types. template @@ -297,7 +314,7 @@ struct [[nodiscard("unnecessary conversion")]] Value [[nodiscard("unnecessary call")]] bool operator==(const MapData& rhs) const; MapType map; - std::vector members; + std::vector members; }; // Type::String @@ -325,6 +342,7 @@ struct [[nodiscard("unnecessary conversion")]] Value [[nodiscard("unnecessary call")]] bool operator==(const ScalarData& rhs) const; std::unique_ptr scalar; + SharedAnyScalar any; }; using SharedData = std::shared_ptr; @@ -383,100 +401,296 @@ GRAPHQLRESPONSE_EXPORT IdType Value::release(); using AwaitableValue = internal::Awaitable; -class [[nodiscard("unnecessary construction")]] Writer final +// Type-erased visitor for alternate representations of Value. +class [[nodiscard("unnecessary construction")]] ValueVisitor final + : public std::enable_shared_from_this { private: struct Concept { virtual ~Concept() = default; - virtual void start_object() const = 0; - virtual void add_member(const std::string& key) const = 0; - virtual void end_object() const = 0; + virtual void add_value(std::shared_ptr&& value) = 0; - virtual void start_array() const = 0; - virtual void end_arrary() const = 0; + virtual void reserve(std::size_t count) = 0; - virtual void write_null() const = 0; - virtual void write_string(const std::string& value) const = 0; - virtual void write_bool(bool value) const = 0; - virtual void write_int(int value) const = 0; - virtual void write_float(double value) const = 0; + virtual void start_object() = 0; + virtual void add_member(std::string&& key) = 0; + virtual void end_object() = 0; + + virtual void start_array() = 0; + virtual void end_array() = 0; + + virtual void add_null() = 0; + virtual void add_string(std::string&& value) = 0; + virtual void add_enum(std::string&& value) = 0; + virtual void add_id(IdType&& value) = 0; + virtual void add_bool(bool value) = 0; + virtual void add_int(int value) = 0; + virtual void add_float(double value) = 0; + + virtual void complete() = 0; }; template struct Model : Concept { - explicit Model(std::unique_ptr pimpl) noexcept + explicit Model(std::shared_ptr pimpl) noexcept : _pimpl { std::move(pimpl) } { } - void start_object() const final + void add_value(std::shared_ptr&& value) final + { + _pimpl->add_value(std::move(value)); + } + + void reserve(std::size_t count) final + { + _pimpl->reserve(count); + } + + void start_object() final { _pimpl->start_object(); } - void add_member(const std::string& key) const final + void add_member(std::string&& key) final { - _pimpl->add_member(key); + _pimpl->add_member(std::move(key)); } - void end_object() const final + void end_object() final { _pimpl->end_object(); } - void start_array() const final + void start_array() final { _pimpl->start_array(); } - void end_arrary() const final + void end_array() final + { + _pimpl->end_array(); + } + + void add_null() final + { + _pimpl->add_null(); + } + + void add_string(std::string&& value) final + { + _pimpl->add_string(std::move(value)); + } + + void add_enum(std::string&& value) final { - _pimpl->end_arrary(); + _pimpl->add_enum(std::move(value)); } - void write_null() const final + void add_id(IdType&& value) final { - _pimpl->write_null(); + _pimpl->add_id(std::move(value)); } - void write_string(const std::string& value) const final + void add_bool(bool value) final { - _pimpl->write_string(value); + _pimpl->add_bool(value); } - void write_bool(bool value) const final + void add_int(int value) final { - _pimpl->write_bool(value); + _pimpl->add_int(value); } - void write_int(int value) const final + void add_float(double value) final { - _pimpl->write_int(value); + _pimpl->add_float(value); } - void write_float(double value) const final + void complete() final { - _pimpl->write_float(value); + _pimpl->complete(); } private: - std::unique_ptr _pimpl; + std::shared_ptr _pimpl; }; - const std::shared_ptr _concept; + const std::shared_ptr _concept; public: template - Writer(std::unique_ptr writer) noexcept - : _concept { std::static_pointer_cast( + ValueVisitor(std::shared_ptr writer) noexcept + : _concept { std::static_pointer_cast( std::make_shared>(std::move(writer))) } { } - GRAPHQLRESPONSE_EXPORT void write(Value value) const; + GRAPHQLRESPONSE_EXPORT void add_value(std::shared_ptr&& value); + + GRAPHQLRESPONSE_EXPORT void reserve(std::size_t count); + + GRAPHQLRESPONSE_EXPORT void start_object(); + GRAPHQLRESPONSE_EXPORT void add_member(std::string&& key); + GRAPHQLRESPONSE_EXPORT void end_object(); + + GRAPHQLRESPONSE_EXPORT void start_array(); + GRAPHQLRESPONSE_EXPORT void end_array(); + + GRAPHQLRESPONSE_EXPORT void add_null(); + GRAPHQLRESPONSE_EXPORT void add_string(std::string&& value); + GRAPHQLRESPONSE_EXPORT void add_enum(std::string&& value); + GRAPHQLRESPONSE_EXPORT void add_id(IdType&& value); + GRAPHQLRESPONSE_EXPORT void add_bool(bool value); + GRAPHQLRESPONSE_EXPORT void add_int(int value); + GRAPHQLRESPONSE_EXPORT void add_float(double value); + + GRAPHQLRESPONSE_EXPORT void complete(); +}; + +// Pending token for ValueVisitor. +struct [[nodiscard("unnecessary construction")]] ValueToken +{ + using OpaqueValue = std::shared_ptr; + + GRAPHQLRESPONSE_EXPORT explicit ValueToken(OpaqueValue&& value); + + using AnyValue = SharedAnyScalar; + + GRAPHQLRESPONSE_EXPORT explicit ValueToken(AnyValue&& value); + + struct Reserve + { + std::size_t capacity; + }; + + GRAPHQLRESPONSE_EXPORT explicit ValueToken(Reserve&& value); + + struct StartObject + { + }; + + GRAPHQLRESPONSE_EXPORT explicit ValueToken(StartObject&& value); + + struct AddMember + { + std::string key; + }; + + GRAPHQLRESPONSE_EXPORT explicit ValueToken(AddMember&& value); + + struct EndObject + { + }; + + GRAPHQLRESPONSE_EXPORT explicit ValueToken(EndObject&& value); + + struct StartArray + { + }; + + GRAPHQLRESPONSE_EXPORT explicit ValueToken(StartArray&& value); + + struct EndArray + { + }; + + GRAPHQLRESPONSE_EXPORT explicit ValueToken(EndArray&& value); + + struct NullValue + { + }; + + GRAPHQLRESPONSE_EXPORT explicit ValueToken(NullValue&& value); + + struct StringValue + { + std::string value; + }; + + GRAPHQLRESPONSE_EXPORT explicit ValueToken(StringValue&& value); + + struct EnumValue + { + std::string value; + }; + + GRAPHQLRESPONSE_EXPORT explicit ValueToken(EnumValue&& value); + + struct IdValue + { + IdType value; + }; + + GRAPHQLRESPONSE_EXPORT explicit ValueToken(IdValue&& value); + + struct BoolValue + { + bool value; + }; + + GRAPHQLRESPONSE_EXPORT explicit ValueToken(BoolValue&& value); + + struct IntValue + { + int value; + }; + + GRAPHQLRESPONSE_EXPORT explicit ValueToken(IntValue&& value); + + struct FloatValue + { + double value; + }; + + GRAPHQLRESPONSE_EXPORT explicit ValueToken(FloatValue&& value); + + GRAPHQLRESPONSE_EXPORT void visit(const std::shared_ptr& visitor) &&; + +private: + using variant_type = + std::variant; + + variant_type _value; +}; + +class [[nodiscard("unnecessary construction")]] ValueTokenStream final +{ +public: + ValueTokenStream() noexcept = default; + ~ValueTokenStream() = default; + + GRAPHQLRESPONSE_EXPORT explicit ValueTokenStream(Value&& value); + + ValueTokenStream(ValueTokenStream&&) noexcept = default; + ValueTokenStream& operator=(ValueTokenStream&&) noexcept = default; + + ValueTokenStream(const ValueTokenStream&) = delete; + ValueTokenStream& operator=(const ValueTokenStream&) = delete; + + template + ValueTokenStream(TArg&& arg) + : _tokens { ValueToken { std::forward(arg) } } + { + } + + template + void push_back(TArg&& arg) + { + _tokens.push_back(ValueToken { std::forward(arg) }); + } + + GRAPHQLRESPONSE_EXPORT void append(ValueTokenStream&& other); + + GRAPHQLRESPONSE_EXPORT void visit(const std::shared_ptr& visitor) &&; + GRAPHQLRESPONSE_EXPORT Value value() &&; + +private: + std::list _tokens; }; } // namespace graphql::response diff --git a/include/graphqlservice/GraphQLService.h b/include/graphqlservice/GraphQLService.h index 0e7f2ea6..5c5f11f6 100644 --- a/include/graphqlservice/GraphQLService.h +++ b/include/graphqlservice/GraphQLService.h @@ -6,40 +6,28 @@ #ifndef GRAPHQLSERVICE_H #define GRAPHQLSERVICE_H -// clang-format off -#ifdef GRAPHQL_DLLEXPORTS - #ifdef IMPL_GRAPHQLSERVICE_DLL - #define GRAPHQLSERVICE_EXPORT __declspec(dllexport) - #else // !IMPL_GRAPHQLSERVICE_DLL - #define GRAPHQLSERVICE_EXPORT __declspec(dllimport) - #endif // !IMPL_GRAPHQLSERVICE_DLL -#else // !GRAPHQL_DLLEXPORTS - #define GRAPHQLSERVICE_EXPORT -#endif // !GRAPHQL_DLLEXPORTS -// clang-format on - -#include "graphqlservice/GraphQLParse.h" -#include "graphqlservice/GraphQLResponse.h" - -#include "graphqlservice/internal/Awaitable.h" -#include "graphqlservice/internal/SortedMap.h" -#include "graphqlservice/internal/Version.h" +#include "GraphQLParse.h" +#include "GraphQLResponse.h" + +#include "internal/Awaitable.h" +#include "internal/DllExports.h" +#include "internal/SortedMap.h" +#include #include #include -#include +#include +#include #include #include #include #include #include #include -#include +#include #include #include -#include #include -#include #include #include #include @@ -56,19 +44,19 @@ namespace service { // Errors should have a message string, and optional locations and a path. struct [[nodiscard("unnecessary construction")]] schema_location { - size_t line = 0; - size_t column = 1; + std::size_t line = 0; + std::size_t column = 1; }; // The implementation details of the error path should be opaque to client code. It is carried along // with the SelectionSetParams and automatically added to any schema errors or exceptions thrown // from an accessor as part of error reporting. -using path_segment = std::variant; +using path_segment = std::variant; struct [[nodiscard("unnecessary construction")]] field_path { std::optional> parent; - std::variant segment; + std::variant segment; }; using error_path = std::vector; @@ -86,6 +74,9 @@ struct [[nodiscard("unnecessary construction")]] schema_error [[nodiscard("unnecessary memory copy")]] GRAPHQLSERVICE_EXPORT response::Value buildErrorValues( std::list&& structuredErrors); +[[nodiscard("unnecessary memory copy")]] GRAPHQLSERVICE_EXPORT response::ValueTokenStream +visitErrorValues(std::list&& structuredErrors); + // This exception bubbles up 1 or more error messages to the JSON results. class [[nodiscard("unnecessary construction")]] schema_exception : public std::exception { @@ -134,18 +125,16 @@ struct [[nodiscard("unnecessary construction")]] RequestState inline namespace keywords { -using namespace std::literals; - -constexpr std::string_view strData { "data"sv }; -constexpr std::string_view strErrors { "errors"sv }; -constexpr std::string_view strMessage { "message"sv }; -constexpr std::string_view strLocations { "locations"sv }; -constexpr std::string_view strLine { "line"sv }; -constexpr std::string_view strColumn { "column"sv }; -constexpr std::string_view strPath { "path"sv }; -constexpr std::string_view strQuery { "query"sv }; -constexpr std::string_view strMutation { "mutation"sv }; -constexpr std::string_view strSubscription { "subscription"sv }; +constexpr std::string_view strData { "data" }; +constexpr std::string_view strErrors { "errors" }; +constexpr std::string_view strMessage { "message" }; +constexpr std::string_view strLocations { "locations" }; +constexpr std::string_view strLine { "line" }; +constexpr std::string_view strColumn { "column" }; +constexpr std::string_view strPath { "path" }; +constexpr std::string_view strQuery { "query" }; +constexpr std::string_view strMutation { "mutation" }; +constexpr std::string_view strSubscription { "subscription" }; } // namespace keywords @@ -171,20 +160,20 @@ enum class [[nodiscard("unnecessary conversion")]] ResolverContext { // Resume coroutine execution on a new worker thread any time co_await is called. This emulates the // behavior of std::async when passing std::launch::async. -struct [[nodiscard("unnecessary construction")]] await_worker_thread : coro::suspend_always +struct [[nodiscard("unnecessary construction")]] await_worker_thread : std::suspend_always { - GRAPHQLSERVICE_EXPORT void await_suspend(coro::coroutine_handle<> h) const; + GRAPHQLSERVICE_EXPORT void await_suspend(std::coroutine_handle<> h) const; }; // Queue coroutine execution on a single dedicated worker thread any time co_await is called from // the thread which created it. -struct [[nodiscard("unnecessary construction")]] await_worker_queue : coro::suspend_always +struct [[nodiscard("unnecessary construction")]] await_worker_queue : std::suspend_always { GRAPHQLSERVICE_EXPORT await_worker_queue(); GRAPHQLSERVICE_EXPORT ~await_worker_queue(); [[nodiscard("unexpected call")]] GRAPHQLSERVICE_EXPORT bool await_ready() const; - GRAPHQLSERVICE_EXPORT void await_suspend(coro::coroutine_handle<> h); + GRAPHQLSERVICE_EXPORT void await_suspend(std::coroutine_handle<> h); private: void resumePending(); @@ -192,7 +181,7 @@ struct [[nodiscard("unnecessary construction")]] await_worker_queue : coro::susp const std::thread::id _startId; std::mutex _mutex {}; std::condition_variable _cv {}; - std::list> _pending {}; + std::list> _pending {}; bool _shutdown = false; std::thread _worker; }; @@ -206,7 +195,7 @@ class [[nodiscard("unnecessary construction")]] await_async final virtual ~Concept() = default; [[nodiscard("unexpected call")]] virtual bool await_ready() const = 0; - virtual void await_suspend(coro::coroutine_handle<> h) const = 0; + virtual void await_suspend(std::coroutine_handle<> h) const = 0; virtual void await_resume() const = 0; }; @@ -223,7 +212,7 @@ class [[nodiscard("unnecessary construction")]] await_async final return _pimpl->await_ready(); } - void await_suspend(coro::coroutine_handle<> h) const final + void await_suspend(std::coroutine_handle<> h) const final { _pimpl->await_suspend(std::move(h)); } @@ -254,7 +243,7 @@ class [[nodiscard("unnecessary construction")]] await_async final GRAPHQLSERVICE_EXPORT await_async(std::launch launch); [[nodiscard("unexpected call")]] GRAPHQLSERVICE_EXPORT bool await_ready() const; - GRAPHQLSERVICE_EXPORT void await_suspend(coro::coroutine_handle<> h) const; + GRAPHQLSERVICE_EXPORT void await_suspend(std::coroutine_handle<> h) const; GRAPHQLSERVICE_EXPORT void await_resume() const; }; @@ -264,8 +253,17 @@ class [[nodiscard("unnecessary construction")]] await_async final using Directives = std::vector>; // Traversing a fragment spread adds a new set of directives. -using FragmentDefinitionDirectiveStack = std::list>; -using FragmentSpreadDirectiveStack = std::list; +struct [[nodiscard("unnecessary construction")]] FragmentDefinitionDirectiveStack +{ + const std::reference_wrapper directives; + const std::shared_ptr outer; +}; + +struct [[nodiscard("unnecessary construction")]] FragmentSpreadDirectiveStack +{ + const Directives directives; + const std::shared_ptr outer; +}; // Pass a common bundle of parameters to all of the generated Object::getField accessors in a // SelectionSet @@ -328,12 +326,12 @@ class [[nodiscard("unnecessary construction")]] AwaitableScalar return { _promise.get_future() }; } - coro::suspend_never initial_suspend() const noexcept + std::suspend_never initial_suspend() const noexcept { return {}; } - coro::suspend_never final_suspend() const noexcept + std::suspend_never final_suspend() const noexcept { return {}; } @@ -357,40 +355,14 @@ class [[nodiscard("unnecessary construction")]] AwaitableScalar std::promise _promise; }; - [[nodiscard("unexpected call")]] bool await_ready() const noexcept + [[nodiscard("unexpected call")]] constexpr bool await_ready() const noexcept { - return std::visit( - [](const auto& value) noexcept { - using value_type = std::decay_t; - - if constexpr (std::is_same_v) - { - return true; - } - else if constexpr (std::is_same_v>) - { - using namespace std::literals; - - return value.wait_for(0s) != std::future_status::timeout; - } - else if constexpr (std::is_same_v>) - { - return true; - } - }, - _value); + return true; } - void await_suspend(coro::coroutine_handle<> h) const + void await_suspend(std::coroutine_handle<> h) const { - std::thread( - [this](coro::coroutine_handle<> h) noexcept { - std::get>(_value).wait(); - h.resume(); - }, - std::move(h)) - .detach(); + h.resume(); } [[nodiscard("unnecessary construction")]] T await_resume() @@ -458,12 +430,12 @@ class [[nodiscard("unnecessary construction")]] AwaitableObject return { _promise.get_future() }; } - coro::suspend_never initial_suspend() const noexcept + std::suspend_never initial_suspend() const noexcept { return {}; } - coro::suspend_never final_suspend() const noexcept + std::suspend_never final_suspend() const noexcept { return {}; } @@ -487,35 +459,14 @@ class [[nodiscard("unnecessary construction")]] AwaitableObject std::promise _promise; }; - [[nodiscard("unexpected call")]] bool await_ready() const noexcept + [[nodiscard("unexpected call")]] constexpr bool await_ready() const noexcept { - return std::visit( - [](const auto& value) noexcept { - using value_type = std::decay_t; - - if constexpr (std::is_same_v) - { - return true; - } - else if constexpr (std::is_same_v>) - { - using namespace std::literals; - - return value.wait_for(0s) != std::future_status::timeout; - } - }, - _value); + return true; } - void await_suspend(coro::coroutine_handle<> h) const + void await_suspend(std::coroutine_handle<> h) const { - std::thread( - [this](coro::coroutine_handle<> h) noexcept { - std::get>(_value).wait(); - h.resume(); - }, - std::move(h)) - .detach(); + h.resume(); } [[nodiscard("unnecessary construction")]] T await_resume() @@ -592,7 +543,10 @@ struct [[nodiscard("unnecessary construction")]] ResolverParams : SelectionSetPa // we're ready to return from the top level Operation. struct [[nodiscard("unnecessary construction")]] ResolverResult { - response::Value data; + [[nodiscard("unnecessary call")]] GRAPHQLSERVICE_EXPORT response::Value document() &&; + [[nodiscard("unnecessary call")]] GRAPHQLSERVICE_EXPORT response::ValueTokenStream visit() &&; + + response::ValueTokenStream data {}; std::list errors {}; }; @@ -647,7 +601,7 @@ GRAPHQLSERVICE_EXPORT response::Value Argument::convert( const response::Value& value); #endif // GRAPHQL_DLLEXPORTS -namespace { +inline namespace modified_argument { // These types are used as scalar arguments even though they are represented with a class. template @@ -701,11 +655,7 @@ struct ModifiedArgument for (auto& error : errors) { - std::ostringstream message; - - message << "Invalid argument: " << name << " error: " << error.message; - - error.message = message.str(); + error.message = std::format("Invalid argument: {} error: {}", name, error.message); } throw schema_exception(std::move(errors)); @@ -774,8 +724,8 @@ struct ModifiedArgument typename ArgumentTraits::type result(values.size()); const auto& elements = values.get(); - std::transform(elements.cbegin(), - elements.cend(), + std::transform(elements.begin(), + elements.end(), result.begin(), [name](const response::Value& element) { response::Value single(response::Type::Map); @@ -847,7 +797,14 @@ struct ModifiedArgument { typename ArgumentTraits::type result(listValue.size()); - std::transform(listValue.cbegin(), listValue.cend(), result.begin(), duplicate); + if constexpr (std::is_same_v && OnlyNoneModifiers) + { + std::copy(listValue.begin(), listValue.end(), result.begin()); + } + else + { + std::ranges::transform(listValue, result.begin(), duplicate); + } return result; } @@ -863,7 +820,7 @@ using BooleanArgument = ModifiedArgument; using IdArgument = ModifiedArgument; using ScalarArgument = ModifiedArgument; -} // namespace +} // namespace modified_argument // Each type should handle fragments with type conditions matching its own // name and any inheritted interfaces. @@ -879,6 +836,10 @@ class [[nodiscard("unnecessary construction")]] Object : public std::enable_shar GRAPHQLSERVICE_EXPORT explicit Object(TypeNames&& typeNames, ResolverMap&& resolvers) noexcept; GRAPHQLSERVICE_EXPORT virtual ~Object() = default; + [[nodiscard("unnecessary call")]] GRAPHQLSERVICE_EXPORT std::shared_ptr StitchObject( + const std::shared_ptr& added, + const std::shared_ptr& schema = {}) const; + [[nodiscard("unnecessary call")]] GRAPHQLSERVICE_EXPORT AwaitableResolver resolve( const SelectionSetParams& selectionSetParams, const peg::ast_node& selection, const FragmentMap& fragments, const response::Value& variables) const; @@ -901,6 +862,7 @@ class [[nodiscard("unnecessary construction")]] Object : public std::enable_shar private: TypeNames _typeNames; ResolverMap _resolvers; + std::array, 2> _stitched; }; // Test if this Type inherits from Object. @@ -961,7 +923,7 @@ template <> GRAPHQLSERVICE_EXPORT void Result::validateScalar(const response::Value& value); #endif // GRAPHQL_DLLEXPORTS -namespace { +inline namespace modified_result { // Test if this Type is Object. template @@ -1076,7 +1038,7 @@ struct ModifiedResult if (!awaitedResult) { - co_return ResolverResult {}; + co_return ResolverResult { { response::ValueToken::NullValue {} } }; } auto modifiedResult = @@ -1103,8 +1065,8 @@ struct ModifiedResult if (value) { ModifiedResult::validateScalar(*value); - co_return ResolverResult { response::Value { - std::shared_ptr { std::move(value) } } }; + co_return ResolverResult { { response::ValueToken::OpaqueValue { + std::shared_ptr { std::move(value) } } } }; } } @@ -1117,7 +1079,7 @@ struct ModifiedResult if (!awaitedResult) { - co_return ResolverResult {}; + co_return ResolverResult { { response::ValueToken::NullValue {} } }; } auto modifiedResult = co_await ModifiedResult::convert(std::move(*awaitedResult), @@ -1140,8 +1102,8 @@ struct ModifiedResult if (value) { ModifiedResult::validateScalar(*value); - co_return ResolverResult { response::Value { - std::shared_ptr { std::move(value) } } }; + co_return ResolverResult { { response::ValueToken::OpaqueValue { + std::shared_ptr { std::move(value) } } } }; } } @@ -1158,7 +1120,7 @@ struct ModifiedResult children.reserve(awaitedResult.size()); params.errorPath = std::make_optional( field_path { parentPath ? std::make_optional(std::cref(*parentPath)) : std::nullopt, - path_segment { size_t { 0 } } }); + path_segment { std::size_t { 0 } } }); using vector_type = std::decay_t; @@ -1172,7 +1134,7 @@ struct ModifiedResult { children.push_back( ModifiedResult::convert(std::move(entry), ResolverParams(params))); - ++std::get(params.errorPath->segment); + ++std::get(params.errorPath->segment); } } else @@ -1181,14 +1143,15 @@ struct ModifiedResult { children.push_back( ModifiedResult::convert(std::move(entry), ResolverParams(params))); - ++std::get(params.errorPath->segment); + ++std::get(params.errorPath->segment); } } - ResolverResult document { response::Value { response::Type::List } }; + ResolverResult document; - document.data.reserve(children.size()); - std::get(params.errorPath->segment) = 0; + document.data.push_back(response::ValueToken::StartArray {}); + document.data.push_back(response::ValueToken::Reserve { children.size() }); + std::get(params.errorPath->segment) = 0; for (auto& child : children) { @@ -1198,11 +1161,11 @@ struct ModifiedResult auto value = co_await std::move(child); - document.data.emplace_back(std::move(value.data)); + document.data.append(std::move(value.data)); if (!value.errors.empty()) { - document.errors.splice(document.errors.end(), value.errors); + document.errors.splice(document.errors.end(), std::move(value.errors)); } } catch (schema_exception& scx) @@ -1216,19 +1179,20 @@ struct ModifiedResult } catch (const std::exception& ex) { - std::ostringstream message; - - message << "Field error name: " << params.fieldName - << " unknown error: " << ex.what(); + auto message = std::format("Field error name: {} unknown error: {}", + params.fieldName, + ex.what()); - document.errors.emplace_back(schema_error { message.str(), + document.errors.emplace_back(schema_error { std::move(message), params.getLocation(), buildErrorPath(params.errorPath) }); } - ++std::get(params.errorPath->segment); + ++std::get(params.errorPath->segment); } + document.data.push_back(response::ValueToken::EndArray {}); + co_return document; } @@ -1264,14 +1228,14 @@ struct ModifiedResult throw schema_exception { { R"ex(not a valid List value)ex" } }; } - for (size_t i = 0; i < value.size(); ++i) + for (std::size_t i = 0; i < value.size(); ++i) { ModifiedResult::validateScalar(value[i]); } } using ResolverCallback = - std::function::type, const ResolverParams&)>; + std::function::type, const ResolverParams&)>; [[nodiscard("unnecessary call")]] static AwaitableResolver resolve( typename ResultTraits::future_type result, ResolverParams&& paramsArg, @@ -1284,7 +1248,8 @@ struct ModifiedResult if (value) { Result::validateScalar(*value); - co_return ResolverResult { response::Value { std::shared_ptr { std::move(value) } } }; + co_return ResolverResult { { response::ValueToken::OpaqueValue { + std::shared_ptr { std::move(value) } } } }; } auto pendingResolver = std::move(resolver); @@ -1296,7 +1261,7 @@ struct ModifiedResult try { co_await params.launch; - document.data = pendingResolver(co_await result, params); + document = pendingResolver(co_await result, params); } catch (schema_exception& scx) { @@ -1309,11 +1274,10 @@ struct ModifiedResult } catch (const std::exception& ex) { - std::ostringstream message; + auto message = + std::format("Field name: {} unknown error: {}", params.fieldName, ex.what()); - message << "Field name: " << params.fieldName << " unknown error: " << ex.what(); - - document.errors.emplace_back(schema_error { message.str(), + document.errors.emplace_back(schema_error { std::move(message), params.getLocation(), buildErrorPath(params.errorPath) }); } @@ -1333,14 +1297,16 @@ using IdResult = ModifiedResult; using ScalarResult = ModifiedResult; using ObjectResult = ModifiedResult; -} // namespace +} // namespace modified_result // Subscription callbacks receive the response::Value representing the result of evaluating the // SelectionSet against the payload. using SubscriptionCallback = std::function; +using SubscriptionVisitor = std::function; +using SubscriptionCallbackOrVisitor = std::variant; // Subscriptions are stored in maps using these keys. -using SubscriptionKey = size_t; +using SubscriptionKey = std::size_t; using SubscriptionName = std::string; using AwaitableSubscribe = internal::Awaitable; @@ -1364,7 +1330,7 @@ struct [[nodiscard("unnecessary construction")]] RequestResolveParams struct [[nodiscard("unnecessary construction")]] RequestSubscribeParams { // Callback which receives the event data. - SubscriptionCallback callback; + SubscriptionCallbackOrVisitor callback; // Required query information. peg::ast query; @@ -1455,7 +1421,7 @@ struct [[nodiscard("unnecessary construction")]] SubscriptionData { explicit SubscriptionData(std::shared_ptr data, SubscriptionName&& field, response::Value arguments, Directives fieldDirectives, peg::ast&& query, - std::string&& operationName, SubscriptionCallback&& callback, + std::string&& operationName, SubscriptionCallbackOrVisitor&& callback, const peg::ast_node& selection); std::shared_ptr data; @@ -1465,7 +1431,7 @@ struct [[nodiscard("unnecessary construction")]] SubscriptionData Directives fieldDirectives; peg::ast query; std::string operationName; - SubscriptionCallback callback; + SubscriptionCallbackOrVisitor callback; const peg::ast_node& selection; }; @@ -1491,6 +1457,9 @@ class [[nodiscard("unnecessary construction")]] Request GRAPHQLSERVICE_EXPORT virtual ~Request(); public: + [[nodiscard("unnecessary call")]] GRAPHQLSERVICE_EXPORT std::shared_ptr stitch( + const std::shared_ptr& added) const; + [[nodiscard("unnecessary call")]] GRAPHQLSERVICE_EXPORT std::list validate( peg::ast& query) const; @@ -1500,6 +1469,8 @@ class [[nodiscard("unnecessary construction")]] Request [[nodiscard("unnecessary call")]] GRAPHQLSERVICE_EXPORT response::AwaitableValue resolve( RequestResolveParams params) const; + [[nodiscard("unnecessary call")]] GRAPHQLSERVICE_EXPORT AwaitableResolver visit( + RequestResolveParams params) const; [[nodiscard("leaked subscription")]] GRAPHQLSERVICE_EXPORT AwaitableSubscribe subscribe( RequestSubscribeParams params); [[nodiscard("potentially leaked subscription")]] GRAPHQLSERVICE_EXPORT AwaitableUnsubscribe @@ -1515,6 +1486,7 @@ class [[nodiscard("unnecessary construction")]] Request collectRegistrations(std::string_view field, RequestDeliverFilter&& filter) const noexcept; const TypeMap _operations; + const std::shared_ptr _schema; mutable std::mutex _validationMutex {}; const std::unique_ptr _validation; mutable std::mutex _subscriptionMutex {}; diff --git a/include/graphqlservice/JSONResponse.h b/include/graphqlservice/JSONResponse.h index c264fd82..aeb8609c 100644 --- a/include/graphqlservice/JSONResponse.h +++ b/include/graphqlservice/JSONResponse.h @@ -6,19 +6,9 @@ #ifndef JSONRESPONSE_H #define JSONRESPONSE_H -// clang-format off -#ifdef GRAPHQL_DLLEXPORTS - #ifdef IMPL_JSONRESPONSE_DLL - #define JSONRESPONSE_EXPORT __declspec(dllexport) - #else // !IMPL_JSONRESPONSE_DLL - #define JSONRESPONSE_EXPORT __declspec(dllimport) - #endif // !IMPL_JSONRESPONSE_DLL -#else // !GRAPHQL_DLLEXPORTS - #define JSONRESPONSE_EXPORT -#endif // !GRAPHQL_DLLEXPORTS -// clang-format on - -#include "graphqlservice/GraphQLResponse.h" +#include "GraphQLResponse.h" + +#include "internal/DllExports.h" namespace graphql::response { diff --git a/include/graphqlservice/JSONResponse.ixx b/include/graphqlservice/JSONResponse.ixx new file mode 100644 index 00000000..ff36ba7e --- /dev/null +++ b/include/graphqlservice/JSONResponse.ixx @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +module; + +#include "JSONResponse.h" + +export module GraphQL.JSONResponse; + +export import GraphQL.Response; + +export namespace graphql::response { + +// clang-format off +using response::toJSON; +using response::parseJSON; +// clang-format on + +} // namespace graphql::response diff --git a/include/graphqlservice/Parse.ixx b/include/graphqlservice/Parse.ixx new file mode 100644 index 00000000..2c696889 --- /dev/null +++ b/include/graphqlservice/Parse.ixx @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +module; + +#include "GraphQLParse.h" + +export module GraphQL.Parse; + +export namespace graphql { + +namespace peg { + +// clang-format off +using peg::ast_node; +using peg::ast_input; +using peg::ast; + +constexpr std::size_t c_defaultDepthLimit = constants::c_defaultDepthLimit; + +using peg::parseSchemaString; +using peg::parseSchemaFile; +using peg::parseString; +using peg::parseFile; +// clang-format on + +} // namespace peg + +using graphql::operator"" _graphql; + +} // namespace graphql diff --git a/include/graphqlservice/Response.ixx b/include/graphqlservice/Response.ixx new file mode 100644 index 00000000..522a4c6e --- /dev/null +++ b/include/graphqlservice/Response.ixx @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +module; + +#include "GraphQLResponse.h" + +export module GraphQL.Response; + +export import GraphQL.Internal.Awaitable; + +export namespace graphql::response { + +// clang-format off +using response::Type; + +using response::MapType; +using response::ListType; +using response::StringType; +using response::BooleanType; +using response::IntType; +using response::FloatType; +using response::ScalarType; + +using response::IdType; + +using response::ValueTypeTraits; +using response::Value; +using response::AwaitableValue; + +using response::ValueVisitor; +using response::ValueToken; +using response::ValueTokenStream; +// clang-format on + +} // namespace graphql::response diff --git a/include/graphqlservice/Service.ixx b/include/graphqlservice/Service.ixx new file mode 100644 index 00000000..74cf357c --- /dev/null +++ b/include/graphqlservice/Service.ixx @@ -0,0 +1,136 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +module; + +#include "GraphQLService.h" + +export module GraphQL.Service; + +export import GraphQL.Parse; +export import GraphQL.Response; + +export import GraphQL.Internal.Awaitable; +export import GraphQL.Internal.SortedMap; + +export namespace graphql { + +namespace schema { + +using schema::Schema; + +} // namespace schema + +namespace service { + +// clang-format off +using service::schema_location; +using service::path_segment; +using service::field_path; + +using service::error_path; +using service::buildErrorPath; + +using service::schema_error; +using service::buildErrorValues; +using service::visitErrorValues; + +using service::schema_exception; +using service::unimplemented_method; + +using service::RequestState; + +constexpr std::string_view strData = keywords::strData; +constexpr std::string_view strErrors = keywords::strErrors; +constexpr std::string_view strMessage = keywords::strMessage; +constexpr std::string_view strLocations = keywords::strLocations; +constexpr std::string_view strLine = keywords::strLine; +constexpr std::string_view strColumn = keywords::strColumn; +constexpr std::string_view strPath = keywords::strPath; +constexpr std::string_view strQuery = keywords::strQuery; +constexpr std::string_view strMutation = keywords::strMutation; +constexpr std::string_view strSubscription = keywords::strSubscription; + +using service::ResolverContext; + +using service::await_worker_thread; +using service::await_worker_queue; +using service::await_async; + +using service::Directives; +using service::FragmentDefinitionDirectiveStack; +using service::FragmentSpreadDirectiveStack; + +using service::SelectionSetParams; +using service::FieldParams; + +using service::AwaitableScalar; +using service::AwaitableObject; + +using service::Fragment; +using service::FragmentMap; + +using service::ResolverParams; +using service::ResolverResult; +using service::AwaitableResolver; +using service::Resolver; +using service::ResolverMap; + +using service::TypeModifier; +using service::Argument; + +using modified_argument::ModifiedArgument; +using modified_argument::IntArgument; +using modified_argument::FloatArgument; +using modified_argument::StringArgument; +using modified_argument::BooleanArgument; +using modified_argument::IdArgument; +using modified_argument::ScalarArgument; + +using service::TypeNames; +using service::Object; +using service::Result; + +using modified_result::ModifiedResult; +using modified_result::IntResult; +using modified_result::FloatResult; +using modified_result::StringResult; +using modified_result::BooleanResult; +using modified_result::IdResult; +using modified_result::ScalarResult; +using modified_result::ObjectResult; + +using service::SubscriptionCallback; +using service::SubscriptionVisitor; +using service::SubscriptionCallbackOrVisitor; +using service::SubscriptionKey; +using service::SubscriptionName; + +using service::AwaitableSubscribe; +using service::AwaitableUnsubscribe; +using service::AwaitableDeliver; + +using service::RequestResolveParams; +using service::RequestSubscribeParams; +using service::RequestUnsubscribeParams; + +using service::SubscriptionArguments; +using service::SubscriptionArgumentFilterCallback; +using service::SubscriptionDirectiveFilterCallback; +using service::SubscriptionFilter; + +using service::RequestDeliverFilter; +using service::RequestDeliverParams; + +using service::TypeMap; +using service::OperationData; +using service::SubscriptionData; + +using service::SubscriptionPlaceholder; + +using service::Request; +// clang-format on + +} // namespace service + +} // namespace graphql diff --git a/include/graphqlservice/internal/Awaitable.h b/include/graphqlservice/internal/Awaitable.h index 45097751..046e6a8b 100644 --- a/include/graphqlservice/internal/Awaitable.h +++ b/include/graphqlservice/internal/Awaitable.h @@ -6,16 +6,7 @@ #ifndef GRAPHQLAWAITABLE_H #define GRAPHQLAWAITABLE_H -// clang-format off -#ifdef USE_STD_EXPERIMENTAL_COROUTINE - #include - namespace coro = std::experimental; -#else // !USE_STD_EXPERIMENTAL_COROUTINE - #include - namespace coro = std; -#endif -// clang-format on - +#include #include #include @@ -45,12 +36,12 @@ class [[nodiscard("unnecessary construction")]] Awaitable return { _promise.get_future() }; } - coro::suspend_never initial_suspend() const noexcept + std::suspend_never initial_suspend() const noexcept { return {}; } - coro::suspend_never final_suspend() const noexcept + std::suspend_never final_suspend() const noexcept { return {}; } @@ -74,7 +65,7 @@ class [[nodiscard("unnecessary construction")]] Awaitable return true; } - void await_suspend(coro::coroutine_handle<> h) const + void await_suspend(std::coroutine_handle<> h) const { h.resume(); } @@ -109,12 +100,12 @@ class [[nodiscard("unnecessary construction")]] Awaitable return { _promise.get_future() }; } - coro::suspend_never initial_suspend() const noexcept + std::suspend_never initial_suspend() const noexcept { return {}; } - coro::suspend_never final_suspend() const noexcept + std::suspend_never final_suspend() const noexcept { return {}; } @@ -143,7 +134,7 @@ class [[nodiscard("unnecessary construction")]] Awaitable return true; } - void await_suspend(coro::coroutine_handle<> h) const + void await_suspend(std::coroutine_handle<> h) const { h.resume(); } diff --git a/include/graphqlservice/internal/Awaitable.ixx b/include/graphqlservice/internal/Awaitable.ixx new file mode 100644 index 00000000..40d5e4c2 --- /dev/null +++ b/include/graphqlservice/internal/Awaitable.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +module; + +#include "Awaitable.h" + +export module GraphQL.Internal.Awaitable; + +export namespace graphql::internal { + +// clang-format off +using internal::Awaitable; +// clang-format on + +} // namespace graphql::internal diff --git a/include/graphqlservice/internal/Base64.h b/include/graphqlservice/internal/Base64.h index 163e1e84..5aaf1713 100644 --- a/include/graphqlservice/internal/Base64.h +++ b/include/graphqlservice/internal/Base64.h @@ -6,17 +6,7 @@ #ifndef GRAPHQLBASE64_H #define GRAPHQLBASE64_H -// clang-format off -#ifdef GRAPHQL_DLLEXPORTS - #ifdef IMPL_GRAPHQLRESPONSE_DLL - #define GRAPHQLRESPONSE_EXPORT __declspec(dllexport) - #else // !IMPL_GRAPHQLRESPONSE_DLL - #define GRAPHQLRESPONSE_EXPORT __declspec(dllimport) - #endif // !IMPL_GRAPHQLRESPONSE_DLL -#else // !GRAPHQL_DLLEXPORTS - #define GRAPHQLRESPONSE_EXPORT -#endif // !GRAPHQL_DLLEXPORTS -// clang-format on +#include "DllExports.h" #include #include diff --git a/include/graphqlservice/internal/Base64.ixx b/include/graphqlservice/internal/Base64.ixx new file mode 100644 index 00000000..1952d46a --- /dev/null +++ b/include/graphqlservice/internal/Base64.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +module; + +#include "Base64.h" + +export module GraphQL.Internal.Base64; + +export namespace graphql::internal { + +// clang-format off +using internal::Base64; +// clang-format on + +} // namespace graphql::internal diff --git a/include/graphqlservice/internal/DllExports.h b/include/graphqlservice/internal/DllExports.h new file mode 100644 index 00000000..b6cbfb39 --- /dev/null +++ b/include/graphqlservice/internal/DllExports.h @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +#ifndef DLLEXPORTS_H +#define DLLEXPORTS_H + +// clang-format off +#ifdef GRAPHQL_DLLEXPORTS + #ifdef IMPL_GRAPHQLCLIENT_DLL + #define GRAPHQLCLIENT_EXPORT __declspec(dllexport) + #else // !IMPL_GRAPHQLCLIENT_DLL + #define GRAPHQLCLIENT_EXPORT __declspec(dllimport) + #endif // !IMPL_GRAPHQLCLIENT_DLL + + #ifdef IMPL_GRAPHQLPEG_DLL + #define GRAPHQLPEG_EXPORT __declspec(dllexport) + #else // !IMPL_GRAPHQLPEG_DLL + #define GRAPHQLPEG_EXPORT __declspec(dllimport) + #endif // !IMPL_GRAPHQLPEG_DLL + + #ifdef IMPL_GRAPHQLRESPONSE_DLL + #define GRAPHQLRESPONSE_EXPORT __declspec(dllexport) + #else // !IMPL_GRAPHQLRESPONSE_DLL + #define GRAPHQLRESPONSE_EXPORT __declspec(dllimport) + #endif // !IMPL_GRAPHQLRESPONSE_DLL + + #ifdef IMPL_GRAPHQLSERVICE_DLL + #define GRAPHQLSERVICE_EXPORT __declspec(dllexport) + #else // !IMPL_GRAPHQLSERVICE_DLL + #define GRAPHQLSERVICE_EXPORT __declspec(dllimport) + #endif // !IMPL_GRAPHQLSERVICE_DLL + + #ifdef IMPL_JSONRESPONSE_DLL + #define JSONRESPONSE_EXPORT __declspec(dllexport) + #else // !IMPL_JSONRESPONSE_DLL + #define JSONRESPONSE_EXPORT __declspec(dllimport) + #endif // !IMPL_JSONRESPONSE_DLL +#else // !GRAPHQL_DLLEXPORTS + #define GRAPHQLCLIENT_EXPORT + #define GRAPHQLPEG_EXPORT + #define GRAPHQLRESPONSE_EXPORT + #define GRAPHQLSERVICE_EXPORT + #define JSONRESPONSE_EXPORT +#endif // !GRAPHQL_DLLEXPORTS +// clang-format on + +#endif // DLLEXPORTS_H diff --git a/include/graphqlservice/internal/Grammar.h b/include/graphqlservice/internal/Grammar.h index 8f3824b1..89bfe0a2 100644 --- a/include/graphqlservice/internal/Grammar.h +++ b/include/graphqlservice/internal/Grammar.h @@ -9,7 +9,7 @@ #ifndef GRAPHQLGRAMMAR_H #define GRAPHQLGRAMMAR_H -#include "graphqlservice/internal/SyntaxTree.h" +#include "SyntaxTree.h" #include diff --git a/include/graphqlservice/internal/Grammar.ixx b/include/graphqlservice/internal/Grammar.ixx new file mode 100644 index 00000000..2c780476 --- /dev/null +++ b/include/graphqlservice/internal/Grammar.ixx @@ -0,0 +1,256 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +module; + +#include "Grammar.h" + +export module GraphQL.Internal.Grammar; + +export namespace graphql::peg { + +// clang-format off +using namespace tao::graphqlpeg; + +using peg::for_each_child; +using peg::on_first_child; +using peg::on_first_child_if; + +using peg::alias; +using peg::alias_name; +using peg::argument; +using peg::argument_content; +using peg::argument_name; +using peg::arguments; +using peg::arguments_content; +using peg::backslash_token; +using peg::block_escape_sequence; +using peg::block_quote; +using peg::block_quote_character; +using peg::block_quote_content; +using peg::block_quote_content_lines; +using peg::block_quote_empty_line; +using peg::block_quote_line; +using peg::block_quote_line_content; +using peg::block_quote_token; +using peg::bool_value; +using peg::comment; +using peg::default_value; +using peg::default_value_content; +using peg::directive; +using peg::directive_content; +using peg::directive_name; +using peg::directives; +using peg::enum_value; +using peg::escaped_char; +using peg::escaped_unicode; +using peg::escaped_unicode_codepoint; +using peg::escaped_unicode_content; +using peg::exponent_indicator; +using peg::exponent_part; +using peg::exponent_part_content; +using peg::false_keyword; +using peg::field; +using peg::field_arguments; +using peg::field_content; +using peg::field_directives; +using peg::field_name; +using peg::field_selection_set; +using peg::field_start; +using peg::float_value; +using peg::fractional_part; +using peg::fractional_part_content; +using peg::fragment_name; +using peg::fragment_spread; +using peg::fragment_token; +using peg::ignored; +using peg::inline_fragment; +using peg::input_value; +using peg::input_value_content; +using peg::integer_part; +using peg::integer_value; +using peg::list_entry; +using peg::list_type; +using peg::list_type_content; +using peg::list_value; +using peg::list_value_content; +using peg::name; +using peg::named_type; +using peg::negative_sign; +using peg::nonnull_type; +using peg::nonzero_digit; +using peg::null_keyword; +using peg::object_field; +using peg::object_field_content; +using peg::object_field_name; +using peg::object_value; +using peg::object_value_content; +using peg::on_keyword; +using peg::operation_type; +using peg::quote_token; +using peg::sign; +using peg::source_character; +using peg::string_escape_sequence; +using peg::string_escape_sequence_content; +using peg::string_quote; +using peg::string_quote_character; +using peg::string_quote_content; +using peg::string_value; +using peg::true_keyword; +using peg::type_condition; +using peg::type_condition_content; +using peg::type_name; +using peg::type_name_content; +using peg::variable; +using peg::variable_content; +using peg::variable_definitions; +using peg::variable_definitions_content; +using peg::variable_name; +using peg::variable_name_content; +using peg::variable_value; +using peg::zero_digit; +using peg::fragement_spread_or_inline_fragment_content; +using peg::fragement_spread_or_inline_fragment; +using peg::operation_name; +using peg::selection; +using peg::selection_set; +using peg::selection_set_content; +using peg::operation_definition_operation_type_content; +using peg::arguments_definition; +using peg::arguments_definition_content; +using peg::arguments_definition_start; +using peg::description; +using peg::executable_definition; +using peg::field_definition; +using peg::field_definition_content; +using peg::field_definition_start; +using peg::fields_definition; +using peg::fields_definition_content; +using peg::fragment_definition; +using peg::fragment_definition_content; +using peg::implements_interfaces; +using peg::implements_interfaces_content; +using peg::interface_type; +using peg::object_name; +using peg::object_type_definition_object_name; +using peg::object_type_definition_start; +using peg::operation_definition; +using peg::root_operation_definition; +using peg::root_operation_definition_content; +using peg::scalar_keyword; +using peg::scalar_name; +using peg::scalar_type_definition; +using peg::scalar_type_definition_content; +using peg::scalar_type_definition_start; +using peg::schema_definition; +using peg::schema_definition_content; +using peg::schema_definition_start; +using peg::schema_keyword; +using peg::type_keyword; +using peg::object_type_definition_implements_interfaces; +using peg::interface_keyword; +using peg::interface_name; +using peg::interface_type_definition_interface_name; +using peg::interface_type_definition_start; +using peg::object_type_definition; +using peg::object_type_definition_content; +using peg::object_type_definition_directives; +using peg::object_type_definition_fields_definition; +using peg::interface_type_definition_implements_interfaces; +using peg::interface_type_definition_directives; +using peg::interface_type_definition_fields_definition; +using peg::enum_keyword; +using peg::enum_name; +using peg::enum_type_definition_directives; +using peg::enum_type_definition_name; +using peg::enum_type_definition_start; +using peg::enum_value_definition; +using peg::enum_value_definition_content; +using peg::enum_value_definition_start; +using peg::enum_values_definition; +using peg::enum_values_definition_content; +using peg::enum_values_definition_start; +using peg::interface_type_definition; +using peg::interface_type_definition_content; +using peg::union_keyword; +using peg::union_member_types; +using peg::union_member_types_content; +using peg::union_member_types_start; +using peg::union_name; +using peg::union_type; +using peg::union_type_definition; +using peg::union_type_definition_content; +using peg::union_type_definition_directives; +using peg::union_type_definition_start; +using peg::enum_type_definition_enum_values_definition; +using peg::enum_type_definition; +using peg::enum_type_definition_content; +using peg::input_field_definition; +using peg::input_field_definition_content; +using peg::input_field_definition_default_value; +using peg::input_field_definition_directives; +using peg::input_field_definition_start; +using peg::input_field_definition_type_name; +using peg::input_fields_definition; +using peg::input_fields_definition_content; +using peg::input_fields_definition_start; +using peg::input_keyword; +using peg::input_object_type_definition_directives; +using peg::input_object_type_definition_object_name; +using peg::input_object_type_definition_start; +using peg::input_object_type_definition_fields_definition; +using peg::directive_definition; +using peg::directive_definition_content; +using peg::directive_definition_start; +using peg::directive_location; +using peg::directive_locations; +using peg::executable_directive_location; +using peg::extend_keyword; +using peg::input_object_type_definition; +using peg::input_object_type_definition_content; +using peg::operation_type_definition; +using peg::repeatable_keyword; +using peg::schema_extension_start; +using peg::type_definition; +using peg::type_system_definition; +using peg::type_system_directive_location; +using peg::schema_extension_operation_type_definitions; +using peg::object_type_extension_start; +using peg::scalar_type_extension; +using peg::scalar_type_extension_content; +using peg::scalar_type_extension_start; +using peg::schema_extension; +using peg::schema_extension_content; +using peg::object_type_extension_implements_interfaces; +using peg::interface_type_extension_start; +using peg::object_type_extension; +using peg::object_type_extension_content; +using peg::object_type_extension_directives; +using peg::object_type_extension_fields_definition; +using peg::interface_type_extension_implements_interfaces; +using peg::interface_type_extension_directives; +using peg::interface_type_extension_fields_definition; +using peg::enum_type_extension; +using peg::enum_type_extension_content; +using peg::enum_type_extension_start; +using peg::executable_document; +using peg::executable_document_content; +using peg::input_object_type_extension; +using peg::input_object_type_extension_content; +using peg::input_object_type_extension_start; +using peg::interface_type_extension; +using peg::interface_type_extension_content; +using peg::mixed_definition; +using peg::mixed_document; +using peg::mixed_document_content; +using peg::schema_document; +using peg::schema_document_content; +using peg::schema_type_definition; +using peg::type_extension; +using peg::type_system_extension; +using peg::union_type_extension; +using peg::union_type_extension_content; +using peg::union_type_extension_start; +// clang-format on + +} // namespace graphql::peg diff --git a/include/graphqlservice/internal/Introspection.h b/include/graphqlservice/internal/Introspection.h index 885af384..e7b926f1 100644 --- a/include/graphqlservice/internal/Introspection.h +++ b/include/graphqlservice/internal/Introspection.h @@ -8,7 +8,8 @@ #include "graphqlservice/introspection/IntrospectionSchema.h" -#include "graphqlservice/internal/Schema.h" +#include "DllExports.h" +#include "Schema.h" namespace graphql::introspection { diff --git a/include/graphqlservice/internal/Introspection.ixx b/include/graphqlservice/internal/Introspection.ixx new file mode 100644 index 00000000..3dab858d --- /dev/null +++ b/include/graphqlservice/internal/Introspection.ixx @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +module; + +#include "Introspection.h" + +export module GraphQL.Internal.Introspection; + +export namespace graphql::introspection { + +// clang-format off +using introspection::Schema; +using introspection::Type; +using introspection::Field; +using introspection::InputValue; +using introspection::EnumValue; +using introspection::Directive; +// clang-format on + +} // namespace graphql::introspection diff --git a/include/graphqlservice/internal/Schema.h b/include/graphqlservice/internal/Schema.h index 24480943..b38d5303 100644 --- a/include/graphqlservice/internal/Schema.h +++ b/include/graphqlservice/internal/Schema.h @@ -6,9 +6,14 @@ #ifndef GRAPHQLSCHEMA_H #define GRAPHQLSCHEMA_H -#include "graphqlservice/GraphQLService.h" +#include "DllExports.h" +#include "SortedMap.h" +#include +#include #include +#include +#include namespace graphql { namespace introspection { @@ -40,6 +45,9 @@ class [[nodiscard("unnecessary construction")]] Schema : public std::enable_shar GRAPHQLSERVICE_EXPORT explicit Schema( bool noIntrospection = false, std::string_view description = ""); + [[nodiscard("unnecessary call")]] GRAPHQLSERVICE_EXPORT std::shared_ptr StitchSchema( + const std::shared_ptr& added) const; + GRAPHQLSERVICE_EXPORT void AddQueryType(std::shared_ptr query); GRAPHQLSERVICE_EXPORT void AddMutationType(std::shared_ptr mutation); GRAPHQLSERVICE_EXPORT void AddSubscriptionType(std::shared_ptr subscription); @@ -69,13 +77,16 @@ class [[nodiscard("unnecessary construction")]] Schema : public std::enable_shar directives() const noexcept; private: + [[nodiscard("unnecessary call")]] std::shared_ptr StitchFieldType( + std::shared_ptr fieldType); + const bool _noIntrospection = false; const std::string_view _description; std::shared_ptr _query; std::shared_ptr _mutation; std::shared_ptr _subscription; - internal::string_view_map _typeMap; + internal::string_view_map _typeMap; std::vector>> _types; std::vector> _directives; std::shared_mutex _nonNullWrappersMutex; diff --git a/include/graphqlservice/internal/Schema.ixx b/include/graphqlservice/internal/Schema.ixx new file mode 100644 index 00000000..19cf2ff3 --- /dev/null +++ b/include/graphqlservice/internal/Schema.ixx @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +module; + +#include "Schema.h" + +export module GraphQL.Internal.Schema; + +export namespace graphql { + +namespace introspection { + +// clang-format off +using introspection::TypeKind; +using introspection::DirectiveLocation; +// clang-format on + +} // namespace introspection + +namespace schema { + +// clang-format off +using schema::Schema; +using schema::BaseType; +using schema::ScalarType; +using schema::ObjectType; +using schema::InterfaceType; +using schema::UnionType; +using schema::EnumValueType; +using schema::EnumType; +using schema::InputObjectType; +using schema::WrapperType; +using schema::Field; +using schema::InputValue; +using schema::EnumValue; +using schema::Directive; +// clang-format on + +} // namespace schema + +} // namespace graphql diff --git a/include/graphqlservice/internal/SortedMap.h b/include/graphqlservice/internal/SortedMap.h index 50e49dec..7fbb39c6 100644 --- a/include/graphqlservice/internal/SortedMap.h +++ b/include/graphqlservice/internal/SortedMap.h @@ -7,6 +7,7 @@ #define GRAPHQLSORTEDMAP_H #include +#include #include #include #include @@ -67,7 +68,7 @@ class [[nodiscard("unnecessary construction")]] sorted_map constexpr sorted_map() noexcept = default; constexpr sorted_map(const sorted_map& other) = default; - sorted_map(sorted_map && other) noexcept = default; + sorted_map(sorted_map&& other) noexcept = default; constexpr sorted_map(std::initializer_list> init) : _data { init } @@ -82,18 +83,18 @@ class [[nodiscard("unnecessary construction")]] sorted_map sorted_map& operator=(const sorted_map& rhs) = default; sorted_map& operator=(sorted_map&& rhs) noexcept = default; - [[nodiscard("unnecessary call")]] constexpr bool operator==(const sorted_map& rhs) - const noexcept + [[nodiscard("unnecessary call")]] constexpr bool operator==( + const sorted_map& rhs) const noexcept { return _data == rhs._data; } - void reserve(size_t size) + void reserve(std::size_t size) { _data.reserve(size); } - [[nodiscard("unnecessary call")]] constexpr size_t capacity() const noexcept + [[nodiscard("unnecessary call")]] constexpr std::size_t capacity() const noexcept { return _data.capacity(); } @@ -108,7 +109,7 @@ class [[nodiscard("unnecessary construction")]] sorted_map return _data.empty(); } - [[nodiscard("unnecessary call")]] constexpr size_t size() const noexcept + [[nodiscard("unnecessary call")]] constexpr std::size_t size() const noexcept { return _data.size(); } @@ -141,7 +142,7 @@ class [[nodiscard("unnecessary construction")]] sorted_map } template - [[nodiscard("unnecessary call")]] constexpr const_iterator find(KeyArg && keyArg) const noexcept + [[nodiscard("unnecessary call")]] constexpr const_iterator find(KeyArg&& keyArg) const noexcept { const K key { std::forward(keyArg) }; @@ -149,7 +150,7 @@ class [[nodiscard("unnecessary construction")]] sorted_map } template - std::pair emplace(KeyArg && keyArg, ValueArgs && ... args) noexcept + std::pair emplace(KeyArg&& keyArg, ValueArgs&&... args) noexcept { K key { std::forward(keyArg) }; const auto [itr, itrEnd] = sorted_map_equal_range(_data.begin(), _data.end(), key); @@ -176,7 +177,7 @@ class [[nodiscard("unnecessary construction")]] sorted_map } template - const_iterator erase(KeyArg && keyArg) noexcept + const_iterator erase(KeyArg&& keyArg) noexcept { const K key { std::forward(keyArg) }; @@ -203,7 +204,7 @@ class [[nodiscard("unnecessary construction")]] sorted_map } template - [[nodiscard("unnecessary call")]] V& at(KeyArg && keyArg) + [[nodiscard("unnecessary call")]] V& at(KeyArg&& keyArg) { const K key { std::forward(keyArg) }; const auto [itr, itrEnd] = sorted_map_equal_range(_data.begin(), _data.end(), key); @@ -230,7 +231,7 @@ class [[nodiscard("unnecessary construction")]] sorted_set constexpr sorted_set() noexcept = default; constexpr sorted_set(const sorted_set& other) = default; - sorted_set(sorted_set && other) noexcept = default; + sorted_set(sorted_set&& other) noexcept = default; constexpr sorted_set(std::initializer_list init) : _data { init } @@ -243,18 +244,18 @@ class [[nodiscard("unnecessary construction")]] sorted_set sorted_set& operator=(const sorted_set& rhs) = default; sorted_set& operator=(sorted_set&& rhs) noexcept = default; - [[nodiscard("unnecessary call")]] constexpr bool operator==(const sorted_set& rhs) - const noexcept + [[nodiscard("unnecessary call")]] constexpr bool operator==( + const sorted_set& rhs) const noexcept { return _data == rhs._data; } - void reserve(size_t size) + void reserve(std::size_t size) { _data.reserve(size); } - [[nodiscard("unnecessary call")]] constexpr size_t capacity() const noexcept + [[nodiscard("unnecessary call")]] constexpr std::size_t capacity() const noexcept { return _data.capacity(); } @@ -269,7 +270,7 @@ class [[nodiscard("unnecessary construction")]] sorted_set return _data.empty(); } - [[nodiscard("unnecessary call")]] constexpr size_t size() const noexcept + [[nodiscard("unnecessary call")]] constexpr std::size_t size() const noexcept { return _data.size(); } @@ -304,7 +305,7 @@ class [[nodiscard("unnecessary construction")]] sorted_set } template - [[nodiscard("unnecessary call")]] constexpr const_iterator find(Arg && arg) const noexcept + [[nodiscard("unnecessary call")]] constexpr const_iterator find(Arg&& arg) const noexcept { const K key { std::forward(arg) }; @@ -312,7 +313,7 @@ class [[nodiscard("unnecessary construction")]] sorted_set } template - std::pair emplace(Arg && key) noexcept + std::pair emplace(Arg&& key) noexcept { const auto [itr, itrEnd] = std::equal_range(_data.begin(), _data.end(), key, [](K lhs, K rhs) noexcept { @@ -343,7 +344,7 @@ class [[nodiscard("unnecessary construction")]] sorted_set } template - const_iterator erase(Arg && arg) noexcept + const_iterator erase(Arg&& arg) noexcept { const K key { std::forward(arg) }; @@ -359,13 +360,14 @@ class [[nodiscard("unnecessary construction")]] sorted_set vector_type _data; }; -struct [[nodiscard("unnecessary construction")]] shorter_or_less { +struct [[nodiscard("unnecessary construction")]] shorter_or_less +{ [[nodiscard("unnecessary call")]] constexpr bool operator()( - std::string_view lhs, std::string_view rhs) - const noexcept { return lhs.size() == rhs.size() ? lhs < rhs : lhs.size() < rhs.size(); -} // namespace graphql::internal -} -; + std::string_view lhs, std::string_view rhs) const noexcept + { + return lhs.size() == rhs.size() ? lhs < rhs : lhs.size() < rhs.size(); + } // namespace graphql::internal +}; template using string_view_map = sorted_map; diff --git a/include/graphqlservice/internal/SortedMap.ixx b/include/graphqlservice/internal/SortedMap.ixx new file mode 100644 index 00000000..156dd864 --- /dev/null +++ b/include/graphqlservice/internal/SortedMap.ixx @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +module; + +#include "SortedMap.h" + +export module GraphQL.Internal.SortedMap; + +export namespace graphql::internal { + +// clang-format off +using internal::sorted_map_key; +using internal::sorted_map_equal_range; +using internal::sorted_map_lookup; +using internal::sorted_map; +using internal::sorted_set; +using internal::shorter_or_less; +using internal::string_view_map; +using internal::string_view_set; +// clang-format on + +} // namespace graphql::internal diff --git a/include/graphqlservice/internal/SyntaxTree.h b/include/graphqlservice/internal/SyntaxTree.h index 7e9be03a..268eb544 100644 --- a/include/graphqlservice/internal/SyntaxTree.h +++ b/include/graphqlservice/internal/SyntaxTree.h @@ -6,13 +6,14 @@ #ifndef GRAPHQLSYNTAXTREE_H #define GRAPHQLSYNTAXTREE_H -#include "graphqlservice/GraphQLParse.h" +#include "DllExports.h" #define TAO_PEGTL_NAMESPACE tao::graphqlpeg #include #include +#include #include #include #include @@ -69,17 +70,17 @@ class [[nodiscard("unnecessary construction")]] ast_node : public parse_tree::ba } template - [[nodiscard("unnecessary call")]] static size_t type_hash() noexcept + [[nodiscard("unnecessary call")]] static std::size_t type_hash() noexcept { // This is cached in a static local variable per-specialization, but each module may have // its own instance of the specialization and the local variable. - static const size_t hash = std::hash {}(type_name()); + static const std::size_t hash = std::hash {}(type_name()); return hash; } std::string_view _type_name; - size_t _type_hash = 0; + std::size_t _type_hash = 0; using unescaped_t = std::variant; diff --git a/include/graphqlservice/internal/SyntaxTree.ixx b/include/graphqlservice/internal/SyntaxTree.ixx new file mode 100644 index 00000000..1fe4f213 --- /dev/null +++ b/include/graphqlservice/internal/SyntaxTree.ixx @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +module; + +#include "SyntaxTree.h" + +export module GraphQL.Internal.SyntaxTree; + +export namespace graphql::peg { + +// clang-format off +using namespace tao::graphqlpeg; +namespace peginternal = tao::graphqlpeg::internal; + +using peg::ast_node; +// clang-format on + +} // namespace graphql::peg diff --git a/include/graphqlservice/internal/Version.h b/include/graphqlservice/internal/Version.h index d42d635b..e99dc41c 100644 --- a/include/graphqlservice/internal/Version.h +++ b/include/graphqlservice/internal/Version.h @@ -6,15 +6,20 @@ #ifndef GRAPHQLVERSION_H #define GRAPHQLVERSION_H +#include #include namespace graphql::internal { -constexpr std::string_view FullVersion { "4.5.9" }; +inline namespace version { -constexpr size_t MajorVersion = 4; -constexpr size_t MinorVersion = 5; -constexpr size_t PatchVersion = 9; +constexpr std::string_view FullVersion { "5.0.0" }; + +constexpr std::size_t MajorVersion = 5; +constexpr std::size_t MinorVersion = 0; +constexpr std::size_t PatchVersion = 0; + +} // namespace version } // namespace graphql::internal diff --git a/include/graphqlservice/internal/Version.ixx b/include/graphqlservice/internal/Version.ixx new file mode 100644 index 00000000..5fd825f0 --- /dev/null +++ b/include/graphqlservice/internal/Version.ixx @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +module; + +#include "Version.h" + +export module GraphQL.Internal.Version; + +export namespace graphql::internal { + +// clang-format off +constexpr std::string_view FullVersion = version::FullVersion; + +constexpr std::size_t MajorVersion = version::MajorVersion; +constexpr std::size_t MinorVersion = version::MinorVersion; +constexpr std::size_t PatchVersion = version::PatchVersion; +// clang-format on + +} // namespace graphql::internal diff --git a/include/graphqlservice/introspection/DirectiveObject.h b/include/graphqlservice/introspection/DirectiveObject.h index 20a0620e..f9714caf 100644 --- a/include/graphqlservice/introspection/DirectiveObject.h +++ b/include/graphqlservice/introspection/DirectiveObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef DIRECTIVEOBJECT_H -#define DIRECTIVEOBJECT_H +#ifndef INTROSPECTION_DIRECTIVEOBJECT_H +#define INTROSPECTION_DIRECTIVEOBJECT_H #include "IntrospectionSchema.h" @@ -85,4 +85,4 @@ class [[nodiscard("unnecessary construction")]] Directive final } // namespace graphql::introspection::object -#endif // DIRECTIVEOBJECT_H +#endif // INTROSPECTION_DIRECTIVEOBJECT_H diff --git a/include/graphqlservice/introspection/DirectiveObject.ixx b/include/graphqlservice/introspection/DirectiveObject.ixx new file mode 100644 index 00000000..8a00c78d --- /dev/null +++ b/include/graphqlservice/introspection/DirectiveObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "DirectiveObject.h" + +export module GraphQL.Introspection.DirectiveObject; + +export namespace graphql::introspection::object { + +using object::Directive; + +} // namespace graphql::introspection::object diff --git a/include/graphqlservice/introspection/EnumValueObject.h b/include/graphqlservice/introspection/EnumValueObject.h index 0ac9e74f..a97c9534 100644 --- a/include/graphqlservice/introspection/EnumValueObject.h +++ b/include/graphqlservice/introspection/EnumValueObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef ENUMVALUEOBJECT_H -#define ENUMVALUEOBJECT_H +#ifndef INTROSPECTION_ENUMVALUEOBJECT_H +#define INTROSPECTION_ENUMVALUEOBJECT_H #include "IntrospectionSchema.h" @@ -78,4 +78,4 @@ class [[nodiscard("unnecessary construction")]] EnumValue final } // namespace graphql::introspection::object -#endif // ENUMVALUEOBJECT_H +#endif // INTROSPECTION_ENUMVALUEOBJECT_H diff --git a/include/graphqlservice/introspection/EnumValueObject.ixx b/include/graphqlservice/introspection/EnumValueObject.ixx new file mode 100644 index 00000000..45be5d13 --- /dev/null +++ b/include/graphqlservice/introspection/EnumValueObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "EnumValueObject.h" + +export module GraphQL.Introspection.EnumValueObject; + +export namespace graphql::introspection::object { + +using object::EnumValue; + +} // namespace graphql::introspection::object diff --git a/include/graphqlservice/introspection/FieldObject.h b/include/graphqlservice/introspection/FieldObject.h index f835c5ae..07fba6ae 100644 --- a/include/graphqlservice/introspection/FieldObject.h +++ b/include/graphqlservice/introspection/FieldObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef FIELDOBJECT_H -#define FIELDOBJECT_H +#ifndef INTROSPECTION_FIELDOBJECT_H +#define INTROSPECTION_FIELDOBJECT_H #include "IntrospectionSchema.h" @@ -92,4 +92,4 @@ class [[nodiscard("unnecessary construction")]] Field final } // namespace graphql::introspection::object -#endif // FIELDOBJECT_H +#endif // INTROSPECTION_FIELDOBJECT_H diff --git a/include/graphqlservice/introspection/FieldObject.ixx b/include/graphqlservice/introspection/FieldObject.ixx new file mode 100644 index 00000000..aa5c8305 --- /dev/null +++ b/include/graphqlservice/introspection/FieldObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "FieldObject.h" + +export module GraphQL.Introspection.FieldObject; + +export namespace graphql::introspection::object { + +using object::Field; + +} // namespace graphql::introspection::object diff --git a/include/graphqlservice/introspection/InputValueObject.h b/include/graphqlservice/introspection/InputValueObject.h index 70faa321..8dda3e79 100644 --- a/include/graphqlservice/introspection/InputValueObject.h +++ b/include/graphqlservice/introspection/InputValueObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef INPUTVALUEOBJECT_H -#define INPUTVALUEOBJECT_H +#ifndef INTROSPECTION_INPUTVALUEOBJECT_H +#define INTROSPECTION_INPUTVALUEOBJECT_H #include "IntrospectionSchema.h" @@ -78,4 +78,4 @@ class [[nodiscard("unnecessary construction")]] InputValue final } // namespace graphql::introspection::object -#endif // INPUTVALUEOBJECT_H +#endif // INTROSPECTION_INPUTVALUEOBJECT_H diff --git a/include/graphqlservice/introspection/InputValueObject.ixx b/include/graphqlservice/introspection/InputValueObject.ixx new file mode 100644 index 00000000..252760df --- /dev/null +++ b/include/graphqlservice/introspection/InputValueObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "InputValueObject.h" + +export module GraphQL.Introspection.InputValueObject; + +export namespace graphql::introspection::object { + +using object::InputValue; + +} // namespace graphql::introspection::object diff --git a/include/graphqlservice/introspection/IntrospectionSchema.h b/include/graphqlservice/introspection/IntrospectionSchema.h index 4adaa9af..b39fe13f 100644 --- a/include/graphqlservice/introspection/IntrospectionSchema.h +++ b/include/graphqlservice/introspection/IntrospectionSchema.h @@ -8,141 +8,25 @@ #ifndef INTROSPECTIONSCHEMA_H #define INTROSPECTIONSCHEMA_H +#include "graphqlservice/GraphQLResponse.h" +#include "graphqlservice/GraphQLService.h" + +#include "graphqlservice/internal/DllExports.h" +#include "graphqlservice/internal/Version.h" #include "graphqlservice/internal/Schema.h" -// Check if the library version is compatible with schemagen 4.5.0 -static_assert(graphql::internal::MajorVersion == 4, "regenerate with schemagen: major version mismatch"); -static_assert(graphql::internal::MinorVersion == 5, "regenerate with schemagen: minor version mismatch"); +#include "IntrospectionSharedTypes.h" #include #include #include #include -namespace graphql { -namespace introspection { - -enum class TypeKind -{ - SCALAR, - OBJECT, - INTERFACE, - UNION, - ENUM, - INPUT_OBJECT, - LIST, - NON_NULL -}; - -[[nodiscard("unnecessary call")]] constexpr auto getTypeKindNames() noexcept -{ - using namespace std::literals; - - return std::array { - R"gql(SCALAR)gql"sv, - R"gql(OBJECT)gql"sv, - R"gql(INTERFACE)gql"sv, - R"gql(UNION)gql"sv, - R"gql(ENUM)gql"sv, - R"gql(INPUT_OBJECT)gql"sv, - R"gql(LIST)gql"sv, - R"gql(NON_NULL)gql"sv - }; -} - -[[nodiscard("unnecessary call")]] constexpr auto getTypeKindValues() noexcept -{ - using namespace std::literals; - - return std::array, 8> { - std::make_pair(R"gql(ENUM)gql"sv, TypeKind::ENUM), - std::make_pair(R"gql(LIST)gql"sv, TypeKind::LIST), - std::make_pair(R"gql(UNION)gql"sv, TypeKind::UNION), - std::make_pair(R"gql(OBJECT)gql"sv, TypeKind::OBJECT), - std::make_pair(R"gql(SCALAR)gql"sv, TypeKind::SCALAR), - std::make_pair(R"gql(NON_NULL)gql"sv, TypeKind::NON_NULL), - std::make_pair(R"gql(INTERFACE)gql"sv, TypeKind::INTERFACE), - std::make_pair(R"gql(INPUT_OBJECT)gql"sv, TypeKind::INPUT_OBJECT) - }; -} - -enum class DirectiveLocation -{ - QUERY, - MUTATION, - SUBSCRIPTION, - FIELD, - FRAGMENT_DEFINITION, - FRAGMENT_SPREAD, - INLINE_FRAGMENT, - VARIABLE_DEFINITION, - SCHEMA, - SCALAR, - OBJECT, - FIELD_DEFINITION, - ARGUMENT_DEFINITION, - INTERFACE, - UNION, - ENUM, - ENUM_VALUE, - INPUT_OBJECT, - INPUT_FIELD_DEFINITION -}; - -[[nodiscard("unnecessary call")]] constexpr auto getDirectiveLocationNames() noexcept -{ - using namespace std::literals; - - return std::array { - R"gql(QUERY)gql"sv, - R"gql(MUTATION)gql"sv, - R"gql(SUBSCRIPTION)gql"sv, - R"gql(FIELD)gql"sv, - R"gql(FRAGMENT_DEFINITION)gql"sv, - R"gql(FRAGMENT_SPREAD)gql"sv, - R"gql(INLINE_FRAGMENT)gql"sv, - R"gql(VARIABLE_DEFINITION)gql"sv, - R"gql(SCHEMA)gql"sv, - R"gql(SCALAR)gql"sv, - R"gql(OBJECT)gql"sv, - R"gql(FIELD_DEFINITION)gql"sv, - R"gql(ARGUMENT_DEFINITION)gql"sv, - R"gql(INTERFACE)gql"sv, - R"gql(UNION)gql"sv, - R"gql(ENUM)gql"sv, - R"gql(ENUM_VALUE)gql"sv, - R"gql(INPUT_OBJECT)gql"sv, - R"gql(INPUT_FIELD_DEFINITION)gql"sv - }; -} - -[[nodiscard("unnecessary call")]] constexpr auto getDirectiveLocationValues() noexcept -{ - using namespace std::literals; - - return std::array, 19> { - std::make_pair(R"gql(ENUM)gql"sv, DirectiveLocation::ENUM), - std::make_pair(R"gql(FIELD)gql"sv, DirectiveLocation::FIELD), - std::make_pair(R"gql(QUERY)gql"sv, DirectiveLocation::QUERY), - std::make_pair(R"gql(UNION)gql"sv, DirectiveLocation::UNION), - std::make_pair(R"gql(OBJECT)gql"sv, DirectiveLocation::OBJECT), - std::make_pair(R"gql(SCALAR)gql"sv, DirectiveLocation::SCALAR), - std::make_pair(R"gql(SCHEMA)gql"sv, DirectiveLocation::SCHEMA), - std::make_pair(R"gql(MUTATION)gql"sv, DirectiveLocation::MUTATION), - std::make_pair(R"gql(INTERFACE)gql"sv, DirectiveLocation::INTERFACE), - std::make_pair(R"gql(ENUM_VALUE)gql"sv, DirectiveLocation::ENUM_VALUE), - std::make_pair(R"gql(INPUT_OBJECT)gql"sv, DirectiveLocation::INPUT_OBJECT), - std::make_pair(R"gql(SUBSCRIPTION)gql"sv, DirectiveLocation::SUBSCRIPTION), - std::make_pair(R"gql(FRAGMENT_SPREAD)gql"sv, DirectiveLocation::FRAGMENT_SPREAD), - std::make_pair(R"gql(INLINE_FRAGMENT)gql"sv, DirectiveLocation::INLINE_FRAGMENT), - std::make_pair(R"gql(FIELD_DEFINITION)gql"sv, DirectiveLocation::FIELD_DEFINITION), - std::make_pair(R"gql(ARGUMENT_DEFINITION)gql"sv, DirectiveLocation::ARGUMENT_DEFINITION), - std::make_pair(R"gql(FRAGMENT_DEFINITION)gql"sv, DirectiveLocation::FRAGMENT_DEFINITION), - std::make_pair(R"gql(VARIABLE_DEFINITION)gql"sv, DirectiveLocation::VARIABLE_DEFINITION), - std::make_pair(R"gql(INPUT_FIELD_DEFINITION)gql"sv, DirectiveLocation::INPUT_FIELD_DEFINITION) - }; -} +// Check if the library version is compatible with schemagen 5.0.0 +static_assert(graphql::internal::MajorVersion == 5, "regenerate with schemagen: major version mismatch"); +static_assert(graphql::internal::MinorVersion == 0, "regenerate with schemagen: minor version mismatch"); +namespace graphql::introspection { class Schema; class Type; class Field; @@ -170,33 +54,6 @@ void AddDirectiveDetails(const std::shared_ptr& typeDirectiv GRAPHQLSERVICE_EXPORT void AddTypesToSchema(const std::shared_ptr& schema); -} // namespace introspection - -namespace service { - -#ifdef GRAPHQL_DLLEXPORTS -// Export all of the built-in converters -template <> -GRAPHQLSERVICE_EXPORT introspection::TypeKind Argument::convert( - const response::Value& value); -template <> -GRAPHQLSERVICE_EXPORT AwaitableResolver Result::convert( - AwaitableScalar result, ResolverParams&& params); -template <> -GRAPHQLSERVICE_EXPORT void Result::validateScalar( - const response::Value& value); -template <> -GRAPHQLSERVICE_EXPORT introspection::DirectiveLocation Argument::convert( - const response::Value& value); -template <> -GRAPHQLSERVICE_EXPORT AwaitableResolver Result::convert( - AwaitableScalar result, ResolverParams&& params); -template <> -GRAPHQLSERVICE_EXPORT void Result::validateScalar( - const response::Value& value); -#endif // GRAPHQL_DLLEXPORTS - -} // namespace service -} // namespace graphql +} // namespace graphql::introspection #endif // INTROSPECTIONSCHEMA_H diff --git a/include/graphqlservice/introspection/IntrospectionSchema.ixx b/include/graphqlservice/introspection/IntrospectionSchema.ixx new file mode 100644 index 00000000..8ce51120 --- /dev/null +++ b/include/graphqlservice/introspection/IntrospectionSchema.ixx @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "IntrospectionSchema.h" + +export module GraphQL.Introspection.IntrospectionSchema; + +export import GraphQL.Introspection.IntrospectionSharedTypes; + +export import GraphQL.Introspection.SchemaObject; +export import GraphQL.Introspection.TypeObject; +export import GraphQL.Introspection.FieldObject; +export import GraphQL.Introspection.InputValueObject; +export import GraphQL.Introspection.EnumValueObject; +export import GraphQL.Introspection.DirectiveObject; + +export namespace graphql::introspection { + +using introspection::AddSchemaDetails; +using introspection::AddTypeDetails; +using introspection::AddFieldDetails; +using introspection::AddInputValueDetails; +using introspection::AddEnumValueDetails; +using introspection::AddDirectiveDetails; + +using introspection::AddTypesToSchema; + +} // namespace graphql::introspection diff --git a/include/graphqlservice/introspection/IntrospectionSharedTypes.h b/include/graphqlservice/introspection/IntrospectionSharedTypes.h new file mode 100644 index 00000000..9e1d5acb --- /dev/null +++ b/include/graphqlservice/introspection/IntrospectionSharedTypes.h @@ -0,0 +1,180 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +#pragma once + +#ifndef INTROSPECTIONSHAREDTYPES_H +#define INTROSPECTIONSHAREDTYPES_H + +#include "graphqlservice/GraphQLResponse.h" + +#include "graphqlservice/internal/DllExports.h" +#include "graphqlservice/internal/Version.h" + +#include +#include +#include +#include +#include +#include + +// Check if the library version is compatible with schemagen 5.0.0 +static_assert(graphql::internal::MajorVersion == 5, "regenerate with schemagen: major version mismatch"); +static_assert(graphql::internal::MinorVersion == 0, "regenerate with schemagen: minor version mismatch"); + +namespace graphql { +namespace introspection { + +enum class TypeKind +{ + SCALAR, + OBJECT, + INTERFACE, + UNION, + ENUM, + INPUT_OBJECT, + LIST, + NON_NULL +}; + +[[nodiscard("unnecessary call")]] constexpr auto getTypeKindNames() noexcept +{ + using namespace std::literals; + + return std::array { + R"gql(SCALAR)gql"sv, + R"gql(OBJECT)gql"sv, + R"gql(INTERFACE)gql"sv, + R"gql(UNION)gql"sv, + R"gql(ENUM)gql"sv, + R"gql(INPUT_OBJECT)gql"sv, + R"gql(LIST)gql"sv, + R"gql(NON_NULL)gql"sv + }; +} + +[[nodiscard("unnecessary call")]] constexpr auto getTypeKindValues() noexcept +{ + using namespace std::literals; + + return std::array, 8> { + std::make_pair(R"gql(ENUM)gql"sv, TypeKind::ENUM), + std::make_pair(R"gql(LIST)gql"sv, TypeKind::LIST), + std::make_pair(R"gql(UNION)gql"sv, TypeKind::UNION), + std::make_pair(R"gql(OBJECT)gql"sv, TypeKind::OBJECT), + std::make_pair(R"gql(SCALAR)gql"sv, TypeKind::SCALAR), + std::make_pair(R"gql(NON_NULL)gql"sv, TypeKind::NON_NULL), + std::make_pair(R"gql(INTERFACE)gql"sv, TypeKind::INTERFACE), + std::make_pair(R"gql(INPUT_OBJECT)gql"sv, TypeKind::INPUT_OBJECT) + }; +} + +enum class DirectiveLocation +{ + QUERY, + MUTATION, + SUBSCRIPTION, + FIELD, + FRAGMENT_DEFINITION, + FRAGMENT_SPREAD, + INLINE_FRAGMENT, + VARIABLE_DEFINITION, + SCHEMA, + SCALAR, + OBJECT, + FIELD_DEFINITION, + ARGUMENT_DEFINITION, + INTERFACE, + UNION, + ENUM, + ENUM_VALUE, + INPUT_OBJECT, + INPUT_FIELD_DEFINITION +}; + +[[nodiscard("unnecessary call")]] constexpr auto getDirectiveLocationNames() noexcept +{ + using namespace std::literals; + + return std::array { + R"gql(QUERY)gql"sv, + R"gql(MUTATION)gql"sv, + R"gql(SUBSCRIPTION)gql"sv, + R"gql(FIELD)gql"sv, + R"gql(FRAGMENT_DEFINITION)gql"sv, + R"gql(FRAGMENT_SPREAD)gql"sv, + R"gql(INLINE_FRAGMENT)gql"sv, + R"gql(VARIABLE_DEFINITION)gql"sv, + R"gql(SCHEMA)gql"sv, + R"gql(SCALAR)gql"sv, + R"gql(OBJECT)gql"sv, + R"gql(FIELD_DEFINITION)gql"sv, + R"gql(ARGUMENT_DEFINITION)gql"sv, + R"gql(INTERFACE)gql"sv, + R"gql(UNION)gql"sv, + R"gql(ENUM)gql"sv, + R"gql(ENUM_VALUE)gql"sv, + R"gql(INPUT_OBJECT)gql"sv, + R"gql(INPUT_FIELD_DEFINITION)gql"sv + }; +} + +[[nodiscard("unnecessary call")]] constexpr auto getDirectiveLocationValues() noexcept +{ + using namespace std::literals; + + return std::array, 19> { + std::make_pair(R"gql(ENUM)gql"sv, DirectiveLocation::ENUM), + std::make_pair(R"gql(FIELD)gql"sv, DirectiveLocation::FIELD), + std::make_pair(R"gql(QUERY)gql"sv, DirectiveLocation::QUERY), + std::make_pair(R"gql(UNION)gql"sv, DirectiveLocation::UNION), + std::make_pair(R"gql(OBJECT)gql"sv, DirectiveLocation::OBJECT), + std::make_pair(R"gql(SCALAR)gql"sv, DirectiveLocation::SCALAR), + std::make_pair(R"gql(SCHEMA)gql"sv, DirectiveLocation::SCHEMA), + std::make_pair(R"gql(MUTATION)gql"sv, DirectiveLocation::MUTATION), + std::make_pair(R"gql(INTERFACE)gql"sv, DirectiveLocation::INTERFACE), + std::make_pair(R"gql(ENUM_VALUE)gql"sv, DirectiveLocation::ENUM_VALUE), + std::make_pair(R"gql(INPUT_OBJECT)gql"sv, DirectiveLocation::INPUT_OBJECT), + std::make_pair(R"gql(SUBSCRIPTION)gql"sv, DirectiveLocation::SUBSCRIPTION), + std::make_pair(R"gql(FRAGMENT_SPREAD)gql"sv, DirectiveLocation::FRAGMENT_SPREAD), + std::make_pair(R"gql(INLINE_FRAGMENT)gql"sv, DirectiveLocation::INLINE_FRAGMENT), + std::make_pair(R"gql(FIELD_DEFINITION)gql"sv, DirectiveLocation::FIELD_DEFINITION), + std::make_pair(R"gql(ARGUMENT_DEFINITION)gql"sv, DirectiveLocation::ARGUMENT_DEFINITION), + std::make_pair(R"gql(FRAGMENT_DEFINITION)gql"sv, DirectiveLocation::FRAGMENT_DEFINITION), + std::make_pair(R"gql(VARIABLE_DEFINITION)gql"sv, DirectiveLocation::VARIABLE_DEFINITION), + std::make_pair(R"gql(INPUT_FIELD_DEFINITION)gql"sv, DirectiveLocation::INPUT_FIELD_DEFINITION) + }; +} + +} // namespace introspection + +namespace service { + +#ifdef GRAPHQL_DLLEXPORTS +// Export all of the built-in converters +template <> +GRAPHQLSERVICE_EXPORT introspection::TypeKind Argument::convert( + const response::Value& value); +template <> +GRAPHQLSERVICE_EXPORT AwaitableResolver Result::convert( + AwaitableScalar result, ResolverParams&& params); +template <> +GRAPHQLSERVICE_EXPORT void Result::validateScalar( + const response::Value& value); +template <> +GRAPHQLSERVICE_EXPORT introspection::DirectiveLocation Argument::convert( + const response::Value& value); +template <> +GRAPHQLSERVICE_EXPORT AwaitableResolver Result::convert( + AwaitableScalar result, ResolverParams&& params); +template <> +GRAPHQLSERVICE_EXPORT void Result::validateScalar( + const response::Value& value); +#endif // GRAPHQL_DLLEXPORTS + +} // namespace service +} // namespace graphql + +#endif // INTROSPECTIONSHAREDTYPES_H diff --git a/include/graphqlservice/introspection/IntrospectionSharedTypes.ixx b/include/graphqlservice/introspection/IntrospectionSharedTypes.ixx new file mode 100644 index 00000000..60538148 --- /dev/null +++ b/include/graphqlservice/introspection/IntrospectionSharedTypes.ixx @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "IntrospectionSharedTypes.h" + +export module GraphQL.Introspection.IntrospectionSharedTypes; + +export namespace graphql::introspection { + +using introspection::TypeKind; +using introspection::getTypeKindNames; +using introspection::getTypeKindValues; + +using introspection::DirectiveLocation; +using introspection::getDirectiveLocationNames; +using introspection::getDirectiveLocationValues; + +} // namespace graphql::introspection diff --git a/include/graphqlservice/introspection/SchemaObject.h b/include/graphqlservice/introspection/SchemaObject.h index fd3659c1..53b12b69 100644 --- a/include/graphqlservice/introspection/SchemaObject.h +++ b/include/graphqlservice/introspection/SchemaObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef SCHEMAOBJECT_H -#define SCHEMAOBJECT_H +#ifndef INTROSPECTION_SCHEMAOBJECT_H +#define INTROSPECTION_SCHEMAOBJECT_H #include "IntrospectionSchema.h" @@ -92,4 +92,4 @@ class [[nodiscard("unnecessary construction")]] Schema final } // namespace graphql::introspection::object -#endif // SCHEMAOBJECT_H +#endif // INTROSPECTION_SCHEMAOBJECT_H diff --git a/include/graphqlservice/introspection/SchemaObject.ixx b/include/graphqlservice/introspection/SchemaObject.ixx new file mode 100644 index 00000000..56735d85 --- /dev/null +++ b/include/graphqlservice/introspection/SchemaObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "SchemaObject.h" + +export module GraphQL.Introspection.SchemaObject; + +export namespace graphql::introspection::object { + +using object::Schema; + +} // namespace graphql::introspection::object diff --git a/include/graphqlservice/introspection/TypeObject.h b/include/graphqlservice/introspection/TypeObject.h index 3b350e1c..993e6dd7 100644 --- a/include/graphqlservice/introspection/TypeObject.h +++ b/include/graphqlservice/introspection/TypeObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef TYPEOBJECT_H -#define TYPEOBJECT_H +#ifndef INTROSPECTION_TYPEOBJECT_H +#define INTROSPECTION_TYPEOBJECT_H #include "IntrospectionSchema.h" @@ -120,4 +120,4 @@ class [[nodiscard("unnecessary construction")]] Type final } // namespace graphql::introspection::object -#endif // TYPEOBJECT_H +#endif // INTROSPECTION_TYPEOBJECT_H diff --git a/include/graphqlservice/introspection/TypeObject.ixx b/include/graphqlservice/introspection/TypeObject.ixx new file mode 100644 index 00000000..001ccdd6 --- /dev/null +++ b/include/graphqlservice/introspection/TypeObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TypeObject.h" + +export module GraphQL.Introspection.TypeObject; + +export namespace graphql::introspection::object { + +using object::Type; + +} // namespace graphql::introspection::object diff --git a/res/ClientGen.rc b/res/ClientGen.rc index b06e107a..5557a731 100644 --- a/res/ClientGen.rc +++ b/res/ClientGen.rc @@ -3,8 +3,8 @@ #include -#define GRAPHQL_RC_VERSION 4,5,9,0 -#define GRAPHQL_RC_VERSION_STR "4.5.9" +#define GRAPHQL_RC_VERSION 5,0,0,0 +#define GRAPHQL_RC_VERSION_STR "5.0.0" #ifndef DEBUG #define VER_DEBUG 0 diff --git a/res/SchemaGen.rc b/res/SchemaGen.rc index f7ae5ab5..9075199f 100644 --- a/res/SchemaGen.rc +++ b/res/SchemaGen.rc @@ -3,8 +3,8 @@ #include -#define GRAPHQL_RC_VERSION 4,5,9,0 -#define GRAPHQL_RC_VERSION_STR "4.5.9" +#define GRAPHQL_RC_VERSION 5,0,0,0 +#define GRAPHQL_RC_VERSION_STR "5.0.0" #ifndef DEBUG #define VER_DEBUG 0 diff --git a/res/graphqlclient_version.rc b/res/graphqlclient_version.rc index a246267a..99596091 100644 --- a/res/graphqlclient_version.rc +++ b/res/graphqlclient_version.rc @@ -3,8 +3,8 @@ #include -#define GRAPHQL_RC_VERSION 4,5,9,0 -#define GRAPHQL_RC_VERSION_STR "4.5.9" +#define GRAPHQL_RC_VERSION 5,0,0,0 +#define GRAPHQL_RC_VERSION_STR "5.0.0" #ifndef DEBUG #define VER_DEBUG 0 diff --git a/res/graphqljson_version.rc b/res/graphqljson_version.rc index d37e857e..6abe6dff 100644 --- a/res/graphqljson_version.rc +++ b/res/graphqljson_version.rc @@ -3,8 +3,8 @@ #include -#define GRAPHQL_RC_VERSION 4,5,9,0 -#define GRAPHQL_RC_VERSION_STR "4.5.9" +#define GRAPHQL_RC_VERSION 5,0,0,0 +#define GRAPHQL_RC_VERSION_STR "5.0.0" #ifndef DEBUG #define VER_DEBUG 0 diff --git a/res/graphqlpeg_version.rc b/res/graphqlpeg_version.rc index 1be5304f..a2e61263 100644 --- a/res/graphqlpeg_version.rc +++ b/res/graphqlpeg_version.rc @@ -3,8 +3,8 @@ #include -#define GRAPHQL_RC_VERSION 4,5,9,0 -#define GRAPHQL_RC_VERSION_STR "4.5.9" +#define GRAPHQL_RC_VERSION 5,0,0,0 +#define GRAPHQL_RC_VERSION_STR "5.0.0" #ifndef DEBUG #define VER_DEBUG 0 diff --git a/res/graphqlresponse_version.rc b/res/graphqlresponse_version.rc index 02682e0b..18389796 100644 --- a/res/graphqlresponse_version.rc +++ b/res/graphqlresponse_version.rc @@ -3,8 +3,8 @@ #include -#define GRAPHQL_RC_VERSION 4,5,9,0 -#define GRAPHQL_RC_VERSION_STR "4.5.9" +#define GRAPHQL_RC_VERSION 5,0,0,0 +#define GRAPHQL_RC_VERSION_STR "5.0.0" #ifndef DEBUG #define VER_DEBUG 0 diff --git a/res/graphqlservice_version.rc b/res/graphqlservice_version.rc index 3cba6e07..76c0d63a 100644 --- a/res/graphqlservice_version.rc +++ b/res/graphqlservice_version.rc @@ -3,8 +3,8 @@ #include -#define GRAPHQL_RC_VERSION 4,5,9,0 -#define GRAPHQL_RC_VERSION_STR "4.5.9" +#define GRAPHQL_RC_VERSION 5,0,0,0 +#define GRAPHQL_RC_VERSION_STR "5.0.0" #ifndef DEBUG #define VER_DEBUG 0 diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index bddfd792..9b86c5af 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -1,31 +1,22 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.28) +add_subdirectory(today) add_subdirectory(client) add_subdirectory(learn) -add_subdirectory(today) +add_subdirectory(stitched) add_subdirectory(validation) if(GRAPHQL_BUILD_HTTP_SAMPLE) - find_package(Boost QUIET) - if(Boost_FOUND) - if(Boost_VERSION VERSION_GREATER_EQUAL "1.81.0") - try_compile(TEST_RESULT - ${CMAKE_CURRENT_BINARY_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/test_boost_beast.cpp - CMAKE_FLAGS -DINCLUDE_DIRECTORIES:STRING=${Boost_INCLUDE_DIR} - CXX_STANDARD 20) - - if(TEST_RESULT) - message(STATUS "Using Boost.Beast ${Boost_VERSION}") - add_subdirectory(proxy) - else() - message(WARNING "GRAPHQL_BUILD_HTTP_SAMPLE requires the Boost.Beast header-only library and a toolchain that supports co_await in Boost.Asio.") - endif() + find_package(boost_beast CONFIG QUIET) + if(boost_beast_FOUND) + if(boost_beast_VERSION VERSION_GREATER_EQUAL "1.81.0") + message(STATUS "Using Boost.Beast ${boost_beast_VERSION}") + add_subdirectory(proxy) else() - message(WARNING "GRAPHQL_BUILD_HTTP_SAMPLE requires the Boost.Beast header-only library >= 1.81.0, but only ${Boost_VERSION} was found in ${Boost_INCLUDE_DIR}.") + message(WARNING "GRAPHQL_BUILD_HTTP_SAMPLE requires the Boost.Beast header-only library >= 1.81.0, but only ${boost_beast_VERSION} was found.") endif() endif() endif() diff --git a/samples/client/CMakeLists.txt b/samples/client/CMakeLists.txt index 71ff74de..1962dc7e 100644 --- a/samples/client/CMakeLists.txt +++ b/samples/client/CMakeLists.txt @@ -1,7 +1,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.28) add_subdirectory(query) add_subdirectory(mutate) @@ -9,32 +9,33 @@ add_subdirectory(subscribe) add_subdirectory(nestedinput) add_subdirectory(multiple) -add_subdirectory(benchmark) +if(GRAPHQL_BUILD_MODULES) + # client_benchmark + add_subdirectory(benchmark) + add_executable(client_benchmark benchmark.cpp) + target_link_libraries(client_benchmark PRIVATE + todaygraphql + benchmark_client) -# client_benchmark -add_executable(client_benchmark benchmark.cpp) -target_link_libraries(client_benchmark PRIVATE - todaygraphql - benchmark_client) + if(WIN32 AND BUILD_SHARED_LIBS) + add_custom_command(OUTPUT copied_sample_dlls + COMMAND ${CMAKE_COMMAND} -E copy_if_different + $ + $ + $ + $ + $ + ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/copied_sample_dlls + DEPENDS + graphqlservice + graphqljson + graphqlpeg + graphqlresponse + graphqlclient) -if(WIN32 AND BUILD_SHARED_LIBS) - add_custom_command(OUTPUT copied_sample_dlls - COMMAND ${CMAKE_COMMAND} -E copy_if_different - $ - $ - $ - $ - $ - ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/copied_sample_dlls - DEPENDS - graphqlservice - graphqljson - graphqlpeg - graphqlresponse - graphqlclient) + add_custom_target(copy_client_sample_dlls DEPENDS copied_sample_dlls) - add_custom_target(copy_client_sample_dlls DEPENDS copied_sample_dlls) - - add_dependencies(client_benchmark copy_client_sample_dlls) + add_dependencies(client_benchmark copy_client_sample_dlls) + endif() endif() diff --git a/samples/client/benchmark.cpp b/samples/client/benchmark.cpp index 8e793a70..41951aa3 100644 --- a/samples/client/benchmark.cpp +++ b/samples/client/benchmark.cpp @@ -1,23 +1,29 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "BenchmarkClient.h" -#include "TodayMock.h" - +#include #include +#include #include #include #include +#include #include #include #include +import GraphQL.Client; +import GraphQL.Service; + +import GraphQL.Today.Mock; +import GraphQL.Today.TodayClient; + using namespace graphql; using namespace std::literals; void outputOverview( - size_t iterations, const std::chrono::steady_clock::duration& totalDuration) noexcept + std::size_t iterations, const std::chrono::steady_clock::duration& totalDuration) noexcept { const auto requestsPerSecond = ((static_cast(iterations) @@ -39,7 +45,7 @@ void outputOverview( void outputSegment( std::string_view name, std::vector& durations) noexcept { - std::sort(durations.begin(), durations.end()); + std::ranges::sort(durations); const auto count = durations.size(); const auto total = @@ -60,14 +66,14 @@ void outputSegment( int main(int argc, char** argv) { - const size_t iterations = [](const char* arg) noexcept -> size_t { + const std::size_t iterations = [](const char* arg) noexcept -> std::size_t { if (arg) { const int parsed = std::atoi(arg); if (parsed > 0) { - return static_cast(parsed); + return static_cast(parsed); } } @@ -80,29 +86,30 @@ int main(int argc, char** argv) const auto mockService = today::mock_service(); const auto& service = mockService->service; std::vector durationResolve(iterations); - std::vector durationParseServiceResponse(iterations); std::vector durationParseResponse(iterations); const auto startTime = std::chrono::steady_clock::now(); try { - using namespace client::query::Query; + using namespace today::client::query::Query; auto query = GetRequestObject(); const auto& name = GetOperationName(); + auto visitor = std::make_shared(); + auto responseVisitor = std::make_shared(visitor); - for (size_t i = 0; i < iterations; ++i) + for (std::size_t i = 0; i < iterations; ++i) { const auto startResolve = std::chrono::steady_clock::now(); - auto response = service->resolve({ query, name }).get(); - const auto startParseServiceResponse = std::chrono::steady_clock::now(); - auto serviceResponse = client::parseServiceResponse(std::move(response)); + auto response = service->visit({ query, name }).get(); const auto startParseResponse = std::chrono::steady_clock::now(); - const auto parsed = parseResponse(std::move(serviceResponse.data)); + + std::move(response.data).visit(responseVisitor); + + const auto parsed = visitor->response(); const auto endParseResponse = std::chrono::steady_clock::now(); - durationResolve[i] = startParseServiceResponse - startResolve; - durationParseServiceResponse[i] = startParseResponse - startParseServiceResponse; + durationResolve[i] = startParseResponse - startResolve; durationParseResponse[i] = endParseResponse - startParseResponse; } } @@ -118,7 +125,6 @@ int main(int argc, char** argv) outputOverview(iterations, totalDuration); outputSegment("Resolve"sv, durationResolve); - outputSegment("ParseServiceResponse"sv, durationParseServiceResponse); outputSegment("ParseResponse"sv, durationParseResponse); return 0; diff --git a/samples/client/benchmark/BenchmarkClient.cpp b/samples/client/benchmark/BenchmarkClient.cpp deleted file mode 100644 index b7c48d94..00000000 --- a/samples/client/benchmark/BenchmarkClient.cpp +++ /dev/null @@ -1,225 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -// WARNING! Do not edit this file manually, your changes will be overwritten. - -#include "BenchmarkClient.h" - -#include "graphqlservice/internal/SortedMap.h" - -#include -#include -#include -#include -#include -#include - -using namespace std::literals; - -namespace graphql::client { -namespace benchmark { - -const std::string& GetRequestText() noexcept -{ - static const auto s_request = R"gql( - # Copyright (c) Microsoft Corporation. All rights reserved. - # Licensed under the MIT License. - - query { - appointments { - pageInfo { - hasNextPage - } - edges { - node { - id - when - subject - isNow - } - } - } - } - )gql"s; - - return s_request; -} - -const peg::ast& GetRequestObject() noexcept -{ - static const auto s_request = []() noexcept { - auto ast = peg::parseString(GetRequestText()); - - // This has already been validated against the schema by clientgen. - ast.validated = true; - - return ast; - }(); - - return s_request; -} - -} // namespace benchmark - -using namespace benchmark; - -template <> -query::Query::Response::appointments_AppointmentConnection::pageInfo_PageInfo Response::parse(response::Value&& response) -{ - query::Query::Response::appointments_AppointmentConnection::pageInfo_PageInfo result; - - if (response.type() == response::Type::Map) - { - auto members = response.release(); - - for (auto& member : members) - { - if (member.first == R"js(hasNextPage)js"sv) - { - result.hasNextPage = ModifiedResponse::parse(std::move(member.second)); - continue; - } - } - } - - return result; -} - -template <> -query::Query::Response::appointments_AppointmentConnection::edges_AppointmentEdge::node_Appointment Response::parse(response::Value&& response) -{ - query::Query::Response::appointments_AppointmentConnection::edges_AppointmentEdge::node_Appointment result; - - if (response.type() == response::Type::Map) - { - auto members = response.release(); - - for (auto& member : members) - { - if (member.first == R"js(id)js"sv) - { - result.id = ModifiedResponse::parse(std::move(member.second)); - continue; - } - if (member.first == R"js(when)js"sv) - { - result.when = ModifiedResponse::parse(std::move(member.second)); - continue; - } - if (member.first == R"js(subject)js"sv) - { - result.subject = ModifiedResponse::parse(std::move(member.second)); - continue; - } - if (member.first == R"js(isNow)js"sv) - { - result.isNow = ModifiedResponse::parse(std::move(member.second)); - continue; - } - } - } - - return result; -} - -template <> -query::Query::Response::appointments_AppointmentConnection::edges_AppointmentEdge Response::parse(response::Value&& response) -{ - query::Query::Response::appointments_AppointmentConnection::edges_AppointmentEdge result; - - if (response.type() == response::Type::Map) - { - auto members = response.release(); - - for (auto& member : members) - { - if (member.first == R"js(node)js"sv) - { - result.node = ModifiedResponse::parse(std::move(member.second)); - continue; - } - } - } - - return result; -} - -template <> -query::Query::Response::appointments_AppointmentConnection Response::parse(response::Value&& response) -{ - query::Query::Response::appointments_AppointmentConnection result; - - if (response.type() == response::Type::Map) - { - auto members = response.release(); - - for (auto& member : members) - { - if (member.first == R"js(pageInfo)js"sv) - { - result.pageInfo = ModifiedResponse::parse(std::move(member.second)); - continue; - } - if (member.first == R"js(edges)js"sv) - { - result.edges = ModifiedResponse::parse(std::move(member.second)); - continue; - } - } - } - - return result; -} - -namespace query::Query { - -const std::string& GetOperationName() noexcept -{ - static const auto s_name = R"gql()gql"s; - - return s_name; -} - -Response parseResponse(response::Value&& response) -{ - Response result; - - if (response.type() == response::Type::Map) - { - auto members = response.release(); - - for (auto& member : members) - { - if (member.first == R"js(appointments)js"sv) - { - result.appointments = ModifiedResponse::parse(std::move(member.second)); - continue; - } - } - } - - return result; -} - -[[nodiscard("unnecessary call")]] const std::string& Traits::GetRequestText() noexcept -{ - return benchmark::GetRequestText(); -} - -[[nodiscard("unnecessary call")]] const peg::ast& Traits::GetRequestObject() noexcept -{ - return benchmark::GetRequestObject(); -} - -[[nodiscard("unnecessary call")]] const std::string& Traits::GetOperationName() noexcept -{ - return Query::GetOperationName(); -} - -[[nodiscard("unnecessary conversion")]] Traits::Response Traits::parseResponse(response::Value&& response) -{ - return Query::parseResponse(std::move(response)); -} - -} // namespace query::Query -} // namespace graphql::client diff --git a/samples/client/benchmark/CMakeLists.txt b/samples/client/benchmark/CMakeLists.txt index ea0ab74c..7679664f 100644 --- a/samples/client/benchmark/CMakeLists.txt +++ b/samples/client/benchmark/CMakeLists.txt @@ -1,13 +1,13 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.28) # Normally this would be handled by find_package(cppgraphqlgen CONFIG). include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/cppgraphqlgen-functions.cmake) if(GRAPHQL_UPDATE_SAMPLES AND GRAPHQL_BUILD_CLIENTGEN) - update_graphql_client_files(benchmark ../../today/schema.today.graphql client.benchmark.today.graphql Benchmark benchmark) + update_graphql_shared_client_files(benchmark today client.benchmark.today.graphql) endif() add_graphql_client_target(benchmark) diff --git a/samples/client/benchmark/TodayClient.cpp b/samples/client/benchmark/TodayClient.cpp new file mode 100644 index 00000000..007b7aad --- /dev/null +++ b/samples/client/benchmark/TodayClient.cpp @@ -0,0 +1,631 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +#include "TodayClient.h" + +#include "graphqlservice/internal/SortedMap.h" + +#include +#include +#include +#include +#include +#include + +using namespace std::literals; + +namespace graphql { +namespace today { +namespace client { + +const std::string& GetRequestText() noexcept +{ + static const auto s_request = R"gql( + # Copyright (c) Microsoft Corporation. All rights reserved. + # Licensed under the MIT License. + + query { + appointments { + pageInfo { + hasNextPage + } + edges { + node { + id + when + subject + isNow + } + } + } + } + )gql"s; + + return s_request; +} + +const peg::ast& GetRequestObject() noexcept +{ + static const auto s_request = []() noexcept { + auto ast = peg::parseString(GetRequestText()); + + // This has already been validated against the schema by clientgen. + ast.validated = true; + + return ast; + }(); + + return s_request; +} + +} // namespace client +} // namespace today +namespace client { + +using namespace today; + +template <> +graphql::today::client::query::Query::Response::appointments_AppointmentConnection::pageInfo_PageInfo Response::parse(response::Value&& response) +{ + graphql::today::client::query::Query::Response::appointments_AppointmentConnection::pageInfo_PageInfo result; + + if (response.type() == response::Type::Map) + { + auto members = response.release(); + + for (auto& member : members) + { + if (member.first == R"js(hasNextPage)js"sv) + { + result.hasNextPage = ModifiedResponse::parse(std::move(member.second)); + continue; + } + } + } + + return result; +} + +template <> +graphql::today::client::query::Query::Response::appointments_AppointmentConnection::edges_AppointmentEdge::node_Appointment Response::parse(response::Value&& response) +{ + graphql::today::client::query::Query::Response::appointments_AppointmentConnection::edges_AppointmentEdge::node_Appointment result; + + if (response.type() == response::Type::Map) + { + auto members = response.release(); + + for (auto& member : members) + { + if (member.first == R"js(id)js"sv) + { + result.id = ModifiedResponse::parse(std::move(member.second)); + continue; + } + if (member.first == R"js(when)js"sv) + { + result.when = ModifiedResponse::parse(std::move(member.second)); + continue; + } + if (member.first == R"js(subject)js"sv) + { + result.subject = ModifiedResponse::parse(std::move(member.second)); + continue; + } + if (member.first == R"js(isNow)js"sv) + { + result.isNow = ModifiedResponse::parse(std::move(member.second)); + continue; + } + } + } + + return result; +} + +template <> +graphql::today::client::query::Query::Response::appointments_AppointmentConnection::edges_AppointmentEdge Response::parse(response::Value&& response) +{ + graphql::today::client::query::Query::Response::appointments_AppointmentConnection::edges_AppointmentEdge result; + + if (response.type() == response::Type::Map) + { + auto members = response.release(); + + for (auto& member : members) + { + if (member.first == R"js(node)js"sv) + { + result.node = ModifiedResponse::parse(std::move(member.second)); + continue; + } + } + } + + return result; +} + +template <> +graphql::today::client::query::Query::Response::appointments_AppointmentConnection Response::parse(response::Value&& response) +{ + graphql::today::client::query::Query::Response::appointments_AppointmentConnection result; + + if (response.type() == response::Type::Map) + { + auto members = response.release(); + + for (auto& member : members) + { + if (member.first == R"js(pageInfo)js"sv) + { + result.pageInfo = ModifiedResponse::parse(std::move(member.second)); + continue; + } + if (member.first == R"js(edges)js"sv) + { + result.edges = ModifiedResponse::parse(std::move(member.second)); + continue; + } + } + } + + return result; +} + +} // namespace client + +namespace today::client::query::Query { + +const std::string& GetOperationName() noexcept +{ + static const auto s_name = R"gql()gql"s; + + return s_name; +} + +struct ResponseVisitor::impl +{ + enum class VisitorState + { + Start, + Member_appointments, + Member_appointments_pageInfo, + Member_appointments_pageInfo_hasNextPage, + Member_appointments_edges, + Member_appointments_edges_0, + Member_appointments_edges_0_, + Member_appointments_edges_0_node, + Member_appointments_edges_0_node_id, + Member_appointments_edges_0_node_when, + Member_appointments_edges_0_node_subject, + Member_appointments_edges_0_node_isNow, + Complete, + }; + + VisitorState state { VisitorState::Start }; + Response response {}; +}; + +ResponseVisitor::ResponseVisitor() noexcept + : _pimpl { std::make_unique() } +{ +} + +ResponseVisitor::~ResponseVisitor() +{ +} + +void ResponseVisitor::add_value([[maybe_unused]] std::shared_ptr&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.appointments = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_pageInfo: + _pimpl->state = impl::VisitorState::Member_appointments; + _pimpl->response.appointments.pageInfo = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_pageInfo_hasNextPage: + _pimpl->state = impl::VisitorState::Member_appointments_pageInfo; + _pimpl->response.appointments.pageInfo.hasNextPage = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0: + _pimpl->response.appointments.edges->push_back(ModifiedResponse::parse(response::Value { *value })); + break; + + case impl::VisitorState::Member_appointments_edges_0_node: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_; + _pimpl->response.appointments.edges->back()->node = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0_node_id: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->id = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0_node_when: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->when = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0_node_subject: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->subject = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0_node_isNow: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->isNow = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::reserve([[maybe_unused]] std::size_t count) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0: + _pimpl->response.appointments.edges->reserve(count); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_; + _pimpl->response.appointments.edges->push_back(std::make_optional({})); + break; + + case impl::VisitorState::Member_appointments_edges_0_node: + _pimpl->response.appointments.edges->back()->node = std::make_optional({}); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_member([[maybe_unused]] std::string&& key) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Start: + if (key == "appointments"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments; + } + break; + + case impl::VisitorState::Member_appointments: + if (key == "pageInfo"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_pageInfo; + } + else if (key == "edges"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges; + } + break; + + case impl::VisitorState::Member_appointments_pageInfo: + if (key == "hasNextPage"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_pageInfo_hasNextPage; + } + break; + + case impl::VisitorState::Member_appointments_edges_0_: + if (key == "node"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + } + break; + + case impl::VisitorState::Member_appointments_edges_0_node: + if (key == "id"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node_id; + } + else if (key == "when"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node_when; + } + else if (key == "subject"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node_subject; + } + else if (key == "isNow"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node_isNow; + } + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_pageInfo: + _pimpl->state = impl::VisitorState::Member_appointments; + break; + + case impl::VisitorState::Member_appointments_edges_0_node: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_; + break; + + case impl::VisitorState::Member_appointments_edges_0_: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0; + break; + + case impl::VisitorState::Member_appointments: + _pimpl->state = impl::VisitorState::Start; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0; + _pimpl->response.appointments.edges = std::make_optional>>({}); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0: + _pimpl->state = impl::VisitorState::Member_appointments; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_null() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0: + _pimpl->response.appointments.edges->push_back(std::nullopt); + break; + + case impl::VisitorState::Member_appointments_edges_0_node: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_; + _pimpl->response.appointments.edges->back()->node = std::nullopt; + break; + + case impl::VisitorState::Member_appointments_edges_0_node_when: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->when = std::nullopt; + break; + + case impl::VisitorState::Member_appointments_edges_0_node_subject: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->subject = std::nullopt; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_string([[maybe_unused]] std::string&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0_node_subject: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->subject = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_enum([[maybe_unused]] std::string&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_id([[maybe_unused]] response::IdType&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0_node_id: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->id = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_bool([[maybe_unused]] bool value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_pageInfo_hasNextPage: + _pimpl->state = impl::VisitorState::Member_appointments_pageInfo; + _pimpl->response.appointments.pageInfo.hasNextPage = value; + break; + + case impl::VisitorState::Member_appointments_edges_0_node_isNow: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->isNow = value; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_int([[maybe_unused]] int value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_float([[maybe_unused]] double value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::complete() +{ + _pimpl->state = impl::VisitorState::Complete; +} + +Response ResponseVisitor::response() +{ + Response response {}; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + _pimpl->state = impl::VisitorState::Start; + std::swap(_pimpl->response, response); + break; + + default: + break; + } + + return response; +} + +Response parseResponse(response::Value&& response) +{ + using namespace graphql::client; + + Response result; + + if (response.type() == response::Type::Map) + { + auto members = response.release(); + + for (auto& member : members) + { + if (member.first == R"js(appointments)js"sv) + { + result.appointments = ModifiedResponse::parse(std::move(member.second)); + continue; + } + } + } + + return result; +} + +[[nodiscard("unnecessary call")]] const std::string& Traits::GetRequestText() noexcept +{ + return client::GetRequestText(); +} + +[[nodiscard("unnecessary call")]] const peg::ast& Traits::GetRequestObject() noexcept +{ + return client::GetRequestObject(); +} + +[[nodiscard("unnecessary call")]] const std::string& Traits::GetOperationName() noexcept +{ + return Query::GetOperationName(); +} + +[[nodiscard("unnecessary conversion")]] Traits::Response Traits::parseResponse(response::Value&& response) +{ + return Query::parseResponse(std::move(response)); +} + +} // namespace today::client::query::Query +} // namespace graphql diff --git a/samples/client/benchmark/BenchmarkClient.h b/samples/client/benchmark/TodayClient.h similarity index 67% rename from samples/client/benchmark/BenchmarkClient.h rename to samples/client/benchmark/TodayClient.h index f7c4e41e..6acd89b6 100644 --- a/samples/client/benchmark/BenchmarkClient.h +++ b/samples/client/benchmark/TodayClient.h @@ -5,8 +5,8 @@ #pragma once -#ifndef BENCHMARKCLIENT_H -#define BENCHMARKCLIENT_H +#ifndef TODAYCLIENT_H +#define TODAYCLIENT_H #include "graphqlservice/GraphQLClient.h" #include "graphqlservice/GraphQLParse.h" @@ -14,20 +14,20 @@ #include "graphqlservice/internal/Version.h" -// Check if the library version is compatible with clientgen 4.5.0 -static_assert(graphql::internal::MajorVersion == 4, "regenerate with clientgen: major version mismatch"); -static_assert(graphql::internal::MinorVersion == 5, "regenerate with clientgen: minor version mismatch"); +#include "TodaySharedTypes.h" #include #include #include -namespace graphql::client { +// Check if the library version is compatible with clientgen 5.0.0 +static_assert(graphql::internal::MajorVersion == 5, "regenerate with clientgen: major version mismatch"); +static_assert(graphql::internal::MinorVersion == 0, "regenerate with clientgen: minor version mismatch"); -/// -/// Operation: query (unnamed) -/// -/// +namespace graphql::today { + +/// # Operation: query (unnamed) +/// ```graphql /// # Copyright (c) Microsoft Corporation. All rights reserved. /// # Licensed under the MIT License. /// @@ -46,8 +46,8 @@ namespace graphql::client { /// } /// } /// } -/// -namespace benchmark { +/// ``` +namespace client { // Return the original text of the request document. [[nodiscard("unnecessary call")]] const std::string& GetRequestText() noexcept; @@ -55,12 +55,10 @@ namespace benchmark { // Return a pre-parsed, pre-validated request object. [[nodiscard("unnecessary call")]] const peg::ast& GetRequestObject() noexcept; -} // namespace benchmark - namespace query::Query { -using benchmark::GetRequestText; -using benchmark::GetRequestObject; +using graphql::today::client::GetRequestText; +using graphql::today::client::GetRequestObject; // Return the name of this operation in the shared request document. [[nodiscard("unnecessary call")]] const std::string& GetOperationName() noexcept; @@ -94,6 +92,37 @@ struct [[nodiscard("unnecessary construction")]] Response appointments_AppointmentConnection appointments {}; }; +class ResponseVisitor + : public std::enable_shared_from_this +{ +public: + ResponseVisitor() noexcept; + ~ResponseVisitor(); + + void add_value(std::shared_ptr&&); + void reserve(std::size_t count); + void start_object(); + void add_member(std::string&& key); + void end_object(); + void start_array(); + void end_array(); + void add_null(); + void add_string(std::string&& value); + void add_enum(std::string&& value); + void add_id(response::IdType&& value); + void add_bool(bool value); + void add_int(int value); + void add_float(double value); + void complete(); + + Response response(); + +private: + struct impl; + + std::unique_ptr _pimpl; +}; + [[nodiscard("unnecessary conversion")]] Response parseResponse(response::Value&& response); struct Traits @@ -103,11 +132,13 @@ struct Traits [[nodiscard("unnecessary call")]] static const std::string& GetOperationName() noexcept; using Response = Query::Response; + using ResponseVisitor = Query::ResponseVisitor; [[nodiscard("unnecessary conversion")]] static Response parseResponse(response::Value&& response); }; } // namespace query::Query -} // namespace graphql::client +} // namespace client +} // namespace graphql::today -#endif // BENCHMARKCLIENT_H +#endif // TODAYCLIENT_H diff --git a/samples/client/benchmark/TodayClient.ixx b/samples/client/benchmark/TodayClient.ixx new file mode 100644 index 00000000..6ddf5cdc --- /dev/null +++ b/samples/client/benchmark/TodayClient.ixx @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayClient.h" + +export module GraphQL.Today.TodayClient; + +export namespace graphql::today { + +namespace client { + +using client::GetRequestText; +using client::GetRequestObject; + +namespace query::Query { + +using graphql::today::client::GetRequestText; +using graphql::today::client::GetRequestObject; +using Query::GetOperationName; + +using Query::Response; +using Query::ResponseVisitor; +using Query::parseResponse; + +using Query::Traits; + +} // namespace query::Query + +} // namespace client +} // namespace graphql::today diff --git a/samples/client/benchmark/benchmark_client_files b/samples/client/benchmark/benchmark_client_files index 2d3350f3..995e48c6 100644 --- a/samples/client/benchmark/benchmark_client_files +++ b/samples/client/benchmark/benchmark_client_files @@ -1 +1 @@ -BenchmarkClient.cpp +TodayClient.cpp diff --git a/samples/client/multiple/CMakeLists.txt b/samples/client/multiple/CMakeLists.txt index d011f933..9f72d51d 100644 --- a/samples/client/multiple/CMakeLists.txt +++ b/samples/client/multiple/CMakeLists.txt @@ -1,7 +1,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.28) # Normally this would be handled by find_package(cppgraphqlgen CONFIG). include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/cppgraphqlgen-functions.cmake) diff --git a/samples/client/multiple/MultipleQueriesClient.cpp b/samples/client/multiple/MultipleQueriesClient.cpp index ed6de3e3..177380a0 100644 --- a/samples/client/multiple/MultipleQueriesClient.cpp +++ b/samples/client/multiple/MultipleQueriesClient.cpp @@ -9,15 +9,16 @@ #include #include -#include +#include #include #include #include using namespace std::literals; -namespace graphql::client { +namespace graphql { namespace multiple { +namespace client { const std::string& GetRequestText() noexcept { @@ -121,11 +122,16 @@ const peg::ast& GetRequestObject() noexcept return s_request; } +} // namespace client + +using namespace graphql::client; + CompleteTaskInput::CompleteTaskInput() noexcept : id {} , testTaskState {} , isComplete {} , clientMutationId {} + , boolList {} { // Explicit definition to prevent ODR violations when LTO is enabled. } @@ -134,11 +140,13 @@ CompleteTaskInput::CompleteTaskInput( response::IdType idArg, std::optional testTaskStateArg, std::optional isCompleteArg, - std::optional clientMutationIdArg) noexcept + std::optional clientMutationIdArg, + std::optional> boolListArg) noexcept : id { std::move(idArg) } , testTaskState { std::move(testTaskStateArg) } , isComplete { std::move(isCompleteArg) } , clientMutationId { std::move(clientMutationIdArg) } + , boolList { std::move(boolListArg) } { } @@ -147,6 +155,7 @@ CompleteTaskInput::CompleteTaskInput(const CompleteTaskInput& other) , testTaskState { ModifiedVariable::duplicate(other.testTaskState) } , isComplete { ModifiedVariable::duplicate(other.isComplete) } , clientMutationId { ModifiedVariable::duplicate(other.clientMutationId) } + , boolList { ModifiedVariable::duplicate(other.boolList) } { } @@ -155,6 +164,7 @@ CompleteTaskInput::CompleteTaskInput(CompleteTaskInput&& other) noexcept , testTaskState { std::move(other.testTaskState) } , isComplete { std::move(other.isComplete) } , clientMutationId { std::move(other.clientMutationId) } + , boolList { std::move(other.boolList) } { } @@ -174,18 +184,20 @@ CompleteTaskInput& CompleteTaskInput::operator=(CompleteTaskInput&& other) noexc testTaskState = std::move(other.testTaskState); isComplete = std::move(other.isComplete); clientMutationId = std::move(other.clientMutationId); + boolList = std::move(other.boolList); return *this; } } // namespace multiple +namespace client { using namespace multiple; template <> -query::Appointments::Response::appointments_AppointmentConnection::edges_AppointmentEdge::node_Appointment Response::parse(response::Value&& response) +graphql::multiple::client::query::Appointments::Response::appointments_AppointmentConnection::edges_AppointmentEdge::node_Appointment Response::parse(response::Value&& response) { - query::Appointments::Response::appointments_AppointmentConnection::edges_AppointmentEdge::node_Appointment result; + graphql::multiple::client::query::Appointments::Response::appointments_AppointmentConnection::edges_AppointmentEdge::node_Appointment result; if (response.type() == response::Type::Map) { @@ -225,9 +237,9 @@ query::Appointments::Response::appointments_AppointmentConnection::edges_Appoint } template <> -query::Appointments::Response::appointments_AppointmentConnection::edges_AppointmentEdge Response::parse(response::Value&& response) +graphql::multiple::client::query::Appointments::Response::appointments_AppointmentConnection::edges_AppointmentEdge Response::parse(response::Value&& response) { - query::Appointments::Response::appointments_AppointmentConnection::edges_AppointmentEdge result; + graphql::multiple::client::query::Appointments::Response::appointments_AppointmentConnection::edges_AppointmentEdge result; if (response.type() == response::Type::Map) { @@ -237,7 +249,7 @@ query::Appointments::Response::appointments_AppointmentConnection::edges_Appoint { if (member.first == R"js(node)js"sv) { - result.node = ModifiedResponse::parse(std::move(member.second)); + result.node = ModifiedResponse::parse(std::move(member.second)); continue; } } @@ -247,9 +259,9 @@ query::Appointments::Response::appointments_AppointmentConnection::edges_Appoint } template <> -query::Appointments::Response::appointments_AppointmentConnection Response::parse(response::Value&& response) +graphql::multiple::client::query::Appointments::Response::appointments_AppointmentConnection Response::parse(response::Value&& response) { - query::Appointments::Response::appointments_AppointmentConnection result; + graphql::multiple::client::query::Appointments::Response::appointments_AppointmentConnection result; if (response.type() == response::Type::Map) { @@ -259,7 +271,7 @@ query::Appointments::Response::appointments_AppointmentConnection Response::parse(std::move(member.second)); + result.edges = ModifiedResponse::parse(std::move(member.second)); continue; } } @@ -268,7 +280,9 @@ query::Appointments::Response::appointments_AppointmentConnection Response(); + Start, + Member_appointments, + Member_appointments_edges, + Member_appointments_edges_0, + Member_appointments_edges_0_, + Member_appointments_edges_0_node, + Member_appointments_edges_0_node_id, + Member_appointments_edges_0_node_subject, + Member_appointments_edges_0_node_when, + Member_appointments_edges_0_node_isNow, + Member_appointments_edges_0_node__typename, + Complete, + }; - for (auto& member : members) - { - if (member.first == R"js(appointments)js"sv) - { - result.appointments = ModifiedResponse::parse(std::move(member.second)); - continue; - } - } - } + VisitorState state { VisitorState::Start }; + Response response {}; +}; - return result; +ResponseVisitor::ResponseVisitor() noexcept + : _pimpl { std::make_unique() } +{ } -[[nodiscard("unnecessary call")]] const std::string& Traits::GetRequestText() noexcept +ResponseVisitor::~ResponseVisitor() { - return multiple::GetRequestText(); } -[[nodiscard("unnecessary call")]] const peg::ast& Traits::GetRequestObject() noexcept +void ResponseVisitor::add_value([[maybe_unused]] std::shared_ptr&& value) { - return multiple::GetRequestObject(); + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.appointments = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0: + _pimpl->response.appointments.edges->push_back(ModifiedResponse::parse(response::Value { *value })); + break; + + case impl::VisitorState::Member_appointments_edges_0_node: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_; + _pimpl->response.appointments.edges->back()->node = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0_node_id: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->id = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0_node_subject: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->subject = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0_node_when: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->when = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0_node_isNow: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->isNow = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0_node__typename: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->_typename = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } } -[[nodiscard("unnecessary call")]] const std::string& Traits::GetOperationName() noexcept +void ResponseVisitor::reserve([[maybe_unused]] std::size_t count) { - return Appointments::GetOperationName(); + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0: + _pimpl->response.appointments.edges->reserve(count); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } } -[[nodiscard("unnecessary conversion")]] Traits::Response Traits::parseResponse(response::Value&& response) +void ResponseVisitor::start_object() { - return Appointments::parseResponse(std::move(response)); -} + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_; + _pimpl->response.appointments.edges->push_back(std::make_optional({})); + break; -} // namespace query::Appointments + case impl::VisitorState::Member_appointments_edges_0_node: + _pimpl->response.appointments.edges->back()->node = std::make_optional({}); + break; -template <> -query::Tasks::Response::tasks_TaskConnection::edges_TaskEdge::node_Task Response::parse(response::Value&& response) -{ - query::Tasks::Response::tasks_TaskConnection::edges_TaskEdge::node_Task result; + case impl::VisitorState::Complete: + break; - if (response.type() == response::Type::Map) + default: + break; + } +} + +void ResponseVisitor::add_member([[maybe_unused]] std::string&& key) +{ + switch (_pimpl->state) { - auto members = response.release(); + case impl::VisitorState::Start: + if (key == "appointments"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments; + } + break; - for (auto& member : members) - { - if (member.first == R"js(id)js"sv) + case impl::VisitorState::Member_appointments: + if (key == "edges"sv) { - result.id = ModifiedResponse::parse(std::move(member.second)); - continue; + _pimpl->state = impl::VisitorState::Member_appointments_edges; } - if (member.first == R"js(title)js"sv) + break; + + case impl::VisitorState::Member_appointments_edges_0_: + if (key == "node"sv) { - result.title = ModifiedResponse::parse(std::move(member.second)); - continue; + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; } - if (member.first == R"js(isComplete)js"sv) + break; + + case impl::VisitorState::Member_appointments_edges_0_node: + if (key == "id"sv) { - result.isComplete = ModifiedResponse::parse(std::move(member.second)); - continue; + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node_id; } - if (member.first == R"js(__typename)js"sv) + else if (key == "subject"sv) { - result._typename = ModifiedResponse::parse(std::move(member.second)); - continue; + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node_subject; } - } + else if (key == "when"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node_when; + } + else if (key == "isNow"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node_isNow; + } + else if (key == "__typename"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node__typename; + } + break; + + case impl::VisitorState::Complete: + break; + + default: + break; } +} - return result; +void ResponseVisitor::end_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0_node: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_; + break; + + case impl::VisitorState::Member_appointments_edges_0_: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0; + break; + + case impl::VisitorState::Member_appointments: + _pimpl->state = impl::VisitorState::Start; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } } -template <> -query::Tasks::Response::tasks_TaskConnection::edges_TaskEdge Response::parse(response::Value&& response) +void ResponseVisitor::start_array() { - query::Tasks::Response::tasks_TaskConnection::edges_TaskEdge result; + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0; + _pimpl->response.appointments.edges = std::make_optional>>({}); + break; - if (response.type() == response::Type::Map) + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_array() +{ + switch (_pimpl->state) { - auto members = response.release(); + case impl::VisitorState::Member_appointments_edges_0: + _pimpl->state = impl::VisitorState::Member_appointments; + break; - for (auto& member : members) - { - if (member.first == R"js(node)js"sv) - { - result.node = ModifiedResponse::parse(std::move(member.second)); - continue; - } - } + case impl::VisitorState::Complete: + break; + + default: + break; } +} - return result; +void ResponseVisitor::add_null() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0: + _pimpl->response.appointments.edges->push_back(std::nullopt); + break; + + case impl::VisitorState::Member_appointments_edges_0_node: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_; + _pimpl->response.appointments.edges->back()->node = std::nullopt; + break; + + case impl::VisitorState::Member_appointments_edges_0_node_subject: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->subject = std::nullopt; + break; + + case impl::VisitorState::Member_appointments_edges_0_node_when: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->when = std::nullopt; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } } -template <> -query::Tasks::Response::tasks_TaskConnection Response::parse(response::Value&& response) +void ResponseVisitor::add_string([[maybe_unused]] std::string&& value) { - query::Tasks::Response::tasks_TaskConnection result; + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0_node_subject: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->subject = std::move(value); + break; - if (response.type() == response::Type::Map) + case impl::VisitorState::Member_appointments_edges_0_node__typename: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->_typename = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_enum([[maybe_unused]] std::string&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) { - auto members = response.release(); + case impl::VisitorState::Complete: + break; - for (auto& member : members) - { - if (member.first == R"js(edges)js"sv) - { - result.edges = ModifiedResponse::parse(std::move(member.second)); - continue; - } - } + default: + break; + } +} + +void ResponseVisitor::add_id([[maybe_unused]] response::IdType&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0_node_id: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->id = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; } +} - return result; +void ResponseVisitor::add_bool([[maybe_unused]] bool value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0_node_isNow: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->isNow = value; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } } -namespace query::Tasks { +void ResponseVisitor::add_int([[maybe_unused]] int value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; -const std::string& GetOperationName() noexcept + default: + break; + } +} + +void ResponseVisitor::add_float([[maybe_unused]] double value) { - static const auto s_name = R"gql(Tasks)gql"s; + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; - return s_name; + default: + break; + } +} + +void ResponseVisitor::complete() +{ + _pimpl->state = impl::VisitorState::Complete; +} + +Response ResponseVisitor::response() +{ + Response response {}; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + _pimpl->state = impl::VisitorState::Start; + std::swap(_pimpl->response, response); + break; + + default: + break; + } + + return response; } Response parseResponse(response::Value&& response) { + using namespace graphql::client; + Response result; if (response.type() == response::Type::Map) @@ -420,9 +685,9 @@ Response parseResponse(response::Value&& response) for (auto& member : members) { - if (member.first == R"js(tasks)js"sv) + if (member.first == R"js(appointments)js"sv) { - result.tasks = ModifiedResponse::parse(std::move(member.second)); + result.appointments = ModifiedResponse::parse(std::move(member.second)); continue; } } @@ -433,30 +698,33 @@ Response parseResponse(response::Value&& response) [[nodiscard("unnecessary call")]] const std::string& Traits::GetRequestText() noexcept { - return multiple::GetRequestText(); + return client::GetRequestText(); } [[nodiscard("unnecessary call")]] const peg::ast& Traits::GetRequestObject() noexcept { - return multiple::GetRequestObject(); + return client::GetRequestObject(); } [[nodiscard("unnecessary call")]] const std::string& Traits::GetOperationName() noexcept { - return Tasks::GetOperationName(); + return Appointments::GetOperationName(); } [[nodiscard("unnecessary conversion")]] Traits::Response Traits::parseResponse(response::Value&& response) { - return Tasks::parseResponse(std::move(response)); + return Appointments::parseResponse(std::move(response)); } -} // namespace query::Tasks +} // namespace multiple::client::query::Appointments +namespace client { + +using namespace multiple; template <> -query::UnreadCounts::Response::unreadCounts_FolderConnection::edges_FolderEdge::node_Folder Response::parse(response::Value&& response) +graphql::multiple::client::query::Tasks::Response::tasks_TaskConnection::edges_TaskEdge::node_Task Response::parse(response::Value&& response) { - query::UnreadCounts::Response::unreadCounts_FolderConnection::edges_FolderEdge::node_Folder result; + graphql::multiple::client::query::Tasks::Response::tasks_TaskConnection::edges_TaskEdge::node_Task result; if (response.type() == response::Type::Map) { @@ -469,14 +737,14 @@ query::UnreadCounts::Response::unreadCounts_FolderConnection::edges_FolderEdge:: result.id = ModifiedResponse::parse(std::move(member.second)); continue; } - if (member.first == R"js(name)js"sv) + if (member.first == R"js(title)js"sv) { - result.name = ModifiedResponse::parse(std::move(member.second)); + result.title = ModifiedResponse::parse(std::move(member.second)); continue; } - if (member.first == R"js(unreadCount)js"sv) + if (member.first == R"js(isComplete)js"sv) { - result.unreadCount = ModifiedResponse::parse(std::move(member.second)); + result.isComplete = ModifiedResponse::parse(std::move(member.second)); continue; } if (member.first == R"js(__typename)js"sv) @@ -491,9 +759,9 @@ query::UnreadCounts::Response::unreadCounts_FolderConnection::edges_FolderEdge:: } template <> -query::UnreadCounts::Response::unreadCounts_FolderConnection::edges_FolderEdge Response::parse(response::Value&& response) +graphql::multiple::client::query::Tasks::Response::tasks_TaskConnection::edges_TaskEdge Response::parse(response::Value&& response) { - query::UnreadCounts::Response::unreadCounts_FolderConnection::edges_FolderEdge result; + graphql::multiple::client::query::Tasks::Response::tasks_TaskConnection::edges_TaskEdge result; if (response.type() == response::Type::Map) { @@ -503,7 +771,7 @@ query::UnreadCounts::Response::unreadCounts_FolderConnection::edges_FolderEdge R { if (member.first == R"js(node)js"sv) { - result.node = ModifiedResponse::parse(std::move(member.second)); + result.node = ModifiedResponse::parse(std::move(member.second)); continue; } } @@ -513,9 +781,9 @@ query::UnreadCounts::Response::unreadCounts_FolderConnection::edges_FolderEdge R } template <> -query::UnreadCounts::Response::unreadCounts_FolderConnection Response::parse(response::Value&& response) +graphql::multiple::client::query::Tasks::Response::tasks_TaskConnection Response::parse(response::Value&& response) { - query::UnreadCounts::Response::unreadCounts_FolderConnection result; + graphql::multiple::client::query::Tasks::Response::tasks_TaskConnection result; if (response.type() == response::Type::Map) { @@ -525,7 +793,7 @@ query::UnreadCounts::Response::unreadCounts_FolderConnection Response::parse(std::move(member.second)); + result.edges = ModifiedResponse::parse(std::move(member.second)); continue; } } @@ -534,148 +802,1445 @@ query::UnreadCounts::Response::unreadCounts_FolderConnection Response(); - - for (auto& member : members) - { - if (member.first == R"js(unreadCounts)js"sv) - { - result.unreadCounts = ModifiedResponse::parse(std::move(member.second)); - continue; - } - } - } + Start, + Member_tasks, + Member_tasks_edges, + Member_tasks_edges_0, + Member_tasks_edges_0_, + Member_tasks_edges_0_node, + Member_tasks_edges_0_node_id, + Member_tasks_edges_0_node_title, + Member_tasks_edges_0_node_isComplete, + Member_tasks_edges_0_node__typename, + Complete, + }; - return result; -} + VisitorState state { VisitorState::Start }; + Response response {}; +}; -[[nodiscard("unnecessary call")]] const std::string& Traits::GetRequestText() noexcept +ResponseVisitor::ResponseVisitor() noexcept + : _pimpl { std::make_unique() } { - return multiple::GetRequestText(); } -[[nodiscard("unnecessary call")]] const peg::ast& Traits::GetRequestObject() noexcept +ResponseVisitor::~ResponseVisitor() { - return multiple::GetRequestObject(); } -[[nodiscard("unnecessary call")]] const std::string& Traits::GetOperationName() noexcept +void ResponseVisitor::add_value([[maybe_unused]] std::shared_ptr&& value) { - return UnreadCounts::GetOperationName(); + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Member_tasks: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.tasks = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_tasks_edges_0: + _pimpl->response.tasks.edges->push_back(ModifiedResponse::parse(response::Value { *value })); + break; + + case impl::VisitorState::Member_tasks_edges_0_node: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_; + _pimpl->response.tasks.edges->back()->node = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_tasks_edges_0_node_id: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->id = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_tasks_edges_0_node_title: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->title = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_tasks_edges_0_node_isComplete: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->isComplete = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_tasks_edges_0_node__typename: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->_typename = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } } -[[nodiscard("unnecessary conversion")]] Traits::Response Traits::parseResponse(response::Value&& response) +void ResponseVisitor::reserve([[maybe_unused]] std::size_t count) { - return UnreadCounts::parseResponse(std::move(response)); -} + switch (_pimpl->state) + { + case impl::VisitorState::Member_tasks_edges_0: + _pimpl->response.tasks.edges->reserve(count); + break; -} // namespace query::UnreadCounts + case impl::VisitorState::Complete: + break; -template <> -TaskState Response::parse(response::Value&& value) + default: + break; + } +} + +void ResponseVisitor::start_object() { - if (!value.maybe_enum()) + switch (_pimpl->state) { - throw std::logic_error { R"ex(not a valid TaskState value)ex" }; - } + case impl::VisitorState::Member_tasks_edges_0: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_; + _pimpl->response.tasks.edges->push_back(std::make_optional({})); + break; - static const std::array, 4> s_values = { - std::make_pair(R"gql(New)gql"sv, TaskState::New), - std::make_pair(R"gql(Started)gql"sv, TaskState::Started), - std::make_pair(R"gql(Complete)gql"sv, TaskState::Complete), - std::make_pair(R"gql(Unassigned)gql"sv, TaskState::Unassigned) - }; + case impl::VisitorState::Member_tasks_edges_0_node: + _pimpl->response.tasks.edges->back()->node = std::make_optional({}); + break; - const auto result = internal::sorted_map_lookup( - s_values, - std::string_view { value.get() }); + case impl::VisitorState::Complete: + break; - if (!result) - { - throw std::logic_error { R"ex(not a valid TaskState value)ex" }; + default: + break; } - - return *result; } -template <> -query::Miscellaneous::Response::anyType_UnionType Response::parse(response::Value&& response) +void ResponseVisitor::add_member([[maybe_unused]] std::string&& key) { - query::Miscellaneous::Response::anyType_UnionType result; - - if (response.type() == response::Type::Map) + switch (_pimpl->state) { - auto members = response.release(); - - for (auto& member : members) - { - if (member.first == R"js(__typename)js"sv) + case impl::VisitorState::Start: + if (key == "tasks"sv) { - result._typename = ModifiedResponse::parse(std::move(member.second)); - continue; + _pimpl->state = impl::VisitorState::Member_tasks; } - if (member.first == R"js(id)js"sv) + break; + + case impl::VisitorState::Member_tasks: + if (key == "edges"sv) { - result.id = ModifiedResponse::parse(std::move(member.second)); - continue; + _pimpl->state = impl::VisitorState::Member_tasks_edges; } - if (member.first == R"js(title)js"sv) + break; + + case impl::VisitorState::Member_tasks_edges_0_: + if (key == "node"sv) { - result.title = ModifiedResponse::parse(std::move(member.second)); - continue; + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; } - if (member.first == R"js(isComplete)js"sv) + break; + + case impl::VisitorState::Member_tasks_edges_0_node: + if (key == "id"sv) { - result.isComplete = ModifiedResponse::parse(std::move(member.second)); - continue; + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node_id; } - if (member.first == R"js(subject)js"sv) + else if (key == "title"sv) { - result.subject = ModifiedResponse::parse(std::move(member.second)); - continue; + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node_title; } - if (member.first == R"js(when)js"sv) + else if (key == "isComplete"sv) { - result.when = ModifiedResponse::parse(std::move(member.second)); - continue; + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node_isComplete; } - if (member.first == R"js(isNow)js"sv) + else if (key == "__typename"sv) { - result.isNow = ModifiedResponse::parse(std::move(member.second)); - continue; + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node__typename; } - } - } + break; - return result; -} + case impl::VisitorState::Complete: + break; -namespace query::Miscellaneous { + default: + break; + } +} + +void ResponseVisitor::end_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_tasks_edges_0_node: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_; + break; + + case impl::VisitorState::Member_tasks_edges_0_: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0; + break; + + case impl::VisitorState::Member_tasks: + _pimpl->state = impl::VisitorState::Start; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_tasks_edges: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0; + _pimpl->response.tasks.edges = std::make_optional>>({}); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_tasks_edges_0: + _pimpl->state = impl::VisitorState::Member_tasks; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_null() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_tasks_edges_0: + _pimpl->response.tasks.edges->push_back(std::nullopt); + break; + + case impl::VisitorState::Member_tasks_edges_0_node: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_; + _pimpl->response.tasks.edges->back()->node = std::nullopt; + break; + + case impl::VisitorState::Member_tasks_edges_0_node_title: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->title = std::nullopt; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_string([[maybe_unused]] std::string&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_tasks_edges_0_node_title: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->title = std::move(value); + break; + + case impl::VisitorState::Member_tasks_edges_0_node__typename: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->_typename = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_enum([[maybe_unused]] std::string&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_id([[maybe_unused]] response::IdType&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_tasks_edges_0_node_id: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->id = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_bool([[maybe_unused]] bool value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_tasks_edges_0_node_isComplete: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->isComplete = value; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_int([[maybe_unused]] int value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_float([[maybe_unused]] double value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::complete() +{ + _pimpl->state = impl::VisitorState::Complete; +} + +Response ResponseVisitor::response() +{ + Response response {}; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + _pimpl->state = impl::VisitorState::Start; + std::swap(_pimpl->response, response); + break; + + default: + break; + } + + return response; +} + +Response parseResponse(response::Value&& response) +{ + using namespace graphql::client; + + Response result; + + if (response.type() == response::Type::Map) + { + auto members = response.release(); + + for (auto& member : members) + { + if (member.first == R"js(tasks)js"sv) + { + result.tasks = ModifiedResponse::parse(std::move(member.second)); + continue; + } + } + } + + return result; +} + +[[nodiscard("unnecessary call")]] const std::string& Traits::GetRequestText() noexcept +{ + return client::GetRequestText(); +} + +[[nodiscard("unnecessary call")]] const peg::ast& Traits::GetRequestObject() noexcept +{ + return client::GetRequestObject(); +} + +[[nodiscard("unnecessary call")]] const std::string& Traits::GetOperationName() noexcept +{ + return Tasks::GetOperationName(); +} + +[[nodiscard("unnecessary conversion")]] Traits::Response Traits::parseResponse(response::Value&& response) +{ + return Tasks::parseResponse(std::move(response)); +} + +} // namespace multiple::client::query::Tasks +namespace client { + +using namespace multiple; + +template <> +graphql::multiple::client::query::UnreadCounts::Response::unreadCounts_FolderConnection::edges_FolderEdge::node_Folder Response::parse(response::Value&& response) +{ + graphql::multiple::client::query::UnreadCounts::Response::unreadCounts_FolderConnection::edges_FolderEdge::node_Folder result; + + if (response.type() == response::Type::Map) + { + auto members = response.release(); + + for (auto& member : members) + { + if (member.first == R"js(id)js"sv) + { + result.id = ModifiedResponse::parse(std::move(member.second)); + continue; + } + if (member.first == R"js(name)js"sv) + { + result.name = ModifiedResponse::parse(std::move(member.second)); + continue; + } + if (member.first == R"js(unreadCount)js"sv) + { + result.unreadCount = ModifiedResponse::parse(std::move(member.second)); + continue; + } + if (member.first == R"js(__typename)js"sv) + { + result._typename = ModifiedResponse::parse(std::move(member.second)); + continue; + } + } + } + + return result; +} + +template <> +graphql::multiple::client::query::UnreadCounts::Response::unreadCounts_FolderConnection::edges_FolderEdge Response::parse(response::Value&& response) +{ + graphql::multiple::client::query::UnreadCounts::Response::unreadCounts_FolderConnection::edges_FolderEdge result; + + if (response.type() == response::Type::Map) + { + auto members = response.release(); + + for (auto& member : members) + { + if (member.first == R"js(node)js"sv) + { + result.node = ModifiedResponse::parse(std::move(member.second)); + continue; + } + } + } + + return result; +} + +template <> +graphql::multiple::client::query::UnreadCounts::Response::unreadCounts_FolderConnection Response::parse(response::Value&& response) +{ + graphql::multiple::client::query::UnreadCounts::Response::unreadCounts_FolderConnection result; + + if (response.type() == response::Type::Map) + { + auto members = response.release(); + + for (auto& member : members) + { + if (member.first == R"js(edges)js"sv) + { + result.edges = ModifiedResponse::parse(std::move(member.second)); + continue; + } + } + } + + return result; +} + +} // namespace client + +namespace multiple::client::query::UnreadCounts { + +const std::string& GetOperationName() noexcept +{ + static const auto s_name = R"gql(UnreadCounts)gql"s; + + return s_name; +} + +struct ResponseVisitor::impl +{ + enum class VisitorState + { + Start, + Member_unreadCounts, + Member_unreadCounts_edges, + Member_unreadCounts_edges_0, + Member_unreadCounts_edges_0_, + Member_unreadCounts_edges_0_node, + Member_unreadCounts_edges_0_node_id, + Member_unreadCounts_edges_0_node_name, + Member_unreadCounts_edges_0_node_unreadCount, + Member_unreadCounts_edges_0_node__typename, + Complete, + }; + + VisitorState state { VisitorState::Start }; + Response response {}; +}; + +ResponseVisitor::ResponseVisitor() noexcept + : _pimpl { std::make_unique() } +{ +} + +ResponseVisitor::~ResponseVisitor() +{ +} + +void ResponseVisitor::add_value([[maybe_unused]] std::shared_ptr&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Member_unreadCounts: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.unreadCounts = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0: + _pimpl->response.unreadCounts.edges->push_back(ModifiedResponse::parse(response::Value { *value })); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_; + _pimpl->response.unreadCounts.edges->back()->node = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node_id: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->id = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node_name: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->name = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node_unreadCount: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->unreadCount = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node__typename: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->_typename = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::reserve([[maybe_unused]] std::size_t count) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_unreadCounts_edges_0: + _pimpl->response.unreadCounts.edges->reserve(count); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_unreadCounts_edges_0: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_; + _pimpl->response.unreadCounts.edges->push_back(std::make_optional({})); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node: + _pimpl->response.unreadCounts.edges->back()->node = std::make_optional({}); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_member([[maybe_unused]] std::string&& key) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Start: + if (key == "unreadCounts"sv) + { + _pimpl->state = impl::VisitorState::Member_unreadCounts; + } + break; + + case impl::VisitorState::Member_unreadCounts: + if (key == "edges"sv) + { + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges; + } + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_: + if (key == "node"sv) + { + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + } + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node: + if (key == "id"sv) + { + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node_id; + } + else if (key == "name"sv) + { + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node_name; + } + else if (key == "unreadCount"sv) + { + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node_unreadCount; + } + else if (key == "__typename"sv) + { + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node__typename; + } + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_unreadCounts_edges_0_node: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_; + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0; + break; + + case impl::VisitorState::Member_unreadCounts: + _pimpl->state = impl::VisitorState::Start; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_unreadCounts_edges: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0; + _pimpl->response.unreadCounts.edges = std::make_optional>>({}); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_unreadCounts_edges_0: + _pimpl->state = impl::VisitorState::Member_unreadCounts; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_null() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_unreadCounts_edges_0: + _pimpl->response.unreadCounts.edges->push_back(std::nullopt); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_; + _pimpl->response.unreadCounts.edges->back()->node = std::nullopt; + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node_name: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->name = std::nullopt; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_string([[maybe_unused]] std::string&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_unreadCounts_edges_0_node_name: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->name = std::move(value); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node__typename: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->_typename = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_enum([[maybe_unused]] std::string&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_id([[maybe_unused]] response::IdType&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_unreadCounts_edges_0_node_id: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->id = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_bool([[maybe_unused]] bool value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_int([[maybe_unused]] int value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_unreadCounts_edges_0_node_unreadCount: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->unreadCount = value; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_float([[maybe_unused]] double value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::complete() +{ + _pimpl->state = impl::VisitorState::Complete; +} + +Response ResponseVisitor::response() +{ + Response response {}; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + _pimpl->state = impl::VisitorState::Start; + std::swap(_pimpl->response, response); + break; + + default: + break; + } + + return response; +} + +Response parseResponse(response::Value&& response) +{ + using namespace graphql::client; + + Response result; + + if (response.type() == response::Type::Map) + { + auto members = response.release(); + + for (auto& member : members) + { + if (member.first == R"js(unreadCounts)js"sv) + { + result.unreadCounts = ModifiedResponse::parse(std::move(member.second)); + continue; + } + } + } + + return result; +} + +[[nodiscard("unnecessary call")]] const std::string& Traits::GetRequestText() noexcept +{ + return client::GetRequestText(); +} + +[[nodiscard("unnecessary call")]] const peg::ast& Traits::GetRequestObject() noexcept +{ + return client::GetRequestObject(); +} + +[[nodiscard("unnecessary call")]] const std::string& Traits::GetOperationName() noexcept +{ + return UnreadCounts::GetOperationName(); +} + +[[nodiscard("unnecessary conversion")]] Traits::Response Traits::parseResponse(response::Value&& response) +{ + return UnreadCounts::parseResponse(std::move(response)); +} + +} // namespace multiple::client::query::UnreadCounts + +namespace client { + +using namespace multiple; + +static const std::array, 4> s_valuesTaskState = { + std::make_pair(R"gql(New)gql"sv, TaskState::New), + std::make_pair(R"gql(Started)gql"sv, TaskState::Started), + std::make_pair(R"gql(Complete)gql"sv, TaskState::Complete), + std::make_pair(R"gql(Unassigned)gql"sv, TaskState::Unassigned) +}; + +template <> +TaskState Response::parse(response::Value&& value) +{ + if (!value.maybe_enum()) + { + throw std::logic_error { R"ex(not a valid TaskState value)ex" }; + } + + const auto result = internal::sorted_map_lookup( + s_valuesTaskState, + std::string_view { value.get() }); + + if (!result) + { + throw std::logic_error { R"ex(not a valid TaskState value)ex" }; + } + + return *result; +} + +template <> +graphql::multiple::client::query::Miscellaneous::Response::anyType_UnionType Response::parse(response::Value&& response) +{ + graphql::multiple::client::query::Miscellaneous::Response::anyType_UnionType result; + + if (response.type() == response::Type::Map) + { + auto members = response.release(); + + for (auto& member : members) + { + if (member.first == R"js(__typename)js"sv) + { + result._typename = ModifiedResponse::parse(std::move(member.second)); + continue; + } + if (member.first == R"js(id)js"sv) + { + result.id = ModifiedResponse::parse(std::move(member.second)); + continue; + } + if (member.first == R"js(title)js"sv) + { + result.title = ModifiedResponse::parse(std::move(member.second)); + continue; + } + if (member.first == R"js(isComplete)js"sv) + { + result.isComplete = ModifiedResponse::parse(std::move(member.second)); + continue; + } + if (member.first == R"js(subject)js"sv) + { + result.subject = ModifiedResponse::parse(std::move(member.second)); + continue; + } + if (member.first == R"js(when)js"sv) + { + result.when = ModifiedResponse::parse(std::move(member.second)); + continue; + } + if (member.first == R"js(isNow)js"sv) + { + result.isNow = ModifiedResponse::parse(std::move(member.second)); + continue; + } + } + } + + return result; +} + +} // namespace client + +namespace multiple::client::query::Miscellaneous { + +const std::string& GetOperationName() noexcept +{ + static const auto s_name = R"gql(Miscellaneous)gql"s; + + return s_name; +} + +struct ResponseVisitor::impl +{ + enum class VisitorState + { + Start, + Member_testTaskState, + Member_anyType, + Member_anyType_0, + Member_anyType_0_, + Member_anyType_0__typename, + Member_anyType_0_id, + Member_anyType_0_title, + Member_anyType_0_isComplete, + Member_anyType_0_subject, + Member_anyType_0_when, + Member_anyType_0_isNow, + Member_default_, + Complete, + }; + + VisitorState state { VisitorState::Start }; + Response response {}; +}; + +ResponseVisitor::ResponseVisitor() noexcept + : _pimpl { std::make_unique() } +{ +} + +ResponseVisitor::~ResponseVisitor() +{ +} + +void ResponseVisitor::add_value([[maybe_unused]] std::shared_ptr&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Member_testTaskState: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.testTaskState = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_anyType_0: + _pimpl->response.anyType.push_back(ModifiedResponse::parse(response::Value { *value })); + break; + + case impl::VisitorState::Member_anyType_0__typename: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->_typename = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_anyType_0_id: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->id = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_anyType_0_title: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->title = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_anyType_0_isComplete: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->isComplete = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_anyType_0_subject: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->subject = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_anyType_0_when: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->when = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_anyType_0_isNow: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->isNow = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_default_: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.default_ = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} -const std::string& GetOperationName() noexcept +void ResponseVisitor::reserve([[maybe_unused]] std::size_t count) { - static const auto s_name = R"gql(Miscellaneous)gql"s; + switch (_pimpl->state) + { + case impl::VisitorState::Member_anyType_0: + _pimpl->response.anyType.reserve(count); + break; - return s_name; + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_anyType_0: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.push_back(std::make_optional({})); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_member([[maybe_unused]] std::string&& key) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Start: + if (key == "testTaskState"sv) + { + _pimpl->state = impl::VisitorState::Member_testTaskState; + } + else if (key == "anyType"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType; + } + else if (key == "default"sv) + { + _pimpl->state = impl::VisitorState::Member_default_; + } + break; + + case impl::VisitorState::Member_anyType_0_: + if (key == "__typename"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType_0__typename; + } + else if (key == "id"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType_0_id; + } + else if (key == "title"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType_0_title; + } + else if (key == "isComplete"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType_0_isComplete; + } + else if (key == "subject"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType_0_subject; + } + else if (key == "when"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType_0_when; + } + else if (key == "isNow"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType_0_isNow; + } + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_anyType_0_: + _pimpl->state = impl::VisitorState::Member_anyType_0; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_anyType: + _pimpl->state = impl::VisitorState::Member_anyType_0; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_anyType_0: + _pimpl->state = impl::VisitorState::Start; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_null() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_anyType_0: + _pimpl->response.anyType.push_back(std::nullopt); + break; + + case impl::VisitorState::Member_anyType_0_title: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->title = std::nullopt; + break; + + case impl::VisitorState::Member_anyType_0_subject: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->subject = std::nullopt; + break; + + case impl::VisitorState::Member_anyType_0_when: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->when = std::nullopt; + break; + + case impl::VisitorState::Member_default_: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.default_ = std::nullopt; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_string([[maybe_unused]] std::string&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_anyType_0__typename: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->_typename = std::move(value); + break; + + case impl::VisitorState::Member_anyType_0_title: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->title = std::move(value); + break; + + case impl::VisitorState::Member_anyType_0_subject: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->subject = std::move(value); + break; + + case impl::VisitorState::Member_default_: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.default_ = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_enum([[maybe_unused]] std::string&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Member_testTaskState: + _pimpl->state = impl::VisitorState::Start; + if (const auto enumValue = internal::sorted_map_lookup(s_valuesTaskState, std::string_view { value })) + { + _pimpl->response.testTaskState = *enumValue; + } + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_id([[maybe_unused]] response::IdType&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_anyType_0_id: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->id = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_bool([[maybe_unused]] bool value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_anyType_0_isComplete: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->isComplete = value; + break; + + case impl::VisitorState::Member_anyType_0_isNow: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->isNow = value; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_int([[maybe_unused]] int value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_float([[maybe_unused]] double value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::complete() +{ + _pimpl->state = impl::VisitorState::Complete; +} + +Response ResponseVisitor::response() +{ + Response response {}; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + _pimpl->state = impl::VisitorState::Start; + std::swap(_pimpl->response, response); + break; + + default: + break; + } + + return response; } Response parseResponse(response::Value&& response) { + using namespace graphql::client; + Response result; if (response.type() == response::Type::Map) @@ -707,12 +2272,12 @@ Response parseResponse(response::Value&& response) [[nodiscard("unnecessary call")]] const std::string& Traits::GetRequestText() noexcept { - return multiple::GetRequestText(); + return client::GetRequestText(); } [[nodiscard("unnecessary call")]] const peg::ast& Traits::GetRequestObject() noexcept { - return multiple::GetRequestObject(); + return client::GetRequestObject(); } [[nodiscard("unnecessary call")]] const std::string& Traits::GetOperationName() noexcept @@ -725,7 +2290,11 @@ Response parseResponse(response::Value&& response) return Miscellaneous::parseResponse(std::move(response)); } -} // namespace query::Miscellaneous +} // namespace multiple::client::query::Miscellaneous + +namespace client { + +using namespace multiple; template <> response::Value Variable::serialize(TaskState&& value) @@ -739,7 +2308,7 @@ response::Value Variable::serialize(TaskState&& value) response::Value result { response::Type::EnumValue }; - result.set(std::string { s_names[static_cast(value)] }); + result.set(std::string { s_names[static_cast(value)] }); return result; } @@ -753,14 +2322,15 @@ response::Value Variable::serialize(CompleteTaskInput&& input result.emplace_back(R"js(testTaskState)js"s, ModifiedVariable::serialize(std::move(inputValue.testTaskState))); result.emplace_back(R"js(isComplete)js"s, ModifiedVariable::serialize(std::move(inputValue.isComplete))); result.emplace_back(R"js(clientMutationId)js"s, ModifiedVariable::serialize(std::move(inputValue.clientMutationId))); + result.emplace_back(R"js(boolList)js"s, ModifiedVariable::serialize(std::move(inputValue.boolList))); return result; } template <> -mutation::CompleteTaskMutation::Response::completedTask_CompleteTaskPayload::completedTask_Task Response::parse(response::Value&& response) +graphql::multiple::client::mutation::CompleteTaskMutation::Response::completedTask_CompleteTaskPayload::completedTask_Task Response::parse(response::Value&& response) { - mutation::CompleteTaskMutation::Response::completedTask_CompleteTaskPayload::completedTask_Task result; + graphql::multiple::client::mutation::CompleteTaskMutation::Response::completedTask_CompleteTaskPayload::completedTask_Task result; if (response.type() == response::Type::Map) { @@ -790,9 +2360,9 @@ mutation::CompleteTaskMutation::Response::completedTask_CompleteTaskPayload::com } template <> -mutation::CompleteTaskMutation::Response::completedTask_CompleteTaskPayload Response::parse(response::Value&& response) +graphql::multiple::client::mutation::CompleteTaskMutation::Response::completedTask_CompleteTaskPayload Response::parse(response::Value&& response) { - mutation::CompleteTaskMutation::Response::completedTask_CompleteTaskPayload result; + graphql::multiple::client::mutation::CompleteTaskMutation::Response::completedTask_CompleteTaskPayload result; if (response.type() == response::Type::Map) { @@ -802,7 +2372,7 @@ mutation::CompleteTaskMutation::Response::completedTask_CompleteTaskPayload Resp { if (member.first == R"js(completedTask)js"sv) { - result.completedTask = ModifiedResponse::parse(std::move(member.second)); + result.completedTask = ModifiedResponse::parse(std::move(member.second)); continue; } if (member.first == R"js(clientMutationId)js"sv) @@ -816,7 +2386,9 @@ mutation::CompleteTaskMutation::Response::completedTask_CompleteTaskPayload Resp return result; } -namespace mutation::CompleteTaskMutation { +} // namespace client + +namespace multiple::client::mutation::CompleteTaskMutation { const std::string& GetOperationName() noexcept { @@ -827,6 +2399,8 @@ const std::string& GetOperationName() noexcept response::Value serializeVariables(Variables&& variables) { + using namespace graphql::client; + response::Value result { response::Type::Map }; result.emplace_back(R"js(input)js"s, ModifiedVariable::serialize(std::move(variables.input))); @@ -835,8 +2409,342 @@ response::Value serializeVariables(Variables&& variables) return result; } +struct ResponseVisitor::impl +{ + enum class VisitorState + { + Start, + Member_completedTask, + Member_completedTask_completedTask, + Member_completedTask_completedTask_completedTaskId, + Member_completedTask_completedTask_title, + Member_completedTask_completedTask_isComplete, + Member_completedTask_clientMutationId, + Complete, + }; + + VisitorState state { VisitorState::Start }; + Response response {}; +}; + +ResponseVisitor::ResponseVisitor() noexcept + : _pimpl { std::make_unique() } +{ +} + +ResponseVisitor::~ResponseVisitor() +{ +} + +void ResponseVisitor::add_value([[maybe_unused]] std::shared_ptr&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Member_completedTask: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.completedTask = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_completedTask_completedTask: + _pimpl->state = impl::VisitorState::Member_completedTask; + _pimpl->response.completedTask.completedTask = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_completedTask_completedTask_completedTaskId: + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + _pimpl->response.completedTask.completedTask->completedTaskId = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_completedTask_completedTask_title: + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + _pimpl->response.completedTask.completedTask->title = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_completedTask_completedTask_isComplete: + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + _pimpl->response.completedTask.completedTask->isComplete = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_completedTask_clientMutationId: + _pimpl->state = impl::VisitorState::Member_completedTask; + _pimpl->response.completedTask.clientMutationId = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::reserve([[maybe_unused]] std::size_t count) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_completedTask_completedTask: + _pimpl->response.completedTask.completedTask = std::make_optional({}); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_member([[maybe_unused]] std::string&& key) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Start: + if (key == "completedTask"sv) + { + _pimpl->state = impl::VisitorState::Member_completedTask; + } + break; + + case impl::VisitorState::Member_completedTask: + if (key == "completedTask"sv) + { + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + } + else if (key == "clientMutationId"sv) + { + _pimpl->state = impl::VisitorState::Member_completedTask_clientMutationId; + } + break; + + case impl::VisitorState::Member_completedTask_completedTask: + if (key == "completedTaskId"sv) + { + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask_completedTaskId; + } + else if (key == "title"sv) + { + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask_title; + } + else if (key == "isComplete"sv) + { + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask_isComplete; + } + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_completedTask_completedTask: + _pimpl->state = impl::VisitorState::Member_completedTask; + break; + + case impl::VisitorState::Member_completedTask: + _pimpl->state = impl::VisitorState::Start; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_null() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_completedTask_completedTask: + _pimpl->state = impl::VisitorState::Member_completedTask; + _pimpl->response.completedTask.completedTask = std::nullopt; + break; + + case impl::VisitorState::Member_completedTask_completedTask_title: + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + _pimpl->response.completedTask.completedTask->title = std::nullopt; + break; + + case impl::VisitorState::Member_completedTask_clientMutationId: + _pimpl->state = impl::VisitorState::Member_completedTask; + _pimpl->response.completedTask.clientMutationId = std::nullopt; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_string([[maybe_unused]] std::string&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_completedTask_completedTask_title: + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + _pimpl->response.completedTask.completedTask->title = std::move(value); + break; + + case impl::VisitorState::Member_completedTask_clientMutationId: + _pimpl->state = impl::VisitorState::Member_completedTask; + _pimpl->response.completedTask.clientMutationId = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_enum([[maybe_unused]] std::string&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_id([[maybe_unused]] response::IdType&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_completedTask_completedTask_completedTaskId: + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + _pimpl->response.completedTask.completedTask->completedTaskId = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_bool([[maybe_unused]] bool value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_completedTask_completedTask_isComplete: + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + _pimpl->response.completedTask.completedTask->isComplete = value; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_int([[maybe_unused]] int value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_float([[maybe_unused]] double value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::complete() +{ + _pimpl->state = impl::VisitorState::Complete; +} + +Response ResponseVisitor::response() +{ + Response response {}; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + _pimpl->state = impl::VisitorState::Start; + std::swap(_pimpl->response, response); + break; + + default: + break; + } + + return response; +} + Response parseResponse(response::Value&& response) { + using namespace graphql::client; + Response result; if (response.type() == response::Type::Map) @@ -858,12 +2766,12 @@ Response parseResponse(response::Value&& response) [[nodiscard("unnecessary call")]] const std::string& Traits::GetRequestText() noexcept { - return multiple::GetRequestText(); + return client::GetRequestText(); } [[nodiscard("unnecessary call")]] const peg::ast& Traits::GetRequestObject() noexcept { - return multiple::GetRequestObject(); + return client::GetRequestObject(); } [[nodiscard("unnecessary call")]] const std::string& Traits::GetOperationName() noexcept @@ -881,5 +2789,5 @@ Response parseResponse(response::Value&& response) return CompleteTaskMutation::parseResponse(std::move(response)); } -} // namespace mutation::CompleteTaskMutation -} // namespace graphql::client +} // namespace multiple::client::mutation::CompleteTaskMutation +} // namespace graphql diff --git a/samples/client/multiple/MultipleQueriesClient.h b/samples/client/multiple/MultipleQueriesClient.h index af157461..a7c7460e 100644 --- a/samples/client/multiple/MultipleQueriesClient.h +++ b/samples/client/multiple/MultipleQueriesClient.h @@ -14,20 +14,18 @@ #include "graphqlservice/internal/Version.h" -// Check if the library version is compatible with clientgen 4.5.0 -static_assert(graphql::internal::MajorVersion == 4, "regenerate with clientgen: major version mismatch"); -static_assert(graphql::internal::MinorVersion == 5, "regenerate with clientgen: minor version mismatch"); - #include #include #include -namespace graphql::client { +// Check if the library version is compatible with clientgen 5.0.0 +static_assert(graphql::internal::MajorVersion == 5, "regenerate with clientgen: major version mismatch"); +static_assert(graphql::internal::MinorVersion == 0, "regenerate with clientgen: minor version mismatch"); + +namespace graphql::multiple { -/// -/// Operations: query Appointments, query Tasks, query UnreadCounts, query Miscellaneous, mutation CompleteTaskMutation -/// -/// +/// # Operations: query Appointments, query Tasks, query UnreadCounts, query Miscellaneous, mutation CompleteTaskMutation +/// ```graphql /// # Copyright (c) Microsoft Corporation. All rights reserved. /// # Licensed under the MIT License. /// @@ -108,8 +106,8 @@ namespace graphql::client { /// clientMutationId @skip(if: $skipClientMutationId) /// } /// } -/// -namespace multiple { +/// ``` +namespace client { // Return the original text of the request document. [[nodiscard("unnecessary call")]] const std::string& GetRequestText() noexcept; @@ -117,6 +115,8 @@ namespace multiple { // Return a pre-parsed, pre-validated request object. [[nodiscard("unnecessary call")]] const peg::ast& GetRequestObject() noexcept; +} // namespace client + enum class [[nodiscard("unnecessary conversion")]] TaskState { Unassigned, @@ -132,7 +132,8 @@ struct [[nodiscard("unnecessary construction")]] CompleteTaskInput response::IdType idArg, std::optional testTaskStateArg, std::optional isCompleteArg, - std::optional clientMutationIdArg) noexcept; + std::optional clientMutationIdArg, + std::optional> boolListArg) noexcept; CompleteTaskInput(const CompleteTaskInput& other); CompleteTaskInput(CompleteTaskInput&& other) noexcept; ~CompleteTaskInput(); @@ -144,14 +145,15 @@ struct [[nodiscard("unnecessary construction")]] CompleteTaskInput std::optional testTaskState; std::optional isComplete; std::optional clientMutationId; + std::optional> boolList; }; -} // namespace multiple +namespace client { namespace query::Appointments { -using multiple::GetRequestText; -using multiple::GetRequestObject; +using graphql::multiple::client::GetRequestText; +using graphql::multiple::client::GetRequestObject; // Return the name of this operation in the shared request document. [[nodiscard("unnecessary call")]] const std::string& GetOperationName() noexcept; @@ -180,6 +182,37 @@ struct [[nodiscard("unnecessary construction")]] Response appointments_AppointmentConnection appointments {}; }; +class ResponseVisitor + : public std::enable_shared_from_this +{ +public: + ResponseVisitor() noexcept; + ~ResponseVisitor(); + + void add_value(std::shared_ptr&&); + void reserve(std::size_t count); + void start_object(); + void add_member(std::string&& key); + void end_object(); + void start_array(); + void end_array(); + void add_null(); + void add_string(std::string&& value); + void add_enum(std::string&& value); + void add_id(response::IdType&& value); + void add_bool(bool value); + void add_int(int value); + void add_float(double value); + void complete(); + + Response response(); + +private: + struct impl; + + std::unique_ptr _pimpl; +}; + [[nodiscard("unnecessary conversion")]] Response parseResponse(response::Value&& response); struct Traits @@ -189,6 +222,7 @@ struct Traits [[nodiscard("unnecessary call")]] static const std::string& GetOperationName() noexcept; using Response = Appointments::Response; + using ResponseVisitor = Appointments::ResponseVisitor; [[nodiscard("unnecessary conversion")]] static Response parseResponse(response::Value&& response); }; @@ -197,8 +231,8 @@ struct Traits namespace query::Tasks { -using multiple::GetRequestText; -using multiple::GetRequestObject; +using graphql::multiple::client::GetRequestText; +using graphql::multiple::client::GetRequestObject; // Return the name of this operation in the shared request document. [[nodiscard("unnecessary call")]] const std::string& GetOperationName() noexcept; @@ -226,6 +260,37 @@ struct [[nodiscard("unnecessary construction")]] Response tasks_TaskConnection tasks {}; }; +class ResponseVisitor + : public std::enable_shared_from_this +{ +public: + ResponseVisitor() noexcept; + ~ResponseVisitor(); + + void add_value(std::shared_ptr&&); + void reserve(std::size_t count); + void start_object(); + void add_member(std::string&& key); + void end_object(); + void start_array(); + void end_array(); + void add_null(); + void add_string(std::string&& value); + void add_enum(std::string&& value); + void add_id(response::IdType&& value); + void add_bool(bool value); + void add_int(int value); + void add_float(double value); + void complete(); + + Response response(); + +private: + struct impl; + + std::unique_ptr _pimpl; +}; + [[nodiscard("unnecessary conversion")]] Response parseResponse(response::Value&& response); struct Traits @@ -235,6 +300,7 @@ struct Traits [[nodiscard("unnecessary call")]] static const std::string& GetOperationName() noexcept; using Response = Tasks::Response; + using ResponseVisitor = Tasks::ResponseVisitor; [[nodiscard("unnecessary conversion")]] static Response parseResponse(response::Value&& response); }; @@ -243,8 +309,8 @@ struct Traits namespace query::UnreadCounts { -using multiple::GetRequestText; -using multiple::GetRequestObject; +using graphql::multiple::client::GetRequestText; +using graphql::multiple::client::GetRequestObject; // Return the name of this operation in the shared request document. [[nodiscard("unnecessary call")]] const std::string& GetOperationName() noexcept; @@ -272,6 +338,37 @@ struct [[nodiscard("unnecessary construction")]] Response unreadCounts_FolderConnection unreadCounts {}; }; +class ResponseVisitor + : public std::enable_shared_from_this +{ +public: + ResponseVisitor() noexcept; + ~ResponseVisitor(); + + void add_value(std::shared_ptr&&); + void reserve(std::size_t count); + void start_object(); + void add_member(std::string&& key); + void end_object(); + void start_array(); + void end_array(); + void add_null(); + void add_string(std::string&& value); + void add_enum(std::string&& value); + void add_id(response::IdType&& value); + void add_bool(bool value); + void add_int(int value); + void add_float(double value); + void complete(); + + Response response(); + +private: + struct impl; + + std::unique_ptr _pimpl; +}; + [[nodiscard("unnecessary conversion")]] Response parseResponse(response::Value&& response); struct Traits @@ -281,6 +378,7 @@ struct Traits [[nodiscard("unnecessary call")]] static const std::string& GetOperationName() noexcept; using Response = UnreadCounts::Response; + using ResponseVisitor = UnreadCounts::ResponseVisitor; [[nodiscard("unnecessary conversion")]] static Response parseResponse(response::Value&& response); }; @@ -289,13 +387,13 @@ struct Traits namespace query::Miscellaneous { -using multiple::GetRequestText; -using multiple::GetRequestObject; +using graphql::multiple::client::GetRequestText; +using graphql::multiple::client::GetRequestObject; // Return the name of this operation in the shared request document. [[nodiscard("unnecessary call")]] const std::string& GetOperationName() noexcept; -using multiple::TaskState; +using graphql::multiple::TaskState; struct [[nodiscard("unnecessary construction")]] Response { @@ -315,6 +413,37 @@ struct [[nodiscard("unnecessary construction")]] Response std::optional default_ {}; }; +class ResponseVisitor + : public std::enable_shared_from_this +{ +public: + ResponseVisitor() noexcept; + ~ResponseVisitor(); + + void add_value(std::shared_ptr&&); + void reserve(std::size_t count); + void start_object(); + void add_member(std::string&& key); + void end_object(); + void start_array(); + void end_array(); + void add_null(); + void add_string(std::string&& value); + void add_enum(std::string&& value); + void add_id(response::IdType&& value); + void add_bool(bool value); + void add_int(int value); + void add_float(double value); + void complete(); + + Response response(); + +private: + struct impl; + + std::unique_ptr _pimpl; +}; + [[nodiscard("unnecessary conversion")]] Response parseResponse(response::Value&& response); struct Traits @@ -324,6 +453,7 @@ struct Traits [[nodiscard("unnecessary call")]] static const std::string& GetOperationName() noexcept; using Response = Miscellaneous::Response; + using ResponseVisitor = Miscellaneous::ResponseVisitor; [[nodiscard("unnecessary conversion")]] static Response parseResponse(response::Value&& response); }; @@ -332,15 +462,15 @@ struct Traits namespace mutation::CompleteTaskMutation { -using multiple::GetRequestText; -using multiple::GetRequestObject; +using graphql::multiple::client::GetRequestText; +using graphql::multiple::client::GetRequestObject; // Return the name of this operation in the shared request document. [[nodiscard("unnecessary call")]] const std::string& GetOperationName() noexcept; -using multiple::TaskState; +using graphql::multiple::TaskState; -using multiple::CompleteTaskInput; +using graphql::multiple::CompleteTaskInput; struct [[nodiscard("unnecessary construction")]] Variables { @@ -368,6 +498,37 @@ struct [[nodiscard("unnecessary construction")]] Response completedTask_CompleteTaskPayload completedTask {}; }; +class ResponseVisitor + : public std::enable_shared_from_this +{ +public: + ResponseVisitor() noexcept; + ~ResponseVisitor(); + + void add_value(std::shared_ptr&&); + void reserve(std::size_t count); + void start_object(); + void add_member(std::string&& key); + void end_object(); + void start_array(); + void end_array(); + void add_null(); + void add_string(std::string&& value); + void add_enum(std::string&& value); + void add_id(response::IdType&& value); + void add_bool(bool value); + void add_int(int value); + void add_float(double value); + void complete(); + + Response response(); + +private: + struct impl; + + std::unique_ptr _pimpl; +}; + [[nodiscard("unnecessary conversion")]] Response parseResponse(response::Value&& response); struct Traits @@ -381,11 +542,13 @@ struct Traits [[nodiscard("unnecessary conversion")]] static response::Value serializeVariables(Variables&& variables); using Response = CompleteTaskMutation::Response; + using ResponseVisitor = CompleteTaskMutation::ResponseVisitor; [[nodiscard("unnecessary conversion")]] static Response parseResponse(response::Value&& response); }; } // namespace mutation::CompleteTaskMutation -} // namespace graphql::client +} // namespace client +} // namespace graphql::multiple #endif // MULTIPLEQUERIESCLIENT_H diff --git a/samples/client/multiple/MultipleQueriesClient.ixx b/samples/client/multiple/MultipleQueriesClient.ixx new file mode 100644 index 00000000..22f60abd --- /dev/null +++ b/samples/client/multiple/MultipleQueriesClient.ixx @@ -0,0 +1,107 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "MultipleQueriesClient.h" + +export module GraphQL.MultipleQueries.MultipleQueriesClient; + +export namespace graphql::multiple { + +namespace client { + +using client::GetRequestText; +using client::GetRequestObject; + +} // namespace client + +using multiple::TaskState; + +using multiple::CompleteTaskInput; + +namespace client { + +namespace query::Appointments { + +using graphql::multiple::client::GetRequestText; +using graphql::multiple::client::GetRequestObject; +using Appointments::GetOperationName; + +using Appointments::Response; +using Appointments::ResponseVisitor; +using Appointments::parseResponse; + +using Appointments::Traits; + +} // namespace query::Appointments + +namespace query::Tasks { + +using graphql::multiple::client::GetRequestText; +using graphql::multiple::client::GetRequestObject; +using Tasks::GetOperationName; + +using Tasks::Response; +using Tasks::ResponseVisitor; +using Tasks::parseResponse; + +using Tasks::Traits; + +} // namespace query::Tasks + +namespace query::UnreadCounts { + +using graphql::multiple::client::GetRequestText; +using graphql::multiple::client::GetRequestObject; +using UnreadCounts::GetOperationName; + +using UnreadCounts::Response; +using UnreadCounts::ResponseVisitor; +using UnreadCounts::parseResponse; + +using UnreadCounts::Traits; + +} // namespace query::UnreadCounts + +namespace query::Miscellaneous { + +using graphql::multiple::client::GetRequestText; +using graphql::multiple::client::GetRequestObject; +using Miscellaneous::GetOperationName; + +using graphql::multiple::TaskState; + +using Miscellaneous::Response; +using Miscellaneous::ResponseVisitor; +using Miscellaneous::parseResponse; + +using Miscellaneous::Traits; + +} // namespace query::Miscellaneous + +namespace mutation::CompleteTaskMutation { + +using graphql::multiple::client::GetRequestText; +using graphql::multiple::client::GetRequestObject; +using CompleteTaskMutation::GetOperationName; + +using graphql::multiple::TaskState; + +using graphql::multiple::CompleteTaskInput; + +using CompleteTaskMutation::Variables; +using CompleteTaskMutation::serializeVariables; + +using CompleteTaskMutation::Response; +using CompleteTaskMutation::ResponseVisitor; +using CompleteTaskMutation::parseResponse; + +using CompleteTaskMutation::Traits; + +} // namespace mutation::CompleteTaskMutation + +} // namespace client +} // namespace graphql::multiple diff --git a/samples/client/mutate/CMakeLists.txt b/samples/client/mutate/CMakeLists.txt index 61b3ab67..bdf63420 100644 --- a/samples/client/mutate/CMakeLists.txt +++ b/samples/client/mutate/CMakeLists.txt @@ -1,7 +1,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.28) # Normally this would be handled by find_package(cppgraphqlgen CONFIG). include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/cppgraphqlgen-functions.cmake) diff --git a/samples/client/mutate/MutateClient.cpp b/samples/client/mutate/MutateClient.cpp index 027a03c9..248b86a8 100644 --- a/samples/client/mutate/MutateClient.cpp +++ b/samples/client/mutate/MutateClient.cpp @@ -9,15 +9,16 @@ #include #include -#include +#include #include #include #include using namespace std::literals; -namespace graphql::client { +namespace graphql { namespace mutate { +namespace client { const std::string& GetRequestText() noexcept { @@ -54,11 +55,16 @@ const peg::ast& GetRequestObject() noexcept return s_request; } +} // namespace client + +using namespace graphql::client; + CompleteTaskInput::CompleteTaskInput() noexcept : id {} , testTaskState {} , isComplete {} , clientMutationId {} + , boolList {} { // Explicit definition to prevent ODR violations when LTO is enabled. } @@ -67,11 +73,13 @@ CompleteTaskInput::CompleteTaskInput( response::IdType idArg, std::optional testTaskStateArg, std::optional isCompleteArg, - std::optional clientMutationIdArg) noexcept + std::optional clientMutationIdArg, + std::optional> boolListArg) noexcept : id { std::move(idArg) } , testTaskState { std::move(testTaskStateArg) } , isComplete { std::move(isCompleteArg) } , clientMutationId { std::move(clientMutationIdArg) } + , boolList { std::move(boolListArg) } { } @@ -80,6 +88,7 @@ CompleteTaskInput::CompleteTaskInput(const CompleteTaskInput& other) , testTaskState { ModifiedVariable::duplicate(other.testTaskState) } , isComplete { ModifiedVariable::duplicate(other.isComplete) } , clientMutationId { ModifiedVariable::duplicate(other.clientMutationId) } + , boolList { ModifiedVariable::duplicate(other.boolList) } { } @@ -88,6 +97,7 @@ CompleteTaskInput::CompleteTaskInput(CompleteTaskInput&& other) noexcept , testTaskState { std::move(other.testTaskState) } , isComplete { std::move(other.isComplete) } , clientMutationId { std::move(other.clientMutationId) } + , boolList { std::move(other.boolList) } { } @@ -107,12 +117,15 @@ CompleteTaskInput& CompleteTaskInput::operator=(CompleteTaskInput&& other) noexc testTaskState = std::move(other.testTaskState); isComplete = std::move(other.isComplete); clientMutationId = std::move(other.clientMutationId); + boolList = std::move(other.boolList); return *this; } } // namespace mutate +namespace client { + using namespace mutate; template <> @@ -127,7 +140,7 @@ response::Value Variable::serialize(TaskState&& value) response::Value result { response::Type::EnumValue }; - result.set(std::string { s_names[static_cast(value)] }); + result.set(std::string { s_names[static_cast(value)] }); return result; } @@ -141,10 +154,18 @@ response::Value Variable::serialize(CompleteTaskInput&& input result.emplace_back(R"js(testTaskState)js"s, ModifiedVariable::serialize(std::move(inputValue.testTaskState))); result.emplace_back(R"js(isComplete)js"s, ModifiedVariable::serialize(std::move(inputValue.isComplete))); result.emplace_back(R"js(clientMutationId)js"s, ModifiedVariable::serialize(std::move(inputValue.clientMutationId))); + result.emplace_back(R"js(boolList)js"s, ModifiedVariable::serialize(std::move(inputValue.boolList))); return result; } +static const std::array, 4> s_valuesTaskState = { + std::make_pair(R"gql(New)gql"sv, TaskState::New), + std::make_pair(R"gql(Started)gql"sv, TaskState::Started), + std::make_pair(R"gql(Complete)gql"sv, TaskState::Complete), + std::make_pair(R"gql(Unassigned)gql"sv, TaskState::Unassigned) +}; + template <> TaskState Response::parse(response::Value&& value) { @@ -153,15 +174,8 @@ TaskState Response::parse(response::Value&& value) throw std::logic_error { R"ex(not a valid TaskState value)ex" }; } - static const std::array, 4> s_values = { - std::make_pair(R"gql(New)gql"sv, TaskState::New), - std::make_pair(R"gql(Started)gql"sv, TaskState::Started), - std::make_pair(R"gql(Complete)gql"sv, TaskState::Complete), - std::make_pair(R"gql(Unassigned)gql"sv, TaskState::Unassigned) - }; - const auto result = internal::sorted_map_lookup( - s_values, + s_valuesTaskState, std::string_view { value.get() }); if (!result) @@ -173,9 +187,9 @@ TaskState Response::parse(response::Value&& value) } template <> -mutation::CompleteTaskMutation::Response::completedTask_CompleteTaskPayload::completedTask_Task Response::parse(response::Value&& response) +graphql::mutate::client::mutation::CompleteTaskMutation::Response::completedTask_CompleteTaskPayload::completedTask_Task Response::parse(response::Value&& response) { - mutation::CompleteTaskMutation::Response::completedTask_CompleteTaskPayload::completedTask_Task result; + graphql::mutate::client::mutation::CompleteTaskMutation::Response::completedTask_CompleteTaskPayload::completedTask_Task result; if (response.type() == response::Type::Map) { @@ -205,9 +219,9 @@ mutation::CompleteTaskMutation::Response::completedTask_CompleteTaskPayload::com } template <> -mutation::CompleteTaskMutation::Response::completedTask_CompleteTaskPayload Response::parse(response::Value&& response) +graphql::mutate::client::mutation::CompleteTaskMutation::Response::completedTask_CompleteTaskPayload Response::parse(response::Value&& response) { - mutation::CompleteTaskMutation::Response::completedTask_CompleteTaskPayload result; + graphql::mutate::client::mutation::CompleteTaskMutation::Response::completedTask_CompleteTaskPayload result; if (response.type() == response::Type::Map) { @@ -217,7 +231,7 @@ mutation::CompleteTaskMutation::Response::completedTask_CompleteTaskPayload Resp { if (member.first == R"js(completedTask)js"sv) { - result.completedTask = ModifiedResponse::parse(std::move(member.second)); + result.completedTask = ModifiedResponse::parse(std::move(member.second)); continue; } if (member.first == R"js(clientMutationId)js"sv) @@ -231,7 +245,9 @@ mutation::CompleteTaskMutation::Response::completedTask_CompleteTaskPayload Resp return result; } -namespace mutation::CompleteTaskMutation { +} // namespace client + +namespace mutate::client::mutation::CompleteTaskMutation { const std::string& GetOperationName() noexcept { @@ -242,6 +258,8 @@ const std::string& GetOperationName() noexcept response::Value serializeVariables(Variables&& variables) { + using namespace graphql::client; + response::Value result { response::Type::Map }; result.emplace_back(R"js(input)js"s, ModifiedVariable::serialize(std::move(variables.input))); @@ -250,8 +268,342 @@ response::Value serializeVariables(Variables&& variables) return result; } +struct ResponseVisitor::impl +{ + enum class VisitorState + { + Start, + Member_completedTask, + Member_completedTask_completedTask, + Member_completedTask_completedTask_completedTaskId, + Member_completedTask_completedTask_title, + Member_completedTask_completedTask_isComplete, + Member_completedTask_clientMutationId, + Complete, + }; + + VisitorState state { VisitorState::Start }; + Response response {}; +}; + +ResponseVisitor::ResponseVisitor() noexcept + : _pimpl { std::make_unique() } +{ +} + +ResponseVisitor::~ResponseVisitor() +{ +} + +void ResponseVisitor::add_value([[maybe_unused]] std::shared_ptr&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Member_completedTask: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.completedTask = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_completedTask_completedTask: + _pimpl->state = impl::VisitorState::Member_completedTask; + _pimpl->response.completedTask.completedTask = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_completedTask_completedTask_completedTaskId: + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + _pimpl->response.completedTask.completedTask->completedTaskId = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_completedTask_completedTask_title: + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + _pimpl->response.completedTask.completedTask->title = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_completedTask_completedTask_isComplete: + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + _pimpl->response.completedTask.completedTask->isComplete = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_completedTask_clientMutationId: + _pimpl->state = impl::VisitorState::Member_completedTask; + _pimpl->response.completedTask.clientMutationId = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::reserve([[maybe_unused]] std::size_t count) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_completedTask_completedTask: + _pimpl->response.completedTask.completedTask = std::make_optional({}); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_member([[maybe_unused]] std::string&& key) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Start: + if (key == "completedTask"sv) + { + _pimpl->state = impl::VisitorState::Member_completedTask; + } + break; + + case impl::VisitorState::Member_completedTask: + if (key == "completedTask"sv) + { + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + } + else if (key == "clientMutationId"sv) + { + _pimpl->state = impl::VisitorState::Member_completedTask_clientMutationId; + } + break; + + case impl::VisitorState::Member_completedTask_completedTask: + if (key == "completedTaskId"sv) + { + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask_completedTaskId; + } + else if (key == "title"sv) + { + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask_title; + } + else if (key == "isComplete"sv) + { + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask_isComplete; + } + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_completedTask_completedTask: + _pimpl->state = impl::VisitorState::Member_completedTask; + break; + + case impl::VisitorState::Member_completedTask: + _pimpl->state = impl::VisitorState::Start; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_null() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_completedTask_completedTask: + _pimpl->state = impl::VisitorState::Member_completedTask; + _pimpl->response.completedTask.completedTask = std::nullopt; + break; + + case impl::VisitorState::Member_completedTask_completedTask_title: + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + _pimpl->response.completedTask.completedTask->title = std::nullopt; + break; + + case impl::VisitorState::Member_completedTask_clientMutationId: + _pimpl->state = impl::VisitorState::Member_completedTask; + _pimpl->response.completedTask.clientMutationId = std::nullopt; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_string([[maybe_unused]] std::string&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_completedTask_completedTask_title: + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + _pimpl->response.completedTask.completedTask->title = std::move(value); + break; + + case impl::VisitorState::Member_completedTask_clientMutationId: + _pimpl->state = impl::VisitorState::Member_completedTask; + _pimpl->response.completedTask.clientMutationId = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_enum([[maybe_unused]] std::string&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_id([[maybe_unused]] response::IdType&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_completedTask_completedTask_completedTaskId: + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + _pimpl->response.completedTask.completedTask->completedTaskId = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_bool([[maybe_unused]] bool value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_completedTask_completedTask_isComplete: + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + _pimpl->response.completedTask.completedTask->isComplete = value; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_int([[maybe_unused]] int value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_float([[maybe_unused]] double value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::complete() +{ + _pimpl->state = impl::VisitorState::Complete; +} + +Response ResponseVisitor::response() +{ + Response response {}; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + _pimpl->state = impl::VisitorState::Start; + std::swap(_pimpl->response, response); + break; + + default: + break; + } + + return response; +} + Response parseResponse(response::Value&& response) { + using namespace graphql::client; + Response result; if (response.type() == response::Type::Map) @@ -273,12 +625,12 @@ Response parseResponse(response::Value&& response) [[nodiscard("unnecessary call")]] const std::string& Traits::GetRequestText() noexcept { - return mutate::GetRequestText(); + return client::GetRequestText(); } [[nodiscard("unnecessary call")]] const peg::ast& Traits::GetRequestObject() noexcept { - return mutate::GetRequestObject(); + return client::GetRequestObject(); } [[nodiscard("unnecessary call")]] const std::string& Traits::GetOperationName() noexcept @@ -296,5 +648,5 @@ Response parseResponse(response::Value&& response) return CompleteTaskMutation::parseResponse(std::move(response)); } -} // namespace mutation::CompleteTaskMutation -} // namespace graphql::client +} // namespace mutate::client::mutation::CompleteTaskMutation +} // namespace graphql diff --git a/samples/client/mutate/MutateClient.h b/samples/client/mutate/MutateClient.h index a2f304eb..72a67a45 100644 --- a/samples/client/mutate/MutateClient.h +++ b/samples/client/mutate/MutateClient.h @@ -14,20 +14,18 @@ #include "graphqlservice/internal/Version.h" -// Check if the library version is compatible with clientgen 4.5.0 -static_assert(graphql::internal::MajorVersion == 4, "regenerate with clientgen: major version mismatch"); -static_assert(graphql::internal::MinorVersion == 5, "regenerate with clientgen: minor version mismatch"); - #include #include #include -namespace graphql::client { +// Check if the library version is compatible with clientgen 5.0.0 +static_assert(graphql::internal::MajorVersion == 5, "regenerate with clientgen: major version mismatch"); +static_assert(graphql::internal::MinorVersion == 0, "regenerate with clientgen: minor version mismatch"); + +namespace graphql::mutate { -/// -/// Operation: mutation CompleteTaskMutation -/// -/// +/// # Operation: mutation CompleteTaskMutation +/// ```graphql /// # Copyright (c) Microsoft Corporation. All rights reserved. /// # Licensed under the MIT License. /// @@ -41,8 +39,8 @@ namespace graphql::client { /// clientMutationId @skip(if: $skipClientMutationId) /// } /// } -/// -namespace mutate { +/// ``` +namespace client { // Return the original text of the request document. [[nodiscard("unnecessary call")]] const std::string& GetRequestText() noexcept; @@ -50,6 +48,8 @@ namespace mutate { // Return a pre-parsed, pre-validated request object. [[nodiscard("unnecessary call")]] const peg::ast& GetRequestObject() noexcept; +} // namespace client + enum class [[nodiscard("unnecessary conversion")]] TaskState { Unassigned, @@ -65,7 +65,8 @@ struct [[nodiscard("unnecessary construction")]] CompleteTaskInput response::IdType idArg, std::optional testTaskStateArg, std::optional isCompleteArg, - std::optional clientMutationIdArg) noexcept; + std::optional clientMutationIdArg, + std::optional> boolListArg) noexcept; CompleteTaskInput(const CompleteTaskInput& other); CompleteTaskInput(CompleteTaskInput&& other) noexcept; ~CompleteTaskInput(); @@ -77,21 +78,22 @@ struct [[nodiscard("unnecessary construction")]] CompleteTaskInput std::optional testTaskState; std::optional isComplete; std::optional clientMutationId; + std::optional> boolList; }; -} // namespace mutate +namespace client { namespace mutation::CompleteTaskMutation { -using mutate::GetRequestText; -using mutate::GetRequestObject; +using graphql::mutate::client::GetRequestText; +using graphql::mutate::client::GetRequestObject; // Return the name of this operation in the shared request document. [[nodiscard("unnecessary call")]] const std::string& GetOperationName() noexcept; -using mutate::TaskState; +using graphql::mutate::TaskState; -using mutate::CompleteTaskInput; +using graphql::mutate::CompleteTaskInput; struct [[nodiscard("unnecessary construction")]] Variables { @@ -119,6 +121,37 @@ struct [[nodiscard("unnecessary construction")]] Response completedTask_CompleteTaskPayload completedTask {}; }; +class ResponseVisitor + : public std::enable_shared_from_this +{ +public: + ResponseVisitor() noexcept; + ~ResponseVisitor(); + + void add_value(std::shared_ptr&&); + void reserve(std::size_t count); + void start_object(); + void add_member(std::string&& key); + void end_object(); + void start_array(); + void end_array(); + void add_null(); + void add_string(std::string&& value); + void add_enum(std::string&& value); + void add_id(response::IdType&& value); + void add_bool(bool value); + void add_int(int value); + void add_float(double value); + void complete(); + + Response response(); + +private: + struct impl; + + std::unique_ptr _pimpl; +}; + [[nodiscard("unnecessary conversion")]] Response parseResponse(response::Value&& response); struct Traits @@ -132,11 +165,13 @@ struct Traits [[nodiscard("unnecessary conversion")]] static response::Value serializeVariables(Variables&& variables); using Response = CompleteTaskMutation::Response; + using ResponseVisitor = CompleteTaskMutation::ResponseVisitor; [[nodiscard("unnecessary conversion")]] static Response parseResponse(response::Value&& response); }; } // namespace mutation::CompleteTaskMutation -} // namespace graphql::client +} // namespace client +} // namespace graphql::mutate #endif // MUTATECLIENT_H diff --git a/samples/client/mutate/MutateClient.ixx b/samples/client/mutate/MutateClient.ixx new file mode 100644 index 00000000..6015a4cb --- /dev/null +++ b/samples/client/mutate/MutateClient.ixx @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "MutateClient.h" + +export module GraphQL.Mutate.MutateClient; + +export namespace graphql::mutate { + +namespace client { + +using client::GetRequestText; +using client::GetRequestObject; + +} // namespace client + +using mutate::TaskState; + +using mutate::CompleteTaskInput; + +namespace client { + +namespace mutation::CompleteTaskMutation { + +using graphql::mutate::client::GetRequestText; +using graphql::mutate::client::GetRequestObject; +using CompleteTaskMutation::GetOperationName; + +using graphql::mutate::TaskState; + +using graphql::mutate::CompleteTaskInput; + +using CompleteTaskMutation::Variables; +using CompleteTaskMutation::serializeVariables; + +using CompleteTaskMutation::Response; +using CompleteTaskMutation::ResponseVisitor; +using CompleteTaskMutation::parseResponse; + +using CompleteTaskMutation::Traits; + +} // namespace mutation::CompleteTaskMutation + +} // namespace client +} // namespace graphql::mutate diff --git a/samples/client/nestedinput/CMakeLists.txt b/samples/client/nestedinput/CMakeLists.txt index b9714ed2..470a7c71 100644 --- a/samples/client/nestedinput/CMakeLists.txt +++ b/samples/client/nestedinput/CMakeLists.txt @@ -1,7 +1,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.28) # Normally this would be handled by find_package(cppgraphqlgen CONFIG). include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/cppgraphqlgen-functions.cmake) diff --git a/samples/client/nestedinput/NestedInputClient.cpp b/samples/client/nestedinput/NestedInputClient.cpp index 2832d921..549132ac 100644 --- a/samples/client/nestedinput/NestedInputClient.cpp +++ b/samples/client/nestedinput/NestedInputClient.cpp @@ -9,15 +9,16 @@ #include #include -#include +#include #include #include #include using namespace std::literals; -namespace graphql::client { +namespace graphql { namespace nestedinput { +namespace client { const std::string& GetRequestText() noexcept { @@ -48,6 +49,10 @@ const peg::ast& GetRequestObject() noexcept return s_request; } +} // namespace client + +using namespace graphql::client; + InputA::InputA() noexcept : a {} { @@ -236,6 +241,8 @@ InputBC& InputBC::operator=(InputBC&& other) noexcept } // namespace nestedinput +namespace client { + using namespace nestedinput; template <> @@ -284,9 +291,9 @@ response::Value Variable::serialize(InputBC&& inputValue) } template <> -query::testQuery::Response::control_Control::test_Output Response::parse(response::Value&& response) +graphql::nestedinput::client::query::testQuery::Response::control_Control::test_Output Response::parse(response::Value&& response) { - query::testQuery::Response::control_Control::test_Output result; + graphql::nestedinput::client::query::testQuery::Response::control_Control::test_Output result; if (response.type() == response::Type::Map) { @@ -306,9 +313,9 @@ query::testQuery::Response::control_Control::test_Output Response -query::testQuery::Response::control_Control Response::parse(response::Value&& response) +graphql::nestedinput::client::query::testQuery::Response::control_Control Response::parse(response::Value&& response) { - query::testQuery::Response::control_Control result; + graphql::nestedinput::client::query::testQuery::Response::control_Control result; if (response.type() == response::Type::Map) { @@ -318,7 +325,7 @@ query::testQuery::Response::control_Control Response::parse(std::move(member.second)); + result.test = ModifiedResponse::parse(std::move(member.second)); continue; } } @@ -327,7 +334,9 @@ query::testQuery::Response::control_Control Response::serialize(std::move(variables.stream))); @@ -345,8 +356,292 @@ response::Value serializeVariables(Variables&& variables) return result; } +struct ResponseVisitor::impl +{ + enum class VisitorState + { + Start, + Member_control, + Member_control_test, + Member_control_test_id, + Complete, + }; + + VisitorState state { VisitorState::Start }; + Response response {}; +}; + +ResponseVisitor::ResponseVisitor() noexcept + : _pimpl { std::make_unique() } +{ +} + +ResponseVisitor::~ResponseVisitor() +{ +} + +void ResponseVisitor::add_value([[maybe_unused]] std::shared_ptr&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Member_control: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.control = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_control_test: + _pimpl->state = impl::VisitorState::Member_control; + _pimpl->response.control.test = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_control_test_id: + _pimpl->state = impl::VisitorState::Member_control_test; + _pimpl->response.control.test->id = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::reserve([[maybe_unused]] std::size_t count) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_control_test: + _pimpl->response.control.test = std::make_optional({}); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_member([[maybe_unused]] std::string&& key) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Start: + if (key == "control"sv) + { + _pimpl->state = impl::VisitorState::Member_control; + } + break; + + case impl::VisitorState::Member_control: + if (key == "test"sv) + { + _pimpl->state = impl::VisitorState::Member_control_test; + } + break; + + case impl::VisitorState::Member_control_test: + if (key == "id"sv) + { + _pimpl->state = impl::VisitorState::Member_control_test_id; + } + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_control_test: + _pimpl->state = impl::VisitorState::Member_control; + break; + + case impl::VisitorState::Member_control: + _pimpl->state = impl::VisitorState::Start; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_null() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_control_test: + _pimpl->state = impl::VisitorState::Member_control; + _pimpl->response.control.test = std::nullopt; + break; + + case impl::VisitorState::Member_control_test_id: + _pimpl->state = impl::VisitorState::Member_control_test; + _pimpl->response.control.test->id = std::nullopt; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_string([[maybe_unused]] std::string&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_enum([[maybe_unused]] std::string&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_id([[maybe_unused]] response::IdType&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_bool([[maybe_unused]] bool value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_control_test_id: + _pimpl->state = impl::VisitorState::Member_control_test; + _pimpl->response.control.test->id = value; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_int([[maybe_unused]] int value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_float([[maybe_unused]] double value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::complete() +{ + _pimpl->state = impl::VisitorState::Complete; +} + +Response ResponseVisitor::response() +{ + Response response {}; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + _pimpl->state = impl::VisitorState::Start; + std::swap(_pimpl->response, response); + break; + + default: + break; + } + + return response; +} + Response parseResponse(response::Value&& response) { + using namespace graphql::client; + Response result; if (response.type() == response::Type::Map) @@ -368,12 +663,12 @@ Response parseResponse(response::Value&& response) [[nodiscard("unnecessary call")]] const std::string& Traits::GetRequestText() noexcept { - return nestedinput::GetRequestText(); + return client::GetRequestText(); } [[nodiscard("unnecessary call")]] const peg::ast& Traits::GetRequestObject() noexcept { - return nestedinput::GetRequestObject(); + return client::GetRequestObject(); } [[nodiscard("unnecessary call")]] const std::string& Traits::GetOperationName() noexcept @@ -391,5 +686,5 @@ Response parseResponse(response::Value&& response) return testQuery::parseResponse(std::move(response)); } -} // namespace query::testQuery -} // namespace graphql::client +} // namespace nestedinput::client::query::testQuery +} // namespace graphql diff --git a/samples/client/nestedinput/NestedInputClient.h b/samples/client/nestedinput/NestedInputClient.h index 78eefa17..06fc137d 100644 --- a/samples/client/nestedinput/NestedInputClient.h +++ b/samples/client/nestedinput/NestedInputClient.h @@ -14,20 +14,18 @@ #include "graphqlservice/internal/Version.h" -// Check if the library version is compatible with clientgen 4.5.0 -static_assert(graphql::internal::MajorVersion == 4, "regenerate with clientgen: major version mismatch"); -static_assert(graphql::internal::MinorVersion == 5, "regenerate with clientgen: minor version mismatch"); - #include #include #include -namespace graphql::client { +// Check if the library version is compatible with clientgen 5.0.0 +static_assert(graphql::internal::MajorVersion == 5, "regenerate with clientgen: major version mismatch"); +static_assert(graphql::internal::MinorVersion == 0, "regenerate with clientgen: minor version mismatch"); + +namespace graphql::nestedinput { -/// -/// Operation: query testQuery -/// -/// +/// # Operation: query testQuery +/// ```graphql /// query testQuery($stream: InputABCD!) { /// control { /// test(new: $stream) { @@ -35,8 +33,8 @@ namespace graphql::client { /// } /// } /// } -/// -namespace nestedinput { +/// ``` +namespace client { // Return the original text of the request document. [[nodiscard("unnecessary call")]] const std::string& GetRequestText() noexcept; @@ -44,6 +42,8 @@ namespace nestedinput { // Return a pre-parsed, pre-validated request object. [[nodiscard("unnecessary call")]] const peg::ast& GetRequestObject() noexcept; +} // namespace client + struct [[nodiscard("unnecessary construction")]] InputA { explicit InputA() noexcept; @@ -116,20 +116,20 @@ struct [[nodiscard("unnecessary construction")]] InputBC InputB b; }; -} // namespace nestedinput +namespace client { namespace query::testQuery { -using nestedinput::GetRequestText; -using nestedinput::GetRequestObject; +using graphql::nestedinput::client::GetRequestText; +using graphql::nestedinput::client::GetRequestObject; // Return the name of this operation in the shared request document. [[nodiscard("unnecessary call")]] const std::string& GetOperationName() noexcept; -using nestedinput::InputA; -using nestedinput::InputB; -using nestedinput::InputABCD; -using nestedinput::InputBC; +using graphql::nestedinput::InputA; +using graphql::nestedinput::InputB; +using graphql::nestedinput::InputABCD; +using graphql::nestedinput::InputBC; struct [[nodiscard("unnecessary construction")]] Variables { @@ -153,6 +153,37 @@ struct [[nodiscard("unnecessary construction")]] Response control_Control control {}; }; +class ResponseVisitor + : public std::enable_shared_from_this +{ +public: + ResponseVisitor() noexcept; + ~ResponseVisitor(); + + void add_value(std::shared_ptr&&); + void reserve(std::size_t count); + void start_object(); + void add_member(std::string&& key); + void end_object(); + void start_array(); + void end_array(); + void add_null(); + void add_string(std::string&& value); + void add_enum(std::string&& value); + void add_id(response::IdType&& value); + void add_bool(bool value); + void add_int(int value); + void add_float(double value); + void complete(); + + Response response(); + +private: + struct impl; + + std::unique_ptr _pimpl; +}; + [[nodiscard("unnecessary conversion")]] Response parseResponse(response::Value&& response); struct Traits @@ -166,11 +197,13 @@ struct Traits [[nodiscard("unnecessary conversion")]] static response::Value serializeVariables(Variables&& variables); using Response = testQuery::Response; + using ResponseVisitor = testQuery::ResponseVisitor; [[nodiscard("unnecessary conversion")]] static Response parseResponse(response::Value&& response); }; } // namespace query::testQuery -} // namespace graphql::client +} // namespace client +} // namespace graphql::nestedinput #endif // NESTEDINPUTCLIENT_H diff --git a/samples/client/nestedinput/NestedInputClient.ixx b/samples/client/nestedinput/NestedInputClient.ixx new file mode 100644 index 00000000..44459e49 --- /dev/null +++ b/samples/client/nestedinput/NestedInputClient.ixx @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "NestedInputClient.h" + +export module GraphQL.NestedInput.NestedInputClient; + +export namespace graphql::nestedinput { + +namespace client { + +using client::GetRequestText; +using client::GetRequestObject; + +} // namespace client + +using nestedinput::InputA; +using nestedinput::InputB; +using nestedinput::InputABCD; +using nestedinput::InputBC; + +namespace client { + +namespace query::testQuery { + +using graphql::nestedinput::client::GetRequestText; +using graphql::nestedinput::client::GetRequestObject; +using testQuery::GetOperationName; + +using graphql::nestedinput::InputA; +using graphql::nestedinput::InputB; +using graphql::nestedinput::InputABCD; +using graphql::nestedinput::InputBC; + +using testQuery::Variables; +using testQuery::serializeVariables; + +using testQuery::Response; +using testQuery::ResponseVisitor; +using testQuery::parseResponse; + +using testQuery::Traits; + +} // namespace query::testQuery + +} // namespace client +} // namespace graphql::nestedinput diff --git a/samples/client/query/CMakeLists.txt b/samples/client/query/CMakeLists.txt index dd483b6c..6fbcfcc1 100644 --- a/samples/client/query/CMakeLists.txt +++ b/samples/client/query/CMakeLists.txt @@ -1,7 +1,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.28) # Normally this would be handled by find_package(cppgraphqlgen CONFIG). include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/cppgraphqlgen-functions.cmake) diff --git a/samples/client/query/QueryClient.cpp b/samples/client/query/QueryClient.cpp index a5752587..934d4593 100644 --- a/samples/client/query/QueryClient.cpp +++ b/samples/client/query/QueryClient.cpp @@ -9,15 +9,16 @@ #include #include -#include +#include #include #include #include using namespace std::literals; -namespace graphql::client { +namespace graphql { namespace query { +namespace client { const std::string& GetRequestText() noexcept { @@ -77,6 +78,7 @@ const std::string& GetRequestText() noexcept subject when isNow + array } } @@ -102,10 +104,20 @@ const peg::ast& GetRequestObject() noexcept return s_request; } +} // namespace client } // namespace query +namespace client { + using namespace query; +static const std::array, 4> s_valuesTaskState = { + std::make_pair(R"gql(New)gql"sv, TaskState::New), + std::make_pair(R"gql(Started)gql"sv, TaskState::Started), + std::make_pair(R"gql(Complete)gql"sv, TaskState::Complete), + std::make_pair(R"gql(Unassigned)gql"sv, TaskState::Unassigned) +}; + template <> TaskState Response::parse(response::Value&& value) { @@ -114,15 +126,8 @@ TaskState Response::parse(response::Value&& value) throw std::logic_error { R"ex(not a valid TaskState value)ex" }; } - static const std::array, 4> s_values = { - std::make_pair(R"gql(New)gql"sv, TaskState::New), - std::make_pair(R"gql(Started)gql"sv, TaskState::Started), - std::make_pair(R"gql(Complete)gql"sv, TaskState::Complete), - std::make_pair(R"gql(Unassigned)gql"sv, TaskState::Unassigned) - }; - const auto result = internal::sorted_map_lookup( - s_values, + s_valuesTaskState, std::string_view { value.get() }); if (!result) @@ -134,9 +139,9 @@ TaskState Response::parse(response::Value&& value) } template <> -query::Query::Response::appointments_AppointmentConnection::edges_AppointmentEdge::node_Appointment Response::parse(response::Value&& response) +graphql::query::client::query::Query::Response::appointments_AppointmentConnection::edges_AppointmentEdge::node_Appointment Response::parse(response::Value&& response) { - query::Query::Response::appointments_AppointmentConnection::edges_AppointmentEdge::node_Appointment result; + graphql::query::client::query::Query::Response::appointments_AppointmentConnection::edges_AppointmentEdge::node_Appointment result; if (response.type() == response::Type::Map) { @@ -176,9 +181,9 @@ query::Query::Response::appointments_AppointmentConnection::edges_AppointmentEdg } template <> -query::Query::Response::appointments_AppointmentConnection::edges_AppointmentEdge Response::parse(response::Value&& response) +graphql::query::client::query::Query::Response::appointments_AppointmentConnection::edges_AppointmentEdge Response::parse(response::Value&& response) { - query::Query::Response::appointments_AppointmentConnection::edges_AppointmentEdge result; + graphql::query::client::query::Query::Response::appointments_AppointmentConnection::edges_AppointmentEdge result; if (response.type() == response::Type::Map) { @@ -188,7 +193,7 @@ query::Query::Response::appointments_AppointmentConnection::edges_AppointmentEdg { if (member.first == R"js(node)js"sv) { - result.node = ModifiedResponse::parse(std::move(member.second)); + result.node = ModifiedResponse::parse(std::move(member.second)); continue; } } @@ -198,9 +203,9 @@ query::Query::Response::appointments_AppointmentConnection::edges_AppointmentEdg } template <> -query::Query::Response::appointments_AppointmentConnection Response::parse(response::Value&& response) +graphql::query::client::query::Query::Response::appointments_AppointmentConnection Response::parse(response::Value&& response) { - query::Query::Response::appointments_AppointmentConnection result; + graphql::query::client::query::Query::Response::appointments_AppointmentConnection result; if (response.type() == response::Type::Map) { @@ -210,7 +215,7 @@ query::Query::Response::appointments_AppointmentConnection Response::parse(std::move(member.second)); + result.edges = ModifiedResponse::parse(std::move(member.second)); continue; } } @@ -220,9 +225,9 @@ query::Query::Response::appointments_AppointmentConnection Response -query::Query::Response::tasks_TaskConnection::edges_TaskEdge::node_Task Response::parse(response::Value&& response) +graphql::query::client::query::Query::Response::tasks_TaskConnection::edges_TaskEdge::node_Task Response::parse(response::Value&& response) { - query::Query::Response::tasks_TaskConnection::edges_TaskEdge::node_Task result; + graphql::query::client::query::Query::Response::tasks_TaskConnection::edges_TaskEdge::node_Task result; if (response.type() == response::Type::Map) { @@ -257,9 +262,9 @@ query::Query::Response::tasks_TaskConnection::edges_TaskEdge::node_Task Response } template <> -query::Query::Response::tasks_TaskConnection::edges_TaskEdge Response::parse(response::Value&& response) +graphql::query::client::query::Query::Response::tasks_TaskConnection::edges_TaskEdge Response::parse(response::Value&& response) { - query::Query::Response::tasks_TaskConnection::edges_TaskEdge result; + graphql::query::client::query::Query::Response::tasks_TaskConnection::edges_TaskEdge result; if (response.type() == response::Type::Map) { @@ -269,7 +274,7 @@ query::Query::Response::tasks_TaskConnection::edges_TaskEdge Response::parse(std::move(member.second)); + result.node = ModifiedResponse::parse(std::move(member.second)); continue; } } @@ -279,9 +284,9 @@ query::Query::Response::tasks_TaskConnection::edges_TaskEdge Response -query::Query::Response::tasks_TaskConnection Response::parse(response::Value&& response) +graphql::query::client::query::Query::Response::tasks_TaskConnection Response::parse(response::Value&& response) { - query::Query::Response::tasks_TaskConnection result; + graphql::query::client::query::Query::Response::tasks_TaskConnection result; if (response.type() == response::Type::Map) { @@ -291,7 +296,7 @@ query::Query::Response::tasks_TaskConnection Response::parse(std::move(member.second)); + result.edges = ModifiedResponse::parse(std::move(member.second)); continue; } } @@ -301,9 +306,9 @@ query::Query::Response::tasks_TaskConnection Response -query::Query::Response::unreadCounts_FolderConnection::edges_FolderEdge::node_Folder Response::parse(response::Value&& response) +graphql::query::client::query::Query::Response::unreadCounts_FolderConnection::edges_FolderEdge::node_Folder Response::parse(response::Value&& response) { - query::Query::Response::unreadCounts_FolderConnection::edges_FolderEdge::node_Folder result; + graphql::query::client::query::Query::Response::unreadCounts_FolderConnection::edges_FolderEdge::node_Folder result; if (response.type() == response::Type::Map) { @@ -338,9 +343,9 @@ query::Query::Response::unreadCounts_FolderConnection::edges_FolderEdge::node_Fo } template <> -query::Query::Response::unreadCounts_FolderConnection::edges_FolderEdge Response::parse(response::Value&& response) +graphql::query::client::query::Query::Response::unreadCounts_FolderConnection::edges_FolderEdge Response::parse(response::Value&& response) { - query::Query::Response::unreadCounts_FolderConnection::edges_FolderEdge result; + graphql::query::client::query::Query::Response::unreadCounts_FolderConnection::edges_FolderEdge result; if (response.type() == response::Type::Map) { @@ -350,7 +355,7 @@ query::Query::Response::unreadCounts_FolderConnection::edges_FolderEdge Response { if (member.first == R"js(node)js"sv) { - result.node = ModifiedResponse::parse(std::move(member.second)); + result.node = ModifiedResponse::parse(std::move(member.second)); continue; } } @@ -360,9 +365,9 @@ query::Query::Response::unreadCounts_FolderConnection::edges_FolderEdge Response } template <> -query::Query::Response::unreadCounts_FolderConnection Response::parse(response::Value&& response) +graphql::query::client::query::Query::Response::unreadCounts_FolderConnection Response::parse(response::Value&& response) { - query::Query::Response::unreadCounts_FolderConnection result; + graphql::query::client::query::Query::Response::unreadCounts_FolderConnection result; if (response.type() == response::Type::Map) { @@ -372,7 +377,7 @@ query::Query::Response::unreadCounts_FolderConnection Response::parse(std::move(member.second)); + result.edges = ModifiedResponse::parse(std::move(member.second)); continue; } } @@ -382,9 +387,9 @@ query::Query::Response::unreadCounts_FolderConnection Response -query::Query::Response::anyType_UnionType Response::parse(response::Value&& response) +graphql::query::client::query::Query::Response::anyType_UnionType Response::parse(response::Value&& response) { - query::Query::Response::anyType_UnionType result; + graphql::query::client::query::Query::Response::anyType_UnionType result; if (response.type() == response::Type::Map) { @@ -427,13 +432,20 @@ query::Query::Response::anyType_UnionType Response::parse(std::move(member.second)); continue; } + if (member.first == R"js(array)js"sv) + { + result.array = ModifiedResponse::parse(std::move(member.second)); + continue; + } } } return result; } -namespace query::Query { +} // namespace client + +namespace query::client::query::Query { const std::string& GetOperationName() noexcept { @@ -442,8 +454,907 @@ const std::string& GetOperationName() noexcept return s_name; } +struct ResponseVisitor::impl +{ + enum class VisitorState + { + Start, + Member_appointments, + Member_appointments_edges, + Member_appointments_edges_0, + Member_appointments_edges_0_, + Member_appointments_edges_0_node, + Member_appointments_edges_0_node_id, + Member_appointments_edges_0_node_subject, + Member_appointments_edges_0_node_when, + Member_appointments_edges_0_node_isNow, + Member_appointments_edges_0_node__typename, + Member_tasks, + Member_tasks_edges, + Member_tasks_edges_0, + Member_tasks_edges_0_, + Member_tasks_edges_0_node, + Member_tasks_edges_0_node_id, + Member_tasks_edges_0_node_title, + Member_tasks_edges_0_node_isComplete, + Member_tasks_edges_0_node__typename, + Member_unreadCounts, + Member_unreadCounts_edges, + Member_unreadCounts_edges_0, + Member_unreadCounts_edges_0_, + Member_unreadCounts_edges_0_node, + Member_unreadCounts_edges_0_node_id, + Member_unreadCounts_edges_0_node_name, + Member_unreadCounts_edges_0_node_unreadCount, + Member_unreadCounts_edges_0_node__typename, + Member_testTaskState, + Member_anyType, + Member_anyType_0, + Member_anyType_0_, + Member_anyType_0__typename, + Member_anyType_0_id, + Member_anyType_0_title, + Member_anyType_0_isComplete, + Member_anyType_0_subject, + Member_anyType_0_when, + Member_anyType_0_isNow, + Member_anyType_0_array, + Member_anyType_0_array_0, + Member_anyType_0_array_0_, + Member_default_, + Complete, + }; + + VisitorState state { VisitorState::Start }; + Response response {}; +}; + +ResponseVisitor::ResponseVisitor() noexcept + : _pimpl { std::make_unique() } +{ +} + +ResponseVisitor::~ResponseVisitor() +{ +} + +void ResponseVisitor::add_value([[maybe_unused]] std::shared_ptr&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.appointments = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0: + _pimpl->response.appointments.edges->push_back(ModifiedResponse::parse(response::Value { *value })); + break; + + case impl::VisitorState::Member_appointments_edges_0_node: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_; + _pimpl->response.appointments.edges->back()->node = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0_node_id: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->id = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0_node_subject: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->subject = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0_node_when: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->when = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0_node_isNow: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->isNow = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0_node__typename: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->_typename = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_tasks: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.tasks = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_tasks_edges_0: + _pimpl->response.tasks.edges->push_back(ModifiedResponse::parse(response::Value { *value })); + break; + + case impl::VisitorState::Member_tasks_edges_0_node: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_; + _pimpl->response.tasks.edges->back()->node = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_tasks_edges_0_node_id: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->id = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_tasks_edges_0_node_title: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->title = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_tasks_edges_0_node_isComplete: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->isComplete = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_tasks_edges_0_node__typename: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->_typename = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_unreadCounts: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.unreadCounts = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0: + _pimpl->response.unreadCounts.edges->push_back(ModifiedResponse::parse(response::Value { *value })); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_; + _pimpl->response.unreadCounts.edges->back()->node = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node_id: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->id = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node_name: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->name = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node_unreadCount: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->unreadCount = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node__typename: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->_typename = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_testTaskState: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.testTaskState = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_anyType_0: + _pimpl->response.anyType.push_back(ModifiedResponse::parse(response::Value { *value })); + break; + + case impl::VisitorState::Member_anyType_0__typename: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->_typename = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_anyType_0_id: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->id = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_anyType_0_title: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->title = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_anyType_0_isComplete: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->isComplete = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_anyType_0_subject: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->subject = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_anyType_0_when: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->when = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_anyType_0_isNow: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->isNow = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_anyType_0_array_0: + _pimpl->response.anyType.back()->array.push_back(ModifiedResponse::parse(response::Value { *value })); + break; + + case impl::VisitorState::Member_default_: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.default_ = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::reserve([[maybe_unused]] std::size_t count) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0: + _pimpl->response.appointments.edges->reserve(count); + break; + + case impl::VisitorState::Member_tasks_edges_0: + _pimpl->response.tasks.edges->reserve(count); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0: + _pimpl->response.unreadCounts.edges->reserve(count); + break; + + case impl::VisitorState::Member_anyType_0: + _pimpl->response.anyType.reserve(count); + break; + + case impl::VisitorState::Member_anyType_0_array_0: + _pimpl->response.anyType.back()->array.reserve(count); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_; + _pimpl->response.appointments.edges->push_back(std::make_optional({})); + break; + + case impl::VisitorState::Member_appointments_edges_0_node: + _pimpl->response.appointments.edges->back()->node = std::make_optional({}); + break; + + case impl::VisitorState::Member_tasks_edges_0: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_; + _pimpl->response.tasks.edges->push_back(std::make_optional({})); + break; + + case impl::VisitorState::Member_tasks_edges_0_node: + _pimpl->response.tasks.edges->back()->node = std::make_optional({}); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_; + _pimpl->response.unreadCounts.edges->push_back(std::make_optional({})); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node: + _pimpl->response.unreadCounts.edges->back()->node = std::make_optional({}); + break; + + case impl::VisitorState::Member_anyType_0: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.push_back(std::make_optional({})); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_member([[maybe_unused]] std::string&& key) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Start: + if (key == "appointments"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments; + } + else if (key == "tasks"sv) + { + _pimpl->state = impl::VisitorState::Member_tasks; + } + else if (key == "unreadCounts"sv) + { + _pimpl->state = impl::VisitorState::Member_unreadCounts; + } + else if (key == "testTaskState"sv) + { + _pimpl->state = impl::VisitorState::Member_testTaskState; + } + else if (key == "anyType"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType; + } + else if (key == "default"sv) + { + _pimpl->state = impl::VisitorState::Member_default_; + } + break; + + case impl::VisitorState::Member_appointments: + if (key == "edges"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges; + } + break; + + case impl::VisitorState::Member_appointments_edges_0_: + if (key == "node"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + } + break; + + case impl::VisitorState::Member_appointments_edges_0_node: + if (key == "id"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node_id; + } + else if (key == "subject"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node_subject; + } + else if (key == "when"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node_when; + } + else if (key == "isNow"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node_isNow; + } + else if (key == "__typename"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node__typename; + } + break; + + case impl::VisitorState::Member_tasks: + if (key == "edges"sv) + { + _pimpl->state = impl::VisitorState::Member_tasks_edges; + } + break; + + case impl::VisitorState::Member_tasks_edges_0_: + if (key == "node"sv) + { + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + } + break; + + case impl::VisitorState::Member_tasks_edges_0_node: + if (key == "id"sv) + { + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node_id; + } + else if (key == "title"sv) + { + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node_title; + } + else if (key == "isComplete"sv) + { + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node_isComplete; + } + else if (key == "__typename"sv) + { + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node__typename; + } + break; + + case impl::VisitorState::Member_unreadCounts: + if (key == "edges"sv) + { + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges; + } + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_: + if (key == "node"sv) + { + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + } + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node: + if (key == "id"sv) + { + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node_id; + } + else if (key == "name"sv) + { + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node_name; + } + else if (key == "unreadCount"sv) + { + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node_unreadCount; + } + else if (key == "__typename"sv) + { + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node__typename; + } + break; + + case impl::VisitorState::Member_anyType_0_: + if (key == "__typename"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType_0__typename; + } + else if (key == "id"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType_0_id; + } + else if (key == "title"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType_0_title; + } + else if (key == "isComplete"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType_0_isComplete; + } + else if (key == "subject"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType_0_subject; + } + else if (key == "when"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType_0_when; + } + else if (key == "isNow"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType_0_isNow; + } + else if (key == "array"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType_0_array; + } + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0_node: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_; + break; + + case impl::VisitorState::Member_appointments_edges_0_: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0; + break; + + case impl::VisitorState::Member_appointments: + _pimpl->state = impl::VisitorState::Start; + break; + + case impl::VisitorState::Member_tasks_edges_0_node: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_; + break; + + case impl::VisitorState::Member_tasks_edges_0_: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0; + break; + + case impl::VisitorState::Member_tasks: + _pimpl->state = impl::VisitorState::Start; + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_; + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0; + break; + + case impl::VisitorState::Member_unreadCounts: + _pimpl->state = impl::VisitorState::Start; + break; + + case impl::VisitorState::Member_anyType_0_: + _pimpl->state = impl::VisitorState::Member_anyType_0; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0; + _pimpl->response.appointments.edges = std::make_optional>>({}); + break; + + case impl::VisitorState::Member_tasks_edges: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0; + _pimpl->response.tasks.edges = std::make_optional>>({}); + break; + + case impl::VisitorState::Member_unreadCounts_edges: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0; + _pimpl->response.unreadCounts.edges = std::make_optional>>({}); + break; + + case impl::VisitorState::Member_anyType: + _pimpl->state = impl::VisitorState::Member_anyType_0; + break; + + case impl::VisitorState::Member_anyType_0_array: + _pimpl->state = impl::VisitorState::Member_anyType_0_array_0; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0: + _pimpl->state = impl::VisitorState::Member_appointments; + break; + + case impl::VisitorState::Member_tasks_edges_0: + _pimpl->state = impl::VisitorState::Member_tasks; + break; + + case impl::VisitorState::Member_unreadCounts_edges_0: + _pimpl->state = impl::VisitorState::Member_unreadCounts; + break; + + case impl::VisitorState::Member_anyType_0: + _pimpl->state = impl::VisitorState::Start; + break; + + case impl::VisitorState::Member_anyType_0_array_0: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_null() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0: + _pimpl->response.appointments.edges->push_back(std::nullopt); + break; + + case impl::VisitorState::Member_appointments_edges_0_node: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_; + _pimpl->response.appointments.edges->back()->node = std::nullopt; + break; + + case impl::VisitorState::Member_appointments_edges_0_node_subject: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->subject = std::nullopt; + break; + + case impl::VisitorState::Member_appointments_edges_0_node_when: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->when = std::nullopt; + break; + + case impl::VisitorState::Member_tasks_edges_0: + _pimpl->response.tasks.edges->push_back(std::nullopt); + break; + + case impl::VisitorState::Member_tasks_edges_0_node: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_; + _pimpl->response.tasks.edges->back()->node = std::nullopt; + break; + + case impl::VisitorState::Member_tasks_edges_0_node_title: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->title = std::nullopt; + break; + + case impl::VisitorState::Member_unreadCounts_edges_0: + _pimpl->response.unreadCounts.edges->push_back(std::nullopt); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_; + _pimpl->response.unreadCounts.edges->back()->node = std::nullopt; + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node_name: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->name = std::nullopt; + break; + + case impl::VisitorState::Member_anyType_0: + _pimpl->response.anyType.push_back(std::nullopt); + break; + + case impl::VisitorState::Member_anyType_0_title: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->title = std::nullopt; + break; + + case impl::VisitorState::Member_anyType_0_subject: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->subject = std::nullopt; + break; + + case impl::VisitorState::Member_anyType_0_when: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->when = std::nullopt; + break; + + case impl::VisitorState::Member_default_: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.default_ = std::nullopt; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_string([[maybe_unused]] std::string&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0_node_subject: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->subject = std::move(value); + break; + + case impl::VisitorState::Member_appointments_edges_0_node__typename: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->_typename = std::move(value); + break; + + case impl::VisitorState::Member_tasks_edges_0_node_title: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->title = std::move(value); + break; + + case impl::VisitorState::Member_tasks_edges_0_node__typename: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->_typename = std::move(value); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node_name: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->name = std::move(value); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node__typename: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->_typename = std::move(value); + break; + + case impl::VisitorState::Member_anyType_0__typename: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->_typename = std::move(value); + break; + + case impl::VisitorState::Member_anyType_0_title: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->title = std::move(value); + break; + + case impl::VisitorState::Member_anyType_0_subject: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->subject = std::move(value); + break; + + case impl::VisitorState::Member_default_: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.default_ = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_enum([[maybe_unused]] std::string&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Member_testTaskState: + _pimpl->state = impl::VisitorState::Start; + if (const auto enumValue = internal::sorted_map_lookup(s_valuesTaskState, std::string_view { value })) + { + _pimpl->response.testTaskState = *enumValue; + } + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_id([[maybe_unused]] response::IdType&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0_node_id: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->id = std::move(value); + break; + + case impl::VisitorState::Member_tasks_edges_0_node_id: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->id = std::move(value); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node_id: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->id = std::move(value); + break; + + case impl::VisitorState::Member_anyType_0_id: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->id = std::move(value); + break; + + case impl::VisitorState::Member_anyType_0_array_0: + _pimpl->response.anyType.back()->array.push_back(std::move(value)); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_bool([[maybe_unused]] bool value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0_node_isNow: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->isNow = value; + break; + + case impl::VisitorState::Member_tasks_edges_0_node_isComplete: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->isComplete = value; + break; + + case impl::VisitorState::Member_anyType_0_isComplete: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->isComplete = value; + break; + + case impl::VisitorState::Member_anyType_0_isNow: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->isNow = value; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_int([[maybe_unused]] int value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_unreadCounts_edges_0_node_unreadCount: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->unreadCount = value; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_float([[maybe_unused]] double value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::complete() +{ + _pimpl->state = impl::VisitorState::Complete; +} + +Response ResponseVisitor::response() +{ + Response response {}; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + _pimpl->state = impl::VisitorState::Start; + std::swap(_pimpl->response, response); + break; + + default: + break; + } + + return response; +} + Response parseResponse(response::Value&& response) { + using namespace graphql::client; + Response result; if (response.type() == response::Type::Map) @@ -490,12 +1401,12 @@ Response parseResponse(response::Value&& response) [[nodiscard("unnecessary call")]] const std::string& Traits::GetRequestText() noexcept { - return query::GetRequestText(); + return client::GetRequestText(); } [[nodiscard("unnecessary call")]] const peg::ast& Traits::GetRequestObject() noexcept { - return query::GetRequestObject(); + return client::GetRequestObject(); } [[nodiscard("unnecessary call")]] const std::string& Traits::GetOperationName() noexcept @@ -508,5 +1419,5 @@ Response parseResponse(response::Value&& response) return Query::parseResponse(std::move(response)); } -} // namespace query::Query -} // namespace graphql::client +} // namespace query::client::query::Query +} // namespace graphql diff --git a/samples/client/query/QueryClient.h b/samples/client/query/QueryClient.h index fb89bcdd..36f7798e 100644 --- a/samples/client/query/QueryClient.h +++ b/samples/client/query/QueryClient.h @@ -14,20 +14,18 @@ #include "graphqlservice/internal/Version.h" -// Check if the library version is compatible with clientgen 4.5.0 -static_assert(graphql::internal::MajorVersion == 4, "regenerate with clientgen: major version mismatch"); -static_assert(graphql::internal::MinorVersion == 5, "regenerate with clientgen: minor version mismatch"); - #include #include #include -namespace graphql::client { +// Check if the library version is compatible with clientgen 5.0.0 +static_assert(graphql::internal::MajorVersion == 5, "regenerate with clientgen: major version mismatch"); +static_assert(graphql::internal::MinorVersion == 0, "regenerate with clientgen: minor version mismatch"); + +namespace graphql::query { -/// -/// Operation: query (unnamed) -/// -/// +/// # Operation: query (unnamed) +/// ```graphql /// # Copyright (c) Microsoft Corporation. All rights reserved. /// # Licensed under the MIT License. /// @@ -83,14 +81,15 @@ namespace graphql::client { /// subject /// when /// isNow +/// array /// } /// } /// /// # Try a field with a C++ keyword /// default /// } -/// -namespace query { +/// ``` +namespace client { // Return the original text of the request document. [[nodiscard("unnecessary call")]] const std::string& GetRequestText() noexcept; @@ -98,6 +97,8 @@ namespace query { // Return a pre-parsed, pre-validated request object. [[nodiscard("unnecessary call")]] const peg::ast& GetRequestObject() noexcept; +} // namespace client + enum class [[nodiscard("unnecessary conversion")]] TaskState { Unassigned, @@ -106,17 +107,17 @@ enum class [[nodiscard("unnecessary conversion")]] TaskState Complete, }; -} // namespace query +namespace client { namespace query::Query { -using query::GetRequestText; -using query::GetRequestObject; +using graphql::query::client::GetRequestText; +using graphql::query::client::GetRequestObject; // Return the name of this operation in the shared request document. [[nodiscard("unnecessary call")]] const std::string& GetOperationName() noexcept; -using query::TaskState; +using graphql::query::TaskState; struct [[nodiscard("unnecessary construction")]] Response { @@ -184,6 +185,7 @@ struct [[nodiscard("unnecessary construction")]] Response std::optional subject {}; std::optional when {}; bool isNow {}; + std::vector array {}; }; appointments_AppointmentConnection appointments {}; @@ -194,6 +196,37 @@ struct [[nodiscard("unnecessary construction")]] Response std::optional default_ {}; }; +class ResponseVisitor + : public std::enable_shared_from_this +{ +public: + ResponseVisitor() noexcept; + ~ResponseVisitor(); + + void add_value(std::shared_ptr&&); + void reserve(std::size_t count); + void start_object(); + void add_member(std::string&& key); + void end_object(); + void start_array(); + void end_array(); + void add_null(); + void add_string(std::string&& value); + void add_enum(std::string&& value); + void add_id(response::IdType&& value); + void add_bool(bool value); + void add_int(int value); + void add_float(double value); + void complete(); + + Response response(); + +private: + struct impl; + + std::unique_ptr _pimpl; +}; + [[nodiscard("unnecessary conversion")]] Response parseResponse(response::Value&& response); struct Traits @@ -203,11 +236,13 @@ struct Traits [[nodiscard("unnecessary call")]] static const std::string& GetOperationName() noexcept; using Response = Query::Response; + using ResponseVisitor = Query::ResponseVisitor; [[nodiscard("unnecessary conversion")]] static Response parseResponse(response::Value&& response); }; } // namespace query::Query -} // namespace graphql::client +} // namespace client +} // namespace graphql::query #endif // QUERYCLIENT_H diff --git a/samples/client/query/QueryClient.ixx b/samples/client/query/QueryClient.ixx new file mode 100644 index 00000000..f069b326 --- /dev/null +++ b/samples/client/query/QueryClient.ixx @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "QueryClient.h" + +export module GraphQL.Query.QueryClient; + +export namespace graphql::query { + +namespace client { + +using client::GetRequestText; +using client::GetRequestObject; + +} // namespace client + +using query::TaskState; + +namespace client { + +namespace query::Query { + +using graphql::query::client::GetRequestText; +using graphql::query::client::GetRequestObject; +using Query::GetOperationName; + +using graphql::query::TaskState; + +using Query::Response; +using Query::ResponseVisitor; +using Query::parseResponse; + +using Query::Traits; + +} // namespace query::Query + +} // namespace client +} // namespace graphql::query diff --git a/samples/client/query/query.today.graphql b/samples/client/query/query.today.graphql index 56bef0a3..32217e9c 100644 --- a/samples/client/query/query.today.graphql +++ b/samples/client/query/query.today.graphql @@ -53,6 +53,7 @@ query { subject when isNow + array } } diff --git a/samples/client/subscribe/CMakeLists.txt b/samples/client/subscribe/CMakeLists.txt index fb3ea5eb..6d937548 100644 --- a/samples/client/subscribe/CMakeLists.txt +++ b/samples/client/subscribe/CMakeLists.txt @@ -1,7 +1,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.28) # Normally this would be handled by find_package(cppgraphqlgen CONFIG). include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/cppgraphqlgen-functions.cmake) diff --git a/samples/client/subscribe/SubscribeClient.cpp b/samples/client/subscribe/SubscribeClient.cpp index 6cbcbe52..8ea64274 100644 --- a/samples/client/subscribe/SubscribeClient.cpp +++ b/samples/client/subscribe/SubscribeClient.cpp @@ -9,15 +9,16 @@ #include #include -#include +#include #include #include #include using namespace std::literals; -namespace graphql::client { +namespace graphql { namespace subscribe { +namespace client { const std::string& GetRequestText() noexcept { @@ -52,14 +53,16 @@ const peg::ast& GetRequestObject() noexcept return s_request; } +} // namespace client } // namespace subscribe +namespace client { using namespace subscribe; template <> -subscription::TestSubscription::Response::nextAppointment_Appointment Response::parse(response::Value&& response) +graphql::subscribe::client::subscription::TestSubscription::Response::nextAppointment_Appointment Response::parse(response::Value&& response) { - subscription::TestSubscription::Response::nextAppointment_Appointment result; + graphql::subscribe::client::subscription::TestSubscription::Response::nextAppointment_Appointment result; if (response.type() == response::Type::Map) { @@ -93,7 +96,9 @@ subscription::TestSubscription::Response::nextAppointment_Appointment Response() } +{ +} + +ResponseVisitor::~ResponseVisitor() +{ +} + +void ResponseVisitor::add_value([[maybe_unused]] std::shared_ptr&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Member_nextAppointment: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.nextAppointment = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_nextAppointment_nextAppointmentId: + _pimpl->state = impl::VisitorState::Member_nextAppointment; + _pimpl->response.nextAppointment->nextAppointmentId = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_nextAppointment_when: + _pimpl->state = impl::VisitorState::Member_nextAppointment; + _pimpl->response.nextAppointment->when = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_nextAppointment_subject: + _pimpl->state = impl::VisitorState::Member_nextAppointment; + _pimpl->response.nextAppointment->subject = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_nextAppointment_isNow: + _pimpl->state = impl::VisitorState::Member_nextAppointment; + _pimpl->response.nextAppointment->isNow = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::reserve([[maybe_unused]] std::size_t count) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_nextAppointment: + _pimpl->response.nextAppointment = std::make_optional({}); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_member([[maybe_unused]] std::string&& key) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Start: + if (key == "nextAppointment"sv) + { + _pimpl->state = impl::VisitorState::Member_nextAppointment; + } + break; + + case impl::VisitorState::Member_nextAppointment: + if (key == "nextAppointmentId"sv) + { + _pimpl->state = impl::VisitorState::Member_nextAppointment_nextAppointmentId; + } + else if (key == "when"sv) + { + _pimpl->state = impl::VisitorState::Member_nextAppointment_when; + } + else if (key == "subject"sv) + { + _pimpl->state = impl::VisitorState::Member_nextAppointment_subject; + } + else if (key == "isNow"sv) + { + _pimpl->state = impl::VisitorState::Member_nextAppointment_isNow; + } + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_nextAppointment: + _pimpl->state = impl::VisitorState::Start; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_null() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_nextAppointment: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.nextAppointment = std::nullopt; + break; + + case impl::VisitorState::Member_nextAppointment_when: + _pimpl->state = impl::VisitorState::Member_nextAppointment; + _pimpl->response.nextAppointment->when = std::nullopt; + break; + + case impl::VisitorState::Member_nextAppointment_subject: + _pimpl->state = impl::VisitorState::Member_nextAppointment; + _pimpl->response.nextAppointment->subject = std::nullopt; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_string([[maybe_unused]] std::string&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_nextAppointment_subject: + _pimpl->state = impl::VisitorState::Member_nextAppointment; + _pimpl->response.nextAppointment->subject = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_enum([[maybe_unused]] std::string&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_id([[maybe_unused]] response::IdType&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_nextAppointment_nextAppointmentId: + _pimpl->state = impl::VisitorState::Member_nextAppointment; + _pimpl->response.nextAppointment->nextAppointmentId = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_bool([[maybe_unused]] bool value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_nextAppointment_isNow: + _pimpl->state = impl::VisitorState::Member_nextAppointment; + _pimpl->response.nextAppointment->isNow = value; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_int([[maybe_unused]] int value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_float([[maybe_unused]] double value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::complete() +{ + _pimpl->state = impl::VisitorState::Complete; +} + +Response ResponseVisitor::response() +{ + Response response {}; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + _pimpl->state = impl::VisitorState::Start; + std::swap(_pimpl->response, response); + break; + + default: + break; + } + + return response; +} + Response parseResponse(response::Value&& response) { + using namespace graphql::client; + Response result; if (response.type() == response::Type::Map) @@ -125,12 +442,12 @@ Response parseResponse(response::Value&& response) [[nodiscard("unnecessary call")]] const std::string& Traits::GetRequestText() noexcept { - return subscribe::GetRequestText(); + return client::GetRequestText(); } [[nodiscard("unnecessary call")]] const peg::ast& Traits::GetRequestObject() noexcept { - return subscribe::GetRequestObject(); + return client::GetRequestObject(); } [[nodiscard("unnecessary call")]] const std::string& Traits::GetOperationName() noexcept @@ -143,5 +460,5 @@ Response parseResponse(response::Value&& response) return TestSubscription::parseResponse(std::move(response)); } -} // namespace subscription::TestSubscription -} // namespace graphql::client +} // namespace subscribe::client::subscription::TestSubscription +} // namespace graphql diff --git a/samples/client/subscribe/SubscribeClient.h b/samples/client/subscribe/SubscribeClient.h index 5d69e191..de494e84 100644 --- a/samples/client/subscribe/SubscribeClient.h +++ b/samples/client/subscribe/SubscribeClient.h @@ -14,20 +14,18 @@ #include "graphqlservice/internal/Version.h" -// Check if the library version is compatible with clientgen 4.5.0 -static_assert(graphql::internal::MajorVersion == 4, "regenerate with clientgen: major version mismatch"); -static_assert(graphql::internal::MinorVersion == 5, "regenerate with clientgen: minor version mismatch"); - #include #include #include -namespace graphql::client { +// Check if the library version is compatible with clientgen 5.0.0 +static_assert(graphql::internal::MajorVersion == 5, "regenerate with clientgen: major version mismatch"); +static_assert(graphql::internal::MinorVersion == 0, "regenerate with clientgen: minor version mismatch"); + +namespace graphql::subscribe { -/// -/// Operation: subscription TestSubscription -/// -/// +/// # Operation: subscription TestSubscription +/// ```graphql /// # Copyright (c) Microsoft Corporation. All rights reserved. /// # Licensed under the MIT License. /// @@ -39,8 +37,8 @@ namespace graphql::client { /// isNow /// } /// } -/// -namespace subscribe { +/// ``` +namespace client { // Return the original text of the request document. [[nodiscard("unnecessary call")]] const std::string& GetRequestText() noexcept; @@ -48,12 +46,10 @@ namespace subscribe { // Return a pre-parsed, pre-validated request object. [[nodiscard("unnecessary call")]] const peg::ast& GetRequestObject() noexcept; -} // namespace subscribe - namespace subscription::TestSubscription { -using subscribe::GetRequestText; -using subscribe::GetRequestObject; +using graphql::subscribe::client::GetRequestText; +using graphql::subscribe::client::GetRequestObject; // Return the name of this operation in the shared request document. [[nodiscard("unnecessary call")]] const std::string& GetOperationName() noexcept; @@ -71,6 +67,37 @@ struct [[nodiscard("unnecessary construction")]] Response std::optional nextAppointment {}; }; +class ResponseVisitor + : public std::enable_shared_from_this +{ +public: + ResponseVisitor() noexcept; + ~ResponseVisitor(); + + void add_value(std::shared_ptr&&); + void reserve(std::size_t count); + void start_object(); + void add_member(std::string&& key); + void end_object(); + void start_array(); + void end_array(); + void add_null(); + void add_string(std::string&& value); + void add_enum(std::string&& value); + void add_id(response::IdType&& value); + void add_bool(bool value); + void add_int(int value); + void add_float(double value); + void complete(); + + Response response(); + +private: + struct impl; + + std::unique_ptr _pimpl; +}; + [[nodiscard("unnecessary conversion")]] Response parseResponse(response::Value&& response); struct Traits @@ -80,11 +107,13 @@ struct Traits [[nodiscard("unnecessary call")]] static const std::string& GetOperationName() noexcept; using Response = TestSubscription::Response; + using ResponseVisitor = TestSubscription::ResponseVisitor; [[nodiscard("unnecessary conversion")]] static Response parseResponse(response::Value&& response); }; } // namespace subscription::TestSubscription -} // namespace graphql::client +} // namespace client +} // namespace graphql::subscribe #endif // SUBSCRIBECLIENT_H diff --git a/samples/client/subscribe/SubscribeClient.ixx b/samples/client/subscribe/SubscribeClient.ixx new file mode 100644 index 00000000..36dfdbca --- /dev/null +++ b/samples/client/subscribe/SubscribeClient.ixx @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "SubscribeClient.h" + +export module GraphQL.Subscribe.SubscribeClient; + +export namespace graphql::subscribe { + +namespace client { + +using client::GetRequestText; +using client::GetRequestObject; + +namespace subscription::TestSubscription { + +using graphql::subscribe::client::GetRequestText; +using graphql::subscribe::client::GetRequestObject; +using TestSubscription::GetOperationName; + +using TestSubscription::Response; +using TestSubscription::ResponseVisitor; +using TestSubscription::parseResponse; + +using TestSubscription::Traits; + +} // namespace subscription::TestSubscription + +} // namespace client +} // namespace graphql::subscribe diff --git a/samples/learn/CMakeLists.txt b/samples/learn/CMakeLists.txt index 3be7bcf0..9f718c47 100644 --- a/samples/learn/CMakeLists.txt +++ b/samples/learn/CMakeLists.txt @@ -1,7 +1,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.28) add_subdirectory(schema) add_library(star_wars STATIC @@ -11,7 +11,8 @@ add_library(star_wars STATIC QueryData.cpp ReviewData.cpp MutationData.cpp - StarWarsData.cpp) + StarWarsData.cpp + SubscriptionData.cpp) target_link_libraries(star_wars PUBLIC learn_schema) target_include_directories(star_wars INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/samples/learn/DroidData.cpp b/samples/learn/DroidData.cpp index ee3d9548..2f40ed22 100644 --- a/samples/learn/DroidData.cpp +++ b/samples/learn/DroidData.cpp @@ -21,18 +21,15 @@ void Droid::addFriends( { friends_.resize(friends.size()); - std::transform(friends.begin(), - friends.end(), - friends_.begin(), - [](const auto& spFriend) noexcept { - return std::visit( - [](const auto& hero) noexcept { - return WeakHero { - std::weak_ptr::element_type> { hero } - }; - }, - spFriend); - }); + std::ranges::transform(friends, friends_.begin(), [](const auto& spFriend) noexcept { + return std::visit( + [](const auto& hero) noexcept { + return WeakHero { + std::weak_ptr::element_type> { hero } + }; + }, + spFriend); + }); } const response::IdType& Droid::getId() const noexcept @@ -49,12 +46,9 @@ std::optional>> Droid::getFriends { std::vector> result(friends_.size()); - std::transform(friends_.begin(), - friends_.end(), - result.begin(), - [](const auto& wpFriend) noexcept { - return make_hero(wpFriend); - }); + std::ranges::transform(friends_, result.begin(), [](const auto& wpFriend) noexcept { + return make_hero(wpFriend); + }); result.erase(std::remove(result.begin(), result.end(), std::shared_ptr {}), result.end()); @@ -65,12 +59,9 @@ std::optional>> Droid::getAppearsIn() const n { std::vector> result(appearsIn_.size()); - std::transform(appearsIn_.begin(), - appearsIn_.end(), - result.begin(), - [](const auto& entry) noexcept { - return std::make_optional(entry); - }); + std::ranges::transform(appearsIn_, result.begin(), [](const auto& entry) noexcept { + return std::make_optional(entry); + }); return result.empty() ? std::nullopt : std::make_optional(std::move(result)); } diff --git a/samples/learn/HumanData.cpp b/samples/learn/HumanData.cpp index 486ce9aa..b9489763 100644 --- a/samples/learn/HumanData.cpp +++ b/samples/learn/HumanData.cpp @@ -20,18 +20,15 @@ void Human::addFriends(std::vector friends) noexcept { friends_.resize(friends.size()); - std::transform(friends.begin(), - friends.end(), - friends_.begin(), - [](const auto& spFriend) noexcept { - return std::visit( - [](const auto& hero) noexcept { - return WeakHero { - std::weak_ptr::element_type> { hero } - }; - }, - spFriend); - }); + std::ranges::transform(friends, friends_.begin(), [](const auto& spFriend) noexcept { + return std::visit( + [](const auto& hero) noexcept { + return WeakHero { + std::weak_ptr::element_type> { hero } + }; + }, + spFriend); + }); } const response::IdType& Human::getId() const noexcept @@ -48,12 +45,9 @@ std::optional>> Human::getFriends { std::vector> result(friends_.size()); - std::transform(friends_.begin(), - friends_.end(), - result.begin(), - [](const auto& wpFriend) noexcept { - return make_hero(wpFriend); - }); + std::ranges::transform(friends_, result.begin(), [](const auto& wpFriend) noexcept { + return make_hero(wpFriend); + }); result.erase(std::remove(result.begin(), result.end(), std::shared_ptr {}), result.end()); @@ -64,12 +58,9 @@ std::optional>> Human::getAppearsIn() const n { std::vector> result(appearsIn_.size()); - std::transform(appearsIn_.begin(), - appearsIn_.end(), - result.begin(), - [](const auto& entry) noexcept { - return std::make_optional(entry); - }); + std::ranges::transform(appearsIn_, result.begin(), [](const auto& entry) noexcept { + return std::make_optional(entry); + }); return result.empty() ? std::nullopt : std::make_optional(std::move(result)); } diff --git a/samples/learn/StarWarsData.cpp b/samples/learn/StarWarsData.cpp index db107051..d62bb221 100644 --- a/samples/learn/StarWarsData.cpp +++ b/samples/learn/StarWarsData.cpp @@ -8,12 +8,13 @@ #include "MutationData.h" #include "QueryData.h" #include "ReviewData.h" +#include "SubscriptionData.h" using namespace std::literals; namespace graphql::star_wars { -std::shared_ptr GetService() noexcept +std::shared_ptr MakeQuery() noexcept { auto luke = std::make_shared("1000"s, std::make_optional("Luke Skywalker"s), @@ -110,12 +111,20 @@ std::shared_ptr GetService() noexcept { artoo->getId(), artoo }, }; - auto query = - std::make_shared(std::move(heroes), std::move(humans), std::move(droids)); - auto mutation = std::make_shared(); - auto service = std::make_shared(std::move(query), std::move(mutation)); + return std::make_shared( + std::make_shared(std::move(heroes), std::move(humans), std::move(droids))); +} - return service; +std::shared_ptr MakeMutation() noexcept +{ + return std::make_shared(std::make_shared()); +} + +std::shared_ptr GetService() noexcept +{ + return std::make_shared(MakeQuery(), + MakeMutation(), + std::shared_ptr {}); } } // namespace graphql::star_wars diff --git a/samples/learn/SubscriptionData.cpp b/samples/learn/SubscriptionData.cpp new file mode 100644 index 00000000..f100703d --- /dev/null +++ b/samples/learn/SubscriptionData.cpp @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "CharacterObject.h" + +#include "SubscriptionData.h" + +namespace graphql::learn { + +Subscription::Subscription() noexcept +{ +} + +std::shared_ptr Subscription::getCharacterChanged() const noexcept +{ + return {}; +} + +} // namespace graphql::learn diff --git a/samples/learn/SubscriptionData.h b/samples/learn/SubscriptionData.h new file mode 100644 index 00000000..1218153d --- /dev/null +++ b/samples/learn/SubscriptionData.h @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +#ifndef SUBSCRIPTIONDATA_H +#define SUBSCRIPTIONDATA_H + +#include "SubscriptionObject.h" + +namespace graphql::learn { + +namespace object { + +class Character; + +} // namespace object + +class Subscription +{ +public: + explicit Subscription() noexcept; + + std::shared_ptr getCharacterChanged() const noexcept; +}; + +} // namespace graphql::learn + +#endif // SUBSCRIPTIONDATA_H diff --git a/samples/learn/schema/CMakeLists.txt b/samples/learn/schema/CMakeLists.txt index 2cab0cc0..589e3b6a 100644 --- a/samples/learn/schema/CMakeLists.txt +++ b/samples/learn/schema/CMakeLists.txt @@ -1,7 +1,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.28) # Normally this would be handled by find_package(cppgraphqlgen CONFIG). include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/cppgraphqlgen-functions.cmake) diff --git a/samples/learn/schema/CharacterObject.h b/samples/learn/schema/CharacterObject.h index fbdf2925..213ab726 100644 --- a/samples/learn/schema/CharacterObject.h +++ b/samples/learn/schema/CharacterObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef CHARACTEROBJECT_H -#define CHARACTEROBJECT_H +#ifndef STARWARS_CHARACTEROBJECT_H +#define STARWARS_CHARACTEROBJECT_H #include "StarWarsSchema.h" @@ -78,4 +78,4 @@ class [[nodiscard("unnecessary construction")]] Character final } // namespace graphql::learn::object -#endif // CHARACTEROBJECT_H +#endif // STARWARS_CHARACTEROBJECT_H diff --git a/samples/learn/schema/CharacterObject.ixx b/samples/learn/schema/CharacterObject.ixx new file mode 100644 index 00000000..9917b823 --- /dev/null +++ b/samples/learn/schema/CharacterObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "CharacterObject.h" + +export module GraphQL.StarWars.CharacterObject; + +export namespace graphql::learn::object { + +using object::Character; + +} // namespace graphql::learn::object diff --git a/samples/learn/schema/DroidObject.cpp b/samples/learn/schema/DroidObject.cpp index 6f9a706f..16473f96 100644 --- a/samples/learn/schema/DroidObject.cpp +++ b/samples/learn/schema/DroidObject.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include diff --git a/samples/learn/schema/DroidObject.h b/samples/learn/schema/DroidObject.h index 0722561d..540cf17e 100644 --- a/samples/learn/schema/DroidObject.h +++ b/samples/learn/schema/DroidObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef DROIDOBJECT_H -#define DROIDOBJECT_H +#ifndef STARWARS_DROIDOBJECT_H +#define STARWARS_DROIDOBJECT_H #include "StarWarsSchema.h" @@ -248,4 +248,4 @@ class [[nodiscard("unnecessary construction")]] Droid final } // namespace graphql::learn::object -#endif // DROIDOBJECT_H +#endif // STARWARS_DROIDOBJECT_H diff --git a/samples/learn/schema/DroidObject.ixx b/samples/learn/schema/DroidObject.ixx new file mode 100644 index 00000000..166edcc7 --- /dev/null +++ b/samples/learn/schema/DroidObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "DroidObject.h" + +export module GraphQL.StarWars.DroidObject; + +export namespace graphql::learn::object { + +using object::Droid; + +} // namespace graphql::learn::object diff --git a/samples/learn/schema/HumanObject.cpp b/samples/learn/schema/HumanObject.cpp index 1094ed45..ab8ad2ed 100644 --- a/samples/learn/schema/HumanObject.cpp +++ b/samples/learn/schema/HumanObject.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include diff --git a/samples/learn/schema/HumanObject.h b/samples/learn/schema/HumanObject.h index 836006ab..f3fede8d 100644 --- a/samples/learn/schema/HumanObject.h +++ b/samples/learn/schema/HumanObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef HUMANOBJECT_H -#define HUMANOBJECT_H +#ifndef STARWARS_HUMANOBJECT_H +#define STARWARS_HUMANOBJECT_H #include "StarWarsSchema.h" @@ -248,4 +248,4 @@ class [[nodiscard("unnecessary construction")]] Human final } // namespace graphql::learn::object -#endif // HUMANOBJECT_H +#endif // STARWARS_HUMANOBJECT_H diff --git a/samples/learn/schema/HumanObject.ixx b/samples/learn/schema/HumanObject.ixx new file mode 100644 index 00000000..9a218221 --- /dev/null +++ b/samples/learn/schema/HumanObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "HumanObject.h" + +export module GraphQL.StarWars.HumanObject; + +export namespace graphql::learn::object { + +using object::Human; + +} // namespace graphql::learn::object diff --git a/samples/learn/schema/MutationObject.cpp b/samples/learn/schema/MutationObject.cpp index 9bdb2b5f..ef6dcbb1 100644 --- a/samples/learn/schema/MutationObject.cpp +++ b/samples/learn/schema/MutationObject.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include diff --git a/samples/learn/schema/MutationObject.h b/samples/learn/schema/MutationObject.h index 004e4dcc..8b5efc89 100644 --- a/samples/learn/schema/MutationObject.h +++ b/samples/learn/schema/MutationObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef MUTATIONOBJECT_H -#define MUTATIONOBJECT_H +#ifndef STARWARS_MUTATIONOBJECT_H +#define STARWARS_MUTATIONOBJECT_H #include "StarWarsSchema.h" @@ -124,4 +124,4 @@ class [[nodiscard("unnecessary construction")]] Mutation final } // namespace graphql::learn::object -#endif // MUTATIONOBJECT_H +#endif // STARWARS_MUTATIONOBJECT_H diff --git a/samples/learn/schema/MutationObject.ixx b/samples/learn/schema/MutationObject.ixx new file mode 100644 index 00000000..1a2b2cba --- /dev/null +++ b/samples/learn/schema/MutationObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "MutationObject.h" + +export module GraphQL.StarWars.MutationObject; + +export namespace graphql::learn::object { + +using object::Mutation; + +} // namespace graphql::learn::object diff --git a/samples/learn/schema/QueryObject.cpp b/samples/learn/schema/QueryObject.cpp index bc1cf10f..061d89aa 100644 --- a/samples/learn/schema/QueryObject.cpp +++ b/samples/learn/schema/QueryObject.cpp @@ -15,7 +15,6 @@ #include #include -#include #include #include diff --git a/samples/learn/schema/QueryObject.h b/samples/learn/schema/QueryObject.h index a1bace97..ca68c38f 100644 --- a/samples/learn/schema/QueryObject.h +++ b/samples/learn/schema/QueryObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef QUERYOBJECT_H -#define QUERYOBJECT_H +#ifndef STARWARS_QUERYOBJECT_H +#define STARWARS_QUERYOBJECT_H #include "StarWarsSchema.h" @@ -182,4 +182,4 @@ class [[nodiscard("unnecessary construction")]] Query final } // namespace graphql::learn::object -#endif // QUERYOBJECT_H +#endif // STARWARS_QUERYOBJECT_H diff --git a/samples/learn/schema/QueryObject.ixx b/samples/learn/schema/QueryObject.ixx new file mode 100644 index 00000000..dfe464c1 --- /dev/null +++ b/samples/learn/schema/QueryObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "QueryObject.h" + +export module GraphQL.StarWars.QueryObject; + +export namespace graphql::learn::object { + +using object::Query; + +} // namespace graphql::learn::object diff --git a/samples/learn/schema/ReviewObject.cpp b/samples/learn/schema/ReviewObject.cpp index ebe1c4b9..240ed953 100644 --- a/samples/learn/schema/ReviewObject.cpp +++ b/samples/learn/schema/ReviewObject.cpp @@ -11,7 +11,6 @@ #include #include -#include #include #include diff --git a/samples/learn/schema/ReviewObject.h b/samples/learn/schema/ReviewObject.h index 69220738..991bcc15 100644 --- a/samples/learn/schema/ReviewObject.h +++ b/samples/learn/schema/ReviewObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef REVIEWOBJECT_H -#define REVIEWOBJECT_H +#ifndef STARWARS_REVIEWOBJECT_H +#define STARWARS_REVIEWOBJECT_H #include "StarWarsSchema.h" @@ -151,4 +151,4 @@ class [[nodiscard("unnecessary construction")]] Review final } // namespace graphql::learn::object -#endif // REVIEWOBJECT_H +#endif // STARWARS_REVIEWOBJECT_H diff --git a/samples/learn/schema/ReviewObject.ixx b/samples/learn/schema/ReviewObject.ixx new file mode 100644 index 00000000..067321d1 --- /dev/null +++ b/samples/learn/schema/ReviewObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "ReviewObject.h" + +export module GraphQL.StarWars.ReviewObject; + +export namespace graphql::learn::object { + +using object::Review; + +} // namespace graphql::learn::object diff --git a/samples/learn/schema/StarWarsSchema.cpp b/samples/learn/schema/StarWarsSchema.cpp index c3ce0a24..6e093b32 100644 --- a/samples/learn/schema/StarWarsSchema.cpp +++ b/samples/learn/schema/StarWarsSchema.cpp @@ -5,6 +5,7 @@ #include "QueryObject.h" #include "MutationObject.h" +#include "SubscriptionObject.h" #include "graphqlservice/internal/Schema.h" @@ -12,8 +13,8 @@ #include #include +#include #include -#include #include #include #include @@ -21,144 +22,17 @@ using namespace std::literals; -namespace graphql { -namespace service { +namespace graphql::learn { -static const auto s_namesEpisode = learn::getEpisodeNames(); -static const auto s_valuesEpisode = learn::getEpisodeValues(); - -template <> -learn::Episode Argument::convert(const response::Value& value) -{ - if (!value.maybe_enum()) - { - throw service::schema_exception { { R"ex(not a valid Episode value)ex" } }; - } - - const auto result = internal::sorted_map_lookup( - s_valuesEpisode, - std::string_view { value.get() }); - - if (!result) - { - throw service::schema_exception { { R"ex(not a valid Episode value)ex" } }; - } - - return *result; -} - -template <> -service::AwaitableResolver Result::convert(service::AwaitableScalar result, ResolverParams&& params) -{ - return ModifiedResult::resolve(std::move(result), std::move(params), - [](learn::Episode value, const ResolverParams&) - { - const auto idx = static_cast(value); - - if (idx >= s_namesEpisode.size()) - { - throw service::schema_exception { { R"ex(Enum value out of range for Episode)ex" } }; - } - - response::Value resolvedResult(response::Type::EnumValue); - - resolvedResult.set(std::string { s_namesEpisode[idx] }); - - return resolvedResult; - }); -} - -template <> -void Result::validateScalar(const response::Value& value) -{ - if (!value.maybe_enum()) - { - throw service::schema_exception { { R"ex(not a valid Episode value)ex" } }; - } - - const auto [itr, itrEnd] = internal::sorted_map_equal_range( - s_valuesEpisode.begin(), - s_valuesEpisode.end(), - std::string_view { value.get() }); - - if (itr == itrEnd) - { - throw service::schema_exception { { R"ex(not a valid Episode value)ex" } }; - } -} - -template <> -learn::ReviewInput Argument::convert(const response::Value& value) -{ - auto valueStars = service::ModifiedArgument::require("stars", value); - auto valueCommentary = service::ModifiedArgument::require("commentary", value); - - return learn::ReviewInput { - valueStars, - std::move(valueCommentary) - }; -} - -} // namespace service - -namespace learn { - -ReviewInput::ReviewInput() noexcept - : stars {} - , commentary {} -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -ReviewInput::ReviewInput( - int starsArg, - std::optional commentaryArg) noexcept - : stars { std::move(starsArg) } - , commentary { std::move(commentaryArg) } -{ -} - -ReviewInput::ReviewInput(const ReviewInput& other) - : stars { service::ModifiedArgument::duplicate(other.stars) } - , commentary { service::ModifiedArgument::duplicate(other.commentary) } -{ -} - -ReviewInput::ReviewInput(ReviewInput&& other) noexcept - : stars { std::move(other.stars) } - , commentary { std::move(other.commentary) } -{ -} - -ReviewInput::~ReviewInput() -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -ReviewInput& ReviewInput::operator=(const ReviewInput& other) -{ - ReviewInput value { other }; - - std::swap(*this, value); - - return *this; -} - -ReviewInput& ReviewInput::operator=(ReviewInput&& other) noexcept -{ - stars = std::move(other.stars); - commentary = std::move(other.commentary); - - return *this; -} - -Operations::Operations(std::shared_ptr query, std::shared_ptr mutation) +Operations::Operations(std::shared_ptr query, std::shared_ptr mutation, std::shared_ptr subscription) : service::Request({ { service::strQuery, query }, - { service::strMutation, mutation } + { service::strMutation, mutation }, + { service::strSubscription, subscription } }, GetSchema()) , _query(std::move(query)) , _mutation(std::move(mutation)) + , _subscription(std::move(subscription)) { } @@ -180,11 +54,14 @@ void AddTypesToSchema(const std::shared_ptr& schema) schema->AddType(R"gql(Review)gql"sv, typeReview); auto typeMutation = schema::ObjectType::Make(R"gql(Mutation)gql"sv, R"md()md"sv); schema->AddType(R"gql(Mutation)gql"sv, typeMutation); + auto typeSubscription = schema::ObjectType::Make(R"gql(Subscription)gql"sv, R"md()md"sv); + schema->AddType(R"gql(Subscription)gql"sv, typeSubscription); + static const auto s_namesEpisode = getEpisodeNames(); typeEpisode->AddEnumValues({ - { service::s_namesEpisode[static_cast(learn::Episode::NEW_HOPE)], R"md()md"sv, std::nullopt }, - { service::s_namesEpisode[static_cast(learn::Episode::EMPIRE)], R"md()md"sv, std::nullopt }, - { service::s_namesEpisode[static_cast(learn::Episode::JEDI)], R"md()md"sv, std::nullopt } + { s_namesEpisode[static_cast(learn::Episode::NEW_HOPE)], R"md()md"sv, std::nullopt }, + { s_namesEpisode[static_cast(learn::Episode::EMPIRE)], R"md()md"sv, std::nullopt }, + { s_namesEpisode[static_cast(learn::Episode::JEDI)], R"md()md"sv, std::nullopt } }); typeReviewInput->AddInputValues({ @@ -199,9 +76,11 @@ void AddTypesToSchema(const std::shared_ptr& schema) AddQueryDetails(typeQuery, schema); AddReviewDetails(typeReview, schema); AddMutationDetails(typeMutation, schema); + AddSubscriptionDetails(typeSubscription, schema); schema->AddQueryType(typeQuery); schema->AddMutationType(typeMutation); + schema->AddSubscriptionType(typeSubscription); } std::shared_ptr GetSchema() @@ -220,5 +99,4 @@ std::shared_ptr GetSchema() return schema; } -} // namespace learn -} // namespace graphql +} // namespace graphql::learn diff --git a/samples/learn/schema/StarWarsSchema.h b/samples/learn/schema/StarWarsSchema.h index 01308601..9529c7ec 100644 --- a/samples/learn/schema/StarWarsSchema.h +++ b/samples/learn/schema/StarWarsSchema.h @@ -8,66 +8,24 @@ #ifndef STARWARSSCHEMA_H #define STARWARSSCHEMA_H +#include "graphqlservice/GraphQLResponse.h" +#include "graphqlservice/GraphQLService.h" + +#include "graphqlservice/internal/Version.h" #include "graphqlservice/internal/Schema.h" -// Check if the library version is compatible with schemagen 4.5.0 -static_assert(graphql::internal::MajorVersion == 4, "regenerate with schemagen: major version mismatch"); -static_assert(graphql::internal::MinorVersion == 5, "regenerate with schemagen: minor version mismatch"); +#include "StarWarsSharedTypes.h" #include #include #include #include -namespace graphql { -namespace learn { - -enum class [[nodiscard("unnecessary conversion")]] Episode -{ - NEW_HOPE, - EMPIRE, - JEDI -}; - -[[nodiscard("unnecessary call")]] constexpr auto getEpisodeNames() noexcept -{ - using namespace std::literals; - - return std::array { - R"gql(NEW_HOPE)gql"sv, - R"gql(EMPIRE)gql"sv, - R"gql(JEDI)gql"sv - }; -} - -[[nodiscard("unnecessary call")]] constexpr auto getEpisodeValues() noexcept -{ - using namespace std::literals; - - return std::array, 3> { - std::make_pair(R"gql(JEDI)gql"sv, Episode::JEDI), - std::make_pair(R"gql(EMPIRE)gql"sv, Episode::EMPIRE), - std::make_pair(R"gql(NEW_HOPE)gql"sv, Episode::NEW_HOPE) - }; -} - -struct [[nodiscard("unnecessary construction")]] ReviewInput -{ - explicit ReviewInput() noexcept; - explicit ReviewInput( - int starsArg, - std::optional commentaryArg) noexcept; - ReviewInput(const ReviewInput& other); - ReviewInput(ReviewInput&& other) noexcept; - ~ReviewInput(); - - ReviewInput& operator=(const ReviewInput& other); - ReviewInput& operator=(ReviewInput&& other) noexcept; - - int stars; - std::optional commentary; -}; +// Check if the library version is compatible with schemagen 5.0.0 +static_assert(graphql::internal::MajorVersion == 5, "regenerate with schemagen: major version mismatch"); +static_assert(graphql::internal::MinorVersion == 0, "regenerate with schemagen: minor version mismatch"); +namespace graphql::learn { namespace object { class Character; @@ -77,6 +35,7 @@ class Droid; class Query; class Review; class Mutation; +class Subscription; } // namespace object @@ -84,13 +43,14 @@ class [[nodiscard("unnecessary construction")]] Operations final : public service::Request { public: - explicit Operations(std::shared_ptr query, std::shared_ptr mutation); + explicit Operations(std::shared_ptr query, std::shared_ptr mutation, std::shared_ptr subscription); - template - explicit Operations(std::shared_ptr query, std::shared_ptr mutation) + template + explicit Operations(std::shared_ptr query, std::shared_ptr mutation, std::shared_ptr subscription = {}) : Operations { std::make_shared(std::move(query)), - std::make_shared(std::move(mutation)) + std::make_shared(std::move(mutation)), + subscription ? std::make_shared(std::move(subscription)) : std::shared_ptr {} } { } @@ -98,6 +58,7 @@ class [[nodiscard("unnecessary construction")]] Operations final private: std::shared_ptr _query; std::shared_ptr _mutation; + std::shared_ptr _subscription; }; void AddCharacterDetails(const std::shared_ptr& typeCharacter, const std::shared_ptr& schema); @@ -107,10 +68,10 @@ void AddDroidDetails(const std::shared_ptr& typeDroid, const void AddQueryDetails(const std::shared_ptr& typeQuery, const std::shared_ptr& schema); void AddReviewDetails(const std::shared_ptr& typeReview, const std::shared_ptr& schema); void AddMutationDetails(const std::shared_ptr& typeMutation, const std::shared_ptr& schema); +void AddSubscriptionDetails(const std::shared_ptr& typeSubscription, const std::shared_ptr& schema); std::shared_ptr GetSchema(); -} // namespace learn -} // namespace graphql +} // namespace graphql::learn #endif // STARWARSSCHEMA_H diff --git a/samples/learn/schema/StarWarsSchema.ixx b/samples/learn/schema/StarWarsSchema.ixx new file mode 100644 index 00000000..838f2815 --- /dev/null +++ b/samples/learn/schema/StarWarsSchema.ixx @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "StarWarsSchema.h" + +export module GraphQL.StarWars.StarWarsSchema; + +export import GraphQL.StarWars.StarWarsSharedTypes; + +export import GraphQL.StarWars.CharacterObject; +export import GraphQL.StarWars.HumanObject; +export import GraphQL.StarWars.DroidObject; +export import GraphQL.StarWars.QueryObject; +export import GraphQL.StarWars.ReviewObject; +export import GraphQL.StarWars.MutationObject; +export import GraphQL.StarWars.SubscriptionObject; + +export namespace graphql::learn { + +using learn::Operations; + +using learn::AddCharacterDetails; +using learn::AddHumanDetails; +using learn::AddDroidDetails; +using learn::AddQueryDetails; +using learn::AddReviewDetails; +using learn::AddMutationDetails; +using learn::AddSubscriptionDetails; + +using learn::GetSchema; + +} // namespace graphql::learn diff --git a/samples/learn/schema/StarWarsSharedTypes.cpp b/samples/learn/schema/StarWarsSharedTypes.cpp new file mode 100644 index 00000000..697d2181 --- /dev/null +++ b/samples/learn/schema/StarWarsSharedTypes.cpp @@ -0,0 +1,149 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +#include "graphqlservice/GraphQLService.h" + +#include "StarWarsSharedTypes.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std::literals; + +namespace graphql { +namespace service { + +static const auto s_namesEpisode = learn::getEpisodeNames(); +static const auto s_valuesEpisode = learn::getEpisodeValues(); + +template <> +learn::Episode Argument::convert(const response::Value& value) +{ + if (!value.maybe_enum()) + { + throw service::schema_exception { { R"ex(not a valid Episode value)ex" } }; + } + + const auto result = internal::sorted_map_lookup( + s_valuesEpisode, + std::string_view { value.get() }); + + if (!result) + { + throw service::schema_exception { { R"ex(not a valid Episode value)ex" } }; + } + + return *result; +} + +template <> +service::AwaitableResolver Result::convert(service::AwaitableScalar result, ResolverParams&& params) +{ + return ModifiedResult::resolve(std::move(result), std::move(params), + [](learn::Episode value, const ResolverParams&) + { + const auto idx = static_cast(value); + + if (idx >= s_namesEpisode.size()) + { + throw service::schema_exception { { R"ex(Enum value out of range for Episode)ex" } }; + } + + return ResolverResult { { response::ValueToken::EnumValue { std::string { s_namesEpisode[idx] } } } }; + }); +} + +template <> +void Result::validateScalar(const response::Value& value) +{ + if (!value.maybe_enum()) + { + throw service::schema_exception { { R"ex(not a valid Episode value)ex" } }; + } + + const auto [itr, itrEnd] = internal::sorted_map_equal_range( + s_valuesEpisode.begin(), + s_valuesEpisode.end(), + std::string_view { value.get() }); + + if (itr == itrEnd) + { + throw service::schema_exception { { R"ex(not a valid Episode value)ex" } }; + } +} + +template <> +learn::ReviewInput Argument::convert(const response::Value& value) +{ + auto valueStars = service::ModifiedArgument::require("stars", value); + auto valueCommentary = service::ModifiedArgument::require("commentary", value); + + return learn::ReviewInput { + valueStars, + std::move(valueCommentary) + }; +} + +} // namespace service + +namespace learn { + +ReviewInput::ReviewInput() noexcept + : stars {} + , commentary {} +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +ReviewInput::ReviewInput( + int starsArg, + std::optional commentaryArg) noexcept + : stars { std::move(starsArg) } + , commentary { std::move(commentaryArg) } +{ +} + +ReviewInput::ReviewInput(const ReviewInput& other) + : stars { service::ModifiedArgument::duplicate(other.stars) } + , commentary { service::ModifiedArgument::duplicate(other.commentary) } +{ +} + +ReviewInput::ReviewInput(ReviewInput&& other) noexcept + : stars { std::move(other.stars) } + , commentary { std::move(other.commentary) } +{ +} + +ReviewInput::~ReviewInput() +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +ReviewInput& ReviewInput::operator=(const ReviewInput& other) +{ + ReviewInput value { other }; + + std::swap(*this, value); + + return *this; +} + +ReviewInput& ReviewInput::operator=(ReviewInput&& other) noexcept +{ + stars = std::move(other.stars); + commentary = std::move(other.commentary); + + return *this; +} + +} // namespace learn +} // namespace graphql diff --git a/samples/learn/schema/StarWarsSharedTypes.h b/samples/learn/schema/StarWarsSharedTypes.h new file mode 100644 index 00000000..b9dee9da --- /dev/null +++ b/samples/learn/schema/StarWarsSharedTypes.h @@ -0,0 +1,78 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +#pragma once + +#ifndef STARWARSSHAREDTYPES_H +#define STARWARSSHAREDTYPES_H + +#include "graphqlservice/GraphQLResponse.h" + +#include "graphqlservice/internal/Version.h" + +#include +#include +#include +#include +#include +#include + +// Check if the library version is compatible with schemagen 5.0.0 +static_assert(graphql::internal::MajorVersion == 5, "regenerate with schemagen: major version mismatch"); +static_assert(graphql::internal::MinorVersion == 0, "regenerate with schemagen: minor version mismatch"); + +namespace graphql { +namespace learn { + +enum class [[nodiscard("unnecessary conversion")]] Episode +{ + NEW_HOPE, + EMPIRE, + JEDI +}; + +[[nodiscard("unnecessary call")]] constexpr auto getEpisodeNames() noexcept +{ + using namespace std::literals; + + return std::array { + R"gql(NEW_HOPE)gql"sv, + R"gql(EMPIRE)gql"sv, + R"gql(JEDI)gql"sv + }; +} + +[[nodiscard("unnecessary call")]] constexpr auto getEpisodeValues() noexcept +{ + using namespace std::literals; + + return std::array, 3> { + std::make_pair(R"gql(JEDI)gql"sv, Episode::JEDI), + std::make_pair(R"gql(EMPIRE)gql"sv, Episode::EMPIRE), + std::make_pair(R"gql(NEW_HOPE)gql"sv, Episode::NEW_HOPE) + }; +} + +struct [[nodiscard("unnecessary construction")]] ReviewInput +{ + explicit ReviewInput() noexcept; + explicit ReviewInput( + int starsArg, + std::optional commentaryArg) noexcept; + ReviewInput(const ReviewInput& other); + ReviewInput(ReviewInput&& other) noexcept; + ~ReviewInput(); + + ReviewInput& operator=(const ReviewInput& other); + ReviewInput& operator=(ReviewInput&& other) noexcept; + + int stars; + std::optional commentary; +}; + +} // namespace learn +} // namespace graphql + +#endif // STARWARSSHAREDTYPES_H diff --git a/samples/learn/schema/StarWarsSharedTypes.ixx b/samples/learn/schema/StarWarsSharedTypes.ixx new file mode 100644 index 00000000..5e6a2fa2 --- /dev/null +++ b/samples/learn/schema/StarWarsSharedTypes.ixx @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "StarWarsSharedTypes.h" + +export module GraphQL.StarWars.StarWarsSharedTypes; + +export namespace graphql::learn { + +using learn::Episode; +using learn::getEpisodeNames; +using learn::getEpisodeValues; + +using learn::ReviewInput; + +} // namespace graphql::learn diff --git a/samples/learn/schema/SubscriptionObject.cpp b/samples/learn/schema/SubscriptionObject.cpp new file mode 100644 index 00000000..96f204f6 --- /dev/null +++ b/samples/learn/schema/SubscriptionObject.cpp @@ -0,0 +1,92 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +#include "SubscriptionObject.h" +#include "CharacterObject.h" + +#include "graphqlservice/internal/Schema.h" + +#include "graphqlservice/introspection/IntrospectionSchema.h" + +#include +#include +#include +#include + +using namespace std::literals; + +namespace graphql::learn { +namespace object { + +Subscription::Subscription(std::unique_ptr pimpl) noexcept + : service::Object{ getTypeNames(), getResolvers() } + , _pimpl { std::move(pimpl) } +{ +} + +service::TypeNames Subscription::getTypeNames() const noexcept +{ + return { + R"gql(Subscription)gql"sv + }; +} + +service::ResolverMap Subscription::getResolvers() const noexcept +{ + return { + { R"gql(__typename)gql"sv, [this](service::ResolverParams&& params) { return resolve_typename(std::move(params)); } }, + { R"gql(newEpisode)gql"sv, [this](service::ResolverParams&& params) { return resolveNewEpisode(std::move(params)); } }, + { R"gql(characterChanged)gql"sv, [this](service::ResolverParams&& params) { return resolveCharacterChanged(std::move(params)); } } + }; +} + +void Subscription::beginSelectionSet(const service::SelectionSetParams& params) const +{ + _pimpl->beginSelectionSet(params); +} + +void Subscription::endSelectionSet(const service::SelectionSetParams& params) const +{ + _pimpl->endSelectionSet(params); +} + +service::AwaitableResolver Subscription::resolveCharacterChanged(service::ResolverParams&& params) const +{ + std::unique_lock resolverLock(_resolverMutex); + service::SelectionSetParams selectionSetParams { static_cast(params) }; + auto directives = std::move(params.fieldDirectives); + auto result = _pimpl->getCharacterChanged(service::FieldParams { std::move(selectionSetParams), std::move(directives) }); + resolverLock.unlock(); + + return service::ModifiedResult::convert(std::move(result), std::move(params)); +} + +service::AwaitableResolver Subscription::resolveNewEpisode(service::ResolverParams&& params) const +{ + std::unique_lock resolverLock(_resolverMutex); + service::SelectionSetParams selectionSetParams { static_cast(params) }; + auto directives = std::move(params.fieldDirectives); + auto result = _pimpl->getNewEpisode(service::FieldParams { std::move(selectionSetParams), std::move(directives) }); + resolverLock.unlock(); + + return service::ModifiedResult::convert(std::move(result), std::move(params)); +} + +service::AwaitableResolver Subscription::resolve_typename(service::ResolverParams&& params) const +{ + return service::Result::convert(std::string{ R"gql(Subscription)gql" }, std::move(params)); +} + +} // namespace object + +void AddSubscriptionDetails(const std::shared_ptr& typeSubscription, const std::shared_ptr& schema) +{ + typeSubscription->AddFields({ + schema::Field::Make(R"gql(characterChanged)gql"sv, R"md()md"sv, std::nullopt, schema->WrapType(introspection::TypeKind::NON_NULL, schema->LookupType(R"gql(Character)gql"sv))), + schema::Field::Make(R"gql(newEpisode)gql"sv, R"md()md"sv, std::nullopt, schema->WrapType(introspection::TypeKind::NON_NULL, schema->LookupType(R"gql(Episode)gql"sv))) + }); +} + +} // namespace graphql::learn diff --git a/samples/learn/schema/SubscriptionObject.h b/samples/learn/schema/SubscriptionObject.h new file mode 100644 index 00000000..93613e3e --- /dev/null +++ b/samples/learn/schema/SubscriptionObject.h @@ -0,0 +1,164 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +#pragma once + +#ifndef STARWARS_SUBSCRIPTIONOBJECT_H +#define STARWARS_SUBSCRIPTIONOBJECT_H + +#include "StarWarsSchema.h" + +namespace graphql::learn::object { +namespace methods::SubscriptionHas { + +template +concept getCharacterChangedWithParams = requires (TImpl impl, service::FieldParams params) +{ + { service::AwaitableObject> { impl.getCharacterChanged(std::move(params)) } }; +}; + +template +concept getCharacterChanged = requires (TImpl impl) +{ + { service::AwaitableObject> { impl.getCharacterChanged() } }; +}; + +template +concept getNewEpisodeWithParams = requires (TImpl impl, service::FieldParams params) +{ + { service::AwaitableScalar { impl.getNewEpisode(std::move(params)) } }; +}; + +template +concept getNewEpisode = requires (TImpl impl) +{ + { service::AwaitableScalar { impl.getNewEpisode() } }; +}; + +template +concept beginSelectionSet = requires (TImpl impl, const service::SelectionSetParams params) +{ + { impl.beginSelectionSet(params) }; +}; + +template +concept endSelectionSet = requires (TImpl impl, const service::SelectionSetParams params) +{ + { impl.endSelectionSet(params) }; +}; + +} // namespace methods::SubscriptionHas + +class [[nodiscard("unnecessary construction")]] Subscription final + : public service::Object +{ +private: + [[nodiscard("unnecessary call")]] service::AwaitableResolver resolveCharacterChanged(service::ResolverParams&& params) const; + [[nodiscard("unnecessary call")]] service::AwaitableResolver resolveNewEpisode(service::ResolverParams&& params) const; + + [[nodiscard("unnecessary call")]] service::AwaitableResolver resolve_typename(service::ResolverParams&& params) const; + + struct [[nodiscard("unnecessary construction")]] Concept + { + virtual ~Concept() = default; + + virtual void beginSelectionSet(const service::SelectionSetParams& params) const = 0; + virtual void endSelectionSet(const service::SelectionSetParams& params) const = 0; + + [[nodiscard("unnecessary call")]] virtual service::AwaitableObject> getCharacterChanged(service::FieldParams&& params) const = 0; + [[nodiscard("unnecessary call")]] virtual service::AwaitableScalar getNewEpisode(service::FieldParams&& params) const = 0; + }; + + template + struct [[nodiscard("unnecessary construction")]] Model final + : Concept + { + explicit Model(std::shared_ptr pimpl) noexcept + : _pimpl { std::move(pimpl) } + { + static_assert(methods::SubscriptionHas::getCharacterChangedWithParams + || methods::SubscriptionHas::getCharacterChanged + || methods::SubscriptionHas::getNewEpisodeWithParams + || methods::SubscriptionHas::getNewEpisode, R"msg(Subscription fields are not implemented)msg"); + } + + [[nodiscard("unnecessary call")]] service::AwaitableObject> getCharacterChanged(service::FieldParams&& params) const override + { + if constexpr (methods::SubscriptionHas::getCharacterChangedWithParams) + { + return { _pimpl->getCharacterChanged(std::move(params)) }; + } + else if constexpr (methods::SubscriptionHas::getCharacterChanged) + { + return { _pimpl->getCharacterChanged() }; + } + else + { + throw service::unimplemented_method(R"ex(Subscription::getCharacterChanged)ex"); + } + } + + [[nodiscard("unnecessary call")]] service::AwaitableScalar getNewEpisode(service::FieldParams&& params) const override + { + if constexpr (methods::SubscriptionHas::getNewEpisodeWithParams) + { + return { _pimpl->getNewEpisode(std::move(params)) }; + } + else if constexpr (methods::SubscriptionHas::getNewEpisode) + { + return { _pimpl->getNewEpisode() }; + } + else + { + throw service::unimplemented_method(R"ex(Subscription::getNewEpisode)ex"); + } + } + + void beginSelectionSet(const service::SelectionSetParams& params) const override + { + if constexpr (methods::SubscriptionHas::beginSelectionSet) + { + _pimpl->beginSelectionSet(params); + } + } + + void endSelectionSet(const service::SelectionSetParams& params) const override + { + if constexpr (methods::SubscriptionHas::endSelectionSet) + { + _pimpl->endSelectionSet(params); + } + } + + private: + const std::shared_ptr _pimpl; + }; + + explicit Subscription(std::unique_ptr pimpl) noexcept; + + [[nodiscard("unnecessary call")]] service::TypeNames getTypeNames() const noexcept; + [[nodiscard("unnecessary call")]] service::ResolverMap getResolvers() const noexcept; + + void beginSelectionSet(const service::SelectionSetParams& params) const override; + void endSelectionSet(const service::SelectionSetParams& params) const override; + + const std::unique_ptr _pimpl; + +public: + template + explicit Subscription(std::shared_ptr pimpl) noexcept + : Subscription { std::unique_ptr { std::make_unique>(std::move(pimpl)) } } + { + } + + [[nodiscard("unnecessary call")]] static constexpr std::string_view getObjectType() noexcept + { + return { R"gql(Subscription)gql" }; + } +}; + +} // namespace graphql::learn::object + +#endif // STARWARS_SUBSCRIPTIONOBJECT_H diff --git a/samples/learn/schema/SubscriptionObject.ixx b/samples/learn/schema/SubscriptionObject.ixx new file mode 100644 index 00000000..2e3178ae --- /dev/null +++ b/samples/learn/schema/SubscriptionObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "SubscriptionObject.h" + +export module GraphQL.StarWars.SubscriptionObject; + +export namespace graphql::learn::object { + +using object::Subscription; + +} // namespace graphql::learn::object diff --git a/samples/learn/schema/learn_schema_files b/samples/learn/schema/learn_schema_files index 4f897e53..4f26ca5c 100644 --- a/samples/learn/schema/learn_schema_files +++ b/samples/learn/schema/learn_schema_files @@ -1,3 +1,4 @@ +StarWarsSharedTypes.cpp StarWarsSchema.cpp CharacterObject.cpp HumanObject.cpp @@ -5,3 +6,4 @@ DroidObject.cpp QueryObject.cpp ReviewObject.cpp MutationObject.cpp +SubscriptionObject.cpp diff --git a/samples/learn/schema/schema.learn.graphql b/samples/learn/schema/schema.learn.graphql index 694955be..1670aa49 100644 --- a/samples/learn/schema/schema.learn.graphql +++ b/samples/learn/schema/schema.learn.graphql @@ -44,4 +44,9 @@ type Review { type Mutation { createReview(ep: Episode! review: ReviewInput!): Review! -} \ No newline at end of file +} + +type Subscription { + characterChanged: Character! + newEpisode: Episode! +} diff --git a/samples/proxy/CMakeLists.txt b/samples/proxy/CMakeLists.txt index df821c20..5b70f5b0 100644 --- a/samples/proxy/CMakeLists.txt +++ b/samples/proxy/CMakeLists.txt @@ -1,10 +1,10 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.28) -add_subdirectory(query) add_subdirectory(schema) +add_subdirectory(query) add_executable(client client.cpp) target_link_libraries(client PRIVATE diff --git a/samples/proxy/client.cpp b/samples/proxy/client.cpp index d8be65f5..5b09b6f2 100644 --- a/samples/proxy/client.cpp +++ b/samples/proxy/client.cpp @@ -5,6 +5,7 @@ #include "schema/ProxySchema.h" #include "schema/QueryObject.h" +#include "schema/QueryResultsObject.h" #include "graphqlservice/JSONResponse.h" @@ -21,6 +22,7 @@ #include #include +#include #include #include #include @@ -45,15 +47,112 @@ constexpr auto c_port = "8080"sv; constexpr auto c_target = "/graphql"sv; constexpr int c_version = 11; // HTTP 1.1 +struct AsyncIoWorker : service::RequestState +{ + AsyncIoWorker() + : worker { std::make_shared() } + { + } + + const service::await_async worker; +}; + +class Results +{ +public: + explicit Results(response::Value&& data, std::vector errors) noexcept; + + service::AwaitableScalar> getData( + service::FieldParams&& fieldParams) const; + service::AwaitableScalar>>> getErrors( + service::FieldParams&& fieldParams) const; + +private: + mutable response::Value m_data; + mutable std::vector m_errors; +}; + +Results::Results(response::Value&& data, std::vector errors) noexcept + : m_data { std::move(data) } + , m_errors { std::move(errors) } +{ +} + +service::AwaitableScalar> Results::getData( + service::FieldParams&& fieldParams) const +{ + auto asyncIoWorker = std::static_pointer_cast(fieldParams.state); + auto data = std::move(m_data); + + // Jump to a worker thread for the resolver where we can run a separate I/O context without + // blocking the I/O context in Query::getRelay. This simulates how you might fan out to + // additional async I/O tasks for sub-field resolvers. + co_await asyncIoWorker->worker; + + net::io_context ioc; + auto future = net::co_spawn( + ioc, + [](response::Value&& data) -> net::awaitable> { + co_return (data.type() == response::Type::Null) + ? std::nullopt + : std::make_optional(response::toJSON(std::move(data))); + }(std::move(data)), + net::use_future); + + ioc.run(); + + co_return future.get(); +} + +service::AwaitableScalar>>> Results::getErrors( + service::FieldParams&& fieldParams) const +{ + auto asyncIoWorker = std::static_pointer_cast(fieldParams.state); + auto errors = std::move(m_errors); + + // Jump to a worker thread for the resolver where we can run a separate I/O context without + // blocking the I/O context in Query::getRelay. This simulates how you might fan out to + // additional async I/O tasks for sub-field resolvers. + co_await asyncIoWorker->worker; + + net::io_context ioc; + auto future = net::co_spawn( + ioc, + [](std::vector errors) + -> net::awaitable>>> { + if (errors.empty()) + { + co_return std::nullopt; + } + + std::vector> results { errors.size() }; + + std::transform(errors.begin(), + errors.end(), + results.begin(), + [](auto& error) noexcept -> std::optional { + return error.message.empty() + ? std::nullopt + : std::make_optional(std::move(error.message)); + }); + + co_return std::make_optional(results); + }(std::move(errors)), + net::use_future); + + ioc.run(); + + co_return future.get(); +} + class Query { public: explicit Query(std::string_view host, std::string_view port, std::string_view target, int version) noexcept; - std::future> getRelay(std::string&& queryArg, - std::optional&& operationNameArg, - std::optional&& variablesArg) const; + std::future> getRelay( + proxy::QueryInput&& inputArg) const; private: const std::string m_host; @@ -73,21 +172,22 @@ Query::Query( // Based on: // https://www.boost.org/doc/libs/1_82_0/libs/beast/example/http/client/awaitable/http_client_awaitable.cpp -std::future> Query::getRelay(std::string&& queryArg, - std::optional&& operationNameArg, std::optional&& variablesArg) const +std::future> Query::getRelay( + proxy::QueryInput&& inputArg) const { response::Value payload { response::Type::Map }; - payload.emplace_back("query"s, response::Value { std::move(queryArg) }); + payload.emplace_back("query"s, response::Value { std::move(inputArg.query) }); - if (operationNameArg) + if (inputArg.operationName) { - payload.emplace_back("operationName"s, response::Value { std::move(*operationNameArg) }); + payload.emplace_back("operationName"s, + response::Value { std::move(*inputArg.operationName) }); } - if (variablesArg) + if (inputArg.variables) { - payload.emplace_back("variables"s, response::Value { std::move(*variablesArg) }); + payload.emplace_back("variables"s, response::Value { std::move(*inputArg.variables) }); } std::string requestBody = response::toJSON(std::move(payload)); @@ -99,7 +199,8 @@ std::future> Query::getRelay(std::string&& queryArg, const char* port, const char* target, int version, - std::string requestBody) -> net::awaitable> { + std::string requestBody) + -> net::awaitable> { // These objects perform our I/O. They use an executor with a default completion token // of use_awaitable. This makes our code easy, but will use exceptions as the default // error handling, i.e. if the connection drops, we might see an exception. @@ -150,7 +251,10 @@ std::future> Query::getRelay(std::string&& queryArg, throw boost::system::system_error(ec, "shutdown"); } - co_return std::make_optional(std::move(res.body())); + auto [data, errors] = client::parseServiceResponse(response::parseJSON(res.body())); + + co_return std::make_shared( + std::make_shared(std::move(data), std::move(errors))); }(m_host.c_str(), m_port.c_str(), m_target.c_str(), m_version, std::move(requestBody)), net::use_future); @@ -173,20 +277,33 @@ int main(int argc, char** argv) std::cout << "Executing query..." << std::endl; - using namespace client::query::relayQuery; + using namespace proxy::client::query::relayQuery; auto query = GetRequestObject(); - auto variables = serializeVariables( - { input, ((argc > 1) ? std::make_optional(argv[1]) : std::nullopt) }); + auto variables = serializeVariables({ QueryInput { OperationType::QUERY, + input, + ((argc > 1) ? std::make_optional(argv[1]) : std::nullopt), + std::nullopt } }); auto launch = service::await_async { std::make_shared() }; + auto state = std::make_shared(); auto serviceResponse = client::parseServiceResponse( - service->resolve({ query, GetOperationName(), std::move(variables), launch }).get()); - auto result = client::query::relayQuery::parseResponse(std::move(serviceResponse.data)); + service->resolve({ query, GetOperationName(), std::move(variables), launch, state }) + .get()); + auto result = parseResponse(std::move(serviceResponse.data)); auto errors = std::move(serviceResponse.errors); - if (result.relay) + if (result.relay.data) + { + std::cout << "Data: " << *result.relay.data << std::endl; + } + + if (result.relay.errors) { - std::cout << *result.relay << std::endl; + for (const auto& message : *result.relay.errors) + { + std::cerr << "Remote Error: " + << (message ? std::string_view { *message } : ""sv) << std::endl; + } } if (!errors.empty()) diff --git a/samples/proxy/query/CMakeLists.txt b/samples/proxy/query/CMakeLists.txt index f774c8f8..f7ae51ab 100644 --- a/samples/proxy/query/CMakeLists.txt +++ b/samples/proxy/query/CMakeLists.txt @@ -1,13 +1,13 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.28) # Normally this would be handled by find_package(cppgraphqlgen CONFIG). include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/cppgraphqlgen-functions.cmake) if(GRAPHQL_UPDATE_SAMPLES AND GRAPHQL_BUILD_CLIENTGEN) - update_graphql_client_files(proxy ../schema/schema.graphql query.graphql Proxy proxy) + update_graphql_shared_client_files(proxy proxy query.graphql) endif() add_graphql_client_target(proxy) diff --git a/samples/proxy/query/ProxyClient.cpp b/samples/proxy/query/ProxyClient.cpp index fdf41b70..14d71d52 100644 --- a/samples/proxy/query/ProxyClient.cpp +++ b/samples/proxy/query/ProxyClient.cpp @@ -9,15 +9,16 @@ #include #include -#include +#include #include #include #include using namespace std::literals; -namespace graphql::client { +namespace graphql { namespace proxy { +namespace client { const std::string& GetRequestText() noexcept { @@ -25,8 +26,11 @@ const std::string& GetRequestText() noexcept # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. - query relayQuery($query: String!, $operationName: String, $variables: String) { - relay(query: $query, operationName: $operationName, variables: $variables) + query relayQuery($input: QueryInput!) { + relay(input: $input) { + data + errors + } } )gql"s; @@ -47,11 +51,98 @@ const peg::ast& GetRequestObject() noexcept return s_request; } +} // namespace client } // namespace proxy +namespace client { + using namespace proxy; -namespace query::relayQuery { +template <> +response::Value Variable::serialize(OperationType&& value) +{ + static const std::array s_names = { + R"gql(QUERY)gql"sv, + R"gql(MUTATION)gql"sv, + R"gql(SUBSCRIPTION)gql"sv + }; + + response::Value result { response::Type::EnumValue }; + + result.set(std::string { s_names[static_cast(value)] }); + + return result; +} + +template <> +response::Value Variable::serialize(QueryInput&& inputValue) +{ + response::Value result { response::Type::Map }; + + result.emplace_back(R"js(type)js"s, ModifiedVariable::serialize(std::move(inputValue.type))); + result.emplace_back(R"js(query)js"s, ModifiedVariable::serialize(std::move(inputValue.query))); + result.emplace_back(R"js(operationName)js"s, ModifiedVariable::serialize(std::move(inputValue.operationName))); + result.emplace_back(R"js(variables)js"s, ModifiedVariable::serialize(std::move(inputValue.variables))); + + return result; +} + +static const std::array, 3> s_valuesOperationType = { + std::make_pair(R"gql(QUERY)gql"sv, OperationType::QUERY), + std::make_pair(R"gql(MUTATION)gql"sv, OperationType::MUTATION), + std::make_pair(R"gql(SUBSCRIPTION)gql"sv, OperationType::SUBSCRIPTION) +}; + +template <> +OperationType Response::parse(response::Value&& value) +{ + if (!value.maybe_enum()) + { + throw std::logic_error { R"ex(not a valid OperationType value)ex" }; + } + + const auto result = internal::sorted_map_lookup( + s_valuesOperationType, + std::string_view { value.get() }); + + if (!result) + { + throw std::logic_error { R"ex(not a valid OperationType value)ex" }; + } + + return *result; +} + +template <> +graphql::proxy::client::query::relayQuery::Response::relay_QueryResults Response::parse(response::Value&& response) +{ + graphql::proxy::client::query::relayQuery::Response::relay_QueryResults result; + + if (response.type() == response::Type::Map) + { + auto members = response.release(); + + for (auto& member : members) + { + if (member.first == R"js(data)js"sv) + { + result.data = ModifiedResponse::parse(std::move(member.second)); + continue; + } + if (member.first == R"js(errors)js"sv) + { + result.errors = ModifiedResponse::parse(std::move(member.second)); + continue; + } + } + } + + return result; +} + +} // namespace client + +namespace proxy::client::query::relayQuery { const std::string& GetOperationName() noexcept { @@ -62,17 +153,307 @@ const std::string& GetOperationName() noexcept response::Value serializeVariables(Variables&& variables) { + using namespace graphql::client; + response::Value result { response::Type::Map }; - result.emplace_back(R"js(query)js"s, ModifiedVariable::serialize(std::move(variables.query))); - result.emplace_back(R"js(operationName)js"s, ModifiedVariable::serialize(std::move(variables.operationName))); - result.emplace_back(R"js(variables)js"s, ModifiedVariable::serialize(std::move(variables.variables))); + result.emplace_back(R"js(input)js"s, ModifiedVariable::serialize(std::move(variables.input))); return result; } +struct ResponseVisitor::impl +{ + enum class VisitorState + { + Start, + Member_relay, + Member_relay_data, + Member_relay_errors, + Member_relay_errors_0, + Member_relay_errors_0_, + Complete, + }; + + VisitorState state { VisitorState::Start }; + Response response {}; +}; + +ResponseVisitor::ResponseVisitor() noexcept + : _pimpl { std::make_unique() } +{ +} + +ResponseVisitor::~ResponseVisitor() +{ +} + +void ResponseVisitor::add_value([[maybe_unused]] std::shared_ptr&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Member_relay: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.relay = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_relay_data: + _pimpl->state = impl::VisitorState::Member_relay; + _pimpl->response.relay.data = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_relay_errors_0: + _pimpl->response.relay.errors->push_back(ModifiedResponse::parse(response::Value { *value })); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::reserve([[maybe_unused]] std::size_t count) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_relay_errors_0: + _pimpl->response.relay.errors->reserve(count); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_member([[maybe_unused]] std::string&& key) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Start: + if (key == "relay"sv) + { + _pimpl->state = impl::VisitorState::Member_relay; + } + break; + + case impl::VisitorState::Member_relay: + if (key == "data"sv) + { + _pimpl->state = impl::VisitorState::Member_relay_data; + } + else if (key == "errors"sv) + { + _pimpl->state = impl::VisitorState::Member_relay_errors; + } + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_relay: + _pimpl->state = impl::VisitorState::Start; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_relay_errors: + _pimpl->state = impl::VisitorState::Member_relay_errors_0; + _pimpl->response.relay.errors = std::make_optional>>({}); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_relay_errors_0: + _pimpl->state = impl::VisitorState::Member_relay; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_null() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_relay_data: + _pimpl->state = impl::VisitorState::Member_relay; + _pimpl->response.relay.data = std::nullopt; + break; + + case impl::VisitorState::Member_relay_errors_0: + _pimpl->response.relay.errors->push_back(std::nullopt); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_string([[maybe_unused]] std::string&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_relay_data: + _pimpl->state = impl::VisitorState::Member_relay; + _pimpl->response.relay.data = std::move(value); + break; + + case impl::VisitorState::Member_relay_errors_0: + _pimpl->response.relay.errors->push_back(std::move(value)); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_enum([[maybe_unused]] std::string&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_id([[maybe_unused]] response::IdType&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_bool([[maybe_unused]] bool value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_int([[maybe_unused]] int value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_float([[maybe_unused]] double value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::complete() +{ + _pimpl->state = impl::VisitorState::Complete; +} + +Response ResponseVisitor::response() +{ + Response response {}; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + _pimpl->state = impl::VisitorState::Start; + std::swap(_pimpl->response, response); + break; + + default: + break; + } + + return response; +} + Response parseResponse(response::Value&& response) { + using namespace graphql::client; + Response result; if (response.type() == response::Type::Map) @@ -83,7 +464,7 @@ Response parseResponse(response::Value&& response) { if (member.first == R"js(relay)js"sv) { - result.relay = ModifiedResponse::parse(std::move(member.second)); + result.relay = ModifiedResponse::parse(std::move(member.second)); continue; } } @@ -94,12 +475,12 @@ Response parseResponse(response::Value&& response) [[nodiscard("unnecessary call")]] const std::string& Traits::GetRequestText() noexcept { - return proxy::GetRequestText(); + return client::GetRequestText(); } [[nodiscard("unnecessary call")]] const peg::ast& Traits::GetRequestObject() noexcept { - return proxy::GetRequestObject(); + return client::GetRequestObject(); } [[nodiscard("unnecessary call")]] const std::string& Traits::GetOperationName() noexcept @@ -117,5 +498,5 @@ Response parseResponse(response::Value&& response) return relayQuery::parseResponse(std::move(response)); } -} // namespace query::relayQuery -} // namespace graphql::client +} // namespace proxy::client::query::relayQuery +} // namespace graphql diff --git a/samples/proxy/query/ProxyClient.h b/samples/proxy/query/ProxyClient.h index 73b15dee..01c01ca4 100644 --- a/samples/proxy/query/ProxyClient.h +++ b/samples/proxy/query/ProxyClient.h @@ -14,28 +14,31 @@ #include "graphqlservice/internal/Version.h" -// Check if the library version is compatible with clientgen 4.5.0 -static_assert(graphql::internal::MajorVersion == 4, "regenerate with clientgen: major version mismatch"); -static_assert(graphql::internal::MinorVersion == 5, "regenerate with clientgen: minor version mismatch"); +#include "ProxySharedTypes.h" #include #include #include -namespace graphql::client { +// Check if the library version is compatible with clientgen 5.0.0 +static_assert(graphql::internal::MajorVersion == 5, "regenerate with clientgen: major version mismatch"); +static_assert(graphql::internal::MinorVersion == 0, "regenerate with clientgen: minor version mismatch"); -/// -/// Operation: query relayQuery -/// -/// +namespace graphql::proxy { + +/// # Operation: query relayQuery +/// ```graphql /// # Copyright (c) Microsoft Corporation. All rights reserved. /// # Licensed under the MIT License. /// -/// query relayQuery($query: String!, $operationName: String, $variables: String) { -/// relay(query: $query, operationName: $operationName, variables: $variables) +/// query relayQuery($input: QueryInput!) { +/// relay(input: $input) { +/// data +/// errors +/// } /// } -/// -namespace proxy { +/// ``` +namespace client { // Return the original text of the request document. [[nodiscard("unnecessary call")]] const std::string& GetRequestText() noexcept; @@ -43,28 +46,65 @@ namespace proxy { // Return a pre-parsed, pre-validated request object. [[nodiscard("unnecessary call")]] const peg::ast& GetRequestObject() noexcept; -} // namespace proxy - namespace query::relayQuery { -using proxy::GetRequestText; -using proxy::GetRequestObject; +using graphql::proxy::client::GetRequestText; +using graphql::proxy::client::GetRequestObject; // Return the name of this operation in the shared request document. [[nodiscard("unnecessary call")]] const std::string& GetOperationName() noexcept; +using graphql::proxy::OperationType; + +using graphql::proxy::QueryInput; + struct [[nodiscard("unnecessary construction")]] Variables { - std::string query {}; - std::optional operationName {}; - std::optional variables {}; + QueryInput input {}; }; [[nodiscard("unnecessary conversion")]] response::Value serializeVariables(Variables&& variables); struct [[nodiscard("unnecessary construction")]] Response { - std::optional relay {}; + struct [[nodiscard("unnecessary construction")]] relay_QueryResults + { + std::optional data {}; + std::optional>> errors {}; + }; + + relay_QueryResults relay {}; +}; + +class ResponseVisitor + : public std::enable_shared_from_this +{ +public: + ResponseVisitor() noexcept; + ~ResponseVisitor(); + + void add_value(std::shared_ptr&&); + void reserve(std::size_t count); + void start_object(); + void add_member(std::string&& key); + void end_object(); + void start_array(); + void end_array(); + void add_null(); + void add_string(std::string&& value); + void add_enum(std::string&& value); + void add_id(response::IdType&& value); + void add_bool(bool value); + void add_int(int value); + void add_float(double value); + void complete(); + + Response response(); + +private: + struct impl; + + std::unique_ptr _pimpl; }; [[nodiscard("unnecessary conversion")]] Response parseResponse(response::Value&& response); @@ -80,11 +120,13 @@ struct Traits [[nodiscard("unnecessary conversion")]] static response::Value serializeVariables(Variables&& variables); using Response = relayQuery::Response; + using ResponseVisitor = relayQuery::ResponseVisitor; [[nodiscard("unnecessary conversion")]] static Response parseResponse(response::Value&& response); }; } // namespace query::relayQuery -} // namespace graphql::client +} // namespace client +} // namespace graphql::proxy #endif // PROXYCLIENT_H diff --git a/samples/proxy/query/ProxyClient.ixx b/samples/proxy/query/ProxyClient.ixx new file mode 100644 index 00000000..51560939 --- /dev/null +++ b/samples/proxy/query/ProxyClient.ixx @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "ProxyClient.h" + +export module GraphQL.Proxy.ProxyClient; + +export namespace graphql::proxy { + +namespace client { + +using client::GetRequestText; +using client::GetRequestObject; + +} // namespace client + +using proxy::OperationType; + +using proxy::QueryInput; + +namespace client { + +namespace query::relayQuery { + +using graphql::proxy::client::GetRequestText; +using graphql::proxy::client::GetRequestObject; +using relayQuery::GetOperationName; + +using graphql::proxy::OperationType; + +using graphql::proxy::QueryInput; + +using relayQuery::Variables; +using relayQuery::serializeVariables; + +using relayQuery::Response; +using relayQuery::ResponseVisitor; +using relayQuery::parseResponse; + +using relayQuery::Traits; + +} // namespace query::relayQuery + +} // namespace client +} // namespace graphql::proxy diff --git a/samples/proxy/query/query.graphql b/samples/proxy/query/query.graphql index 9d75a165..63567f4b 100644 --- a/samples/proxy/query/query.graphql +++ b/samples/proxy/query/query.graphql @@ -1,6 +1,9 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -query relayQuery($query: String!, $operationName: String, $variables: String) { - relay(query: $query, operationName: $operationName, variables: $variables) +query relayQuery($input: QueryInput!) { + relay(input: $input) { + data + errors + } } diff --git a/samples/proxy/schema/CMakeLists.txt b/samples/proxy/schema/CMakeLists.txt index 03912cd3..bb9a16a1 100644 --- a/samples/proxy/schema/CMakeLists.txt +++ b/samples/proxy/schema/CMakeLists.txt @@ -1,7 +1,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.28) # Normally this would be handled by find_package(cppgraphqlgen CONFIG). include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/cppgraphqlgen-functions.cmake) diff --git a/samples/proxy/schema/ProxySchema.cpp b/samples/proxy/schema/ProxySchema.cpp index 86781b95..5bb1337b 100644 --- a/samples/proxy/schema/ProxySchema.cpp +++ b/samples/proxy/schema/ProxySchema.cpp @@ -11,8 +11,8 @@ #include #include +#include #include -#include #include #include #include @@ -20,8 +20,7 @@ using namespace std::literals; -namespace graphql { -namespace proxy { +namespace graphql::proxy { Operations::Operations(std::shared_ptr query) : service::Request({ @@ -33,10 +32,31 @@ Operations::Operations(std::shared_ptr query) void AddTypesToSchema(const std::shared_ptr& schema) { + auto typeOperationType = schema::EnumType::Make(R"gql(OperationType)gql"sv, R"md()md"sv); + schema->AddType(R"gql(OperationType)gql"sv, typeOperationType); + auto typeQueryInput = schema::InputObjectType::Make(R"gql(QueryInput)gql"sv, R"md()md"sv); + schema->AddType(R"gql(QueryInput)gql"sv, typeQueryInput); auto typeQuery = schema::ObjectType::Make(R"gql(Query)gql"sv, R"md()md"sv); schema->AddType(R"gql(Query)gql"sv, typeQuery); + auto typeQueryResults = schema::ObjectType::Make(R"gql(QueryResults)gql"sv, R"md()md"sv); + schema->AddType(R"gql(QueryResults)gql"sv, typeQueryResults); + + static const auto s_namesOperationType = getOperationTypeNames(); + typeOperationType->AddEnumValues({ + { s_namesOperationType[static_cast(proxy::OperationType::QUERY)], R"md()md"sv, std::nullopt }, + { s_namesOperationType[static_cast(proxy::OperationType::MUTATION)], R"md()md"sv, std::nullopt }, + { s_namesOperationType[static_cast(proxy::OperationType::SUBSCRIPTION)], R"md()md"sv, std::nullopt } + }); + + typeQueryInput->AddInputValues({ + schema::InputValue::Make(R"gql(type)gql"sv, R"md()md"sv, schema->WrapType(introspection::TypeKind::NON_NULL, schema->LookupType(R"gql(OperationType)gql"sv)), R"gql()gql"sv), + schema::InputValue::Make(R"gql(query)gql"sv, R"md()md"sv, schema->WrapType(introspection::TypeKind::NON_NULL, schema->LookupType(R"gql(String)gql"sv)), R"gql()gql"sv), + schema::InputValue::Make(R"gql(operationName)gql"sv, R"md()md"sv, schema->LookupType(R"gql(String)gql"sv), R"gql(null)gql"sv), + schema::InputValue::Make(R"gql(variables)gql"sv, R"md()md"sv, schema->LookupType(R"gql(String)gql"sv), R"gql(null)gql"sv) + }); AddQueryDetails(typeQuery, schema); + AddQueryResultsDetails(typeQueryResults, schema); schema->AddQueryType(typeQuery); } @@ -57,5 +77,4 @@ std::shared_ptr GetSchema() return schema; } -} // namespace proxy -} // namespace graphql +} // namespace graphql::proxy diff --git a/samples/proxy/schema/ProxySchema.h b/samples/proxy/schema/ProxySchema.h index c1d565d3..bbaa0e6c 100644 --- a/samples/proxy/schema/ProxySchema.h +++ b/samples/proxy/schema/ProxySchema.h @@ -8,22 +8,28 @@ #ifndef PROXYSCHEMA_H #define PROXYSCHEMA_H +#include "graphqlservice/GraphQLResponse.h" +#include "graphqlservice/GraphQLService.h" + +#include "graphqlservice/internal/Version.h" #include "graphqlservice/internal/Schema.h" -// Check if the library version is compatible with schemagen 4.5.0 -static_assert(graphql::internal::MajorVersion == 4, "regenerate with schemagen: major version mismatch"); -static_assert(graphql::internal::MinorVersion == 5, "regenerate with schemagen: minor version mismatch"); +#include "ProxySharedTypes.h" #include #include #include #include -namespace graphql { -namespace proxy { +// Check if the library version is compatible with schemagen 5.0.0 +static_assert(graphql::internal::MajorVersion == 5, "regenerate with schemagen: major version mismatch"); +static_assert(graphql::internal::MinorVersion == 0, "regenerate with schemagen: minor version mismatch"); + +namespace graphql::proxy { namespace object { class Query; +class QueryResults; } // namespace object @@ -46,10 +52,10 @@ class [[nodiscard("unnecessary construction")]] Operations final }; void AddQueryDetails(const std::shared_ptr& typeQuery, const std::shared_ptr& schema); +void AddQueryResultsDetails(const std::shared_ptr& typeQueryResults, const std::shared_ptr& schema); std::shared_ptr GetSchema(); -} // namespace proxy -} // namespace graphql +} // namespace graphql::proxy #endif // PROXYSCHEMA_H diff --git a/samples/proxy/schema/ProxySchema.ixx b/samples/proxy/schema/ProxySchema.ixx new file mode 100644 index 00000000..6e5893e3 --- /dev/null +++ b/samples/proxy/schema/ProxySchema.ixx @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "ProxySchema.h" + +export module GraphQL.Proxy.ProxySchema; + +export import GraphQL.Proxy.ProxySharedTypes; + +export import GraphQL.Proxy.QueryObject; +export import GraphQL.Proxy.QueryResultsObject; + +export namespace graphql::proxy { + +using proxy::Operations; + +using proxy::AddQueryDetails; +using proxy::AddQueryResultsDetails; + +using proxy::GetSchema; + +} // namespace graphql::proxy diff --git a/samples/proxy/schema/ProxySharedTypes.cpp b/samples/proxy/schema/ProxySharedTypes.cpp new file mode 100644 index 00000000..e29d62da --- /dev/null +++ b/samples/proxy/schema/ProxySharedTypes.cpp @@ -0,0 +1,165 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +#include "graphqlservice/GraphQLService.h" + +#include "ProxySharedTypes.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std::literals; + +namespace graphql { +namespace service { + +static const auto s_namesOperationType = proxy::getOperationTypeNames(); +static const auto s_valuesOperationType = proxy::getOperationTypeValues(); + +template <> +proxy::OperationType Argument::convert(const response::Value& value) +{ + if (!value.maybe_enum()) + { + throw service::schema_exception { { R"ex(not a valid OperationType value)ex" } }; + } + + const auto result = internal::sorted_map_lookup( + s_valuesOperationType, + std::string_view { value.get() }); + + if (!result) + { + throw service::schema_exception { { R"ex(not a valid OperationType value)ex" } }; + } + + return *result; +} + +template <> +service::AwaitableResolver Result::convert(service::AwaitableScalar result, ResolverParams&& params) +{ + return ModifiedResult::resolve(std::move(result), std::move(params), + [](proxy::OperationType value, const ResolverParams&) + { + const auto idx = static_cast(value); + + if (idx >= s_namesOperationType.size()) + { + throw service::schema_exception { { R"ex(Enum value out of range for OperationType)ex" } }; + } + + return ResolverResult { { response::ValueToken::EnumValue { std::string { s_namesOperationType[idx] } } } }; + }); +} + +template <> +void Result::validateScalar(const response::Value& value) +{ + if (!value.maybe_enum()) + { + throw service::schema_exception { { R"ex(not a valid OperationType value)ex" } }; + } + + const auto [itr, itrEnd] = internal::sorted_map_equal_range( + s_valuesOperationType.begin(), + s_valuesOperationType.end(), + std::string_view { value.get() }); + + if (itr == itrEnd) + { + throw service::schema_exception { { R"ex(not a valid OperationType value)ex" } }; + } +} + +template <> +proxy::QueryInput Argument::convert(const response::Value& value) +{ + auto valueType = service::ModifiedArgument::require("type", value); + auto valueQuery = service::ModifiedArgument::require("query", value); + auto valueOperationName = service::ModifiedArgument::require("operationName", value); + auto valueVariables = service::ModifiedArgument::require("variables", value); + + return proxy::QueryInput { + std::move(valueType), + std::move(valueQuery), + std::move(valueOperationName), + std::move(valueVariables) + }; +} + +} // namespace service + +namespace proxy { + +QueryInput::QueryInput() noexcept + : type {} + , query {} + , operationName {} + , variables {} +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +QueryInput::QueryInput( + OperationType typeArg, + std::string queryArg, + std::optional operationNameArg, + std::optional variablesArg) noexcept + : type { std::move(typeArg) } + , query { std::move(queryArg) } + , operationName { std::move(operationNameArg) } + , variables { std::move(variablesArg) } +{ +} + +QueryInput::QueryInput(const QueryInput& other) + : type { service::ModifiedArgument::duplicate(other.type) } + , query { service::ModifiedArgument::duplicate(other.query) } + , operationName { service::ModifiedArgument::duplicate(other.operationName) } + , variables { service::ModifiedArgument::duplicate(other.variables) } +{ +} + +QueryInput::QueryInput(QueryInput&& other) noexcept + : type { std::move(other.type) } + , query { std::move(other.query) } + , operationName { std::move(other.operationName) } + , variables { std::move(other.variables) } +{ +} + +QueryInput::~QueryInput() +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +QueryInput& QueryInput::operator=(const QueryInput& other) +{ + QueryInput value { other }; + + std::swap(*this, value); + + return *this; +} + +QueryInput& QueryInput::operator=(QueryInput&& other) noexcept +{ + type = std::move(other.type); + query = std::move(other.query); + operationName = std::move(other.operationName); + variables = std::move(other.variables); + + return *this; +} + +} // namespace proxy +} // namespace graphql diff --git a/samples/proxy/schema/ProxySharedTypes.h b/samples/proxy/schema/ProxySharedTypes.h new file mode 100644 index 00000000..56ec1fa1 --- /dev/null +++ b/samples/proxy/schema/ProxySharedTypes.h @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +#pragma once + +#ifndef PROXYSHAREDTYPES_H +#define PROXYSHAREDTYPES_H + +#include "graphqlservice/GraphQLResponse.h" + +#include "graphqlservice/internal/Version.h" + +#include +#include +#include +#include +#include +#include + +// Check if the library version is compatible with schemagen 5.0.0 +static_assert(graphql::internal::MajorVersion == 5, "regenerate with schemagen: major version mismatch"); +static_assert(graphql::internal::MinorVersion == 0, "regenerate with schemagen: minor version mismatch"); + +namespace graphql { +namespace proxy { + +enum class [[nodiscard("unnecessary conversion")]] OperationType +{ + QUERY, + MUTATION, + SUBSCRIPTION +}; + +[[nodiscard("unnecessary call")]] constexpr auto getOperationTypeNames() noexcept +{ + using namespace std::literals; + + return std::array { + R"gql(QUERY)gql"sv, + R"gql(MUTATION)gql"sv, + R"gql(SUBSCRIPTION)gql"sv + }; +} + +[[nodiscard("unnecessary call")]] constexpr auto getOperationTypeValues() noexcept +{ + using namespace std::literals; + + return std::array, 3> { + std::make_pair(R"gql(QUERY)gql"sv, OperationType::QUERY), + std::make_pair(R"gql(MUTATION)gql"sv, OperationType::MUTATION), + std::make_pair(R"gql(SUBSCRIPTION)gql"sv, OperationType::SUBSCRIPTION) + }; +} + +struct [[nodiscard("unnecessary construction")]] QueryInput +{ + explicit QueryInput() noexcept; + explicit QueryInput( + OperationType typeArg, + std::string queryArg, + std::optional operationNameArg, + std::optional variablesArg) noexcept; + QueryInput(const QueryInput& other); + QueryInput(QueryInput&& other) noexcept; + ~QueryInput(); + + QueryInput& operator=(const QueryInput& other); + QueryInput& operator=(QueryInput&& other) noexcept; + + OperationType type; + std::string query; + std::optional operationName; + std::optional variables; +}; + +} // namespace proxy +} // namespace graphql + +#endif // PROXYSHAREDTYPES_H diff --git a/samples/proxy/schema/ProxySharedTypes.ixx b/samples/proxy/schema/ProxySharedTypes.ixx new file mode 100644 index 00000000..75dd847d --- /dev/null +++ b/samples/proxy/schema/ProxySharedTypes.ixx @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "ProxySharedTypes.h" + +export module GraphQL.Proxy.ProxySharedTypes; + +export namespace graphql::proxy { + +using proxy::OperationType; +using proxy::getOperationTypeNames; +using proxy::getOperationTypeValues; + +using proxy::QueryInput; + +} // namespace graphql::proxy diff --git a/samples/proxy/schema/QueryObject.cpp b/samples/proxy/schema/QueryObject.cpp index f8989a70..cdfc3817 100644 --- a/samples/proxy/schema/QueryObject.cpp +++ b/samples/proxy/schema/QueryObject.cpp @@ -4,6 +4,7 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. #include "QueryObject.h" +#include "QueryResultsObject.h" #include "graphqlservice/internal/Introspection.h" @@ -12,7 +13,6 @@ #include #include -#include #include #include @@ -57,16 +57,14 @@ void Query::endSelectionSet(const service::SelectionSetParams& params) const service::AwaitableResolver Query::resolveRelay(service::ResolverParams&& params) const { - auto argQuery = service::ModifiedArgument::require("query", params.arguments); - auto argOperationName = service::ModifiedArgument::require("operationName", params.arguments); - auto argVariables = service::ModifiedArgument::require("variables", params.arguments); + auto argInput = service::ModifiedArgument::require("input", params.arguments); std::unique_lock resolverLock(_resolverMutex); service::SelectionSetParams selectionSetParams { static_cast(params) }; auto directives = std::move(params.fieldDirectives); - auto result = _pimpl->getRelay(service::FieldParams { std::move(selectionSetParams), std::move(directives) }, std::move(argQuery), std::move(argOperationName), std::move(argVariables)); + auto result = _pimpl->getRelay(service::FieldParams { std::move(selectionSetParams), std::move(directives) }, std::move(argInput)); resolverLock.unlock(); - return service::ModifiedResult::convert(std::move(result), std::move(params)); + return service::ModifiedResult::convert(std::move(result), std::move(params)); } service::AwaitableResolver Query::resolve_typename(service::ResolverParams&& params) const @@ -93,10 +91,8 @@ service::AwaitableResolver Query::resolve_type(service::ResolverParams&& params) void AddQueryDetails(const std::shared_ptr& typeQuery, const std::shared_ptr& schema) { typeQuery->AddFields({ - schema::Field::Make(R"gql(relay)gql"sv, R"md()md"sv, std::nullopt, schema->LookupType(R"gql(String)gql"sv), { - schema::InputValue::Make(R"gql(query)gql"sv, R"md()md"sv, schema->WrapType(introspection::TypeKind::NON_NULL, schema->LookupType(R"gql(String)gql"sv)), R"gql()gql"sv), - schema::InputValue::Make(R"gql(operationName)gql"sv, R"md()md"sv, schema->LookupType(R"gql(String)gql"sv), R"gql()gql"sv), - schema::InputValue::Make(R"gql(variables)gql"sv, R"md()md"sv, schema->LookupType(R"gql(String)gql"sv), R"gql()gql"sv) + schema::Field::Make(R"gql(relay)gql"sv, R"md()md"sv, std::nullopt, schema->WrapType(introspection::TypeKind::NON_NULL, schema->LookupType(R"gql(QueryResults)gql"sv)), { + schema::InputValue::Make(R"gql(input)gql"sv, R"md()md"sv, schema->WrapType(introspection::TypeKind::NON_NULL, schema->LookupType(R"gql(QueryInput)gql"sv)), R"gql()gql"sv) }) }); } diff --git a/samples/proxy/schema/QueryObject.h b/samples/proxy/schema/QueryObject.h index f8973f09..c93e5f75 100644 --- a/samples/proxy/schema/QueryObject.h +++ b/samples/proxy/schema/QueryObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef QUERYOBJECT_H -#define QUERYOBJECT_H +#ifndef PROXY_QUERYOBJECT_H +#define PROXY_QUERYOBJECT_H #include "ProxySchema.h" @@ -14,15 +14,15 @@ namespace graphql::proxy::object { namespace methods::QueryHas { template -concept getRelayWithParams = requires (TImpl impl, service::FieldParams params, std::string queryArg, std::optional operationNameArg, std::optional variablesArg) +concept getRelayWithParams = requires (TImpl impl, service::FieldParams params, QueryInput inputArg) { - { service::AwaitableScalar> { impl.getRelay(std::move(params), std::move(queryArg), std::move(operationNameArg), std::move(variablesArg)) } }; + { service::AwaitableObject> { impl.getRelay(std::move(params), std::move(inputArg)) } }; }; template -concept getRelay = requires (TImpl impl, std::string queryArg, std::optional operationNameArg, std::optional variablesArg) +concept getRelay = requires (TImpl impl, QueryInput inputArg) { - { service::AwaitableScalar> { impl.getRelay(std::move(queryArg), std::move(operationNameArg), std::move(variablesArg)) } }; + { service::AwaitableObject> { impl.getRelay(std::move(inputArg)) } }; }; template @@ -58,7 +58,7 @@ class [[nodiscard("unnecessary construction")]] Query final virtual void beginSelectionSet(const service::SelectionSetParams& params) const = 0; virtual void endSelectionSet(const service::SelectionSetParams& params) const = 0; - [[nodiscard("unnecessary call")]] virtual service::AwaitableScalar> getRelay(service::FieldParams&& params, std::string&& queryArg, std::optional&& operationNameArg, std::optional&& variablesArg) const = 0; + [[nodiscard("unnecessary call")]] virtual service::AwaitableObject> getRelay(service::FieldParams&& params, QueryInput&& inputArg) const = 0; }; template @@ -70,16 +70,16 @@ class [[nodiscard("unnecessary construction")]] Query final { } - [[nodiscard("unnecessary call")]] service::AwaitableScalar> getRelay(service::FieldParams&& params, std::string&& queryArg, std::optional&& operationNameArg, std::optional&& variablesArg) const override + [[nodiscard("unnecessary call")]] service::AwaitableObject> getRelay(service::FieldParams&& params, QueryInput&& inputArg) const override { if constexpr (methods::QueryHas::getRelayWithParams) { - return { _pimpl->getRelay(std::move(params), std::move(queryArg), std::move(operationNameArg), std::move(variablesArg)) }; + return { _pimpl->getRelay(std::move(params), std::move(inputArg)) }; } else { static_assert(methods::QueryHas::getRelay, R"msg(Query::getRelay is not implemented)msg"); - return { _pimpl->getRelay(std::move(queryArg), std::move(operationNameArg), std::move(variablesArg)) }; + return { _pimpl->getRelay(std::move(inputArg)) }; } } @@ -128,4 +128,4 @@ class [[nodiscard("unnecessary construction")]] Query final } // namespace graphql::proxy::object -#endif // QUERYOBJECT_H +#endif // PROXY_QUERYOBJECT_H diff --git a/samples/proxy/schema/QueryObject.ixx b/samples/proxy/schema/QueryObject.ixx new file mode 100644 index 00000000..fcc61c1d --- /dev/null +++ b/samples/proxy/schema/QueryObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "QueryObject.h" + +export module GraphQL.Proxy.QueryObject; + +export namespace graphql::proxy::object { + +using object::Query; + +} // namespace graphql::proxy::object diff --git a/samples/proxy/schema/QueryResultsObject.cpp b/samples/proxy/schema/QueryResultsObject.cpp new file mode 100644 index 00000000..80d2e5f0 --- /dev/null +++ b/samples/proxy/schema/QueryResultsObject.cpp @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +#include "QueryResultsObject.h" + +#include "graphqlservice/internal/Schema.h" + +#include "graphqlservice/introspection/IntrospectionSchema.h" + +#include +#include +#include +#include + +using namespace std::literals; + +namespace graphql::proxy { +namespace object { + +QueryResults::QueryResults(std::unique_ptr pimpl) noexcept + : service::Object{ getTypeNames(), getResolvers() } + , _pimpl { std::move(pimpl) } +{ +} + +service::TypeNames QueryResults::getTypeNames() const noexcept +{ + return { + R"gql(QueryResults)gql"sv + }; +} + +service::ResolverMap QueryResults::getResolvers() const noexcept +{ + return { + { R"gql(data)gql"sv, [this](service::ResolverParams&& params) { return resolveData(std::move(params)); } }, + { R"gql(errors)gql"sv, [this](service::ResolverParams&& params) { return resolveErrors(std::move(params)); } }, + { R"gql(__typename)gql"sv, [this](service::ResolverParams&& params) { return resolve_typename(std::move(params)); } } + }; +} + +void QueryResults::beginSelectionSet(const service::SelectionSetParams& params) const +{ + _pimpl->beginSelectionSet(params); +} + +void QueryResults::endSelectionSet(const service::SelectionSetParams& params) const +{ + _pimpl->endSelectionSet(params); +} + +service::AwaitableResolver QueryResults::resolveData(service::ResolverParams&& params) const +{ + std::unique_lock resolverLock(_resolverMutex); + service::SelectionSetParams selectionSetParams { static_cast(params) }; + auto directives = std::move(params.fieldDirectives); + auto result = _pimpl->getData(service::FieldParams { std::move(selectionSetParams), std::move(directives) }); + resolverLock.unlock(); + + return service::ModifiedResult::convert(std::move(result), std::move(params)); +} + +service::AwaitableResolver QueryResults::resolveErrors(service::ResolverParams&& params) const +{ + std::unique_lock resolverLock(_resolverMutex); + service::SelectionSetParams selectionSetParams { static_cast(params) }; + auto directives = std::move(params.fieldDirectives); + auto result = _pimpl->getErrors(service::FieldParams { std::move(selectionSetParams), std::move(directives) }); + resolverLock.unlock(); + + return service::ModifiedResult::convert(std::move(result), std::move(params)); +} + +service::AwaitableResolver QueryResults::resolve_typename(service::ResolverParams&& params) const +{ + return service::Result::convert(std::string{ R"gql(QueryResults)gql" }, std::move(params)); +} + +} // namespace object + +void AddQueryResultsDetails(const std::shared_ptr& typeQueryResults, const std::shared_ptr& schema) +{ + typeQueryResults->AddFields({ + schema::Field::Make(R"gql(data)gql"sv, R"md()md"sv, std::nullopt, schema->LookupType(R"gql(String)gql"sv)), + schema::Field::Make(R"gql(errors)gql"sv, R"md()md"sv, std::nullopt, schema->WrapType(introspection::TypeKind::LIST, schema->LookupType(R"gql(String)gql"sv))) + }); +} + +} // namespace graphql::proxy diff --git a/samples/proxy/schema/QueryResultsObject.h b/samples/proxy/schema/QueryResultsObject.h new file mode 100644 index 00000000..2649a572 --- /dev/null +++ b/samples/proxy/schema/QueryResultsObject.h @@ -0,0 +1,154 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +#pragma once + +#ifndef PROXY_QUERYRESULTSOBJECT_H +#define PROXY_QUERYRESULTSOBJECT_H + +#include "ProxySchema.h" + +namespace graphql::proxy::object { +namespace methods::QueryResultsHas { + +template +concept getDataWithParams = requires (TImpl impl, service::FieldParams params) +{ + { service::AwaitableScalar> { impl.getData(std::move(params)) } }; +}; + +template +concept getData = requires (TImpl impl) +{ + { service::AwaitableScalar> { impl.getData() } }; +}; + +template +concept getErrorsWithParams = requires (TImpl impl, service::FieldParams params) +{ + { service::AwaitableScalar>>> { impl.getErrors(std::move(params)) } }; +}; + +template +concept getErrors = requires (TImpl impl) +{ + { service::AwaitableScalar>>> { impl.getErrors() } }; +}; + +template +concept beginSelectionSet = requires (TImpl impl, const service::SelectionSetParams params) +{ + { impl.beginSelectionSet(params) }; +}; + +template +concept endSelectionSet = requires (TImpl impl, const service::SelectionSetParams params) +{ + { impl.endSelectionSet(params) }; +}; + +} // namespace methods::QueryResultsHas + +class [[nodiscard("unnecessary construction")]] QueryResults final + : public service::Object +{ +private: + [[nodiscard("unnecessary call")]] service::AwaitableResolver resolveData(service::ResolverParams&& params) const; + [[nodiscard("unnecessary call")]] service::AwaitableResolver resolveErrors(service::ResolverParams&& params) const; + + [[nodiscard("unnecessary call")]] service::AwaitableResolver resolve_typename(service::ResolverParams&& params) const; + + struct [[nodiscard("unnecessary construction")]] Concept + { + virtual ~Concept() = default; + + virtual void beginSelectionSet(const service::SelectionSetParams& params) const = 0; + virtual void endSelectionSet(const service::SelectionSetParams& params) const = 0; + + [[nodiscard("unnecessary call")]] virtual service::AwaitableScalar> getData(service::FieldParams&& params) const = 0; + [[nodiscard("unnecessary call")]] virtual service::AwaitableScalar>>> getErrors(service::FieldParams&& params) const = 0; + }; + + template + struct [[nodiscard("unnecessary construction")]] Model final + : Concept + { + explicit Model(std::shared_ptr pimpl) noexcept + : _pimpl { std::move(pimpl) } + { + } + + [[nodiscard("unnecessary call")]] service::AwaitableScalar> getData(service::FieldParams&& params) const override + { + if constexpr (methods::QueryResultsHas::getDataWithParams) + { + return { _pimpl->getData(std::move(params)) }; + } + else + { + static_assert(methods::QueryResultsHas::getData, R"msg(QueryResults::getData is not implemented)msg"); + return { _pimpl->getData() }; + } + } + + [[nodiscard("unnecessary call")]] service::AwaitableScalar>>> getErrors(service::FieldParams&& params) const override + { + if constexpr (methods::QueryResultsHas::getErrorsWithParams) + { + return { _pimpl->getErrors(std::move(params)) }; + } + else + { + static_assert(methods::QueryResultsHas::getErrors, R"msg(QueryResults::getErrors is not implemented)msg"); + return { _pimpl->getErrors() }; + } + } + + void beginSelectionSet(const service::SelectionSetParams& params) const override + { + if constexpr (methods::QueryResultsHas::beginSelectionSet) + { + _pimpl->beginSelectionSet(params); + } + } + + void endSelectionSet(const service::SelectionSetParams& params) const override + { + if constexpr (methods::QueryResultsHas::endSelectionSet) + { + _pimpl->endSelectionSet(params); + } + } + + private: + const std::shared_ptr _pimpl; + }; + + explicit QueryResults(std::unique_ptr pimpl) noexcept; + + [[nodiscard("unnecessary call")]] service::TypeNames getTypeNames() const noexcept; + [[nodiscard("unnecessary call")]] service::ResolverMap getResolvers() const noexcept; + + void beginSelectionSet(const service::SelectionSetParams& params) const override; + void endSelectionSet(const service::SelectionSetParams& params) const override; + + const std::unique_ptr _pimpl; + +public: + template + explicit QueryResults(std::shared_ptr pimpl) noexcept + : QueryResults { std::unique_ptr { std::make_unique>(std::move(pimpl)) } } + { + } + + [[nodiscard("unnecessary call")]] static constexpr std::string_view getObjectType() noexcept + { + return { R"gql(QueryResults)gql" }; + } +}; + +} // namespace graphql::proxy::object + +#endif // PROXY_QUERYRESULTSOBJECT_H diff --git a/samples/proxy/schema/QueryResultsObject.ixx b/samples/proxy/schema/QueryResultsObject.ixx new file mode 100644 index 00000000..27514748 --- /dev/null +++ b/samples/proxy/schema/QueryResultsObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "QueryResultsObject.h" + +export module GraphQL.Proxy.QueryResultsObject; + +export namespace graphql::proxy::object { + +using object::QueryResults; + +} // namespace graphql::proxy::object diff --git a/samples/proxy/schema/proxy_schema_files b/samples/proxy/schema/proxy_schema_files index dc325096..abca35d2 100644 --- a/samples/proxy/schema/proxy_schema_files +++ b/samples/proxy/schema/proxy_schema_files @@ -1,2 +1,4 @@ +ProxySharedTypes.cpp ProxySchema.cpp QueryObject.cpp +QueryResultsObject.cpp diff --git a/samples/proxy/schema/schema.graphql b/samples/proxy/schema/schema.graphql index 197569ae..02d9cde0 100644 --- a/samples/proxy/schema/schema.graphql +++ b/samples/proxy/schema/schema.graphql @@ -2,5 +2,23 @@ # Licensed under the MIT License. type Query { - relay(query: String!, operationName: String, variables: String): String + relay(input: QueryInput!): QueryResults! +} + +enum OperationType { + QUERY + MUTATION + SUBSCRIPTION +} + +input QueryInput { + type: OperationType! + query: String! + operationName: String = null + variables: String = null +} + +type QueryResults { + data: String + errors: [String] } diff --git a/samples/proxy/server.cpp b/samples/proxy/server.cpp index cdb6e35b..6f43556d 100644 --- a/samples/proxy/server.cpp +++ b/samples/proxy/server.cpp @@ -161,8 +161,8 @@ int main() auto variables = (variablesItr != payload.end() && variablesItr->second.type() == response::Type::String) - ? response::parseJSON(operationNameItr->second - .get()) + ? response::parseJSON( + variablesItr->second.get()) : response::Value {}; msg = http::response { http::status::ok, diff --git a/samples/stitched/CMakeLists.txt b/samples/stitched/CMakeLists.txt new file mode 100644 index 00000000..f2396571 --- /dev/null +++ b/samples/stitched/CMakeLists.txt @@ -0,0 +1,34 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +cmake_minimum_required(VERSION 3.28) + +add_library(stitchedschema STATIC + StitchedSchema.cpp) +target_link_libraries(stitchedschema PUBLIC star_wars todaygraphql) +target_include_directories(stitchedschema INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) + +add_executable(stitched sample.cpp) +target_link_libraries(stitched PRIVATE + stitchedschema + graphqljson) + +if(WIN32 AND BUILD_SHARED_LIBS) + add_custom_command(OUTPUT copied_sample_dlls + COMMAND ${CMAKE_COMMAND} -E copy_if_different + $ + $ + $ + $ + ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E touch copied_sample_dlls + DEPENDS + graphqlservice + graphqljson + graphqlpeg + graphqlresponse) + + add_custom_target(copy_stitched_sample_dlls DEPENDS copied_sample_dlls) + + add_dependencies(stitched copy_stitched_sample_dlls) +endif() diff --git a/samples/stitched/StitchedSchema.cpp b/samples/stitched/StitchedSchema.cpp new file mode 100644 index 00000000..7466e4ae --- /dev/null +++ b/samples/stitched/StitchedSchema.cpp @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "StitchedSchema.h" + +#include "StarWarsData.h" +#include "TodayMock.h" + +namespace graphql::stitched { + +std::shared_ptr GetService() +{ + return star_wars::GetService()->stitch(today::mock_service()->service); +} + +} // namespace graphql::stitched diff --git a/samples/stitched/StitchedSchema.h b/samples/stitched/StitchedSchema.h new file mode 100644 index 00000000..4603b05a --- /dev/null +++ b/samples/stitched/StitchedSchema.h @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +#ifndef STITCHEDSCHEMA_H +#define STITCHEDSCHEMA_H + +#include "graphqlservice/GraphQLService.h" + +#include "StarWarsSharedTypes.h" +#include "TodaySharedTypes.h" + +namespace graphql::stitched { + +std::shared_ptr GetService(); + +} // namespace graphql::stitched + +#endif // STITCHEDSCHEMA_H diff --git a/samples/stitched/sample.cpp b/samples/stitched/sample.cpp new file mode 100644 index 00000000..ca392726 --- /dev/null +++ b/samples/stitched/sample.cpp @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "StitchedSchema.h" + +#include "graphqlservice/JSONResponse.h" + +#include +#include +#include +#include + +using namespace graphql; + +int main(int argc, char** argv) +{ + auto service = stitched::GetService(); + + std::cout << "Created the service..." << std::endl; + + try + { + peg::ast query; + + if (argc > 1) + { + query = peg::parseFile(argv[1]); + } + else + { + std::istream_iterator start { std::cin >> std::noskipws }, end {}; + std::string input { start, end }; + + query = peg::parseString(std::move(input)); + } + + if (!query.root) + { + std::cerr << "Unknown error!" << std::endl; + std::cerr << std::endl; + return 1; + } + + std::cout << "Executing query..." << std::endl; + + std::cout << response::toJSON( + service->resolve({ query, ((argc > 2) ? argv[2] : "") }).get()) + << std::endl; + } + catch (const std::runtime_error& ex) + { + std::cerr << ex.what() << std::endl; + return 1; + } + + return 0; +} diff --git a/samples/today/CMakeLists.txt b/samples/today/CMakeLists.txt index 7c9eaa33..1ed024a7 100644 --- a/samples/today/CMakeLists.txt +++ b/samples/today/CMakeLists.txt @@ -1,19 +1,35 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.28) # todaygraphql add_subdirectory(schema) add_library(todaygraphql STATIC TodayMock.cpp) +target_compile_features(todaygraphql PUBLIC cxx_std_20) target_link_libraries(todaygraphql PUBLIC today_schema) -target_include_directories(todaygraphql PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_sources(todaygraphql PUBLIC FILE_SET HEADERS + BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} + FILES ${CMAKE_CURRENT_SOURCE_DIR}/TodayMock.h) +if(GRAPHQL_BUILD_MODULES) + target_sources(todaygraphql PUBLIC FILE_SET CXX_MODULES + BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} + FILES ${CMAKE_CURRENT_SOURCE_DIR}/TodayMock.ixx) +endif() # todaygraphql_nointrospection add_subdirectory(nointrospection) add_library(todaygraphql_nointrospection STATIC TodayMock.cpp) +target_compile_features(todaygraphql_nointrospection PUBLIC cxx_std_20) target_link_libraries(todaygraphql_nointrospection PUBLIC today_nointrospection_schema) -target_include_directories(todaygraphql_nointrospection PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_sources(todaygraphql_nointrospection PUBLIC FILE_SET HEADERS + BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} + FILES ${CMAKE_CURRENT_SOURCE_DIR}/TodayMock.h) +if(GRAPHQL_BUILD_MODULES) + target_sources(todaygraphql_nointrospection PUBLIC FILE_SET CXX_MODULES + BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} + FILES ${CMAKE_CURRENT_SOURCE_DIR}/TodayMock.ixx) +endif() if(MSVC) # warning C4702: unreachable code @@ -21,49 +37,51 @@ if(MSVC) target_compile_options(todaygraphql_nointrospection PUBLIC /wd4702) endif() -# sample -add_executable(sample sample.cpp) -target_link_libraries(sample PRIVATE - todaygraphql - graphqljson) - -# sample_nointrospection -add_executable(sample_nointrospection sample.cpp) -target_link_libraries(sample_nointrospection PRIVATE - todaygraphql_nointrospection - graphqljson) - -# benchmark -add_executable(benchmark benchmark.cpp) -target_link_libraries(benchmark PRIVATE - todaygraphql - graphqljson) - -# benchmark_nointrospection -add_executable(benchmark_nointrospection benchmark.cpp) -target_link_libraries(benchmark_nointrospection PRIVATE - todaygraphql_nointrospection - graphqljson) - -if(WIN32 AND BUILD_SHARED_LIBS) - add_custom_command(OUTPUT copied_sample_dlls - COMMAND ${CMAKE_COMMAND} -E copy_if_different - $ - $ - $ - $ - ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E touch copied_sample_dlls - DEPENDS - graphqlservice - graphqljson - graphqlpeg - graphqlresponse) - - add_custom_target(copy_today_sample_dlls DEPENDS copied_sample_dlls) +if(GRAPHQL_BUILD_MODULES) + # sample + add_executable(sample sample.cpp) + target_link_libraries(sample PRIVATE + todaygraphql + graphqljson) - add_dependencies(sample copy_today_sample_dlls) - add_dependencies(sample_nointrospection copy_today_sample_dlls) - add_dependencies(benchmark copy_today_sample_dlls) - add_dependencies(benchmark_nointrospection copy_today_sample_dlls) + # sample_nointrospection + add_executable(sample_nointrospection sample.cpp) + target_link_libraries(sample_nointrospection PRIVATE + todaygraphql_nointrospection + graphqljson) + + # benchmark + add_executable(benchmark benchmark.cpp) + target_link_libraries(benchmark PRIVATE + todaygraphql + graphqljson) + + # benchmark_nointrospection + add_executable(benchmark_nointrospection benchmark.cpp) + target_link_libraries(benchmark_nointrospection PRIVATE + todaygraphql_nointrospection + graphqljson) + + if(WIN32 AND BUILD_SHARED_LIBS) + add_custom_command(OUTPUT copied_sample_dlls + COMMAND ${CMAKE_COMMAND} -E copy_if_different + $ + $ + $ + $ + ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E touch copied_sample_dlls + DEPENDS + graphqlservice + graphqljson + graphqlpeg + graphqlresponse) + + add_custom_target(copy_today_sample_dlls DEPENDS copied_sample_dlls) + + add_dependencies(sample copy_today_sample_dlls) + add_dependencies(sample_nointrospection copy_today_sample_dlls) + add_dependencies(benchmark copy_today_sample_dlls) + add_dependencies(benchmark_nointrospection copy_today_sample_dlls) + endif() endif() diff --git a/samples/today/TodayMock.cpp b/samples/today/TodayMock.cpp index 5e4a6ffd..c05834d7 100644 --- a/samples/today/TodayMock.cpp +++ b/samples/today/TodayMock.cpp @@ -3,19 +3,20 @@ #include "TodayMock.h" -#include "AppointmentConnectionObject.h" -#include "CompleteTaskPayloadObject.h" -#include "ExpensiveObject.h" -#include "FolderConnectionObject.h" -#include "NestedTypeObject.h" -#include "TaskConnectionObject.h" -#include "UnionTypeObject.h" +#include "TodayAppointmentConnectionObject.h" +#include "TodayCompleteTaskPayloadObject.h" +#include "TodayExpensiveObject.h" +#include "TodayFolderConnectionObject.h" +#include "TodayNestedTypeObject.h" +#include "TodayTaskConnectionObject.h" +#include "TodayUnionTypeObject.h" #include #include -#include +#include #include #include +#include namespace graphql::today { @@ -25,7 +26,7 @@ const response::IdType& getFakeAppointmentId() noexcept std::string_view fakeIdString { "fakeAppointmentId" }; response::IdType result(fakeIdString.size()); - std::copy(fakeIdString.cbegin(), fakeIdString.cend(), result.begin()); + std::ranges::copy(fakeIdString, result.begin()); return response::IdType { std::move(result) }; }(); @@ -39,7 +40,7 @@ const response::IdType& getFakeTaskId() noexcept std::string_view fakeIdString { "fakeTaskId" }; response::IdType result(fakeIdString.size()); - std::copy(fakeIdString.cbegin(), fakeIdString.cend(), result.begin()); + std::ranges::copy(fakeIdString, result.begin()); return response::IdType { std::move(result) }; }(); @@ -53,7 +54,7 @@ const response::IdType& getFakeFolderId() noexcept std::string_view fakeIdString { "fakeFolderId" }; response::IdType result(fakeIdString.size()); - std::copy(fakeIdString.cbegin(), fakeIdString.cend(), result.begin()); + std::ranges::copy(fakeIdString, result.begin()); return response::IdType { std::move(result) }; }(); @@ -61,52 +62,71 @@ const response::IdType& getFakeFolderId() noexcept return s_fakeId; } -std::unique_ptr mock_service() noexcept +std::shared_ptr mock_query(const std::shared_ptr& service) noexcept { - auto result = std::make_unique(); - - auto query = std::make_shared( - [mockService = result.get()]() -> std::vector> { - ++mockService->getAppointmentsCount; + return std::make_shared( + [weakService = std::weak_ptr { service }]() -> std::vector> { + if (auto mockService = weakService.lock()) + { + ++mockService->getAppointmentsCount; + } return { std::make_shared(response::IdType(getFakeAppointmentId()), "tomorrow", "Lunch?", false) }; }, - [mockService = result.get()]() -> std::vector> { - ++mockService->getTasksCount; + [weakService = std::weak_ptr { service }]() -> std::vector> { + if (auto mockService = weakService.lock()) + { + ++mockService->getTasksCount; + } return { std::make_shared(response::IdType(getFakeTaskId()), "Don't forget", true) }; }, - [mockService = result.get()]() -> std::vector> { - ++mockService->getUnreadCountsCount; + [weakService = std::weak_ptr { service }]() -> std::vector> { + if (auto mockService = weakService.lock()) + { + ++mockService->getUnreadCountsCount; + } return { std::make_shared(response::IdType(getFakeFolderId()), "\"Fake\" Inbox", 3) }; }); - auto mutation = std::make_shared( +} + +std::shared_ptr mock_mutation() noexcept +{ + return std::make_shared( [](CompleteTaskInput&& input) -> std::shared_ptr { return std::make_shared( std::make_shared(std::move(input.id), "Mutated Task!", *(input.isComplete)), std::move(input.clientMutationId)); }); - auto subscription = std::make_shared( +} + +std::shared_ptr mock_subscription() noexcept +{ + return std::make_shared( [](const std::shared_ptr&) -> std::shared_ptr { return { std::make_shared(response::IdType(getFakeAppointmentId()), "tomorrow", "Lunch?", true) }; }); +} - result->service = std::make_shared(std::move(query), - std::move(mutation), - std::move(subscription)); +std::shared_ptr mock_service() noexcept +{ + auto result = std::make_shared(); + + result->service = + std::make_shared(mock_query(result), mock_mutation(), mock_subscription()); return result; } -RequestState::RequestState(size_t id) +RequestState::RequestState(std::size_t id) : requestId(id) { } @@ -166,6 +186,11 @@ std::optional Appointment::getForceError() const throw std::runtime_error(R"ex(this error was forced)ex"); } +std::vector Appointment::getArray() const +{ + return {}; +} + AppointmentEdge::AppointmentEdge(std::shared_ptr appointment) : _appointment(std::move(appointment)) { @@ -199,8 +224,7 @@ std::optional>> Appointment auto result = std::make_optional>>( _appointments.size()); - std::transform(_appointments.cbegin(), - _appointments.cend(), + std::ranges::transform(_appointments, result->begin(), [](const std::shared_ptr& node) { return std::make_shared( @@ -269,12 +293,9 @@ std::optional>> TaskConnection::ge { auto result = std::make_optional>>(_tasks.size()); - std::transform(_tasks.cbegin(), - _tasks.cend(), - result->begin(), - [](const std::shared_ptr& node) { - return std::make_shared(std::make_shared(node)); - }); + std::ranges::transform(_tasks, result->begin(), [](const std::shared_ptr& node) { + return std::make_shared(std::make_shared(node)); + }); return result; } @@ -339,12 +360,9 @@ std::optional>> FolderConnection auto result = std::make_optional>>(_folders.size()); - std::transform(_folders.cbegin(), - _folders.cend(), - result->begin(), - [](const std::shared_ptr& node) { - return std::make_shared(std::make_shared(node)); - }); + std::ranges::transform(_folders, result->begin(), [](const std::shared_ptr& node) { + return std::make_shared(std::make_shared(node)); + }); return result; } @@ -477,7 +495,7 @@ auto operator co_await(std::chrono::duration<_Rep, _Period> delay) return true; } - void await_suspend(coro::coroutine_handle<> h) noexcept + void await_suspend(std::coroutine_handle<> h) noexcept { h.resume(); } @@ -576,10 +594,8 @@ struct EdgeConstraints { if (*first < 0) { - std::ostringstream error; - - error << "Invalid argument: first value: " << *first; - throw service::schema_exception { { service::schema_error { error.str() } } }; + auto error = std::format("Invalid argument: first value: {}", *first); + throw service::schema_exception { { service::schema_error { std::move(error) } } }; } if (itrLast - itrFirst > *first) @@ -592,10 +608,8 @@ struct EdgeConstraints { if (*last < 0) { - std::ostringstream error; - - error << "Invalid argument: last value: " << *last; - throw service::schema_exception { { service::schema_error { error.str() } } }; + auto error = std::format("Invalid argument: last value: {}", *last); + throw service::schema_exception { { service::schema_error { std::move(error) } } }; } if (itrLast - itrFirst > *last) @@ -709,12 +723,9 @@ std::vector> Query::getAppointmentsById( { std::vector> result(ids.size()); - std::transform(ids.cbegin(), - ids.cend(), - result.begin(), - [this, ¶ms](const response::IdType& id) { - return std::make_shared(findAppointment(params, id)); - }); + std::ranges::transform(ids, result.begin(), [this, ¶ms](const response::IdType& id) { + return std::make_shared(findAppointment(params, id)); + }); return result; } @@ -724,12 +735,9 @@ std::vector> Query::getTasksById( { std::vector> result(ids.size()); - std::transform(ids.cbegin(), - ids.cend(), - result.begin(), - [this, ¶ms](const response::IdType& id) { - return std::make_shared(findTask(params, id)); - }); + std::ranges::transform(ids, result.begin(), [this, ¶ms](const response::IdType& id) { + return std::make_shared(findTask(params, id)); + }); return result; } @@ -739,12 +747,9 @@ std::vector> Query::getUnreadCountsById( { std::vector> result(ids.size()); - std::transform(ids.cbegin(), - ids.cend(), - result.begin(), - [this, ¶ms](const response::IdType& id) { - return std::make_shared(findUnreadCount(params, id)); - }); + std::ranges::transform(ids, result.begin(), [this, ¶ms](const response::IdType& id) { + return std::make_shared(findUnreadCount(params, id)); + }); return result; } @@ -778,13 +783,10 @@ std::vector> Query::getAnyType( std::vector> result(_appointments.size()); - std::transform(_appointments.cbegin(), - _appointments.cend(), - result.begin(), - [](const auto& appointment) noexcept { - return std::make_shared( - std::make_shared(appointment)); - }); + std::ranges::transform(_appointments, result.begin(), [](const auto& appointment) noexcept { + return std::make_shared( + std::make_shared(appointment)); + }); return result; } @@ -845,16 +847,16 @@ std::shared_ptr Subscription::getNodeChange(const response::IdType throw std::runtime_error("Unexpected call to getNodeChange"); } -size_t NextAppointmentChange::_notifySubscribeCount = 0; -size_t NextAppointmentChange::_subscriptionCount = 0; -size_t NextAppointmentChange::_notifyUnsubscribeCount = 0; +std::size_t NextAppointmentChange::_notifySubscribeCount = 0; +std::size_t NextAppointmentChange::_subscriptionCount = 0; +std::size_t NextAppointmentChange::_notifyUnsubscribeCount = 0; NextAppointmentChange::NextAppointmentChange(nextAppointmentChange&& changeNextAppointment) : _changeNextAppointment(std::move(changeNextAppointment)) { } -size_t NextAppointmentChange::getCount(service::ResolverContext resolverContext) +std::size_t NextAppointmentChange::getCount(service::ResolverContext resolverContext) { switch (resolverContext) { @@ -929,15 +931,15 @@ NestedType::NestedType(service::FieldParams&& params, int depth) : depth(depth) { _capturedParams.push({ { params.operationDirectives }, - params.fragmentDefinitionDirectives->empty() + !params.fragmentDefinitionDirectives ? service::Directives {} - : service::Directives { params.fragmentDefinitionDirectives->front().get() }, - params.fragmentSpreadDirectives->empty() + : service::Directives { params.fragmentDefinitionDirectives->directives.get() }, + !params.fragmentSpreadDirectives ? service::Directives {} - : service::Directives { params.fragmentSpreadDirectives->front() }, - params.inlineFragmentDirectives->empty() + : service::Directives { params.fragmentSpreadDirectives->directives }, + !params.inlineFragmentDirectives ? service::Directives {} - : service::Directives { params.inlineFragmentDirectives->front() }, + : service::Directives { params.inlineFragmentDirectives->directives }, std::move(params.fieldDirectives) }); } @@ -963,9 +965,9 @@ std::stack NestedType::getCapturedParams() noexcept std::mutex Expensive::testMutex {}; std::mutex Expensive::pendingExpensiveMutex {}; std::condition_variable Expensive::pendingExpensiveCondition {}; -size_t Expensive::pendingExpensive = 0; +std::size_t Expensive::pendingExpensive = 0; -std::atomic Expensive::instances = 0; +std::atomic Expensive::instances = 0; bool Expensive::Reset() noexcept { diff --git a/samples/today/TodayMock.h b/samples/today/TodayMock.h index 6ed3a3c5..6709cba9 100644 --- a/samples/today/TodayMock.h +++ b/samples/today/TodayMock.h @@ -8,17 +8,17 @@ #include "TodaySchema.h" -#include "AppointmentEdgeObject.h" -#include "AppointmentObject.h" -#include "FolderEdgeObject.h" -#include "FolderObject.h" -#include "MutationObject.h" -#include "NodeObject.h" -#include "PageInfoObject.h" -#include "QueryObject.h" -#include "SubscriptionObject.h" -#include "TaskEdgeObject.h" -#include "TaskObject.h" +#include "TodayAppointmentEdgeObject.h" +#include "TodayAppointmentObject.h" +#include "TodayFolderEdgeObject.h" +#include "TodayFolderObject.h" +#include "TodayMutationObject.h" +#include "TodayNodeObject.h" +#include "TodayPageInfoObject.h" +#include "TodayQueryObject.h" +#include "TodaySubscriptionObject.h" +#include "TodayTaskEdgeObject.h" +#include "TodayTaskObject.h" #include #include @@ -34,26 +34,26 @@ const response::IdType& getFakeFolderId() noexcept; struct TodayMockService { std::shared_ptr service {}; - size_t getAppointmentsCount {}; - size_t getTasksCount {}; - size_t getUnreadCountsCount {}; + std::size_t getAppointmentsCount {}; + std::size_t getTasksCount {}; + std::size_t getUnreadCountsCount {}; }; -std::unique_ptr mock_service() noexcept; +std::shared_ptr mock_service() noexcept; struct RequestState : service::RequestState { - RequestState(size_t id); + RequestState(std::size_t id); - const size_t requestId; + const std::size_t requestId; - size_t appointmentsRequestId = 0; - size_t tasksRequestId = 0; - size_t unreadCountsRequestId = 0; + std::size_t appointmentsRequestId = 0; + std::size_t tasksRequestId = 0; + std::size_t unreadCountsRequestId = 0; - size_t loadAppointmentsCount = 0; - size_t loadTasksCount = 0; - size_t loadUnreadCountsCount = 0; + std::size_t loadAppointmentsCount = 0; + std::size_t loadTasksCount = 0; + std::size_t loadUnreadCountsCount = 0; }; class Appointment; @@ -146,6 +146,7 @@ class Appointment std::shared_ptr getSubject() const noexcept; bool getIsNow() const noexcept; std::optional getForceError() const; + std::vector getArray() const; private: response::IdType _id; @@ -318,7 +319,7 @@ class NextAppointmentChange explicit NextAppointmentChange(nextAppointmentChange&& changeNextAppointment); - static size_t getCount(service::ResolverContext resolverContext); + static std::size_t getCount(service::ResolverContext resolverContext); std::shared_ptr getNextAppointmentChange( const service::FieldParams& params) const; @@ -327,9 +328,9 @@ class NextAppointmentChange private: nextAppointmentChange _changeNextAppointment; - static size_t _notifySubscribeCount; - static size_t _subscriptionCount; - static size_t _notifyUnsubscribeCount; + static std::size_t _notifySubscribeCount; + static std::size_t _subscriptionCount; + static std::size_t _notifyUnsubscribeCount; }; class NodeChange @@ -388,20 +389,20 @@ class Expensive std::future getOrder(const service::FieldParams& params) const noexcept; - static constexpr size_t count = 5; + static constexpr std::size_t count = 5; static std::mutex testMutex; private: // Block async calls to getOrder until pendingExpensive == count static std::mutex pendingExpensiveMutex; static std::condition_variable pendingExpensiveCondition; - static size_t pendingExpensive; + static std::size_t pendingExpensive; // Number of instances - static std::atomic instances; + static std::atomic instances; // Initialized in the constructor - const size_t order; + const std::size_t order; }; class EmptyOperations : public service::Request diff --git a/samples/today/TodayMock.ixx b/samples/today/TodayMock.ixx new file mode 100644 index 00000000..3bd76425 --- /dev/null +++ b/samples/today/TodayMock.ixx @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +module; + +#include "TodayMock.h" + +export module GraphQL.Today.Mock; + +export import GraphQL.Today.TodaySchema; + +export namespace graphql::today { + +// clang-format off +using today::getFakeAppointmentId; +using today::getFakeTaskId; +using today::getFakeFolderId; + +using today::TodayMockService; +using today::mock_service; + +using today::RequestState; + +using today::Query; + +using today::PageInfo; +using today::Appointment; +using today::AppointmentEdge; +using today::Task; +using today::TaskEdge; +using today::TaskConnection; +using today::Folder; +using today::FolderEdge; +using today::FolderConnection; +using today::CompleteTaskPayload; +using today::Mutation; +using today::Subscription; +using today::NextAppointmentChange; +using today::NodeChange; +using today::CapturedParams; +using today::NestedType; +using today::Expensive; +using today::EmptyOperations; +// clang-format on + +} // namespace graphql::today diff --git a/samples/today/benchmark.cpp b/samples/today/benchmark.cpp index 20c4bde1..4442135c 100644 --- a/samples/today/benchmark.cpp +++ b/samples/today/benchmark.cpp @@ -1,24 +1,28 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "TodayMock.h" - -#include "graphqlservice/JSONResponse.h" - +#include #include #include #include #include +#include #include #include #include +import GraphQL.Parse; +import GraphQL.JSONResponse; +import GraphQL.Service; + +import GraphQL.Today.Mock; + using namespace graphql; using namespace std::literals; void outputOverview( - size_t iterations, const std::chrono::steady_clock::duration& totalDuration) noexcept + std::size_t iterations, const std::chrono::steady_clock::duration& totalDuration) noexcept { const auto requestsPerSecond = ((static_cast(iterations) @@ -40,7 +44,7 @@ void outputOverview( void outputSegment( std::string_view name, std::vector& durations) noexcept { - std::sort(durations.begin(), durations.end()); + std::ranges::sort(durations); const auto count = durations.size(); const auto total = @@ -61,14 +65,14 @@ void outputSegment( int main(int argc, char** argv) { - const size_t iterations = [](const char* arg) noexcept -> size_t { + const std::size_t iterations = [](const char* arg) noexcept -> std::size_t { if (arg) { const int parsed = std::atoi(arg); if (parsed > 0) { - return static_cast(parsed); + return static_cast(parsed); } } @@ -88,7 +92,7 @@ int main(int argc, char** argv) try { - for (size_t i = 0; i < iterations; ++i) + for (std::size_t i = 0; i < iterations; ++i) { const auto startParse = std::chrono::steady_clock::now(); auto query = peg::parseString(R"gql(query { diff --git a/samples/today/nointrospection/AppointmentConnectionObject.cpp b/samples/today/nointrospection/AppointmentConnectionObject.cpp index d0fe0178..ad970e9c 100644 --- a/samples/today/nointrospection/AppointmentConnectionObject.cpp +++ b/samples/today/nointrospection/AppointmentConnectionObject.cpp @@ -3,9 +3,9 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "AppointmentConnectionObject.h" -#include "PageInfoObject.h" -#include "AppointmentEdgeObject.h" +#include "TodayAppointmentConnectionObject.h" +#include "TodayPageInfoObject.h" +#include "TodayAppointmentEdgeObject.h" #include "graphqlservice/internal/Schema.h" @@ -13,7 +13,6 @@ #include #include -#include #include #include diff --git a/samples/today/nointrospection/AppointmentConnectionObject.ixx b/samples/today/nointrospection/AppointmentConnectionObject.ixx new file mode 100644 index 00000000..3f73da58 --- /dev/null +++ b/samples/today/nointrospection/AppointmentConnectionObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayAppointmentConnectionObject.h" + +export module GraphQL.Today.AppointmentConnectionObject; + +export namespace graphql::today::object { + +using object::AppointmentConnection; + +} // namespace graphql::today::object diff --git a/samples/today/nointrospection/AppointmentEdgeObject.cpp b/samples/today/nointrospection/AppointmentEdgeObject.cpp index f599fdf2..1aa60cf4 100644 --- a/samples/today/nointrospection/AppointmentEdgeObject.cpp +++ b/samples/today/nointrospection/AppointmentEdgeObject.cpp @@ -3,8 +3,8 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "AppointmentEdgeObject.h" -#include "AppointmentObject.h" +#include "TodayAppointmentEdgeObject.h" +#include "TodayAppointmentObject.h" #include "graphqlservice/internal/Schema.h" @@ -12,7 +12,6 @@ #include #include -#include #include #include diff --git a/samples/today/nointrospection/AppointmentEdgeObject.ixx b/samples/today/nointrospection/AppointmentEdgeObject.ixx new file mode 100644 index 00000000..46a48b9e --- /dev/null +++ b/samples/today/nointrospection/AppointmentEdgeObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayAppointmentEdgeObject.h" + +export module GraphQL.Today.AppointmentEdgeObject; + +export namespace graphql::today::object { + +using object::AppointmentEdge; + +} // namespace graphql::today::object diff --git a/samples/today/nointrospection/AppointmentObject.cpp b/samples/today/nointrospection/AppointmentObject.cpp index 63062308..ada849cd 100644 --- a/samples/today/nointrospection/AppointmentObject.cpp +++ b/samples/today/nointrospection/AppointmentObject.cpp @@ -3,7 +3,7 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "AppointmentObject.h" +#include "TodayAppointmentObject.h" #include "graphqlservice/internal/Schema.h" @@ -11,7 +11,6 @@ #include #include -#include #include #include @@ -40,6 +39,7 @@ service::ResolverMap Appointment::getResolvers() const noexcept return { { R"gql(id)gql"sv, [this](service::ResolverParams&& params) { return resolveId(std::move(params)); } }, { R"gql(when)gql"sv, [this](service::ResolverParams&& params) { return resolveWhen(std::move(params)); } }, + { R"gql(array)gql"sv, [this](service::ResolverParams&& params) { return resolveArray(std::move(params)); } }, { R"gql(isNow)gql"sv, [this](service::ResolverParams&& params) { return resolveIsNow(std::move(params)); } }, { R"gql(subject)gql"sv, [this](service::ResolverParams&& params) { return resolveSubject(std::move(params)); } }, { R"gql(__typename)gql"sv, [this](service::ResolverParams&& params) { return resolve_typename(std::move(params)); } }, @@ -112,6 +112,17 @@ service::AwaitableResolver Appointment::resolveForceError(service::ResolverParam return service::ModifiedResult::convert(std::move(result), std::move(params)); } +service::AwaitableResolver Appointment::resolveArray(service::ResolverParams&& params) const +{ + std::unique_lock resolverLock(_resolverMutex); + service::SelectionSetParams selectionSetParams { static_cast(params) }; + auto directives = std::move(params.fieldDirectives); + auto result = _pimpl->getArray(service::FieldParams { std::move(selectionSetParams), std::move(directives) }); + resolverLock.unlock(); + + return service::ModifiedResult::convert(std::move(result), std::move(params)); +} + service::AwaitableResolver Appointment::resolve_typename(service::ResolverParams&& params) const { return service::Result::convert(std::string{ R"gql(Appointment)gql" }, std::move(params)); @@ -129,7 +140,8 @@ void AddAppointmentDetails(const std::shared_ptr& typeAppoin schema::Field::Make(R"gql(when)gql"sv, R"md()md"sv, std::nullopt, schema->LookupType(R"gql(DateTime)gql"sv)), schema::Field::Make(R"gql(subject)gql"sv, R"md()md"sv, std::nullopt, schema->LookupType(R"gql(String)gql"sv)), schema::Field::Make(R"gql(isNow)gql"sv, R"md()md"sv, std::nullopt, schema->WrapType(introspection::TypeKind::NON_NULL, schema->LookupType(R"gql(Boolean)gql"sv))), - schema::Field::Make(R"gql(forceError)gql"sv, R"md()md"sv, std::nullopt, schema->LookupType(R"gql(String)gql"sv)) + schema::Field::Make(R"gql(forceError)gql"sv, R"md()md"sv, std::nullopt, schema->LookupType(R"gql(String)gql"sv)), + schema::Field::Make(R"gql(array)gql"sv, R"md()md"sv, std::nullopt, schema->WrapType(introspection::TypeKind::NON_NULL, schema->WrapType(introspection::TypeKind::LIST, schema->WrapType(introspection::TypeKind::NON_NULL, schema->LookupType(R"gql(ID)gql"sv))))) }); } diff --git a/samples/today/nointrospection/AppointmentObject.ixx b/samples/today/nointrospection/AppointmentObject.ixx new file mode 100644 index 00000000..2fc474d8 --- /dev/null +++ b/samples/today/nointrospection/AppointmentObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayAppointmentObject.h" + +export module GraphQL.Today.AppointmentObject; + +export namespace graphql::today::object { + +using object::Appointment; + +} // namespace graphql::today::object diff --git a/samples/today/nointrospection/CMakeLists.txt b/samples/today/nointrospection/CMakeLists.txt index 5bea126a..b4376710 100644 --- a/samples/today/nointrospection/CMakeLists.txt +++ b/samples/today/nointrospection/CMakeLists.txt @@ -1,13 +1,13 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.28) # Normally this would be handled by find_package(cppgraphqlgen CONFIG). include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/cppgraphqlgen-functions.cmake) if(GRAPHQL_UPDATE_SAMPLES) - update_graphql_schema_files(today_nointrospection ../schema.today.graphql Today today --stubs --no-introspection) + update_graphql_schema_files(today_nointrospection ../schema.today.graphql Today today --stubs --no-introspection --prefix-headers) endif() add_graphql_schema_target(today_nointrospection) diff --git a/samples/today/nointrospection/CompleteTaskPayloadObject.cpp b/samples/today/nointrospection/CompleteTaskPayloadObject.cpp index 0e0e08b0..3052500b 100644 --- a/samples/today/nointrospection/CompleteTaskPayloadObject.cpp +++ b/samples/today/nointrospection/CompleteTaskPayloadObject.cpp @@ -3,8 +3,8 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "CompleteTaskPayloadObject.h" -#include "TaskObject.h" +#include "TodayCompleteTaskPayloadObject.h" +#include "TodayTaskObject.h" #include "graphqlservice/internal/Schema.h" @@ -12,7 +12,6 @@ #include #include -#include #include #include diff --git a/samples/today/nointrospection/CompleteTaskPayloadObject.ixx b/samples/today/nointrospection/CompleteTaskPayloadObject.ixx new file mode 100644 index 00000000..f32cd43c --- /dev/null +++ b/samples/today/nointrospection/CompleteTaskPayloadObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayCompleteTaskPayloadObject.h" + +export module GraphQL.Today.CompleteTaskPayloadObject; + +export namespace graphql::today::object { + +using object::CompleteTaskPayload; + +} // namespace graphql::today::object diff --git a/samples/today/nointrospection/ExpensiveObject.cpp b/samples/today/nointrospection/ExpensiveObject.cpp index 4583f271..7c359517 100644 --- a/samples/today/nointrospection/ExpensiveObject.cpp +++ b/samples/today/nointrospection/ExpensiveObject.cpp @@ -3,7 +3,7 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "ExpensiveObject.h" +#include "TodayExpensiveObject.h" #include "graphqlservice/internal/Schema.h" @@ -11,7 +11,6 @@ #include #include -#include #include #include diff --git a/samples/today/nointrospection/ExpensiveObject.ixx b/samples/today/nointrospection/ExpensiveObject.ixx new file mode 100644 index 00000000..8cdea947 --- /dev/null +++ b/samples/today/nointrospection/ExpensiveObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayExpensiveObject.h" + +export module GraphQL.Today.ExpensiveObject; + +export namespace graphql::today::object { + +using object::Expensive; + +} // namespace graphql::today::object diff --git a/samples/today/nointrospection/FolderConnectionObject.cpp b/samples/today/nointrospection/FolderConnectionObject.cpp index 75a588a3..334dcf14 100644 --- a/samples/today/nointrospection/FolderConnectionObject.cpp +++ b/samples/today/nointrospection/FolderConnectionObject.cpp @@ -3,9 +3,9 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "FolderConnectionObject.h" -#include "PageInfoObject.h" -#include "FolderEdgeObject.h" +#include "TodayFolderConnectionObject.h" +#include "TodayPageInfoObject.h" +#include "TodayFolderEdgeObject.h" #include "graphqlservice/internal/Schema.h" @@ -13,7 +13,6 @@ #include #include -#include #include #include diff --git a/samples/today/nointrospection/FolderConnectionObject.ixx b/samples/today/nointrospection/FolderConnectionObject.ixx new file mode 100644 index 00000000..1a8e93ea --- /dev/null +++ b/samples/today/nointrospection/FolderConnectionObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayFolderConnectionObject.h" + +export module GraphQL.Today.FolderConnectionObject; + +export namespace graphql::today::object { + +using object::FolderConnection; + +} // namespace graphql::today::object diff --git a/samples/today/nointrospection/FolderEdgeObject.cpp b/samples/today/nointrospection/FolderEdgeObject.cpp index 203c1aa2..5e2e341f 100644 --- a/samples/today/nointrospection/FolderEdgeObject.cpp +++ b/samples/today/nointrospection/FolderEdgeObject.cpp @@ -3,8 +3,8 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "FolderEdgeObject.h" -#include "FolderObject.h" +#include "TodayFolderEdgeObject.h" +#include "TodayFolderObject.h" #include "graphqlservice/internal/Schema.h" @@ -12,7 +12,6 @@ #include #include -#include #include #include diff --git a/samples/today/nointrospection/FolderEdgeObject.ixx b/samples/today/nointrospection/FolderEdgeObject.ixx new file mode 100644 index 00000000..214cb206 --- /dev/null +++ b/samples/today/nointrospection/FolderEdgeObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayFolderEdgeObject.h" + +export module GraphQL.Today.FolderEdgeObject; + +export namespace graphql::today::object { + +using object::FolderEdge; + +} // namespace graphql::today::object diff --git a/samples/today/nointrospection/FolderObject.cpp b/samples/today/nointrospection/FolderObject.cpp index 9f0b44e5..4b35c950 100644 --- a/samples/today/nointrospection/FolderObject.cpp +++ b/samples/today/nointrospection/FolderObject.cpp @@ -3,7 +3,7 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "FolderObject.h" +#include "TodayFolderObject.h" #include "graphqlservice/internal/Schema.h" @@ -11,7 +11,6 @@ #include #include -#include #include #include diff --git a/samples/today/nointrospection/FolderObject.ixx b/samples/today/nointrospection/FolderObject.ixx new file mode 100644 index 00000000..bd90ad08 --- /dev/null +++ b/samples/today/nointrospection/FolderObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayFolderObject.h" + +export module GraphQL.Today.FolderObject; + +export namespace graphql::today::object { + +using object::Folder; + +} // namespace graphql::today::object diff --git a/samples/today/nointrospection/MutationObject.cpp b/samples/today/nointrospection/MutationObject.cpp index 4482e672..82232467 100644 --- a/samples/today/nointrospection/MutationObject.cpp +++ b/samples/today/nointrospection/MutationObject.cpp @@ -3,8 +3,8 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "MutationObject.h" -#include "CompleteTaskPayloadObject.h" +#include "TodayMutationObject.h" +#include "TodayCompleteTaskPayloadObject.h" #include "graphqlservice/internal/Schema.h" @@ -12,7 +12,6 @@ #include #include -#include #include #include diff --git a/samples/today/nointrospection/MutationObject.ixx b/samples/today/nointrospection/MutationObject.ixx new file mode 100644 index 00000000..07954b56 --- /dev/null +++ b/samples/today/nointrospection/MutationObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayMutationObject.h" + +export module GraphQL.Today.MutationObject; + +export namespace graphql::today::object { + +using object::Mutation; + +} // namespace graphql::today::object diff --git a/samples/today/nointrospection/NestedTypeObject.cpp b/samples/today/nointrospection/NestedTypeObject.cpp index 0298ed0a..19fc5677 100644 --- a/samples/today/nointrospection/NestedTypeObject.cpp +++ b/samples/today/nointrospection/NestedTypeObject.cpp @@ -3,8 +3,8 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "NestedTypeObject.h" -#include "NestedTypeObject.h" +#include "TodayNestedTypeObject.h" +#include "TodayNestedTypeObject.h" #include "graphqlservice/internal/Schema.h" @@ -12,7 +12,6 @@ #include #include -#include #include #include diff --git a/samples/today/nointrospection/NestedTypeObject.ixx b/samples/today/nointrospection/NestedTypeObject.ixx new file mode 100644 index 00000000..514905ed --- /dev/null +++ b/samples/today/nointrospection/NestedTypeObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayNestedTypeObject.h" + +export module GraphQL.Today.NestedTypeObject; + +export namespace graphql::today::object { + +using object::NestedType; + +} // namespace graphql::today::object diff --git a/samples/today/nointrospection/NodeObject.cpp b/samples/today/nointrospection/NodeObject.cpp index 42b73132..924d2996 100644 --- a/samples/today/nointrospection/NodeObject.cpp +++ b/samples/today/nointrospection/NodeObject.cpp @@ -3,7 +3,7 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "NodeObject.h" +#include "TodayNodeObject.h" #include "graphqlservice/internal/Schema.h" diff --git a/samples/today/nointrospection/NodeObject.ixx b/samples/today/nointrospection/NodeObject.ixx new file mode 100644 index 00000000..af5bfd22 --- /dev/null +++ b/samples/today/nointrospection/NodeObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayNodeObject.h" + +export module GraphQL.Today.NodeObject; + +export namespace graphql::today::object { + +using object::Node; + +} // namespace graphql::today::object diff --git a/samples/today/nointrospection/PageInfoObject.cpp b/samples/today/nointrospection/PageInfoObject.cpp index c356dbac..ea2b05eb 100644 --- a/samples/today/nointrospection/PageInfoObject.cpp +++ b/samples/today/nointrospection/PageInfoObject.cpp @@ -3,7 +3,7 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "PageInfoObject.h" +#include "TodayPageInfoObject.h" #include "graphqlservice/internal/Schema.h" @@ -11,7 +11,6 @@ #include #include -#include #include #include diff --git a/samples/today/nointrospection/PageInfoObject.ixx b/samples/today/nointrospection/PageInfoObject.ixx new file mode 100644 index 00000000..2e8855dd --- /dev/null +++ b/samples/today/nointrospection/PageInfoObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayPageInfoObject.h" + +export module GraphQL.Today.PageInfoObject; + +export namespace graphql::today::object { + +using object::PageInfo; + +} // namespace graphql::today::object diff --git a/samples/today/nointrospection/QueryObject.cpp b/samples/today/nointrospection/QueryObject.cpp index e624f909..f727c691 100644 --- a/samples/today/nointrospection/QueryObject.cpp +++ b/samples/today/nointrospection/QueryObject.cpp @@ -3,17 +3,17 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "QueryObject.h" -#include "NodeObject.h" -#include "AppointmentConnectionObject.h" -#include "TaskConnectionObject.h" -#include "FolderConnectionObject.h" -#include "AppointmentObject.h" -#include "TaskObject.h" -#include "FolderObject.h" -#include "NestedTypeObject.h" -#include "ExpensiveObject.h" -#include "UnionTypeObject.h" +#include "TodayQueryObject.h" +#include "TodayNodeObject.h" +#include "TodayAppointmentConnectionObject.h" +#include "TodayTaskConnectionObject.h" +#include "TodayFolderConnectionObject.h" +#include "TodayAppointmentObject.h" +#include "TodayTaskObject.h" +#include "TodayFolderObject.h" +#include "TodayNestedTypeObject.h" +#include "TodayExpensiveObject.h" +#include "TodayUnionTypeObject.h" #include "graphqlservice/internal/Schema.h" @@ -21,7 +21,6 @@ #include #include -#include #include #include diff --git a/samples/today/nointrospection/QueryObject.ixx b/samples/today/nointrospection/QueryObject.ixx new file mode 100644 index 00000000..02c0269f --- /dev/null +++ b/samples/today/nointrospection/QueryObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayQueryObject.h" + +export module GraphQL.Today.QueryObject; + +export namespace graphql::today::object { + +using object::Query; + +} // namespace graphql::today::object diff --git a/samples/today/nointrospection/SubscriptionObject.cpp b/samples/today/nointrospection/SubscriptionObject.cpp index 104e9fe0..f850a9dd 100644 --- a/samples/today/nointrospection/SubscriptionObject.cpp +++ b/samples/today/nointrospection/SubscriptionObject.cpp @@ -3,9 +3,9 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "SubscriptionObject.h" -#include "AppointmentObject.h" -#include "NodeObject.h" +#include "TodaySubscriptionObject.h" +#include "TodayAppointmentObject.h" +#include "TodayNodeObject.h" #include "graphqlservice/internal/Schema.h" @@ -13,7 +13,6 @@ #include #include -#include #include #include diff --git a/samples/today/nointrospection/SubscriptionObject.ixx b/samples/today/nointrospection/SubscriptionObject.ixx new file mode 100644 index 00000000..2cd09ade --- /dev/null +++ b/samples/today/nointrospection/SubscriptionObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodaySubscriptionObject.h" + +export module GraphQL.Today.SubscriptionObject; + +export namespace graphql::today::object { + +using object::Subscription; + +} // namespace graphql::today::object diff --git a/samples/today/nointrospection/TaskConnectionObject.cpp b/samples/today/nointrospection/TaskConnectionObject.cpp index a49a51ac..060c55ab 100644 --- a/samples/today/nointrospection/TaskConnectionObject.cpp +++ b/samples/today/nointrospection/TaskConnectionObject.cpp @@ -3,9 +3,9 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "TaskConnectionObject.h" -#include "PageInfoObject.h" -#include "TaskEdgeObject.h" +#include "TodayTaskConnectionObject.h" +#include "TodayPageInfoObject.h" +#include "TodayTaskEdgeObject.h" #include "graphqlservice/internal/Schema.h" @@ -13,7 +13,6 @@ #include #include -#include #include #include diff --git a/samples/today/nointrospection/TaskConnectionObject.ixx b/samples/today/nointrospection/TaskConnectionObject.ixx new file mode 100644 index 00000000..93354afd --- /dev/null +++ b/samples/today/nointrospection/TaskConnectionObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayTaskConnectionObject.h" + +export module GraphQL.Today.TaskConnectionObject; + +export namespace graphql::today::object { + +using object::TaskConnection; + +} // namespace graphql::today::object diff --git a/samples/today/nointrospection/TaskEdgeObject.cpp b/samples/today/nointrospection/TaskEdgeObject.cpp index 300a0737..37b888da 100644 --- a/samples/today/nointrospection/TaskEdgeObject.cpp +++ b/samples/today/nointrospection/TaskEdgeObject.cpp @@ -3,8 +3,8 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "TaskEdgeObject.h" -#include "TaskObject.h" +#include "TodayTaskEdgeObject.h" +#include "TodayTaskObject.h" #include "graphqlservice/internal/Schema.h" @@ -12,7 +12,6 @@ #include #include -#include #include #include diff --git a/samples/today/nointrospection/TaskEdgeObject.ixx b/samples/today/nointrospection/TaskEdgeObject.ixx new file mode 100644 index 00000000..20f2aa47 --- /dev/null +++ b/samples/today/nointrospection/TaskEdgeObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayTaskEdgeObject.h" + +export module GraphQL.Today.TaskEdgeObject; + +export namespace graphql::today::object { + +using object::TaskEdge; + +} // namespace graphql::today::object diff --git a/samples/today/nointrospection/TaskObject.cpp b/samples/today/nointrospection/TaskObject.cpp index cd58aa7a..483afb5b 100644 --- a/samples/today/nointrospection/TaskObject.cpp +++ b/samples/today/nointrospection/TaskObject.cpp @@ -3,7 +3,7 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "TaskObject.h" +#include "TodayTaskObject.h" #include "graphqlservice/internal/Schema.h" @@ -11,7 +11,6 @@ #include #include -#include #include #include diff --git a/samples/today/nointrospection/TaskObject.ixx b/samples/today/nointrospection/TaskObject.ixx new file mode 100644 index 00000000..5fb669e6 --- /dev/null +++ b/samples/today/nointrospection/TaskObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayTaskObject.h" + +export module GraphQL.Today.TaskObject; + +export namespace graphql::today::object { + +using object::Task; + +} // namespace graphql::today::object diff --git a/samples/today/schema/AppointmentConnectionObject.h b/samples/today/nointrospection/TodayAppointmentConnectionObject.h similarity index 97% rename from samples/today/schema/AppointmentConnectionObject.h rename to samples/today/nointrospection/TodayAppointmentConnectionObject.h index cfca668b..4640070e 100644 --- a/samples/today/schema/AppointmentConnectionObject.h +++ b/samples/today/nointrospection/TodayAppointmentConnectionObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef APPOINTMENTCONNECTIONOBJECT_H -#define APPOINTMENTCONNECTIONOBJECT_H +#ifndef TODAY_TODAYAPPOINTMENTCONNECTIONOBJECT_H +#define TODAY_TODAYAPPOINTMENTCONNECTIONOBJECT_H #include "TodaySchema.h" @@ -157,4 +157,4 @@ class [[nodiscard("unnecessary construction")]] AppointmentConnection final } // namespace graphql::today::object -#endif // APPOINTMENTCONNECTIONOBJECT_H +#endif // TODAY_TODAYAPPOINTMENTCONNECTIONOBJECT_H diff --git a/samples/today/schema/AppointmentEdgeObject.h b/samples/today/nointrospection/TodayAppointmentEdgeObject.h similarity index 97% rename from samples/today/schema/AppointmentEdgeObject.h rename to samples/today/nointrospection/TodayAppointmentEdgeObject.h index d8c71b48..bda2c57f 100644 --- a/samples/today/schema/AppointmentEdgeObject.h +++ b/samples/today/nointrospection/TodayAppointmentEdgeObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef APPOINTMENTEDGEOBJECT_H -#define APPOINTMENTEDGEOBJECT_H +#ifndef TODAY_TODAYAPPOINTMENTEDGEOBJECT_H +#define TODAY_TODAYAPPOINTMENTEDGEOBJECT_H #include "TodaySchema.h" @@ -157,4 +157,4 @@ class [[nodiscard("unnecessary construction")]] AppointmentEdge final } // namespace graphql::today::object -#endif // APPOINTMENTEDGEOBJECT_H +#endif // TODAY_TODAYAPPOINTMENTEDGEOBJECT_H diff --git a/samples/today/nointrospection/AppointmentObject.h b/samples/today/nointrospection/TodayAppointmentObject.h similarity index 87% rename from samples/today/nointrospection/AppointmentObject.h rename to samples/today/nointrospection/TodayAppointmentObject.h index 0267f113..7507a086 100644 --- a/samples/today/nointrospection/AppointmentObject.h +++ b/samples/today/nointrospection/TodayAppointmentObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef APPOINTMENTOBJECT_H -#define APPOINTMENTOBJECT_H +#ifndef TODAY_TODAYAPPOINTMENTOBJECT_H +#define TODAY_TODAYAPPOINTMENTOBJECT_H #include "TodaySchema.h" @@ -80,6 +80,18 @@ concept getForceError = requires (TImpl impl) { service::AwaitableScalar> { impl.getForceError() } }; }; +template +concept getArrayWithParams = requires (TImpl impl, service::FieldParams params) +{ + { service::AwaitableScalar> { impl.getArray(std::move(params)) } }; +}; + +template +concept getArray = requires (TImpl impl) +{ + { service::AwaitableScalar> { impl.getArray() } }; +}; + template concept beginSelectionSet = requires (TImpl impl, const service::SelectionSetParams params) { @@ -103,6 +115,7 @@ class [[nodiscard("unnecessary construction")]] Appointment final [[nodiscard("unnecessary call")]] service::AwaitableResolver resolveSubject(service::ResolverParams&& params) const; [[nodiscard("unnecessary call")]] service::AwaitableResolver resolveIsNow(service::ResolverParams&& params) const; [[nodiscard("unnecessary call")]] service::AwaitableResolver resolveForceError(service::ResolverParams&& params) const; + [[nodiscard("unnecessary call")]] service::AwaitableResolver resolveArray(service::ResolverParams&& params) const; [[nodiscard("unnecessary call")]] service::AwaitableResolver resolve_typename(service::ResolverParams&& params) const; @@ -118,6 +131,7 @@ class [[nodiscard("unnecessary construction")]] Appointment final [[nodiscard("unnecessary call")]] virtual service::AwaitableScalar> getSubject(service::FieldParams&& params) const = 0; [[nodiscard("unnecessary call")]] virtual service::AwaitableScalar getIsNow(service::FieldParams&& params) const = 0; [[nodiscard("unnecessary call")]] virtual service::AwaitableScalar> getForceError(service::FieldParams&& params) const = 0; + [[nodiscard("unnecessary call")]] virtual service::AwaitableScalar> getArray(service::FieldParams&& params) const = 0; }; template @@ -209,6 +223,22 @@ class [[nodiscard("unnecessary construction")]] Appointment final } } + [[nodiscard("unnecessary call")]] service::AwaitableScalar> getArray(service::FieldParams&& params) const override + { + if constexpr (methods::AppointmentHas::getArrayWithParams) + { + return { _pimpl->getArray(std::move(params)) }; + } + else if constexpr (methods::AppointmentHas::getArray) + { + return { _pimpl->getArray() }; + } + else + { + throw service::unimplemented_method(R"ex(Appointment::getArray)ex"); + } + } + void beginSelectionSet(const service::SelectionSetParams& params) const override { if constexpr (methods::AppointmentHas::beginSelectionSet) @@ -266,4 +296,4 @@ class [[nodiscard("unnecessary construction")]] Appointment final } // namespace graphql::today::object -#endif // APPOINTMENTOBJECT_H +#endif // TODAY_TODAYAPPOINTMENTOBJECT_H diff --git a/samples/today/nointrospection/CompleteTaskPayloadObject.h b/samples/today/nointrospection/TodayCompleteTaskPayloadObject.h similarity index 97% rename from samples/today/nointrospection/CompleteTaskPayloadObject.h rename to samples/today/nointrospection/TodayCompleteTaskPayloadObject.h index ccac3881..1fd8cb0c 100644 --- a/samples/today/nointrospection/CompleteTaskPayloadObject.h +++ b/samples/today/nointrospection/TodayCompleteTaskPayloadObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef COMPLETETASKPAYLOADOBJECT_H -#define COMPLETETASKPAYLOADOBJECT_H +#ifndef TODAY_TODAYCOMPLETETASKPAYLOADOBJECT_H +#define TODAY_TODAYCOMPLETETASKPAYLOADOBJECT_H #include "TodaySchema.h" @@ -157,4 +157,4 @@ class [[nodiscard("unnecessary construction")]] CompleteTaskPayload final } // namespace graphql::today::object -#endif // COMPLETETASKPAYLOADOBJECT_H +#endif // TODAY_TODAYCOMPLETETASKPAYLOADOBJECT_H diff --git a/samples/today/nointrospection/ExpensiveObject.h b/samples/today/nointrospection/TodayExpensiveObject.h similarity index 96% rename from samples/today/nointrospection/ExpensiveObject.h rename to samples/today/nointrospection/TodayExpensiveObject.h index bea1cd2c..6d682efb 100644 --- a/samples/today/nointrospection/ExpensiveObject.h +++ b/samples/today/nointrospection/TodayExpensiveObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef EXPENSIVEOBJECT_H -#define EXPENSIVEOBJECT_H +#ifndef TODAY_TODAYEXPENSIVEOBJECT_H +#define TODAY_TODAYEXPENSIVEOBJECT_H #include "TodaySchema.h" @@ -127,4 +127,4 @@ class [[nodiscard("unnecessary construction")]] Expensive final } // namespace graphql::today::object -#endif // EXPENSIVEOBJECT_H +#endif // TODAY_TODAYEXPENSIVEOBJECT_H diff --git a/samples/today/schema/FolderConnectionObject.h b/samples/today/nointrospection/TodayFolderConnectionObject.h similarity index 97% rename from samples/today/schema/FolderConnectionObject.h rename to samples/today/nointrospection/TodayFolderConnectionObject.h index 61e6729f..1911fb42 100644 --- a/samples/today/schema/FolderConnectionObject.h +++ b/samples/today/nointrospection/TodayFolderConnectionObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef FOLDERCONNECTIONOBJECT_H -#define FOLDERCONNECTIONOBJECT_H +#ifndef TODAY_TODAYFOLDERCONNECTIONOBJECT_H +#define TODAY_TODAYFOLDERCONNECTIONOBJECT_H #include "TodaySchema.h" @@ -157,4 +157,4 @@ class [[nodiscard("unnecessary construction")]] FolderConnection final } // namespace graphql::today::object -#endif // FOLDERCONNECTIONOBJECT_H +#endif // TODAY_TODAYFOLDERCONNECTIONOBJECT_H diff --git a/samples/today/schema/FolderEdgeObject.h b/samples/today/nointrospection/TodayFolderEdgeObject.h similarity index 97% rename from samples/today/schema/FolderEdgeObject.h rename to samples/today/nointrospection/TodayFolderEdgeObject.h index dc7a0d28..259c758d 100644 --- a/samples/today/schema/FolderEdgeObject.h +++ b/samples/today/nointrospection/TodayFolderEdgeObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef FOLDEREDGEOBJECT_H -#define FOLDEREDGEOBJECT_H +#ifndef TODAY_TODAYFOLDEREDGEOBJECT_H +#define TODAY_TODAYFOLDEREDGEOBJECT_H #include "TodaySchema.h" @@ -157,4 +157,4 @@ class [[nodiscard("unnecessary construction")]] FolderEdge final } // namespace graphql::today::object -#endif // FOLDEREDGEOBJECT_H +#endif // TODAY_TODAYFOLDEREDGEOBJECT_H diff --git a/samples/today/schema/FolderObject.h b/samples/today/nointrospection/TodayFolderObject.h similarity index 98% rename from samples/today/schema/FolderObject.h rename to samples/today/nointrospection/TodayFolderObject.h index 2376bc55..50f9849f 100644 --- a/samples/today/schema/FolderObject.h +++ b/samples/today/nointrospection/TodayFolderObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef FOLDEROBJECT_H -#define FOLDEROBJECT_H +#ifndef TODAY_TODAYFOLDEROBJECT_H +#define TODAY_TODAYFOLDEROBJECT_H #include "TodaySchema.h" @@ -206,4 +206,4 @@ class [[nodiscard("unnecessary construction")]] Folder final } // namespace graphql::today::object -#endif // FOLDEROBJECT_H +#endif // TODAY_TODAYFOLDEROBJECT_H diff --git a/samples/today/schema/MutationObject.h b/samples/today/nointrospection/TodayMutationObject.h similarity index 97% rename from samples/today/schema/MutationObject.h rename to samples/today/nointrospection/TodayMutationObject.h index 0da072bc..13659bd9 100644 --- a/samples/today/schema/MutationObject.h +++ b/samples/today/nointrospection/TodayMutationObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef MUTATIONOBJECT_H -#define MUTATIONOBJECT_H +#ifndef TODAY_TODAYMUTATIONOBJECT_H +#define TODAY_TODAYMUTATIONOBJECT_H #include "TodaySchema.h" @@ -157,4 +157,4 @@ class [[nodiscard("unnecessary construction")]] Mutation final } // namespace graphql::today::object -#endif // MUTATIONOBJECT_H +#endif // TODAY_TODAYMUTATIONOBJECT_H diff --git a/samples/today/schema/NestedTypeObject.h b/samples/today/nointrospection/TodayNestedTypeObject.h similarity index 97% rename from samples/today/schema/NestedTypeObject.h rename to samples/today/nointrospection/TodayNestedTypeObject.h index 539f72db..aab91089 100644 --- a/samples/today/schema/NestedTypeObject.h +++ b/samples/today/nointrospection/TodayNestedTypeObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef NESTEDTYPEOBJECT_H -#define NESTEDTYPEOBJECT_H +#ifndef TODAY_TODAYNESTEDTYPEOBJECT_H +#define TODAY_TODAYNESTEDTYPEOBJECT_H #include "TodaySchema.h" @@ -157,4 +157,4 @@ class [[nodiscard("unnecessary construction")]] NestedType final } // namespace graphql::today::object -#endif // NESTEDTYPEOBJECT_H +#endif // TODAY_TODAYNESTEDTYPEOBJECT_H diff --git a/samples/today/schema/NodeObject.h b/samples/today/nointrospection/TodayNodeObject.h similarity index 95% rename from samples/today/schema/NodeObject.h rename to samples/today/nointrospection/TodayNodeObject.h index 0310cdba..a394b40d 100644 --- a/samples/today/schema/NodeObject.h +++ b/samples/today/nointrospection/TodayNodeObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef NODEOBJECT_H -#define NODEOBJECT_H +#ifndef TODAY_TODAYNODEOBJECT_H +#define TODAY_TODAYNODEOBJECT_H #include "TodaySchema.h" @@ -78,4 +78,4 @@ class [[nodiscard("unnecessary construction")]] Node final } // namespace graphql::today::object -#endif // NODEOBJECT_H +#endif // TODAY_TODAYNODEOBJECT_H diff --git a/samples/today/nointrospection/PageInfoObject.h b/samples/today/nointrospection/TodayPageInfoObject.h similarity index 97% rename from samples/today/nointrospection/PageInfoObject.h rename to samples/today/nointrospection/TodayPageInfoObject.h index f4f10741..bd63667a 100644 --- a/samples/today/nointrospection/PageInfoObject.h +++ b/samples/today/nointrospection/TodayPageInfoObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef PAGEINFOOBJECT_H -#define PAGEINFOOBJECT_H +#ifndef TODAY_TODAYPAGEINFOOBJECT_H +#define TODAY_TODAYPAGEINFOOBJECT_H #include "TodaySchema.h" @@ -157,4 +157,4 @@ class [[nodiscard("unnecessary construction")]] PageInfo final } // namespace graphql::today::object -#endif // PAGEINFOOBJECT_H +#endif // TODAY_TODAYPAGEINFOOBJECT_H diff --git a/samples/today/nointrospection/QueryObject.h b/samples/today/nointrospection/TodayQueryObject.h similarity index 99% rename from samples/today/nointrospection/QueryObject.h rename to samples/today/nointrospection/TodayQueryObject.h index 5ecf2358..56799232 100644 --- a/samples/today/nointrospection/QueryObject.h +++ b/samples/today/nointrospection/TodayQueryObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef QUERYOBJECT_H -#define QUERYOBJECT_H +#ifndef TODAY_TODAYQUERYOBJECT_H +#define TODAY_TODAYQUERYOBJECT_H #include "TodaySchema.h" @@ -487,4 +487,4 @@ class [[nodiscard("unnecessary construction")]] Query final } // namespace graphql::today::object -#endif // QUERYOBJECT_H +#endif // TODAY_TODAYQUERYOBJECT_H diff --git a/samples/today/nointrospection/TodaySchema.cpp b/samples/today/nointrospection/TodaySchema.cpp index 55d3f6ae..f8a4d23c 100644 --- a/samples/today/nointrospection/TodaySchema.cpp +++ b/samples/today/nointrospection/TodaySchema.cpp @@ -3,9 +3,9 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "QueryObject.h" -#include "MutationObject.h" -#include "SubscriptionObject.h" +#include "TodayQueryObject.h" +#include "TodayMutationObject.h" +#include "TodaySubscriptionObject.h" #include "graphqlservice/internal/Schema.h" @@ -13,8 +13,8 @@ #include #include +#include #include -#include #include #include #include @@ -22,718 +22,7 @@ using namespace std::literals; -namespace graphql { -namespace service { - -static const auto s_namesTaskState = today::getTaskStateNames(); -static const auto s_valuesTaskState = today::getTaskStateValues(); - -template <> -today::TaskState Argument::convert(const response::Value& value) -{ - if (!value.maybe_enum()) - { - throw service::schema_exception { { R"ex(not a valid TaskState value)ex" } }; - } - - const auto result = internal::sorted_map_lookup( - s_valuesTaskState, - std::string_view { value.get() }); - - if (!result) - { - throw service::schema_exception { { R"ex(not a valid TaskState value)ex" } }; - } - - return *result; -} - -template <> -service::AwaitableResolver Result::convert(service::AwaitableScalar result, ResolverParams&& params) -{ - return ModifiedResult::resolve(std::move(result), std::move(params), - [](today::TaskState value, const ResolverParams&) - { - const auto idx = static_cast(value); - - if (idx >= s_namesTaskState.size()) - { - throw service::schema_exception { { R"ex(Enum value out of range for TaskState)ex" } }; - } - - response::Value resolvedResult(response::Type::EnumValue); - - resolvedResult.set(std::string { s_namesTaskState[idx] }); - - return resolvedResult; - }); -} - -template <> -void Result::validateScalar(const response::Value& value) -{ - if (!value.maybe_enum()) - { - throw service::schema_exception { { R"ex(not a valid TaskState value)ex" } }; - } - - const auto [itr, itrEnd] = internal::sorted_map_equal_range( - s_valuesTaskState.begin(), - s_valuesTaskState.end(), - std::string_view { value.get() }); - - if (itr == itrEnd) - { - throw service::schema_exception { { R"ex(not a valid TaskState value)ex" } }; - } -} - -template <> -today::CompleteTaskInput Argument::convert(const response::Value& value) -{ - const auto defaultValue = []() - { - response::Value values(response::Type::Map); - response::Value entry; - - entry = response::Value(true); - values.emplace_back("isComplete", std::move(entry)); - - return values; - }(); - - auto valueId = service::ModifiedArgument::require("id", value); - auto valueTestTaskState = service::ModifiedArgument::require("testTaskState", value); - auto pairIsComplete = service::ModifiedArgument::find("isComplete", value); - auto valueIsComplete = (pairIsComplete.second - ? std::move(pairIsComplete.first) - : service::ModifiedArgument::require("isComplete", defaultValue)); - auto valueClientMutationId = service::ModifiedArgument::require("clientMutationId", value); - - return today::CompleteTaskInput { - std::move(valueId), - valueTestTaskState, - std::move(valueIsComplete), - std::move(valueClientMutationId) - }; -} - -template <> -today::ThirdNestedInput Argument::convert(const response::Value& value) -{ - auto valueId = service::ModifiedArgument::require("id", value); - auto valueSecond = service::ModifiedArgument::require("second", value); - - return today::ThirdNestedInput { - std::move(valueId), - std::move(valueSecond) - }; -} - -template <> -today::FourthNestedInput Argument::convert(const response::Value& value) -{ - auto valueId = service::ModifiedArgument::require("id", value); - - return today::FourthNestedInput { - std::move(valueId) - }; -} - -template <> -today::IncludeNullableSelfInput Argument::convert(const response::Value& value) -{ - auto valueSelf = service::ModifiedArgument::require("self", value); - - return today::IncludeNullableSelfInput { - std::move(valueSelf) - }; -} - -template <> -today::IncludeNonNullableListSelfInput Argument::convert(const response::Value& value) -{ - auto valueSelves = service::ModifiedArgument::require("selves", value); - - return today::IncludeNonNullableListSelfInput { - std::move(valueSelves) - }; -} - -template <> -today::StringOperationFilterInput Argument::convert(const response::Value& value) -{ - auto valueAnd_ = service::ModifiedArgument::require("and", value); - auto valueOr_ = service::ModifiedArgument::require("or", value); - auto valueEqual = service::ModifiedArgument::require("equal", value); - auto valueNotEqual = service::ModifiedArgument::require("notEqual", value); - auto valueContains = service::ModifiedArgument::require("contains", value); - auto valueNotContains = service::ModifiedArgument::require("notContains", value); - auto valueIn = service::ModifiedArgument::require("in", value); - auto valueNotIn = service::ModifiedArgument::require("notIn", value); - auto valueStartsWith = service::ModifiedArgument::require("startsWith", value); - auto valueNotStartsWith = service::ModifiedArgument::require("notStartsWith", value); - auto valueEndsWith = service::ModifiedArgument::require("endsWith", value); - auto valueNotEndsWith = service::ModifiedArgument::require("notEndsWith", value); - - return today::StringOperationFilterInput { - std::move(valueAnd_), - std::move(valueOr_), - std::move(valueEqual), - std::move(valueNotEqual), - std::move(valueContains), - std::move(valueNotContains), - std::move(valueIn), - std::move(valueNotIn), - std::move(valueStartsWith), - std::move(valueNotStartsWith), - std::move(valueEndsWith), - std::move(valueNotEndsWith) - }; -} - -template <> -today::SecondNestedInput Argument::convert(const response::Value& value) -{ - auto valueId = service::ModifiedArgument::require("id", value); - auto valueThird = service::ModifiedArgument::require("third", value); - - return today::SecondNestedInput { - std::move(valueId), - std::move(valueThird) - }; -} - -template <> -today::ForwardDeclaredInput Argument::convert(const response::Value& value) -{ - auto valueNullableSelf = service::ModifiedArgument::require("nullableSelf", value); - auto valueListSelves = service::ModifiedArgument::require("listSelves", value); - - return today::ForwardDeclaredInput { - std::move(valueNullableSelf), - std::move(valueListSelves) - }; -} - -template <> -today::FirstNestedInput Argument::convert(const response::Value& value) -{ - auto valueId = service::ModifiedArgument::require("id", value); - auto valueSecond = service::ModifiedArgument::require("second", value); - auto valueThird = service::ModifiedArgument::require("third", value); - - return today::FirstNestedInput { - std::move(valueId), - std::move(valueSecond), - std::move(valueThird) - }; -} - -} // namespace service - -namespace today { - -CompleteTaskInput::CompleteTaskInput() noexcept - : id {} - , testTaskState {} - , isComplete {} - , clientMutationId {} -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -CompleteTaskInput::CompleteTaskInput( - response::IdType idArg, - std::optional testTaskStateArg, - std::optional isCompleteArg, - std::optional clientMutationIdArg) noexcept - : id { std::move(idArg) } - , testTaskState { std::move(testTaskStateArg) } - , isComplete { std::move(isCompleteArg) } - , clientMutationId { std::move(clientMutationIdArg) } -{ -} - -CompleteTaskInput::CompleteTaskInput(const CompleteTaskInput& other) - : id { service::ModifiedArgument::duplicate(other.id) } - , testTaskState { service::ModifiedArgument::duplicate(other.testTaskState) } - , isComplete { service::ModifiedArgument::duplicate(other.isComplete) } - , clientMutationId { service::ModifiedArgument::duplicate(other.clientMutationId) } -{ -} - -CompleteTaskInput::CompleteTaskInput(CompleteTaskInput&& other) noexcept - : id { std::move(other.id) } - , testTaskState { std::move(other.testTaskState) } - , isComplete { std::move(other.isComplete) } - , clientMutationId { std::move(other.clientMutationId) } -{ -} - -CompleteTaskInput::~CompleteTaskInput() -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -CompleteTaskInput& CompleteTaskInput::operator=(const CompleteTaskInput& other) -{ - CompleteTaskInput value { other }; - - std::swap(*this, value); - - return *this; -} - -CompleteTaskInput& CompleteTaskInput::operator=(CompleteTaskInput&& other) noexcept -{ - id = std::move(other.id); - testTaskState = std::move(other.testTaskState); - isComplete = std::move(other.isComplete); - clientMutationId = std::move(other.clientMutationId); - - return *this; -} - -ThirdNestedInput::ThirdNestedInput() noexcept - : id {} - , second {} -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -ThirdNestedInput::ThirdNestedInput( - response::IdType idArg, - std::unique_ptr secondArg) noexcept - : id { std::move(idArg) } - , second { std::move(secondArg) } -{ -} - -ThirdNestedInput::ThirdNestedInput(const ThirdNestedInput& other) - : id { service::ModifiedArgument::duplicate(other.id) } - , second { service::ModifiedArgument::duplicate(other.second) } -{ -} - -ThirdNestedInput::ThirdNestedInput(ThirdNestedInput&& other) noexcept - : id { std::move(other.id) } - , second { std::move(other.second) } -{ -} - -ThirdNestedInput::~ThirdNestedInput() -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -ThirdNestedInput& ThirdNestedInput::operator=(const ThirdNestedInput& other) -{ - ThirdNestedInput value { other }; - - std::swap(*this, value); - - return *this; -} - -ThirdNestedInput& ThirdNestedInput::operator=(ThirdNestedInput&& other) noexcept -{ - id = std::move(other.id); - second = std::move(other.second); - - return *this; -} - -FourthNestedInput::FourthNestedInput() noexcept - : id {} -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -FourthNestedInput::FourthNestedInput( - response::IdType idArg) noexcept - : id { std::move(idArg) } -{ -} - -FourthNestedInput::FourthNestedInput(const FourthNestedInput& other) - : id { service::ModifiedArgument::duplicate(other.id) } -{ -} - -FourthNestedInput::FourthNestedInput(FourthNestedInput&& other) noexcept - : id { std::move(other.id) } -{ -} - -FourthNestedInput::~FourthNestedInput() -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -FourthNestedInput& FourthNestedInput::operator=(const FourthNestedInput& other) -{ - FourthNestedInput value { other }; - - std::swap(*this, value); - - return *this; -} - -FourthNestedInput& FourthNestedInput::operator=(FourthNestedInput&& other) noexcept -{ - id = std::move(other.id); - - return *this; -} - -IncludeNullableSelfInput::IncludeNullableSelfInput() noexcept - : self {} -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -IncludeNullableSelfInput::IncludeNullableSelfInput( - std::unique_ptr selfArg) noexcept - : self { std::move(selfArg) } -{ -} - -IncludeNullableSelfInput::IncludeNullableSelfInput(const IncludeNullableSelfInput& other) - : self { service::ModifiedArgument::duplicate(other.self) } -{ -} - -IncludeNullableSelfInput::IncludeNullableSelfInput(IncludeNullableSelfInput&& other) noexcept - : self { std::move(other.self) } -{ -} - -IncludeNullableSelfInput::~IncludeNullableSelfInput() -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -IncludeNullableSelfInput& IncludeNullableSelfInput::operator=(const IncludeNullableSelfInput& other) -{ - IncludeNullableSelfInput value { other }; - - std::swap(*this, value); - - return *this; -} - -IncludeNullableSelfInput& IncludeNullableSelfInput::operator=(IncludeNullableSelfInput&& other) noexcept -{ - self = std::move(other.self); - - return *this; -} - -IncludeNonNullableListSelfInput::IncludeNonNullableListSelfInput() noexcept - : selves {} -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -IncludeNonNullableListSelfInput::IncludeNonNullableListSelfInput( - std::vector selvesArg) noexcept - : selves { std::move(selvesArg) } -{ -} - -IncludeNonNullableListSelfInput::IncludeNonNullableListSelfInput(const IncludeNonNullableListSelfInput& other) - : selves { service::ModifiedArgument::duplicate(other.selves) } -{ -} - -IncludeNonNullableListSelfInput::IncludeNonNullableListSelfInput(IncludeNonNullableListSelfInput&& other) noexcept - : selves { std::move(other.selves) } -{ -} - -IncludeNonNullableListSelfInput::~IncludeNonNullableListSelfInput() -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -IncludeNonNullableListSelfInput& IncludeNonNullableListSelfInput::operator=(const IncludeNonNullableListSelfInput& other) -{ - IncludeNonNullableListSelfInput value { other }; - - std::swap(*this, value); - - return *this; -} - -IncludeNonNullableListSelfInput& IncludeNonNullableListSelfInput::operator=(IncludeNonNullableListSelfInput&& other) noexcept -{ - selves = std::move(other.selves); - - return *this; -} - -StringOperationFilterInput::StringOperationFilterInput() noexcept - : and_ {} - , or_ {} - , equal {} - , notEqual {} - , contains {} - , notContains {} - , in {} - , notIn {} - , startsWith {} - , notStartsWith {} - , endsWith {} - , notEndsWith {} -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -StringOperationFilterInput::StringOperationFilterInput( - std::optional> and_Arg, - std::optional> or_Arg, - std::optional equalArg, - std::optional notEqualArg, - std::optional containsArg, - std::optional notContainsArg, - std::optional> inArg, - std::optional> notInArg, - std::optional startsWithArg, - std::optional notStartsWithArg, - std::optional endsWithArg, - std::optional notEndsWithArg) noexcept - : and_ { std::move(and_Arg) } - , or_ { std::move(or_Arg) } - , equal { std::move(equalArg) } - , notEqual { std::move(notEqualArg) } - , contains { std::move(containsArg) } - , notContains { std::move(notContainsArg) } - , in { std::move(inArg) } - , notIn { std::move(notInArg) } - , startsWith { std::move(startsWithArg) } - , notStartsWith { std::move(notStartsWithArg) } - , endsWith { std::move(endsWithArg) } - , notEndsWith { std::move(notEndsWithArg) } -{ -} - -StringOperationFilterInput::StringOperationFilterInput(const StringOperationFilterInput& other) - : and_ { service::ModifiedArgument::duplicate(other.and_) } - , or_ { service::ModifiedArgument::duplicate(other.or_) } - , equal { service::ModifiedArgument::duplicate(other.equal) } - , notEqual { service::ModifiedArgument::duplicate(other.notEqual) } - , contains { service::ModifiedArgument::duplicate(other.contains) } - , notContains { service::ModifiedArgument::duplicate(other.notContains) } - , in { service::ModifiedArgument::duplicate(other.in) } - , notIn { service::ModifiedArgument::duplicate(other.notIn) } - , startsWith { service::ModifiedArgument::duplicate(other.startsWith) } - , notStartsWith { service::ModifiedArgument::duplicate(other.notStartsWith) } - , endsWith { service::ModifiedArgument::duplicate(other.endsWith) } - , notEndsWith { service::ModifiedArgument::duplicate(other.notEndsWith) } -{ -} - -StringOperationFilterInput::StringOperationFilterInput(StringOperationFilterInput&& other) noexcept - : and_ { std::move(other.and_) } - , or_ { std::move(other.or_) } - , equal { std::move(other.equal) } - , notEqual { std::move(other.notEqual) } - , contains { std::move(other.contains) } - , notContains { std::move(other.notContains) } - , in { std::move(other.in) } - , notIn { std::move(other.notIn) } - , startsWith { std::move(other.startsWith) } - , notStartsWith { std::move(other.notStartsWith) } - , endsWith { std::move(other.endsWith) } - , notEndsWith { std::move(other.notEndsWith) } -{ -} - -StringOperationFilterInput::~StringOperationFilterInput() -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -StringOperationFilterInput& StringOperationFilterInput::operator=(const StringOperationFilterInput& other) -{ - StringOperationFilterInput value { other }; - - std::swap(*this, value); - - return *this; -} - -StringOperationFilterInput& StringOperationFilterInput::operator=(StringOperationFilterInput&& other) noexcept -{ - and_ = std::move(other.and_); - or_ = std::move(other.or_); - equal = std::move(other.equal); - notEqual = std::move(other.notEqual); - contains = std::move(other.contains); - notContains = std::move(other.notContains); - in = std::move(other.in); - notIn = std::move(other.notIn); - startsWith = std::move(other.startsWith); - notStartsWith = std::move(other.notStartsWith); - endsWith = std::move(other.endsWith); - notEndsWith = std::move(other.notEndsWith); - - return *this; -} - -SecondNestedInput::SecondNestedInput() noexcept - : id {} - , third {} -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -SecondNestedInput::SecondNestedInput( - response::IdType idArg, - ThirdNestedInput thirdArg) noexcept - : id { std::move(idArg) } - , third { std::move(thirdArg) } -{ -} - -SecondNestedInput::SecondNestedInput(const SecondNestedInput& other) - : id { service::ModifiedArgument::duplicate(other.id) } - , third { service::ModifiedArgument::duplicate(other.third) } -{ -} - -SecondNestedInput::SecondNestedInput(SecondNestedInput&& other) noexcept - : id { std::move(other.id) } - , third { std::move(other.third) } -{ -} - -SecondNestedInput::~SecondNestedInput() -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -SecondNestedInput& SecondNestedInput::operator=(const SecondNestedInput& other) -{ - SecondNestedInput value { other }; - - std::swap(*this, value); - - return *this; -} - -SecondNestedInput& SecondNestedInput::operator=(SecondNestedInput&& other) noexcept -{ - id = std::move(other.id); - third = std::move(other.third); - - return *this; -} - -ForwardDeclaredInput::ForwardDeclaredInput() noexcept - : nullableSelf {} - , listSelves {} -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -ForwardDeclaredInput::ForwardDeclaredInput( - std::unique_ptr nullableSelfArg, - IncludeNonNullableListSelfInput listSelvesArg) noexcept - : nullableSelf { std::move(nullableSelfArg) } - , listSelves { std::move(listSelvesArg) } -{ -} - -ForwardDeclaredInput::ForwardDeclaredInput(const ForwardDeclaredInput& other) - : nullableSelf { service::ModifiedArgument::duplicate(other.nullableSelf) } - , listSelves { service::ModifiedArgument::duplicate(other.listSelves) } -{ -} - -ForwardDeclaredInput::ForwardDeclaredInput(ForwardDeclaredInput&& other) noexcept - : nullableSelf { std::move(other.nullableSelf) } - , listSelves { std::move(other.listSelves) } -{ -} - -ForwardDeclaredInput::~ForwardDeclaredInput() -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -ForwardDeclaredInput& ForwardDeclaredInput::operator=(const ForwardDeclaredInput& other) -{ - ForwardDeclaredInput value { other }; - - std::swap(*this, value); - - return *this; -} - -ForwardDeclaredInput& ForwardDeclaredInput::operator=(ForwardDeclaredInput&& other) noexcept -{ - nullableSelf = std::move(other.nullableSelf); - listSelves = std::move(other.listSelves); - - return *this; -} - -FirstNestedInput::FirstNestedInput() noexcept - : id {} - , second {} - , third {} -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -FirstNestedInput::FirstNestedInput( - response::IdType idArg, - SecondNestedInput secondArg, - ThirdNestedInput thirdArg) noexcept - : id { std::move(idArg) } - , second { std::move(secondArg) } - , third { std::move(thirdArg) } -{ -} - -FirstNestedInput::FirstNestedInput(const FirstNestedInput& other) - : id { service::ModifiedArgument::duplicate(other.id) } - , second { service::ModifiedArgument::duplicate(other.second) } - , third { service::ModifiedArgument::duplicate(other.third) } -{ -} - -FirstNestedInput::FirstNestedInput(FirstNestedInput&& other) noexcept - : id { std::move(other.id) } - , second { std::move(other.second) } - , third { std::move(other.third) } -{ -} - -FirstNestedInput::~FirstNestedInput() -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -FirstNestedInput& FirstNestedInput::operator=(const FirstNestedInput& other) -{ - FirstNestedInput value { other }; - - std::swap(*this, value); - - return *this; -} - -FirstNestedInput& FirstNestedInput::operator=(FirstNestedInput&& other) noexcept -{ - id = std::move(other.id); - second = std::move(other.second); - third = std::move(other.third); - - return *this; -} +namespace graphql::today { Operations::Operations(std::shared_ptr query, std::shared_ptr mutation, std::shared_ptr subscription) : service::Request({ @@ -808,18 +97,20 @@ void AddTypesToSchema(const std::shared_ptr& schema) auto typeExpensive = schema::ObjectType::Make(R"gql(Expensive)gql"sv, R"md()md"sv); schema->AddType(R"gql(Expensive)gql"sv, typeExpensive); + static const auto s_namesTaskState = getTaskStateNames(); typeTaskState->AddEnumValues({ - { service::s_namesTaskState[static_cast(today::TaskState::Unassigned)], R"md()md"sv, std::make_optional(R"md(Need to deprecate an [enum value](https://spec.graphql.org/October2021/#sec-Schema-Introspection.Deprecation))md"sv) }, - { service::s_namesTaskState[static_cast(today::TaskState::New)], R"md()md"sv, std::nullopt }, - { service::s_namesTaskState[static_cast(today::TaskState::Started)], R"md()md"sv, std::nullopt }, - { service::s_namesTaskState[static_cast(today::TaskState::Complete)], R"md()md"sv, std::nullopt } + { s_namesTaskState[static_cast(today::TaskState::Unassigned)], R"md()md"sv, std::make_optional(R"md(Need to deprecate an [enum value](https://spec.graphql.org/October2021/#sec-Schema-Introspection.Deprecation))md"sv) }, + { s_namesTaskState[static_cast(today::TaskState::New)], R"md()md"sv, std::nullopt }, + { s_namesTaskState[static_cast(today::TaskState::Started)], R"md()md"sv, std::nullopt }, + { s_namesTaskState[static_cast(today::TaskState::Complete)], R"md()md"sv, std::nullopt } }); typeCompleteTaskInput->AddInputValues({ schema::InputValue::Make(R"gql(id)gql"sv, R"md()md"sv, schema->WrapType(introspection::TypeKind::NON_NULL, schema->LookupType(R"gql(ID)gql"sv)), R"gql()gql"sv), schema::InputValue::Make(R"gql(testTaskState)gql"sv, R"md()md"sv, schema->LookupType(R"gql(TaskState)gql"sv), R"gql()gql"sv), schema::InputValue::Make(R"gql(isComplete)gql"sv, R"md()md"sv, schema->LookupType(R"gql(Boolean)gql"sv), R"gql(true)gql"sv), - schema::InputValue::Make(R"gql(clientMutationId)gql"sv, R"md()md"sv, schema->LookupType(R"gql(String)gql"sv), R"gql()gql"sv) + schema::InputValue::Make(R"gql(clientMutationId)gql"sv, R"md()md"sv, schema->LookupType(R"gql(String)gql"sv), R"gql()gql"sv), + schema::InputValue::Make(R"gql(boolList)gql"sv, R"md()md"sv, schema->WrapType(introspection::TypeKind::LIST, schema->WrapType(introspection::TypeKind::NON_NULL, schema->LookupType(R"gql(Boolean)gql"sv))), R"gql()gql"sv) }); typeThirdNestedInput->AddInputValues({ schema::InputValue::Make(R"gql(id)gql"sv, R"md()md"sv, schema->WrapType(introspection::TypeKind::NON_NULL, schema->LookupType(R"gql(ID)gql"sv)), R"gql()gql"sv), @@ -936,5 +227,4 @@ std::shared_ptr GetSchema() return schema; } -} // namespace today -} // namespace graphql +} // namespace graphql::today diff --git a/samples/today/nointrospection/TodaySchema.h b/samples/today/nointrospection/TodaySchema.h index ebcd3905..80b99eee 100644 --- a/samples/today/nointrospection/TodaySchema.h +++ b/samples/today/nointrospection/TodaySchema.h @@ -8,227 +8,24 @@ #ifndef TODAYSCHEMA_H #define TODAYSCHEMA_H +#include "graphqlservice/GraphQLResponse.h" +#include "graphqlservice/GraphQLService.h" + +#include "graphqlservice/internal/Version.h" #include "graphqlservice/internal/Schema.h" -// Check if the library version is compatible with schemagen 4.5.0 -static_assert(graphql::internal::MajorVersion == 4, "regenerate with schemagen: major version mismatch"); -static_assert(graphql::internal::MinorVersion == 5, "regenerate with schemagen: minor version mismatch"); +#include "TodaySharedTypes.h" #include #include #include #include -namespace graphql { -namespace today { - -enum class [[nodiscard("unnecessary conversion")]] TaskState -{ - Unassigned, - New, - Started, - Complete -}; - -[[nodiscard("unnecessary call")]] constexpr auto getTaskStateNames() noexcept -{ - using namespace std::literals; - - return std::array { - R"gql(Unassigned)gql"sv, - R"gql(New)gql"sv, - R"gql(Started)gql"sv, - R"gql(Complete)gql"sv - }; -} - -[[nodiscard("unnecessary call")]] constexpr auto getTaskStateValues() noexcept -{ - using namespace std::literals; - - return std::array, 4> { - std::make_pair(R"gql(New)gql"sv, TaskState::New), - std::make_pair(R"gql(Started)gql"sv, TaskState::Started), - std::make_pair(R"gql(Complete)gql"sv, TaskState::Complete), - std::make_pair(R"gql(Unassigned)gql"sv, TaskState::Unassigned) - }; -} - -struct [[nodiscard("unnecessary construction")]] CompleteTaskInput -{ - explicit CompleteTaskInput() noexcept; - explicit CompleteTaskInput( - response::IdType idArg, - std::optional testTaskStateArg, - std::optional isCompleteArg, - std::optional clientMutationIdArg) noexcept; - CompleteTaskInput(const CompleteTaskInput& other); - CompleteTaskInput(CompleteTaskInput&& other) noexcept; - ~CompleteTaskInput(); - - CompleteTaskInput& operator=(const CompleteTaskInput& other); - CompleteTaskInput& operator=(CompleteTaskInput&& other) noexcept; - - response::IdType id; - std::optional testTaskState; - std::optional isComplete; - std::optional clientMutationId; -}; - -struct SecondNestedInput; - -struct [[nodiscard("unnecessary construction")]] ThirdNestedInput -{ - explicit ThirdNestedInput() noexcept; - explicit ThirdNestedInput( - response::IdType idArg, - std::unique_ptr secondArg) noexcept; - ThirdNestedInput(const ThirdNestedInput& other); - ThirdNestedInput(ThirdNestedInput&& other) noexcept; - ~ThirdNestedInput(); - - ThirdNestedInput& operator=(const ThirdNestedInput& other); - ThirdNestedInput& operator=(ThirdNestedInput&& other) noexcept; - - response::IdType id; - std::unique_ptr second; -}; - -struct [[nodiscard("unnecessary construction")]] FourthNestedInput -{ - explicit FourthNestedInput() noexcept; - explicit FourthNestedInput( - response::IdType idArg) noexcept; - FourthNestedInput(const FourthNestedInput& other); - FourthNestedInput(FourthNestedInput&& other) noexcept; - ~FourthNestedInput(); - - FourthNestedInput& operator=(const FourthNestedInput& other); - FourthNestedInput& operator=(FourthNestedInput&& other) noexcept; - - response::IdType id; -}; - -struct [[nodiscard("unnecessary construction")]] IncludeNullableSelfInput -{ - explicit IncludeNullableSelfInput() noexcept; - explicit IncludeNullableSelfInput( - std::unique_ptr selfArg) noexcept; - IncludeNullableSelfInput(const IncludeNullableSelfInput& other); - IncludeNullableSelfInput(IncludeNullableSelfInput&& other) noexcept; - ~IncludeNullableSelfInput(); - - IncludeNullableSelfInput& operator=(const IncludeNullableSelfInput& other); - IncludeNullableSelfInput& operator=(IncludeNullableSelfInput&& other) noexcept; - - std::unique_ptr self; -}; - -struct [[nodiscard("unnecessary construction")]] IncludeNonNullableListSelfInput -{ - explicit IncludeNonNullableListSelfInput() noexcept; - explicit IncludeNonNullableListSelfInput( - std::vector selvesArg) noexcept; - IncludeNonNullableListSelfInput(const IncludeNonNullableListSelfInput& other); - IncludeNonNullableListSelfInput(IncludeNonNullableListSelfInput&& other) noexcept; - ~IncludeNonNullableListSelfInput(); - - IncludeNonNullableListSelfInput& operator=(const IncludeNonNullableListSelfInput& other); - IncludeNonNullableListSelfInput& operator=(IncludeNonNullableListSelfInput&& other) noexcept; - - std::vector selves; -}; - -struct [[nodiscard("unnecessary construction")]] StringOperationFilterInput -{ - explicit StringOperationFilterInput() noexcept; - explicit StringOperationFilterInput( - std::optional> and_Arg, - std::optional> or_Arg, - std::optional equalArg, - std::optional notEqualArg, - std::optional containsArg, - std::optional notContainsArg, - std::optional> inArg, - std::optional> notInArg, - std::optional startsWithArg, - std::optional notStartsWithArg, - std::optional endsWithArg, - std::optional notEndsWithArg) noexcept; - StringOperationFilterInput(const StringOperationFilterInput& other); - StringOperationFilterInput(StringOperationFilterInput&& other) noexcept; - ~StringOperationFilterInput(); - - StringOperationFilterInput& operator=(const StringOperationFilterInput& other); - StringOperationFilterInput& operator=(StringOperationFilterInput&& other) noexcept; - - std::optional> and_; - std::optional> or_; - std::optional equal; - std::optional notEqual; - std::optional contains; - std::optional notContains; - std::optional> in; - std::optional> notIn; - std::optional startsWith; - std::optional notStartsWith; - std::optional endsWith; - std::optional notEndsWith; -}; - -struct [[nodiscard("unnecessary construction")]] SecondNestedInput -{ - explicit SecondNestedInput() noexcept; - explicit SecondNestedInput( - response::IdType idArg, - ThirdNestedInput thirdArg) noexcept; - SecondNestedInput(const SecondNestedInput& other); - SecondNestedInput(SecondNestedInput&& other) noexcept; - ~SecondNestedInput(); - - SecondNestedInput& operator=(const SecondNestedInput& other); - SecondNestedInput& operator=(SecondNestedInput&& other) noexcept; - - response::IdType id; - ThirdNestedInput third; -}; - -struct [[nodiscard("unnecessary construction")]] ForwardDeclaredInput -{ - explicit ForwardDeclaredInput() noexcept; - explicit ForwardDeclaredInput( - std::unique_ptr nullableSelfArg, - IncludeNonNullableListSelfInput listSelvesArg) noexcept; - ForwardDeclaredInput(const ForwardDeclaredInput& other); - ForwardDeclaredInput(ForwardDeclaredInput&& other) noexcept; - ~ForwardDeclaredInput(); - - ForwardDeclaredInput& operator=(const ForwardDeclaredInput& other); - ForwardDeclaredInput& operator=(ForwardDeclaredInput&& other) noexcept; - - std::unique_ptr nullableSelf; - IncludeNonNullableListSelfInput listSelves; -}; - -struct [[nodiscard("unnecessary construction")]] FirstNestedInput -{ - explicit FirstNestedInput() noexcept; - explicit FirstNestedInput( - response::IdType idArg, - SecondNestedInput secondArg, - ThirdNestedInput thirdArg) noexcept; - FirstNestedInput(const FirstNestedInput& other); - FirstNestedInput(FirstNestedInput&& other) noexcept; - ~FirstNestedInput(); - - FirstNestedInput& operator=(const FirstNestedInput& other); - FirstNestedInput& operator=(FirstNestedInput&& other) noexcept; - - response::IdType id; - SecondNestedInput second; - ThirdNestedInput third; -}; +// Check if the library version is compatible with schemagen 5.0.0 +static_assert(graphql::internal::MajorVersion == 5, "regenerate with schemagen: major version mismatch"); +static_assert(graphql::internal::MinorVersion == 0, "regenerate with schemagen: minor version mismatch"); +namespace graphql::today { namespace object { class Node; @@ -299,7 +96,6 @@ void AddExpensiveDetails(const std::shared_ptr& typeExpensiv std::shared_ptr GetSchema(); -} // namespace today -} // namespace graphql +} // namespace graphql::today #endif // TODAYSCHEMA_H diff --git a/samples/today/nointrospection/TodaySchema.ixx b/samples/today/nointrospection/TodaySchema.ixx new file mode 100644 index 00000000..56aad8e6 --- /dev/null +++ b/samples/today/nointrospection/TodaySchema.ixx @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodaySchema.h" + +export module GraphQL.Today.TodaySchema; + +export import GraphQL.Today.TodaySharedTypes; + +export import GraphQL.Today.NodeObject; +export import GraphQL.Today.UnionTypeObject; +export import GraphQL.Today.QueryObject; +export import GraphQL.Today.PageInfoObject; +export import GraphQL.Today.AppointmentEdgeObject; +export import GraphQL.Today.AppointmentConnectionObject; +export import GraphQL.Today.TaskEdgeObject; +export import GraphQL.Today.TaskConnectionObject; +export import GraphQL.Today.FolderEdgeObject; +export import GraphQL.Today.FolderConnectionObject; +export import GraphQL.Today.CompleteTaskPayloadObject; +export import GraphQL.Today.MutationObject; +export import GraphQL.Today.SubscriptionObject; +export import GraphQL.Today.AppointmentObject; +export import GraphQL.Today.TaskObject; +export import GraphQL.Today.FolderObject; +export import GraphQL.Today.NestedTypeObject; +export import GraphQL.Today.ExpensiveObject; + +export namespace graphql::today { + +using today::Operations; + +using today::AddNodeDetails; +using today::AddUnionTypeDetails; +using today::AddQueryDetails; +using today::AddPageInfoDetails; +using today::AddAppointmentEdgeDetails; +using today::AddAppointmentConnectionDetails; +using today::AddTaskEdgeDetails; +using today::AddTaskConnectionDetails; +using today::AddFolderEdgeDetails; +using today::AddFolderConnectionDetails; +using today::AddCompleteTaskPayloadDetails; +using today::AddMutationDetails; +using today::AddSubscriptionDetails; +using today::AddAppointmentDetails; +using today::AddTaskDetails; +using today::AddFolderDetails; +using today::AddNestedTypeDetails; +using today::AddExpensiveDetails; + +using today::GetSchema; + +} // namespace graphql::today diff --git a/samples/today/nointrospection/TodaySharedTypes.cpp b/samples/today/nointrospection/TodaySharedTypes.cpp new file mode 100644 index 00000000..ecae70c3 --- /dev/null +++ b/samples/today/nointrospection/TodaySharedTypes.cpp @@ -0,0 +1,747 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +#include "graphqlservice/GraphQLService.h" + +#include "TodaySharedTypes.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std::literals; + +namespace graphql { +namespace service { + +static const auto s_namesTaskState = today::getTaskStateNames(); +static const auto s_valuesTaskState = today::getTaskStateValues(); + +template <> +today::TaskState Argument::convert(const response::Value& value) +{ + if (!value.maybe_enum()) + { + throw service::schema_exception { { R"ex(not a valid TaskState value)ex" } }; + } + + const auto result = internal::sorted_map_lookup( + s_valuesTaskState, + std::string_view { value.get() }); + + if (!result) + { + throw service::schema_exception { { R"ex(not a valid TaskState value)ex" } }; + } + + return *result; +} + +template <> +service::AwaitableResolver Result::convert(service::AwaitableScalar result, ResolverParams&& params) +{ + return ModifiedResult::resolve(std::move(result), std::move(params), + [](today::TaskState value, const ResolverParams&) + { + const auto idx = static_cast(value); + + if (idx >= s_namesTaskState.size()) + { + throw service::schema_exception { { R"ex(Enum value out of range for TaskState)ex" } }; + } + + return ResolverResult { { response::ValueToken::EnumValue { std::string { s_namesTaskState[idx] } } } }; + }); +} + +template <> +void Result::validateScalar(const response::Value& value) +{ + if (!value.maybe_enum()) + { + throw service::schema_exception { { R"ex(not a valid TaskState value)ex" } }; + } + + const auto [itr, itrEnd] = internal::sorted_map_equal_range( + s_valuesTaskState.begin(), + s_valuesTaskState.end(), + std::string_view { value.get() }); + + if (itr == itrEnd) + { + throw service::schema_exception { { R"ex(not a valid TaskState value)ex" } }; + } +} + +template <> +today::CompleteTaskInput Argument::convert(const response::Value& value) +{ + const auto defaultValue = []() + { + response::Value values(response::Type::Map); + response::Value entry; + + entry = response::Value(true); + values.emplace_back("isComplete", std::move(entry)); + + return values; + }(); + + auto valueId = service::ModifiedArgument::require("id", value); + auto valueTestTaskState = service::ModifiedArgument::require("testTaskState", value); + auto pairIsComplete = service::ModifiedArgument::find("isComplete", value); + auto valueIsComplete = (pairIsComplete.second + ? std::move(pairIsComplete.first) + : service::ModifiedArgument::require("isComplete", defaultValue)); + auto valueClientMutationId = service::ModifiedArgument::require("clientMutationId", value); + auto valueBoolList = service::ModifiedArgument::require("boolList", value); + + return today::CompleteTaskInput { + std::move(valueId), + valueTestTaskState, + std::move(valueIsComplete), + std::move(valueClientMutationId), + std::move(valueBoolList) + }; +} + +template <> +today::ThirdNestedInput Argument::convert(const response::Value& value) +{ + auto valueId = service::ModifiedArgument::require("id", value); + auto valueSecond = service::ModifiedArgument::require("second", value); + + return today::ThirdNestedInput { + std::move(valueId), + std::move(valueSecond) + }; +} + +template <> +today::FourthNestedInput Argument::convert(const response::Value& value) +{ + auto valueId = service::ModifiedArgument::require("id", value); + + return today::FourthNestedInput { + std::move(valueId) + }; +} + +template <> +today::IncludeNullableSelfInput Argument::convert(const response::Value& value) +{ + auto valueSelf = service::ModifiedArgument::require("self", value); + + return today::IncludeNullableSelfInput { + std::move(valueSelf) + }; +} + +template <> +today::IncludeNonNullableListSelfInput Argument::convert(const response::Value& value) +{ + auto valueSelves = service::ModifiedArgument::require("selves", value); + + return today::IncludeNonNullableListSelfInput { + std::move(valueSelves) + }; +} + +template <> +today::StringOperationFilterInput Argument::convert(const response::Value& value) +{ + auto valueAnd_ = service::ModifiedArgument::require("and", value); + auto valueOr_ = service::ModifiedArgument::require("or", value); + auto valueEqual = service::ModifiedArgument::require("equal", value); + auto valueNotEqual = service::ModifiedArgument::require("notEqual", value); + auto valueContains = service::ModifiedArgument::require("contains", value); + auto valueNotContains = service::ModifiedArgument::require("notContains", value); + auto valueIn = service::ModifiedArgument::require("in", value); + auto valueNotIn = service::ModifiedArgument::require("notIn", value); + auto valueStartsWith = service::ModifiedArgument::require("startsWith", value); + auto valueNotStartsWith = service::ModifiedArgument::require("notStartsWith", value); + auto valueEndsWith = service::ModifiedArgument::require("endsWith", value); + auto valueNotEndsWith = service::ModifiedArgument::require("notEndsWith", value); + + return today::StringOperationFilterInput { + std::move(valueAnd_), + std::move(valueOr_), + std::move(valueEqual), + std::move(valueNotEqual), + std::move(valueContains), + std::move(valueNotContains), + std::move(valueIn), + std::move(valueNotIn), + std::move(valueStartsWith), + std::move(valueNotStartsWith), + std::move(valueEndsWith), + std::move(valueNotEndsWith) + }; +} + +template <> +today::SecondNestedInput Argument::convert(const response::Value& value) +{ + auto valueId = service::ModifiedArgument::require("id", value); + auto valueThird = service::ModifiedArgument::require("third", value); + + return today::SecondNestedInput { + std::move(valueId), + std::move(valueThird) + }; +} + +template <> +today::ForwardDeclaredInput Argument::convert(const response::Value& value) +{ + auto valueNullableSelf = service::ModifiedArgument::require("nullableSelf", value); + auto valueListSelves = service::ModifiedArgument::require("listSelves", value); + + return today::ForwardDeclaredInput { + std::move(valueNullableSelf), + std::move(valueListSelves) + }; +} + +template <> +today::FirstNestedInput Argument::convert(const response::Value& value) +{ + auto valueId = service::ModifiedArgument::require("id", value); + auto valueSecond = service::ModifiedArgument::require("second", value); + auto valueThird = service::ModifiedArgument::require("third", value); + + return today::FirstNestedInput { + std::move(valueId), + std::move(valueSecond), + std::move(valueThird) + }; +} + +} // namespace service + +namespace today { + +CompleteTaskInput::CompleteTaskInput() noexcept + : id {} + , testTaskState {} + , isComplete {} + , clientMutationId {} + , boolList {} +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +CompleteTaskInput::CompleteTaskInput( + response::IdType idArg, + std::optional testTaskStateArg, + std::optional isCompleteArg, + std::optional clientMutationIdArg, + std::optional> boolListArg) noexcept + : id { std::move(idArg) } + , testTaskState { std::move(testTaskStateArg) } + , isComplete { std::move(isCompleteArg) } + , clientMutationId { std::move(clientMutationIdArg) } + , boolList { std::move(boolListArg) } +{ +} + +CompleteTaskInput::CompleteTaskInput(const CompleteTaskInput& other) + : id { service::ModifiedArgument::duplicate(other.id) } + , testTaskState { service::ModifiedArgument::duplicate(other.testTaskState) } + , isComplete { service::ModifiedArgument::duplicate(other.isComplete) } + , clientMutationId { service::ModifiedArgument::duplicate(other.clientMutationId) } + , boolList { service::ModifiedArgument::duplicate(other.boolList) } +{ +} + +CompleteTaskInput::CompleteTaskInput(CompleteTaskInput&& other) noexcept + : id { std::move(other.id) } + , testTaskState { std::move(other.testTaskState) } + , isComplete { std::move(other.isComplete) } + , clientMutationId { std::move(other.clientMutationId) } + , boolList { std::move(other.boolList) } +{ +} + +CompleteTaskInput::~CompleteTaskInput() +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +CompleteTaskInput& CompleteTaskInput::operator=(const CompleteTaskInput& other) +{ + CompleteTaskInput value { other }; + + std::swap(*this, value); + + return *this; +} + +CompleteTaskInput& CompleteTaskInput::operator=(CompleteTaskInput&& other) noexcept +{ + id = std::move(other.id); + testTaskState = std::move(other.testTaskState); + isComplete = std::move(other.isComplete); + clientMutationId = std::move(other.clientMutationId); + boolList = std::move(other.boolList); + + return *this; +} + + +ThirdNestedInput::ThirdNestedInput() noexcept + : id {} + , second {} +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +ThirdNestedInput::ThirdNestedInput( + response::IdType idArg, + std::unique_ptr secondArg) noexcept + : id { std::move(idArg) } + , second { std::move(secondArg) } +{ +} + +ThirdNestedInput::ThirdNestedInput(const ThirdNestedInput& other) + : id { service::ModifiedArgument::duplicate(other.id) } + , second { service::ModifiedArgument::duplicate(other.second) } +{ +} + +ThirdNestedInput::ThirdNestedInput(ThirdNestedInput&& other) noexcept + : id { std::move(other.id) } + , second { std::move(other.second) } +{ +} + +ThirdNestedInput::~ThirdNestedInput() +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +ThirdNestedInput& ThirdNestedInput::operator=(const ThirdNestedInput& other) +{ + ThirdNestedInput value { other }; + + std::swap(*this, value); + + return *this; +} + +ThirdNestedInput& ThirdNestedInput::operator=(ThirdNestedInput&& other) noexcept +{ + id = std::move(other.id); + second = std::move(other.second); + + return *this; +} + + +FourthNestedInput::FourthNestedInput() noexcept + : id {} +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +FourthNestedInput::FourthNestedInput( + response::IdType idArg) noexcept + : id { std::move(idArg) } +{ +} + +FourthNestedInput::FourthNestedInput(const FourthNestedInput& other) + : id { service::ModifiedArgument::duplicate(other.id) } +{ +} + +FourthNestedInput::FourthNestedInput(FourthNestedInput&& other) noexcept + : id { std::move(other.id) } +{ +} + +FourthNestedInput::~FourthNestedInput() +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +FourthNestedInput& FourthNestedInput::operator=(const FourthNestedInput& other) +{ + FourthNestedInput value { other }; + + std::swap(*this, value); + + return *this; +} + +FourthNestedInput& FourthNestedInput::operator=(FourthNestedInput&& other) noexcept +{ + id = std::move(other.id); + + return *this; +} + + +IncludeNullableSelfInput::IncludeNullableSelfInput() noexcept + : self {} +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +IncludeNullableSelfInput::IncludeNullableSelfInput( + std::unique_ptr selfArg) noexcept + : self { std::move(selfArg) } +{ +} + +IncludeNullableSelfInput::IncludeNullableSelfInput(const IncludeNullableSelfInput& other) + : self { service::ModifiedArgument::duplicate(other.self) } +{ +} + +IncludeNullableSelfInput::IncludeNullableSelfInput(IncludeNullableSelfInput&& other) noexcept + : self { std::move(other.self) } +{ +} + +IncludeNullableSelfInput::~IncludeNullableSelfInput() +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +IncludeNullableSelfInput& IncludeNullableSelfInput::operator=(const IncludeNullableSelfInput& other) +{ + IncludeNullableSelfInput value { other }; + + std::swap(*this, value); + + return *this; +} + +IncludeNullableSelfInput& IncludeNullableSelfInput::operator=(IncludeNullableSelfInput&& other) noexcept +{ + self = std::move(other.self); + + return *this; +} + + +IncludeNonNullableListSelfInput::IncludeNonNullableListSelfInput() noexcept + : selves {} +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +IncludeNonNullableListSelfInput::IncludeNonNullableListSelfInput( + std::vector selvesArg) noexcept + : selves { std::move(selvesArg) } +{ +} + +IncludeNonNullableListSelfInput::IncludeNonNullableListSelfInput(const IncludeNonNullableListSelfInput& other) + : selves { service::ModifiedArgument::duplicate(other.selves) } +{ +} + +IncludeNonNullableListSelfInput::IncludeNonNullableListSelfInput(IncludeNonNullableListSelfInput&& other) noexcept + : selves { std::move(other.selves) } +{ +} + +IncludeNonNullableListSelfInput::~IncludeNonNullableListSelfInput() +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +IncludeNonNullableListSelfInput& IncludeNonNullableListSelfInput::operator=(const IncludeNonNullableListSelfInput& other) +{ + IncludeNonNullableListSelfInput value { other }; + + std::swap(*this, value); + + return *this; +} + +IncludeNonNullableListSelfInput& IncludeNonNullableListSelfInput::operator=(IncludeNonNullableListSelfInput&& other) noexcept +{ + selves = std::move(other.selves); + + return *this; +} + + +StringOperationFilterInput::StringOperationFilterInput() noexcept + : and_ {} + , or_ {} + , equal {} + , notEqual {} + , contains {} + , notContains {} + , in {} + , notIn {} + , startsWith {} + , notStartsWith {} + , endsWith {} + , notEndsWith {} +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +StringOperationFilterInput::StringOperationFilterInput( + std::optional> and_Arg, + std::optional> or_Arg, + std::optional equalArg, + std::optional notEqualArg, + std::optional containsArg, + std::optional notContainsArg, + std::optional> inArg, + std::optional> notInArg, + std::optional startsWithArg, + std::optional notStartsWithArg, + std::optional endsWithArg, + std::optional notEndsWithArg) noexcept + : and_ { std::move(and_Arg) } + , or_ { std::move(or_Arg) } + , equal { std::move(equalArg) } + , notEqual { std::move(notEqualArg) } + , contains { std::move(containsArg) } + , notContains { std::move(notContainsArg) } + , in { std::move(inArg) } + , notIn { std::move(notInArg) } + , startsWith { std::move(startsWithArg) } + , notStartsWith { std::move(notStartsWithArg) } + , endsWith { std::move(endsWithArg) } + , notEndsWith { std::move(notEndsWithArg) } +{ +} + +StringOperationFilterInput::StringOperationFilterInput(const StringOperationFilterInput& other) + : and_ { service::ModifiedArgument::duplicate(other.and_) } + , or_ { service::ModifiedArgument::duplicate(other.or_) } + , equal { service::ModifiedArgument::duplicate(other.equal) } + , notEqual { service::ModifiedArgument::duplicate(other.notEqual) } + , contains { service::ModifiedArgument::duplicate(other.contains) } + , notContains { service::ModifiedArgument::duplicate(other.notContains) } + , in { service::ModifiedArgument::duplicate(other.in) } + , notIn { service::ModifiedArgument::duplicate(other.notIn) } + , startsWith { service::ModifiedArgument::duplicate(other.startsWith) } + , notStartsWith { service::ModifiedArgument::duplicate(other.notStartsWith) } + , endsWith { service::ModifiedArgument::duplicate(other.endsWith) } + , notEndsWith { service::ModifiedArgument::duplicate(other.notEndsWith) } +{ +} + +StringOperationFilterInput::StringOperationFilterInput(StringOperationFilterInput&& other) noexcept + : and_ { std::move(other.and_) } + , or_ { std::move(other.or_) } + , equal { std::move(other.equal) } + , notEqual { std::move(other.notEqual) } + , contains { std::move(other.contains) } + , notContains { std::move(other.notContains) } + , in { std::move(other.in) } + , notIn { std::move(other.notIn) } + , startsWith { std::move(other.startsWith) } + , notStartsWith { std::move(other.notStartsWith) } + , endsWith { std::move(other.endsWith) } + , notEndsWith { std::move(other.notEndsWith) } +{ +} + +StringOperationFilterInput::~StringOperationFilterInput() +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +StringOperationFilterInput& StringOperationFilterInput::operator=(const StringOperationFilterInput& other) +{ + StringOperationFilterInput value { other }; + + std::swap(*this, value); + + return *this; +} + +StringOperationFilterInput& StringOperationFilterInput::operator=(StringOperationFilterInput&& other) noexcept +{ + and_ = std::move(other.and_); + or_ = std::move(other.or_); + equal = std::move(other.equal); + notEqual = std::move(other.notEqual); + contains = std::move(other.contains); + notContains = std::move(other.notContains); + in = std::move(other.in); + notIn = std::move(other.notIn); + startsWith = std::move(other.startsWith); + notStartsWith = std::move(other.notStartsWith); + endsWith = std::move(other.endsWith); + notEndsWith = std::move(other.notEndsWith); + + return *this; +} + + +SecondNestedInput::SecondNestedInput() noexcept + : id {} + , third {} +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +SecondNestedInput::SecondNestedInput( + response::IdType idArg, + ThirdNestedInput thirdArg) noexcept + : id { std::move(idArg) } + , third { std::move(thirdArg) } +{ +} + +SecondNestedInput::SecondNestedInput(const SecondNestedInput& other) + : id { service::ModifiedArgument::duplicate(other.id) } + , third { service::ModifiedArgument::duplicate(other.third) } +{ +} + +SecondNestedInput::SecondNestedInput(SecondNestedInput&& other) noexcept + : id { std::move(other.id) } + , third { std::move(other.third) } +{ +} + +SecondNestedInput::~SecondNestedInput() +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +SecondNestedInput& SecondNestedInput::operator=(const SecondNestedInput& other) +{ + SecondNestedInput value { other }; + + std::swap(*this, value); + + return *this; +} + +SecondNestedInput& SecondNestedInput::operator=(SecondNestedInput&& other) noexcept +{ + id = std::move(other.id); + third = std::move(other.third); + + return *this; +} + + +ForwardDeclaredInput::ForwardDeclaredInput() noexcept + : nullableSelf {} + , listSelves {} +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +ForwardDeclaredInput::ForwardDeclaredInput( + std::unique_ptr nullableSelfArg, + IncludeNonNullableListSelfInput listSelvesArg) noexcept + : nullableSelf { std::move(nullableSelfArg) } + , listSelves { std::move(listSelvesArg) } +{ +} + +ForwardDeclaredInput::ForwardDeclaredInput(const ForwardDeclaredInput& other) + : nullableSelf { service::ModifiedArgument::duplicate(other.nullableSelf) } + , listSelves { service::ModifiedArgument::duplicate(other.listSelves) } +{ +} + +ForwardDeclaredInput::ForwardDeclaredInput(ForwardDeclaredInput&& other) noexcept + : nullableSelf { std::move(other.nullableSelf) } + , listSelves { std::move(other.listSelves) } +{ +} + +ForwardDeclaredInput::~ForwardDeclaredInput() +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +ForwardDeclaredInput& ForwardDeclaredInput::operator=(const ForwardDeclaredInput& other) +{ + ForwardDeclaredInput value { other }; + + std::swap(*this, value); + + return *this; +} + +ForwardDeclaredInput& ForwardDeclaredInput::operator=(ForwardDeclaredInput&& other) noexcept +{ + nullableSelf = std::move(other.nullableSelf); + listSelves = std::move(other.listSelves); + + return *this; +} + + +FirstNestedInput::FirstNestedInput() noexcept + : id {} + , second {} + , third {} +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +FirstNestedInput::FirstNestedInput( + response::IdType idArg, + SecondNestedInput secondArg, + ThirdNestedInput thirdArg) noexcept + : id { std::move(idArg) } + , second { std::move(secondArg) } + , third { std::move(thirdArg) } +{ +} + +FirstNestedInput::FirstNestedInput(const FirstNestedInput& other) + : id { service::ModifiedArgument::duplicate(other.id) } + , second { service::ModifiedArgument::duplicate(other.second) } + , third { service::ModifiedArgument::duplicate(other.third) } +{ +} + +FirstNestedInput::FirstNestedInput(FirstNestedInput&& other) noexcept + : id { std::move(other.id) } + , second { std::move(other.second) } + , third { std::move(other.third) } +{ +} + +FirstNestedInput::~FirstNestedInput() +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +FirstNestedInput& FirstNestedInput::operator=(const FirstNestedInput& other) +{ + FirstNestedInput value { other }; + + std::swap(*this, value); + + return *this; +} + +FirstNestedInput& FirstNestedInput::operator=(FirstNestedInput&& other) noexcept +{ + id = std::move(other.id); + second = std::move(other.second); + third = std::move(other.third); + + return *this; +} + +} // namespace today +} // namespace graphql diff --git a/samples/today/nointrospection/TodaySharedTypes.h b/samples/today/nointrospection/TodaySharedTypes.h new file mode 100644 index 00000000..a9f1ace9 --- /dev/null +++ b/samples/today/nointrospection/TodaySharedTypes.h @@ -0,0 +1,241 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +#pragma once + +#ifndef TODAYSHAREDTYPES_H +#define TODAYSHAREDTYPES_H + +#include "graphqlservice/GraphQLResponse.h" + +#include "graphqlservice/internal/Version.h" + +#include +#include +#include +#include +#include +#include + +// Check if the library version is compatible with schemagen 5.0.0 +static_assert(graphql::internal::MajorVersion == 5, "regenerate with schemagen: major version mismatch"); +static_assert(graphql::internal::MinorVersion == 0, "regenerate with schemagen: minor version mismatch"); + +namespace graphql { +namespace today { + +enum class [[nodiscard("unnecessary conversion")]] TaskState +{ + Unassigned, + New, + Started, + Complete +}; + +[[nodiscard("unnecessary call")]] constexpr auto getTaskStateNames() noexcept +{ + using namespace std::literals; + + return std::array { + R"gql(Unassigned)gql"sv, + R"gql(New)gql"sv, + R"gql(Started)gql"sv, + R"gql(Complete)gql"sv + }; +} + +[[nodiscard("unnecessary call")]] constexpr auto getTaskStateValues() noexcept +{ + using namespace std::literals; + + return std::array, 4> { + std::make_pair(R"gql(New)gql"sv, TaskState::New), + std::make_pair(R"gql(Started)gql"sv, TaskState::Started), + std::make_pair(R"gql(Complete)gql"sv, TaskState::Complete), + std::make_pair(R"gql(Unassigned)gql"sv, TaskState::Unassigned) + }; +} + +struct [[nodiscard("unnecessary construction")]] CompleteTaskInput +{ + explicit CompleteTaskInput() noexcept; + explicit CompleteTaskInput( + response::IdType idArg, + std::optional testTaskStateArg, + std::optional isCompleteArg, + std::optional clientMutationIdArg, + std::optional> boolListArg) noexcept; + CompleteTaskInput(const CompleteTaskInput& other); + CompleteTaskInput(CompleteTaskInput&& other) noexcept; + ~CompleteTaskInput(); + + CompleteTaskInput& operator=(const CompleteTaskInput& other); + CompleteTaskInput& operator=(CompleteTaskInput&& other) noexcept; + + response::IdType id; + std::optional testTaskState; + std::optional isComplete; + std::optional clientMutationId; + std::optional> boolList; +}; + +struct SecondNestedInput; + +struct [[nodiscard("unnecessary construction")]] ThirdNestedInput +{ + explicit ThirdNestedInput() noexcept; + explicit ThirdNestedInput( + response::IdType idArg, + std::unique_ptr secondArg) noexcept; + ThirdNestedInput(const ThirdNestedInput& other); + ThirdNestedInput(ThirdNestedInput&& other) noexcept; + ~ThirdNestedInput(); + + ThirdNestedInput& operator=(const ThirdNestedInput& other); + ThirdNestedInput& operator=(ThirdNestedInput&& other) noexcept; + + response::IdType id; + std::unique_ptr second; +}; + +struct [[nodiscard("unnecessary construction")]] FourthNestedInput +{ + explicit FourthNestedInput() noexcept; + explicit FourthNestedInput( + response::IdType idArg) noexcept; + FourthNestedInput(const FourthNestedInput& other); + FourthNestedInput(FourthNestedInput&& other) noexcept; + ~FourthNestedInput(); + + FourthNestedInput& operator=(const FourthNestedInput& other); + FourthNestedInput& operator=(FourthNestedInput&& other) noexcept; + + response::IdType id; +}; + +struct [[nodiscard("unnecessary construction")]] IncludeNullableSelfInput +{ + explicit IncludeNullableSelfInput() noexcept; + explicit IncludeNullableSelfInput( + std::unique_ptr selfArg) noexcept; + IncludeNullableSelfInput(const IncludeNullableSelfInput& other); + IncludeNullableSelfInput(IncludeNullableSelfInput&& other) noexcept; + ~IncludeNullableSelfInput(); + + IncludeNullableSelfInput& operator=(const IncludeNullableSelfInput& other); + IncludeNullableSelfInput& operator=(IncludeNullableSelfInput&& other) noexcept; + + std::unique_ptr self; +}; + +struct [[nodiscard("unnecessary construction")]] IncludeNonNullableListSelfInput +{ + explicit IncludeNonNullableListSelfInput() noexcept; + explicit IncludeNonNullableListSelfInput( + std::vector selvesArg) noexcept; + IncludeNonNullableListSelfInput(const IncludeNonNullableListSelfInput& other); + IncludeNonNullableListSelfInput(IncludeNonNullableListSelfInput&& other) noexcept; + ~IncludeNonNullableListSelfInput(); + + IncludeNonNullableListSelfInput& operator=(const IncludeNonNullableListSelfInput& other); + IncludeNonNullableListSelfInput& operator=(IncludeNonNullableListSelfInput&& other) noexcept; + + std::vector selves; +}; + +struct [[nodiscard("unnecessary construction")]] StringOperationFilterInput +{ + explicit StringOperationFilterInput() noexcept; + explicit StringOperationFilterInput( + std::optional> and_Arg, + std::optional> or_Arg, + std::optional equalArg, + std::optional notEqualArg, + std::optional containsArg, + std::optional notContainsArg, + std::optional> inArg, + std::optional> notInArg, + std::optional startsWithArg, + std::optional notStartsWithArg, + std::optional endsWithArg, + std::optional notEndsWithArg) noexcept; + StringOperationFilterInput(const StringOperationFilterInput& other); + StringOperationFilterInput(StringOperationFilterInput&& other) noexcept; + ~StringOperationFilterInput(); + + StringOperationFilterInput& operator=(const StringOperationFilterInput& other); + StringOperationFilterInput& operator=(StringOperationFilterInput&& other) noexcept; + + std::optional> and_; + std::optional> or_; + std::optional equal; + std::optional notEqual; + std::optional contains; + std::optional notContains; + std::optional> in; + std::optional> notIn; + std::optional startsWith; + std::optional notStartsWith; + std::optional endsWith; + std::optional notEndsWith; +}; + +struct [[nodiscard("unnecessary construction")]] SecondNestedInput +{ + explicit SecondNestedInput() noexcept; + explicit SecondNestedInput( + response::IdType idArg, + ThirdNestedInput thirdArg) noexcept; + SecondNestedInput(const SecondNestedInput& other); + SecondNestedInput(SecondNestedInput&& other) noexcept; + ~SecondNestedInput(); + + SecondNestedInput& operator=(const SecondNestedInput& other); + SecondNestedInput& operator=(SecondNestedInput&& other) noexcept; + + response::IdType id; + ThirdNestedInput third; +}; + +struct [[nodiscard("unnecessary construction")]] ForwardDeclaredInput +{ + explicit ForwardDeclaredInput() noexcept; + explicit ForwardDeclaredInput( + std::unique_ptr nullableSelfArg, + IncludeNonNullableListSelfInput listSelvesArg) noexcept; + ForwardDeclaredInput(const ForwardDeclaredInput& other); + ForwardDeclaredInput(ForwardDeclaredInput&& other) noexcept; + ~ForwardDeclaredInput(); + + ForwardDeclaredInput& operator=(const ForwardDeclaredInput& other); + ForwardDeclaredInput& operator=(ForwardDeclaredInput&& other) noexcept; + + std::unique_ptr nullableSelf; + IncludeNonNullableListSelfInput listSelves; +}; + +struct [[nodiscard("unnecessary construction")]] FirstNestedInput +{ + explicit FirstNestedInput() noexcept; + explicit FirstNestedInput( + response::IdType idArg, + SecondNestedInput secondArg, + ThirdNestedInput thirdArg) noexcept; + FirstNestedInput(const FirstNestedInput& other); + FirstNestedInput(FirstNestedInput&& other) noexcept; + ~FirstNestedInput(); + + FirstNestedInput& operator=(const FirstNestedInput& other); + FirstNestedInput& operator=(FirstNestedInput&& other) noexcept; + + response::IdType id; + SecondNestedInput second; + ThirdNestedInput third; +}; + +} // namespace today +} // namespace graphql + +#endif // TODAYSHAREDTYPES_H diff --git a/samples/today/nointrospection/TodaySharedTypes.ixx b/samples/today/nointrospection/TodaySharedTypes.ixx new file mode 100644 index 00000000..ee76a95a --- /dev/null +++ b/samples/today/nointrospection/TodaySharedTypes.ixx @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodaySharedTypes.h" + +export module GraphQL.Today.TodaySharedTypes; + +export namespace graphql::today { + +using today::TaskState; +using today::getTaskStateNames; +using today::getTaskStateValues; + +using today::CompleteTaskInput; +using today::ThirdNestedInput; +using today::FourthNestedInput; +using today::IncludeNullableSelfInput; +using today::IncludeNonNullableListSelfInput; +using today::StringOperationFilterInput; +using today::SecondNestedInput; +using today::ForwardDeclaredInput; +using today::FirstNestedInput; + +} // namespace graphql::today diff --git a/samples/today/nointrospection/SubscriptionObject.h b/samples/today/nointrospection/TodaySubscriptionObject.h similarity index 97% rename from samples/today/nointrospection/SubscriptionObject.h rename to samples/today/nointrospection/TodaySubscriptionObject.h index c9179d85..5e21ac98 100644 --- a/samples/today/nointrospection/SubscriptionObject.h +++ b/samples/today/nointrospection/TodaySubscriptionObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef SUBSCRIPTIONOBJECT_H -#define SUBSCRIPTIONOBJECT_H +#ifndef TODAY_TODAYSUBSCRIPTIONOBJECT_H +#define TODAY_TODAYSUBSCRIPTIONOBJECT_H #include "TodaySchema.h" @@ -157,4 +157,4 @@ class [[nodiscard("unnecessary construction")]] Subscription final } // namespace graphql::today::object -#endif // SUBSCRIPTIONOBJECT_H +#endif // TODAY_TODAYSUBSCRIPTIONOBJECT_H diff --git a/samples/today/schema/TaskConnectionObject.h b/samples/today/nointrospection/TodayTaskConnectionObject.h similarity index 97% rename from samples/today/schema/TaskConnectionObject.h rename to samples/today/nointrospection/TodayTaskConnectionObject.h index c82b2ae7..46d4153e 100644 --- a/samples/today/schema/TaskConnectionObject.h +++ b/samples/today/nointrospection/TodayTaskConnectionObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef TASKCONNECTIONOBJECT_H -#define TASKCONNECTIONOBJECT_H +#ifndef TODAY_TODAYTASKCONNECTIONOBJECT_H +#define TODAY_TODAYTASKCONNECTIONOBJECT_H #include "TodaySchema.h" @@ -157,4 +157,4 @@ class [[nodiscard("unnecessary construction")]] TaskConnection final } // namespace graphql::today::object -#endif // TASKCONNECTIONOBJECT_H +#endif // TODAY_TODAYTASKCONNECTIONOBJECT_H diff --git a/samples/today/nointrospection/TaskEdgeObject.h b/samples/today/nointrospection/TodayTaskEdgeObject.h similarity index 97% rename from samples/today/nointrospection/TaskEdgeObject.h rename to samples/today/nointrospection/TodayTaskEdgeObject.h index 4049d5c3..f4e29482 100644 --- a/samples/today/nointrospection/TaskEdgeObject.h +++ b/samples/today/nointrospection/TodayTaskEdgeObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef TASKEDGEOBJECT_H -#define TASKEDGEOBJECT_H +#ifndef TODAY_TODAYTASKEDGEOBJECT_H +#define TODAY_TODAYTASKEDGEOBJECT_H #include "TodaySchema.h" @@ -157,4 +157,4 @@ class [[nodiscard("unnecessary construction")]] TaskEdge final } // namespace graphql::today::object -#endif // TASKEDGEOBJECT_H +#endif // TODAY_TODAYTASKEDGEOBJECT_H diff --git a/samples/today/schema/TaskObject.h b/samples/today/nointrospection/TodayTaskObject.h similarity index 98% rename from samples/today/schema/TaskObject.h rename to samples/today/nointrospection/TodayTaskObject.h index 36548d0b..d2d31f91 100644 --- a/samples/today/schema/TaskObject.h +++ b/samples/today/nointrospection/TodayTaskObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef TASKOBJECT_H -#define TASKOBJECT_H +#ifndef TODAY_TODAYTASKOBJECT_H +#define TODAY_TODAYTASKOBJECT_H #include "TodaySchema.h" @@ -206,4 +206,4 @@ class [[nodiscard("unnecessary construction")]] Task final } // namespace graphql::today::object -#endif // TASKOBJECT_H +#endif // TODAY_TODAYTASKOBJECT_H diff --git a/samples/today/schema/UnionTypeObject.h b/samples/today/nointrospection/TodayUnionTypeObject.h similarity index 95% rename from samples/today/schema/UnionTypeObject.h rename to samples/today/nointrospection/TodayUnionTypeObject.h index 1be8494e..b99d420d 100644 --- a/samples/today/schema/UnionTypeObject.h +++ b/samples/today/nointrospection/TodayUnionTypeObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef UNIONTYPEOBJECT_H -#define UNIONTYPEOBJECT_H +#ifndef TODAY_TODAYUNIONTYPEOBJECT_H +#define TODAY_TODAYUNIONTYPEOBJECT_H #include "TodaySchema.h" @@ -78,4 +78,4 @@ class [[nodiscard("unnecessary construction")]] UnionType final } // namespace graphql::today::object -#endif // UNIONTYPEOBJECT_H +#endif // TODAY_TODAYUNIONTYPEOBJECT_H diff --git a/samples/today/nointrospection/UnionTypeObject.cpp b/samples/today/nointrospection/UnionTypeObject.cpp index 9904e2dc..fe762703 100644 --- a/samples/today/nointrospection/UnionTypeObject.cpp +++ b/samples/today/nointrospection/UnionTypeObject.cpp @@ -3,7 +3,7 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "UnionTypeObject.h" +#include "TodayUnionTypeObject.h" #include "graphqlservice/internal/Schema.h" diff --git a/samples/today/nointrospection/UnionTypeObject.ixx b/samples/today/nointrospection/UnionTypeObject.ixx new file mode 100644 index 00000000..73496d86 --- /dev/null +++ b/samples/today/nointrospection/UnionTypeObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayUnionTypeObject.h" + +export module GraphQL.Today.UnionTypeObject; + +export namespace graphql::today::object { + +using object::UnionType; + +} // namespace graphql::today::object diff --git a/samples/today/nointrospection/today_nointrospection_schema_files b/samples/today/nointrospection/today_nointrospection_schema_files index a9ed9ecf..2e24cdcf 100644 --- a/samples/today/nointrospection/today_nointrospection_schema_files +++ b/samples/today/nointrospection/today_nointrospection_schema_files @@ -1,3 +1,4 @@ +TodaySharedTypes.cpp TodaySchema.cpp NodeObject.cpp UnionTypeObject.cpp diff --git a/samples/today/sample.cpp b/samples/today/sample.cpp index e47f6bda..e4a313bd 100644 --- a/samples/today/sample.cpp +++ b/samples/today/sample.cpp @@ -1,20 +1,22 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "TodayMock.h" - -#include "graphqlservice/JSONResponse.h" - #include #include #include #include +import GraphQL.Parse; +import GraphQL.JSONResponse; +import GraphQL.Service; + +import GraphQL.Today.Mock; + using namespace graphql; int main(int argc, char** argv) { - const auto mockService = today::mock_service(); + const auto mockService = graphql::today::mock_service(); const auto& service = mockService->service; std::cout << "Created the service..." << std::endl; diff --git a/samples/today/schema.today.graphql b/samples/today/schema.today.graphql index 8c794450..59b77ae9 100644 --- a/samples/today/schema.today.graphql +++ b/samples/today/schema.today.graphql @@ -87,6 +87,7 @@ input CompleteTaskInput { testTaskState: TaskState isComplete: Boolean = true clientMutationId: String + boolList: [Boolean!] } type CompleteTaskPayload { @@ -131,6 +132,7 @@ type Appointment implements Node { subject: String isNow: Boolean! forceError: String + array: [ID!]! } type Task implements Node { diff --git a/samples/today/schema/AppointmentConnectionObject.cpp b/samples/today/schema/AppointmentConnectionObject.cpp index d0fe0178..ad970e9c 100644 --- a/samples/today/schema/AppointmentConnectionObject.cpp +++ b/samples/today/schema/AppointmentConnectionObject.cpp @@ -3,9 +3,9 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "AppointmentConnectionObject.h" -#include "PageInfoObject.h" -#include "AppointmentEdgeObject.h" +#include "TodayAppointmentConnectionObject.h" +#include "TodayPageInfoObject.h" +#include "TodayAppointmentEdgeObject.h" #include "graphqlservice/internal/Schema.h" @@ -13,7 +13,6 @@ #include #include -#include #include #include diff --git a/samples/today/schema/AppointmentConnectionObject.ixx b/samples/today/schema/AppointmentConnectionObject.ixx new file mode 100644 index 00000000..3f73da58 --- /dev/null +++ b/samples/today/schema/AppointmentConnectionObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayAppointmentConnectionObject.h" + +export module GraphQL.Today.AppointmentConnectionObject; + +export namespace graphql::today::object { + +using object::AppointmentConnection; + +} // namespace graphql::today::object diff --git a/samples/today/schema/AppointmentEdgeObject.cpp b/samples/today/schema/AppointmentEdgeObject.cpp index f599fdf2..1aa60cf4 100644 --- a/samples/today/schema/AppointmentEdgeObject.cpp +++ b/samples/today/schema/AppointmentEdgeObject.cpp @@ -3,8 +3,8 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "AppointmentEdgeObject.h" -#include "AppointmentObject.h" +#include "TodayAppointmentEdgeObject.h" +#include "TodayAppointmentObject.h" #include "graphqlservice/internal/Schema.h" @@ -12,7 +12,6 @@ #include #include -#include #include #include diff --git a/samples/today/schema/AppointmentEdgeObject.ixx b/samples/today/schema/AppointmentEdgeObject.ixx new file mode 100644 index 00000000..46a48b9e --- /dev/null +++ b/samples/today/schema/AppointmentEdgeObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayAppointmentEdgeObject.h" + +export module GraphQL.Today.AppointmentEdgeObject; + +export namespace graphql::today::object { + +using object::AppointmentEdge; + +} // namespace graphql::today::object diff --git a/samples/today/schema/AppointmentObject.cpp b/samples/today/schema/AppointmentObject.cpp index 63062308..ada849cd 100644 --- a/samples/today/schema/AppointmentObject.cpp +++ b/samples/today/schema/AppointmentObject.cpp @@ -3,7 +3,7 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "AppointmentObject.h" +#include "TodayAppointmentObject.h" #include "graphqlservice/internal/Schema.h" @@ -11,7 +11,6 @@ #include #include -#include #include #include @@ -40,6 +39,7 @@ service::ResolverMap Appointment::getResolvers() const noexcept return { { R"gql(id)gql"sv, [this](service::ResolverParams&& params) { return resolveId(std::move(params)); } }, { R"gql(when)gql"sv, [this](service::ResolverParams&& params) { return resolveWhen(std::move(params)); } }, + { R"gql(array)gql"sv, [this](service::ResolverParams&& params) { return resolveArray(std::move(params)); } }, { R"gql(isNow)gql"sv, [this](service::ResolverParams&& params) { return resolveIsNow(std::move(params)); } }, { R"gql(subject)gql"sv, [this](service::ResolverParams&& params) { return resolveSubject(std::move(params)); } }, { R"gql(__typename)gql"sv, [this](service::ResolverParams&& params) { return resolve_typename(std::move(params)); } }, @@ -112,6 +112,17 @@ service::AwaitableResolver Appointment::resolveForceError(service::ResolverParam return service::ModifiedResult::convert(std::move(result), std::move(params)); } +service::AwaitableResolver Appointment::resolveArray(service::ResolverParams&& params) const +{ + std::unique_lock resolverLock(_resolverMutex); + service::SelectionSetParams selectionSetParams { static_cast(params) }; + auto directives = std::move(params.fieldDirectives); + auto result = _pimpl->getArray(service::FieldParams { std::move(selectionSetParams), std::move(directives) }); + resolverLock.unlock(); + + return service::ModifiedResult::convert(std::move(result), std::move(params)); +} + service::AwaitableResolver Appointment::resolve_typename(service::ResolverParams&& params) const { return service::Result::convert(std::string{ R"gql(Appointment)gql" }, std::move(params)); @@ -129,7 +140,8 @@ void AddAppointmentDetails(const std::shared_ptr& typeAppoin schema::Field::Make(R"gql(when)gql"sv, R"md()md"sv, std::nullopt, schema->LookupType(R"gql(DateTime)gql"sv)), schema::Field::Make(R"gql(subject)gql"sv, R"md()md"sv, std::nullopt, schema->LookupType(R"gql(String)gql"sv)), schema::Field::Make(R"gql(isNow)gql"sv, R"md()md"sv, std::nullopt, schema->WrapType(introspection::TypeKind::NON_NULL, schema->LookupType(R"gql(Boolean)gql"sv))), - schema::Field::Make(R"gql(forceError)gql"sv, R"md()md"sv, std::nullopt, schema->LookupType(R"gql(String)gql"sv)) + schema::Field::Make(R"gql(forceError)gql"sv, R"md()md"sv, std::nullopt, schema->LookupType(R"gql(String)gql"sv)), + schema::Field::Make(R"gql(array)gql"sv, R"md()md"sv, std::nullopt, schema->WrapType(introspection::TypeKind::NON_NULL, schema->WrapType(introspection::TypeKind::LIST, schema->WrapType(introspection::TypeKind::NON_NULL, schema->LookupType(R"gql(ID)gql"sv))))) }); } diff --git a/samples/today/schema/AppointmentObject.ixx b/samples/today/schema/AppointmentObject.ixx new file mode 100644 index 00000000..2fc474d8 --- /dev/null +++ b/samples/today/schema/AppointmentObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayAppointmentObject.h" + +export module GraphQL.Today.AppointmentObject; + +export namespace graphql::today::object { + +using object::Appointment; + +} // namespace graphql::today::object diff --git a/samples/today/schema/CMakeLists.txt b/samples/today/schema/CMakeLists.txt index 0c6403ec..2c2dafe1 100644 --- a/samples/today/schema/CMakeLists.txt +++ b/samples/today/schema/CMakeLists.txt @@ -1,13 +1,13 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.28) # Normally this would be handled by find_package(cppgraphqlgen CONFIG). include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/cppgraphqlgen-functions.cmake) if(GRAPHQL_UPDATE_SAMPLES) - update_graphql_schema_files(today ../schema.today.graphql Today today --stubs) + update_graphql_schema_files(today ../schema.today.graphql Today today --stubs --prefix-headers) endif() add_graphql_schema_target(today) diff --git a/samples/today/schema/CompleteTaskPayloadObject.cpp b/samples/today/schema/CompleteTaskPayloadObject.cpp index 0e0e08b0..3052500b 100644 --- a/samples/today/schema/CompleteTaskPayloadObject.cpp +++ b/samples/today/schema/CompleteTaskPayloadObject.cpp @@ -3,8 +3,8 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "CompleteTaskPayloadObject.h" -#include "TaskObject.h" +#include "TodayCompleteTaskPayloadObject.h" +#include "TodayTaskObject.h" #include "graphqlservice/internal/Schema.h" @@ -12,7 +12,6 @@ #include #include -#include #include #include diff --git a/samples/today/schema/CompleteTaskPayloadObject.ixx b/samples/today/schema/CompleteTaskPayloadObject.ixx new file mode 100644 index 00000000..f32cd43c --- /dev/null +++ b/samples/today/schema/CompleteTaskPayloadObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayCompleteTaskPayloadObject.h" + +export module GraphQL.Today.CompleteTaskPayloadObject; + +export namespace graphql::today::object { + +using object::CompleteTaskPayload; + +} // namespace graphql::today::object diff --git a/samples/today/schema/ExpensiveObject.cpp b/samples/today/schema/ExpensiveObject.cpp index 4583f271..7c359517 100644 --- a/samples/today/schema/ExpensiveObject.cpp +++ b/samples/today/schema/ExpensiveObject.cpp @@ -3,7 +3,7 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "ExpensiveObject.h" +#include "TodayExpensiveObject.h" #include "graphqlservice/internal/Schema.h" @@ -11,7 +11,6 @@ #include #include -#include #include #include diff --git a/samples/today/schema/ExpensiveObject.ixx b/samples/today/schema/ExpensiveObject.ixx new file mode 100644 index 00000000..8cdea947 --- /dev/null +++ b/samples/today/schema/ExpensiveObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayExpensiveObject.h" + +export module GraphQL.Today.ExpensiveObject; + +export namespace graphql::today::object { + +using object::Expensive; + +} // namespace graphql::today::object diff --git a/samples/today/schema/FolderConnectionObject.cpp b/samples/today/schema/FolderConnectionObject.cpp index 75a588a3..334dcf14 100644 --- a/samples/today/schema/FolderConnectionObject.cpp +++ b/samples/today/schema/FolderConnectionObject.cpp @@ -3,9 +3,9 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "FolderConnectionObject.h" -#include "PageInfoObject.h" -#include "FolderEdgeObject.h" +#include "TodayFolderConnectionObject.h" +#include "TodayPageInfoObject.h" +#include "TodayFolderEdgeObject.h" #include "graphqlservice/internal/Schema.h" @@ -13,7 +13,6 @@ #include #include -#include #include #include diff --git a/samples/today/schema/FolderConnectionObject.ixx b/samples/today/schema/FolderConnectionObject.ixx new file mode 100644 index 00000000..1a8e93ea --- /dev/null +++ b/samples/today/schema/FolderConnectionObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayFolderConnectionObject.h" + +export module GraphQL.Today.FolderConnectionObject; + +export namespace graphql::today::object { + +using object::FolderConnection; + +} // namespace graphql::today::object diff --git a/samples/today/schema/FolderEdgeObject.cpp b/samples/today/schema/FolderEdgeObject.cpp index 203c1aa2..5e2e341f 100644 --- a/samples/today/schema/FolderEdgeObject.cpp +++ b/samples/today/schema/FolderEdgeObject.cpp @@ -3,8 +3,8 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "FolderEdgeObject.h" -#include "FolderObject.h" +#include "TodayFolderEdgeObject.h" +#include "TodayFolderObject.h" #include "graphqlservice/internal/Schema.h" @@ -12,7 +12,6 @@ #include #include -#include #include #include diff --git a/samples/today/schema/FolderEdgeObject.ixx b/samples/today/schema/FolderEdgeObject.ixx new file mode 100644 index 00000000..214cb206 --- /dev/null +++ b/samples/today/schema/FolderEdgeObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayFolderEdgeObject.h" + +export module GraphQL.Today.FolderEdgeObject; + +export namespace graphql::today::object { + +using object::FolderEdge; + +} // namespace graphql::today::object diff --git a/samples/today/schema/FolderObject.cpp b/samples/today/schema/FolderObject.cpp index 9f0b44e5..4b35c950 100644 --- a/samples/today/schema/FolderObject.cpp +++ b/samples/today/schema/FolderObject.cpp @@ -3,7 +3,7 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "FolderObject.h" +#include "TodayFolderObject.h" #include "graphqlservice/internal/Schema.h" @@ -11,7 +11,6 @@ #include #include -#include #include #include diff --git a/samples/today/schema/FolderObject.ixx b/samples/today/schema/FolderObject.ixx new file mode 100644 index 00000000..bd90ad08 --- /dev/null +++ b/samples/today/schema/FolderObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayFolderObject.h" + +export module GraphQL.Today.FolderObject; + +export namespace graphql::today::object { + +using object::Folder; + +} // namespace graphql::today::object diff --git a/samples/today/schema/MutationObject.cpp b/samples/today/schema/MutationObject.cpp index 4482e672..82232467 100644 --- a/samples/today/schema/MutationObject.cpp +++ b/samples/today/schema/MutationObject.cpp @@ -3,8 +3,8 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "MutationObject.h" -#include "CompleteTaskPayloadObject.h" +#include "TodayMutationObject.h" +#include "TodayCompleteTaskPayloadObject.h" #include "graphqlservice/internal/Schema.h" @@ -12,7 +12,6 @@ #include #include -#include #include #include diff --git a/samples/today/schema/MutationObject.ixx b/samples/today/schema/MutationObject.ixx new file mode 100644 index 00000000..07954b56 --- /dev/null +++ b/samples/today/schema/MutationObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayMutationObject.h" + +export module GraphQL.Today.MutationObject; + +export namespace graphql::today::object { + +using object::Mutation; + +} // namespace graphql::today::object diff --git a/samples/today/schema/NestedTypeObject.cpp b/samples/today/schema/NestedTypeObject.cpp index bd9c90d1..26043170 100644 --- a/samples/today/schema/NestedTypeObject.cpp +++ b/samples/today/schema/NestedTypeObject.cpp @@ -3,8 +3,8 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "NestedTypeObject.h" -#include "NestedTypeObject.h" +#include "TodayNestedTypeObject.h" +#include "TodayNestedTypeObject.h" #include "graphqlservice/internal/Schema.h" @@ -12,7 +12,6 @@ #include #include -#include #include #include diff --git a/samples/today/schema/NestedTypeObject.ixx b/samples/today/schema/NestedTypeObject.ixx new file mode 100644 index 00000000..514905ed --- /dev/null +++ b/samples/today/schema/NestedTypeObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayNestedTypeObject.h" + +export module GraphQL.Today.NestedTypeObject; + +export namespace graphql::today::object { + +using object::NestedType; + +} // namespace graphql::today::object diff --git a/samples/today/schema/NodeObject.cpp b/samples/today/schema/NodeObject.cpp index 42b73132..924d2996 100644 --- a/samples/today/schema/NodeObject.cpp +++ b/samples/today/schema/NodeObject.cpp @@ -3,7 +3,7 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "NodeObject.h" +#include "TodayNodeObject.h" #include "graphqlservice/internal/Schema.h" diff --git a/samples/today/schema/NodeObject.ixx b/samples/today/schema/NodeObject.ixx new file mode 100644 index 00000000..af5bfd22 --- /dev/null +++ b/samples/today/schema/NodeObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayNodeObject.h" + +export module GraphQL.Today.NodeObject; + +export namespace graphql::today::object { + +using object::Node; + +} // namespace graphql::today::object diff --git a/samples/today/schema/PageInfoObject.cpp b/samples/today/schema/PageInfoObject.cpp index c356dbac..ea2b05eb 100644 --- a/samples/today/schema/PageInfoObject.cpp +++ b/samples/today/schema/PageInfoObject.cpp @@ -3,7 +3,7 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "PageInfoObject.h" +#include "TodayPageInfoObject.h" #include "graphqlservice/internal/Schema.h" @@ -11,7 +11,6 @@ #include #include -#include #include #include diff --git a/samples/today/schema/PageInfoObject.ixx b/samples/today/schema/PageInfoObject.ixx new file mode 100644 index 00000000..2e8855dd --- /dev/null +++ b/samples/today/schema/PageInfoObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayPageInfoObject.h" + +export module GraphQL.Today.PageInfoObject; + +export namespace graphql::today::object { + +using object::PageInfo; + +} // namespace graphql::today::object diff --git a/samples/today/schema/QueryObject.cpp b/samples/today/schema/QueryObject.cpp index ac999274..64343e99 100644 --- a/samples/today/schema/QueryObject.cpp +++ b/samples/today/schema/QueryObject.cpp @@ -3,17 +3,17 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "QueryObject.h" -#include "NodeObject.h" -#include "AppointmentConnectionObject.h" -#include "TaskConnectionObject.h" -#include "FolderConnectionObject.h" -#include "AppointmentObject.h" -#include "TaskObject.h" -#include "FolderObject.h" -#include "NestedTypeObject.h" -#include "ExpensiveObject.h" -#include "UnionTypeObject.h" +#include "TodayQueryObject.h" +#include "TodayNodeObject.h" +#include "TodayAppointmentConnectionObject.h" +#include "TodayTaskConnectionObject.h" +#include "TodayFolderConnectionObject.h" +#include "TodayAppointmentObject.h" +#include "TodayTaskObject.h" +#include "TodayFolderObject.h" +#include "TodayNestedTypeObject.h" +#include "TodayExpensiveObject.h" +#include "TodayUnionTypeObject.h" #include "graphqlservice/internal/Introspection.h" @@ -22,7 +22,6 @@ #include #include -#include #include #include diff --git a/samples/today/schema/QueryObject.ixx b/samples/today/schema/QueryObject.ixx new file mode 100644 index 00000000..02c0269f --- /dev/null +++ b/samples/today/schema/QueryObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayQueryObject.h" + +export module GraphQL.Today.QueryObject; + +export namespace graphql::today::object { + +using object::Query; + +} // namespace graphql::today::object diff --git a/samples/today/schema/SubscriptionObject.cpp b/samples/today/schema/SubscriptionObject.cpp index 104e9fe0..f850a9dd 100644 --- a/samples/today/schema/SubscriptionObject.cpp +++ b/samples/today/schema/SubscriptionObject.cpp @@ -3,9 +3,9 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "SubscriptionObject.h" -#include "AppointmentObject.h" -#include "NodeObject.h" +#include "TodaySubscriptionObject.h" +#include "TodayAppointmentObject.h" +#include "TodayNodeObject.h" #include "graphqlservice/internal/Schema.h" @@ -13,7 +13,6 @@ #include #include -#include #include #include diff --git a/samples/today/schema/SubscriptionObject.ixx b/samples/today/schema/SubscriptionObject.ixx new file mode 100644 index 00000000..2cd09ade --- /dev/null +++ b/samples/today/schema/SubscriptionObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodaySubscriptionObject.h" + +export module GraphQL.Today.SubscriptionObject; + +export namespace graphql::today::object { + +using object::Subscription; + +} // namespace graphql::today::object diff --git a/samples/today/schema/TaskConnectionObject.cpp b/samples/today/schema/TaskConnectionObject.cpp index a49a51ac..060c55ab 100644 --- a/samples/today/schema/TaskConnectionObject.cpp +++ b/samples/today/schema/TaskConnectionObject.cpp @@ -3,9 +3,9 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "TaskConnectionObject.h" -#include "PageInfoObject.h" -#include "TaskEdgeObject.h" +#include "TodayTaskConnectionObject.h" +#include "TodayPageInfoObject.h" +#include "TodayTaskEdgeObject.h" #include "graphqlservice/internal/Schema.h" @@ -13,7 +13,6 @@ #include #include -#include #include #include diff --git a/samples/today/schema/TaskConnectionObject.ixx b/samples/today/schema/TaskConnectionObject.ixx new file mode 100644 index 00000000..93354afd --- /dev/null +++ b/samples/today/schema/TaskConnectionObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayTaskConnectionObject.h" + +export module GraphQL.Today.TaskConnectionObject; + +export namespace graphql::today::object { + +using object::TaskConnection; + +} // namespace graphql::today::object diff --git a/samples/today/schema/TaskEdgeObject.cpp b/samples/today/schema/TaskEdgeObject.cpp index 300a0737..37b888da 100644 --- a/samples/today/schema/TaskEdgeObject.cpp +++ b/samples/today/schema/TaskEdgeObject.cpp @@ -3,8 +3,8 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "TaskEdgeObject.h" -#include "TaskObject.h" +#include "TodayTaskEdgeObject.h" +#include "TodayTaskObject.h" #include "graphqlservice/internal/Schema.h" @@ -12,7 +12,6 @@ #include #include -#include #include #include diff --git a/samples/today/schema/TaskEdgeObject.ixx b/samples/today/schema/TaskEdgeObject.ixx new file mode 100644 index 00000000..20f2aa47 --- /dev/null +++ b/samples/today/schema/TaskEdgeObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayTaskEdgeObject.h" + +export module GraphQL.Today.TaskEdgeObject; + +export namespace graphql::today::object { + +using object::TaskEdge; + +} // namespace graphql::today::object diff --git a/samples/today/schema/TaskObject.cpp b/samples/today/schema/TaskObject.cpp index cd58aa7a..483afb5b 100644 --- a/samples/today/schema/TaskObject.cpp +++ b/samples/today/schema/TaskObject.cpp @@ -3,7 +3,7 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "TaskObject.h" +#include "TodayTaskObject.h" #include "graphqlservice/internal/Schema.h" @@ -11,7 +11,6 @@ #include #include -#include #include #include diff --git a/samples/today/schema/TaskObject.ixx b/samples/today/schema/TaskObject.ixx new file mode 100644 index 00000000..5fb669e6 --- /dev/null +++ b/samples/today/schema/TaskObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayTaskObject.h" + +export module GraphQL.Today.TaskObject; + +export namespace graphql::today::object { + +using object::Task; + +} // namespace graphql::today::object diff --git a/samples/today/nointrospection/AppointmentConnectionObject.h b/samples/today/schema/TodayAppointmentConnectionObject.h similarity index 97% rename from samples/today/nointrospection/AppointmentConnectionObject.h rename to samples/today/schema/TodayAppointmentConnectionObject.h index cfca668b..4640070e 100644 --- a/samples/today/nointrospection/AppointmentConnectionObject.h +++ b/samples/today/schema/TodayAppointmentConnectionObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef APPOINTMENTCONNECTIONOBJECT_H -#define APPOINTMENTCONNECTIONOBJECT_H +#ifndef TODAY_TODAYAPPOINTMENTCONNECTIONOBJECT_H +#define TODAY_TODAYAPPOINTMENTCONNECTIONOBJECT_H #include "TodaySchema.h" @@ -157,4 +157,4 @@ class [[nodiscard("unnecessary construction")]] AppointmentConnection final } // namespace graphql::today::object -#endif // APPOINTMENTCONNECTIONOBJECT_H +#endif // TODAY_TODAYAPPOINTMENTCONNECTIONOBJECT_H diff --git a/samples/today/nointrospection/AppointmentEdgeObject.h b/samples/today/schema/TodayAppointmentEdgeObject.h similarity index 97% rename from samples/today/nointrospection/AppointmentEdgeObject.h rename to samples/today/schema/TodayAppointmentEdgeObject.h index d8c71b48..bda2c57f 100644 --- a/samples/today/nointrospection/AppointmentEdgeObject.h +++ b/samples/today/schema/TodayAppointmentEdgeObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef APPOINTMENTEDGEOBJECT_H -#define APPOINTMENTEDGEOBJECT_H +#ifndef TODAY_TODAYAPPOINTMENTEDGEOBJECT_H +#define TODAY_TODAYAPPOINTMENTEDGEOBJECT_H #include "TodaySchema.h" @@ -157,4 +157,4 @@ class [[nodiscard("unnecessary construction")]] AppointmentEdge final } // namespace graphql::today::object -#endif // APPOINTMENTEDGEOBJECT_H +#endif // TODAY_TODAYAPPOINTMENTEDGEOBJECT_H diff --git a/samples/today/schema/AppointmentObject.h b/samples/today/schema/TodayAppointmentObject.h similarity index 87% rename from samples/today/schema/AppointmentObject.h rename to samples/today/schema/TodayAppointmentObject.h index 0267f113..7507a086 100644 --- a/samples/today/schema/AppointmentObject.h +++ b/samples/today/schema/TodayAppointmentObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef APPOINTMENTOBJECT_H -#define APPOINTMENTOBJECT_H +#ifndef TODAY_TODAYAPPOINTMENTOBJECT_H +#define TODAY_TODAYAPPOINTMENTOBJECT_H #include "TodaySchema.h" @@ -80,6 +80,18 @@ concept getForceError = requires (TImpl impl) { service::AwaitableScalar> { impl.getForceError() } }; }; +template +concept getArrayWithParams = requires (TImpl impl, service::FieldParams params) +{ + { service::AwaitableScalar> { impl.getArray(std::move(params)) } }; +}; + +template +concept getArray = requires (TImpl impl) +{ + { service::AwaitableScalar> { impl.getArray() } }; +}; + template concept beginSelectionSet = requires (TImpl impl, const service::SelectionSetParams params) { @@ -103,6 +115,7 @@ class [[nodiscard("unnecessary construction")]] Appointment final [[nodiscard("unnecessary call")]] service::AwaitableResolver resolveSubject(service::ResolverParams&& params) const; [[nodiscard("unnecessary call")]] service::AwaitableResolver resolveIsNow(service::ResolverParams&& params) const; [[nodiscard("unnecessary call")]] service::AwaitableResolver resolveForceError(service::ResolverParams&& params) const; + [[nodiscard("unnecessary call")]] service::AwaitableResolver resolveArray(service::ResolverParams&& params) const; [[nodiscard("unnecessary call")]] service::AwaitableResolver resolve_typename(service::ResolverParams&& params) const; @@ -118,6 +131,7 @@ class [[nodiscard("unnecessary construction")]] Appointment final [[nodiscard("unnecessary call")]] virtual service::AwaitableScalar> getSubject(service::FieldParams&& params) const = 0; [[nodiscard("unnecessary call")]] virtual service::AwaitableScalar getIsNow(service::FieldParams&& params) const = 0; [[nodiscard("unnecessary call")]] virtual service::AwaitableScalar> getForceError(service::FieldParams&& params) const = 0; + [[nodiscard("unnecessary call")]] virtual service::AwaitableScalar> getArray(service::FieldParams&& params) const = 0; }; template @@ -209,6 +223,22 @@ class [[nodiscard("unnecessary construction")]] Appointment final } } + [[nodiscard("unnecessary call")]] service::AwaitableScalar> getArray(service::FieldParams&& params) const override + { + if constexpr (methods::AppointmentHas::getArrayWithParams) + { + return { _pimpl->getArray(std::move(params)) }; + } + else if constexpr (methods::AppointmentHas::getArray) + { + return { _pimpl->getArray() }; + } + else + { + throw service::unimplemented_method(R"ex(Appointment::getArray)ex"); + } + } + void beginSelectionSet(const service::SelectionSetParams& params) const override { if constexpr (methods::AppointmentHas::beginSelectionSet) @@ -266,4 +296,4 @@ class [[nodiscard("unnecessary construction")]] Appointment final } // namespace graphql::today::object -#endif // APPOINTMENTOBJECT_H +#endif // TODAY_TODAYAPPOINTMENTOBJECT_H diff --git a/samples/today/schema/CompleteTaskPayloadObject.h b/samples/today/schema/TodayCompleteTaskPayloadObject.h similarity index 97% rename from samples/today/schema/CompleteTaskPayloadObject.h rename to samples/today/schema/TodayCompleteTaskPayloadObject.h index ccac3881..1fd8cb0c 100644 --- a/samples/today/schema/CompleteTaskPayloadObject.h +++ b/samples/today/schema/TodayCompleteTaskPayloadObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef COMPLETETASKPAYLOADOBJECT_H -#define COMPLETETASKPAYLOADOBJECT_H +#ifndef TODAY_TODAYCOMPLETETASKPAYLOADOBJECT_H +#define TODAY_TODAYCOMPLETETASKPAYLOADOBJECT_H #include "TodaySchema.h" @@ -157,4 +157,4 @@ class [[nodiscard("unnecessary construction")]] CompleteTaskPayload final } // namespace graphql::today::object -#endif // COMPLETETASKPAYLOADOBJECT_H +#endif // TODAY_TODAYCOMPLETETASKPAYLOADOBJECT_H diff --git a/samples/today/schema/ExpensiveObject.h b/samples/today/schema/TodayExpensiveObject.h similarity index 96% rename from samples/today/schema/ExpensiveObject.h rename to samples/today/schema/TodayExpensiveObject.h index bea1cd2c..6d682efb 100644 --- a/samples/today/schema/ExpensiveObject.h +++ b/samples/today/schema/TodayExpensiveObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef EXPENSIVEOBJECT_H -#define EXPENSIVEOBJECT_H +#ifndef TODAY_TODAYEXPENSIVEOBJECT_H +#define TODAY_TODAYEXPENSIVEOBJECT_H #include "TodaySchema.h" @@ -127,4 +127,4 @@ class [[nodiscard("unnecessary construction")]] Expensive final } // namespace graphql::today::object -#endif // EXPENSIVEOBJECT_H +#endif // TODAY_TODAYEXPENSIVEOBJECT_H diff --git a/samples/today/nointrospection/FolderConnectionObject.h b/samples/today/schema/TodayFolderConnectionObject.h similarity index 97% rename from samples/today/nointrospection/FolderConnectionObject.h rename to samples/today/schema/TodayFolderConnectionObject.h index 61e6729f..1911fb42 100644 --- a/samples/today/nointrospection/FolderConnectionObject.h +++ b/samples/today/schema/TodayFolderConnectionObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef FOLDERCONNECTIONOBJECT_H -#define FOLDERCONNECTIONOBJECT_H +#ifndef TODAY_TODAYFOLDERCONNECTIONOBJECT_H +#define TODAY_TODAYFOLDERCONNECTIONOBJECT_H #include "TodaySchema.h" @@ -157,4 +157,4 @@ class [[nodiscard("unnecessary construction")]] FolderConnection final } // namespace graphql::today::object -#endif // FOLDERCONNECTIONOBJECT_H +#endif // TODAY_TODAYFOLDERCONNECTIONOBJECT_H diff --git a/samples/today/nointrospection/FolderEdgeObject.h b/samples/today/schema/TodayFolderEdgeObject.h similarity index 97% rename from samples/today/nointrospection/FolderEdgeObject.h rename to samples/today/schema/TodayFolderEdgeObject.h index dc7a0d28..259c758d 100644 --- a/samples/today/nointrospection/FolderEdgeObject.h +++ b/samples/today/schema/TodayFolderEdgeObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef FOLDEREDGEOBJECT_H -#define FOLDEREDGEOBJECT_H +#ifndef TODAY_TODAYFOLDEREDGEOBJECT_H +#define TODAY_TODAYFOLDEREDGEOBJECT_H #include "TodaySchema.h" @@ -157,4 +157,4 @@ class [[nodiscard("unnecessary construction")]] FolderEdge final } // namespace graphql::today::object -#endif // FOLDEREDGEOBJECT_H +#endif // TODAY_TODAYFOLDEREDGEOBJECT_H diff --git a/samples/today/nointrospection/FolderObject.h b/samples/today/schema/TodayFolderObject.h similarity index 98% rename from samples/today/nointrospection/FolderObject.h rename to samples/today/schema/TodayFolderObject.h index 2376bc55..50f9849f 100644 --- a/samples/today/nointrospection/FolderObject.h +++ b/samples/today/schema/TodayFolderObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef FOLDEROBJECT_H -#define FOLDEROBJECT_H +#ifndef TODAY_TODAYFOLDEROBJECT_H +#define TODAY_TODAYFOLDEROBJECT_H #include "TodaySchema.h" @@ -206,4 +206,4 @@ class [[nodiscard("unnecessary construction")]] Folder final } // namespace graphql::today::object -#endif // FOLDEROBJECT_H +#endif // TODAY_TODAYFOLDEROBJECT_H diff --git a/samples/today/nointrospection/MutationObject.h b/samples/today/schema/TodayMutationObject.h similarity index 97% rename from samples/today/nointrospection/MutationObject.h rename to samples/today/schema/TodayMutationObject.h index 0da072bc..13659bd9 100644 --- a/samples/today/nointrospection/MutationObject.h +++ b/samples/today/schema/TodayMutationObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef MUTATIONOBJECT_H -#define MUTATIONOBJECT_H +#ifndef TODAY_TODAYMUTATIONOBJECT_H +#define TODAY_TODAYMUTATIONOBJECT_H #include "TodaySchema.h" @@ -157,4 +157,4 @@ class [[nodiscard("unnecessary construction")]] Mutation final } // namespace graphql::today::object -#endif // MUTATIONOBJECT_H +#endif // TODAY_TODAYMUTATIONOBJECT_H diff --git a/samples/today/nointrospection/NestedTypeObject.h b/samples/today/schema/TodayNestedTypeObject.h similarity index 97% rename from samples/today/nointrospection/NestedTypeObject.h rename to samples/today/schema/TodayNestedTypeObject.h index 539f72db..aab91089 100644 --- a/samples/today/nointrospection/NestedTypeObject.h +++ b/samples/today/schema/TodayNestedTypeObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef NESTEDTYPEOBJECT_H -#define NESTEDTYPEOBJECT_H +#ifndef TODAY_TODAYNESTEDTYPEOBJECT_H +#define TODAY_TODAYNESTEDTYPEOBJECT_H #include "TodaySchema.h" @@ -157,4 +157,4 @@ class [[nodiscard("unnecessary construction")]] NestedType final } // namespace graphql::today::object -#endif // NESTEDTYPEOBJECT_H +#endif // TODAY_TODAYNESTEDTYPEOBJECT_H diff --git a/samples/today/nointrospection/NodeObject.h b/samples/today/schema/TodayNodeObject.h similarity index 95% rename from samples/today/nointrospection/NodeObject.h rename to samples/today/schema/TodayNodeObject.h index 0310cdba..a394b40d 100644 --- a/samples/today/nointrospection/NodeObject.h +++ b/samples/today/schema/TodayNodeObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef NODEOBJECT_H -#define NODEOBJECT_H +#ifndef TODAY_TODAYNODEOBJECT_H +#define TODAY_TODAYNODEOBJECT_H #include "TodaySchema.h" @@ -78,4 +78,4 @@ class [[nodiscard("unnecessary construction")]] Node final } // namespace graphql::today::object -#endif // NODEOBJECT_H +#endif // TODAY_TODAYNODEOBJECT_H diff --git a/samples/today/schema/PageInfoObject.h b/samples/today/schema/TodayPageInfoObject.h similarity index 97% rename from samples/today/schema/PageInfoObject.h rename to samples/today/schema/TodayPageInfoObject.h index f4f10741..bd63667a 100644 --- a/samples/today/schema/PageInfoObject.h +++ b/samples/today/schema/TodayPageInfoObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef PAGEINFOOBJECT_H -#define PAGEINFOOBJECT_H +#ifndef TODAY_TODAYPAGEINFOOBJECT_H +#define TODAY_TODAYPAGEINFOOBJECT_H #include "TodaySchema.h" @@ -157,4 +157,4 @@ class [[nodiscard("unnecessary construction")]] PageInfo final } // namespace graphql::today::object -#endif // PAGEINFOOBJECT_H +#endif // TODAY_TODAYPAGEINFOOBJECT_H diff --git a/samples/today/schema/QueryObject.h b/samples/today/schema/TodayQueryObject.h similarity index 99% rename from samples/today/schema/QueryObject.h rename to samples/today/schema/TodayQueryObject.h index 9a1226a4..c0416c9f 100644 --- a/samples/today/schema/QueryObject.h +++ b/samples/today/schema/TodayQueryObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef QUERYOBJECT_H -#define QUERYOBJECT_H +#ifndef TODAY_TODAYQUERYOBJECT_H +#define TODAY_TODAYQUERYOBJECT_H #include "TodaySchema.h" @@ -491,4 +491,4 @@ class [[nodiscard("unnecessary construction")]] Query final } // namespace graphql::today::object -#endif // QUERYOBJECT_H +#endif // TODAY_TODAYQUERYOBJECT_H diff --git a/samples/today/schema/TodaySchema.cpp b/samples/today/schema/TodaySchema.cpp index ddde3725..831e7bbf 100644 --- a/samples/today/schema/TodaySchema.cpp +++ b/samples/today/schema/TodaySchema.cpp @@ -3,9 +3,9 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "QueryObject.h" -#include "MutationObject.h" -#include "SubscriptionObject.h" +#include "TodayQueryObject.h" +#include "TodayMutationObject.h" +#include "TodaySubscriptionObject.h" #include "graphqlservice/internal/Schema.h" @@ -13,8 +13,8 @@ #include #include +#include #include -#include #include #include #include @@ -22,718 +22,7 @@ using namespace std::literals; -namespace graphql { -namespace service { - -static const auto s_namesTaskState = today::getTaskStateNames(); -static const auto s_valuesTaskState = today::getTaskStateValues(); - -template <> -today::TaskState Argument::convert(const response::Value& value) -{ - if (!value.maybe_enum()) - { - throw service::schema_exception { { R"ex(not a valid TaskState value)ex" } }; - } - - const auto result = internal::sorted_map_lookup( - s_valuesTaskState, - std::string_view { value.get() }); - - if (!result) - { - throw service::schema_exception { { R"ex(not a valid TaskState value)ex" } }; - } - - return *result; -} - -template <> -service::AwaitableResolver Result::convert(service::AwaitableScalar result, ResolverParams&& params) -{ - return ModifiedResult::resolve(std::move(result), std::move(params), - [](today::TaskState value, const ResolverParams&) - { - const auto idx = static_cast(value); - - if (idx >= s_namesTaskState.size()) - { - throw service::schema_exception { { R"ex(Enum value out of range for TaskState)ex" } }; - } - - response::Value resolvedResult(response::Type::EnumValue); - - resolvedResult.set(std::string { s_namesTaskState[idx] }); - - return resolvedResult; - }); -} - -template <> -void Result::validateScalar(const response::Value& value) -{ - if (!value.maybe_enum()) - { - throw service::schema_exception { { R"ex(not a valid TaskState value)ex" } }; - } - - const auto [itr, itrEnd] = internal::sorted_map_equal_range( - s_valuesTaskState.begin(), - s_valuesTaskState.end(), - std::string_view { value.get() }); - - if (itr == itrEnd) - { - throw service::schema_exception { { R"ex(not a valid TaskState value)ex" } }; - } -} - -template <> -today::CompleteTaskInput Argument::convert(const response::Value& value) -{ - const auto defaultValue = []() - { - response::Value values(response::Type::Map); - response::Value entry; - - entry = response::Value(true); - values.emplace_back("isComplete", std::move(entry)); - - return values; - }(); - - auto valueId = service::ModifiedArgument::require("id", value); - auto valueTestTaskState = service::ModifiedArgument::require("testTaskState", value); - auto pairIsComplete = service::ModifiedArgument::find("isComplete", value); - auto valueIsComplete = (pairIsComplete.second - ? std::move(pairIsComplete.first) - : service::ModifiedArgument::require("isComplete", defaultValue)); - auto valueClientMutationId = service::ModifiedArgument::require("clientMutationId", value); - - return today::CompleteTaskInput { - std::move(valueId), - valueTestTaskState, - std::move(valueIsComplete), - std::move(valueClientMutationId) - }; -} - -template <> -today::ThirdNestedInput Argument::convert(const response::Value& value) -{ - auto valueId = service::ModifiedArgument::require("id", value); - auto valueSecond = service::ModifiedArgument::require("second", value); - - return today::ThirdNestedInput { - std::move(valueId), - std::move(valueSecond) - }; -} - -template <> -today::FourthNestedInput Argument::convert(const response::Value& value) -{ - auto valueId = service::ModifiedArgument::require("id", value); - - return today::FourthNestedInput { - std::move(valueId) - }; -} - -template <> -today::IncludeNullableSelfInput Argument::convert(const response::Value& value) -{ - auto valueSelf = service::ModifiedArgument::require("self", value); - - return today::IncludeNullableSelfInput { - std::move(valueSelf) - }; -} - -template <> -today::IncludeNonNullableListSelfInput Argument::convert(const response::Value& value) -{ - auto valueSelves = service::ModifiedArgument::require("selves", value); - - return today::IncludeNonNullableListSelfInput { - std::move(valueSelves) - }; -} - -template <> -today::StringOperationFilterInput Argument::convert(const response::Value& value) -{ - auto valueAnd_ = service::ModifiedArgument::require("and", value); - auto valueOr_ = service::ModifiedArgument::require("or", value); - auto valueEqual = service::ModifiedArgument::require("equal", value); - auto valueNotEqual = service::ModifiedArgument::require("notEqual", value); - auto valueContains = service::ModifiedArgument::require("contains", value); - auto valueNotContains = service::ModifiedArgument::require("notContains", value); - auto valueIn = service::ModifiedArgument::require("in", value); - auto valueNotIn = service::ModifiedArgument::require("notIn", value); - auto valueStartsWith = service::ModifiedArgument::require("startsWith", value); - auto valueNotStartsWith = service::ModifiedArgument::require("notStartsWith", value); - auto valueEndsWith = service::ModifiedArgument::require("endsWith", value); - auto valueNotEndsWith = service::ModifiedArgument::require("notEndsWith", value); - - return today::StringOperationFilterInput { - std::move(valueAnd_), - std::move(valueOr_), - std::move(valueEqual), - std::move(valueNotEqual), - std::move(valueContains), - std::move(valueNotContains), - std::move(valueIn), - std::move(valueNotIn), - std::move(valueStartsWith), - std::move(valueNotStartsWith), - std::move(valueEndsWith), - std::move(valueNotEndsWith) - }; -} - -template <> -today::SecondNestedInput Argument::convert(const response::Value& value) -{ - auto valueId = service::ModifiedArgument::require("id", value); - auto valueThird = service::ModifiedArgument::require("third", value); - - return today::SecondNestedInput { - std::move(valueId), - std::move(valueThird) - }; -} - -template <> -today::ForwardDeclaredInput Argument::convert(const response::Value& value) -{ - auto valueNullableSelf = service::ModifiedArgument::require("nullableSelf", value); - auto valueListSelves = service::ModifiedArgument::require("listSelves", value); - - return today::ForwardDeclaredInput { - std::move(valueNullableSelf), - std::move(valueListSelves) - }; -} - -template <> -today::FirstNestedInput Argument::convert(const response::Value& value) -{ - auto valueId = service::ModifiedArgument::require("id", value); - auto valueSecond = service::ModifiedArgument::require("second", value); - auto valueThird = service::ModifiedArgument::require("third", value); - - return today::FirstNestedInput { - std::move(valueId), - std::move(valueSecond), - std::move(valueThird) - }; -} - -} // namespace service - -namespace today { - -CompleteTaskInput::CompleteTaskInput() noexcept - : id {} - , testTaskState {} - , isComplete {} - , clientMutationId {} -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -CompleteTaskInput::CompleteTaskInput( - response::IdType idArg, - std::optional testTaskStateArg, - std::optional isCompleteArg, - std::optional clientMutationIdArg) noexcept - : id { std::move(idArg) } - , testTaskState { std::move(testTaskStateArg) } - , isComplete { std::move(isCompleteArg) } - , clientMutationId { std::move(clientMutationIdArg) } -{ -} - -CompleteTaskInput::CompleteTaskInput(const CompleteTaskInput& other) - : id { service::ModifiedArgument::duplicate(other.id) } - , testTaskState { service::ModifiedArgument::duplicate(other.testTaskState) } - , isComplete { service::ModifiedArgument::duplicate(other.isComplete) } - , clientMutationId { service::ModifiedArgument::duplicate(other.clientMutationId) } -{ -} - -CompleteTaskInput::CompleteTaskInput(CompleteTaskInput&& other) noexcept - : id { std::move(other.id) } - , testTaskState { std::move(other.testTaskState) } - , isComplete { std::move(other.isComplete) } - , clientMutationId { std::move(other.clientMutationId) } -{ -} - -CompleteTaskInput::~CompleteTaskInput() -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -CompleteTaskInput& CompleteTaskInput::operator=(const CompleteTaskInput& other) -{ - CompleteTaskInput value { other }; - - std::swap(*this, value); - - return *this; -} - -CompleteTaskInput& CompleteTaskInput::operator=(CompleteTaskInput&& other) noexcept -{ - id = std::move(other.id); - testTaskState = std::move(other.testTaskState); - isComplete = std::move(other.isComplete); - clientMutationId = std::move(other.clientMutationId); - - return *this; -} - -ThirdNestedInput::ThirdNestedInput() noexcept - : id {} - , second {} -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -ThirdNestedInput::ThirdNestedInput( - response::IdType idArg, - std::unique_ptr secondArg) noexcept - : id { std::move(idArg) } - , second { std::move(secondArg) } -{ -} - -ThirdNestedInput::ThirdNestedInput(const ThirdNestedInput& other) - : id { service::ModifiedArgument::duplicate(other.id) } - , second { service::ModifiedArgument::duplicate(other.second) } -{ -} - -ThirdNestedInput::ThirdNestedInput(ThirdNestedInput&& other) noexcept - : id { std::move(other.id) } - , second { std::move(other.second) } -{ -} - -ThirdNestedInput::~ThirdNestedInput() -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -ThirdNestedInput& ThirdNestedInput::operator=(const ThirdNestedInput& other) -{ - ThirdNestedInput value { other }; - - std::swap(*this, value); - - return *this; -} - -ThirdNestedInput& ThirdNestedInput::operator=(ThirdNestedInput&& other) noexcept -{ - id = std::move(other.id); - second = std::move(other.second); - - return *this; -} - -FourthNestedInput::FourthNestedInput() noexcept - : id {} -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -FourthNestedInput::FourthNestedInput( - response::IdType idArg) noexcept - : id { std::move(idArg) } -{ -} - -FourthNestedInput::FourthNestedInput(const FourthNestedInput& other) - : id { service::ModifiedArgument::duplicate(other.id) } -{ -} - -FourthNestedInput::FourthNestedInput(FourthNestedInput&& other) noexcept - : id { std::move(other.id) } -{ -} - -FourthNestedInput::~FourthNestedInput() -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -FourthNestedInput& FourthNestedInput::operator=(const FourthNestedInput& other) -{ - FourthNestedInput value { other }; - - std::swap(*this, value); - - return *this; -} - -FourthNestedInput& FourthNestedInput::operator=(FourthNestedInput&& other) noexcept -{ - id = std::move(other.id); - - return *this; -} - -IncludeNullableSelfInput::IncludeNullableSelfInput() noexcept - : self {} -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -IncludeNullableSelfInput::IncludeNullableSelfInput( - std::unique_ptr selfArg) noexcept - : self { std::move(selfArg) } -{ -} - -IncludeNullableSelfInput::IncludeNullableSelfInput(const IncludeNullableSelfInput& other) - : self { service::ModifiedArgument::duplicate(other.self) } -{ -} - -IncludeNullableSelfInput::IncludeNullableSelfInput(IncludeNullableSelfInput&& other) noexcept - : self { std::move(other.self) } -{ -} - -IncludeNullableSelfInput::~IncludeNullableSelfInput() -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -IncludeNullableSelfInput& IncludeNullableSelfInput::operator=(const IncludeNullableSelfInput& other) -{ - IncludeNullableSelfInput value { other }; - - std::swap(*this, value); - - return *this; -} - -IncludeNullableSelfInput& IncludeNullableSelfInput::operator=(IncludeNullableSelfInput&& other) noexcept -{ - self = std::move(other.self); - - return *this; -} - -IncludeNonNullableListSelfInput::IncludeNonNullableListSelfInput() noexcept - : selves {} -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -IncludeNonNullableListSelfInput::IncludeNonNullableListSelfInput( - std::vector selvesArg) noexcept - : selves { std::move(selvesArg) } -{ -} - -IncludeNonNullableListSelfInput::IncludeNonNullableListSelfInput(const IncludeNonNullableListSelfInput& other) - : selves { service::ModifiedArgument::duplicate(other.selves) } -{ -} - -IncludeNonNullableListSelfInput::IncludeNonNullableListSelfInput(IncludeNonNullableListSelfInput&& other) noexcept - : selves { std::move(other.selves) } -{ -} - -IncludeNonNullableListSelfInput::~IncludeNonNullableListSelfInput() -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -IncludeNonNullableListSelfInput& IncludeNonNullableListSelfInput::operator=(const IncludeNonNullableListSelfInput& other) -{ - IncludeNonNullableListSelfInput value { other }; - - std::swap(*this, value); - - return *this; -} - -IncludeNonNullableListSelfInput& IncludeNonNullableListSelfInput::operator=(IncludeNonNullableListSelfInput&& other) noexcept -{ - selves = std::move(other.selves); - - return *this; -} - -StringOperationFilterInput::StringOperationFilterInput() noexcept - : and_ {} - , or_ {} - , equal {} - , notEqual {} - , contains {} - , notContains {} - , in {} - , notIn {} - , startsWith {} - , notStartsWith {} - , endsWith {} - , notEndsWith {} -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -StringOperationFilterInput::StringOperationFilterInput( - std::optional> and_Arg, - std::optional> or_Arg, - std::optional equalArg, - std::optional notEqualArg, - std::optional containsArg, - std::optional notContainsArg, - std::optional> inArg, - std::optional> notInArg, - std::optional startsWithArg, - std::optional notStartsWithArg, - std::optional endsWithArg, - std::optional notEndsWithArg) noexcept - : and_ { std::move(and_Arg) } - , or_ { std::move(or_Arg) } - , equal { std::move(equalArg) } - , notEqual { std::move(notEqualArg) } - , contains { std::move(containsArg) } - , notContains { std::move(notContainsArg) } - , in { std::move(inArg) } - , notIn { std::move(notInArg) } - , startsWith { std::move(startsWithArg) } - , notStartsWith { std::move(notStartsWithArg) } - , endsWith { std::move(endsWithArg) } - , notEndsWith { std::move(notEndsWithArg) } -{ -} - -StringOperationFilterInput::StringOperationFilterInput(const StringOperationFilterInput& other) - : and_ { service::ModifiedArgument::duplicate(other.and_) } - , or_ { service::ModifiedArgument::duplicate(other.or_) } - , equal { service::ModifiedArgument::duplicate(other.equal) } - , notEqual { service::ModifiedArgument::duplicate(other.notEqual) } - , contains { service::ModifiedArgument::duplicate(other.contains) } - , notContains { service::ModifiedArgument::duplicate(other.notContains) } - , in { service::ModifiedArgument::duplicate(other.in) } - , notIn { service::ModifiedArgument::duplicate(other.notIn) } - , startsWith { service::ModifiedArgument::duplicate(other.startsWith) } - , notStartsWith { service::ModifiedArgument::duplicate(other.notStartsWith) } - , endsWith { service::ModifiedArgument::duplicate(other.endsWith) } - , notEndsWith { service::ModifiedArgument::duplicate(other.notEndsWith) } -{ -} - -StringOperationFilterInput::StringOperationFilterInput(StringOperationFilterInput&& other) noexcept - : and_ { std::move(other.and_) } - , or_ { std::move(other.or_) } - , equal { std::move(other.equal) } - , notEqual { std::move(other.notEqual) } - , contains { std::move(other.contains) } - , notContains { std::move(other.notContains) } - , in { std::move(other.in) } - , notIn { std::move(other.notIn) } - , startsWith { std::move(other.startsWith) } - , notStartsWith { std::move(other.notStartsWith) } - , endsWith { std::move(other.endsWith) } - , notEndsWith { std::move(other.notEndsWith) } -{ -} - -StringOperationFilterInput::~StringOperationFilterInput() -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -StringOperationFilterInput& StringOperationFilterInput::operator=(const StringOperationFilterInput& other) -{ - StringOperationFilterInput value { other }; - - std::swap(*this, value); - - return *this; -} - -StringOperationFilterInput& StringOperationFilterInput::operator=(StringOperationFilterInput&& other) noexcept -{ - and_ = std::move(other.and_); - or_ = std::move(other.or_); - equal = std::move(other.equal); - notEqual = std::move(other.notEqual); - contains = std::move(other.contains); - notContains = std::move(other.notContains); - in = std::move(other.in); - notIn = std::move(other.notIn); - startsWith = std::move(other.startsWith); - notStartsWith = std::move(other.notStartsWith); - endsWith = std::move(other.endsWith); - notEndsWith = std::move(other.notEndsWith); - - return *this; -} - -SecondNestedInput::SecondNestedInput() noexcept - : id {} - , third {} -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -SecondNestedInput::SecondNestedInput( - response::IdType idArg, - ThirdNestedInput thirdArg) noexcept - : id { std::move(idArg) } - , third { std::move(thirdArg) } -{ -} - -SecondNestedInput::SecondNestedInput(const SecondNestedInput& other) - : id { service::ModifiedArgument::duplicate(other.id) } - , third { service::ModifiedArgument::duplicate(other.third) } -{ -} - -SecondNestedInput::SecondNestedInput(SecondNestedInput&& other) noexcept - : id { std::move(other.id) } - , third { std::move(other.third) } -{ -} - -SecondNestedInput::~SecondNestedInput() -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -SecondNestedInput& SecondNestedInput::operator=(const SecondNestedInput& other) -{ - SecondNestedInput value { other }; - - std::swap(*this, value); - - return *this; -} - -SecondNestedInput& SecondNestedInput::operator=(SecondNestedInput&& other) noexcept -{ - id = std::move(other.id); - third = std::move(other.third); - - return *this; -} - -ForwardDeclaredInput::ForwardDeclaredInput() noexcept - : nullableSelf {} - , listSelves {} -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -ForwardDeclaredInput::ForwardDeclaredInput( - std::unique_ptr nullableSelfArg, - IncludeNonNullableListSelfInput listSelvesArg) noexcept - : nullableSelf { std::move(nullableSelfArg) } - , listSelves { std::move(listSelvesArg) } -{ -} - -ForwardDeclaredInput::ForwardDeclaredInput(const ForwardDeclaredInput& other) - : nullableSelf { service::ModifiedArgument::duplicate(other.nullableSelf) } - , listSelves { service::ModifiedArgument::duplicate(other.listSelves) } -{ -} - -ForwardDeclaredInput::ForwardDeclaredInput(ForwardDeclaredInput&& other) noexcept - : nullableSelf { std::move(other.nullableSelf) } - , listSelves { std::move(other.listSelves) } -{ -} - -ForwardDeclaredInput::~ForwardDeclaredInput() -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -ForwardDeclaredInput& ForwardDeclaredInput::operator=(const ForwardDeclaredInput& other) -{ - ForwardDeclaredInput value { other }; - - std::swap(*this, value); - - return *this; -} - -ForwardDeclaredInput& ForwardDeclaredInput::operator=(ForwardDeclaredInput&& other) noexcept -{ - nullableSelf = std::move(other.nullableSelf); - listSelves = std::move(other.listSelves); - - return *this; -} - -FirstNestedInput::FirstNestedInput() noexcept - : id {} - , second {} - , third {} -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -FirstNestedInput::FirstNestedInput( - response::IdType idArg, - SecondNestedInput secondArg, - ThirdNestedInput thirdArg) noexcept - : id { std::move(idArg) } - , second { std::move(secondArg) } - , third { std::move(thirdArg) } -{ -} - -FirstNestedInput::FirstNestedInput(const FirstNestedInput& other) - : id { service::ModifiedArgument::duplicate(other.id) } - , second { service::ModifiedArgument::duplicate(other.second) } - , third { service::ModifiedArgument::duplicate(other.third) } -{ -} - -FirstNestedInput::FirstNestedInput(FirstNestedInput&& other) noexcept - : id { std::move(other.id) } - , second { std::move(other.second) } - , third { std::move(other.third) } -{ -} - -FirstNestedInput::~FirstNestedInput() -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -FirstNestedInput& FirstNestedInput::operator=(const FirstNestedInput& other) -{ - FirstNestedInput value { other }; - - std::swap(*this, value); - - return *this; -} - -FirstNestedInput& FirstNestedInput::operator=(FirstNestedInput&& other) noexcept -{ - id = std::move(other.id); - second = std::move(other.second); - third = std::move(other.third); - - return *this; -} +namespace graphql::today { Operations::Operations(std::shared_ptr query, std::shared_ptr mutation, std::shared_ptr subscription) : service::Request({ @@ -811,18 +100,20 @@ void AddTypesToSchema(const std::shared_ptr& schema) auto typeExpensive = schema::ObjectType::Make(R"gql(Expensive)gql"sv, R"md()md"sv); schema->AddType(R"gql(Expensive)gql"sv, typeExpensive); + static const auto s_namesTaskState = getTaskStateNames(); typeTaskState->AddEnumValues({ - { service::s_namesTaskState[static_cast(today::TaskState::Unassigned)], R"md()md"sv, std::make_optional(R"md(Need to deprecate an [enum value](https://spec.graphql.org/October2021/#sec-Schema-Introspection.Deprecation))md"sv) }, - { service::s_namesTaskState[static_cast(today::TaskState::New)], R"md()md"sv, std::nullopt }, - { service::s_namesTaskState[static_cast(today::TaskState::Started)], R"md()md"sv, std::nullopt }, - { service::s_namesTaskState[static_cast(today::TaskState::Complete)], R"md()md"sv, std::nullopt } + { s_namesTaskState[static_cast(today::TaskState::Unassigned)], R"md()md"sv, std::make_optional(R"md(Need to deprecate an [enum value](https://spec.graphql.org/October2021/#sec-Schema-Introspection.Deprecation))md"sv) }, + { s_namesTaskState[static_cast(today::TaskState::New)], R"md()md"sv, std::nullopt }, + { s_namesTaskState[static_cast(today::TaskState::Started)], R"md()md"sv, std::nullopt }, + { s_namesTaskState[static_cast(today::TaskState::Complete)], R"md()md"sv, std::nullopt } }); typeCompleteTaskInput->AddInputValues({ schema::InputValue::Make(R"gql(id)gql"sv, R"md()md"sv, schema->WrapType(introspection::TypeKind::NON_NULL, schema->LookupType(R"gql(ID)gql"sv)), R"gql()gql"sv), schema::InputValue::Make(R"gql(testTaskState)gql"sv, R"md()md"sv, schema->LookupType(R"gql(TaskState)gql"sv), R"gql()gql"sv), schema::InputValue::Make(R"gql(isComplete)gql"sv, R"md()md"sv, schema->LookupType(R"gql(Boolean)gql"sv), R"gql(true)gql"sv), - schema::InputValue::Make(R"gql(clientMutationId)gql"sv, R"md()md"sv, schema->LookupType(R"gql(String)gql"sv), R"gql()gql"sv) + schema::InputValue::Make(R"gql(clientMutationId)gql"sv, R"md()md"sv, schema->LookupType(R"gql(String)gql"sv), R"gql()gql"sv), + schema::InputValue::Make(R"gql(boolList)gql"sv, R"md()md"sv, schema->WrapType(introspection::TypeKind::LIST, schema->WrapType(introspection::TypeKind::NON_NULL, schema->LookupType(R"gql(Boolean)gql"sv))), R"gql()gql"sv) }); typeThirdNestedInput->AddInputValues({ schema::InputValue::Make(R"gql(id)gql"sv, R"md()md"sv, schema->WrapType(introspection::TypeKind::NON_NULL, schema->LookupType(R"gql(ID)gql"sv)), R"gql()gql"sv), @@ -939,5 +230,4 @@ std::shared_ptr GetSchema() return schema; } -} // namespace today -} // namespace graphql +} // namespace graphql::today diff --git a/samples/today/schema/TodaySchema.h b/samples/today/schema/TodaySchema.h index ebcd3905..80b99eee 100644 --- a/samples/today/schema/TodaySchema.h +++ b/samples/today/schema/TodaySchema.h @@ -8,227 +8,24 @@ #ifndef TODAYSCHEMA_H #define TODAYSCHEMA_H +#include "graphqlservice/GraphQLResponse.h" +#include "graphqlservice/GraphQLService.h" + +#include "graphqlservice/internal/Version.h" #include "graphqlservice/internal/Schema.h" -// Check if the library version is compatible with schemagen 4.5.0 -static_assert(graphql::internal::MajorVersion == 4, "regenerate with schemagen: major version mismatch"); -static_assert(graphql::internal::MinorVersion == 5, "regenerate with schemagen: minor version mismatch"); +#include "TodaySharedTypes.h" #include #include #include #include -namespace graphql { -namespace today { - -enum class [[nodiscard("unnecessary conversion")]] TaskState -{ - Unassigned, - New, - Started, - Complete -}; - -[[nodiscard("unnecessary call")]] constexpr auto getTaskStateNames() noexcept -{ - using namespace std::literals; - - return std::array { - R"gql(Unassigned)gql"sv, - R"gql(New)gql"sv, - R"gql(Started)gql"sv, - R"gql(Complete)gql"sv - }; -} - -[[nodiscard("unnecessary call")]] constexpr auto getTaskStateValues() noexcept -{ - using namespace std::literals; - - return std::array, 4> { - std::make_pair(R"gql(New)gql"sv, TaskState::New), - std::make_pair(R"gql(Started)gql"sv, TaskState::Started), - std::make_pair(R"gql(Complete)gql"sv, TaskState::Complete), - std::make_pair(R"gql(Unassigned)gql"sv, TaskState::Unassigned) - }; -} - -struct [[nodiscard("unnecessary construction")]] CompleteTaskInput -{ - explicit CompleteTaskInput() noexcept; - explicit CompleteTaskInput( - response::IdType idArg, - std::optional testTaskStateArg, - std::optional isCompleteArg, - std::optional clientMutationIdArg) noexcept; - CompleteTaskInput(const CompleteTaskInput& other); - CompleteTaskInput(CompleteTaskInput&& other) noexcept; - ~CompleteTaskInput(); - - CompleteTaskInput& operator=(const CompleteTaskInput& other); - CompleteTaskInput& operator=(CompleteTaskInput&& other) noexcept; - - response::IdType id; - std::optional testTaskState; - std::optional isComplete; - std::optional clientMutationId; -}; - -struct SecondNestedInput; - -struct [[nodiscard("unnecessary construction")]] ThirdNestedInput -{ - explicit ThirdNestedInput() noexcept; - explicit ThirdNestedInput( - response::IdType idArg, - std::unique_ptr secondArg) noexcept; - ThirdNestedInput(const ThirdNestedInput& other); - ThirdNestedInput(ThirdNestedInput&& other) noexcept; - ~ThirdNestedInput(); - - ThirdNestedInput& operator=(const ThirdNestedInput& other); - ThirdNestedInput& operator=(ThirdNestedInput&& other) noexcept; - - response::IdType id; - std::unique_ptr second; -}; - -struct [[nodiscard("unnecessary construction")]] FourthNestedInput -{ - explicit FourthNestedInput() noexcept; - explicit FourthNestedInput( - response::IdType idArg) noexcept; - FourthNestedInput(const FourthNestedInput& other); - FourthNestedInput(FourthNestedInput&& other) noexcept; - ~FourthNestedInput(); - - FourthNestedInput& operator=(const FourthNestedInput& other); - FourthNestedInput& operator=(FourthNestedInput&& other) noexcept; - - response::IdType id; -}; - -struct [[nodiscard("unnecessary construction")]] IncludeNullableSelfInput -{ - explicit IncludeNullableSelfInput() noexcept; - explicit IncludeNullableSelfInput( - std::unique_ptr selfArg) noexcept; - IncludeNullableSelfInput(const IncludeNullableSelfInput& other); - IncludeNullableSelfInput(IncludeNullableSelfInput&& other) noexcept; - ~IncludeNullableSelfInput(); - - IncludeNullableSelfInput& operator=(const IncludeNullableSelfInput& other); - IncludeNullableSelfInput& operator=(IncludeNullableSelfInput&& other) noexcept; - - std::unique_ptr self; -}; - -struct [[nodiscard("unnecessary construction")]] IncludeNonNullableListSelfInput -{ - explicit IncludeNonNullableListSelfInput() noexcept; - explicit IncludeNonNullableListSelfInput( - std::vector selvesArg) noexcept; - IncludeNonNullableListSelfInput(const IncludeNonNullableListSelfInput& other); - IncludeNonNullableListSelfInput(IncludeNonNullableListSelfInput&& other) noexcept; - ~IncludeNonNullableListSelfInput(); - - IncludeNonNullableListSelfInput& operator=(const IncludeNonNullableListSelfInput& other); - IncludeNonNullableListSelfInput& operator=(IncludeNonNullableListSelfInput&& other) noexcept; - - std::vector selves; -}; - -struct [[nodiscard("unnecessary construction")]] StringOperationFilterInput -{ - explicit StringOperationFilterInput() noexcept; - explicit StringOperationFilterInput( - std::optional> and_Arg, - std::optional> or_Arg, - std::optional equalArg, - std::optional notEqualArg, - std::optional containsArg, - std::optional notContainsArg, - std::optional> inArg, - std::optional> notInArg, - std::optional startsWithArg, - std::optional notStartsWithArg, - std::optional endsWithArg, - std::optional notEndsWithArg) noexcept; - StringOperationFilterInput(const StringOperationFilterInput& other); - StringOperationFilterInput(StringOperationFilterInput&& other) noexcept; - ~StringOperationFilterInput(); - - StringOperationFilterInput& operator=(const StringOperationFilterInput& other); - StringOperationFilterInput& operator=(StringOperationFilterInput&& other) noexcept; - - std::optional> and_; - std::optional> or_; - std::optional equal; - std::optional notEqual; - std::optional contains; - std::optional notContains; - std::optional> in; - std::optional> notIn; - std::optional startsWith; - std::optional notStartsWith; - std::optional endsWith; - std::optional notEndsWith; -}; - -struct [[nodiscard("unnecessary construction")]] SecondNestedInput -{ - explicit SecondNestedInput() noexcept; - explicit SecondNestedInput( - response::IdType idArg, - ThirdNestedInput thirdArg) noexcept; - SecondNestedInput(const SecondNestedInput& other); - SecondNestedInput(SecondNestedInput&& other) noexcept; - ~SecondNestedInput(); - - SecondNestedInput& operator=(const SecondNestedInput& other); - SecondNestedInput& operator=(SecondNestedInput&& other) noexcept; - - response::IdType id; - ThirdNestedInput third; -}; - -struct [[nodiscard("unnecessary construction")]] ForwardDeclaredInput -{ - explicit ForwardDeclaredInput() noexcept; - explicit ForwardDeclaredInput( - std::unique_ptr nullableSelfArg, - IncludeNonNullableListSelfInput listSelvesArg) noexcept; - ForwardDeclaredInput(const ForwardDeclaredInput& other); - ForwardDeclaredInput(ForwardDeclaredInput&& other) noexcept; - ~ForwardDeclaredInput(); - - ForwardDeclaredInput& operator=(const ForwardDeclaredInput& other); - ForwardDeclaredInput& operator=(ForwardDeclaredInput&& other) noexcept; - - std::unique_ptr nullableSelf; - IncludeNonNullableListSelfInput listSelves; -}; - -struct [[nodiscard("unnecessary construction")]] FirstNestedInput -{ - explicit FirstNestedInput() noexcept; - explicit FirstNestedInput( - response::IdType idArg, - SecondNestedInput secondArg, - ThirdNestedInput thirdArg) noexcept; - FirstNestedInput(const FirstNestedInput& other); - FirstNestedInput(FirstNestedInput&& other) noexcept; - ~FirstNestedInput(); - - FirstNestedInput& operator=(const FirstNestedInput& other); - FirstNestedInput& operator=(FirstNestedInput&& other) noexcept; - - response::IdType id; - SecondNestedInput second; - ThirdNestedInput third; -}; +// Check if the library version is compatible with schemagen 5.0.0 +static_assert(graphql::internal::MajorVersion == 5, "regenerate with schemagen: major version mismatch"); +static_assert(graphql::internal::MinorVersion == 0, "regenerate with schemagen: minor version mismatch"); +namespace graphql::today { namespace object { class Node; @@ -299,7 +96,6 @@ void AddExpensiveDetails(const std::shared_ptr& typeExpensiv std::shared_ptr GetSchema(); -} // namespace today -} // namespace graphql +} // namespace graphql::today #endif // TODAYSCHEMA_H diff --git a/samples/today/schema/TodaySchema.ixx b/samples/today/schema/TodaySchema.ixx new file mode 100644 index 00000000..56aad8e6 --- /dev/null +++ b/samples/today/schema/TodaySchema.ixx @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodaySchema.h" + +export module GraphQL.Today.TodaySchema; + +export import GraphQL.Today.TodaySharedTypes; + +export import GraphQL.Today.NodeObject; +export import GraphQL.Today.UnionTypeObject; +export import GraphQL.Today.QueryObject; +export import GraphQL.Today.PageInfoObject; +export import GraphQL.Today.AppointmentEdgeObject; +export import GraphQL.Today.AppointmentConnectionObject; +export import GraphQL.Today.TaskEdgeObject; +export import GraphQL.Today.TaskConnectionObject; +export import GraphQL.Today.FolderEdgeObject; +export import GraphQL.Today.FolderConnectionObject; +export import GraphQL.Today.CompleteTaskPayloadObject; +export import GraphQL.Today.MutationObject; +export import GraphQL.Today.SubscriptionObject; +export import GraphQL.Today.AppointmentObject; +export import GraphQL.Today.TaskObject; +export import GraphQL.Today.FolderObject; +export import GraphQL.Today.NestedTypeObject; +export import GraphQL.Today.ExpensiveObject; + +export namespace graphql::today { + +using today::Operations; + +using today::AddNodeDetails; +using today::AddUnionTypeDetails; +using today::AddQueryDetails; +using today::AddPageInfoDetails; +using today::AddAppointmentEdgeDetails; +using today::AddAppointmentConnectionDetails; +using today::AddTaskEdgeDetails; +using today::AddTaskConnectionDetails; +using today::AddFolderEdgeDetails; +using today::AddFolderConnectionDetails; +using today::AddCompleteTaskPayloadDetails; +using today::AddMutationDetails; +using today::AddSubscriptionDetails; +using today::AddAppointmentDetails; +using today::AddTaskDetails; +using today::AddFolderDetails; +using today::AddNestedTypeDetails; +using today::AddExpensiveDetails; + +using today::GetSchema; + +} // namespace graphql::today diff --git a/samples/today/schema/TodaySharedTypes.cpp b/samples/today/schema/TodaySharedTypes.cpp new file mode 100644 index 00000000..ecae70c3 --- /dev/null +++ b/samples/today/schema/TodaySharedTypes.cpp @@ -0,0 +1,747 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +#include "graphqlservice/GraphQLService.h" + +#include "TodaySharedTypes.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std::literals; + +namespace graphql { +namespace service { + +static const auto s_namesTaskState = today::getTaskStateNames(); +static const auto s_valuesTaskState = today::getTaskStateValues(); + +template <> +today::TaskState Argument::convert(const response::Value& value) +{ + if (!value.maybe_enum()) + { + throw service::schema_exception { { R"ex(not a valid TaskState value)ex" } }; + } + + const auto result = internal::sorted_map_lookup( + s_valuesTaskState, + std::string_view { value.get() }); + + if (!result) + { + throw service::schema_exception { { R"ex(not a valid TaskState value)ex" } }; + } + + return *result; +} + +template <> +service::AwaitableResolver Result::convert(service::AwaitableScalar result, ResolverParams&& params) +{ + return ModifiedResult::resolve(std::move(result), std::move(params), + [](today::TaskState value, const ResolverParams&) + { + const auto idx = static_cast(value); + + if (idx >= s_namesTaskState.size()) + { + throw service::schema_exception { { R"ex(Enum value out of range for TaskState)ex" } }; + } + + return ResolverResult { { response::ValueToken::EnumValue { std::string { s_namesTaskState[idx] } } } }; + }); +} + +template <> +void Result::validateScalar(const response::Value& value) +{ + if (!value.maybe_enum()) + { + throw service::schema_exception { { R"ex(not a valid TaskState value)ex" } }; + } + + const auto [itr, itrEnd] = internal::sorted_map_equal_range( + s_valuesTaskState.begin(), + s_valuesTaskState.end(), + std::string_view { value.get() }); + + if (itr == itrEnd) + { + throw service::schema_exception { { R"ex(not a valid TaskState value)ex" } }; + } +} + +template <> +today::CompleteTaskInput Argument::convert(const response::Value& value) +{ + const auto defaultValue = []() + { + response::Value values(response::Type::Map); + response::Value entry; + + entry = response::Value(true); + values.emplace_back("isComplete", std::move(entry)); + + return values; + }(); + + auto valueId = service::ModifiedArgument::require("id", value); + auto valueTestTaskState = service::ModifiedArgument::require("testTaskState", value); + auto pairIsComplete = service::ModifiedArgument::find("isComplete", value); + auto valueIsComplete = (pairIsComplete.second + ? std::move(pairIsComplete.first) + : service::ModifiedArgument::require("isComplete", defaultValue)); + auto valueClientMutationId = service::ModifiedArgument::require("clientMutationId", value); + auto valueBoolList = service::ModifiedArgument::require("boolList", value); + + return today::CompleteTaskInput { + std::move(valueId), + valueTestTaskState, + std::move(valueIsComplete), + std::move(valueClientMutationId), + std::move(valueBoolList) + }; +} + +template <> +today::ThirdNestedInput Argument::convert(const response::Value& value) +{ + auto valueId = service::ModifiedArgument::require("id", value); + auto valueSecond = service::ModifiedArgument::require("second", value); + + return today::ThirdNestedInput { + std::move(valueId), + std::move(valueSecond) + }; +} + +template <> +today::FourthNestedInput Argument::convert(const response::Value& value) +{ + auto valueId = service::ModifiedArgument::require("id", value); + + return today::FourthNestedInput { + std::move(valueId) + }; +} + +template <> +today::IncludeNullableSelfInput Argument::convert(const response::Value& value) +{ + auto valueSelf = service::ModifiedArgument::require("self", value); + + return today::IncludeNullableSelfInput { + std::move(valueSelf) + }; +} + +template <> +today::IncludeNonNullableListSelfInput Argument::convert(const response::Value& value) +{ + auto valueSelves = service::ModifiedArgument::require("selves", value); + + return today::IncludeNonNullableListSelfInput { + std::move(valueSelves) + }; +} + +template <> +today::StringOperationFilterInput Argument::convert(const response::Value& value) +{ + auto valueAnd_ = service::ModifiedArgument::require("and", value); + auto valueOr_ = service::ModifiedArgument::require("or", value); + auto valueEqual = service::ModifiedArgument::require("equal", value); + auto valueNotEqual = service::ModifiedArgument::require("notEqual", value); + auto valueContains = service::ModifiedArgument::require("contains", value); + auto valueNotContains = service::ModifiedArgument::require("notContains", value); + auto valueIn = service::ModifiedArgument::require("in", value); + auto valueNotIn = service::ModifiedArgument::require("notIn", value); + auto valueStartsWith = service::ModifiedArgument::require("startsWith", value); + auto valueNotStartsWith = service::ModifiedArgument::require("notStartsWith", value); + auto valueEndsWith = service::ModifiedArgument::require("endsWith", value); + auto valueNotEndsWith = service::ModifiedArgument::require("notEndsWith", value); + + return today::StringOperationFilterInput { + std::move(valueAnd_), + std::move(valueOr_), + std::move(valueEqual), + std::move(valueNotEqual), + std::move(valueContains), + std::move(valueNotContains), + std::move(valueIn), + std::move(valueNotIn), + std::move(valueStartsWith), + std::move(valueNotStartsWith), + std::move(valueEndsWith), + std::move(valueNotEndsWith) + }; +} + +template <> +today::SecondNestedInput Argument::convert(const response::Value& value) +{ + auto valueId = service::ModifiedArgument::require("id", value); + auto valueThird = service::ModifiedArgument::require("third", value); + + return today::SecondNestedInput { + std::move(valueId), + std::move(valueThird) + }; +} + +template <> +today::ForwardDeclaredInput Argument::convert(const response::Value& value) +{ + auto valueNullableSelf = service::ModifiedArgument::require("nullableSelf", value); + auto valueListSelves = service::ModifiedArgument::require("listSelves", value); + + return today::ForwardDeclaredInput { + std::move(valueNullableSelf), + std::move(valueListSelves) + }; +} + +template <> +today::FirstNestedInput Argument::convert(const response::Value& value) +{ + auto valueId = service::ModifiedArgument::require("id", value); + auto valueSecond = service::ModifiedArgument::require("second", value); + auto valueThird = service::ModifiedArgument::require("third", value); + + return today::FirstNestedInput { + std::move(valueId), + std::move(valueSecond), + std::move(valueThird) + }; +} + +} // namespace service + +namespace today { + +CompleteTaskInput::CompleteTaskInput() noexcept + : id {} + , testTaskState {} + , isComplete {} + , clientMutationId {} + , boolList {} +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +CompleteTaskInput::CompleteTaskInput( + response::IdType idArg, + std::optional testTaskStateArg, + std::optional isCompleteArg, + std::optional clientMutationIdArg, + std::optional> boolListArg) noexcept + : id { std::move(idArg) } + , testTaskState { std::move(testTaskStateArg) } + , isComplete { std::move(isCompleteArg) } + , clientMutationId { std::move(clientMutationIdArg) } + , boolList { std::move(boolListArg) } +{ +} + +CompleteTaskInput::CompleteTaskInput(const CompleteTaskInput& other) + : id { service::ModifiedArgument::duplicate(other.id) } + , testTaskState { service::ModifiedArgument::duplicate(other.testTaskState) } + , isComplete { service::ModifiedArgument::duplicate(other.isComplete) } + , clientMutationId { service::ModifiedArgument::duplicate(other.clientMutationId) } + , boolList { service::ModifiedArgument::duplicate(other.boolList) } +{ +} + +CompleteTaskInput::CompleteTaskInput(CompleteTaskInput&& other) noexcept + : id { std::move(other.id) } + , testTaskState { std::move(other.testTaskState) } + , isComplete { std::move(other.isComplete) } + , clientMutationId { std::move(other.clientMutationId) } + , boolList { std::move(other.boolList) } +{ +} + +CompleteTaskInput::~CompleteTaskInput() +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +CompleteTaskInput& CompleteTaskInput::operator=(const CompleteTaskInput& other) +{ + CompleteTaskInput value { other }; + + std::swap(*this, value); + + return *this; +} + +CompleteTaskInput& CompleteTaskInput::operator=(CompleteTaskInput&& other) noexcept +{ + id = std::move(other.id); + testTaskState = std::move(other.testTaskState); + isComplete = std::move(other.isComplete); + clientMutationId = std::move(other.clientMutationId); + boolList = std::move(other.boolList); + + return *this; +} + + +ThirdNestedInput::ThirdNestedInput() noexcept + : id {} + , second {} +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +ThirdNestedInput::ThirdNestedInput( + response::IdType idArg, + std::unique_ptr secondArg) noexcept + : id { std::move(idArg) } + , second { std::move(secondArg) } +{ +} + +ThirdNestedInput::ThirdNestedInput(const ThirdNestedInput& other) + : id { service::ModifiedArgument::duplicate(other.id) } + , second { service::ModifiedArgument::duplicate(other.second) } +{ +} + +ThirdNestedInput::ThirdNestedInput(ThirdNestedInput&& other) noexcept + : id { std::move(other.id) } + , second { std::move(other.second) } +{ +} + +ThirdNestedInput::~ThirdNestedInput() +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +ThirdNestedInput& ThirdNestedInput::operator=(const ThirdNestedInput& other) +{ + ThirdNestedInput value { other }; + + std::swap(*this, value); + + return *this; +} + +ThirdNestedInput& ThirdNestedInput::operator=(ThirdNestedInput&& other) noexcept +{ + id = std::move(other.id); + second = std::move(other.second); + + return *this; +} + + +FourthNestedInput::FourthNestedInput() noexcept + : id {} +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +FourthNestedInput::FourthNestedInput( + response::IdType idArg) noexcept + : id { std::move(idArg) } +{ +} + +FourthNestedInput::FourthNestedInput(const FourthNestedInput& other) + : id { service::ModifiedArgument::duplicate(other.id) } +{ +} + +FourthNestedInput::FourthNestedInput(FourthNestedInput&& other) noexcept + : id { std::move(other.id) } +{ +} + +FourthNestedInput::~FourthNestedInput() +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +FourthNestedInput& FourthNestedInput::operator=(const FourthNestedInput& other) +{ + FourthNestedInput value { other }; + + std::swap(*this, value); + + return *this; +} + +FourthNestedInput& FourthNestedInput::operator=(FourthNestedInput&& other) noexcept +{ + id = std::move(other.id); + + return *this; +} + + +IncludeNullableSelfInput::IncludeNullableSelfInput() noexcept + : self {} +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +IncludeNullableSelfInput::IncludeNullableSelfInput( + std::unique_ptr selfArg) noexcept + : self { std::move(selfArg) } +{ +} + +IncludeNullableSelfInput::IncludeNullableSelfInput(const IncludeNullableSelfInput& other) + : self { service::ModifiedArgument::duplicate(other.self) } +{ +} + +IncludeNullableSelfInput::IncludeNullableSelfInput(IncludeNullableSelfInput&& other) noexcept + : self { std::move(other.self) } +{ +} + +IncludeNullableSelfInput::~IncludeNullableSelfInput() +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +IncludeNullableSelfInput& IncludeNullableSelfInput::operator=(const IncludeNullableSelfInput& other) +{ + IncludeNullableSelfInput value { other }; + + std::swap(*this, value); + + return *this; +} + +IncludeNullableSelfInput& IncludeNullableSelfInput::operator=(IncludeNullableSelfInput&& other) noexcept +{ + self = std::move(other.self); + + return *this; +} + + +IncludeNonNullableListSelfInput::IncludeNonNullableListSelfInput() noexcept + : selves {} +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +IncludeNonNullableListSelfInput::IncludeNonNullableListSelfInput( + std::vector selvesArg) noexcept + : selves { std::move(selvesArg) } +{ +} + +IncludeNonNullableListSelfInput::IncludeNonNullableListSelfInput(const IncludeNonNullableListSelfInput& other) + : selves { service::ModifiedArgument::duplicate(other.selves) } +{ +} + +IncludeNonNullableListSelfInput::IncludeNonNullableListSelfInput(IncludeNonNullableListSelfInput&& other) noexcept + : selves { std::move(other.selves) } +{ +} + +IncludeNonNullableListSelfInput::~IncludeNonNullableListSelfInput() +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +IncludeNonNullableListSelfInput& IncludeNonNullableListSelfInput::operator=(const IncludeNonNullableListSelfInput& other) +{ + IncludeNonNullableListSelfInput value { other }; + + std::swap(*this, value); + + return *this; +} + +IncludeNonNullableListSelfInput& IncludeNonNullableListSelfInput::operator=(IncludeNonNullableListSelfInput&& other) noexcept +{ + selves = std::move(other.selves); + + return *this; +} + + +StringOperationFilterInput::StringOperationFilterInput() noexcept + : and_ {} + , or_ {} + , equal {} + , notEqual {} + , contains {} + , notContains {} + , in {} + , notIn {} + , startsWith {} + , notStartsWith {} + , endsWith {} + , notEndsWith {} +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +StringOperationFilterInput::StringOperationFilterInput( + std::optional> and_Arg, + std::optional> or_Arg, + std::optional equalArg, + std::optional notEqualArg, + std::optional containsArg, + std::optional notContainsArg, + std::optional> inArg, + std::optional> notInArg, + std::optional startsWithArg, + std::optional notStartsWithArg, + std::optional endsWithArg, + std::optional notEndsWithArg) noexcept + : and_ { std::move(and_Arg) } + , or_ { std::move(or_Arg) } + , equal { std::move(equalArg) } + , notEqual { std::move(notEqualArg) } + , contains { std::move(containsArg) } + , notContains { std::move(notContainsArg) } + , in { std::move(inArg) } + , notIn { std::move(notInArg) } + , startsWith { std::move(startsWithArg) } + , notStartsWith { std::move(notStartsWithArg) } + , endsWith { std::move(endsWithArg) } + , notEndsWith { std::move(notEndsWithArg) } +{ +} + +StringOperationFilterInput::StringOperationFilterInput(const StringOperationFilterInput& other) + : and_ { service::ModifiedArgument::duplicate(other.and_) } + , or_ { service::ModifiedArgument::duplicate(other.or_) } + , equal { service::ModifiedArgument::duplicate(other.equal) } + , notEqual { service::ModifiedArgument::duplicate(other.notEqual) } + , contains { service::ModifiedArgument::duplicate(other.contains) } + , notContains { service::ModifiedArgument::duplicate(other.notContains) } + , in { service::ModifiedArgument::duplicate(other.in) } + , notIn { service::ModifiedArgument::duplicate(other.notIn) } + , startsWith { service::ModifiedArgument::duplicate(other.startsWith) } + , notStartsWith { service::ModifiedArgument::duplicate(other.notStartsWith) } + , endsWith { service::ModifiedArgument::duplicate(other.endsWith) } + , notEndsWith { service::ModifiedArgument::duplicate(other.notEndsWith) } +{ +} + +StringOperationFilterInput::StringOperationFilterInput(StringOperationFilterInput&& other) noexcept + : and_ { std::move(other.and_) } + , or_ { std::move(other.or_) } + , equal { std::move(other.equal) } + , notEqual { std::move(other.notEqual) } + , contains { std::move(other.contains) } + , notContains { std::move(other.notContains) } + , in { std::move(other.in) } + , notIn { std::move(other.notIn) } + , startsWith { std::move(other.startsWith) } + , notStartsWith { std::move(other.notStartsWith) } + , endsWith { std::move(other.endsWith) } + , notEndsWith { std::move(other.notEndsWith) } +{ +} + +StringOperationFilterInput::~StringOperationFilterInput() +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +StringOperationFilterInput& StringOperationFilterInput::operator=(const StringOperationFilterInput& other) +{ + StringOperationFilterInput value { other }; + + std::swap(*this, value); + + return *this; +} + +StringOperationFilterInput& StringOperationFilterInput::operator=(StringOperationFilterInput&& other) noexcept +{ + and_ = std::move(other.and_); + or_ = std::move(other.or_); + equal = std::move(other.equal); + notEqual = std::move(other.notEqual); + contains = std::move(other.contains); + notContains = std::move(other.notContains); + in = std::move(other.in); + notIn = std::move(other.notIn); + startsWith = std::move(other.startsWith); + notStartsWith = std::move(other.notStartsWith); + endsWith = std::move(other.endsWith); + notEndsWith = std::move(other.notEndsWith); + + return *this; +} + + +SecondNestedInput::SecondNestedInput() noexcept + : id {} + , third {} +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +SecondNestedInput::SecondNestedInput( + response::IdType idArg, + ThirdNestedInput thirdArg) noexcept + : id { std::move(idArg) } + , third { std::move(thirdArg) } +{ +} + +SecondNestedInput::SecondNestedInput(const SecondNestedInput& other) + : id { service::ModifiedArgument::duplicate(other.id) } + , third { service::ModifiedArgument::duplicate(other.third) } +{ +} + +SecondNestedInput::SecondNestedInput(SecondNestedInput&& other) noexcept + : id { std::move(other.id) } + , third { std::move(other.third) } +{ +} + +SecondNestedInput::~SecondNestedInput() +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +SecondNestedInput& SecondNestedInput::operator=(const SecondNestedInput& other) +{ + SecondNestedInput value { other }; + + std::swap(*this, value); + + return *this; +} + +SecondNestedInput& SecondNestedInput::operator=(SecondNestedInput&& other) noexcept +{ + id = std::move(other.id); + third = std::move(other.third); + + return *this; +} + + +ForwardDeclaredInput::ForwardDeclaredInput() noexcept + : nullableSelf {} + , listSelves {} +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +ForwardDeclaredInput::ForwardDeclaredInput( + std::unique_ptr nullableSelfArg, + IncludeNonNullableListSelfInput listSelvesArg) noexcept + : nullableSelf { std::move(nullableSelfArg) } + , listSelves { std::move(listSelvesArg) } +{ +} + +ForwardDeclaredInput::ForwardDeclaredInput(const ForwardDeclaredInput& other) + : nullableSelf { service::ModifiedArgument::duplicate(other.nullableSelf) } + , listSelves { service::ModifiedArgument::duplicate(other.listSelves) } +{ +} + +ForwardDeclaredInput::ForwardDeclaredInput(ForwardDeclaredInput&& other) noexcept + : nullableSelf { std::move(other.nullableSelf) } + , listSelves { std::move(other.listSelves) } +{ +} + +ForwardDeclaredInput::~ForwardDeclaredInput() +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +ForwardDeclaredInput& ForwardDeclaredInput::operator=(const ForwardDeclaredInput& other) +{ + ForwardDeclaredInput value { other }; + + std::swap(*this, value); + + return *this; +} + +ForwardDeclaredInput& ForwardDeclaredInput::operator=(ForwardDeclaredInput&& other) noexcept +{ + nullableSelf = std::move(other.nullableSelf); + listSelves = std::move(other.listSelves); + + return *this; +} + + +FirstNestedInput::FirstNestedInput() noexcept + : id {} + , second {} + , third {} +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +FirstNestedInput::FirstNestedInput( + response::IdType idArg, + SecondNestedInput secondArg, + ThirdNestedInput thirdArg) noexcept + : id { std::move(idArg) } + , second { std::move(secondArg) } + , third { std::move(thirdArg) } +{ +} + +FirstNestedInput::FirstNestedInput(const FirstNestedInput& other) + : id { service::ModifiedArgument::duplicate(other.id) } + , second { service::ModifiedArgument::duplicate(other.second) } + , third { service::ModifiedArgument::duplicate(other.third) } +{ +} + +FirstNestedInput::FirstNestedInput(FirstNestedInput&& other) noexcept + : id { std::move(other.id) } + , second { std::move(other.second) } + , third { std::move(other.third) } +{ +} + +FirstNestedInput::~FirstNestedInput() +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +FirstNestedInput& FirstNestedInput::operator=(const FirstNestedInput& other) +{ + FirstNestedInput value { other }; + + std::swap(*this, value); + + return *this; +} + +FirstNestedInput& FirstNestedInput::operator=(FirstNestedInput&& other) noexcept +{ + id = std::move(other.id); + second = std::move(other.second); + third = std::move(other.third); + + return *this; +} + +} // namespace today +} // namespace graphql diff --git a/samples/today/schema/TodaySharedTypes.h b/samples/today/schema/TodaySharedTypes.h new file mode 100644 index 00000000..a9f1ace9 --- /dev/null +++ b/samples/today/schema/TodaySharedTypes.h @@ -0,0 +1,241 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +#pragma once + +#ifndef TODAYSHAREDTYPES_H +#define TODAYSHAREDTYPES_H + +#include "graphqlservice/GraphQLResponse.h" + +#include "graphqlservice/internal/Version.h" + +#include +#include +#include +#include +#include +#include + +// Check if the library version is compatible with schemagen 5.0.0 +static_assert(graphql::internal::MajorVersion == 5, "regenerate with schemagen: major version mismatch"); +static_assert(graphql::internal::MinorVersion == 0, "regenerate with schemagen: minor version mismatch"); + +namespace graphql { +namespace today { + +enum class [[nodiscard("unnecessary conversion")]] TaskState +{ + Unassigned, + New, + Started, + Complete +}; + +[[nodiscard("unnecessary call")]] constexpr auto getTaskStateNames() noexcept +{ + using namespace std::literals; + + return std::array { + R"gql(Unassigned)gql"sv, + R"gql(New)gql"sv, + R"gql(Started)gql"sv, + R"gql(Complete)gql"sv + }; +} + +[[nodiscard("unnecessary call")]] constexpr auto getTaskStateValues() noexcept +{ + using namespace std::literals; + + return std::array, 4> { + std::make_pair(R"gql(New)gql"sv, TaskState::New), + std::make_pair(R"gql(Started)gql"sv, TaskState::Started), + std::make_pair(R"gql(Complete)gql"sv, TaskState::Complete), + std::make_pair(R"gql(Unassigned)gql"sv, TaskState::Unassigned) + }; +} + +struct [[nodiscard("unnecessary construction")]] CompleteTaskInput +{ + explicit CompleteTaskInput() noexcept; + explicit CompleteTaskInput( + response::IdType idArg, + std::optional testTaskStateArg, + std::optional isCompleteArg, + std::optional clientMutationIdArg, + std::optional> boolListArg) noexcept; + CompleteTaskInput(const CompleteTaskInput& other); + CompleteTaskInput(CompleteTaskInput&& other) noexcept; + ~CompleteTaskInput(); + + CompleteTaskInput& operator=(const CompleteTaskInput& other); + CompleteTaskInput& operator=(CompleteTaskInput&& other) noexcept; + + response::IdType id; + std::optional testTaskState; + std::optional isComplete; + std::optional clientMutationId; + std::optional> boolList; +}; + +struct SecondNestedInput; + +struct [[nodiscard("unnecessary construction")]] ThirdNestedInput +{ + explicit ThirdNestedInput() noexcept; + explicit ThirdNestedInput( + response::IdType idArg, + std::unique_ptr secondArg) noexcept; + ThirdNestedInput(const ThirdNestedInput& other); + ThirdNestedInput(ThirdNestedInput&& other) noexcept; + ~ThirdNestedInput(); + + ThirdNestedInput& operator=(const ThirdNestedInput& other); + ThirdNestedInput& operator=(ThirdNestedInput&& other) noexcept; + + response::IdType id; + std::unique_ptr second; +}; + +struct [[nodiscard("unnecessary construction")]] FourthNestedInput +{ + explicit FourthNestedInput() noexcept; + explicit FourthNestedInput( + response::IdType idArg) noexcept; + FourthNestedInput(const FourthNestedInput& other); + FourthNestedInput(FourthNestedInput&& other) noexcept; + ~FourthNestedInput(); + + FourthNestedInput& operator=(const FourthNestedInput& other); + FourthNestedInput& operator=(FourthNestedInput&& other) noexcept; + + response::IdType id; +}; + +struct [[nodiscard("unnecessary construction")]] IncludeNullableSelfInput +{ + explicit IncludeNullableSelfInput() noexcept; + explicit IncludeNullableSelfInput( + std::unique_ptr selfArg) noexcept; + IncludeNullableSelfInput(const IncludeNullableSelfInput& other); + IncludeNullableSelfInput(IncludeNullableSelfInput&& other) noexcept; + ~IncludeNullableSelfInput(); + + IncludeNullableSelfInput& operator=(const IncludeNullableSelfInput& other); + IncludeNullableSelfInput& operator=(IncludeNullableSelfInput&& other) noexcept; + + std::unique_ptr self; +}; + +struct [[nodiscard("unnecessary construction")]] IncludeNonNullableListSelfInput +{ + explicit IncludeNonNullableListSelfInput() noexcept; + explicit IncludeNonNullableListSelfInput( + std::vector selvesArg) noexcept; + IncludeNonNullableListSelfInput(const IncludeNonNullableListSelfInput& other); + IncludeNonNullableListSelfInput(IncludeNonNullableListSelfInput&& other) noexcept; + ~IncludeNonNullableListSelfInput(); + + IncludeNonNullableListSelfInput& operator=(const IncludeNonNullableListSelfInput& other); + IncludeNonNullableListSelfInput& operator=(IncludeNonNullableListSelfInput&& other) noexcept; + + std::vector selves; +}; + +struct [[nodiscard("unnecessary construction")]] StringOperationFilterInput +{ + explicit StringOperationFilterInput() noexcept; + explicit StringOperationFilterInput( + std::optional> and_Arg, + std::optional> or_Arg, + std::optional equalArg, + std::optional notEqualArg, + std::optional containsArg, + std::optional notContainsArg, + std::optional> inArg, + std::optional> notInArg, + std::optional startsWithArg, + std::optional notStartsWithArg, + std::optional endsWithArg, + std::optional notEndsWithArg) noexcept; + StringOperationFilterInput(const StringOperationFilterInput& other); + StringOperationFilterInput(StringOperationFilterInput&& other) noexcept; + ~StringOperationFilterInput(); + + StringOperationFilterInput& operator=(const StringOperationFilterInput& other); + StringOperationFilterInput& operator=(StringOperationFilterInput&& other) noexcept; + + std::optional> and_; + std::optional> or_; + std::optional equal; + std::optional notEqual; + std::optional contains; + std::optional notContains; + std::optional> in; + std::optional> notIn; + std::optional startsWith; + std::optional notStartsWith; + std::optional endsWith; + std::optional notEndsWith; +}; + +struct [[nodiscard("unnecessary construction")]] SecondNestedInput +{ + explicit SecondNestedInput() noexcept; + explicit SecondNestedInput( + response::IdType idArg, + ThirdNestedInput thirdArg) noexcept; + SecondNestedInput(const SecondNestedInput& other); + SecondNestedInput(SecondNestedInput&& other) noexcept; + ~SecondNestedInput(); + + SecondNestedInput& operator=(const SecondNestedInput& other); + SecondNestedInput& operator=(SecondNestedInput&& other) noexcept; + + response::IdType id; + ThirdNestedInput third; +}; + +struct [[nodiscard("unnecessary construction")]] ForwardDeclaredInput +{ + explicit ForwardDeclaredInput() noexcept; + explicit ForwardDeclaredInput( + std::unique_ptr nullableSelfArg, + IncludeNonNullableListSelfInput listSelvesArg) noexcept; + ForwardDeclaredInput(const ForwardDeclaredInput& other); + ForwardDeclaredInput(ForwardDeclaredInput&& other) noexcept; + ~ForwardDeclaredInput(); + + ForwardDeclaredInput& operator=(const ForwardDeclaredInput& other); + ForwardDeclaredInput& operator=(ForwardDeclaredInput&& other) noexcept; + + std::unique_ptr nullableSelf; + IncludeNonNullableListSelfInput listSelves; +}; + +struct [[nodiscard("unnecessary construction")]] FirstNestedInput +{ + explicit FirstNestedInput() noexcept; + explicit FirstNestedInput( + response::IdType idArg, + SecondNestedInput secondArg, + ThirdNestedInput thirdArg) noexcept; + FirstNestedInput(const FirstNestedInput& other); + FirstNestedInput(FirstNestedInput&& other) noexcept; + ~FirstNestedInput(); + + FirstNestedInput& operator=(const FirstNestedInput& other); + FirstNestedInput& operator=(FirstNestedInput&& other) noexcept; + + response::IdType id; + SecondNestedInput second; + ThirdNestedInput third; +}; + +} // namespace today +} // namespace graphql + +#endif // TODAYSHAREDTYPES_H diff --git a/samples/today/schema/TodaySharedTypes.ixx b/samples/today/schema/TodaySharedTypes.ixx new file mode 100644 index 00000000..ee76a95a --- /dev/null +++ b/samples/today/schema/TodaySharedTypes.ixx @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodaySharedTypes.h" + +export module GraphQL.Today.TodaySharedTypes; + +export namespace graphql::today { + +using today::TaskState; +using today::getTaskStateNames; +using today::getTaskStateValues; + +using today::CompleteTaskInput; +using today::ThirdNestedInput; +using today::FourthNestedInput; +using today::IncludeNullableSelfInput; +using today::IncludeNonNullableListSelfInput; +using today::StringOperationFilterInput; +using today::SecondNestedInput; +using today::ForwardDeclaredInput; +using today::FirstNestedInput; + +} // namespace graphql::today diff --git a/samples/today/schema/SubscriptionObject.h b/samples/today/schema/TodaySubscriptionObject.h similarity index 97% rename from samples/today/schema/SubscriptionObject.h rename to samples/today/schema/TodaySubscriptionObject.h index c9179d85..5e21ac98 100644 --- a/samples/today/schema/SubscriptionObject.h +++ b/samples/today/schema/TodaySubscriptionObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef SUBSCRIPTIONOBJECT_H -#define SUBSCRIPTIONOBJECT_H +#ifndef TODAY_TODAYSUBSCRIPTIONOBJECT_H +#define TODAY_TODAYSUBSCRIPTIONOBJECT_H #include "TodaySchema.h" @@ -157,4 +157,4 @@ class [[nodiscard("unnecessary construction")]] Subscription final } // namespace graphql::today::object -#endif // SUBSCRIPTIONOBJECT_H +#endif // TODAY_TODAYSUBSCRIPTIONOBJECT_H diff --git a/samples/today/nointrospection/TaskConnectionObject.h b/samples/today/schema/TodayTaskConnectionObject.h similarity index 97% rename from samples/today/nointrospection/TaskConnectionObject.h rename to samples/today/schema/TodayTaskConnectionObject.h index c82b2ae7..46d4153e 100644 --- a/samples/today/nointrospection/TaskConnectionObject.h +++ b/samples/today/schema/TodayTaskConnectionObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef TASKCONNECTIONOBJECT_H -#define TASKCONNECTIONOBJECT_H +#ifndef TODAY_TODAYTASKCONNECTIONOBJECT_H +#define TODAY_TODAYTASKCONNECTIONOBJECT_H #include "TodaySchema.h" @@ -157,4 +157,4 @@ class [[nodiscard("unnecessary construction")]] TaskConnection final } // namespace graphql::today::object -#endif // TASKCONNECTIONOBJECT_H +#endif // TODAY_TODAYTASKCONNECTIONOBJECT_H diff --git a/samples/today/schema/TaskEdgeObject.h b/samples/today/schema/TodayTaskEdgeObject.h similarity index 97% rename from samples/today/schema/TaskEdgeObject.h rename to samples/today/schema/TodayTaskEdgeObject.h index 4049d5c3..f4e29482 100644 --- a/samples/today/schema/TaskEdgeObject.h +++ b/samples/today/schema/TodayTaskEdgeObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef TASKEDGEOBJECT_H -#define TASKEDGEOBJECT_H +#ifndef TODAY_TODAYTASKEDGEOBJECT_H +#define TODAY_TODAYTASKEDGEOBJECT_H #include "TodaySchema.h" @@ -157,4 +157,4 @@ class [[nodiscard("unnecessary construction")]] TaskEdge final } // namespace graphql::today::object -#endif // TASKEDGEOBJECT_H +#endif // TODAY_TODAYTASKEDGEOBJECT_H diff --git a/samples/today/nointrospection/TaskObject.h b/samples/today/schema/TodayTaskObject.h similarity index 98% rename from samples/today/nointrospection/TaskObject.h rename to samples/today/schema/TodayTaskObject.h index 36548d0b..d2d31f91 100644 --- a/samples/today/nointrospection/TaskObject.h +++ b/samples/today/schema/TodayTaskObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef TASKOBJECT_H -#define TASKOBJECT_H +#ifndef TODAY_TODAYTASKOBJECT_H +#define TODAY_TODAYTASKOBJECT_H #include "TodaySchema.h" @@ -206,4 +206,4 @@ class [[nodiscard("unnecessary construction")]] Task final } // namespace graphql::today::object -#endif // TASKOBJECT_H +#endif // TODAY_TODAYTASKOBJECT_H diff --git a/samples/today/nointrospection/UnionTypeObject.h b/samples/today/schema/TodayUnionTypeObject.h similarity index 95% rename from samples/today/nointrospection/UnionTypeObject.h rename to samples/today/schema/TodayUnionTypeObject.h index 1be8494e..b99d420d 100644 --- a/samples/today/nointrospection/UnionTypeObject.h +++ b/samples/today/schema/TodayUnionTypeObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef UNIONTYPEOBJECT_H -#define UNIONTYPEOBJECT_H +#ifndef TODAY_TODAYUNIONTYPEOBJECT_H +#define TODAY_TODAYUNIONTYPEOBJECT_H #include "TodaySchema.h" @@ -78,4 +78,4 @@ class [[nodiscard("unnecessary construction")]] UnionType final } // namespace graphql::today::object -#endif // UNIONTYPEOBJECT_H +#endif // TODAY_TODAYUNIONTYPEOBJECT_H diff --git a/samples/today/schema/UnionTypeObject.cpp b/samples/today/schema/UnionTypeObject.cpp index 9904e2dc..fe762703 100644 --- a/samples/today/schema/UnionTypeObject.cpp +++ b/samples/today/schema/UnionTypeObject.cpp @@ -3,7 +3,7 @@ // WARNING! Do not edit this file manually, your changes will be overwritten. -#include "UnionTypeObject.h" +#include "TodayUnionTypeObject.h" #include "graphqlservice/internal/Schema.h" diff --git a/samples/today/schema/UnionTypeObject.ixx b/samples/today/schema/UnionTypeObject.ixx new file mode 100644 index 00000000..73496d86 --- /dev/null +++ b/samples/today/schema/UnionTypeObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TodayUnionTypeObject.h" + +export module GraphQL.Today.UnionTypeObject; + +export namespace graphql::today::object { + +using object::UnionType; + +} // namespace graphql::today::object diff --git a/samples/today/schema/today_schema_files b/samples/today/schema/today_schema_files index a9ed9ecf..2e24cdcf 100644 --- a/samples/today/schema/today_schema_files +++ b/samples/today/schema/today_schema_files @@ -1,3 +1,4 @@ +TodaySharedTypes.cpp TodaySchema.cpp NodeObject.cpp UnionTypeObject.cpp diff --git a/samples/validation/CMakeLists.txt b/samples/validation/CMakeLists.txt index 8db4b408..5968f24b 100644 --- a/samples/validation/CMakeLists.txt +++ b/samples/validation/CMakeLists.txt @@ -1,7 +1,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.28) if(GRAPHQL_BUILD_TESTS) add_subdirectory(schema) diff --git a/samples/validation/schema/AlienObject.cpp b/samples/validation/schema/AlienObject.cpp index 0687dff9..b6c0d4be 100644 --- a/samples/validation/schema/AlienObject.cpp +++ b/samples/validation/schema/AlienObject.cpp @@ -11,7 +11,6 @@ #include #include -#include #include #include diff --git a/samples/validation/schema/AlienObject.h b/samples/validation/schema/AlienObject.h index 1b627b50..2b34dc4f 100644 --- a/samples/validation/schema/AlienObject.h +++ b/samples/validation/schema/AlienObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef ALIENOBJECT_H -#define ALIENOBJECT_H +#ifndef VALIDATION_ALIENOBJECT_H +#define VALIDATION_ALIENOBJECT_H #include "ValidationSchema.h" @@ -176,4 +176,4 @@ class [[nodiscard("unnecessary construction")]] Alien final } // namespace graphql::validation::object -#endif // ALIENOBJECT_H +#endif // VALIDATION_ALIENOBJECT_H diff --git a/samples/validation/schema/AlienObject.ixx b/samples/validation/schema/AlienObject.ixx new file mode 100644 index 00000000..54db3153 --- /dev/null +++ b/samples/validation/schema/AlienObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "AlienObject.h" + +export module GraphQL.Validation.AlienObject; + +export namespace graphql::validation::object { + +using object::Alien; + +} // namespace graphql::validation::object diff --git a/samples/validation/schema/ArgumentsObject.cpp b/samples/validation/schema/ArgumentsObject.cpp index 80a06987..0a180be7 100644 --- a/samples/validation/schema/ArgumentsObject.cpp +++ b/samples/validation/schema/ArgumentsObject.cpp @@ -11,7 +11,6 @@ #include #include -#include #include #include diff --git a/samples/validation/schema/ArgumentsObject.h b/samples/validation/schema/ArgumentsObject.h index b0ef4997..9bfeff3b 100644 --- a/samples/validation/schema/ArgumentsObject.h +++ b/samples/validation/schema/ArgumentsObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef ARGUMENTSOBJECT_H -#define ARGUMENTSOBJECT_H +#ifndef VALIDATION_ARGUMENTSOBJECT_H +#define VALIDATION_ARGUMENTSOBJECT_H #include "ValidationSchema.h" @@ -337,4 +337,4 @@ class [[nodiscard("unnecessary construction")]] Arguments final } // namespace graphql::validation::object -#endif // ARGUMENTSOBJECT_H +#endif // VALIDATION_ARGUMENTSOBJECT_H diff --git a/samples/validation/schema/ArgumentsObject.ixx b/samples/validation/schema/ArgumentsObject.ixx new file mode 100644 index 00000000..dc4502c7 --- /dev/null +++ b/samples/validation/schema/ArgumentsObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "ArgumentsObject.h" + +export module GraphQL.Validation.ArgumentsObject; + +export namespace graphql::validation::object { + +using object::Arguments; + +} // namespace graphql::validation::object diff --git a/samples/validation/schema/CMakeLists.txt b/samples/validation/schema/CMakeLists.txt index 106fad47..4d680f6c 100644 --- a/samples/validation/schema/CMakeLists.txt +++ b/samples/validation/schema/CMakeLists.txt @@ -1,7 +1,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.28) # Normally this would be handled by find_package(cppgraphqlgen CONFIG). include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/cppgraphqlgen-functions.cmake) diff --git a/samples/validation/schema/CatObject.cpp b/samples/validation/schema/CatObject.cpp index 35dcb20a..30df47f5 100644 --- a/samples/validation/schema/CatObject.cpp +++ b/samples/validation/schema/CatObject.cpp @@ -11,7 +11,6 @@ #include #include -#include #include #include diff --git a/samples/validation/schema/CatObject.h b/samples/validation/schema/CatObject.h index f9e03753..1dca910a 100644 --- a/samples/validation/schema/CatObject.h +++ b/samples/validation/schema/CatObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef CATOBJECT_H -#define CATOBJECT_H +#ifndef VALIDATION_CATOBJECT_H +#define VALIDATION_CATOBJECT_H #include "ValidationSchema.h" @@ -236,4 +236,4 @@ class [[nodiscard("unnecessary construction")]] Cat final } // namespace graphql::validation::object -#endif // CATOBJECT_H +#endif // VALIDATION_CATOBJECT_H diff --git a/samples/validation/schema/CatObject.ixx b/samples/validation/schema/CatObject.ixx new file mode 100644 index 00000000..74415bca --- /dev/null +++ b/samples/validation/schema/CatObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "CatObject.h" + +export module GraphQL.Validation.CatObject; + +export namespace graphql::validation::object { + +using object::Cat; + +} // namespace graphql::validation::object diff --git a/samples/validation/schema/CatOrDogObject.h b/samples/validation/schema/CatOrDogObject.h index dce444bb..fa0d89e6 100644 --- a/samples/validation/schema/CatOrDogObject.h +++ b/samples/validation/schema/CatOrDogObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef CATORDOGOBJECT_H -#define CATORDOGOBJECT_H +#ifndef VALIDATION_CATORDOGOBJECT_H +#define VALIDATION_CATORDOGOBJECT_H #include "ValidationSchema.h" @@ -78,4 +78,4 @@ class [[nodiscard("unnecessary construction")]] CatOrDog final } // namespace graphql::validation::object -#endif // CATORDOGOBJECT_H +#endif // VALIDATION_CATORDOGOBJECT_H diff --git a/samples/validation/schema/CatOrDogObject.ixx b/samples/validation/schema/CatOrDogObject.ixx new file mode 100644 index 00000000..ed07b094 --- /dev/null +++ b/samples/validation/schema/CatOrDogObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "CatOrDogObject.h" + +export module GraphQL.Validation.CatOrDogObject; + +export namespace graphql::validation::object { + +using object::CatOrDog; + +} // namespace graphql::validation::object diff --git a/samples/validation/schema/DogObject.cpp b/samples/validation/schema/DogObject.cpp index e6785b43..6e5aeb7b 100644 --- a/samples/validation/schema/DogObject.cpp +++ b/samples/validation/schema/DogObject.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include diff --git a/samples/validation/schema/DogObject.h b/samples/validation/schema/DogObject.h index 4b7fc5b8..b31942ec 100644 --- a/samples/validation/schema/DogObject.h +++ b/samples/validation/schema/DogObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef DOGOBJECT_H -#define DOGOBJECT_H +#ifndef VALIDATION_DOGOBJECT_H +#define VALIDATION_DOGOBJECT_H #include "ValidationSchema.h" @@ -297,4 +297,4 @@ class [[nodiscard("unnecessary construction")]] Dog final } // namespace graphql::validation::object -#endif // DOGOBJECT_H +#endif // VALIDATION_DOGOBJECT_H diff --git a/samples/validation/schema/DogObject.ixx b/samples/validation/schema/DogObject.ixx new file mode 100644 index 00000000..14b6f3e8 --- /dev/null +++ b/samples/validation/schema/DogObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "DogObject.h" + +export module GraphQL.Validation.DogObject; + +export namespace graphql::validation::object { + +using object::Dog; + +} // namespace graphql::validation::object diff --git a/samples/validation/schema/DogOrHumanObject.h b/samples/validation/schema/DogOrHumanObject.h index 3f227aff..9bc5b046 100644 --- a/samples/validation/schema/DogOrHumanObject.h +++ b/samples/validation/schema/DogOrHumanObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef DOGORHUMANOBJECT_H -#define DOGORHUMANOBJECT_H +#ifndef VALIDATION_DOGORHUMANOBJECT_H +#define VALIDATION_DOGORHUMANOBJECT_H #include "ValidationSchema.h" @@ -78,4 +78,4 @@ class [[nodiscard("unnecessary construction")]] DogOrHuman final } // namespace graphql::validation::object -#endif // DOGORHUMANOBJECT_H +#endif // VALIDATION_DOGORHUMANOBJECT_H diff --git a/samples/validation/schema/DogOrHumanObject.ixx b/samples/validation/schema/DogOrHumanObject.ixx new file mode 100644 index 00000000..e301fe5b --- /dev/null +++ b/samples/validation/schema/DogOrHumanObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "DogOrHumanObject.h" + +export module GraphQL.Validation.DogOrHumanObject; + +export namespace graphql::validation::object { + +using object::DogOrHuman; + +} // namespace graphql::validation::object diff --git a/samples/validation/schema/HumanObject.cpp b/samples/validation/schema/HumanObject.cpp index 7294ffd2..6b0450a1 100644 --- a/samples/validation/schema/HumanObject.cpp +++ b/samples/validation/schema/HumanObject.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include diff --git a/samples/validation/schema/HumanObject.h b/samples/validation/schema/HumanObject.h index f9d54db5..ebc1ecb4 100644 --- a/samples/validation/schema/HumanObject.h +++ b/samples/validation/schema/HumanObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef HUMANOBJECT_H -#define HUMANOBJECT_H +#ifndef VALIDATION_HUMANOBJECT_H +#define VALIDATION_HUMANOBJECT_H #include "ValidationSchema.h" @@ -177,4 +177,4 @@ class [[nodiscard("unnecessary construction")]] Human final } // namespace graphql::validation::object -#endif // HUMANOBJECT_H +#endif // VALIDATION_HUMANOBJECT_H diff --git a/samples/validation/schema/HumanObject.ixx b/samples/validation/schema/HumanObject.ixx new file mode 100644 index 00000000..cc70f3ce --- /dev/null +++ b/samples/validation/schema/HumanObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "HumanObject.h" + +export module GraphQL.Validation.HumanObject; + +export namespace graphql::validation::object { + +using object::Human; + +} // namespace graphql::validation::object diff --git a/samples/validation/schema/HumanOrAlienObject.h b/samples/validation/schema/HumanOrAlienObject.h index c98320cd..1a77214a 100644 --- a/samples/validation/schema/HumanOrAlienObject.h +++ b/samples/validation/schema/HumanOrAlienObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef HUMANORALIENOBJECT_H -#define HUMANORALIENOBJECT_H +#ifndef VALIDATION_HUMANORALIENOBJECT_H +#define VALIDATION_HUMANORALIENOBJECT_H #include "ValidationSchema.h" @@ -78,4 +78,4 @@ class [[nodiscard("unnecessary construction")]] HumanOrAlien final } // namespace graphql::validation::object -#endif // HUMANORALIENOBJECT_H +#endif // VALIDATION_HUMANORALIENOBJECT_H diff --git a/samples/validation/schema/HumanOrAlienObject.ixx b/samples/validation/schema/HumanOrAlienObject.ixx new file mode 100644 index 00000000..8deb8777 --- /dev/null +++ b/samples/validation/schema/HumanOrAlienObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "HumanOrAlienObject.h" + +export module GraphQL.Validation.HumanOrAlienObject; + +export namespace graphql::validation::object { + +using object::HumanOrAlien; + +} // namespace graphql::validation::object diff --git a/samples/validation/schema/MessageObject.cpp b/samples/validation/schema/MessageObject.cpp index 5897962e..df48b7f7 100644 --- a/samples/validation/schema/MessageObject.cpp +++ b/samples/validation/schema/MessageObject.cpp @@ -11,7 +11,6 @@ #include #include -#include #include #include diff --git a/samples/validation/schema/MessageObject.h b/samples/validation/schema/MessageObject.h index c7ff067b..d01e45ff 100644 --- a/samples/validation/schema/MessageObject.h +++ b/samples/validation/schema/MessageObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef MESSAGEOBJECT_H -#define MESSAGEOBJECT_H +#ifndef VALIDATION_MESSAGEOBJECT_H +#define VALIDATION_MESSAGEOBJECT_H #include "ValidationSchema.h" @@ -157,4 +157,4 @@ class [[nodiscard("unnecessary construction")]] Message final } // namespace graphql::validation::object -#endif // MESSAGEOBJECT_H +#endif // VALIDATION_MESSAGEOBJECT_H diff --git a/samples/validation/schema/MessageObject.ixx b/samples/validation/schema/MessageObject.ixx new file mode 100644 index 00000000..3ee9c892 --- /dev/null +++ b/samples/validation/schema/MessageObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "MessageObject.h" + +export module GraphQL.Validation.MessageObject; + +export namespace graphql::validation::object { + +using object::Message; + +} // namespace graphql::validation::object diff --git a/samples/validation/schema/MutateDogResultObject.cpp b/samples/validation/schema/MutateDogResultObject.cpp index 7ca813a2..ab872c93 100644 --- a/samples/validation/schema/MutateDogResultObject.cpp +++ b/samples/validation/schema/MutateDogResultObject.cpp @@ -11,7 +11,6 @@ #include #include -#include #include #include diff --git a/samples/validation/schema/MutateDogResultObject.h b/samples/validation/schema/MutateDogResultObject.h index e23bc62c..f7163d4b 100644 --- a/samples/validation/schema/MutateDogResultObject.h +++ b/samples/validation/schema/MutateDogResultObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef MUTATEDOGRESULTOBJECT_H -#define MUTATEDOGRESULTOBJECT_H +#ifndef VALIDATION_MUTATEDOGRESULTOBJECT_H +#define VALIDATION_MUTATEDOGRESULTOBJECT_H #include "ValidationSchema.h" @@ -127,4 +127,4 @@ class [[nodiscard("unnecessary construction")]] MutateDogResult final } // namespace graphql::validation::object -#endif // MUTATEDOGRESULTOBJECT_H +#endif // VALIDATION_MUTATEDOGRESULTOBJECT_H diff --git a/samples/validation/schema/MutateDogResultObject.ixx b/samples/validation/schema/MutateDogResultObject.ixx new file mode 100644 index 00000000..fb8915c7 --- /dev/null +++ b/samples/validation/schema/MutateDogResultObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "MutateDogResultObject.h" + +export module GraphQL.Validation.MutateDogResultObject; + +export namespace graphql::validation::object { + +using object::MutateDogResult; + +} // namespace graphql::validation::object diff --git a/samples/validation/schema/MutationObject.cpp b/samples/validation/schema/MutationObject.cpp index 1ec98fb1..6a1fc565 100644 --- a/samples/validation/schema/MutationObject.cpp +++ b/samples/validation/schema/MutationObject.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include diff --git a/samples/validation/schema/MutationObject.h b/samples/validation/schema/MutationObject.h index 99a7e3e7..885e5dfe 100644 --- a/samples/validation/schema/MutationObject.h +++ b/samples/validation/schema/MutationObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef MUTATIONOBJECT_H -#define MUTATIONOBJECT_H +#ifndef VALIDATION_MUTATIONOBJECT_H +#define VALIDATION_MUTATIONOBJECT_H #include "ValidationSchema.h" @@ -127,4 +127,4 @@ class [[nodiscard("unnecessary construction")]] Mutation final } // namespace graphql::validation::object -#endif // MUTATIONOBJECT_H +#endif // VALIDATION_MUTATIONOBJECT_H diff --git a/samples/validation/schema/MutationObject.ixx b/samples/validation/schema/MutationObject.ixx new file mode 100644 index 00000000..3e61de52 --- /dev/null +++ b/samples/validation/schema/MutationObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "MutationObject.h" + +export module GraphQL.Validation.MutationObject; + +export namespace graphql::validation::object { + +using object::Mutation; + +} // namespace graphql::validation::object diff --git a/samples/validation/schema/NodeObject.h b/samples/validation/schema/NodeObject.h index feb5d5f5..228b6c4f 100644 --- a/samples/validation/schema/NodeObject.h +++ b/samples/validation/schema/NodeObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef NODEOBJECT_H -#define NODEOBJECT_H +#ifndef VALIDATION_NODEOBJECT_H +#define VALIDATION_NODEOBJECT_H #include "ValidationSchema.h" @@ -78,4 +78,4 @@ class [[nodiscard("unnecessary construction")]] Node final } // namespace graphql::validation::object -#endif // NODEOBJECT_H +#endif // VALIDATION_NODEOBJECT_H diff --git a/samples/validation/schema/NodeObject.ixx b/samples/validation/schema/NodeObject.ixx new file mode 100644 index 00000000..f3d91fde --- /dev/null +++ b/samples/validation/schema/NodeObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "NodeObject.h" + +export module GraphQL.Validation.NodeObject; + +export namespace graphql::validation::object { + +using object::Node; + +} // namespace graphql::validation::object diff --git a/samples/validation/schema/PetObject.h b/samples/validation/schema/PetObject.h index 0bd8d73c..44371729 100644 --- a/samples/validation/schema/PetObject.h +++ b/samples/validation/schema/PetObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef PETOBJECT_H -#define PETOBJECT_H +#ifndef VALIDATION_PETOBJECT_H +#define VALIDATION_PETOBJECT_H #include "ValidationSchema.h" @@ -78,4 +78,4 @@ class [[nodiscard("unnecessary construction")]] Pet final } // namespace graphql::validation::object -#endif // PETOBJECT_H +#endif // VALIDATION_PETOBJECT_H diff --git a/samples/validation/schema/PetObject.ixx b/samples/validation/schema/PetObject.ixx new file mode 100644 index 00000000..9c77c197 --- /dev/null +++ b/samples/validation/schema/PetObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "PetObject.h" + +export module GraphQL.Validation.PetObject; + +export namespace graphql::validation::object { + +using object::Pet; + +} // namespace graphql::validation::object diff --git a/samples/validation/schema/QueryObject.cpp b/samples/validation/schema/QueryObject.cpp index 2bf27de3..c15a5f0b 100644 --- a/samples/validation/schema/QueryObject.cpp +++ b/samples/validation/schema/QueryObject.cpp @@ -17,7 +17,6 @@ #include #include -#include #include #include diff --git a/samples/validation/schema/QueryObject.h b/samples/validation/schema/QueryObject.h index 709a05bf..50b145f4 100644 --- a/samples/validation/schema/QueryObject.h +++ b/samples/validation/schema/QueryObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef QUERYOBJECT_H -#define QUERYOBJECT_H +#ifndef VALIDATION_QUERYOBJECT_H +#define VALIDATION_QUERYOBJECT_H #include "ValidationSchema.h" @@ -337,4 +337,4 @@ class [[nodiscard("unnecessary construction")]] Query final } // namespace graphql::validation::object -#endif // QUERYOBJECT_H +#endif // VALIDATION_QUERYOBJECT_H diff --git a/samples/validation/schema/QueryObject.ixx b/samples/validation/schema/QueryObject.ixx new file mode 100644 index 00000000..f25b794a --- /dev/null +++ b/samples/validation/schema/QueryObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "QueryObject.h" + +export module GraphQL.Validation.QueryObject; + +export namespace graphql::validation::object { + +using object::Query; + +} // namespace graphql::validation::object diff --git a/samples/validation/schema/ResourceObject.h b/samples/validation/schema/ResourceObject.h index 1a28b4f4..002b5a1c 100644 --- a/samples/validation/schema/ResourceObject.h +++ b/samples/validation/schema/ResourceObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef RESOURCEOBJECT_H -#define RESOURCEOBJECT_H +#ifndef VALIDATION_RESOURCEOBJECT_H +#define VALIDATION_RESOURCEOBJECT_H #include "ValidationSchema.h" @@ -78,4 +78,4 @@ class [[nodiscard("unnecessary construction")]] Resource final } // namespace graphql::validation::object -#endif // RESOURCEOBJECT_H +#endif // VALIDATION_RESOURCEOBJECT_H diff --git a/samples/validation/schema/ResourceObject.ixx b/samples/validation/schema/ResourceObject.ixx new file mode 100644 index 00000000..6fd30498 --- /dev/null +++ b/samples/validation/schema/ResourceObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "ResourceObject.h" + +export module GraphQL.Validation.ResourceObject; + +export namespace graphql::validation::object { + +using object::Resource; + +} // namespace graphql::validation::object diff --git a/samples/validation/schema/SentientObject.h b/samples/validation/schema/SentientObject.h index be4ca01d..9d596491 100644 --- a/samples/validation/schema/SentientObject.h +++ b/samples/validation/schema/SentientObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef SENTIENTOBJECT_H -#define SENTIENTOBJECT_H +#ifndef VALIDATION_SENTIENTOBJECT_H +#define VALIDATION_SENTIENTOBJECT_H #include "ValidationSchema.h" @@ -78,4 +78,4 @@ class [[nodiscard("unnecessary construction")]] Sentient final } // namespace graphql::validation::object -#endif // SENTIENTOBJECT_H +#endif // VALIDATION_SENTIENTOBJECT_H diff --git a/samples/validation/schema/SentientObject.ixx b/samples/validation/schema/SentientObject.ixx new file mode 100644 index 00000000..9793549c --- /dev/null +++ b/samples/validation/schema/SentientObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "SentientObject.h" + +export module GraphQL.Validation.SentientObject; + +export namespace graphql::validation::object { + +using object::Sentient; + +} // namespace graphql::validation::object diff --git a/samples/validation/schema/SubscriptionObject.cpp b/samples/validation/schema/SubscriptionObject.cpp index 694693b8..e0437189 100644 --- a/samples/validation/schema/SubscriptionObject.cpp +++ b/samples/validation/schema/SubscriptionObject.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include diff --git a/samples/validation/schema/SubscriptionObject.h b/samples/validation/schema/SubscriptionObject.h index 59358465..bb7ea834 100644 --- a/samples/validation/schema/SubscriptionObject.h +++ b/samples/validation/schema/SubscriptionObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef SUBSCRIPTIONOBJECT_H -#define SUBSCRIPTIONOBJECT_H +#ifndef VALIDATION_SUBSCRIPTIONOBJECT_H +#define VALIDATION_SUBSCRIPTIONOBJECT_H #include "ValidationSchema.h" @@ -157,4 +157,4 @@ class [[nodiscard("unnecessary construction")]] Subscription final } // namespace graphql::validation::object -#endif // SUBSCRIPTIONOBJECT_H +#endif // VALIDATION_SUBSCRIPTIONOBJECT_H diff --git a/samples/validation/schema/SubscriptionObject.ixx b/samples/validation/schema/SubscriptionObject.ixx new file mode 100644 index 00000000..db16c987 --- /dev/null +++ b/samples/validation/schema/SubscriptionObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "SubscriptionObject.h" + +export module GraphQL.Validation.SubscriptionObject; + +export namespace graphql::validation::object { + +using object::Subscription; + +} // namespace graphql::validation::object diff --git a/samples/validation/schema/ValidationSchema.cpp b/samples/validation/schema/ValidationSchema.cpp index e5d18879..4a99aec5 100644 --- a/samples/validation/schema/ValidationSchema.cpp +++ b/samples/validation/schema/ValidationSchema.cpp @@ -13,8 +13,8 @@ #include #include +#include #include -#include #include #include #include @@ -22,199 +22,7 @@ using namespace std::literals; -namespace graphql { -namespace service { - -static const auto s_namesDogCommand = validation::getDogCommandNames(); -static const auto s_valuesDogCommand = validation::getDogCommandValues(); - -template <> -validation::DogCommand Argument::convert(const response::Value& value) -{ - if (!value.maybe_enum()) - { - throw service::schema_exception { { R"ex(not a valid DogCommand value)ex" } }; - } - - const auto result = internal::sorted_map_lookup( - s_valuesDogCommand, - std::string_view { value.get() }); - - if (!result) - { - throw service::schema_exception { { R"ex(not a valid DogCommand value)ex" } }; - } - - return *result; -} - -template <> -service::AwaitableResolver Result::convert(service::AwaitableScalar result, ResolverParams&& params) -{ - return ModifiedResult::resolve(std::move(result), std::move(params), - [](validation::DogCommand value, const ResolverParams&) - { - const auto idx = static_cast(value); - - if (idx >= s_namesDogCommand.size()) - { - throw service::schema_exception { { R"ex(Enum value out of range for DogCommand)ex" } }; - } - - response::Value resolvedResult(response::Type::EnumValue); - - resolvedResult.set(std::string { s_namesDogCommand[idx] }); - - return resolvedResult; - }); -} - -template <> -void Result::validateScalar(const response::Value& value) -{ - if (!value.maybe_enum()) - { - throw service::schema_exception { { R"ex(not a valid DogCommand value)ex" } }; - } - - const auto [itr, itrEnd] = internal::sorted_map_equal_range( - s_valuesDogCommand.begin(), - s_valuesDogCommand.end(), - std::string_view { value.get() }); - - if (itr == itrEnd) - { - throw service::schema_exception { { R"ex(not a valid DogCommand value)ex" } }; - } -} - -static const auto s_namesCatCommand = validation::getCatCommandNames(); -static const auto s_valuesCatCommand = validation::getCatCommandValues(); - -template <> -validation::CatCommand Argument::convert(const response::Value& value) -{ - if (!value.maybe_enum()) - { - throw service::schema_exception { { R"ex(not a valid CatCommand value)ex" } }; - } - - const auto result = internal::sorted_map_lookup( - s_valuesCatCommand, - std::string_view { value.get() }); - - if (!result) - { - throw service::schema_exception { { R"ex(not a valid CatCommand value)ex" } }; - } - - return *result; -} - -template <> -service::AwaitableResolver Result::convert(service::AwaitableScalar result, ResolverParams&& params) -{ - return ModifiedResult::resolve(std::move(result), std::move(params), - [](validation::CatCommand value, const ResolverParams&) - { - const auto idx = static_cast(value); - - if (idx >= s_namesCatCommand.size()) - { - throw service::schema_exception { { R"ex(Enum value out of range for CatCommand)ex" } }; - } - - response::Value resolvedResult(response::Type::EnumValue); - - resolvedResult.set(std::string { s_namesCatCommand[idx] }); - - return resolvedResult; - }); -} - -template <> -void Result::validateScalar(const response::Value& value) -{ - if (!value.maybe_enum()) - { - throw service::schema_exception { { R"ex(not a valid CatCommand value)ex" } }; - } - - const auto [itr, itrEnd] = internal::sorted_map_equal_range( - s_valuesCatCommand.begin(), - s_valuesCatCommand.end(), - std::string_view { value.get() }); - - if (itr == itrEnd) - { - throw service::schema_exception { { R"ex(not a valid CatCommand value)ex" } }; - } -} - -template <> -validation::ComplexInput Argument::convert(const response::Value& value) -{ - auto valueName = service::ModifiedArgument::require("name", value); - auto valueOwner = service::ModifiedArgument::require("owner", value); - - return validation::ComplexInput { - std::move(valueName), - std::move(valueOwner) - }; -} - -} // namespace service - -namespace validation { - -ComplexInput::ComplexInput() noexcept - : name {} - , owner {} -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -ComplexInput::ComplexInput( - std::optional nameArg, - std::optional ownerArg) noexcept - : name { std::move(nameArg) } - , owner { std::move(ownerArg) } -{ -} - -ComplexInput::ComplexInput(const ComplexInput& other) - : name { service::ModifiedArgument::duplicate(other.name) } - , owner { service::ModifiedArgument::duplicate(other.owner) } -{ -} - -ComplexInput::ComplexInput(ComplexInput&& other) noexcept - : name { std::move(other.name) } - , owner { std::move(other.owner) } -{ -} - -ComplexInput::~ComplexInput() -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} - -ComplexInput& ComplexInput::operator=(const ComplexInput& other) -{ - ComplexInput value { other }; - - std::swap(*this, value); - - return *this; -} - -ComplexInput& ComplexInput::operator=(ComplexInput&& other) noexcept -{ - name = std::move(other.name); - owner = std::move(other.owner); - - return *this; -} +namespace graphql::validation { Operations::Operations(std::shared_ptr query, std::shared_ptr mutation, std::shared_ptr subscription) : service::Request({ @@ -271,13 +79,15 @@ void AddTypesToSchema(const std::shared_ptr& schema) auto typeArguments = schema::ObjectType::Make(R"gql(Arguments)gql"sv, R"md()md"sv); schema->AddType(R"gql(Arguments)gql"sv, typeArguments); + static const auto s_namesDogCommand = getDogCommandNames(); typeDogCommand->AddEnumValues({ - { service::s_namesDogCommand[static_cast(validation::DogCommand::SIT)], R"md()md"sv, std::nullopt }, - { service::s_namesDogCommand[static_cast(validation::DogCommand::DOWN)], R"md()md"sv, std::nullopt }, - { service::s_namesDogCommand[static_cast(validation::DogCommand::HEEL)], R"md()md"sv, std::nullopt } + { s_namesDogCommand[static_cast(validation::DogCommand::SIT)], R"md()md"sv, std::nullopt }, + { s_namesDogCommand[static_cast(validation::DogCommand::DOWN)], R"md()md"sv, std::nullopt }, + { s_namesDogCommand[static_cast(validation::DogCommand::HEEL)], R"md()md"sv, std::nullopt } }); + static const auto s_namesCatCommand = getCatCommandNames(); typeCatCommand->AddEnumValues({ - { service::s_namesCatCommand[static_cast(validation::CatCommand::JUMP)], R"md()md"sv, std::nullopt } + { s_namesCatCommand[static_cast(validation::CatCommand::JUMP)], R"md()md"sv, std::nullopt } }); typeComplexInput->AddInputValues({ @@ -326,5 +136,4 @@ std::shared_ptr GetSchema() return schema; } -} // namespace validation -} // namespace graphql +} // namespace graphql::validation diff --git a/samples/validation/schema/ValidationSchema.h b/samples/validation/schema/ValidationSchema.h index be93c2b8..80d071f7 100644 --- a/samples/validation/schema/ValidationSchema.h +++ b/samples/validation/schema/ValidationSchema.h @@ -8,89 +8,24 @@ #ifndef VALIDATIONSCHEMA_H #define VALIDATIONSCHEMA_H +#include "graphqlservice/GraphQLResponse.h" +#include "graphqlservice/GraphQLService.h" + +#include "graphqlservice/internal/Version.h" #include "graphqlservice/internal/Schema.h" -// Check if the library version is compatible with schemagen 4.5.0 -static_assert(graphql::internal::MajorVersion == 4, "regenerate with schemagen: major version mismatch"); -static_assert(graphql::internal::MinorVersion == 5, "regenerate with schemagen: minor version mismatch"); +#include "ValidationSharedTypes.h" #include #include #include #include -namespace graphql { -namespace validation { - -enum class [[nodiscard("unnecessary conversion")]] DogCommand -{ - SIT, - DOWN, - HEEL -}; - -[[nodiscard("unnecessary call")]] constexpr auto getDogCommandNames() noexcept -{ - using namespace std::literals; - - return std::array { - R"gql(SIT)gql"sv, - R"gql(DOWN)gql"sv, - R"gql(HEEL)gql"sv - }; -} - -[[nodiscard("unnecessary call")]] constexpr auto getDogCommandValues() noexcept -{ - using namespace std::literals; - - return std::array, 3> { - std::make_pair(R"gql(SIT)gql"sv, DogCommand::SIT), - std::make_pair(R"gql(DOWN)gql"sv, DogCommand::DOWN), - std::make_pair(R"gql(HEEL)gql"sv, DogCommand::HEEL) - }; -} - -enum class [[nodiscard("unnecessary conversion")]] CatCommand -{ - JUMP -}; - -[[nodiscard("unnecessary call")]] constexpr auto getCatCommandNames() noexcept -{ - using namespace std::literals; - - return std::array { - R"gql(JUMP)gql"sv - }; -} - -[[nodiscard("unnecessary call")]] constexpr auto getCatCommandValues() noexcept -{ - using namespace std::literals; - - return std::array, 1> { - std::make_pair(R"gql(JUMP)gql"sv, CatCommand::JUMP) - }; -} - -struct [[nodiscard("unnecessary construction")]] ComplexInput -{ - explicit ComplexInput() noexcept; - explicit ComplexInput( - std::optional nameArg, - std::optional ownerArg) noexcept; - ComplexInput(const ComplexInput& other); - ComplexInput(ComplexInput&& other) noexcept; - ~ComplexInput(); - - ComplexInput& operator=(const ComplexInput& other); - ComplexInput& operator=(ComplexInput&& other) noexcept; - - std::optional name; - std::optional owner; -}; +// Check if the library version is compatible with schemagen 5.0.0 +static_assert(graphql::internal::MajorVersion == 5, "regenerate with schemagen: major version mismatch"); +static_assert(graphql::internal::MinorVersion == 0, "regenerate with schemagen: minor version mismatch"); +namespace graphql::validation { namespace object { class Sentient; @@ -159,7 +94,6 @@ void AddArgumentsDetails(const std::shared_ptr& typeArgument std::shared_ptr GetSchema(); -} // namespace validation -} // namespace graphql +} // namespace graphql::validation #endif // VALIDATIONSCHEMA_H diff --git a/samples/validation/schema/ValidationSchema.ixx b/samples/validation/schema/ValidationSchema.ixx new file mode 100644 index 00000000..32a656ad --- /dev/null +++ b/samples/validation/schema/ValidationSchema.ixx @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "ValidationSchema.h" + +export module GraphQL.Validation.ValidationSchema; + +export import GraphQL.Validation.ValidationSharedTypes; + +export import GraphQL.Validation.SentientObject; +export import GraphQL.Validation.PetObject; +export import GraphQL.Validation.NodeObject; +export import GraphQL.Validation.ResourceObject; +export import GraphQL.Validation.CatOrDogObject; +export import GraphQL.Validation.DogOrHumanObject; +export import GraphQL.Validation.HumanOrAlienObject; +export import GraphQL.Validation.QueryObject; +export import GraphQL.Validation.DogObject; +export import GraphQL.Validation.AlienObject; +export import GraphQL.Validation.HumanObject; +export import GraphQL.Validation.CatObject; +export import GraphQL.Validation.MutationObject; +export import GraphQL.Validation.MutateDogResultObject; +export import GraphQL.Validation.SubscriptionObject; +export import GraphQL.Validation.MessageObject; +export import GraphQL.Validation.ArgumentsObject; + +export namespace graphql::validation { + +using validation::Operations; + +using validation::AddSentientDetails; +using validation::AddPetDetails; +using validation::AddNodeDetails; +using validation::AddResourceDetails; +using validation::AddCatOrDogDetails; +using validation::AddDogOrHumanDetails; +using validation::AddHumanOrAlienDetails; +using validation::AddQueryDetails; +using validation::AddDogDetails; +using validation::AddAlienDetails; +using validation::AddHumanDetails; +using validation::AddCatDetails; +using validation::AddMutationDetails; +using validation::AddMutateDogResultDetails; +using validation::AddSubscriptionDetails; +using validation::AddMessageDetails; +using validation::AddArgumentsDetails; + +using validation::GetSchema; + +} // namespace graphql::validation diff --git a/samples/validation/schema/ValidationSharedTypes.cpp b/samples/validation/schema/ValidationSharedTypes.cpp new file mode 100644 index 00000000..aa67bdbb --- /dev/null +++ b/samples/validation/schema/ValidationSharedTypes.cpp @@ -0,0 +1,208 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +#include "graphqlservice/GraphQLService.h" + +#include "ValidationSharedTypes.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std::literals; + +namespace graphql { +namespace service { + +static const auto s_namesDogCommand = validation::getDogCommandNames(); +static const auto s_valuesDogCommand = validation::getDogCommandValues(); + +template <> +validation::DogCommand Argument::convert(const response::Value& value) +{ + if (!value.maybe_enum()) + { + throw service::schema_exception { { R"ex(not a valid DogCommand value)ex" } }; + } + + const auto result = internal::sorted_map_lookup( + s_valuesDogCommand, + std::string_view { value.get() }); + + if (!result) + { + throw service::schema_exception { { R"ex(not a valid DogCommand value)ex" } }; + } + + return *result; +} + +template <> +service::AwaitableResolver Result::convert(service::AwaitableScalar result, ResolverParams&& params) +{ + return ModifiedResult::resolve(std::move(result), std::move(params), + [](validation::DogCommand value, const ResolverParams&) + { + const auto idx = static_cast(value); + + if (idx >= s_namesDogCommand.size()) + { + throw service::schema_exception { { R"ex(Enum value out of range for DogCommand)ex" } }; + } + + return ResolverResult { { response::ValueToken::EnumValue { std::string { s_namesDogCommand[idx] } } } }; + }); +} + +template <> +void Result::validateScalar(const response::Value& value) +{ + if (!value.maybe_enum()) + { + throw service::schema_exception { { R"ex(not a valid DogCommand value)ex" } }; + } + + const auto [itr, itrEnd] = internal::sorted_map_equal_range( + s_valuesDogCommand.begin(), + s_valuesDogCommand.end(), + std::string_view { value.get() }); + + if (itr == itrEnd) + { + throw service::schema_exception { { R"ex(not a valid DogCommand value)ex" } }; + } +} + +static const auto s_namesCatCommand = validation::getCatCommandNames(); +static const auto s_valuesCatCommand = validation::getCatCommandValues(); + +template <> +validation::CatCommand Argument::convert(const response::Value& value) +{ + if (!value.maybe_enum()) + { + throw service::schema_exception { { R"ex(not a valid CatCommand value)ex" } }; + } + + const auto result = internal::sorted_map_lookup( + s_valuesCatCommand, + std::string_view { value.get() }); + + if (!result) + { + throw service::schema_exception { { R"ex(not a valid CatCommand value)ex" } }; + } + + return *result; +} + +template <> +service::AwaitableResolver Result::convert(service::AwaitableScalar result, ResolverParams&& params) +{ + return ModifiedResult::resolve(std::move(result), std::move(params), + [](validation::CatCommand value, const ResolverParams&) + { + const auto idx = static_cast(value); + + if (idx >= s_namesCatCommand.size()) + { + throw service::schema_exception { { R"ex(Enum value out of range for CatCommand)ex" } }; + } + + return ResolverResult { { response::ValueToken::EnumValue { std::string { s_namesCatCommand[idx] } } } }; + }); +} + +template <> +void Result::validateScalar(const response::Value& value) +{ + if (!value.maybe_enum()) + { + throw service::schema_exception { { R"ex(not a valid CatCommand value)ex" } }; + } + + const auto [itr, itrEnd] = internal::sorted_map_equal_range( + s_valuesCatCommand.begin(), + s_valuesCatCommand.end(), + std::string_view { value.get() }); + + if (itr == itrEnd) + { + throw service::schema_exception { { R"ex(not a valid CatCommand value)ex" } }; + } +} + +template <> +validation::ComplexInput Argument::convert(const response::Value& value) +{ + auto valueName = service::ModifiedArgument::require("name", value); + auto valueOwner = service::ModifiedArgument::require("owner", value); + + return validation::ComplexInput { + std::move(valueName), + std::move(valueOwner) + }; +} + +} // namespace service + +namespace validation { + +ComplexInput::ComplexInput() noexcept + : name {} + , owner {} +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +ComplexInput::ComplexInput( + std::optional nameArg, + std::optional ownerArg) noexcept + : name { std::move(nameArg) } + , owner { std::move(ownerArg) } +{ +} + +ComplexInput::ComplexInput(const ComplexInput& other) + : name { service::ModifiedArgument::duplicate(other.name) } + , owner { service::ModifiedArgument::duplicate(other.owner) } +{ +} + +ComplexInput::ComplexInput(ComplexInput&& other) noexcept + : name { std::move(other.name) } + , owner { std::move(other.owner) } +{ +} + +ComplexInput::~ComplexInput() +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +ComplexInput& ComplexInput::operator=(const ComplexInput& other) +{ + ComplexInput value { other }; + + std::swap(*this, value); + + return *this; +} + +ComplexInput& ComplexInput::operator=(ComplexInput&& other) noexcept +{ + name = std::move(other.name); + owner = std::move(other.owner); + + return *this; +} + +} // namespace validation +} // namespace graphql diff --git a/samples/validation/schema/ValidationSharedTypes.h b/samples/validation/schema/ValidationSharedTypes.h new file mode 100644 index 00000000..7b963d0a --- /dev/null +++ b/samples/validation/schema/ValidationSharedTypes.h @@ -0,0 +1,101 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +#pragma once + +#ifndef VALIDATIONSHAREDTYPES_H +#define VALIDATIONSHAREDTYPES_H + +#include "graphqlservice/GraphQLResponse.h" + +#include "graphqlservice/internal/Version.h" + +#include +#include +#include +#include +#include +#include + +// Check if the library version is compatible with schemagen 5.0.0 +static_assert(graphql::internal::MajorVersion == 5, "regenerate with schemagen: major version mismatch"); +static_assert(graphql::internal::MinorVersion == 0, "regenerate with schemagen: minor version mismatch"); + +namespace graphql { +namespace validation { + +enum class [[nodiscard("unnecessary conversion")]] DogCommand +{ + SIT, + DOWN, + HEEL +}; + +[[nodiscard("unnecessary call")]] constexpr auto getDogCommandNames() noexcept +{ + using namespace std::literals; + + return std::array { + R"gql(SIT)gql"sv, + R"gql(DOWN)gql"sv, + R"gql(HEEL)gql"sv + }; +} + +[[nodiscard("unnecessary call")]] constexpr auto getDogCommandValues() noexcept +{ + using namespace std::literals; + + return std::array, 3> { + std::make_pair(R"gql(SIT)gql"sv, DogCommand::SIT), + std::make_pair(R"gql(DOWN)gql"sv, DogCommand::DOWN), + std::make_pair(R"gql(HEEL)gql"sv, DogCommand::HEEL) + }; +} + +enum class [[nodiscard("unnecessary conversion")]] CatCommand +{ + JUMP +}; + +[[nodiscard("unnecessary call")]] constexpr auto getCatCommandNames() noexcept +{ + using namespace std::literals; + + return std::array { + R"gql(JUMP)gql"sv + }; +} + +[[nodiscard("unnecessary call")]] constexpr auto getCatCommandValues() noexcept +{ + using namespace std::literals; + + return std::array, 1> { + std::make_pair(R"gql(JUMP)gql"sv, CatCommand::JUMP) + }; +} + +struct [[nodiscard("unnecessary construction")]] ComplexInput +{ + explicit ComplexInput() noexcept; + explicit ComplexInput( + std::optional nameArg, + std::optional ownerArg) noexcept; + ComplexInput(const ComplexInput& other); + ComplexInput(ComplexInput&& other) noexcept; + ~ComplexInput(); + + ComplexInput& operator=(const ComplexInput& other); + ComplexInput& operator=(ComplexInput&& other) noexcept; + + std::optional name; + std::optional owner; +}; + +} // namespace validation +} // namespace graphql + +#endif // VALIDATIONSHAREDTYPES_H diff --git a/samples/validation/schema/ValidationSharedTypes.ixx b/samples/validation/schema/ValidationSharedTypes.ixx new file mode 100644 index 00000000..9dbcfe15 --- /dev/null +++ b/samples/validation/schema/ValidationSharedTypes.ixx @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "ValidationSharedTypes.h" + +export module GraphQL.Validation.ValidationSharedTypes; + +export namespace graphql::validation { + +using validation::DogCommand; +using validation::getDogCommandNames; +using validation::getDogCommandValues; + +using validation::CatCommand; +using validation::getCatCommandNames; +using validation::getCatCommandValues; + +using validation::ComplexInput; + +} // namespace graphql::validation diff --git a/samples/validation/schema/validation_schema_files b/samples/validation/schema/validation_schema_files index 60750fdd..f091b606 100644 --- a/samples/validation/schema/validation_schema_files +++ b/samples/validation/schema/validation_schema_files @@ -1,3 +1,4 @@ +ValidationSharedTypes.cpp ValidationSchema.cpp SentientObject.cpp PetObject.cpp diff --git a/src/Base64.cpp b/src/Base64.cpp index 94e571f5..b40bc6ee 100644 --- a/src/Base64.cpp +++ b/src/Base64.cpp @@ -4,6 +4,7 @@ #include "graphqlservice/internal/Base64.h" #include +#include #include namespace graphql::internal { @@ -111,7 +112,7 @@ std::string Base64::toBase64(const std::vector& bytes) return result; } - size_t count = bytes.size(); + std::size_t count = bytes.size(); const std::uint8_t* data = bytes.data(); result.reserve((count + (count % 3)) * 4 / 3); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2d763474..8a402246 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,7 +1,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.28) if(GRAPHQL_UPDATE_VERSION) # internal/Version.h @@ -71,60 +71,80 @@ function(add_bigobj_flag target) endif() endfunction() -add_library(graphqlcoro INTERFACE) -target_compile_features(graphqlcoro INTERFACE cxx_std_20) - -function(check_coroutine_impl COROUTINE_HEADER COROUTINE_NAMESPACE OPTIONAL_FLAGS OUT_RESULT) - set(TEST_FILE "test_${OUT_RESULT}.cpp") - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/test_coroutine.cpp.in ${TEST_FILE} @ONLY) - - try_compile(TEST_RESULT - ${CMAKE_CURRENT_BINARY_DIR} - ${CMAKE_CURRENT_BINARY_DIR}/${TEST_FILE} - CXX_STANDARD 20) - - if(NOT TEST_RESULT) - # Retry with each of the optional flags. - foreach(OPTIONAL_FLAG IN LISTS OPTIONAL_FLAGS) - try_compile(TEST_RESULT - ${CMAKE_CURRENT_BINARY_DIR} - ${CMAKE_CURRENT_BINARY_DIR}/${TEST_FILE} - CMAKE_FLAGS "-DCOMPILE_DEFINITIONS:STRING=${OPTIONAL_FLAG}" - CXX_STANDARD 20) - - if(TEST_RESULT) - # Looks like the optional flag was required, go ahead and add it to the compile options. - target_compile_options(graphqlcoro INTERFACE ${OPTIONAL_FLAG}) - break() - endif() - endforeach(OPTIONAL_FLAG) - endif() +file(REAL_PATH ../include/ INCLUDE_ROOT) - set(${OUT_RESULT} ${TEST_RESULT} PARENT_SCOPE) -endfunction() +# graphql_internal_modules +add_library(graphql_internal_modules OBJECT) +add_library(cppgraphqlgen::graphql_internal_modules ALIAS graphql_internal_modules) +set_target_properties(graphql_internal_modules PROPERTIES LINKER_LANGUAGE CXX) +target_compile_features(graphql_internal_modules PUBLIC cxx_std_20) +target_link_libraries(graphql_internal_modules PUBLIC taocpp::pegtl) +target_include_directories(graphql_internal_modules PUBLIC + $ + $) +target_sources(graphql_internal_modules PUBLIC FILE_SET HEADERS + BASE_DIRS + ${INCLUDE_ROOT} + FILES + ${INCLUDE_ROOT}/graphqlservice/internal/Awaitable.h + ${INCLUDE_ROOT}/graphqlservice/internal/Base64.h + ${INCLUDE_ROOT}/graphqlservice/internal/DllExports.h + ${INCLUDE_ROOT}/graphqlservice/internal/Grammar.h + ${INCLUDE_ROOT}/graphqlservice/internal/Introspection.h + ${INCLUDE_ROOT}/graphqlservice/internal/Schema.h + ${INCLUDE_ROOT}/graphqlservice/internal/SortedMap.h + ${INCLUDE_ROOT}/graphqlservice/internal/SyntaxTree.h + ${INCLUDE_ROOT}/graphqlservice/internal/Version.h) +if(GRAPHQL_BUILD_MODULES) + target_sources(graphql_internal_modules PUBLIC FILE_SET CXX_MODULES + BASE_DIRS + ${INCLUDE_ROOT} + FILES + ${INCLUDE_ROOT}/graphqlservice/internal/Awaitable.ixx + ${INCLUDE_ROOT}/graphqlservice/internal/Base64.ixx + ${INCLUDE_ROOT}/graphqlservice/internal/Grammar.ixx + ${INCLUDE_ROOT}/graphqlservice/internal/Introspection.ixx + ${INCLUDE_ROOT}/graphqlservice/internal/Schema.ixx + ${INCLUDE_ROOT}/graphqlservice/internal/SortedMap.ixx + ${INCLUDE_ROOT}/graphqlservice/internal/SyntaxTree.ixx + ${INCLUDE_ROOT}/graphqlservice/internal/Version.ixx) +endif() -check_coroutine_impl("coroutine" "std" "-fcoroutines" STD_COROUTINE) -if(STD_COROUTINE) - message(STATUS "Using std coroutine") -else() - check_coroutine_impl("experimental/coroutine" "std::experimental" "" STD_EXPERIMENTAL_COROUTINE) - if(STD_EXPERIMENTAL_COROUTINE) - message(STATUS "Using std::experimental coroutine") - target_compile_definitions(graphqlcoro INTERFACE USE_STD_EXPERIMENTAL_COROUTINE) - else() - message(FATAL_ERROR "Missing coroutine support") - endif() +# graphql_introspection_modules +add_library(graphql_introspection_modules OBJECT) +add_library(cppgraphqlgen::graphql_introspection_modules ALIAS graphql_introspection_modules) +set_target_properties(graphql_introspection_modules PROPERTIES LINKER_LANGUAGE CXX) +target_compile_features(graphql_introspection_modules PUBLIC cxx_std_20) +target_link_libraries(graphql_introspection_modules PUBLIC taocpp::pegtl) +target_include_directories(graphql_introspection_modules PUBLIC + $ + $) +file(GLOB INTROSPECTION_HEADERS ${INCLUDE_ROOT}/graphqlservice/introspection/*.h) +target_sources(graphql_introspection_modules PUBLIC FILE_SET HEADERS + BASE_DIRS ${INCLUDE_ROOT} + FILES ${INTROSPECTION_HEADERS}) +if(GRAPHQL_BUILD_MODULES) + file(GLOB INTROSPECTION_MODULES ${INCLUDE_ROOT}/graphqlservice/introspection/*.ixx) + target_sources(graphql_introspection_modules PUBLIC FILE_SET CXX_MODULES + BASE_DIRS ${INCLUDE_ROOT} + FILES ${INTROSPECTION_MODULES}) endif() # graphqlpeg add_library(graphqlpeg SyntaxTree.cpp) add_library(cppgraphqlgen::graphqlpeg ALIAS graphqlpeg) -target_link_libraries(graphqlpeg PUBLIC - graphqlcoro - taocpp::pegtl) +target_compile_features(graphqlpeg PUBLIC cxx_std_20) +target_link_libraries(graphqlpeg PUBLIC taocpp::pegtl) +target_sources(graphqlpeg PUBLIC FILE_SET HEADERS + BASE_DIRS ${INCLUDE_ROOT} + FILES ${INCLUDE_ROOT}/graphqlservice/GraphQLParse.h) +if(GRAPHQL_BUILD_MODULES) + target_sources(graphqlpeg PUBLIC FILE_SET CXX_MODULES + BASE_DIRS ${INCLUDE_ROOT} + FILES ${INCLUDE_ROOT}/graphqlservice/Parse.ixx) +endif() target_include_directories(graphqlpeg PUBLIC $ - $ $) add_bigobj_flag(graphqlpeg) @@ -145,10 +165,19 @@ add_library(graphqlresponse Base64.cpp GraphQLResponse.cpp) add_library(cppgraphqlgen::graphqlresponse ALIAS graphqlresponse) +target_compile_features(graphqlresponse PUBLIC cxx_std_20) target_include_directories(graphqlresponse PUBLIC $ $) -target_link_libraries(graphqlresponse PUBLIC graphqlcoro) +target_link_libraries(graphqlresponse PUBLIC graphql_internal_modules) +target_sources(graphqlresponse PUBLIC FILE_SET HEADERS + BASE_DIRS ${INCLUDE_ROOT} + FILES ${INCLUDE_ROOT}/graphqlservice/GraphQLResponse.h) +if(GRAPHQL_BUILD_MODULES) + target_sources(graphqlresponse PUBLIC FILE_SET CXX_MODULES + BASE_DIRS ${INCLUDE_ROOT} + FILES ${INCLUDE_ROOT}/graphqlservice/Response.ixx) +endif() if(GRAPHQL_UPDATE_VERSION) update_version_rc(graphqlresponse) @@ -168,6 +197,7 @@ if(GRAPHQL_BUILD_SCHEMAGEN OR GRAPHQL_BUILD_CLIENTGEN) SchemaLoader.cpp GeneratorLoader.cpp GeneratorUtil.cpp) + target_compile_features(generator_util PUBLIC cxx_std_20) target_link_libraries(generator_util PUBLIC graphqlpeg graphqlresponse) @@ -271,44 +301,9 @@ if(GRAPHQL_BUILD_SCHEMAGEN) RUNTIME DESTINATION ${GRAPHQL_INSTALL_TOOLS_DIR}/${PROJECT_NAME}) endif() -# Common schemagen and clientgen filesystem and Boost dependencies +# Common schemagen and clientgen Boost dependencies if(GRAPHQL_BUILD_SCHEMAGEN OR GRAPHQL_BUILD_CLIENTGEN) - # Try compiling a test program with std::filesystem or one of its alternatives. - function(check_filesystem_impl OPTIONAL_LIBS) - try_compile(TEST_RESULT - ${CMAKE_CURRENT_BINARY_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/test_filesystem.cpp - CXX_STANDARD 20) - - if(NOT TEST_RESULT) - # Retry with each of the optional libraries. - foreach(OPTIONAL_LIB IN LISTS OPTIONAL_LIBS) - try_compile(TEST_RESULT - ${CMAKE_CURRENT_BINARY_DIR} - ${CMAKE_CURRENT_BINARY_DIR}/${TEST_FILE} - LINK_LIBRARIES ${OPTIONAL_LIB} - CXX_STANDARD 20) - - if(TEST_RESULT) - # Looks like the optional library was required, go ahead and add it to the link options. - if(GRAPHQL_BUILD_SCHEMAGEN) - target_link_libraries(schemagen PRIVATE ${OPTIONAL_LIB}) - endif() - if(GRAPHQL_BUILD_CLIENTGEN) - target_link_libraries(clientgen PRIVATE ${OPTIONAL_LIB}) - endif() - break() - endif() - endforeach(OPTIONAL_LIB) - endif() - endfunction(check_filesystem_impl) - - # Try compiling a minimal program without any extra libraries, then with each optional library until it succeeded: - # stdc++fs - # c++fs - check_filesystem_impl("stdc++fs;c++fs" STD_FILESYTEM) - - find_package(Boost QUIET REQUIRED COMPONENTS program_options) + find_package(boost_program_options CONFIG REQUIRED) if(GRAPHQL_BUILD_SCHEMAGEN) target_link_libraries(schemagen PRIVATE Boost::program_options) endif() @@ -329,10 +324,21 @@ add_library(graphqlservice Introspection.cpp ${INTROSPECTION_SCHEMA_FILES}) add_library(cppgraphqlgen::graphqlservice ALIAS graphqlservice) +target_compile_features(graphqlservice PUBLIC cxx_std_20) target_link_libraries(graphqlservice PUBLIC + graphql_internal_modules + graphql_introspection_modules graphqlpeg graphqlresponse Threads::Threads) +target_sources(graphqlservice PUBLIC FILE_SET HEADERS + BASE_DIRS ${INCLUDE_ROOT} + FILES ${INCLUDE_ROOT}/graphqlservice/GraphQLService.h) +if(GRAPHQL_BUILD_MODULES) + target_sources(graphqlservice PUBLIC FILE_SET CXX_MODULES + BASE_DIRS ${INCLUDE_ROOT} + FILES ${INCLUDE_ROOT}/graphqlservice/Service.ixx) +endif() if(GRAPHQL_UPDATE_SAMPLES) add_dependencies(graphqlservice copy_introspection_schema_headers) @@ -357,9 +363,18 @@ endif() # graphqlclient add_library(graphqlclient GraphQLClient.cpp) add_library(cppgraphqlgen::graphqlclient ALIAS graphqlclient) +target_compile_features(graphqlclient PUBLIC cxx_std_20) target_link_libraries(graphqlclient PUBLIC graphqlpeg graphqlresponse) +target_sources(graphqlclient PUBLIC FILE_SET HEADERS + BASE_DIRS ${INCLUDE_ROOT} + FILES ${INCLUDE_ROOT}/graphqlservice/GraphQLClient.h) +if(GRAPHQL_BUILD_MODULES) + target_sources(graphqlclient PUBLIC FILE_SET CXX_MODULES + BASE_DIRS ${INCLUDE_ROOT} + FILES ${INCLUDE_ROOT}/graphqlservice/Client.ixx) +endif() if(GRAPHQL_UPDATE_VERSION) update_version_rc(graphqlclient) @@ -373,20 +388,31 @@ if(WIN32 AND BUILD_SHARED_LIBS) add_version_rc(graphqlclient) endif() -# RapidJSON is the only option for JSON serialization used in this project, but if you want -# to use another JSON library you can implement an alternate version of the functions in -# JSONResponse.cpp to serialize to and from GraphQLResponse and build graphqljson from that. -# You will also need to define how to build the graphqljson library target with your -# implementation, and you should set BUILD_GRAPHQLJSON so that the test dependencies know -# about your version of graphqljson. -if(GRAPHQL_USE_RAPIDJSON) +if(GRAPHQL_USE_TAOCPP_JSON) + find_package(taocpp-json CONFIG REQUIRED) + get_target_property(TAOCPP_JSON_INCLUDE_DIRS taocpp::json INTERFACE_INCLUDE_DIRECTORIES) + set(BUILD_GRAPHQLJSON ON) + add_library(graphqljson TaoCppJSONResponse.cpp) + target_include_directories(graphqljson PRIVATE ${TAOCPP_JSON_INCLUDE_DIRS}) +elseif(GRAPHQL_USE_RAPIDJSON) find_package(RapidJSON CONFIG REQUIRED) - set(BUILD_GRAPHQLJSON ON) - add_library(graphqljson JSONResponse.cpp) + add_library(graphqljson RapidJSONResponse.cpp) + target_include_directories(graphqljson SYSTEM PRIVATE ${RAPIDJSON_INCLUDE_DIRS}) +endif() + +if(BUILD_GRAPHQLJSON) add_library(cppgraphqlgen::graphqljson ALIAS graphqljson) + target_compile_features(graphqljson PUBLIC cxx_std_20) target_link_libraries(graphqljson PUBLIC graphqlresponse) - target_include_directories(graphqljson SYSTEM PRIVATE ${RAPIDJSON_INCLUDE_DIRS}) + target_sources(graphqljson PUBLIC FILE_SET HEADERS + BASE_DIRS ${INCLUDE_ROOT} + FILES ${INCLUDE_ROOT}/graphqlservice/JSONResponse.h) + if(GRAPHQL_BUILD_MODULES) + target_sources(graphqljson PUBLIC FILE_SET CXX_MODULES + BASE_DIRS ${INCLUDE_ROOT} + FILES ${INCLUDE_ROOT}/graphqlservice/JSONResponse.ixx) + endif() if(GRAPHQL_UPDATE_VERSION) update_version_rc(graphqljson) @@ -402,48 +428,48 @@ if(GRAPHQL_USE_RAPIDJSON) endif() install(TARGETS + graphql_internal_modules + graphql_introspection_modules graphqlclient graphqlpeg - graphqlcoro graphqlresponse graphqlservice EXPORT cppgraphqlgen-targets RUNTIME DESTINATION bin ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib) - -install(FILES - ${CMAKE_CURRENT_SOURCE_DIR}/../include/graphqlservice/GraphQLClient.h - ${CMAKE_CURRENT_SOURCE_DIR}/../include/graphqlservice/GraphQLParse.h - ${CMAKE_CURRENT_SOURCE_DIR}/../include/graphqlservice/GraphQLResponse.h - ${CMAKE_CURRENT_SOURCE_DIR}/../include/graphqlservice/GraphQLService.h - CONFIGURATIONS ${GRAPHQL_INSTALL_CONFIGURATIONS} - DESTINATION ${GRAPHQL_INSTALL_INCLUDE_DIR}/graphqlservice) - -install(FILES - ${CMAKE_CURRENT_SOURCE_DIR}/../include/graphqlservice/internal/Awaitable.h - ${CMAKE_CURRENT_SOURCE_DIR}/../include/graphqlservice/internal/Base64.h - ${CMAKE_CURRENT_SOURCE_DIR}/../include/graphqlservice/internal/Grammar.h - ${CMAKE_CURRENT_SOURCE_DIR}/../include/graphqlservice/internal/Introspection.h - ${CMAKE_CURRENT_SOURCE_DIR}/../include/graphqlservice/internal/Schema.h - ${CMAKE_CURRENT_SOURCE_DIR}/../include/graphqlservice/internal/SortedMap.h - ${CMAKE_CURRENT_SOURCE_DIR}/../include/graphqlservice/internal/SyntaxTree.h - ${CMAKE_CURRENT_SOURCE_DIR}/../include/graphqlservice/internal/Version.h - CONFIGURATIONS ${GRAPHQL_INSTALL_CONFIGURATIONS} - DESTINATION ${GRAPHQL_INSTALL_INCLUDE_DIR}/graphqlservice/internal) + LIBRARY DESTINATION lib + FILE_SET HEADERS + CONFIGURATIONS ${GRAPHQL_INSTALL_CONFIGURATIONS} + DESTINATION ${GRAPHQL_INSTALL_INCLUDE_DIR} + FILE_SET CXX_MODULES + CONFIGURATIONS ${GRAPHQL_INSTALL_CONFIGURATIONS} + DESTINATION ${GRAPHQL_INSTALL_INCLUDE_DIR}) + +if(WIN32 AND BUILD_SHARED_LIBS) + install(TARGETS + graphqlpeg_version + graphqlresponse_version + graphqlservice_version + graphqlclient_version + graphqljson_version + EXPORT cppgraphqlgen-targets + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib) +endif() # graphqljson if(BUILD_GRAPHQLJSON) - target_link_libraries(graphqljson PUBLIC graphqlresponse) - install(TARGETS graphqljson EXPORT cppgraphqlgen-targets RUNTIME DESTINATION bin ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib) - install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../include/graphqlservice/JSONResponse.h - CONFIGURATIONS ${GRAPHQL_INSTALL_CONFIGURATIONS} - DESTINATION ${GRAPHQL_INSTALL_INCLUDE_DIR}/graphqlservice) + LIBRARY DESTINATION lib + FILE_SET HEADERS + CONFIGURATIONS ${GRAPHQL_INSTALL_CONFIGURATIONS} + DESTINATION ${GRAPHQL_INSTALL_INCLUDE_DIR} + FILE_SET CXX_MODULES + CONFIGURATIONS ${GRAPHQL_INSTALL_CONFIGURATIONS} + DESTINATION ${GRAPHQL_INSTALL_INCLUDE_DIR}) else() set(GRAPHQL_BUILD_TESTS OFF CACHE BOOL "GRAPHQL_BUILD_TESTS depends on BUILD_GRAPHQLJSON" FORCE) endif() diff --git a/src/ClientGenerator.cpp b/src/ClientGenerator.cpp index 1a747ec5..857e892e 100644 --- a/src/ClientGenerator.cpp +++ b/src/ClientGenerator.cpp @@ -5,7 +5,6 @@ #include "GeneratorUtil.h" #include "graphqlservice/internal/Version.h" - #include "graphqlservice/introspection/IntrospectionSchema.h" #ifdef _MSC_VER @@ -20,8 +19,10 @@ #pragma warning(pop) #endif // _MSC_VER +#include #include #include +#include #include #include #include @@ -40,6 +41,7 @@ Generator::Generator( , _headerDir(getHeaderDir()) , _sourceDir(getSourceDir()) , _headerPath(getHeaderPath()) + , _modulePath(getModulePath()) , _sourcePath(getSourcePath()) { } @@ -77,6 +79,15 @@ std::string Generator::getHeaderPath() const noexcept return fullPath.string(); } +std::string Generator::getModulePath() const noexcept +{ + std::filesystem::path fullPath { _headerDir }; + + fullPath /= (std::string { _schemaLoader.getFilenamePrefix() } + "Client.ixx"); + + return fullPath.string(); +} + std::string Generator::getSourcePath() const noexcept { std::filesystem::path fullPath { _sourceDir }; @@ -88,7 +99,7 @@ std::string Generator::getSourcePath() const noexcept const std::string& Generator::getClientNamespace() const noexcept { - static const auto s_namespace = R"cpp(graphql::client)cpp"s; + static const auto s_namespace = R"cpp(client)cpp"s; return s_namespace; } @@ -103,12 +114,11 @@ const std::string& Generator::getOperationNamespace(const Operation& operation) for (const auto& entry : operations) { - std::ostringstream oss; - - oss << _requestLoader.getOperationType(entry) << R"cpp(::)cpp" - << _requestLoader.getOperationNamespace(entry); + auto value = std::format(R"cpp({}::{})cpp", + _requestLoader.getOperationType(entry), + _requestLoader.getOperationNamespace(entry)); - result.emplace(entry.name, oss.str()); + result.emplace(entry.name, std::move(value)); } return result; @@ -128,15 +138,17 @@ std::string Generator::getResponseFieldCppType( case introspection::TypeKind::INTERFACE: case introspection::TypeKind::UNION: { - std::ostringstream oss; + std::string prefix; if (!currentScope.empty()) { - oss << currentScope << R"cpp(::)cpp"; + prefix = std::format(R"cpp({}::)cpp", currentScope); } - oss << responseField.cppName << R"cpp(_)cpp" << responseField.type->name(); - result = SchemaLoader::getSafeCppName(oss.str()); + result = SchemaLoader::getSafeCppName(std::format(R"cpp({}{}_{})cpp", + prefix, + responseField.cppName, + responseField.type->name())); break; } @@ -156,6 +168,11 @@ std::vector Generator::Build() const noexcept builtFiles.push_back(_headerPath); } + if (outputModule() && _options.verbose) + { + builtFiles.push_back(_modulePath); + } + if (outputSource()) { builtFiles.push_back(_sourcePath); @@ -175,6 +192,20 @@ bool Generator::outputHeader() const noexcept #include "graphqlservice/GraphQLResponse.h" #include "graphqlservice/internal/Version.h" +)cpp"; + + if (_requestLoader.useSharedTypes()) + { + headerFile << R"cpp( +#include ")cpp" << _schemaLoader.getFilenamePrefix() + << R"cpp(SharedTypes.h" +)cpp"; + } + + headerFile << R"cpp( +#include +#include +#include // Check if the library version is compatible with clientgen )cpp" << graphql::internal::MajorVersion << R"cpp(.)cpp" << graphql::internal::MinorVersion @@ -186,153 +217,167 @@ static_assert(graphql::internal::MinorVersion == )cpp" << graphql::internal::MinorVersion << R"cpp(, "regenerate with clientgen: minor version mismatch"); -#include -#include -#include - )cpp"; - PendingBlankLine pendingSeparator { headerFile }; - NamespaceScope clientNamespaceScope { headerFile, getClientNamespace() }; + const auto schemaNamespace = std::format("graphql::{}", _schemaLoader.getSchemaNamespace()); + NamespaceScope schemaNamespaceScope { headerFile, schemaNamespace }; outputRequestComment(headerFile); - NamespaceScope schemaNamespaceScope { headerFile, _schemaLoader.getSchemaNamespace() }; + NamespaceScope clientNamespaceScope { headerFile, getClientNamespace() }; + PendingBlankLine pendingSeparator { headerFile }; outputGetRequestDeclaration(headerFile); const auto& operations = _requestLoader.getOperations(); - std::unordered_set declaredEnum; - for (const auto& operation : operations) + if (!_requestLoader.useSharedTypes()) { - // Define all of the enums referenced either in variables or the response. - for (const auto& enumType : _requestLoader.getReferencedEnums(operation)) - { - const auto cppType = _schemaLoader.getCppType(enumType->name()); + std::unordered_set declaredEnum; - if (!declaredEnum.insert(cppType).second) + for (const auto& operation : operations) + { + // Define all of the enums referenced either in variables or the response. + for (const auto& enumType : _requestLoader.getReferencedEnums(operation)) { - continue; - } + const auto cppType = _schemaLoader.getCppType(enumType->name()); - pendingSeparator.reset(); + if (!declaredEnum.insert(cppType).second) + { + continue; + } + + pendingSeparator.reset(); + + if (clientNamespaceScope.exit()) + { + headerFile << std::endl; + } - headerFile << R"cpp(enum class [[nodiscard("unnecessary conversion")]] )cpp" << cppType - << R"cpp( + headerFile << R"cpp(enum class [[nodiscard("unnecessary conversion")]] )cpp" + << cppType << R"cpp( { )cpp"; - for (const auto& enumValue : enumType->enumValues()) - { - headerFile << R"cpp( )cpp" << SchemaLoader::getSafeCppName(enumValue->name()) - << R"cpp(, + for (const auto& enumValue : enumType->enumValues()) + { + headerFile << R"cpp( )cpp" << SchemaLoader::getSafeCppName(enumValue->name()) + << R"cpp(, )cpp"; - } + } - headerFile << R"cpp(}; + headerFile << R"cpp(}; )cpp"; - pendingSeparator.add(); + pendingSeparator.add(); + } } - } - std::unordered_set declaredInput; - std::unordered_set forwardDeclaredInput; + std::unordered_set declaredInput; + std::unordered_set forwardDeclaredInput; - for (const auto& operation : operations) - { - // Define all of the input object structs referenced in variables. - for (const auto& inputType : _requestLoader.getReferencedInputTypes(operation)) + for (const auto& operation : operations) { - const auto cppType = _schemaLoader.getCppType(inputType.type->name()); - - if (!declaredInput.insert(cppType).second) + // Define all of the input object structs referenced in variables. + for (const auto& inputType : _requestLoader.getReferencedInputTypes(operation)) { - continue; - } + const auto cppType = _schemaLoader.getCppType(inputType.type->name()); - pendingSeparator.reset(); + if (!declaredInput.insert(cppType).second) + { + continue; + } - if (!inputType.declarations.empty()) - { - // Forward declare nullable dependencies - for (auto declaration : inputType.declarations) + pendingSeparator.reset(); + + if (clientNamespaceScope.exit()) + { + headerFile << std::endl; + } + + if (!inputType.declarations.empty()) { - if (declaredInput.find(declaration) == declaredInput.end() - && forwardDeclaredInput.insert(declaration).second) + // Forward declare nullable dependencies + for (auto declaration : inputType.declarations) { - headerFile << R"cpp(struct )cpp" << declaration << R"cpp(; + if (declaredInput.find(declaration) == declaredInput.end() + && forwardDeclaredInput.insert(declaration).second) + { + headerFile << R"cpp(struct )cpp" << declaration << R"cpp(; )cpp"; - pendingSeparator.add(); + pendingSeparator.add(); + } } - } - pendingSeparator.reset(); - } + pendingSeparator.reset(); + } - headerFile << R"cpp(struct [[nodiscard("unnecessary construction")]] )cpp" << cppType - << R"cpp( + headerFile << R"cpp(struct [[nodiscard("unnecessary construction")]] )cpp" + << cppType << R"cpp( { explicit )cpp" << cppType - << R"cpp(()cpp"; + << R"cpp(()cpp"; - bool firstField = true; + bool firstField = true; - for (const auto& inputField : inputType.type->inputFields()) - { - if (firstField) + for (const auto& inputField : inputType.type->inputFields()) { - headerFile << R"cpp() noexcept; + if (firstField) + { + headerFile << R"cpp() noexcept; explicit )cpp" << cppType << R"cpp(()cpp"; - } - else - { - headerFile << R"cpp(,)cpp"; - } + } + else + { + headerFile << R"cpp(,)cpp"; + } - firstField = false; + firstField = false; - const auto inputCppType = _requestLoader.getInputCppType(inputField->type().lock()); + const auto inputCppType = + _requestLoader.getInputCppType(inputField->type().lock()); - headerFile << R"cpp( - )cpp" << inputCppType - << R"cpp( )cpp" << SchemaLoader::getSafeCppName(inputField->name()) - << R"cpp(Arg)cpp"; - } + headerFile << R"cpp( + )cpp" << inputCppType << R"cpp( )cpp" + << SchemaLoader::getSafeCppName(inputField->name()) + << R"cpp(Arg)cpp"; + } - headerFile << R"cpp() noexcept; + headerFile << R"cpp() noexcept; )cpp" << cppType << R"cpp((const )cpp" - << cppType << R"cpp(& other); + << cppType << R"cpp(& other); )cpp" << cppType << R"cpp(()cpp" - << cppType << R"cpp(&& other) noexcept; + << cppType << R"cpp(&& other) noexcept; ~)cpp" << cppType << R"cpp((); )cpp" << cppType << R"cpp(& operator=(const )cpp" - << cppType << R"cpp(& other); + << cppType << R"cpp(& other); )cpp" << cppType << R"cpp(& operator=()cpp" - << cppType << R"cpp(&& other) noexcept; + << cppType << R"cpp(&& other) noexcept; )cpp"; - for (const auto& inputField : inputType.type->inputFields()) - { - headerFile << R"cpp( )cpp" - << _requestLoader.getInputCppType(inputField->type().lock()) - << R"cpp( )cpp" << SchemaLoader::getSafeCppName(inputField->name()) - << R"cpp(; + for (const auto& inputField : inputType.type->inputFields()) + { + headerFile << R"cpp( )cpp" + << _requestLoader.getInputCppType(inputField->type().lock()) + << R"cpp( )cpp" << SchemaLoader::getSafeCppName(inputField->name()) + << R"cpp(; )cpp"; - } + } - headerFile << R"cpp(}; + headerFile << R"cpp(}; )cpp"; - pendingSeparator.add(); + pendingSeparator.add(); + } } } pendingSeparator.reset(); - schemaNamespaceScope.exit(); - pendingSeparator.add(); + if (clientNamespaceScope.enter()) + { + pendingSeparator.add(); + } for (const auto& operation : operations) { @@ -341,9 +386,11 @@ static_assert(graphql::internal::MinorVersion == )cpp" NamespaceScope operationNamespaceScope { headerFile, getOperationNamespace(operation) }; headerFile << R"cpp( -using )cpp" << _schemaLoader.getSchemaNamespace() +using graphql::)cpp" + << _schemaLoader.getSchemaNamespace() << R"cpp(::)cpp" << getClientNamespace() << R"cpp(::GetRequestText; -using )cpp" << _schemaLoader.getSchemaNamespace() +using graphql::)cpp" + << _schemaLoader.getSchemaNamespace() << R"cpp(::)cpp" << getClientNamespace() << R"cpp(::GetRequestObject; )cpp"; @@ -352,8 +399,8 @@ using )cpp" << _schemaLoader.getSchemaNamespace() // Alias all of the enums referenced either in variables or the response. for (const auto& enumType : _requestLoader.getReferencedEnums(operation)) { - headerFile << R"cpp(using )cpp" << _schemaLoader.getSchemaNamespace() << R"cpp(::)cpp" - << _schemaLoader.getCppType(enumType->name()) << R"cpp(; + headerFile << R"cpp(using graphql::)cpp" << _schemaLoader.getSchemaNamespace() + << R"cpp(::)cpp" << _schemaLoader.getCppType(enumType->name()) << R"cpp(; )cpp"; pendingSeparator.add(); @@ -364,8 +411,9 @@ using )cpp" << _schemaLoader.getSchemaNamespace() // Alias all of the input object structs referenced in variables. for (const auto& inputType : _requestLoader.getReferencedInputTypes(operation)) { - headerFile << R"cpp(using )cpp" << _schemaLoader.getSchemaNamespace() << R"cpp(::)cpp" - << _schemaLoader.getCppType(inputType.type->name()) << R"cpp(; + headerFile << R"cpp(using graphql::)cpp" << _schemaLoader.getSchemaNamespace() + << R"cpp(::)cpp" << _schemaLoader.getCppType(inputType.type->name()) + << R"cpp(; )cpp"; pendingSeparator.add(); @@ -430,6 +478,37 @@ using )cpp" << _schemaLoader.getSchemaNamespace() headerFile << R"cpp(}; +class ResponseVisitor + : public std::enable_shared_from_this +{ +public: + ResponseVisitor() noexcept; + ~ResponseVisitor(); + + void add_value(std::shared_ptr&&); + void reserve(std::size_t count); + void start_object(); + void add_member(std::string&& key); + void end_object(); + void start_array(); + void end_array(); + void add_null(); + void add_string(std::string&& value); + void add_enum(std::string&& value); + void add_id(response::IdType&& value); + void add_bool(bool value); + void add_int(int value); + void add_float(double value); + void complete(); + + Response response(); + +private: + struct impl; + + std::unique_ptr _pimpl; +}; + [[nodiscard("unnecessary conversion")]] Response parseResponse(response::Value&& response); struct Traits @@ -452,6 +531,8 @@ struct Traits headerFile << R"cpp( using Response = )cpp" << _requestLoader.getOperationNamespace(operation) << R"cpp(::Response; + using ResponseVisitor = )cpp" + << _requestLoader.getOperationNamespace(operation) << R"cpp(::ResponseVisitor; [[nodiscard("unnecessary conversion")]] static Response parseResponse(response::Value&& response); }; @@ -467,8 +548,7 @@ struct Traits void Generator::outputRequestComment(std::ostream& headerFile) const noexcept { headerFile << R"cpp( -/// -/// Operation)cpp"; +/// # Operation)cpp"; const auto& operations = _requestLoader.getOperations(); @@ -495,8 +575,7 @@ void Generator::outputRequestComment(std::ostream& headerFile) const noexcept } headerFile << R"cpp( -/// -/// +/// ```graphql )cpp"; std::istringstream request { std::string { _requestLoader.getRequestText() } }; @@ -506,7 +585,7 @@ void Generator::outputRequestComment(std::ostream& headerFile) const noexcept headerFile << R"cpp(/// )cpp" << line << std::endl; } - headerFile << R"cpp(/// + headerFile << R"cpp(/// ``` )cpp"; } @@ -531,7 +610,7 @@ void Generator::outputGetOperationNameDeclaration(std::ostream& headerFile) cons } bool Generator::outputResponseFieldType(std::ostream& headerFile, - const ResponseField& responseField, size_t indent /* = 0 */) const noexcept + const ResponseField& responseField, std::size_t indent /* = 0 */) const noexcept { switch (responseField.type->kind()) { @@ -601,203 +680,408 @@ bool Generator::outputResponseFieldType(std::ostream& headerFile, return true; } -bool Generator::outputSource() const noexcept +bool Generator::outputModule() const noexcept { - std::ofstream sourceFile(_sourcePath, std::ios_base::trunc); + std::ofstream moduleFile(_modulePath, std::ios_base::trunc); - sourceFile << R"cpp(// Copyright (c) Microsoft Corporation. All rights reserved. + moduleFile << R"cpp(// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // WARNING! Do not edit this file manually, your changes will be overwritten. +module; + #include ")cpp" << _schemaLoader.getFilenamePrefix() - << R"cpp(Client.h" + << + R"cpp(Client.h" -#include "graphqlservice/internal/SortedMap.h" +export module GraphQL.)cpp" + << _schemaLoader.getFilenamePrefix() << R"cpp(.)cpp" + << _schemaLoader.getFilenamePrefix() << + R"cpp(Client; -#include -#include -#include -#include -#include -#include +export )cpp"; -using namespace std::literals; + const auto schemaNamespace = std::format("graphql::{}", _schemaLoader.getSchemaNamespace()); + NamespaceScope schemaNamespaceScope { moduleFile, schemaNamespace }; -)cpp"; + moduleFile << std::endl; - NamespaceScope clientNamespaceScope { sourceFile, getClientNamespace() }; - NamespaceScope schemaNamespaceScope { sourceFile, _schemaLoader.getSchemaNamespace() }; - PendingBlankLine pendingSeparator { sourceFile }; + NamespaceScope clientNamespaceScope { moduleFile, getClientNamespace() }; + PendingBlankLine pendingSeparator { moduleFile }; - outputGetRequestImplementation(sourceFile); + moduleFile << R"cpp( +using )cpp" << getClientNamespace() + << R"cpp(::GetRequestText; +using )cpp" << getClientNamespace() + << R"cpp(::GetRequestObject; +)cpp"; const auto& operations = _requestLoader.getOperations(); - std::unordered_set outputInputMethods; + std::unordered_set declaredEnum; for (const auto& operation : operations) { - for (const auto& inputType : _requestLoader.getReferencedInputTypes(operation)) + if (!_requestLoader.getReferencedEnums(operation).empty()) { - const auto cppType = _schemaLoader.getCppType(inputType.type->name()); + pendingSeparator.reset(); - if (!outputInputMethods.insert(cppType).second) + if (clientNamespaceScope.exit()) { - continue; + moduleFile << std::endl; } - pendingSeparator.reset(); - - sourceFile << cppType << R"cpp(::)cpp" << cppType << R"cpp(() noexcept)cpp"; + // Define all of the enums referenced either in variables or the response. + for (const auto& enumType : _requestLoader.getReferencedEnums(operation)) + { + const auto cppType = _schemaLoader.getCppType(enumType->name()); - bool firstField = true; + if (!declaredEnum.insert(cppType).second) + { + continue; + } - for (const auto& inputField : inputType.type->inputFields()) - { - sourceFile << R"cpp( - )cpp" << (firstField ? R"cpp(:)cpp" : R"cpp(,)cpp") - << R"cpp( )cpp" << SchemaLoader::getSafeCppName(inputField->name()) - << R"cpp( {})cpp"; - firstField = false; + moduleFile << R"cpp(using )cpp" << _schemaLoader.getSchemaNamespace() + << R"cpp(::)cpp" << cppType << R"cpp(; +)cpp"; } + } + } - sourceFile << R"cpp( -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} + if (!declaredEnum.empty()) + { + pendingSeparator.add(); + } -)cpp" << cppType << R"cpp(::)cpp" - << cppType << R"cpp(()cpp"; + std::unordered_set declaredInput; + std::unordered_set forwardDeclaredInput; - firstField = true; + for (const auto& operation : operations) + { + if (!_requestLoader.getReferencedInputTypes(operation).empty()) + { + pendingSeparator.reset(); - for (const auto& inputField : inputType.type->inputFields()) + if (clientNamespaceScope.exit()) { - if (!firstField) - { - sourceFile << R"cpp(,)cpp"; - } - - firstField = false; - sourceFile << R"cpp( - )cpp" << _requestLoader.getInputCppType(inputField->type().lock()) - << R"cpp( )cpp" << SchemaLoader::getSafeCppName(inputField->name()) - << R"cpp(Arg)cpp"; + moduleFile << std::endl; } - sourceFile << R"cpp() noexcept -)cpp"; - - firstField = true; - - for (const auto& inputField : inputType.type->inputFields()) + // Define all of the input object structs referenced in variables. + for (const auto& inputType : _requestLoader.getReferencedInputTypes(operation)) { - sourceFile << (firstField ? R"cpp( : )cpp" : R"cpp( , )cpp"); - firstField = false; + const auto cppType = _schemaLoader.getCppType(inputType.type->name()); - const auto name = SchemaLoader::getSafeCppName(inputField->name()); + if (!declaredInput.insert(cppType).second) + { + continue; + } - sourceFile << name << R"cpp( { std::move()cpp" << name << R"cpp(Arg) } + moduleFile << R"cpp(using )cpp" << _schemaLoader.getSchemaNamespace() + << R"cpp(::)cpp" << cppType << R"cpp(; )cpp"; } + } + } - sourceFile << R"cpp({ -} + if (!declaredInput.empty()) + { + pendingSeparator.add(); + } -)cpp" << cppType << R"cpp(::)cpp" - << cppType << R"cpp((const )cpp" << cppType << R"cpp(& other) -)cpp"; + pendingSeparator.reset(); + if (clientNamespaceScope.enter()) + { + pendingSeparator.add(); + } - firstField = true; + for (const auto& operation : operations) + { + pendingSeparator.reset(); - for (const auto& inputField : inputType.type->inputFields()) - { - sourceFile << (firstField ? R"cpp( : )cpp" : R"cpp( , )cpp"); - firstField = false; + NamespaceScope operationNamespaceScope { moduleFile, getOperationNamespace(operation) }; + const auto operationNamespace = _requestLoader.getOperationNamespace(operation); - const auto name = SchemaLoader::getSafeCppName(inputField->name()); - const auto [type, modifiers] = - RequestLoader::unwrapSchemaType(inputField->type().lock()); + moduleFile << R"cpp( +using graphql::)cpp" + << _schemaLoader.getSchemaNamespace() << R"cpp(::)cpp" << getClientNamespace() + << R"cpp(::GetRequestText; +using graphql::)cpp" + << _schemaLoader.getSchemaNamespace() << R"cpp(::)cpp" << getClientNamespace() + << R"cpp(::GetRequestObject; +using )cpp" << operationNamespace + << R"cpp(::GetOperationName; - sourceFile << name << R"cpp( { ModifiedVariable<)cpp" - << _schemaLoader.getCppType(type->name()) << R"cpp(>::duplicate)cpp" - << getTypeModifierList(modifiers) << R"cpp((other.)cpp" << name - << R"cpp() } )cpp"; - } - - sourceFile << R"cpp({ -} -)cpp" << cppType << R"cpp(::)cpp" - << cppType << R"cpp(()cpp" << cppType << R"cpp(&& other) noexcept + // Alias all of the enums referenced either in variables or the response. + for (const auto& enumType : _requestLoader.getReferencedEnums(operation)) + { + moduleFile << R"cpp(using graphql::)cpp" << _schemaLoader.getSchemaNamespace() + << R"cpp(::)cpp" << _schemaLoader.getCppType(enumType->name()) << R"cpp(; )cpp"; - firstField = true; - - for (const auto& inputField : inputType.type->inputFields()) - { - sourceFile << (firstField ? R"cpp( : )cpp" : R"cpp( , )cpp"); - firstField = false; + pendingSeparator.add(); + } - const auto name = SchemaLoader::getSafeCppName(inputField->name()); + pendingSeparator.reset(); - sourceFile << name << R"cpp( { std::move(other.)cpp" << name << R"cpp() } + // Alias all of the input object structs referenced in variables. + for (const auto& inputType : _requestLoader.getReferencedInputTypes(operation)) + { + moduleFile << R"cpp(using graphql::)cpp" << _schemaLoader.getSchemaNamespace() + << R"cpp(::)cpp" << _schemaLoader.getCppType(inputType.type->name()) + << R"cpp(; )cpp"; - } - sourceFile << R"cpp({ -} + pendingSeparator.add(); + } -)cpp" << cppType << R"cpp(::~)cpp" - << cppType << R"cpp(() -{ - // Explicit definition to prevent ODR violations when LTO is enabled. -} + pendingSeparator.reset(); -)cpp" << cppType << R"cpp(& )cpp" - << cppType << R"cpp(::operator=(const )cpp" << cppType << R"cpp(& other) -{ - return *this = )cpp" - << cppType << R"cpp( { other }; -} + const auto& variables = _requestLoader.getVariables(operation); -)cpp" << cppType << R"cpp(& )cpp" - << cppType << R"cpp(::operator=()cpp" << cppType << R"cpp(&& other) noexcept -{ + if (!variables.empty()) + { + moduleFile << R"cpp(using )cpp" << operationNamespace << R"cpp(::Variables; +using )cpp" << operationNamespace + << R"cpp(::serializeVariables; )cpp"; - for (const auto& inputField : inputType.type->inputFields()) - { - const auto name = SchemaLoader::getSafeCppName(inputField->name()); + pendingSeparator.add(); + } - sourceFile << R"cpp( )cpp" << name << R"cpp( = std::move(other.)cpp" << name - << R"cpp(); -)cpp"; - } + pendingSeparator.reset(); + + moduleFile << R"cpp(using )cpp" << operationNamespace << R"cpp(::Response; +using )cpp" << operationNamespace + << R"cpp(::ResponseVisitor; +using )cpp" << operationNamespace + << R"cpp(::parseResponse; + +using )cpp" << operationNamespace + << R"cpp(::Traits; - sourceFile << R"cpp( - return *this; -} )cpp"; - pendingSeparator.add(); - } + pendingSeparator.add(); } pendingSeparator.reset(); - schemaNamespaceScope.exit(); - sourceFile << R"cpp( -using namespace )cpp" - << _schemaLoader.getSchemaNamespace() << R"cpp(; -)cpp"; + return true; +} - pendingSeparator.add(); +bool Generator::outputSource() const noexcept +{ + std::ofstream sourceFile(_sourcePath, std::ios_base::trunc); - std::unordered_set outputModifiedVariableEnum; - std::unordered_set outputModifiedVariableInput; - std::unordered_set outputModifiedResponseEnum; + sourceFile << R"cpp(// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +#include ")cpp" << _schemaLoader.getFilenamePrefix() + << R"cpp(Client.h" + +#include "graphqlservice/internal/SortedMap.h" + +#include +#include +#include +#include +#include +#include + +using namespace std::literals; + +)cpp"; + + NamespaceScope graphqlNamespaceScope { sourceFile, "graphql" }; + NamespaceScope schemaNamespaceScope { sourceFile, _schemaLoader.getSchemaNamespace() }; + NamespaceScope clientNamespaceScope { sourceFile, getClientNamespace() }; + PendingBlankLine pendingSeparator { sourceFile }; + + outputGetRequestImplementation(sourceFile); + + const auto& operations = _requestLoader.getOperations(); + + if (!_requestLoader.useSharedTypes()) + { + + std::unordered_set outputInputMethods; + + for (const auto& operation : operations) + { + for (const auto& inputType : _requestLoader.getReferencedInputTypes(operation)) + { + const auto cppType = _schemaLoader.getCppType(inputType.type->name()); + + if (!outputInputMethods.insert(cppType).second) + { + continue; + } + + pendingSeparator.reset(); + + if (clientNamespaceScope.exit()) + { + sourceFile << R"cpp( +using namespace graphql::)cpp" << getClientNamespace() + << R"cpp(; + +)cpp"; + } + + sourceFile << cppType << R"cpp(::)cpp" << cppType << R"cpp(() noexcept)cpp"; + + bool firstField = true; + + for (const auto& inputField : inputType.type->inputFields()) + { + sourceFile << R"cpp( + )cpp" << (firstField ? R"cpp(:)cpp" : R"cpp(,)cpp") + << R"cpp( )cpp" << SchemaLoader::getSafeCppName(inputField->name()) + << R"cpp( {})cpp"; + firstField = false; + } + + sourceFile << R"cpp( +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +)cpp" << cppType << R"cpp(::)cpp" + << cppType << R"cpp(()cpp"; + + firstField = true; + + for (const auto& inputField : inputType.type->inputFields()) + { + if (!firstField) + { + sourceFile << R"cpp(,)cpp"; + } + + firstField = false; + sourceFile << R"cpp( + )cpp" << _requestLoader.getInputCppType(inputField->type().lock()) + << R"cpp( )cpp" << SchemaLoader::getSafeCppName(inputField->name()) + << R"cpp(Arg)cpp"; + } + + sourceFile << R"cpp() noexcept +)cpp"; + + firstField = true; + + for (const auto& inputField : inputType.type->inputFields()) + { + sourceFile << (firstField ? R"cpp( : )cpp" : R"cpp( , )cpp"); + firstField = false; + + const auto name = SchemaLoader::getSafeCppName(inputField->name()); + + sourceFile << name << R"cpp( { std::move()cpp" << name << R"cpp(Arg) } +)cpp"; + } + + sourceFile << R"cpp({ +} + +)cpp" << cppType << R"cpp(::)cpp" + << cppType << R"cpp((const )cpp" << cppType << R"cpp(& other) +)cpp"; + + firstField = true; + + for (const auto& inputField : inputType.type->inputFields()) + { + sourceFile << (firstField ? R"cpp( : )cpp" : R"cpp( , )cpp"); + firstField = false; + + const auto name = SchemaLoader::getSafeCppName(inputField->name()); + const auto [type, modifiers] = + RequestLoader::unwrapSchemaType(inputField->type().lock()); + + sourceFile << name << R"cpp( { ModifiedVariable<)cpp" + << _schemaLoader.getCppType(type->name()) << R"cpp(>::duplicate)cpp" + << getTypeModifierList(modifiers) << R"cpp((other.)cpp" << name + << R"cpp() } +)cpp"; + } + + sourceFile << R"cpp({ +} + +)cpp" << cppType << R"cpp(::)cpp" + << cppType << R"cpp(()cpp" << cppType << R"cpp(&& other) noexcept +)cpp"; + + firstField = true; + + for (const auto& inputField : inputType.type->inputFields()) + { + sourceFile << (firstField ? R"cpp( : )cpp" : R"cpp( , )cpp"); + firstField = false; + + const auto name = SchemaLoader::getSafeCppName(inputField->name()); + + sourceFile << name << R"cpp( { std::move(other.)cpp" << name << R"cpp() } +)cpp"; + } + + sourceFile << R"cpp({ +} + +)cpp" << cppType << R"cpp(::~)cpp" + << cppType << R"cpp(() +{ + // Explicit definition to prevent ODR violations when LTO is enabled. +} + +)cpp" << cppType << R"cpp(& )cpp" + << cppType << R"cpp(::operator=(const )cpp" << cppType << R"cpp(& other) +{ + return *this = )cpp" << cppType + << R"cpp( { other }; +} + +)cpp" << cppType << R"cpp(& )cpp" + << cppType << R"cpp(::operator=()cpp" << cppType + << R"cpp(&& other) noexcept +{ +)cpp"; + + for (const auto& inputField : inputType.type->inputFields()) + { + const auto name = SchemaLoader::getSafeCppName(inputField->name()); + + sourceFile << R"cpp( )cpp" << name << R"cpp( = std::move(other.)cpp" << name + << R"cpp(); +)cpp"; + } + + sourceFile << R"cpp( + return *this; +} +)cpp"; + + pendingSeparator.add(); + } + } + } + + pendingSeparator.reset(); + clientNamespaceScope.exit(); + if (schemaNamespaceScope.exit()) + { + pendingSeparator.add(); + } + + std::unordered_set outputModifiedVariableEnum; + std::unordered_set outputModifiedVariableInput; + std::unordered_set outputModifiedResponseEnum; for (const auto& operation : operations) { @@ -816,6 +1100,15 @@ using namespace )cpp" pendingSeparator.reset(); + if (clientNamespaceScope.enter()) + { + sourceFile << R"cpp( +using namespace )cpp" << _schemaLoader.getSchemaNamespace() + << R"cpp(; + +)cpp"; + } + const auto& enumValues = enumType->enumValues(); sourceFile << R"cpp(template <> @@ -846,7 +1139,7 @@ response::Value Variable<)cpp" response::Value result { response::Type::EnumValue }; - result.set(std::string { s_names[static_cast(value)] }); + result.set(std::string { s_names[static_cast(value)] }); return result; } @@ -865,6 +1158,15 @@ response::Value Variable<)cpp" pendingSeparator.reset(); + if (clientNamespaceScope.enter()) + { + sourceFile << R"cpp( +using namespace )cpp" << _schemaLoader.getSchemaNamespace() + << R"cpp(; + +)cpp"; + } + sourceFile << R"cpp(template <> response::Value Variable<)cpp" << cppType << R"cpp(>::serialize()cpp" << cppType << R"cpp(&& inputValue) @@ -907,37 +1209,33 @@ response::Value Variable<)cpp" pendingSeparator.reset(); - const auto& enumValues = enumType->enumValues(); + if (clientNamespaceScope.enter()) + { + sourceFile << R"cpp( +using namespace )cpp" << _schemaLoader.getSchemaNamespace() + << R"cpp(; - sourceFile << R"cpp(template <> -)cpp" << cppType << R"cpp( Response<)cpp" - << cppType << R"cpp(>::parse(response::Value&& value) -{ - if (!value.maybe_enum()) - { - throw std::logic_error { R"ex(not a valid )cpp" - << enumType->name() << R"cpp( value)ex" }; - } +)cpp"; + } + + const auto& enumValues = enumType->enumValues(); - static const std::array, )cpp" << enumValues.size() - << R"cpp(> s_values = {)cpp"; + sourceFile << R"cpp(static const std::array, )cpp" << enumValues.size() << R"cpp(> s_values)cpp" << cppType + << R"cpp( = {)cpp"; std::vector> sortedValues( enumValues.size()); - std::transform(enumValues.cbegin(), - enumValues.cend(), + std::ranges::transform(enumValues, sortedValues.begin(), [](const auto& value) noexcept { return std::make_pair(value->name(), SchemaLoader::getSafeCppName(value->name())); }); - std::sort(sortedValues.begin(), - sortedValues.end(), - [](const auto& lhs, const auto& rhs) noexcept { - return internal::shorter_or_less {}(lhs.first, rhs.first); - }); + std::ranges::sort(sortedValues, [](const auto& lhs, const auto& rhs) noexcept { + return internal::shorter_or_less {}(lhs.first, rhs.first); + }); bool firstValue = true; @@ -950,17 +1248,28 @@ response::Value Variable<)cpp" firstValue = false; sourceFile << R"cpp( - std::make_pair(R"gql()cpp" + std::make_pair(R"gql()cpp" << enumValue.first << R"cpp()gql"sv, )cpp" << cppType << R"cpp(::)cpp" << enumValue.second << R"cpp())cpp"; pendingSeparator.add(); } pendingSeparator.reset(); - sourceFile << R"cpp( }; + sourceFile << R"cpp(}; + +template <> +)cpp" << cppType << R"cpp( Response<)cpp" + << cppType << R"cpp(>::parse(response::Value&& value) +{ + if (!value.maybe_enum()) + { + throw std::logic_error { R"ex(not a valid )cpp" + << enumType->name() << R"cpp( value)ex" }; + } const auto result = internal::sorted_map_lookup( - s_values, + s_values)cpp" << cppType + << R"cpp(, std::string_view { value.get() }); if (!result) @@ -976,24 +1285,40 @@ response::Value Variable<)cpp" pendingSeparator.add(); } - std::ostringstream oss; - - oss << getOperationNamespace(operation) << R"cpp(::Response)cpp"; - - const auto currentScope = oss.str(); + const auto operationNamespace = std::format("{}::client::{}", + _schemaLoader.getSchemaNamespace(), + getOperationNamespace(operation)); + const auto graphqlCurrentScope = + std::format(R"cpp(graphql::{}::Response)cpp", operationNamespace); const auto& responseType = _requestLoader.getResponseType(operation); for (const auto& responseField : responseType.fields) { - if (outputModifiedResponseImplementation(sourceFile, currentScope, responseField)) + if (clientNamespaceScope.enter()) + { + sourceFile << R"cpp( +using namespace )cpp" << _schemaLoader.getSchemaNamespace() + << R"cpp(; +)cpp"; + } + + if (outputModifiedResponseImplementation(sourceFile, + graphqlCurrentScope, + responseField)) { pendingSeparator.add(); } } pendingSeparator.reset(); + if (clientNamespaceScope.exit()) + { + sourceFile << std::endl; + } - NamespaceScope operationNamespaceScope { sourceFile, getOperationNamespace(operation) }; + NamespaceScope operationNamespaceScope { sourceFile, operationNamespace }; + const auto schemaCurrentScope = + std::format("{}::Response", getOperationNamespace(operation)); outputGetOperationNameImplementation(sourceFile, operation); @@ -1002,6 +1327,9 @@ response::Value Variable<)cpp" sourceFile << R"cpp( response::Value serializeVariables(Variables&& variables) { + using namespace graphql::)cpp" + << getClientNamespace() << R"cpp(; + response::Value result { response::Type::Map }; )cpp"; @@ -1023,147 +1351,1635 @@ response::Value serializeVariables(Variables&& variables) } sourceFile << R"cpp( -Response parseResponse(response::Value&& response) +struct ResponseVisitor::impl { - Response result; - - if (response.type() == response::Type::Map) + enum class VisitorState { - auto members = response.release(); - - for (auto& member : members) - { + Start, )cpp"; - std::unordered_set fieldNames; - for (const auto& responseField : responseType.fields) { - if (fieldNames.emplace(responseField.name).second) - { - sourceFile << R"cpp( if (member.first == R"js()cpp" << responseField.name - << R"cpp()js"sv) - { - result.)cpp" - << responseField.cppName << R"cpp( = ModifiedResponse<)cpp" - << getResponseFieldCppType(responseField, currentScope) - << R"cpp(>::parse)cpp" << getTypeModifierList(responseField.modifiers) - << R"cpp((std::move(member.second)); - continue; - } -)cpp"; - } + outputResponseFieldVisitorStates(sourceFile, responseField); } - sourceFile << R"cpp( } - } + sourceFile << R"cpp( Complete, + }; - return result; -} + VisitorState state { VisitorState::Start }; + Response response {}; +}; -[[nodiscard("unnecessary call")]] const std::string& Traits::GetRequestText() noexcept +ResponseVisitor::ResponseVisitor() noexcept + : _pimpl { std::make_unique() } { - return )cpp" << _schemaLoader.getSchemaNamespace() - << R"cpp(::GetRequestText(); } -[[nodiscard("unnecessary call")]] const peg::ast& Traits::GetRequestObject() noexcept +ResponseVisitor::~ResponseVisitor() { - return )cpp" << _schemaLoader.getSchemaNamespace() - << R"cpp(::GetRequestObject(); } -[[nodiscard("unnecessary call")]] const std::string& Traits::GetOperationName() noexcept +void ResponseVisitor::add_value([[maybe_unused]] std::shared_ptr&& value) { - return )cpp" << _requestLoader.getOperationNamespace(operation) - << R"cpp(::GetOperationName(); -} -)cpp"; + using namespace graphql::client; - if (!variables.empty()) + switch (_pimpl->state) + {)cpp"; + + for (const auto& responseField : responseType.fields) { - sourceFile << R"cpp( -[[nodiscard("unnecessary conversion")]] response::Value Traits::serializeVariables(Traits::Variables&& variables) -{ - return )cpp" << _requestLoader.getOperationNamespace(operation) - << R"cpp(::serializeVariables(std::move(variables)); -} -)cpp"; + outputResponseFieldVisitorAddValue(sourceFile, responseField); } sourceFile << R"cpp( -[[nodiscard("unnecessary conversion")]] Traits::Response Traits::parseResponse(response::Value&& response) -{ - return )cpp" << _requestLoader.getOperationNamespace(operation) - << R"cpp(::parseResponse(std::move(response)); + case impl::VisitorState::Complete: + break; + + default: + break; + } } +void ResponseVisitor::reserve([[maybe_unused]] std::size_t count) +{ + switch (_pimpl->state) + {)cpp"; + + for (const auto& responseField : responseType.fields) + { + outputResponseFieldVisitorReserve(sourceFile, responseField); + } + + sourceFile << R"cpp( + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_object() +{ + switch (_pimpl->state) + {)cpp"; + + for (const auto& responseField : responseType.fields) + { + outputResponseFieldVisitorStartObject(sourceFile, responseField); + } + + sourceFile << R"cpp( + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_member([[maybe_unused]] std::string&& key) +{ + switch (_pimpl->state) + {)cpp"; + + outputResponseFieldVisitorAddMember(sourceFile, responseType.fields); + + sourceFile << R"cpp( + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_object() +{ + switch (_pimpl->state) + {)cpp"; + + for (const auto& responseField : responseType.fields) + { + outputResponseFieldVisitorEndObject(sourceFile, responseField); + } + + sourceFile << R"cpp( + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_array() +{ + switch (_pimpl->state) + {)cpp"; + + for (const auto& responseField : responseType.fields) + { + outputResponseFieldVisitorStartArray(sourceFile, responseField); + } + + sourceFile << R"cpp( + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_array() +{ + switch (_pimpl->state) + {)cpp"; + + for (const auto& responseField : responseType.fields) + { + outputResponseFieldVisitorEndArray(sourceFile, responseField); + } + + sourceFile << R"cpp( + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_null() +{ + switch (_pimpl->state) + {)cpp"; + + for (const auto& responseField : responseType.fields) + { + outputResponseFieldVisitorAddNull(sourceFile, responseField); + } + + sourceFile << R"cpp( + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_string([[maybe_unused]] std::string&& value) +{ + switch (_pimpl->state) + {)cpp"; + + for (const auto& responseField : responseType.fields) + { + outputResponseFieldVisitorAddString(sourceFile, responseField); + } + + sourceFile << R"cpp( + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_enum([[maybe_unused]] std::string&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + {)cpp"; + + for (const auto& responseField : responseType.fields) + { + outputResponseFieldVisitorAddEnum(sourceFile, responseField); + } + + sourceFile << R"cpp( + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_id([[maybe_unused]] response::IdType&& value) +{ + switch (_pimpl->state) + {)cpp"; + + for (const auto& responseField : responseType.fields) + { + outputResponseFieldVisitorAddId(sourceFile, responseField); + } + + sourceFile << R"cpp( + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_bool([[maybe_unused]] bool value) +{ + switch (_pimpl->state) + {)cpp"; + + for (const auto& responseField : responseType.fields) + { + outputResponseFieldVisitorAddBool(sourceFile, responseField); + } + + sourceFile << R"cpp( + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_int([[maybe_unused]] int value) +{ + switch (_pimpl->state) + {)cpp"; + + for (const auto& responseField : responseType.fields) + { + outputResponseFieldVisitorAddInt(sourceFile, responseField); + } + + sourceFile << R"cpp( + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_float([[maybe_unused]] double value) +{ + switch (_pimpl->state) + {)cpp"; + + for (const auto& responseField : responseType.fields) + { + outputResponseFieldVisitorAddFloat(sourceFile, responseField); + } + + sourceFile << R"cpp( + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::complete() +{ + _pimpl->state = impl::VisitorState::Complete; +} + +Response ResponseVisitor::response() +{ + Response response {}; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + _pimpl->state = impl::VisitorState::Start; + std::swap(_pimpl->response, response); + break; + + default: + break; + } + + return response; +} + +Response parseResponse(response::Value&& response) +{ + using namespace graphql::)cpp" + << getClientNamespace() << R"cpp(; + + Response result; + + if (response.type() == response::Type::Map) + { + auto members = response.release(); + + for (auto& member : members) + { +)cpp"; + + std::unordered_set fieldNames; + + for (const auto& responseField : responseType.fields) + { + if (fieldNames.emplace(responseField.name).second) + { + sourceFile << R"cpp( if (member.first == R"js()cpp" << responseField.name + << R"cpp()js"sv) + { + result.)cpp" + << responseField.cppName << R"cpp( = ModifiedResponse<)cpp" + << getResponseFieldCppType(responseField, schemaCurrentScope) + << R"cpp(>::parse)cpp" << getTypeModifierList(responseField.modifiers) + << R"cpp((std::move(member.second)); + continue; + } +)cpp"; + } + } + + sourceFile << R"cpp( } + } + + return result; +} + +[[nodiscard("unnecessary call")]] const std::string& Traits::GetRequestText() noexcept +{ + return )cpp" << getClientNamespace() + << R"cpp(::GetRequestText(); +} + +[[nodiscard("unnecessary call")]] const peg::ast& Traits::GetRequestObject() noexcept +{ + return )cpp" << getClientNamespace() + << R"cpp(::GetRequestObject(); +} + +[[nodiscard("unnecessary call")]] const std::string& Traits::GetOperationName() noexcept +{ + return )cpp" << _requestLoader.getOperationNamespace(operation) + << R"cpp(::GetOperationName(); +} +)cpp"; + + if (!variables.empty()) + { + sourceFile << R"cpp( +[[nodiscard("unnecessary conversion")]] response::Value Traits::serializeVariables(Traits::Variables&& variables) +{ + return )cpp" << _requestLoader.getOperationNamespace(operation) + << R"cpp(::serializeVariables(std::move(variables)); +} +)cpp"; + } + + sourceFile << R"cpp( +[[nodiscard("unnecessary conversion")]] Traits::Response Traits::parseResponse(response::Value&& response) +{ + return )cpp" << _requestLoader.getOperationNamespace(operation) + << R"cpp(::parseResponse(std::move(response)); +} + +)cpp"; + + pendingSeparator.add(); + } + + return true; +} + +void Generator::outputGetRequestImplementation(std::ostream& sourceFile) const noexcept +{ + sourceFile << R"cpp( +const std::string& GetRequestText() noexcept +{ + static const auto s_request = R"gql( +)cpp"; + + std::istringstream request { std::string { _requestLoader.getRequestText() } }; + + for (std::string line; std::getline(request, line);) + { + sourceFile << R"cpp( )cpp" << line << std::endl; + } + + sourceFile << R"cpp( )gql"s; + + return s_request; +} + +const peg::ast& GetRequestObject() noexcept +{ + static const auto s_request = []() noexcept { + auto ast = peg::parseString(GetRequestText()); + + // This has already been validated against the schema by clientgen. + ast.validated = true; + + return ast; + }(); + + return s_request; +} +)cpp"; +} + +void Generator::outputGetOperationNameImplementation( + std::ostream& sourceFile, const Operation& operation) const noexcept +{ + sourceFile << R"cpp( +const std::string& GetOperationName() noexcept +{ + static const auto s_name = R"gql()cpp" + << operation.name << R"cpp()gql"s; + + return s_name; +} +)cpp"; +} + +bool Generator::outputModifiedResponseImplementation(std::ostream& sourceFile, + const std::string& outerScope, const ResponseField& responseField) const noexcept +{ + const auto cppType = + std::format(R"cpp({}::{})cpp", outerScope, getResponseFieldCppType(responseField)); + std::unordered_set fieldNames; + + switch (responseField.type->kind()) + { + case introspection::TypeKind::OBJECT: + case introspection::TypeKind::INTERFACE: + case introspection::TypeKind::UNION: + { + for (const auto& field : responseField.children) + { + if (fieldNames.emplace(field.name).second) + { + outputModifiedResponseImplementation(sourceFile, cppType, field); + } + } + break; + } + + default: + // This is a scalar type, it doesn't require a type declaration. + return false; + } + + fieldNames.clear(); + + // This is a complex type that requires a custom ModifiedResponse implementation. + sourceFile << R"cpp( +template <> +)cpp" << cppType + << R"cpp( Response<)cpp" << cppType << R"cpp(>::parse(response::Value&& response) +{ + )cpp" << cppType + << R"cpp( result; + + if (response.type() == response::Type::Map) + { + auto members = response.release(); + + for (auto& member : members) + { +)cpp"; + + switch (responseField.type->kind()) + { + case introspection::TypeKind::OBJECT: + case introspection::TypeKind::INTERFACE: + case introspection::TypeKind::UNION: + { + for (const auto& field : responseField.children) + { + if (fieldNames.emplace(field.name).second) + { + sourceFile << R"cpp( if (member.first == R"js()cpp" << field.name + << R"cpp()js"sv) + { + result.)cpp" << field.cppName + << R"cpp( = ModifiedResponse<)cpp" + << getResponseFieldCppType(field, cppType) << R"cpp(>::parse)cpp" + << getTypeModifierList(field.modifiers) + << R"cpp((std::move(member.second)); + continue; + } +)cpp"; + } + } + break; + } + + default: + break; + } + + sourceFile << R"cpp( } + } + + return result; +} +)cpp"; + + return true; +} + +std::string Generator::getTypeModifierList(const TypeModifierStack& modifiers) noexcept +{ + if (modifiers.empty()) + { + return {}; + } + + bool firstModifier = true; + std::ostringstream oss; + + oss << '<'; + + for (auto modifier : modifiers) + { + if (!firstModifier) + { + oss << R"cpp(, )cpp"; + } + + firstModifier = false; + + switch (modifier) + { + case service::TypeModifier::None: + oss << R"cpp(TypeModifier::None)cpp"; + break; + + case service::TypeModifier::Nullable: + oss << R"cpp(TypeModifier::Nullable)cpp"; + break; + + case service::TypeModifier::List: + oss << R"cpp(TypeModifier::List)cpp"; + break; + } + } + + oss << '>'; + + return oss.str(); +} + +void Generator::outputResponseFieldVisitorStates(std::ostream& sourceFile, + const ResponseField& responseField, std::string_view parent /* = {} */) const noexcept +{ + auto state = + std::format("{}_{}", parent.empty() ? R"cpp(Member)cpp"sv : parent, responseField.cppName); + + sourceFile << R"cpp( )cpp" << state << R"cpp(, +)cpp"; + + std::size_t arrayDimensions = 0; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + case service::TypeModifier::Nullable: + break; + + case service::TypeModifier::List: + state = std::format("{}_{}", state, arrayDimensions++); + sourceFile << R"cpp( )cpp" << state << R"cpp(, +)cpp"; + break; + } + } + + if (arrayDimensions > 0) + { + sourceFile << R"cpp( )cpp" << state << R"cpp(_, +)cpp"; + } + + std::unordered_set fieldNames; + + switch (responseField.type->kind()) + { + case introspection::TypeKind::OBJECT: + case introspection::TypeKind::INTERFACE: + case introspection::TypeKind::UNION: + { + for (const auto& field : responseField.children) + { + if (fieldNames.emplace(field.name).second) + { + outputResponseFieldVisitorStates(sourceFile, field, state); + } + } + + break; + } + + default: + break; + } +} + +void Generator::outputResponseFieldVisitorAddValue(std::ostream& sourceFile, + const ResponseField& responseField, bool arrayElement /* = false */, + std::string_view parentState /* = {} */, std::string_view parentAccessor /* = {} */, + std::string_view parentCppType /* = {} */) const noexcept +{ + auto state = std::format("{}_{}", + parentState.empty() ? R"cpp(Member)cpp"sv : parentState, + responseField.cppName); + auto accessor = std::format("{}{}", parentAccessor, responseField.cppName); + auto cppType = getResponseFieldCppType(responseField, + parentCppType.empty() ? R"cpp(Response)cpp"sv : parentCppType); + + bool isNullable = false; + std::size_t arrayDimensions = 0; + std::optional lastNullableDimension {}; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + break; + + case service::TypeModifier::Nullable: + isNullable = true; + break; + + case service::TypeModifier::List: + if (isNullable) + { + lastNullableDimension = arrayDimensions; + isNullable = false; + } + + state = std::format("{}_{}", state, arrayDimensions++); + break; + } + } + + sourceFile << R"cpp( + case impl::VisitorState::)cpp" + << state << R"cpp(:)cpp"; + + if (arrayDimensions == 0) + { + sourceFile << R"cpp( + _pimpl->state = impl::VisitorState::)cpp" + << (parentState.empty() ? "Start"sv : parentState); + + if (arrayElement) + { + sourceFile << R"cpp(_)cpp"; + } + + sourceFile << R"cpp(;)cpp"; + } + + sourceFile << R"cpp( + _pimpl->response.)cpp" + << accessor; + + if (arrayDimensions > 0) + { + sourceFile << ((lastNullableDimension && *lastNullableDimension + 1 == arrayDimensions) + ? R"cpp(->)cpp" + : R"cpp(.)cpp") + << R"cpp(push_back()cpp"; + } + else + { + sourceFile << R"cpp( = )cpp"; + } + + sourceFile << R"cpp(ModifiedResponse<)cpp" << cppType << R"cpp(>::parse)cpp"; + + if (arrayDimensions > 0) + { + TypeModifierStack skippedModifiers; + + skippedModifiers.reserve(responseField.modifiers.size() - arrayDimensions); + std::ranges::copy(std::views::all(responseField.modifiers) | std::views::reverse + | std::views::take_while([](auto modifier) noexcept { + return modifier != service::TypeModifier::List; + }) + | std::views::reverse, + std::back_inserter(skippedModifiers)); + sourceFile << getTypeModifierList(skippedModifiers); + } + else + { + sourceFile << getTypeModifierList(responseField.modifiers); + } + + sourceFile << R"cpp((response::Value { *value }))cpp"; + + if (arrayDimensions > 0) + { + sourceFile << R"cpp())cpp"; + } + + sourceFile << R"cpp(; + break; +)cpp"; + + std::unordered_set fieldNames; + + switch (responseField.type->kind()) + { + case introspection::TypeKind::OBJECT: + case introspection::TypeKind::INTERFACE: + case introspection::TypeKind::UNION: + { + bool dereference = true; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + break; + + case service::TypeModifier::Nullable: + accessor.append(R"cpp(->)cpp"); + dereference = false; + break; + + case service::TypeModifier::List: + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + + accessor.append(R"cpp(back())cpp"); + dereference = true; + break; + } + } + + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + + for (const auto& field : responseField.children) + { + if (fieldNames.emplace(field.name).second) + { + outputResponseFieldVisitorAddValue(sourceFile, + field, + arrayDimensions > 0, + state, + accessor, + cppType); + } + } + + break; + } + + default: + break; + } +} + +void Generator::outputResponseFieldVisitorReserve(std::ostream& sourceFile, + const ResponseField& responseField, std::string_view parentState /* = {} */, + std::string_view parentAccessor /* = {} */, + std::string_view parentCppType /* = {} */) const noexcept +{ + auto state = std::format("{}_{}", + parentState.empty() ? R"cpp(Member)cpp"sv : parentState, + responseField.cppName); + auto accessor = std::format("{}{}", parentAccessor, responseField.cppName); + auto cppType = getResponseFieldCppType(responseField, + parentCppType.empty() ? R"cpp(Response)cpp"sv : parentCppType); + + std::size_t arrayDimensions = 0; + bool dereference = true; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + break; + + case service::TypeModifier::Nullable: + accessor.append(R"cpp(->)cpp"); + dereference = false; + break; + + case service::TypeModifier::List: + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + state = std::format("{}_{}", state, arrayDimensions++); + + sourceFile << R"cpp( + case impl::VisitorState::)cpp" + << state << R"cpp(: + _pimpl->response.)cpp" + << accessor << R"cpp(reserve(count); + break; +)cpp"; + + accessor.append(R"cpp(back())cpp"); + dereference = true; + break; + } + } + + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + + std::unordered_set fieldNames; + + switch (responseField.type->kind()) + { + case introspection::TypeKind::OBJECT: + case introspection::TypeKind::INTERFACE: + case introspection::TypeKind::UNION: + { + for (const auto& field : responseField.children) + { + if (fieldNames.emplace(field.name).second) + { + outputResponseFieldVisitorReserve(sourceFile, field, state, accessor, cppType); + } + } + + break; + } + + default: + break; + } +} + +void Generator::outputResponseFieldVisitorStartObject(std::ostream& sourceFile, + const ResponseField& responseField, std::string_view parentState /* = {} */, + std::string_view parentAccessor /* = {} */, + std::string_view parentCppType /* = {} */) const noexcept +{ + if (responseField.type->kind() == introspection::TypeKind::SCALAR + && SchemaLoader::getBuiltinTypes().contains(responseField.type->name())) + { + return; + } + + auto state = std::format("{}_{}", + parentState.empty() ? R"cpp(Member)cpp"sv : parentState, + responseField.cppName); + auto accessor = std::format("{}{}", parentAccessor, responseField.cppName); + auto cppType = getResponseFieldCppType(responseField, + parentCppType.empty() ? R"cpp(Response)cpp"sv : parentCppType); + + bool isNullable = false; + std::size_t arrayDimensions = 0; + std::optional lastNullableDimension {}; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + break; + + case service::TypeModifier::Nullable: + isNullable = true; + break; + + case service::TypeModifier::List: + if (isNullable) + { + lastNullableDimension = arrayDimensions; + isNullable = false; + } + + state = std::format("{}_{}", state, arrayDimensions++); + break; + } + } + + if (isNullable && arrayDimensions == 0) + { + switch (responseField.type->kind()) + { + case introspection::TypeKind::OBJECT: + case introspection::TypeKind::INTERFACE: + case introspection::TypeKind::UNION: + break; + + default: + isNullable = false; + break; + } + } + + if (isNullable || arrayDimensions > 0) + { + sourceFile << R"cpp( + case impl::VisitorState::)cpp" + << state << R"cpp(:)cpp"; + + if (arrayDimensions > 0) + { + sourceFile << R"cpp( + _pimpl->state = impl::VisitorState::)cpp" + << state << R"cpp(_;)cpp"; + } + + sourceFile << R"cpp( + _pimpl->response.)cpp" + << accessor; + + if (arrayDimensions > 0) + { + sourceFile << ((lastNullableDimension && *lastNullableDimension + 1 == arrayDimensions) + ? R"cpp(->)cpp" + : R"cpp(.)cpp") + << R"cpp(push_back()cpp"; + } + else + { + sourceFile << R"cpp( = )cpp"; + } + + if (isNullable) + { + sourceFile << R"cpp(std::make_optional<)cpp" << cppType << R"cpp(>({}))cpp"; + } + else + { + sourceFile << R"cpp({})cpp"; + } + + if (arrayDimensions > 0) + { + sourceFile << R"cpp())cpp"; + } + + sourceFile << R"cpp(; + break; +)cpp"; + } + + std::unordered_set fieldNames; + + switch (responseField.type->kind()) + { + case introspection::TypeKind::OBJECT: + case introspection::TypeKind::INTERFACE: + case introspection::TypeKind::UNION: + { + bool dereference = true; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + break; + + case service::TypeModifier::Nullable: + accessor.append(R"cpp(->)cpp"); + dereference = false; + break; + + case service::TypeModifier::List: + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + + accessor.append(R"cpp(back())cpp"); + dereference = true; + break; + } + } + + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + + for (const auto& field : responseField.children) + { + if (fieldNames.emplace(field.name).second) + { + outputResponseFieldVisitorStartObject(sourceFile, + field, + state, + accessor, + cppType); + } + } + + break; + } + + default: + break; + } +} + +void Generator::outputResponseFieldVisitorAddMember(std::ostream& sourceFile, + const ResponseFieldList& children, bool arrayElement /* = false */, + std::string_view parentState /* = {} */) const noexcept +{ + sourceFile << R"cpp( + case impl::VisitorState::)cpp" + << (parentState.empty() ? R"cpp(Start)cpp"sv : parentState); + + if (arrayElement) + { + sourceFile << R"cpp(_)cpp"; + } + + sourceFile << R"cpp(: + )cpp"; + + std::unordered_set fieldNames; + bool firstField = true; + + for (const auto& field : children) + { + if (!fieldNames.emplace(field.name).second) + { + continue; + } + + auto state = std::format("{}_{}", + parentState.empty() ? R"cpp(Member)cpp"sv : parentState, + field.cppName); + + if (!firstField) + { + sourceFile << R"cpp(else )cpp"; + } + + firstField = false; + + sourceFile << R"cpp(if (key == ")cpp" << field.name << R"cpp("sv) + { + _pimpl->state = impl::VisitorState::)cpp" + << state << R"cpp(; + } + )cpp"; + } + + sourceFile << R"cpp(break; +)cpp"; + + fieldNames.clear(); + + for (const auto& field : children) + { + switch (field.type->kind()) + { + case introspection::TypeKind::OBJECT: + case introspection::TypeKind::INTERFACE: + case introspection::TypeKind::UNION: + break; + + default: + continue; + } + + if (!fieldNames.emplace(field.name).second) + { + continue; + } + + auto state = std::format("{}_{}", + parentState.empty() ? R"cpp(Member)cpp"sv : parentState, + field.cppName); + std::size_t arrayDimensions = 0; + + for (auto modifier : field.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + case service::TypeModifier::Nullable: + break; + + case service::TypeModifier::List: + state = std::format("{}_{}", state, arrayDimensions++); + break; + } + } + + outputResponseFieldVisitorAddMember(sourceFile, field.children, arrayDimensions > 0, state); + } +} + +void Generator::outputResponseFieldVisitorEndObject(std::ostream& sourceFile, + const ResponseField& responseField, bool arrayElement /* = false */, + std::string_view parentState /* = {} */) const noexcept +{ + switch (responseField.type->kind()) + { + case introspection::TypeKind::OBJECT: + case introspection::TypeKind::INTERFACE: + case introspection::TypeKind::UNION: + break; + + default: + return; + } + + auto state = std::format("{}_{}", + parentState.empty() ? R"cpp(Member)cpp"sv : parentState, + responseField.cppName); + + std::size_t arrayDimensions = 0; + std::string arrayState; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + case service::TypeModifier::Nullable: + break; + + case service::TypeModifier::List: + state = std::format("{}_{}", state, arrayDimensions++); + break; + } + } + + std::unordered_set fieldNames; + + for (const auto& field : responseField.children) + { + if (fieldNames.emplace(field.name).second) + { + outputResponseFieldVisitorEndObject(sourceFile, field, arrayDimensions > 0, state); + } + } + + if (arrayDimensions > 0) + { + sourceFile << R"cpp( + case impl::VisitorState::)cpp" + << state << R"cpp(_: + _pimpl->state = impl::VisitorState::)cpp" + << state; + } + else + { + sourceFile << R"cpp( + case impl::VisitorState::)cpp" + << state << R"cpp(: + _pimpl->state = impl::VisitorState::)cpp" + << (parentState.empty() ? "Start"sv : parentState); + + if (arrayElement) + { + sourceFile << R"cpp(_)cpp"; + } + } + + sourceFile << R"cpp(; + break; )cpp"; +} - pendingSeparator.add(); +void Generator::outputResponseFieldVisitorStartArray(std::ostream& sourceFile, + const ResponseField& responseField, std::string_view parentState /* = {} */, + std::string_view parentAccessor /* = {} */, + std::string_view parentCppType /* = {} */) const noexcept +{ + auto state = std::format("{}_{}", + parentState.empty() ? R"cpp(Member)cpp"sv : parentState, + responseField.cppName); + auto accessor = std::format("{}{}", parentAccessor, responseField.cppName); + auto cppType = getResponseFieldCppType(responseField, + parentCppType.empty() ? R"cpp(Response)cpp"sv : parentCppType); + + bool dereference = true; + std::size_t arrayDimensions = 0; + std::size_t skipModifiers = 0; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + break; + + case service::TypeModifier::Nullable: + dereference = false; + break; + + case service::TypeModifier::List: + sourceFile << R"cpp( + case impl::VisitorState::)cpp" + << state << R"cpp(:)cpp"; + + state = std::format("{}_{}", state, arrayDimensions++); + + sourceFile << R"cpp( + _pimpl->state = impl::VisitorState::)cpp" + << state << R"cpp(;)cpp"; + + if (!dereference) + { + sourceFile << R"cpp( + _pimpl->response.)cpp" + << accessor; + + if (arrayDimensions > 1) + { + sourceFile << R"cpp(push_back()cpp"; + } + else + { + sourceFile << R"cpp( = )cpp"; + } + + TypeModifierStack skippedModifiers; + + skippedModifiers.reserve(responseField.modifiers.size() - skipModifiers); + std::ranges::copy( + std::views::all(responseField.modifiers) | std::views::drop(skipModifiers), + std::back_inserter(skippedModifiers)); + + sourceFile << R"cpp(std::make_optional<)cpp" + << RequestLoader::getOutputCppType(cppType, skippedModifiers) + << R"cpp(>({}))cpp"; + + if (arrayDimensions > 1) + { + sourceFile << R"cpp();)cpp"; + } + else + { + sourceFile << R"cpp(;)cpp"; + } + } + + sourceFile << R"cpp( + break; +)cpp"; + + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + else + { + accessor.append(R"cpp(->)cpp"); + } + + accessor.append(R"cpp(back())cpp"); + dereference = true; + break; + } + + ++skipModifiers; } - return true; + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + + std::unordered_set fieldNames; + + switch (responseField.type->kind()) + { + case introspection::TypeKind::OBJECT: + case introspection::TypeKind::INTERFACE: + case introspection::TypeKind::UNION: + { + for (const auto& field : responseField.children) + { + if (fieldNames.emplace(field.name).second) + { + outputResponseFieldVisitorStartArray(sourceFile, + field, + state, + accessor, + cppType); + } + } + + break; + } + + default: + break; + } } -void Generator::outputGetRequestImplementation(std::ostream& sourceFile) const noexcept +void Generator::outputResponseFieldVisitorEndArray(std::ostream& sourceFile, + const ResponseField& responseField, bool arrayElement /* = false */, + std::string_view parentState /* = {} */) const noexcept { - sourceFile << R"cpp( -const std::string& GetRequestText() noexcept + auto state = std::format("{}_{}", + parentState.empty() ? R"cpp(Member)cpp"sv : parentState, + responseField.cppName); + + std::size_t arrayDimensions = 0; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + case service::TypeModifier::Nullable: + break; + + case service::TypeModifier::List: + { + auto child = std::format("{}_{}", state, arrayDimensions++); + std::string_view parent { state }; + + if (arrayDimensions == 1) + { + parent = parentState.empty() ? "Start"sv : parentState; + } + + sourceFile << R"cpp( + case impl::VisitorState::)cpp" + << child << R"cpp(: + _pimpl->state = impl::VisitorState::)cpp" + << parent; + + if (arrayElement) + { + sourceFile << R"cpp(_)cpp"; + } + + sourceFile << R"cpp(; + break; +)cpp"; + + state = std::move(child); + break; + } + } + } + + std::unordered_set fieldNames; + + for (const auto& field : responseField.children) + { + if (fieldNames.emplace(field.name).second) + { + outputResponseFieldVisitorEndArray(sourceFile, field, arrayDimensions > 0, state); + } + } +} + +void Generator::outputResponseFieldVisitorAddNull(std::ostream& sourceFile, + const ResponseField& responseField, bool arrayElement /* = false */, + std::string_view parentState /* = {} */, + std::string_view parentAccessor /* = {} */) const noexcept { - static const auto s_request = R"gql( + auto state = std::format("{}_{}", + parentState.empty() ? R"cpp(Member)cpp"sv : parentState, + responseField.cppName); + auto accessor = std::format("{}{}", parentAccessor, responseField.cppName); + + bool isNullable = false; + std::size_t arrayDimensions = 0; + std::optional lastNullableDimension {}; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + break; + + case service::TypeModifier::Nullable: + isNullable = true; + break; + + case service::TypeModifier::List: + if (isNullable) + { + lastNullableDimension = arrayDimensions; + isNullable = false; + } + + state = std::format("{}_{}", state, arrayDimensions++); + break; + } + } + + if (isNullable) + { + sourceFile << R"cpp( + case impl::VisitorState::)cpp" + << state << R"cpp(:)cpp"; + + if (arrayDimensions == 0) + { + sourceFile << R"cpp( + _pimpl->state = impl::VisitorState::)cpp" + << (parentState.empty() ? "Start"sv : parentState); + + if (arrayElement) + { + sourceFile << R"cpp(_)cpp"; + } + + sourceFile << R"cpp(;)cpp"; + } + + sourceFile << R"cpp( + _pimpl->response.)cpp" + << accessor; + + if (arrayDimensions > 0) + { + sourceFile << ((lastNullableDimension && *lastNullableDimension + 1 == arrayDimensions) + ? R"cpp(->)cpp" + : R"cpp(.)cpp") + << R"cpp(push_back()cpp"; + } + else + { + sourceFile << R"cpp( = )cpp"; + } + + sourceFile << R"cpp(std::nullopt)cpp"; + + if (arrayDimensions > 0) + { + sourceFile << R"cpp())cpp"; + } + + sourceFile << R"cpp(; + break; )cpp"; + } + + std::unordered_set fieldNames; + + switch (responseField.type->kind()) + { + case introspection::TypeKind::OBJECT: + case introspection::TypeKind::INTERFACE: + case introspection::TypeKind::UNION: + { + bool dereference = true; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + break; + + case service::TypeModifier::Nullable: + accessor.append(R"cpp(->)cpp"); + dereference = false; + break; + + case service::TypeModifier::List: + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + + accessor.append(R"cpp(back())cpp"); + dereference = true; + break; + } + } + + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + + for (const auto& field : responseField.children) + { + if (fieldNames.emplace(field.name).second) + { + outputResponseFieldVisitorAddNull(sourceFile, + field, + arrayDimensions > 0, + state, + accessor); + } + } + + break; + } + + default: + break; + } +} + +void Generator::outputResponseFieldVisitorAddMovedValue(std::ostream& sourceFile, + const ResponseField& responseField, std::string_view movedCppType, + bool arrayElement /* = false */, std::string_view parentState /* = {} */, + std::string_view parentAccessor /* = {} */) const noexcept +{ + auto state = std::format("{}_{}", + parentState.empty() ? R"cpp(Member)cpp"sv : parentState, + responseField.cppName); + auto accessor = std::format("{}{}", parentAccessor, responseField.cppName); + + bool isNullable = false; + std::size_t arrayDimensions = 0; + std::optional lastNullableDimension {}; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + break; + + case service::TypeModifier::Nullable: + isNullable = true; + break; + + case service::TypeModifier::List: + if (isNullable) + { + lastNullableDimension = arrayDimensions; + isNullable = false; + } - std::istringstream request { std::string { _requestLoader.getRequestText() } }; + state = std::format("{}_{}", state, arrayDimensions++); + break; + } + } - for (std::string line; std::getline(request, line);) + if (getResponseFieldCppType(responseField) == movedCppType) { - sourceFile << R"cpp( )cpp" << line << std::endl; - } + sourceFile << R"cpp( + case impl::VisitorState::)cpp" + << state << R"cpp(:)cpp"; - sourceFile << R"cpp( )gql"s; + if (arrayDimensions == 0) + { + sourceFile << R"cpp( + _pimpl->state = impl::VisitorState::)cpp" + << (parentState.empty() ? "Start"sv : parentState); - return s_request; -} + if (arrayElement) + { + sourceFile << R"cpp(_)cpp"; + } -const peg::ast& GetRequestObject() noexcept -{ - static const auto s_request = []() noexcept { - auto ast = peg::parseString(GetRequestText()); + sourceFile << R"cpp(;)cpp"; + } - // This has already been validated against the schema by clientgen. - ast.validated = true; + sourceFile << R"cpp( + _pimpl->response.)cpp" + << accessor; - return ast; - }(); + if (arrayDimensions > 0) + { + sourceFile << ((lastNullableDimension && *lastNullableDimension + 1 == arrayDimensions) + ? R"cpp(->)cpp" + : R"cpp(.)cpp") + << R"cpp(push_back()cpp"; + } + else + { + sourceFile << R"cpp( = )cpp"; + } - return s_request; -} -)cpp"; -} + sourceFile << R"cpp(std::move(value))cpp"; -void Generator::outputGetOperationNameImplementation( - std::ostream& sourceFile, const Operation& operation) const noexcept -{ - sourceFile << R"cpp( -const std::string& GetOperationName() noexcept -{ - static const auto s_name = R"gql()cpp" - << operation.name << R"cpp()gql"s; + if (arrayDimensions > 0) + { + sourceFile << R"cpp())cpp"; + } - return s_name; -} + sourceFile << R"cpp(; + break; )cpp"; -} - -bool Generator::outputModifiedResponseImplementation(std::ostream& sourceFile, - const std::string& outerScope, const ResponseField& responseField) const noexcept -{ - std::ostringstream oss; - - oss << outerScope << R"cpp(::)cpp" << getResponseFieldCppType(responseField); + } - const auto cppType = oss.str(); std::unordered_set fieldNames; switch (responseField.type->kind()) @@ -1172,39 +2988,156 @@ bool Generator::outputModifiedResponseImplementation(std::ostream& sourceFile, case introspection::TypeKind::INTERFACE: case introspection::TypeKind::UNION: { + bool dereference = true; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + break; + + case service::TypeModifier::Nullable: + accessor.append(R"cpp(->)cpp"); + dereference = false; + break; + + case service::TypeModifier::List: + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + + accessor.append(R"cpp(back())cpp"); + dereference = true; + break; + } + } + + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + for (const auto& field : responseField.children) { if (fieldNames.emplace(field.name).second) { - outputModifiedResponseImplementation(sourceFile, cppType, field); + outputResponseFieldVisitorAddMovedValue(sourceFile, + field, + movedCppType, + arrayDimensions > 0, + state, + accessor); } } + break; } default: - // This is a scalar type, it doesn't require a type declaration. - return false; + break; } +} - fieldNames.clear(); +void Generator::outputResponseFieldVisitorAddString( + std::ostream& sourceFile, const ResponseField& responseField) const noexcept +{ + outputResponseFieldVisitorAddMovedValue(sourceFile, responseField, R"cpp(std::string)cpp"sv); +} - // This is a complex type that requires a custom ModifiedResponse implementation. - sourceFile << R"cpp( -template <> -)cpp" << cppType - << R"cpp( Response<)cpp" << cppType << R"cpp(>::parse(response::Value&& response) +void Generator::outputResponseFieldVisitorAddEnum(std::ostream& sourceFile, + const ResponseField& responseField, bool arrayElement /* = false */, + std::string_view parentState /* = {} */, std::string_view parentAccessor /* = {} */, + std::string_view parentCppType /* = {} */) const noexcept { - )cpp" << cppType - << R"cpp( result; + auto state = std::format("{}_{}", + parentState.empty() ? R"cpp(Member)cpp"sv : parentState, + responseField.cppName); + auto accessor = std::format("{}{}", parentAccessor, responseField.cppName); + auto cppType = getResponseFieldCppType(responseField, + parentCppType.empty() ? R"cpp(Response)cpp"sv : parentCppType); + + bool isNullable = false; + std::size_t arrayDimensions = 0; + std::optional lastNullableDimension {}; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + break; - if (response.type() == response::Type::Map) + case service::TypeModifier::Nullable: + isNullable = true; + break; + + case service::TypeModifier::List: + if (isNullable) + { + lastNullableDimension = arrayDimensions; + isNullable = false; + } + + state = std::format("{}_{}", state, arrayDimensions++); + break; + } + } + + if (responseField.type->kind() == introspection::TypeKind::ENUM) { - auto members = response.release(); + sourceFile << R"cpp( + case impl::VisitorState::)cpp" + << state << R"cpp(:)cpp"; - for (auto& member : members) + if (arrayDimensions == 0) + { + sourceFile << R"cpp( + _pimpl->state = impl::VisitorState::)cpp" + << (parentState.empty() ? "Start"sv : parentState); + + if (arrayElement) + { + sourceFile << R"cpp(_)cpp"; + } + + sourceFile << R"cpp(;)cpp"; + } + + sourceFile << R"cpp( + if (const auto enumValue = internal::sorted_map_lookup(s_values)cpp" + << cppType << R"cpp(, std::string_view { value })) + { + _pimpl->response.)cpp" + << accessor; + + if (arrayDimensions > 0) + { + sourceFile << ((lastNullableDimension && *lastNullableDimension + 1 == arrayDimensions) + ? R"cpp(->)cpp" + : R"cpp(.)cpp") + << R"cpp(push_back()cpp"; + } + else + { + sourceFile << R"cpp( = )cpp"; + } + + sourceFile << R"cpp(*enumValue)cpp"; + + if (arrayDimensions > 0) { + sourceFile << R"cpp())cpp"; + } + + sourceFile << R"cpp(; + } + break; )cpp"; + } + + std::unordered_set fieldNames; switch (responseField.type->kind()) { @@ -1212,80 +3145,227 @@ template <> case introspection::TypeKind::INTERFACE: case introspection::TypeKind::UNION: { - for (const auto& field : responseField.children) + bool dereference = true; + + for (auto modifier : responseField.modifiers) { - if (fieldNames.emplace(field.name).second) + switch (modifier) { - sourceFile << R"cpp( if (member.first == R"js()cpp" << field.name - << R"cpp()js"sv) + case service::TypeModifier::None: + break; + + case service::TypeModifier::Nullable: + accessor.append(R"cpp(->)cpp"); + dereference = false; + break; + + case service::TypeModifier::List: + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + + accessor.append(R"cpp(back())cpp"); + dereference = true; + break; + } + } + + if (dereference) { - result.)cpp" << field.cppName - << R"cpp( = ModifiedResponse<)cpp" - << getResponseFieldCppType(field, cppType) << R"cpp(>::parse)cpp" - << getTypeModifierList(field.modifiers) - << R"cpp((std::move(member.second)); - continue; + accessor.append(R"cpp(.)cpp"); } -)cpp"; + + for (const auto& field : responseField.children) + { + if (fieldNames.emplace(field.name).second) + { + outputResponseFieldVisitorAddEnum(sourceFile, + field, + arrayDimensions > 0, + state, + accessor, + cppType); } } + break; } default: break; } - - sourceFile << R"cpp( } - } - - return result; } -)cpp"; - return true; +void Generator::outputResponseFieldVisitorAddId( + std::ostream& sourceFile, const ResponseField& responseField) const noexcept +{ + outputResponseFieldVisitorAddMovedValue(sourceFile, + responseField, + R"cpp(response::IdType)cpp"sv); } -std::string Generator::getTypeModifierList(const TypeModifierStack& modifiers) noexcept +void Generator::outputResponseFieldVisitorAddCopiedValue(std::ostream& sourceFile, + const ResponseField& responseField, std::string_view copiedCppType, + bool arrayElement /* = false */, std::string_view parentState /* = {} */, + std::string_view parentAccessor /* = {} */) const noexcept { - if (modifiers.empty()) - { - return {}; - } - - bool firstModifier = true; - std::ostringstream oss; + auto state = std::format("{}_{}", + parentState.empty() ? R"cpp(Member)cpp"sv : parentState, + responseField.cppName); + auto accessor = std::format("{}{}", parentAccessor, responseField.cppName); - oss << '<'; + bool isNullable = false; + std::size_t arrayDimensions = 0; + std::optional lastNullableDimension {}; - for (auto modifier : modifiers) + for (auto modifier : responseField.modifiers) { - if (!firstModifier) - { - oss << R"cpp(, )cpp"; - } - - firstModifier = false; - switch (modifier) { case service::TypeModifier::None: - oss << R"cpp(TypeModifier::None)cpp"; break; case service::TypeModifier::Nullable: - oss << R"cpp(TypeModifier::Nullable)cpp"; + isNullable = true; break; case service::TypeModifier::List: - oss << R"cpp(TypeModifier::List)cpp"; + if (isNullable) + { + lastNullableDimension = arrayDimensions; + isNullable = false; + } + + state = std::format("{}_{}", state, arrayDimensions++); break; } } - oss << '>'; + if (getResponseFieldCppType(responseField) == copiedCppType) + { + sourceFile << R"cpp( + case impl::VisitorState::)cpp" + << state << R"cpp(:)cpp"; - return oss.str(); + if (arrayDimensions == 0) + { + sourceFile << R"cpp( + _pimpl->state = impl::VisitorState::)cpp" + << (parentState.empty() ? "Start"sv : parentState); + + if (arrayElement) + { + sourceFile << R"cpp(_)cpp"; + } + + sourceFile << R"cpp(;)cpp"; + } + + sourceFile << R"cpp( + _pimpl->response.)cpp" + << accessor; + + if (arrayDimensions > 0) + { + sourceFile << ((lastNullableDimension && *lastNullableDimension + 1 == arrayDimensions) + ? R"cpp(->)cpp" + : R"cpp(.)cpp") + << R"cpp(push_back()cpp"; + } + else + { + sourceFile << R"cpp( = )cpp"; + } + + sourceFile << R"cpp(value)cpp"; + + if (arrayDimensions > 0) + { + sourceFile << R"cpp())cpp"; + } + + sourceFile << R"cpp(; + break; +)cpp"; + } + + std::unordered_set fieldNames; + + switch (responseField.type->kind()) + { + case introspection::TypeKind::OBJECT: + case introspection::TypeKind::INTERFACE: + case introspection::TypeKind::UNION: + { + bool dereference = true; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + break; + + case service::TypeModifier::Nullable: + accessor.append(R"cpp(->)cpp"); + dereference = false; + break; + + case service::TypeModifier::List: + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + + accessor.append(R"cpp(back())cpp"); + dereference = true; + break; + } + } + + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + + for (const auto& field : responseField.children) + { + if (fieldNames.emplace(field.name).second) + { + outputResponseFieldVisitorAddCopiedValue(sourceFile, + field, + copiedCppType, + arrayDimensions > 0, + state, + accessor); + } + } + + break; + } + + default: + break; + } +} + +void Generator::outputResponseFieldVisitorAddBool( + std::ostream& sourceFile, const ResponseField& responseField) const noexcept +{ + outputResponseFieldVisitorAddCopiedValue(sourceFile, responseField, R"cpp(bool)cpp"sv); +} + +void Generator::outputResponseFieldVisitorAddInt( + std::ostream& sourceFile, const ResponseField& responseField) const noexcept +{ + outputResponseFieldVisitorAddCopiedValue(sourceFile, responseField, R"cpp(int)cpp"sv); +} + +void Generator::outputResponseFieldVisitorAddFloat( + std::ostream& sourceFile, const ResponseField& responseField) const noexcept +{ + outputResponseFieldVisitorAddCopiedValue(sourceFile, responseField, R"cpp(double)cpp"sv); } } // namespace graphql::generator::client @@ -1315,6 +3395,7 @@ int main(int argc, char** argv) bool buildCustom = false; bool verbose = false; bool noIntrospection = false; + bool sharedTypes = false; std::string schemaFileName; std::string requestFileName; std::string operationName; @@ -1344,7 +3425,9 @@ int main(int argc, char** argv) po::value(&headerDir), "Target path for the Client.h header file")("no-introspection", po::bool_switch(&noIntrospection), - "Do not expect support for Introspection"); + "Do not expect support for Introspection")("shared-types", + po::bool_switch(&sharedTypes), + "Re-use shared types from SharedTypes.h"); positional.add("schema", 1).add("request", 1).add("prefix", 1).add("namespace", 1); try @@ -1406,6 +3489,7 @@ int main(int argc, char** argv) { operationName.empty() ? std::nullopt : std::make_optional(std::move(operationName)) }, noIntrospection, + sharedTypes, }, graphql::generator::client::GeneratorOptions { { std::move(headerDir), std::move(sourceDir) }, @@ -1448,9 +3532,9 @@ int main(int argc, char** argv) for (const auto& segment : error.path) { - if (std::holds_alternative(segment)) + if (std::holds_alternative(segment)) { - std::cerr << '[' << std::get(segment) << ']'; + std::cerr << '[' << std::get(segment) << ']'; } else { diff --git a/src/GeneratorLoader.cpp b/src/GeneratorLoader.cpp index c57ed8fe..38c3c29a 100644 --- a/src/GeneratorLoader.cpp +++ b/src/GeneratorLoader.cpp @@ -6,6 +6,7 @@ #include "graphqlservice/internal/Grammar.h" #include +#include namespace graphql::generator { @@ -96,13 +97,12 @@ void DefaultValueVisitor::visit(const peg::ast_node& value) } else if (value.is_type()) { - std::ostringstream error; const auto position = value.begin(); + const auto error = std::format("Unexpected variable in default value line: {} column: {}", + position.line, + position.column); - error << "Unexpected variable in default value line: " << position.line - << " column: " << position.column; - - throw std::runtime_error(error.str()); + throw std::runtime_error(error); } } diff --git a/src/GeneratorUtil.cpp b/src/GeneratorUtil.cpp index 8e3acaaf..131370c9 100644 --- a/src/GeneratorUtil.cpp +++ b/src/GeneratorUtil.cpp @@ -4,6 +4,7 @@ #include "GeneratorUtil.h" #include +#include namespace graphql::generator { @@ -12,17 +13,14 @@ IncludeGuardScope::IncludeGuardScope( : _outputFile(outputFile) , _includeGuardName(headerFileName.size(), char {}) { - std::transform(headerFileName.begin(), - headerFileName.end(), - _includeGuardName.begin(), - [](char ch) noexcept -> char { - if (ch == '.') - { - return '_'; - } - - return static_cast(std::toupper(ch)); - }); + std::ranges::transform(headerFileName, _includeGuardName.begin(), [](char ch) noexcept -> char { + if (ch == '.') + { + return '_'; + } + + return static_cast(std::toupper(ch)); + }); _outputFile << R"cpp(// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. diff --git a/src/GraphQLClient.cpp b/src/GraphQLClient.cpp index df616f21..b146f018 100644 --- a/src/GraphQLClient.cpp +++ b/src/GraphQLClient.cpp @@ -3,6 +3,8 @@ #include "graphqlservice/GraphQLClient.h" +#include + using namespace std::literals; namespace graphql::client { @@ -21,7 +23,7 @@ ErrorLocation parseServiceErrorLocation(response::Value&& location) { if (member.second.type() == response::Type::Int) { - result.line = static_cast(member.second.get()); + result.line = static_cast(member.second.get()); } continue; @@ -31,7 +33,7 @@ ErrorLocation parseServiceErrorLocation(response::Value&& location) { if (member.second.type() == response::Type::Int) { - result.column = static_cast(member.second.get()); + result.column = static_cast(member.second.get()); } continue; @@ -90,8 +92,7 @@ Error parseServiceError(response::Value&& error) auto locations = member.second.release(); result.locations.reserve(locations.size()); - std::transform(locations.begin(), - locations.end(), + std::ranges::transform(locations, std::back_inserter(result.locations), [](response::Value& location) { return parseServiceErrorLocation(std::move(location)); @@ -108,8 +109,7 @@ Error parseServiceError(response::Value&& error) auto segments = member.second.release(); result.path.reserve(segments.size()); - std::transform(segments.begin(), - segments.end(), + std::ranges::transform(segments, std::back_inserter(result.path), [](response::Value& segment) { return parseServiceErrorPathSegment(std::move(segment)); @@ -148,8 +148,7 @@ ServiceResponse parseServiceResponse(response::Value response) auto errors = member.second.release(); result.errors.reserve(errors.size()); - std::transform(errors.begin(), - errors.end(), + std::ranges::transform(errors, std::back_inserter(result.errors), [](response::Value& error) { return parseServiceError(std::move(error)); diff --git a/src/GraphQLResponse.cpp b/src/GraphQLResponse.cpp index 4036d628..e963ea07 100644 --- a/src/GraphQLResponse.cpp +++ b/src/GraphQLResponse.cpp @@ -6,11 +6,17 @@ #include "graphqlservice/internal/Base64.h" #include +#include +#include #include #include #include +#include #include +#include +#include #include +#include namespace graphql::response { @@ -31,7 +37,7 @@ IdType::~IdType() // omitted, declare it explicitly and define it in graphqlresponse. } -IdType::IdType(size_t count, typename ByteData::value_type value /* = 0 */) +IdType::IdType(std::size_t count, typename ByteData::value_type value /* = 0 */) : _data { ByteData(count, value) } { } @@ -161,7 +167,7 @@ bool IdType::empty() const noexcept _data); } -size_t IdType::size() const noexcept +std::size_t IdType::size() const noexcept { return std::visit( [](const auto& data) noexcept { @@ -170,7 +176,7 @@ size_t IdType::size() const noexcept _data); } -size_t IdType::max_size() const noexcept +std::size_t IdType::max_size() const noexcept { return std::visit( [](const auto& data) noexcept { @@ -179,7 +185,7 @@ size_t IdType::max_size() const noexcept _data); } -void IdType::reserve(size_t new_cap) +void IdType::reserve(std::size_t new_cap) { std::visit( [new_cap](auto& data) { @@ -188,7 +194,7 @@ void IdType::reserve(size_t new_cap) _data); } -size_t IdType::capacity() const noexcept +std::size_t IdType::capacity() const noexcept { return std::visit( [](const auto& data) noexcept { @@ -215,7 +221,7 @@ void IdType::clear() noexcept _data); } -const std::uint8_t& IdType::at(size_t pos) const +const std::uint8_t& IdType::at(std::size_t pos) const { if (!std::holds_alternative(_data)) { @@ -225,7 +231,7 @@ const std::uint8_t& IdType::at(size_t pos) const return std::get(_data).at(pos); } -std::uint8_t& IdType::at(size_t pos) +std::uint8_t& IdType::at(std::size_t pos) { if (!std::holds_alternative(_data)) { @@ -235,7 +241,7 @@ std::uint8_t& IdType::at(size_t pos) return std::get(_data).at(pos); } -const std::uint8_t& IdType::operator[](size_t pos) const +const std::uint8_t& IdType::operator[](std::size_t pos) const { if (!std::holds_alternative(_data)) { @@ -245,7 +251,7 @@ const std::uint8_t& IdType::operator[](size_t pos) const return std::get(_data)[pos]; } -std::uint8_t& IdType::operator[](size_t pos) +std::uint8_t& IdType::operator[](std::size_t pos) { if (!std::holds_alternative(_data)) { @@ -518,6 +524,11 @@ bool Value::NullData::operator==(const NullData&) const bool Value::ScalarData::operator==(const ScalarData& rhs) const { + if (any || rhs.any) + { + return any == rhs.any; + } + if (scalar && rhs.scalar) { return *scalar == *rhs.scalar; @@ -620,7 +631,7 @@ void Value::set(ScalarType&& value) throw std::logic_error("Invalid call to Value::set for ScalarType"); } - _data = { ScalarData { std::make_unique(std::move(value)) } }; + _data = { ScalarData { std::make_unique(std::move(value)), {} } }; } template <> @@ -978,6 +989,11 @@ Value::Value(IdType&& value) { } +Value::Value(AnyScalar&& value) + : _data(TypeData { ScalarData { {}, std::make_shared(std::move(value)) } }) +{ +} + Value::Value(Value&& other) noexcept : _data(std::move(other._data)) { @@ -1003,7 +1019,7 @@ Value::Value(const Value& other) copy.map.push_back({ entry.first, Value { entry.second } }); } - std::map members; + std::map members; for (const auto& entry : copy.map) { @@ -1027,7 +1043,7 @@ Value::Value(const Value& other) ListType copy {}; copy.reserve(other.size()); - for (size_t i = 0; i < other.size(); ++i) + for (std::size_t i = 0; i < other.size(); ++i) { copy.push_back(Value { other[i] }); } @@ -1065,8 +1081,19 @@ Value::Value(const Value& other) break; case Type::Scalar: - _data = { ScalarData { std::make_unique(other.get()) } }; + { + const auto& scalarData = std::get(other._data); + + if (scalarData.any) + { + _data = { ScalarData { {}, scalarData.any } }; + } + else + { + _data = { ScalarData { std::make_unique(other.get()), {} } }; + } break; + } } } @@ -1090,38 +1117,40 @@ Type Value::typeOf(const TypeData& data) noexcept // As long as the order of the variant alternatives matches the Type enum, we can cast the index // to the Type in one step. static_assert( - std::is_same_v(Type::Map), TypeData>, + std::is_same_v(Type::Map), TypeData>, MapData>, "type mistmatch"); static_assert( - std::is_same_v(Type::List), TypeData>, + std::is_same_v(Type::List), TypeData>, ListType>, "type mistmatch"); static_assert( - std::is_same_v(Type::String), TypeData>, + std::is_same_v(Type::String), TypeData>, StringData>, "type mistmatch"); - static_assert( - std::is_same_v(Type::Boolean), TypeData>, - BooleanType>, + static_assert(std::is_same_v< + std::variant_alternative_t(Type::Boolean), TypeData>, + BooleanType>, "type mistmatch"); static_assert( - std::is_same_v(Type::Int), TypeData>, + std::is_same_v(Type::Int), TypeData>, IntType>, "type mistmatch"); static_assert( - std::is_same_v(Type::Float), TypeData>, + std::is_same_v(Type::Float), TypeData>, FloatType>, "type mistmatch"); static_assert( - std::is_same_v(Type::EnumValue), TypeData>, + std::is_same_v< + std::variant_alternative_t(Type::EnumValue), TypeData>, EnumData>, "type mistmatch"); static_assert( - std::is_same_v(Type::ID), TypeData>, IdType>, + std::is_same_v(Type::ID), TypeData>, + IdType>, "type mistmatch"); static_assert( - std::is_same_v(Type::Scalar), TypeData>, + std::is_same_v(Type::Scalar), TypeData>, ScalarData>, "type mistmatch"); @@ -1223,6 +1252,40 @@ Type Value::type() const noexcept return typeOf(_data); } +bool Value::isAny() const noexcept +{ + const auto& typeData = data(); + + if (!std::holds_alternative(typeData)) + { + return false; + } + + return static_cast(std::get(typeData).any); +} + +SharedAnyScalar Value::releaseAny() +{ + if (std::holds_alternative(_data)) + { + *this = Value { *std::get(_data) }; + } + + if (!std::holds_alternative(_data)) + { + throw std::logic_error("Invalid call to Value::releaseAny"); + } + + auto any = std::move(std::get(_data).any); + + if (!any) + { + throw std::logic_error("Invalid call to Value::releaseAny"); + } + + return any; +} + Value&& Value::from_json() noexcept { if (std::holds_alternative(_data)) @@ -1270,7 +1333,7 @@ bool Value::maybe_id() const noexcept return false; } -void Value::reserve(size_t count) +void Value::reserve(std::size_t count) { if (std::holds_alternative(_data)) { @@ -1299,7 +1362,7 @@ void Value::reserve(size_t count) } } -size_t Value::size() const +std::size_t Value::size() const { switch (type()) { @@ -1331,10 +1394,9 @@ bool Value::emplace_back(std::string&& name, Value&& value) } auto& mapData = std::get(_data); - const auto [itr, itrEnd] = std::equal_range(mapData.members.cbegin(), - mapData.members.cend(), + const auto [itr, itrEnd] = std::ranges::equal_range(mapData.members, std::nullopt, - [&mapData, &name](std::optional lhs, std::optional rhs) noexcept { + [&mapData, &name](std::optional lhs, std::optional rhs) noexcept { std::string_view lhsName { lhs == std::nullopt ? name : mapData.map[*lhs].first }; std::string_view rhsName { rhs == std::nullopt ? name : mapData.map[*rhs].first }; return lhsName < rhsName; @@ -1361,10 +1423,9 @@ MapType::const_iterator Value::find(std::string_view name) const } const auto& mapData = std::get(typeData); - const auto [itr, itrEnd] = std::equal_range(mapData.members.cbegin(), - mapData.members.cend(), + const auto [itr, itrEnd] = std::ranges::equal_range(mapData.members, std::nullopt, - [&mapData, name](std::optional lhs, std::optional rhs) noexcept { + [&mapData, name](std::optional lhs, std::optional rhs) noexcept { std::string_view lhsName { lhs == std::nullopt ? name : mapData.map[*lhs].first }; std::string_view rhsName { rhs == std::nullopt ? name : mapData.map[*rhs].first }; return lhsName < rhsName; @@ -1429,7 +1490,7 @@ void Value::emplace_back(Value&& value) std::get(_data).emplace_back(std::move(value)); } -const Value& Value::operator[](size_t index) const +const Value& Value::operator[](std::size_t index) const { const auto& typeData = data(); @@ -1441,87 +1502,498 @@ const Value& Value::operator[](size_t index) const return std::get(typeData).at(index); } -void Writer::write(Value response) const +void ValueVisitor::add_value(std::shared_ptr&& value) +{ + _concept->add_value(std::move(value)); +} + +void ValueVisitor::reserve(std::size_t count) +{ + _concept->reserve(count); +} + +void ValueVisitor::start_object() +{ + _concept->start_object(); +} + +void ValueVisitor::add_member(std::string&& key) +{ + _concept->add_member(std::move(key)); +} + +void ValueVisitor::end_object() +{ + _concept->end_object(); +} + +void ValueVisitor::start_array() +{ + _concept->start_array(); +} + +void ValueVisitor::end_array() +{ + _concept->end_array(); +} + +void ValueVisitor::add_null() +{ + _concept->add_null(); +} + +void ValueVisitor::add_string(std::string&& value) +{ + _concept->add_string(std::move(value)); +} + +void ValueVisitor::add_enum(std::string&& value) +{ + _concept->add_enum(std::move(value)); +} + +void ValueVisitor::add_id(response::IdType&& value) +{ + _concept->add_id(std::move(value)); +} + +void ValueVisitor::add_bool(bool value) +{ + _concept->add_bool(value); +} + +void ValueVisitor::add_int(int value) +{ + _concept->add_int(value); +} + +void ValueVisitor::add_float(double value) +{ + _concept->add_float(value); +} + +void ValueVisitor::complete() +{ + _concept->complete(); +} + +ValueToken::ValueToken(OpaqueValue&& value) + : _value { std::move(value) } +{ +} + +ValueToken::ValueToken(AnyValue&& value) + : _value { std::move(value) } +{ +} + +ValueToken::ValueToken(Reserve&& value) + : _value { std::move(value) } +{ +} + +ValueToken::ValueToken(StartObject&& value) + : _value { std::move(value) } +{ +} + +ValueToken::ValueToken(AddMember&& value) + : _value { std::move(value) } +{ +} + +ValueToken::ValueToken(EndObject&& value) + : _value { std::move(value) } +{ +} + +ValueToken::ValueToken(StartArray&& value) + : _value { std::move(value) } +{ +} + +ValueToken::ValueToken(EndArray&& value) + : _value { std::move(value) } +{ +} + +ValueToken::ValueToken(NullValue&& value) + : _value { std::move(value) } +{ +} + +ValueToken::ValueToken(StringValue&& value) + : _value { std::move(value) } +{ +} + +ValueToken::ValueToken(EnumValue&& value) + : _value { std::move(value) } +{ +} + +ValueToken::ValueToken(IdValue&& value) + : _value { std::move(value) } +{ +} + +ValueToken::ValueToken(BoolValue&& value) + : _value { std::move(value) } +{ +} + +ValueToken::ValueToken(IntValue&& value) + : _value { std::move(value) } +{ +} + +ValueToken::ValueToken(FloatValue&& value) + : _value { std::move(value) } +{ +} + +void ValueToken::visit(const std::shared_ptr& visitor) && +{ + std::visit( + [&visitor](auto&& value) { + using value_type = std::decay_t; + + if constexpr (std::is_same_v) + { + visitor->add_value(std::move(value)); + } + else if constexpr (std::is_same_v) + { + value->serialize(value->value, visitor); + } + else if constexpr (std::is_same_v) + { + visitor->reserve(value.capacity); + } + else if constexpr (std::is_same_v) + { + visitor->start_object(); + } + else if constexpr (std::is_same_v) + { + visitor->add_member(std::move(value.key)); + } + else if constexpr (std::is_same_v) + { + visitor->end_object(); + } + else if constexpr (std::is_same_v) + { + visitor->start_array(); + } + else if constexpr (std::is_same_v) + { + visitor->end_array(); + } + else if constexpr (std::is_same_v) + { + visitor->add_null(); + } + else if constexpr (std::is_same_v) + { + visitor->add_string(std::move(value.value)); + } + else if constexpr (std::is_same_v) + { + visitor->add_enum(std::move(value.value)); + } + else if constexpr (std::is_same_v) + { + visitor->add_id(std::move(value.value)); + } + else if constexpr (std::is_same_v) + { + visitor->add_bool(value.value); + } + else if constexpr (std::is_same_v) + { + visitor->add_int(value.value); + } + else if constexpr (std::is_same_v) + { + visitor->add_float(value.value); + } + }, + std::move(_value)); +} + +class ValueTokenStreamVisitor +{ +public: + void add_value(std::shared_ptr&& value); + void reserve(std::size_t count); + void start_object(); + void add_member(std::string&& key); + void end_object(); + void start_array(); + void end_array(); + void add_null(); + void add_string(std::string&& value); + void add_enum(std::string&& value); + void add_id(response::IdType&& value); + void add_bool(bool value); + void add_int(int value); + void add_float(double value); + void complete(); + + Value value(); + +private: + void add_value(Value&& value); + + Value _result {}; + std::stack _values {}; + std::stack _keys {}; +}; + +void ValueTokenStreamVisitor::add_value(std::shared_ptr&& value) +{ + add_value(Value { std::move(value) }); +} + +void ValueTokenStreamVisitor::reserve(std::size_t count) +{ + _values.top().reserve(count); +} + +void ValueTokenStreamVisitor::start_object() +{ + _values.push(Value { response::Type::Map }); +} + +void ValueTokenStreamVisitor::add_member(std::string&& key) +{ + _keys.push(std::move(key)); +} + +void ValueTokenStreamVisitor::end_object() +{ + auto value = std::move(_values.top()); + + _values.pop(); + add_value(std::move(value)); +} + +void ValueTokenStreamVisitor::start_array() { - switch (response.type()) + _values.push(Value { response::Type::List }); +} + +void ValueTokenStreamVisitor::end_array() +{ + auto value = std::move(_values.top()); + + _values.pop(); + add_value(std::move(value)); +} + +void ValueTokenStreamVisitor::add_null() +{ + add_value(Value {}); +} + +void ValueTokenStreamVisitor::add_string(std::string&& value) +{ + add_value(Value { std::move(value) }); +} + +void ValueTokenStreamVisitor::add_enum(std::string&& value) +{ + Value enumValue { response::Type::EnumValue }; + + enumValue.set(std::move(value)); + add_value(std::move(enumValue)); +} + +void ValueTokenStreamVisitor::add_id(response::IdType&& value) +{ + add_value(Value { std::move(value) }); +} + +void ValueTokenStreamVisitor::add_bool(bool value) +{ + add_value(Value { std::move(value) }); +} + +void ValueTokenStreamVisitor::add_int(int value) +{ + add_value(Value { std::move(value) }); +} + +void ValueTokenStreamVisitor::add_float(double value) +{ + add_value(Value { std::move(value) }); +} + +void ValueTokenStreamVisitor::complete() +{ +} + +Value ValueTokenStreamVisitor::value() +{ + auto value = std::move(_result); + + return value; +} + +void ValueTokenStreamVisitor::add_value(Value&& value) +{ + if (_values.empty()) + { + _result = std::move(value); + return; + } + + switch (_values.top().type()) + { + case response::Type::Map: + _values.top().emplace_back(std::move(_keys.top()), std::move(value)); + _keys.pop(); + break; + + case response::Type::List: + _values.top().emplace_back(std::move(value)); + break; + + default: + throw std::logic_error("Invalid call to Value::emplace_back"); + break; + } +} + +ValueTokenStream::ValueTokenStream(Value&& value) +{ + switch (value.type()) { case Type::Map: { - auto members = response.release(); + auto members = value.release(); - _concept->start_object(); + push_back(ValueToken::StartObject {}); + push_back(ValueToken::Reserve { members.size() }); for (auto& entry : members) { - _concept->add_member(entry.first); - write(std::move(entry.second)); + push_back(ValueToken::AddMember { std::move(entry.first) }); + append(ValueTokenStream { std::move(entry.second) }); } - _concept->end_object(); + push_back(ValueToken::EndObject {}); break; } case Type::List: { - auto elements = response.release(); + auto elements = value.release(); - _concept->start_array(); + push_back(ValueToken::StartArray {}); + push_back(ValueToken::Reserve { elements.size() }); for (auto& entry : elements) { - write(std::move(entry)); + append(ValueTokenStream { std::move(entry) }); } - _concept->end_arrary(); + push_back(ValueToken::EndArray {}); break; } case Type::String: - case Type::EnumValue: - case Type::ID: { - auto value = response.release(); + auto stringValue = value.release(); - _concept->write_string(value); + push_back(ValueToken::StringValue { std::move(stringValue) }); break; } case Type::Null: { - _concept->write_null(); + push_back(ValueToken::NullValue {}); break; } case Type::Boolean: { - _concept->write_bool(response.get()); + push_back(ValueToken::BoolValue { value.get() }); break; } case Type::Int: { - _concept->write_int(response.get()); + push_back(ValueToken::IntValue { value.get() }); break; } case Type::Float: { - _concept->write_float(response.get()); + push_back(ValueToken::FloatValue { value.get() }); + break; + } + + case Type::EnumValue: + { + auto enumValue = value.release(); + + push_back(ValueToken::EnumValue { std::move(enumValue) }); + break; + } + + case Type::ID: + { + auto idValue = value.release(); + + push_back(ValueToken::IdValue { std::move(idValue) }); break; } case Type::Scalar: { - write(response.release()); + if (value.isAny()) + { + push_back(ValueToken::AnyValue { value.releaseAny() }); + } + else + { + append(ValueTokenStream { value.release() }); + } break; } default: { - _concept->write_null(); + push_back(ValueToken::NullValue {}); break; } } } +void ValueTokenStream::append(ValueTokenStream&& other) +{ + _tokens.splice(_tokens.end(), std::move(other._tokens)); +} + +void ValueTokenStream::visit(const std::shared_ptr& visitor) && +{ + for (auto& token : _tokens) + { + std::move(token).visit(visitor); + } + + visitor->complete(); +} + +Value ValueTokenStream::value() && +{ + auto visitor = std::make_shared(); + + std::move(*this).visit(std::make_shared(visitor)); + + return visitor->value(); +} + } // namespace graphql::response diff --git a/src/GraphQLService.cpp b/src/GraphQLService.cpp index a01daba5..abc98739 100644 --- a/src/GraphQLService.cpp +++ b/src/GraphQLService.cpp @@ -4,67 +4,90 @@ #include "graphqlservice/GraphQLService.h" #include "graphqlservice/internal/Grammar.h" +#include "graphqlservice/internal/Introspection.h" + +#include "graphqlservice/introspection/SchemaObject.h" +#include "graphqlservice/introspection/TypeObject.h" #include "Validation.h" #include #include +#include #include +#include + +using namespace std::literals; namespace graphql::service { -void addErrorMessage(std::string&& message, response::Value& error) +response::ValueTokenStream addErrorMessage(std::string&& message) { - error.emplace_back(std::string { strMessage }, response::Value(std::move(message))); + response::ValueTokenStream result {}; + + result.push_back(response::ValueToken::AddMember { std::string { strMessage } }); + result.push_back(response::ValueToken::StringValue { std::move(message) }); + + return result; } -void addErrorLocation(const schema_location& location, response::Value& error) +response::ValueTokenStream addErrorLocation(const schema_location& location) { + response::ValueTokenStream result {}; + if (location.line == 0) { - return; + return result; } - response::Value errorLocation(response::Type::Map); + result.push_back(response::ValueToken::AddMember { std::string { strLocations } }); - errorLocation.reserve(2); - errorLocation.emplace_back(std::string { strLine }, - response::Value(static_cast(location.line))); - errorLocation.emplace_back(std::string { strColumn }, - response::Value(static_cast(location.column))); + result.push_back(response::ValueToken::StartArray {}); + result.push_back(response::ValueToken::Reserve { 1 }); + result.push_back(response::ValueToken::StartObject {}); + result.push_back(response::ValueToken::Reserve { 2 }); - response::Value errorLocations(response::Type::List); + result.push_back(response::ValueToken::AddMember { std::string { strLine } }); + result.push_back(response::ValueToken::IntValue { static_cast(location.line) }); + result.push_back(response::ValueToken::AddMember { std::string { strColumn } }); + result.push_back(response::ValueToken::IntValue { static_cast(location.column) }); - errorLocations.reserve(1); - errorLocations.emplace_back(std::move(errorLocation)); + result.push_back(response::ValueToken::EndObject {}); + result.push_back(response::ValueToken::EndArray {}); - error.emplace_back(std::string { strLocations }, std::move(errorLocations)); + return result; } -void addErrorPath(const error_path& path, response::Value& error) +response::ValueTokenStream addErrorPath(const error_path& path) { + response::ValueTokenStream result {}; + if (path.empty()) { - return; + return result; } - response::Value errorPath(response::Type::List); + result.push_back(response::ValueToken::AddMember { std::string { strPath } }); + result.push_back(response::ValueToken::StartArray {}); + result.push_back(response::ValueToken::Reserve { path.size() }); - errorPath.reserve(path.size()); for (const auto& segment : path) { if (std::holds_alternative(segment)) { - errorPath.emplace_back( - response::Value { std::string { std::get(segment) } }); + result.push_back(response::ValueToken::StringValue { + std::string { std::get(segment) } }); } - else if (std::holds_alternative(segment)) + else if (std::holds_alternative(segment)) { - errorPath.emplace_back(response::Value(static_cast(std::get(segment)))); + result.push_back(response::ValueToken::IntValue { + static_cast(std::get(segment)) }); } } - error.emplace_back(std::string { strPath }, std::move(errorPath)); + result.push_back(response::ValueToken::EndArray {}); + + return result; } error_path buildErrorPath(const std::optional& path) @@ -82,8 +105,7 @@ error_path buildErrorPath(const std::optional& path) } result.reserve(segments.size()); - std::transform(segments.cbegin(), - segments.cend(), + std::ranges::transform(segments, std::back_inserter(result), [](const auto& segment) noexcept { return segment.get(); @@ -95,22 +117,30 @@ error_path buildErrorPath(const std::optional& path) response::Value buildErrorValues(std::list&& structuredErrors) { - response::Value errors(response::Type::List); + return visitErrorValues(std::move(structuredErrors)).value(); +} + +response::ValueTokenStream visitErrorValues(std::list&& structuredErrors) +{ + response::ValueTokenStream errors; - errors.reserve(structuredErrors.size()); + errors.push_back(response::ValueToken::StartArray {}); + errors.push_back(response::ValueToken::Reserve { structuredErrors.size() }); for (auto& error : structuredErrors) { - response::Value entry(response::Type::Map); + errors.push_back(response::ValueToken::StartObject {}); + errors.push_back(response::ValueToken::Reserve { 3 }); - entry.reserve(3); - addErrorMessage(std::move(error.message), entry); - addErrorLocation(error.location, entry); - addErrorPath(error.path, entry); + errors.append(addErrorMessage(std::move(error.message))); + errors.append(addErrorLocation(error.location)); + errors.append(addErrorPath(error.path)); - errors.emplace_back(std::move(entry)); + errors.push_back(response::ValueToken::EndObject {}); } + errors.push_back(response::ValueToken::EndArray {}); + return errors; } @@ -129,12 +159,9 @@ std::list schema_exception::convertMessages( { std::list errors; - std::transform(messages.begin(), - messages.end(), - std::back_inserter(errors), - [](std::string& message) noexcept { - return schema_error { std::move(message) }; - }); + std::ranges::transform(messages, std::back_inserter(errors), [](std::string& message) noexcept { + return schema_error { std::move(message) }; + }); return errors; } @@ -170,19 +197,13 @@ unimplemented_method::unimplemented_method(std::string_view methodName) std::string unimplemented_method::getMessage(std::string_view methodName) noexcept { - using namespace std::literals; - - std::ostringstream oss; - - oss << methodName << R"ex( is not implemented)ex"sv; - - return oss.str(); + return std::format(R"ex({} is not implemented)ex", methodName); } -void await_worker_thread::await_suspend(coro::coroutine_handle<> h) const +void await_worker_thread::await_suspend(std::coroutine_handle<> h) const { std::thread( - [](coro::coroutine_handle<>&& h) { + [](std::coroutine_handle<>&& h) { h.resume(); }, std::move(h)) @@ -213,7 +234,7 @@ bool await_worker_queue::await_ready() const return std::this_thread::get_id() != _startId; } -void await_worker_queue::await_suspend(coro::coroutine_handle<> h) +void await_worker_queue::await_suspend(std::coroutine_handle<> h) { std::unique_lock lock { _mutex }; @@ -232,7 +253,7 @@ void await_worker_queue::resumePending() return _shutdown || !_pending.empty(); }); - std::list> pending; + std::list> pending; std::swap(pending, _pending); @@ -250,7 +271,7 @@ void await_worker_queue::resumePending() // Default to immediate synchronous execution. await_async::await_async() : _pimpl { std::static_pointer_cast( - std::make_shared>(std::make_shared())) } + std::make_shared>(std::make_shared())) } { } @@ -258,9 +279,9 @@ await_async::await_async() await_async::await_async(std::launch launch) : _pimpl { ((launch & std::launch::async) == std::launch::async) ? std::static_pointer_cast(std::make_shared>( - std::make_shared())) - : std::static_pointer_cast(std::make_shared>( - std::make_shared())) } + std::make_shared())) + : std::static_pointer_cast(std::make_shared>( + std::make_shared())) } { } @@ -269,7 +290,7 @@ bool await_async::await_ready() const return _pimpl->await_ready(); } -void await_async::await_suspend(coro::coroutine_handle<> h) const +void await_async::await_suspend(std::coroutine_handle<> h) const { _pimpl->await_suspend(std::move(h)); } @@ -371,12 +392,10 @@ void ValueVisitor::visitVariable(const peg::ast_node& variable) if (itr == _variables.get().cend()) { auto position = variable.begin(); - std::ostringstream error; - - error << "Unknown variable name: " << name; + auto error = std::format("Unknown variable name: {}", name); throw schema_exception { - { schema_error { error.str(), { position.line, position.column } } } + { schema_error { std::move(error), { position.line, position.column } } } }; } @@ -535,11 +554,9 @@ bool DirectiveVisitor::shouldSkip() const if (arguments.type() != response::Type::Map) { - std::ostringstream error; - - error << "Invalid arguments to directive: " << directiveName; + auto error = std::format("Invalid arguments to directive: {}", directiveName); - throw schema_exception { { error.str() } }; + throw schema_exception { { std::move(error) } }; } bool argumentTrue = false; @@ -550,12 +567,11 @@ bool DirectiveVisitor::shouldSkip() const if (argumentTrue || argumentFalse || argumentValue.type() != response::Type::Boolean || argumentName != "if") { - std::ostringstream error; + auto error = std::format("Invalid argument to directive: {} name: {}", + directiveName, + argumentName); - error << "Invalid argument to directive: " << directiveName - << " name: " << argumentName; - - throw schema_exception { { error.str() } }; + throw schema_exception { { std::move(error) } }; } argumentTrue = argumentValue.get(); @@ -572,11 +588,9 @@ bool DirectiveVisitor::shouldSkip() const } else { - std::ostringstream error; - - error << "Missing argument directive: " << directiveName << " name: if"; + auto error = std::format("Missing argument directive: {} name: if", directiveName); - throw schema_exception { { error.str() } }; + throw schema_exception { { std::move(error) } }; } } @@ -633,6 +647,29 @@ schema_location ResolverParams::getLocation() const return { position.line, position.column }; } +response::Value ResolverResult::document() && +{ + return std::move(*this).visit().value(); +} + +response::ValueTokenStream ResolverResult::visit() && +{ + response::ValueTokenStream result { response::ValueToken::StartObject {} }; + + result.push_back(response::ValueToken::AddMember { std::string { strData } }); + result.append(std::move(data)); + + if (!errors.empty()) + { + result.push_back(response::ValueToken::AddMember { std::string { strErrors } }); + result.append(visitErrorValues(std::move(errors))); + } + + result.push_back(response::ValueToken::EndObject {}); + + return result; +} + template <> int Argument::convert(const response::Value& value) { @@ -700,11 +737,9 @@ void blockSubFields(const ResolverParams& params) if (params.selection != nullptr) { auto position = params.selection->begin(); - std::ostringstream error; - - error << "Field may not have sub-fields name: " << params.fieldName; + auto error = std::format("Field may not have sub-fields name: {}", params.fieldName); - throw schema_exception { { schema_error { error.str(), + throw schema_exception { { schema_error { std::move(error), { position.line, position.column }, buildErrorPath(params.errorPath) } } }; } @@ -718,7 +753,7 @@ AwaitableResolver Result::convert(AwaitableScalar result, ResolverPara return ModifiedResult::resolve(std::move(result), std::move(params), [](int&& value, const ResolverParams&) { - return response::Value(value); + return ResolverResult { { response::ValueToken::IntValue { value } } }; }); } @@ -730,7 +765,7 @@ AwaitableResolver Result::convert(AwaitableScalar result, Resolv return ModifiedResult::resolve(std::move(result), std::move(params), [](double&& value, const ResolverParams&) { - return response::Value(value); + return ResolverResult { { response::ValueToken::FloatValue { value } } }; }); } @@ -743,7 +778,7 @@ AwaitableResolver Result::convert( return ModifiedResult::resolve(std::move(result), std::move(params), [](std::string&& value, const ResolverParams&) { - return response::Value(std::move(value)); + return ResolverResult { { response::ValueToken::StringValue { std::move(value) } } }; }); } @@ -755,7 +790,7 @@ AwaitableResolver Result::convert(AwaitableScalar result, ResolverPa return ModifiedResult::resolve(std::move(result), std::move(params), [](bool&& value, const ResolverParams&) { - return response::Value(value); + return ResolverResult { { response::ValueToken::BoolValue { value } } }; }); } @@ -768,7 +803,8 @@ AwaitableResolver Result::convert( return ModifiedResult::resolve(std::move(result), std::move(params), [](response::Value&& value, const ResolverParams&) { - return response::Value(std::move(value)); + return ResolverResult { { response::ValueToken::OpaqueValue { + std::make_shared(std::move(value)) } } }; }); } @@ -781,7 +817,7 @@ AwaitableResolver Result::convert( return ModifiedResult::resolve(std::move(result), std::move(params), [](response::IdType&& value, const ResolverParams&) { - return response::Value(std::move(value)); + return ResolverResult { { response::ValueToken::IdValue { std::move(value) } } }; }); } @@ -791,11 +827,9 @@ void requireSubFields(const ResolverParams& params) if (params.selection == nullptr) { auto position = params.field.begin(); - std::ostringstream error; + auto error = std::format("Field must have sub-fields name: {}", params.fieldName); - error << "Field must have sub-fields name: " << params.fieldName; - - throw schema_exception { { schema_error { error.str(), + throw schema_exception { { schema_error { std::move(error), { position.line, position.column }, buildErrorPath(params.errorPath) } } }; } @@ -816,7 +850,7 @@ AwaitableResolver Result::convert( if (!awaitedResult) { - co_return ResolverResult {}; + co_return ResolverResult { { response::ValueToken::NullValue {} } }; } auto document = co_await awaitedResult->resolve(params, @@ -885,7 +919,7 @@ class SelectionVisitor public: explicit SelectionVisitor(const SelectionSetParams& selectionSetParams, const FragmentMap& fragments, const response::Value& variables, const TypeNames& typeNames, - const ResolverMap& resolvers, size_t count); + const ResolverMap& resolvers, std::size_t count); void visit(const peg::ast_node& selection); @@ -913,6 +947,8 @@ class SelectionVisitor const TypeNames& _typeNames; const ResolverMap& _resolvers; + static const Directives s_emptyFragmentDefinitionDirectives; + std::shared_ptr _fragmentDefinitionDirectives; std::shared_ptr _fragmentSpreadDirectives; std::shared_ptr _inlineFragmentDirectives; @@ -920,9 +956,11 @@ class SelectionVisitor std::vector _values; }; +const Directives SelectionVisitor::s_emptyFragmentDefinitionDirectives {}; + SelectionVisitor::SelectionVisitor(const SelectionSetParams& selectionSetParams, const FragmentMap& fragments, const response::Value& variables, const TypeNames& typeNames, - const ResolverMap& resolvers, size_t count) + const ResolverMap& resolvers, std::size_t count) : _resolverContext(selectionSetParams.resolverContext) , _state(selectionSetParams.state) , _operationDirectives(selectionSetParams.operationDirectives) @@ -935,21 +973,16 @@ SelectionVisitor::SelectionVisitor(const SelectionSetParams& selectionSetParams, , _typeNames(typeNames) , _resolvers(resolvers) , _fragmentDefinitionDirectives { std::make_shared( - *selectionSetParams.fragmentDefinitionDirectives) } + FragmentDefinitionDirectiveStack { std::cref(s_emptyFragmentDefinitionDirectives), + selectionSetParams.fragmentDefinitionDirectives }) } , _fragmentSpreadDirectives { std::make_shared( - *selectionSetParams.fragmentSpreadDirectives) } + FragmentSpreadDirectiveStack { {}, selectionSetParams.fragmentSpreadDirectives }) } , _inlineFragmentDirectives { std::make_shared( - *selectionSetParams.inlineFragmentDirectives) } + FragmentSpreadDirectiveStack { {}, selectionSetParams.inlineFragmentDirectives }) } { - static const Directives s_emptyFragmentDefinitionDirectives; - // Traversing a SelectionSet from an Object type field should start tracking new fragment // directives. The outer fragment directives are still there in the FragmentSpreadDirectiveStack // if the field accessors want to inspect them. - _fragmentDefinitionDirectives->push_front(std::cref(s_emptyFragmentDefinitionDirectives)); - _fragmentSpreadDirectives->push_front({}); - _inlineFragmentDirectives->push_front({}); - _names.reserve(count); _values.reserve(count); } @@ -1010,12 +1043,10 @@ void SelectionVisitor::visitField(const peg::ast_node& field) { std::promise promise; auto position = field.begin(); - std::ostringstream error; - - error << "Unknown field name: " << name; + auto error = std::format("Unknown field name: {}", name); promise.set_exception( - std::make_exception_ptr(schema_exception { { schema_error { error.str(), + std::make_exception_ptr(schema_exception { { schema_error { std::move(error), { position.line, position.column }, buildErrorPath(_path ? std::make_optional(_path->get()) : std::nullopt) } } })); @@ -1104,12 +1135,10 @@ void SelectionVisitor::visitField(const peg::ast_node& field) catch (const std::exception& ex) { std::promise promise; - std::ostringstream message; - - message << "Field error name: " << alias << " unknown error: " << ex.what(); + auto message = std::format("Field error name: {} unknown error: {}", alias, ex.what()); promise.set_exception( - std::make_exception_ptr(schema_exception { { schema_error { message.str(), + std::make_exception_ptr(schema_exception { { schema_error { std::move(message), { position.line, position.column }, buildErrorPath(selectionSetParams.errorPath) } } })); @@ -1125,11 +1154,9 @@ void SelectionVisitor::visitFragmentSpread(const peg::ast_node& fragmentSpread) if (itr == _fragments.end()) { auto position = fragmentSpread.begin(); - std::ostringstream error; - - error << "Unknown fragment name: " << name; + auto error = std::format("Unknown fragment name: {}", name); - throw schema_exception { { schema_error { error.str(), + throw schema_exception { { schema_error { std::move(error), { position.line, position.column }, buildErrorPath(_path ? std::make_optional(_path->get()) : std::nullopt) } } }; } @@ -1152,10 +1179,14 @@ void SelectionVisitor::visitFragmentSpread(const peg::ast_node& fragmentSpread) return; } - _fragmentDefinitionDirectives->push_front(itr->second.getDirectives()); - _fragmentSpreadDirectives->push_front(directiveVisitor.getDirectives()); + _fragmentDefinitionDirectives = std::make_shared( + FragmentDefinitionDirectiveStack { itr->second.getDirectives(), + _fragmentDefinitionDirectives }); + _fragmentSpreadDirectives = std::make_shared( + FragmentSpreadDirectiveStack { directiveVisitor.getDirectives(), + _fragmentSpreadDirectives }); - const size_t count = itr->second.getSelection().children.size(); + const std::size_t count = itr->second.getSelection().children.size(); if (count > 1) { @@ -1168,8 +1199,8 @@ void SelectionVisitor::visitFragmentSpread(const peg::ast_node& fragmentSpread) visit(*selection); } - _fragmentSpreadDirectives->pop_front(); - _fragmentDefinitionDirectives->pop_front(); + _fragmentSpreadDirectives = _fragmentSpreadDirectives->outer; + _fragmentDefinitionDirectives = _fragmentDefinitionDirectives->outer; } void SelectionVisitor::visitInlineFragment(const peg::ast_node& inlineFragment) @@ -1198,9 +1229,11 @@ void SelectionVisitor::visitInlineFragment(const peg::ast_node& inlineFragment) { peg::on_first_child(inlineFragment, [this, &directiveVisitor](const peg::ast_node& child) { - _inlineFragmentDirectives->push_front(directiveVisitor.getDirectives()); + _inlineFragmentDirectives = std::make_shared( + FragmentSpreadDirectiveStack { directiveVisitor.getDirectives(), + _inlineFragmentDirectives }); - const size_t count = child.children.size(); + const std::size_t count = child.children.size(); if (count > 1) { @@ -1213,7 +1246,7 @@ void SelectionVisitor::visitInlineFragment(const peg::ast_node& inlineFragment) visit(*selection); } - _inlineFragmentDirectives->pop_front(); + _inlineFragmentDirectives = _inlineFragmentDirectives->outer; }); } } @@ -1224,6 +1257,67 @@ Object::Object(TypeNames&& typeNames, ResolverMap&& resolvers) noexcept { } +std::shared_ptr Object::StitchObject(const std::shared_ptr& added, + const std::shared_ptr& schema /* = {} */) const +{ + auto typeNames = _typeNames; + auto resolvers = _resolvers; + + if (schema && schema->supportsIntrospection()) + { + constexpr auto schemaField = R"gql(__schema)gql"sv; + constexpr auto typeField = R"gql(__type)gql"sv; + + resolvers.erase(schemaField); + resolvers.emplace(schemaField, [schema](ResolverParams&& params) { + return Result::convert( + std::static_pointer_cast(std::make_shared( + std::make_shared(schema))), + std::move(params)); + }); + + resolvers.erase(typeField); + resolvers.emplace(typeField, [schema](ResolverParams&& params) { + auto argName = ModifiedArgument::require("name", params.arguments); + const auto& baseType = schema->LookupType(argName); + std::shared_ptr result { baseType + ? std::make_shared( + std::make_shared(baseType)) + : nullptr }; + + return ModifiedResult::convert( + result, + std::move(params)); + }); + } + + bool hasStitchedResolvers = false; + + if (added) + { + for (const auto& name : added->_typeNames) + { + typeNames.emplace(name); + } + + for (const auto& [name, resolver] : added->_resolvers) + { + hasStitchedResolvers = resolvers.emplace(name, resolver).second || hasStitchedResolvers; + } + } + + auto object = std::make_shared(std::move(typeNames), std::move(resolvers)); + + object->_stitched[0] = shared_from_this(); + + if (hasStitchedResolvers) + { + object->_stitched[1] = added; + } + + return object; +} + AwaitableResolver Object::resolve(const SelectionSetParams& selectionSetParams, const peg::ast_node& selection, const FragmentMap& fragments, const response::Value& variables) const @@ -1246,9 +1340,10 @@ AwaitableResolver Object::resolve(const SelectionSetParams& selectionSetParams, auto children = visitor.getValues(); const auto launch = selectionSetParams.launch; - ResolverResult document { response::Value { response::Type::Map } }; + ResolverResult document {}; - document.data.reserve(children.size()); + document.data.push_back(response::ValueToken::StartObject {}); + document.data.push_back(response::ValueToken::Reserve { children.size() }); const auto parent = selectionSetParams.errorPath ? std::make_optional(std::cref(*selectionSetParams.errorPath)) @@ -1262,22 +1357,12 @@ AwaitableResolver Object::resolve(const SelectionSetParams& selectionSetParams, auto value = co_await std::move(child.result); - if (!document.data.emplace_back(std::string { child.name }, std::move(value.data))) - { - std::ostringstream message; - - message << "Ambiguous field error name: " << child.name; - - field_path path { parent, path_segment { child.name } }; - - document.errors.push_back({ message.str(), - child.location.value_or(schema_location {}), - buildErrorPath(std::make_optional(path)) }); - } + document.data.push_back(response::ValueToken::AddMember { std::string { child.name } }); + document.data.append(std::move(value.data)); if (!value.errors.empty()) { - document.errors.splice(document.errors.end(), value.errors); + document.errors.splice(document.errors.end(), std::move(value.errors)); } } catch (schema_exception& scx) @@ -1286,26 +1371,28 @@ AwaitableResolver Object::resolve(const SelectionSetParams& selectionSetParams, if (!errors.empty()) { - std::copy(errors.begin(), errors.end(), std::back_inserter(document.errors)); + std::ranges::copy(errors, std::back_inserter(document.errors)); } - document.data.emplace_back(std::string { child.name }, {}); + document.data.push_back(response::ValueToken::AddMember { std::string { child.name } }); + document.data.push_back(response::ValueToken::NullValue {}); } catch (const std::exception& ex) { - std::ostringstream message; - - message << "Field error name: " << child.name << " unknown error: " << ex.what(); - + auto message = + std::format("Field error name: {} unknown error: {}", child.name, ex.what()); field_path path { parent, path_segment { child.name } }; - document.errors.push_back({ message.str(), + document.errors.push_back({ std::move(message), child.location.value_or(schema_location {}), buildErrorPath(std::make_optional(path)) }); - document.data.emplace_back(std::string { child.name }, {}); + document.data.push_back(response::ValueToken::AddMember { std::string { child.name } }); + document.data.push_back(response::ValueToken::NullValue {}); } } + document.data.push_back(response::ValueToken::EndObject {}); + co_return std::move(document); } @@ -1403,7 +1490,7 @@ AwaitableResolver OperationDefinitionVisitor::getValue() { if (!_result) { - co_return ResolverResult {}; + co_return ResolverResult { { response::ValueToken::NullValue {} } }; } auto result = std::move(*_result); @@ -1469,9 +1556,9 @@ void OperationDefinitionVisitor::visit( _resolverContext, _params->state, _params->directives, - std::make_shared(), - std::make_shared(), - std::make_shared(), + std::shared_ptr {}, + std::shared_ptr {}, + std::shared_ptr {}, std::nullopt, _launch, }; @@ -1484,7 +1571,8 @@ void OperationDefinitionVisitor::visit( SubscriptionData::SubscriptionData(std::shared_ptr data, SubscriptionName&& field, response::Value arguments, Directives fieldDirectives, peg::ast&& query, - std::string&& operationName, SubscriptionCallback&& callback, const peg::ast_node& selection) + std::string&& operationName, SubscriptionCallbackOrVisitor&& callback, + const peg::ast_node& selection) : data(std::move(data)) , field(std::move(field)) , arguments(std::move(arguments)) @@ -1601,12 +1689,10 @@ void SubscriptionDefinitionVisitor::visitField(const peg::ast_node& field) if (!_field.empty()) { auto position = field.begin(); - std::ostringstream error; - - error << "Extra subscription root field name: " << name; + auto error = std::format("Extra subscription root field name: {}", name); throw schema_exception { - { schema_error { error.str(), { position.line, position.column } } } + { schema_error { std::move(error), { position.line, position.column } } } }; } @@ -1648,12 +1734,10 @@ void SubscriptionDefinitionVisitor::visitFragmentSpread(const peg::ast_node& fra if (itr == _fragments.end()) { auto position = fragmentSpread.begin(); - std::ostringstream error; - - error << "Unknown fragment name: " << name; + auto error = std::format("Unknown fragment name: {}", name); throw schema_exception { - { schema_error { error.str(), { position.line, position.column } } } + { schema_error { std::move(error), { position.line, position.column } } } }; } @@ -1716,6 +1800,7 @@ void SubscriptionDefinitionVisitor::visitInlineFragment(const peg::ast_node& inl Request::Request(TypeMap operationTypes, std::shared_ptr schema) : _operations(std::move(operationTypes)) + , _schema(schema) , _validation(std::make_unique(std::move(schema))) { } @@ -1727,6 +1812,98 @@ Request::~Request() // forward declaration of the class. } +std::shared_ptr Request::stitch(const std::shared_ptr& added) const +{ + TypeMap operations; + auto schema = _schema->StitchSchema(added->_schema); + std::shared_ptr query; + auto itrOriginalQuery = _operations.find(strQuery); + auto itrAddedQuery = added->_operations.find(strQuery); + + if (itrOriginalQuery != _operations.end() && itrOriginalQuery->second) + { + if (itrAddedQuery != added->_operations.end() && itrAddedQuery->second) + { + query = itrOriginalQuery->second->StitchObject(itrAddedQuery->second, schema); + } + else + { + query = itrOriginalQuery->second->StitchObject({}, schema); + } + } + else if (itrAddedQuery != added->_operations.end() && itrAddedQuery->second) + { + query = itrAddedQuery->second->StitchObject({}, schema); + } + + if (query) + { + operations.emplace(strQuery, std::move(query)); + } + + std::shared_ptr mutation; + auto itrOriginalMutation = _operations.find(strMutation); + auto itrAddedMutation = added->_operations.find(strMutation); + + if (itrOriginalMutation != _operations.end() && itrOriginalMutation->second) + { + if (itrAddedMutation != added->_operations.end() && itrAddedMutation->second) + { + mutation = itrOriginalMutation->second->StitchObject(itrAddedMutation->second); + } + else + { + mutation = itrOriginalMutation->second; + } + } + else if (itrAddedMutation != added->_operations.end() && itrAddedMutation->second) + { + mutation = itrAddedMutation->second; + } + + if (mutation) + { + operations.emplace(strMutation, std::move(mutation)); + } + + std::shared_ptr subscription; + auto itrOriginalSubscription = _operations.find(strSubscription); + auto itrAddedSubscription = added->_operations.find(strSubscription); + + if (itrOriginalSubscription != _operations.end() && itrOriginalSubscription->second) + { + if (itrAddedSubscription != added->_operations.end() && itrAddedSubscription->second) + { + subscription = + itrOriginalSubscription->second->StitchObject(itrAddedSubscription->second); + } + else + { + subscription = itrOriginalSubscription->second; + } + } + else if (itrAddedSubscription != added->_operations.end() && itrAddedSubscription->second) + { + subscription = itrAddedSubscription->second; + } + + if (subscription) + { + operations.emplace(strSubscription, std::move(subscription)); + } + + class StitchedRequest : public Request + { + public: + StitchedRequest(TypeMap operations, std::shared_ptr schema) + : Request { std::move(operations), std::move(schema) } + { + } + }; + + return std::make_shared(std::move(operations), std::move(schema)); +} + std::list Request::validate(peg::ast& query) const { std::list errors; @@ -1785,6 +1962,11 @@ std::pair Request::findOperationDefiniti } response::AwaitableValue Request::resolve(RequestResolveParams params) const +{ + co_return (co_await visit(std::move(params))).document(); +} + +AwaitableResolver Request::visit(RequestResolveParams params) const { try { @@ -1801,31 +1983,27 @@ response::AwaitableValue Request::resolve(RequestResolveParams params) const if (!operationDefinition) { - std::ostringstream message; - - message << "Missing operation"; + auto message = "Missing operation"s; if (!params.operationName.empty()) { - message << " name: " << params.operationName; + message += std::format(" name: {}", params.operationName); } - throw schema_exception { { message.str() } }; + throw schema_exception { { std::move(message) } }; } else if (operationType == strSubscription) { auto position = operationDefinition->begin(); - std::ostringstream message; - - message << "Unexpected subscription"; + auto message = "Unexpected subscription"s; if (!params.operationName.empty()) { - message << " name: " << params.operationName; + message += std::format(" name: {}", params.operationName); } throw schema_exception { - { schema_error { message.str(), { position.line, position.column } } } + { schema_error { std::move(message), { position.line, position.column } } } }; } @@ -1845,27 +2023,11 @@ response::AwaitableValue Request::resolve(RequestResolveParams params) const co_await params.launch; operationVisitor.visit(operationType, *operationDefinition); - auto result = co_await operationVisitor.getValue(); - response::Value document { response::Type::Map }; - - document.emplace_back(std::string { strData }, std::move(result.data)); - - if (!result.errors.empty()) - { - document.emplace_back(std::string { strErrors }, - buildErrorValues(std::move(result.errors))); - } - - co_return std::move(document); + co_return co_await operationVisitor.getValue(); } catch (schema_exception& ex) { - response::Value document(response::Type::Map); - - document.emplace_back(std::string { strData }, response::Value()); - document.emplace_back(std::string { strErrors }, ex.getErrors()); - - co_return std::move(document); + co_return { {}, ex.getStructuredErrors() }; } } @@ -1894,10 +2056,10 @@ AwaitableSubscribe Request::subscribe(RequestSubscribeParams params) ResolverContext::NotifySubscribe, registration->data->state, registration->data->directives, - std::make_shared(), - std::make_shared(), - std::make_shared(), - {}, + std::shared_ptr {}, + std::shared_ptr {}, + std::shared_ptr {}, + std::nullopt, launch, }; @@ -1956,10 +2118,10 @@ AwaitableUnsubscribe Request::unsubscribe(RequestUnsubscribeParams params) ResolverContext::NotifyUnsubscribe, registration->data->state, registration->data->directives, - std::make_shared(), - std::make_shared(), - std::make_shared(), - {}, + std::shared_ptr {}, + std::shared_ptr {}, + std::shared_ptr {}, + std::nullopt, params.launch, }; @@ -2019,39 +2181,43 @@ AwaitableDeliver Request::deliver(RequestDeliverParams params) const ResolverContext::Subscription, registration->data->state, registration->data->directives, - std::make_shared(), - std::make_shared(), - std::make_shared(), + std::shared_ptr {}, + std::shared_ptr {}, + std::shared_ptr {}, std::nullopt, params.launch, }; - response::Value document { response::Type::Map }; + ResolverResult document {}; try { co_await params.launch; - auto result = co_await optionalOrDefaultSubscription->resolve(selectionSetParams, + document = co_await optionalOrDefaultSubscription->resolve(selectionSetParams, registration->selection, registration->data->fragments, registration->data->variables); - - document.emplace_back(std::string { strData }, std::move(result.data)); - - if (!result.errors.empty()) - { - document.emplace_back(std::string { strErrors }, - buildErrorValues(std::move(result.errors))); - } } catch (schema_exception& ex) { - document.emplace_back(std::string { strData }, response::Value()); - document.emplace_back(std::string { strErrors }, ex.getErrors()); + document.errors.splice(document.errors.end(), ex.getStructuredErrors()); } - registration->callback(std::move(document)); + std::visit( + [result = std::move(document)](const auto& callback) mutable { + using callback_type = std::decay_t; + + if constexpr (std::is_same_v) + { + callback(std::move(result).document()); + } + else if constexpr (std::is_same_v) + { + callback(std::move(result)); + } + }, + registration->callback); } co_return; @@ -2079,31 +2245,27 @@ SubscriptionKey Request::addSubscription(RequestSubscribeParams&& params) if (!operationDefinition) { - std::ostringstream message; - - message << "Missing subscription"; + auto message = "Missing subscription"s; if (!params.operationName.empty()) { - message << " name: " << params.operationName; + message += std::format(" name: {}", params.operationName); } - throw schema_exception { { message.str() } }; + throw schema_exception { { std::move(message) } }; } else if (operationType != strSubscription) { auto position = operationDefinition->begin(); - std::ostringstream message; - - message << "Unexpected operation type: " << operationType; + auto message = std::format("Unexpected operation type: {}", operationType); if (!params.operationName.empty()) { - message << " name: " << params.operationName; + message += std::format(" name: {}", params.operationName); } throw schema_exception { - { schema_error { message.str(), { position.line, position.column } } } + { schema_error { std::move(message), { position.line, position.column } } } }; } @@ -2169,8 +2331,7 @@ std::vector> Request::collectRegistratio { // Return all of the registered subscriptions for this field. registrations.reserve(itrListeners->second.size()); - std::transform(itrListeners->second.begin(), - itrListeners->second.end(), + std::ranges::transform(itrListeners->second, std::back_inserter(registrations), [this](const auto& key) noexcept { const auto itr = _subscriptions.find(key); diff --git a/src/Introspection.cpp b/src/Introspection.cpp index 5ac539e9..06bfb9cd 100644 --- a/src/Introspection.cpp +++ b/src/Introspection.cpp @@ -31,7 +31,7 @@ std::vector> Schema::getTypes() const const auto& types = _schema->types(); std::vector> result(types.size()); - std::transform(types.begin(), types.end(), result.begin(), [](const auto& entry) { + std::ranges::transform(types, result.begin(), [](const auto& entry) { return std::make_shared(std::make_shared(entry.second)); }); @@ -67,7 +67,7 @@ std::vector> Schema::getDirectives() const const auto& directives = _schema->directives(); std::vector> result(directives.size()); - std::transform(directives.begin(), directives.end(), result.begin(), [](const auto& entry) { + std::ranges::transform(directives, result.begin(), [](const auto& entry) { return std::make_shared(std::make_shared(entry)); }); @@ -141,7 +141,7 @@ std::optional>> Type::getInterfaces() const auto& interfaces = _type->interfaces(); auto result = std::make_optional>>(interfaces.size()); - std::transform(interfaces.begin(), interfaces.end(), result->begin(), [](const auto& entry) { + std::ranges::transform(interfaces, result->begin(), [](const auto& entry) { return std::make_shared(std::make_shared(entry)); }); @@ -164,16 +164,13 @@ std::optional>> Type::getPossibleTypes auto result = std::make_optional>>(possibleTypes.size()); - std::transform(possibleTypes.begin(), - possibleTypes.end(), - result->begin(), - [](const auto& entry) { - auto typeEntry = entry.lock(); + std::ranges::transform(possibleTypes, result->begin(), [](const auto& entry) { + auto typeEntry = entry.lock(); - return typeEntry && typeEntry->kind() == introspection::TypeKind::OBJECT - ? std::make_shared(std::make_shared(std::move(typeEntry))) - : std::shared_ptr {}; - }); + return typeEntry && typeEntry->kind() == introspection::TypeKind::OBJECT + ? std::make_shared(std::make_shared(std::move(typeEntry))) + : std::shared_ptr {}; + }); result->erase(std::remove(result->begin(), result->end(), std::shared_ptr {}), result->cend()); @@ -225,7 +222,7 @@ std::optional>> Type::getInputFi auto result = std::make_optional>>(inputFields.size()); - std::transform(inputFields.begin(), inputFields.end(), result->begin(), [](const auto& entry) { + std::ranges::transform(inputFields, result->begin(), [](const auto& entry) { return std::make_shared(std::make_shared(entry)); }); @@ -279,7 +276,7 @@ std::vector> Field::getArgs() const const auto& args = _field->args(); std::vector> result(args.size()); - std::transform(args.begin(), args.end(), result.begin(), [](const auto& entry) { + std::ranges::transform(args, result.begin(), [](const auto& entry) { return std::make_shared(std::make_shared(entry)); }); @@ -394,7 +391,7 @@ std::vector> Directive::getArgs() const const auto& args = _directive->args(); std::vector> result(args.size()); - std::transform(args.begin(), args.end(), result.begin(), [](const auto& entry) { + std::ranges::transform(args, result.begin(), [](const auto& entry) { return std::make_shared(std::make_shared(entry)); }); diff --git a/src/JSONResponse.cpp b/src/RapidJSONResponse.cpp similarity index 81% rename from src/JSONResponse.cpp rename to src/RapidJSONResponse.cpp index 8f135478..abdcaee4 100644 --- a/src/JSONResponse.cpp +++ b/src/RapidJSONResponse.cpp @@ -16,20 +16,31 @@ namespace graphql::response { -class StringWriter +class StreamWriter : public std::enable_shared_from_this { public: - StringWriter(rapidjson::StringBuffer& buffer) + StreamWriter(rapidjson::StringBuffer& buffer) : _writer { buffer } { } + void add_value(std::shared_ptr&& value) + { + auto writer = std::make_shared(shared_from_this()); + + ValueTokenStream(Value { *value }).visit(writer); + } + + void reserve(std::size_t /* count */) + { + } + void start_object() { _writer.StartObject(); } - void add_member(const std::string& key) + void add_member(std::string&& key) { _writer.Key(key.c_str()); } @@ -44,36 +55,50 @@ class StringWriter _writer.StartArray(); } - void end_arrary() + void end_array() { _writer.EndArray(); } - void write_null() + void add_null() { _writer.Null(); } - void write_string(const std::string& value) + void add_string(std::string&& value) { _writer.String(value.c_str()); } - void write_bool(bool value) + void add_enum(std::string&& value) + { + add_string(std::move(value)); + } + + void add_id(IdType&& value) + { + add_string(value.release()); + } + + void add_bool(bool value) { _writer.Bool(value); } - void write_int(int value) + void add_int(int value) { _writer.Int(value); } - void write_float(double value) + void add_float(double value) { _writer.Double(value); } + void complete() + { + } + private: rapidjson::Writer _writer; }; @@ -81,9 +106,10 @@ class StringWriter std::string toJSON(Value&& response) { rapidjson::StringBuffer buffer; - Writer writer { std::make_unique(buffer) }; + auto writer = std::make_shared(std::make_shared(buffer)); + + ValueTokenStream(std::move(response)).visit(writer); - writer.write(std::move(response)); return buffer.GetString(); } diff --git a/src/RequestLoader.cpp b/src/RequestLoader.cpp index 4b36020c..e4d3d7c4 100644 --- a/src/RequestLoader.cpp +++ b/src/RequestLoader.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -139,6 +140,11 @@ const RequestVariableList& RequestLoader::getVariables(const Operation& operatio return operation.variables; } +bool RequestLoader::useSharedTypes() const noexcept +{ + return _requestOptions.sharedTypes; +} + const RequestInputTypeList& RequestLoader::getReferencedInputTypes( const Operation& operation) const noexcept { @@ -162,7 +168,7 @@ std::string RequestLoader::getInputCppType( const RequestSchemaType& inputType, const TypeModifierStack& modifiers) const noexcept { bool nonNull = true; - size_t templateCount = 0; + std::size_t templateCount = 0; std::ostringstream cppType; for (auto modifier : modifiers) @@ -211,7 +217,7 @@ std::string RequestLoader::getInputCppType( cppType << _schemaLoader.getCppType(inputType->name()); - for (size_t i = 0; i < templateCount; ++i) + for (std::size_t i = 0; i < templateCount; ++i) { cppType << R"cpp(>)cpp"; } @@ -223,7 +229,7 @@ std::string RequestLoader::getOutputCppType( std::string_view outputCppType, const TypeModifierStack& modifiers) noexcept { bool nonNull = true; - size_t templateCount = 0; + std::size_t templateCount = 0; std::ostringstream cppType; for (auto modifier : modifiers) @@ -260,7 +266,7 @@ std::string RequestLoader::getOutputCppType( cppType << outputCppType; - for (size_t i = 0; i < templateCount; ++i) + for (std::size_t i = 0; i < templateCount; ++i) { cppType << R"cpp(>)cpp"; } @@ -417,8 +423,7 @@ void RequestLoader::addTypesToSchema() { std::vector values(enumType.values.size()); - std::transform(enumType.values.cbegin(), - enumType.values.cend(), + std::ranges::transform(enumType.values, values.begin(), [](const EnumValueType& value) noexcept { return schema::EnumValueType { @@ -440,8 +445,7 @@ void RequestLoader::addTypesToSchema() { std::vector> fields(inputType.fields.size()); - std::transform(inputType.fields.cbegin(), - inputType.fields.cend(), + std::ranges::transform(inputType.fields, fields.begin(), [this](const InputField& field) noexcept { return schema::InputValue::Make(field.name, @@ -462,8 +466,7 @@ void RequestLoader::addTypesToSchema() { std::vector> options(unionType.options.size()); - std::transform(unionType.options.cbegin(), - unionType.options.cend(), + std::ranges::transform(unionType.options, options.begin(), [this](std::string_view option) noexcept { return _schema->LookupType(option); @@ -481,15 +484,13 @@ void RequestLoader::addTypesToSchema() { std::vector> fields(interfaceType.fields.size()); - std::transform(interfaceType.fields.cbegin(), - interfaceType.fields.cend(), + std::ranges::transform(interfaceType.fields, fields.begin(), [this](const OutputField& field) noexcept { std::vector> arguments( field.arguments.size()); - std::transform(field.arguments.cbegin(), - field.arguments.cend(), + std::ranges::transform(field.arguments, arguments.begin(), [this](const InputField& argument) noexcept { return schema::InputValue::Make(argument.name, @@ -518,8 +519,7 @@ void RequestLoader::addTypesToSchema() std::vector> interfaces( objectType.interfaces.size()); - std::transform(objectType.interfaces.cbegin(), - objectType.interfaces.cend(), + std::ranges::transform(objectType.interfaces, interfaces.begin(), [&interfaceTypes](std::string_view interfaceName) noexcept { return interfaceTypes[interfaceName]; @@ -532,15 +532,13 @@ void RequestLoader::addTypesToSchema() { std::vector> fields(objectType.fields.size()); - std::transform(objectType.fields.cbegin(), - objectType.fields.cend(), + std::ranges::transform(objectType.fields, fields.begin(), [this](const OutputField& field) noexcept { std::vector> arguments( field.arguments.size()); - std::transform(field.arguments.cbegin(), - field.arguments.cend(), + std::ranges::transform(field.arguments, arguments.begin(), [this](const InputField& argument) noexcept { return schema::InputValue::Make(argument.name, @@ -564,23 +562,20 @@ void RequestLoader::addTypesToSchema() { std::vector locations(directive.locations.size()); - std::transform(directive.locations.cbegin(), - directive.locations.cend(), + std::ranges::transform(directive.locations, locations.begin(), [](std::string_view locationName) noexcept { response::Value locationValue(response::Type::EnumValue); locationValue.set(std::string { locationName }); - return service::Argument::convert( - locationValue); + return service::Argument::convert(locationValue); }); std::vector> arguments( directive.arguments.size()); - std::transform(directive.arguments.cbegin(), - directive.arguments.cend(), + std::ranges::transform(directive.arguments, arguments.begin(), [this](const InputField& argument) noexcept { return schema::InputValue::Make(argument.name, @@ -624,11 +619,11 @@ RequestSchemaType RequestLoader::getSchemaType( { bool nonNull = true; - for (auto itr = modifiers.crbegin(); itr != modifiers.crend(); ++itr) + for (const auto modifier : std::views::all(modifiers) | std::views::reverse) { if (nonNull) { - switch (*itr) + switch (modifier) { case service::TypeModifier::None: case service::TypeModifier::List: @@ -643,7 +638,7 @@ RequestSchemaType RequestLoader::getSchemaType( } } - switch (*itr) + switch (modifier) { case service::TypeModifier::None: { @@ -701,7 +696,8 @@ std::string_view RequestLoader::trimWhitespace(std::string_view content) noexcep if (skip >= 0 && length >= skip) { - content = content.substr(static_cast(skip), static_cast(length - skip)); + content = + content.substr(static_cast(skip), static_cast(length - skip)); } return content; @@ -742,16 +738,14 @@ void RequestLoader::findOperation() if (_operations.empty()) { - std::ostringstream message; - - message << "Missing operation"; + auto message = "Missing operation"s; if (_requestOptions.operationName && !_requestOptions.operationName->empty()) { - message << " name: " << *_requestOptions.operationName; + message += std::format(" name: {}", *_requestOptions.operationName); } - throw service::schema_exception { { message.str() } }; + throw service::schema_exception { { std::move(message) } }; } std::list errors; @@ -773,17 +767,15 @@ void RequestLoader::findOperation() if (!operation.responseType.type) { - std::ostringstream message; const auto position = operation.operation->begin(); - - message << "Unsupported operation type: " << operation.type; + auto message = std::format("Unsupported operation type: {}", operation.type); if (!operation.name.empty()) { - message << " name: " << operation.name; + message += std::format(" name: {}", operation.name); } - service::schema_error error { message.str(), + service::schema_error error { std::move(message), service::schema_location { position.line, position.column } }; errors.push_back(std::move(error)); @@ -851,12 +843,11 @@ void RequestLoader::collectVariables(Operation& operation) noexcept && variable.defaultValue.type() == response::Type::Null && (modifiers.empty() || modifiers.front() != service::TypeModifier::Nullable)) { - std::ostringstream error; - - error << "Expected Non-Null default value for variable name: " << variable.name; + auto error = std::format("Expected Non-Null default value for variable name: {}", + variable.name); throw service::schema_exception { - { service::schema_error { error.str(), std::move(defaultValueLocation) } } + { service::schema_error { std::move(error), std::move(defaultValueLocation) } } }; } @@ -910,29 +901,26 @@ void RequestLoader::reorderInputTypeDependencies(Operation& operation) } // Build the dependency list for each input type. - std::for_each(operation.referencedInputTypes.begin(), - operation.referencedInputTypes.end(), - [](RequestInputType& entry) noexcept { - const auto& fields = entry.type->inputFields(); - std::for_each(fields.begin(), - fields.end(), - [&entry](const std::shared_ptr& field) noexcept { - const auto [inputType, modifiers] = unwrapSchemaType(field->type().lock()); - - if (inputType->kind() == introspection::TypeKind::INPUT_OBJECT) + std::ranges::for_each(operation.referencedInputTypes, [](RequestInputType& entry) noexcept { + const auto& fields = entry.type->inputFields(); + std::ranges::for_each(fields, + [&entry](const std::shared_ptr& field) noexcept { + const auto [inputType, modifiers] = unwrapSchemaType(field->type().lock()); + + if (inputType->kind() == introspection::TypeKind::INPUT_OBJECT) + { + // https://spec.graphql.org/October2021/#sec-Input-Objects.Circular-References + if (!modifiers.empty() && modifiers.front() != service::TypeModifier::None) { - // https://spec.graphql.org/October2021/#sec-Input-Objects.Circular-References - if (!modifiers.empty() && modifiers.front() != service::TypeModifier::None) - { - entry.declarations.push_back(inputType->name()); - } - else - { - entry.dependencies.insert(inputType->name()); - } + entry.declarations.push_back(inputType->name()); } - }); - }); + else + { + entry.dependencies.insert(inputType->name()); + } + } + }); + }); std::unordered_set handled; auto itr = operation.referencedInputTypes.begin(); @@ -957,11 +945,9 @@ void RequestLoader::reorderInputTypeDependencies(Operation& operation) // input types which are referenced in the request. if (itrDependent == itr) { - std::ostringstream error; + const auto error = std::format("Input object cycle type: {}", itr->type->name()); - error << "Input object cycle type: " << itr->type; - - throw std::logic_error(error.str()); + throw std::logic_error(error); } if (itrDependent != operation.referencedInputTypes.end()) @@ -1193,12 +1179,10 @@ void RequestLoader::SelectionVisitor::visitFragmentSpread(const peg::ast_node& f if (itr == _fragments.end()) { auto position = fragmentSpread.begin(); - std::ostringstream error; - - error << "Unknown fragment name: " << name; + auto error = std::format("Unknown fragment name: {}", name); throw service::schema_exception { - { service::schema_error { error.str(), { position.line, position.column } } } + { service::schema_error { std::move(error), { position.line, position.column } } } }; } diff --git a/src/Schema.cpp b/src/Schema.cpp index 2e4ac1ba..f40f2a38 100644 --- a/src/Schema.cpp +++ b/src/Schema.cpp @@ -17,6 +17,606 @@ Schema::Schema(bool noIntrospection, std::string_view description) { } +std::shared_ptr Schema::StitchSchema(const std::shared_ptr& added) const +{ + const auto noIntrospection = _noIntrospection || added->_noIntrospection; + const auto description = _description.empty() ? added->_description : _description; + auto schema = std::make_shared(noIntrospection, description); + + if (_types.empty()) + { + schema->_query = added->_query; + schema->_mutation = added->_mutation; + schema->_subscription = added->_subscription; + schema->_typeMap = added->_typeMap; + schema->_types = added->_types; + schema->_directives = added->_directives; + } + else if (added->_types.empty()) + { + schema->_query = _query; + schema->_mutation = _mutation; + schema->_subscription = _subscription; + schema->_typeMap = _typeMap; + schema->_types = _types; + schema->_directives = _directives; + } + else + { + internal::string_view_map> objectTypes; + internal::string_view_map> interfaceTypes; + internal::string_view_map> unionTypes; + internal::string_view_map> enumTypes; + internal::string_view_map> inputObjectTypes; + + for (const auto& entry : _types) + { + const auto& [name, originalType] = entry; + + switch (originalType->kind()) + { + case introspection::TypeKind::SCALAR: + { + const auto originalDescription = originalType->description(); + const auto originalSpecifiedByURL = originalType->specifiedByURL(); + const auto itrAdded = added->_typeMap.find(name); + auto scalarType = ScalarType::Make(name, + originalDescription.empty() && itrAdded != added->_typeMap.end() + ? added->_types[itrAdded->second].second->description() + : originalDescription, + originalSpecifiedByURL.empty() && itrAdded != added->_typeMap.end() + ? added->_types[itrAdded->second].second->description() + : originalSpecifiedByURL); + + schema->AddType(name, std::move(scalarType)); + break; + } + + case introspection::TypeKind::OBJECT: + { + const auto originalDescription = originalType->description(); + const auto itrAdded = added->_typeMap.find(name); + auto objectType = ObjectType::Make(name, + originalDescription.empty() && itrAdded != added->_typeMap.end() + ? added->_types[itrAdded->second].second->description() + : originalDescription); + + schema->AddType(name, objectType); + objectTypes[name] = std::move(objectType); + break; + } + + case introspection::TypeKind::INTERFACE: + { + const auto originalDescription = originalType->description(); + const auto itrAdded = added->_typeMap.find(name); + auto interfaceType = InterfaceType::Make(name, + originalDescription.empty() && itrAdded != added->_typeMap.end() + ? added->_types[itrAdded->second].second->description() + : originalDescription); + + schema->AddType(name, interfaceType); + interfaceTypes[name] = std::move(interfaceType); + break; + } + + case introspection::TypeKind::UNION: + { + const auto originalDescription = originalType->description(); + const auto itrAdded = added->_typeMap.find(name); + auto unionType = UnionType::Make(name, + originalDescription.empty() && itrAdded != added->_typeMap.end() + ? added->_types[itrAdded->second].second->description() + : originalDescription); + + schema->AddType(name, unionType); + unionTypes[name] = std::move(unionType); + break; + } + + case introspection::TypeKind::ENUM: + { + const auto originalDescription = originalType->description(); + const auto itrAdded = added->_typeMap.find(name); + auto enumType = EnumType::Make(name, + originalDescription.empty() && itrAdded != added->_typeMap.end() + ? added->_types[itrAdded->second].second->description() + : originalDescription); + + schema->AddType(name, enumType); + enumTypes[name] = std::move(enumType); + break; + } + + case introspection::TypeKind::INPUT_OBJECT: + { + const auto originalDescription = originalType->description(); + const auto itrAdded = added->_typeMap.find(name); + auto inputObjectType = InputObjectType::Make(name, + originalDescription.empty() && itrAdded != added->_typeMap.end() + ? added->_types[itrAdded->second].second->description() + : originalDescription); + + schema->AddType(name, inputObjectType); + inputObjectTypes[name] = std::move(inputObjectType); + break; + } + + case introspection::TypeKind::LIST: + case introspection::TypeKind::NON_NULL: + break; + } + } + + for (const auto& entry : added->_types) + { + const auto& [name, addedType] = entry; + const auto itrOriginal = _typeMap.find(name); + + if (itrOriginal != _typeMap.end()) + { + continue; + } + + switch (addedType->kind()) + { + case introspection::TypeKind::SCALAR: + { + auto scalarType = ScalarType::Make(name, + addedType->description(), + addedType->specifiedByURL()); + + schema->AddType(name, std::move(scalarType)); + break; + } + + case introspection::TypeKind::OBJECT: + { + auto objectType = ObjectType::Make(name, addedType->description()); + + schema->AddType(name, objectType); + objectTypes[name] = std::move(objectType); + break; + } + + case introspection::TypeKind::INTERFACE: + { + auto interfaceType = InterfaceType::Make(name, addedType->description()); + + schema->AddType(name, interfaceType); + interfaceTypes[name] = std::move(interfaceType); + break; + } + + case introspection::TypeKind::UNION: + { + auto unionType = UnionType::Make(name, addedType->description()); + + schema->AddType(name, unionType); + unionTypes[name] = std::move(unionType); + break; + } + + case introspection::TypeKind::ENUM: + { + auto enumType = EnumType::Make(name, addedType->description()); + + schema->AddType(name, enumType); + enumTypes[name] = std::move(enumType); + break; + } + + case introspection::TypeKind::INPUT_OBJECT: + { + auto inputObjectType = InputObjectType::Make(name, addedType->description()); + + schema->AddType(name, inputObjectType); + inputObjectTypes[name] = std::move(inputObjectType); + break; + } + + case introspection::TypeKind::LIST: + case introspection::TypeKind::NON_NULL: + break; + } + } + + for (const auto& entry : enumTypes) + { + const auto& [name, stitchedType] = entry; + const auto itrOriginal = _typeMap.find(name); + const auto itrAdded = added->_typeMap.find(name); + internal::string_view_set names; + std::vector stitchedValues; + + if (itrOriginal != _typeMap.end()) + { + const auto& originalType = _types[itrOriginal->second].second; + const auto& enumValues = originalType->enumValues(); + + for (const auto& value : enumValues) + { + names.emplace(value->name()); + stitchedValues.push_back({ + value->name(), + value->description(), + value->deprecationReason(), + }); + } + } + + if (itrAdded != added->_typeMap.end()) + { + const auto& addedType = added->_types[itrAdded->second].second; + const auto& enumValues = addedType->enumValues(); + + for (const auto& value : enumValues) + { + if (!names.emplace(value->name()).second) + { + continue; + } + + stitchedValues.push_back({ + value->name(), + value->description(), + value->deprecationReason(), + }); + } + } + + stitchedType->AddEnumValues(std::move(stitchedValues)); + } + + for (const auto& entry : inputObjectTypes) + { + const auto& [name, stitchedType] = entry; + const auto itrOriginal = _typeMap.find(name); + const auto itrAdded = added->_typeMap.find(name); + internal::string_view_set names; + std::vector> stitchedValues; + + if (itrOriginal != _typeMap.end()) + { + const auto& originalType = _types[itrOriginal->second].second; + const auto& inputObjectValues = originalType->inputFields(); + + for (const auto& value : inputObjectValues) + { + names.emplace(value->name()); + stitchedValues.push_back(InputValue::Make(value->name(), + value->description(), + schema->StitchFieldType(value->type().lock()), + value->defaultValue())); + } + } + + if (itrAdded != added->_typeMap.end()) + { + const auto& addedType = added->_types[itrAdded->second].second; + const auto& inputObjectValues = addedType->inputFields(); + + for (const auto& value : inputObjectValues) + { + if (!names.emplace(value->name()).second) + { + continue; + } + + stitchedValues.push_back(InputValue::Make(value->name(), + value->description(), + schema->StitchFieldType(value->type().lock()), + value->defaultValue())); + } + } + + stitchedType->AddInputValues(std::move(stitchedValues)); + } + + for (const auto& entry : interfaceTypes) + { + const auto& [name, stitchedType] = entry; + const auto itrOriginal = _typeMap.find(name); + const auto itrAdded = added->_typeMap.find(name); + internal::string_view_set names; + std::vector> stitchedFields; + + if (itrOriginal != _typeMap.end()) + { + const auto& originalType = _types[itrOriginal->second].second; + const auto& interfaceFields = originalType->fields(); + + for (const auto& interfaceField : interfaceFields) + { + std::vector> stitchedArgs; + + for (const auto& arg : interfaceField->args()) + { + stitchedArgs.push_back(InputValue::Make(arg->name(), + arg->description(), + schema->StitchFieldType(arg->type().lock()), + arg->defaultValue())); + } + + names.emplace(interfaceField->name()); + stitchedFields.push_back(Field::Make(interfaceField->name(), + interfaceField->description(), + interfaceField->deprecationReason(), + schema->StitchFieldType(interfaceField->type().lock()), + std::move(stitchedArgs))); + } + } + + if (itrAdded != added->_typeMap.end()) + { + const auto& addedType = added->_types[itrAdded->second].second; + const auto& interfaceFields = addedType->fields(); + + for (const auto& interfaceField : interfaceFields) + { + if (!names.emplace(interfaceField->name()).second) + { + continue; + } + + std::vector> stitchedArgs; + + for (const auto& arg : interfaceField->args()) + { + stitchedArgs.push_back(InputValue::Make(arg->name(), + arg->description(), + schema->StitchFieldType(arg->type().lock()), + arg->defaultValue())); + } + + stitchedFields.push_back(Field::Make(interfaceField->name(), + interfaceField->description(), + interfaceField->deprecationReason(), + schema->StitchFieldType(interfaceField->type().lock()), + std::move(stitchedArgs))); + } + } + + stitchedType->AddFields(std::move(stitchedFields)); + } + + for (const auto& entry : unionTypes) + { + const auto& [name, stitchedType] = entry; + const auto itrOriginal = _typeMap.find(name); + const auto itrAdded = added->_typeMap.find(name); + internal::string_view_set names; + std::vector> stitchedValues; + + if (itrOriginal != _typeMap.end()) + { + const auto& originalType = _types[itrOriginal->second].second; + const auto& possibleTypes = originalType->possibleTypes(); + + for (const auto& possibleType : possibleTypes) + { + const auto possible = possibleType.lock(); + + names.emplace(possible->name()); + stitchedValues.push_back(schema->LookupType(possible->name())); + } + } + + if (itrAdded != added->_typeMap.end()) + { + const auto& addedType = added->_types[itrAdded->second].second; + const auto& possibleTypes = addedType->possibleTypes(); + + for (const auto& possibleType : possibleTypes) + { + const auto possible = possibleType.lock(); + + if (!names.emplace(possible->name()).second) + { + continue; + } + + stitchedValues.push_back(schema->LookupType(possible->name())); + } + } + + stitchedType->AddPossibleTypes(std::move(stitchedValues)); + } + + for (const auto& entry : objectTypes) + { + const auto& [name, stitchedType] = entry; + const auto itrOriginal = _typeMap.find(name); + const auto itrAdded = added->_typeMap.find(name); + internal::string_view_set interfaceNames; + internal::string_view_set fieldNames; + std::vector> stitchedInterfaces; + std::vector> stitchedValues; + + if (itrOriginal != _typeMap.end()) + { + const auto& originalType = _types[itrOriginal->second].second; + const auto& objectInterfaces = originalType->interfaces(); + + for (const auto& interfaceType : objectInterfaces) + { + interfaceNames.emplace(interfaceType->name()); + stitchedInterfaces.push_back(interfaceTypes[interfaceType->name()]); + } + + const auto& objectFields = originalType->fields(); + + for (const auto& objectField : objectFields) + { + std::vector> stitchedArgs; + + for (const auto& arg : objectField->args()) + { + stitchedArgs.push_back(InputValue::Make(arg->name(), + arg->description(), + schema->StitchFieldType(arg->type().lock()), + arg->defaultValue())); + } + + fieldNames.emplace(objectField->name()); + stitchedValues.push_back(Field::Make(objectField->name(), + objectField->description(), + objectField->deprecationReason(), + schema->StitchFieldType(objectField->type().lock()), + std::move(stitchedArgs))); + } + } + + if (itrAdded != added->_typeMap.end()) + { + const auto& addedType = added->_types[itrAdded->second].second; + const auto& objectInterfaces = addedType->interfaces(); + + for (const auto& interfaceType : objectInterfaces) + { + if (!interfaceNames.emplace(interfaceType->name()).second) + { + continue; + } + + stitchedInterfaces.push_back(interfaceTypes[interfaceType->name()]); + } + + const auto& objectFields = addedType->fields(); + + for (const auto& objectField : objectFields) + { + if (!fieldNames.emplace(objectField->name()).second) + { + continue; + } + + std::vector> stitchedArgs; + + for (const auto& arg : objectField->args()) + { + stitchedArgs.push_back(InputValue::Make(arg->name(), + arg->description(), + schema->StitchFieldType(arg->type().lock()), + arg->defaultValue())); + } + + stitchedValues.push_back(Field::Make(objectField->name(), + objectField->description(), + objectField->deprecationReason(), + schema->StitchFieldType(objectField->type().lock()), + std::move(stitchedArgs))); + } + } + + stitchedType->AddInterfaces(std::move(stitchedInterfaces)); + stitchedType->AddFields(std::move(stitchedValues)); + } + + internal::string_view_set directiveNames; + std::vector> stitchedDirectives; + + for (const auto& originalDirective : _directives) + { + if (!directiveNames.emplace(originalDirective->name()).second) + { + continue; + } + + std::vector> stitchedArgs; + + for (const auto& arg : originalDirective->args()) + { + stitchedArgs.push_back(InputValue::Make(arg->name(), + arg->description(), + schema->StitchFieldType(arg->type().lock()), + arg->defaultValue())); + } + + stitchedDirectives.push_back(Directive::Make(originalDirective->name(), + originalDirective->description(), + std::vector { originalDirective->locations() }, + std::move(stitchedArgs), + originalDirective->isRepeatable())); + } + + for (const auto& addedDirective : added->_directives) + { + if (!directiveNames.emplace(addedDirective->name()).second) + { + continue; + } + + std::vector> stitchedArgs; + + for (const auto& arg : addedDirective->args()) + { + stitchedArgs.push_back(InputValue::Make(arg->name(), + arg->description(), + schema->StitchFieldType(arg->type().lock()), + arg->defaultValue())); + } + + stitchedDirectives.push_back(Directive::Make(addedDirective->name(), + addedDirective->description(), + std::vector { addedDirective->locations() }, + std::move(stitchedArgs), + addedDirective->isRepeatable())); + } + + for (auto& directive : stitchedDirectives) + { + schema->AddDirective(std::move(directive)); + } + + if (_query) + { + schema->AddQueryType(objectTypes[_query->name()]); + } + else if (added->_query) + { + schema->AddQueryType(objectTypes[added->_query->name()]); + } + + if (_mutation) + { + schema->AddMutationType(objectTypes[_mutation->name()]); + } + else if (added->_mutation) + { + schema->AddMutationType(objectTypes[added->_mutation->name()]); + } + + if (_subscription) + { + schema->AddSubscriptionType(objectTypes[_subscription->name()]); + } + else if (added->_subscription) + { + schema->AddSubscriptionType(objectTypes[added->_subscription->name()]); + } + } + + return schema; +} + +std::shared_ptr Schema::StitchFieldType(std::shared_ptr fieldType) +{ + switch (fieldType->kind()) + { + case introspection::TypeKind::LIST: + return WrapType(introspection::TypeKind::LIST, + StitchFieldType(fieldType->ofType().lock())); + + case introspection::TypeKind::NON_NULL: + return WrapType(introspection::TypeKind::NON_NULL, + StitchFieldType(fieldType->ofType().lock())); + + default: + return LookupType(fieldType->name()); + } +} + void Schema::AddQueryType(std::shared_ptr query) { _query = query; @@ -371,12 +971,9 @@ EnumType::EnumType(init&& params) void EnumType::AddEnumValues(std::vector&& enumValues) { _enumValues.resize(enumValues.size()); - std::transform(enumValues.begin(), - enumValues.end(), - _enumValues.begin(), - [](const auto& value) { - return EnumValue::Make(value.value, value.description, value.deprecationReason); - }); + std::ranges::transform(enumValues, _enumValues.begin(), [](const auto& value) { + return EnumValue::Make(value.value, value.description, value.deprecationReason); + }); } std::string_view EnumType::name() const noexcept diff --git a/src/SchemaGenerator.cpp b/src/SchemaGenerator.cpp index b9aeb3c7..78776ed2 100644 --- a/src/SchemaGenerator.cpp +++ b/src/SchemaGenerator.cpp @@ -4,6 +4,8 @@ #include "SchemaGenerator.h" #include "GeneratorUtil.h" +#include "graphqlservice/internal/Version.h" + #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 26495) @@ -16,10 +18,14 @@ #pragma warning(pop) #endif // _MSC_VER +#include #include +#include #include +#include #include #include +#include #include #include #include @@ -34,8 +40,12 @@ Generator::Generator(SchemaOptions&& schemaOptions, GeneratorOptions&& options) , _options(std::move(options)) , _headerDir(getHeaderDir()) , _sourceDir(getSourceDir()) - , _headerPath(getHeaderPath()) - , _sourcePath(getSourcePath()) + , _schemaHeaderPath(getSchemaHeaderPath()) + , _schemaModulePath(getSchemaModulePath()) + , _schemaSourcePath(getSchemaSourcePath()) + , _sharedTypesHeaderPath(getSharedTypesHeaderPath()) + , _sharedTypesModulePath(getSharedTypesModulePath()) + , _sharedTypesSourcePath(getSharedTypesSourcePath()) { } @@ -45,71 +55,594 @@ std::string Generator::getHeaderDir() const noexcept { return std::filesystem::path { _options.paths.headerPath }.string(); } - else + else + { + return {}; + } +} + +std::string Generator::getSourceDir() const noexcept +{ + if (!_options.paths.sourcePath.empty()) + { + return std::filesystem::path(_options.paths.sourcePath).string(); + } + else + { + return {}; + } +} + +std::string Generator::getSchemaHeaderPath() const noexcept +{ + std::filesystem::path fullPath { _headerDir }; + + fullPath /= (std::string { _loader.getFilenamePrefix() } + "Schema.h"); + return fullPath.string(); +} + +std::string Generator::getSchemaModulePath() const noexcept +{ + std::filesystem::path fullPath { _headerDir }; + + fullPath /= (std::string { _loader.getFilenamePrefix() } + "Schema.ixx"); + return fullPath.string(); +} + +std::string Generator::getSchemaSourcePath() const noexcept +{ + std::filesystem::path fullPath { _sourceDir }; + + fullPath /= (std::string { _loader.getFilenamePrefix() } + "Schema.cpp"); + return fullPath.string(); +} + +std::string Generator::getSharedTypesHeaderPath() const noexcept +{ + std::filesystem::path fullPath { _headerDir }; + + fullPath /= (std::string { _loader.getFilenamePrefix() } + "SharedTypes.h"); + return fullPath.string(); +} + +std::string Generator::getSharedTypesModulePath() const noexcept +{ + std::filesystem::path fullPath { _headerDir }; + + fullPath /= (std::string { _loader.getFilenamePrefix() } + "SharedTypes.ixx"); + return fullPath.string(); +} + +std::string Generator::getSharedTypesSourcePath() const noexcept +{ + std::filesystem::path fullPath { _sourceDir }; + + fullPath /= (std::string { _loader.getFilenamePrefix() } + "SharedTypes.cpp"); + return fullPath.string(); +} + +std::vector Generator::Build() const noexcept +{ + std::vector builtFiles; + + if (outputSharedTypesHeader() && _options.verbose) + { + builtFiles.push_back(_sharedTypesHeaderPath); + } + + if (outputSharedTypesModule() && _options.verbose) + { + builtFiles.push_back(_sharedTypesModulePath); + } + + if (outputSchemaHeader() && _options.verbose) + { + builtFiles.push_back(_schemaHeaderPath); + } + + if (outputSchemaModule() && _options.verbose) + { + builtFiles.push_back(_schemaModulePath); + } + + if (outputSharedTypesSource()) + { + builtFiles.push_back(_sharedTypesSourcePath); + } + + if (outputSchemaSource()) + { + builtFiles.push_back(_schemaSourcePath); + } + + auto separateFiles = outputSeparateFiles(); + + for (auto& file : separateFiles) + { + builtFiles.push_back(std::move(file)); + } + + return builtFiles; +} + +bool Generator::outputSchemaHeader() const noexcept +{ + std::ofstream headerFile(_schemaHeaderPath, std::ios_base::trunc); + IncludeGuardScope includeGuard { headerFile, + std::filesystem::path(_schemaHeaderPath).filename().string() }; + + headerFile << R"cpp(#include "graphqlservice/GraphQLResponse.h" +#include "graphqlservice/GraphQLService.h" + +)cpp"; + + if (_loader.isIntrospection()) + { + headerFile << R"cpp(#include "graphqlservice/internal/DllExports.h" +)cpp"; + } + + headerFile << R"cpp(#include "graphqlservice/internal/Version.h" +#include "graphqlservice/internal/Schema.h" +)cpp"; + + if (!_loader.getEnumTypes().empty() || !_loader.getInputTypes().empty()) + { + headerFile << R"cpp( +#include ")cpp" << _loader.getFilenamePrefix() + << R"cpp(SharedTypes.h" +)cpp"; + } + + headerFile << R"cpp( +#include +#include +#include +#include + +// Check if the library version is compatible with schemagen )cpp" + << graphql::internal::MajorVersion << R"cpp(.)cpp" << graphql::internal::MinorVersion + << R"cpp(.0 +static_assert(graphql::internal::MajorVersion == )cpp" + << graphql::internal::MajorVersion + << R"cpp(, "regenerate with schemagen: major version mismatch"); +static_assert(graphql::internal::MinorVersion == )cpp" + << graphql::internal::MinorVersion + << R"cpp(, "regenerate with schemagen: minor version mismatch"); + +)cpp"; + + const auto schemaNamespace = std::format(R"cpp(graphql::{})cpp", _loader.getSchemaNamespace()); + NamespaceScope schemaNamespaceScope { headerFile, schemaNamespace }; + NamespaceScope objectNamespace { headerFile, "object", true }; + PendingBlankLine pendingSeparator { headerFile }; + + if (!_loader.getInterfaceTypes().empty()) + { + objectNamespace.enter(); + headerFile << std::endl; + + // Forward declare all of the interface types + for (const auto& interfaceType : _loader.getInterfaceTypes()) + { + headerFile << R"cpp(class )cpp" << interfaceType.cppType << R"cpp(; +)cpp"; + } + + headerFile << std::endl; + } + + if (!_loader.getUnionTypes().empty()) + { + if (objectNamespace.enter()) + { + headerFile << std::endl; + } + + // Forward declare all of the union types + for (const auto& unionType : _loader.getUnionTypes()) + { + headerFile << R"cpp(class )cpp" << unionType.cppType << R"cpp(; +)cpp"; + } + + headerFile << std::endl; + } + + if (!_loader.getObjectTypes().empty()) + { + if (_loader.isIntrospection()) + { + if (objectNamespace.exit()) + { + headerFile << std::endl; + } + + // Forward declare all of the concrete types for the Introspection schema + for (const auto& objectType : _loader.getObjectTypes()) + { + headerFile << R"cpp(class )cpp" << objectType.cppType << R"cpp(; +)cpp"; + } + + headerFile << std::endl; + } + + if (objectNamespace.enter()) + { + headerFile << std::endl; + } + + // Forward declare all of the object types + for (const auto& objectType : _loader.getObjectTypes()) + { + headerFile << R"cpp(class )cpp" << objectType.cppType << R"cpp(; +)cpp"; + } + + headerFile << std::endl; + } + + if (objectNamespace.exit()) + { + headerFile << std::endl; + } + + if (!_loader.isIntrospection()) + { + bool hasSubscription = false; + bool firstOperation = true; + + headerFile << R"cpp(class [[nodiscard("unnecessary construction")]] Operations final + : public service::Request +{ +public: + explicit Operations()cpp"; + + for (const auto& operation : _loader.getOperationTypes()) + { + hasSubscription = hasSubscription || operation.operation == service::strSubscription; + + if (!firstOperation) + { + headerFile << R"cpp(, )cpp"; + } + + firstOperation = false; + headerFile << R"cpp(std::shared_ptr )cpp" + << operation.operation; + } + + headerFile << R"cpp(); +)cpp"; + + if (!_loader.getOperationTypes().empty()) + { + firstOperation = true; + + headerFile << R"cpp( + template <)cpp"; + for (const auto& operation : _loader.getOperationTypes()) + { + if (!firstOperation) + { + headerFile << R"cpp(, )cpp"; + } + + firstOperation = false; + headerFile << R"cpp(class T)cpp" << operation.cppType; + + if (hasSubscription && operation.operation == service::strSubscription) + { + headerFile << R"cpp( = service::SubscriptionPlaceholder)cpp"; + } + } + + headerFile << R"cpp(> + explicit Operations()cpp"; + + firstOperation = true; + + for (const auto& operation : _loader.getOperationTypes()) + { + if (!firstOperation) + { + headerFile << R"cpp(, )cpp"; + } + + firstOperation = false; + headerFile << R"cpp(std::shared_ptr )cpp" + << operation.operation; + + if (hasSubscription && operation.operation == service::strSubscription) + { + headerFile << R"cpp( = {})cpp"; + } + } + + headerFile << R"cpp() + : Operations {)cpp"; + + firstOperation = true; + + for (const auto& operation : _loader.getOperationTypes()) + { + if (!firstOperation) + { + headerFile << R"cpp(,)cpp"; + } + + firstOperation = false; + + if (hasSubscription && operation.operation == service::strSubscription) + { + headerFile << R"cpp( + )cpp" << operation.operation + << R"cpp( ? std::make_shared(std::move()cpp" << operation.operation + << R"cpp()) : std::shared_ptr {})cpp"; + } + else + { + headerFile << R"cpp( + std::make_shared(std::move()cpp" + << operation.operation << R"cpp()))cpp"; + } + } + + headerFile << R"cpp( + } + { + } +)cpp"; + } + + headerFile << R"cpp( +private: +)cpp"; + + for (const auto& operation : _loader.getOperationTypes()) + { + headerFile << R"cpp( std::shared_ptr _)cpp" << operation.operation << R"cpp(; +)cpp"; + } + + headerFile << R"cpp(}; + +)cpp"; + } + + if (!_loader.getInterfaceTypes().empty()) + { + for (const auto& interfaceType : _loader.getInterfaceTypes()) + { + headerFile << R"cpp(void Add)cpp" << interfaceType.cppType + << R"cpp(Details(const std::shared_ptr& type)cpp" + << interfaceType.cppType + << R"cpp(, const std::shared_ptr& schema); +)cpp"; + } + + headerFile << std::endl; + } + + if (!_loader.getUnionTypes().empty()) + { + for (const auto& unionType : _loader.getUnionTypes()) + { + headerFile << R"cpp(void Add)cpp" << unionType.cppType + << R"cpp(Details(const std::shared_ptr& type)cpp" + << unionType.cppType + << R"cpp(, const std::shared_ptr& schema); +)cpp"; + } + + headerFile << std::endl; + } + + if (!_loader.getObjectTypes().empty()) + { + for (const auto& objectType : _loader.getObjectTypes()) + { + headerFile << R"cpp(void Add)cpp" << objectType.cppType + << R"cpp(Details(const std::shared_ptr& type)cpp" + << objectType.cppType + << R"cpp(, const std::shared_ptr& schema); +)cpp"; + } + + headerFile << std::endl; + } + + if (_loader.isIntrospection()) + { + headerFile + << R"cpp(GRAPHQLSERVICE_EXPORT void AddTypesToSchema(const std::shared_ptr& schema); + +)cpp"; + } + else + { + headerFile << R"cpp(std::shared_ptr GetSchema(); + +)cpp"; + } + + return true; +} + +bool Generator::outputSchemaModule() const noexcept +{ + std::ofstream moduleFile(_schemaModulePath, std::ios_base::trunc); + const auto schemaNamespace = std::format(R"cpp(graphql::{})cpp", _loader.getSchemaNamespace()); + + moduleFile << R"cpp(// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include ")cpp" << _loader.getFilenamePrefix() + << + R"cpp(Schema.h" + +export module GraphQL.)cpp" + << _loader.getFilenamePrefix() << R"cpp(.)cpp" << _loader.getFilenamePrefix() << + R"cpp(Schema; +)cpp"; + + if (!_loader.getEnumTypes().empty() || !_loader.getInputTypes().empty()) + { + moduleFile << R"cpp( +export import GraphQL.)cpp" + << _loader.getFilenamePrefix() << R"cpp(.)cpp" << _loader.getFilenamePrefix() << + R"cpp(SharedTypes; +)cpp"; + } + + PendingBlankLine pendingSeparator { moduleFile }; + + if (!_loader.getInterfaceTypes().empty()) + { + pendingSeparator.reset(); + + for (const auto& interfaceType : _loader.getInterfaceTypes()) + { + moduleFile << R"cpp(export import GraphQL.)cpp" << _loader.getFilenamePrefix() + << R"cpp(.)cpp" << interfaceType.cppType << R"cpp(Object; +)cpp"; + } + } + + if (!_loader.getUnionTypes().empty()) + { + pendingSeparator.reset(); + + for (const auto& unionType : _loader.getUnionTypes()) + { + moduleFile << R"cpp(export import GraphQL.)cpp" << _loader.getFilenamePrefix() + << R"cpp(.)cpp" << unionType.cppType << R"cpp(Object; +)cpp"; + } + } + + if (!_loader.getObjectTypes().empty()) + { + pendingSeparator.reset(); + + for (const auto& objectType : _loader.getObjectTypes()) + { + moduleFile << R"cpp(export import GraphQL.)cpp" << _loader.getFilenamePrefix() + << R"cpp(.)cpp" << objectType.cppType << R"cpp(Object; +)cpp"; + } + } + + moduleFile << R"cpp( +export )cpp"; + + NamespaceScope graphqlNamespace { moduleFile, schemaNamespace }; + + pendingSeparator.add(); + + if (!_loader.isIntrospection()) + { + pendingSeparator.reset(); + + moduleFile << R"cpp(using )cpp" << _loader.getSchemaNamespace() << R"cpp(::Operations; + +)cpp"; + } + + if (!_loader.getInterfaceTypes().empty()) + { + pendingSeparator.reset(); + + for (const auto& interfaceType : _loader.getInterfaceTypes()) + { + moduleFile << R"cpp(using )cpp" << _loader.getSchemaNamespace() << R"cpp(::Add)cpp" + << interfaceType.cppType << R"cpp(Details; +)cpp"; + } + } + + if (!_loader.getUnionTypes().empty()) + { + pendingSeparator.reset(); + + for (const auto& unionType : _loader.getUnionTypes()) + { + moduleFile << R"cpp(using )cpp" << _loader.getSchemaNamespace() << R"cpp(::Add)cpp" + << unionType.cppType << R"cpp(Details; +)cpp"; + } + } + + if (!_loader.getObjectTypes().empty()) { - return {}; + pendingSeparator.reset(); + + for (const auto& objectType : _loader.getObjectTypes()) + { + moduleFile << R"cpp(using )cpp" << _loader.getSchemaNamespace() << R"cpp(::Add)cpp" + << objectType.cppType << R"cpp(Details; +)cpp"; + } } -} -std::string Generator::getSourceDir() const noexcept -{ - if (!_options.paths.sourcePath.empty()) + if (_loader.isIntrospection()) { - return std::filesystem::path(_options.paths.sourcePath).string(); + moduleFile << R"cpp( +using )cpp" << _loader.getSchemaNamespace() + << R"cpp(::AddTypesToSchema; + +)cpp"; } else { - return {}; - } -} - -std::string Generator::getHeaderPath() const noexcept -{ - std::filesystem::path fullPath { _headerDir }; - - fullPath /= (std::string { _loader.getFilenamePrefix() } + "Schema.h"); - return fullPath.string(); -} + moduleFile << R"cpp( +using )cpp" << _loader.getSchemaNamespace() + << R"cpp(::GetSchema; -std::string Generator::getSourcePath() const noexcept -{ - std::filesystem::path fullPath { _sourceDir }; +)cpp"; + } - fullPath /= (std::string { _loader.getFilenamePrefix() } + "Schema.cpp"); - return fullPath.string(); + return true; } -std::vector Generator::Build() const noexcept +bool Generator::outputSharedTypesHeader() const noexcept { - std::vector builtFiles; - - if (outputHeader() && _options.verbose) + if (_loader.getEnumTypes().empty() && _loader.getInputTypes().empty()) { - builtFiles.push_back(_headerPath); + return false; } - if (outputSource()) - { - builtFiles.push_back(_sourcePath); - } + std::ofstream headerFile(_sharedTypesHeaderPath, std::ios_base::trunc); + IncludeGuardScope includeGuard { headerFile, + std::filesystem::path(_sharedTypesHeaderPath).filename().string() }; - auto separateFiles = outputSeparateFiles(); + headerFile << R"cpp(#include "graphqlservice/GraphQLResponse.h" - for (auto& file : separateFiles) +)cpp"; + + if (_loader.isIntrospection()) { - builtFiles.push_back(std::move(file)); + headerFile << R"cpp(#include "graphqlservice/internal/DllExports.h" +)cpp"; } - return builtFiles; -} - -bool Generator::outputHeader() const noexcept -{ - std::ofstream headerFile(_headerPath, std::ios_base::trunc); - IncludeGuardScope includeGuard { headerFile, - std::filesystem::path(_headerPath).filename().string() }; + headerFile << R"cpp(#include "graphqlservice/internal/Version.h" - headerFile << R"cpp(#include "graphqlservice/internal/Schema.h" +#include +#include +#include +#include +#include +#include // Check if the library version is compatible with schemagen )cpp" << graphql::internal::MajorVersion << R"cpp(.)cpp" << graphql::internal::MinorVersion @@ -121,32 +654,12 @@ static_assert(graphql::internal::MinorVersion == )cpp" << graphql::internal::MinorVersion << R"cpp(, "regenerate with schemagen: minor version mismatch"); -#include -#include -#include -#include - )cpp"; NamespaceScope graphqlNamespace { headerFile, "graphql" }; NamespaceScope schemaNamespace { headerFile, _loader.getSchemaNamespace() }; - NamespaceScope objectNamespace { headerFile, "object", true }; PendingBlankLine pendingSeparator { headerFile }; - std::string_view queryType; - - if (!_loader.isIntrospection()) - { - for (const auto& operation : _loader.getOperationTypes()) - { - if (operation.operation == service::strQuery) - { - queryType = operation.type; - break; - } - } - } - if (!_loader.getEnumTypes().empty()) { pendingSeparator.reset(); @@ -221,17 +734,14 @@ static_assert(graphql::internal::MinorVersion == )cpp" std::vector> sortedValues( enumType.values.size()); - std::transform(enumType.values.cbegin(), - enumType.values.cend(), + std::ranges::transform(enumType.values, sortedValues.begin(), [](const auto& value) noexcept { return std::make_pair(value.value, value.cppValue); }); - std::sort(sortedValues.begin(), - sortedValues.end(), - [](const auto& lhs, const auto& rhs) noexcept { - return internal::shorter_or_less {}(lhs.first, rhs.first); - }); + std::ranges::sort(sortedValues, [](const auto& lhs, const auto& rhs) noexcept { + return internal::shorter_or_less {}(lhs.first, rhs.first); + }); firstValue = true; @@ -319,346 +829,161 @@ static_assert(graphql::internal::MinorVersion == )cpp" headerFile << R"cpp() noexcept; )cpp" << introspectionExport << inputType.cppType << R"cpp((const )cpp" << inputType.cppType - << R"cpp(& other); - )cpp" << introspectionExport - << inputType.cppType << R"cpp(()cpp" << inputType.cppType - << R"cpp(&& other) noexcept; - ~)cpp" << inputType.cppType - << R"cpp((); - - )cpp" << introspectionExport - << inputType.cppType << R"cpp(& operator=(const )cpp" << inputType.cppType - << R"cpp(& other); - )cpp" << introspectionExport - << inputType.cppType << R"cpp(& operator=()cpp" << inputType.cppType - << R"cpp(&& other) noexcept; -)cpp"; - - firstField = true; - - for (const auto& inputField : inputType.fields) - { - if (firstField) - { - headerFile << std::endl; - } - - firstField = false; - - headerFile << getFieldDeclaration(inputField); - } - headerFile << R"cpp(}; - -)cpp"; - } - } - - if (!_loader.getInterfaceTypes().empty()) - { - objectNamespace.enter(); - headerFile << std::endl; - - // Forward declare all of the interface types - for (const auto& interfaceType : _loader.getInterfaceTypes()) - { - headerFile << R"cpp(class )cpp" << interfaceType.cppType << R"cpp(; -)cpp"; - } - - headerFile << std::endl; - } - - if (!_loader.getUnionTypes().empty()) - { - if (objectNamespace.enter()) - { - headerFile << std::endl; - } - - // Forward declare all of the union types - for (const auto& unionType : _loader.getUnionTypes()) - { - headerFile << R"cpp(class )cpp" << unionType.cppType << R"cpp(; -)cpp"; - } - - headerFile << std::endl; - } - - if (!_loader.getObjectTypes().empty()) - { - if (_loader.isIntrospection()) - { - if (objectNamespace.exit()) - { - headerFile << std::endl; - } - - // Forward declare all of the concrete types for the Introspection schema - for (const auto& objectType : _loader.getObjectTypes()) - { - headerFile << R"cpp(class )cpp" << objectType.cppType << R"cpp(; -)cpp"; - } - - headerFile << std::endl; - } - - if (objectNamespace.enter()) - { - headerFile << std::endl; - } - - // Forward declare all of the object types - for (const auto& objectType : _loader.getObjectTypes()) - { - headerFile << R"cpp(class )cpp" << objectType.cppType << R"cpp(; -)cpp"; - } - - headerFile << std::endl; - } - - if (objectNamespace.exit()) - { - headerFile << std::endl; - } - - if (!_loader.isIntrospection()) - { - bool hasSubscription = false; - bool firstOperation = true; - - headerFile << R"cpp(class [[nodiscard("unnecessary construction")]] Operations final - : public service::Request -{ -public: - explicit Operations()cpp"; - - for (const auto& operation : _loader.getOperationTypes()) - { - hasSubscription = hasSubscription || operation.operation == service::strSubscription; - - if (!firstOperation) - { - headerFile << R"cpp(, )cpp"; - } - - firstOperation = false; - headerFile << R"cpp(std::shared_ptr )cpp" - << operation.operation; - } - - headerFile << R"cpp(); -)cpp"; - - if (!_loader.getOperationTypes().empty()) - { - firstOperation = true; - - headerFile << R"cpp( - template <)cpp"; - for (const auto& operation : _loader.getOperationTypes()) - { - if (!firstOperation) - { - headerFile << R"cpp(, )cpp"; - } - - firstOperation = false; - headerFile << R"cpp(class T)cpp" << operation.cppType; - - if (hasSubscription && operation.operation == service::strSubscription) - { - headerFile << R"cpp( = service::SubscriptionPlaceholder)cpp"; - } - } - - headerFile << R"cpp(> - explicit Operations()cpp"; - - firstOperation = true; - - for (const auto& operation : _loader.getOperationTypes()) - { - if (!firstOperation) - { - headerFile << R"cpp(, )cpp"; - } - - firstOperation = false; - headerFile << R"cpp(std::shared_ptr )cpp" - << operation.operation; - - if (hasSubscription && operation.operation == service::strSubscription) - { - headerFile << R"cpp( = {})cpp"; - } - } + << R"cpp(& other); + )cpp" << introspectionExport + << inputType.cppType << R"cpp(()cpp" << inputType.cppType + << R"cpp(&& other) noexcept; + ~)cpp" << inputType.cppType + << R"cpp((); - headerFile << R"cpp() - : Operations {)cpp"; + )cpp" << introspectionExport + << inputType.cppType << R"cpp(& operator=(const )cpp" << inputType.cppType + << R"cpp(& other); + )cpp" << introspectionExport + << inputType.cppType << R"cpp(& operator=()cpp" << inputType.cppType + << R"cpp(&& other) noexcept; +)cpp"; - firstOperation = true; + firstField = true; - for (const auto& operation : _loader.getOperationTypes()) + for (const auto& inputField : inputType.fields) { - if (!firstOperation) + if (firstField) { - headerFile << R"cpp(,)cpp"; + headerFile << std::endl; } - firstOperation = false; + firstField = false; - if (hasSubscription && operation.operation == service::strSubscription) - { - headerFile << R"cpp( - )cpp" << operation.operation - << R"cpp( ? std::make_shared(std::move()cpp" << operation.operation - << R"cpp()) : std::shared_ptr {})cpp"; - } - else - { - headerFile << R"cpp( - std::make_shared(std::move()cpp" - << operation.operation << R"cpp()))cpp"; - } + headerFile << getFieldDeclaration(inputField); } + headerFile << R"cpp(}; - headerFile << R"cpp( - } - { - } )cpp"; } + } - headerFile << R"cpp( -private: -)cpp"; - - for (const auto& operation : _loader.getOperationTypes()) + if (_loader.isIntrospection()) + { + if (schemaNamespace.exit()) { - headerFile << R"cpp( std::shared_ptr _)cpp" << operation.operation << R"cpp(; -)cpp"; + pendingSeparator.add(); } - headerFile << R"cpp(}; + pendingSeparator.reset(); + NamespaceScope serviceNamespace { headerFile, "service" }; + + headerFile << R"cpp( +#ifdef GRAPHQL_DLLEXPORTS +// Export all of the built-in converters )cpp"; - } - if (!_loader.getInterfaceTypes().empty()) - { - for (const auto& interfaceType : _loader.getInterfaceTypes()) + for (const auto& enumType : _loader.getEnumTypes()) { - headerFile << R"cpp(void Add)cpp" << interfaceType.cppType - << R"cpp(Details(const std::shared_ptr& type)cpp" - << interfaceType.cppType - << R"cpp(, const std::shared_ptr& schema); + headerFile << R"cpp(template <> +GRAPHQLSERVICE_EXPORT )cpp" + << _loader.getSchemaNamespace() << R"cpp(::)cpp" << enumType.cppType + << R"cpp( Argument<)cpp" << _loader.getSchemaNamespace() << R"cpp(::)cpp" + << enumType.cppType << R"cpp(>::convert( + const response::Value& value); +template <> +GRAPHQLSERVICE_EXPORT AwaitableResolver Result<)cpp" + << _loader.getSchemaNamespace() << R"cpp(::)cpp" << enumType.cppType + << R"cpp(>::convert( + AwaitableScalar<)cpp" + << _loader.getSchemaNamespace() << R"cpp(::)cpp" << enumType.cppType + << R"cpp(> result, ResolverParams&& params); +template <> +GRAPHQLSERVICE_EXPORT void Result<)cpp" + << _loader.getSchemaNamespace() << R"cpp(::)cpp" << enumType.cppType + << R"cpp(>::validateScalar( + const response::Value& value); )cpp"; } - headerFile << std::endl; - } - - if (!_loader.getUnionTypes().empty()) - { - for (const auto& unionType : _loader.getUnionTypes()) + for (const auto& inputType : _loader.getInputTypes()) { - headerFile << R"cpp(void Add)cpp" << unionType.cppType - << R"cpp(Details(const std::shared_ptr& type)cpp" - << unionType.cppType - << R"cpp(, const std::shared_ptr& schema); + headerFile << R"cpp(template <> +GRAPHQLSERVICE_EXPORT )cpp" + << _loader.getSchemaNamespace() << R"cpp(::)cpp" << inputType.cppType + << R"cpp( Argument<)cpp" << inputType.cppType << R"cpp(>::convert( + const response::Value& value); )cpp"; } - headerFile << std::endl; - } + headerFile << R"cpp(#endif // GRAPHQL_DLLEXPORTS - if (!_loader.getObjectTypes().empty()) - { - for (const auto& objectType : _loader.getObjectTypes()) - { - headerFile << R"cpp(void Add)cpp" << objectType.cppType - << R"cpp(Details(const std::shared_ptr& type)cpp" - << objectType.cppType - << R"cpp(, const std::shared_ptr& schema); )cpp"; - } - - headerFile << std::endl; } - NamespaceScope serviceNamespace { headerFile, "service", true }; + return true; +} - if (_loader.isIntrospection()) +bool Generator::outputSharedTypesModule() const noexcept +{ + if (_loader.getEnumTypes().empty() && _loader.getInputTypes().empty()) { - headerFile - << R"cpp(GRAPHQLSERVICE_EXPORT void AddTypesToSchema(const std::shared_ptr& schema); + return false; + } -)cpp"; + std::ofstream moduleFile(_sharedTypesModulePath, std::ios_base::trunc); + const auto schemaNamespace = std::format(R"cpp(graphql::{})cpp", _loader.getSchemaNamespace()); - if (!_loader.getEnumTypes().empty() || !_loader.getInputTypes().empty()) - { - if (schemaNamespace.exit()) - { - headerFile << std::endl; - } + moduleFile << R"cpp(// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. - serviceNamespace.enter(); +// WARNING! Do not edit this file manually, your changes will be overwritten. - headerFile << R"cpp( -#ifdef GRAPHQL_DLLEXPORTS -// Export all of the built-in converters -)cpp"; +module; - for (const auto& enumType : _loader.getEnumTypes()) - { - headerFile << R"cpp(template <> -GRAPHQLSERVICE_EXPORT )cpp" << _loader.getSchemaNamespace() - << R"cpp(::)cpp" << enumType.cppType << R"cpp( Argument<)cpp" - << _loader.getSchemaNamespace() << R"cpp(::)cpp" << enumType.cppType - << R"cpp(>::convert( - const response::Value& value); -template <> -GRAPHQLSERVICE_EXPORT AwaitableResolver Result<)cpp" - << _loader.getSchemaNamespace() << R"cpp(::)cpp" << enumType.cppType - << R"cpp(>::convert( - AwaitableScalar<)cpp" << _loader.getSchemaNamespace() - << R"cpp(::)cpp" << enumType.cppType - << R"cpp(> result, ResolverParams&& params); -template <> -GRAPHQLSERVICE_EXPORT void Result<)cpp" - << _loader.getSchemaNamespace() << R"cpp(::)cpp" << enumType.cppType - << R"cpp(>::validateScalar( - const response::Value& value); -)cpp"; - } +#include ")cpp" << _loader.getFilenamePrefix() + << + R"cpp(SharedTypes.h" - for (const auto& inputType : _loader.getInputTypes()) - { - headerFile << R"cpp(template <> -GRAPHQLSERVICE_EXPORT )cpp" << _loader.getSchemaNamespace() - << R"cpp(::)cpp" << inputType.cppType << R"cpp( Argument<)cpp" - << inputType.cppType << R"cpp(>::convert( - const response::Value& value); +export module GraphQL.)cpp" + << _loader.getFilenamePrefix() << R"cpp(.)cpp" << _loader.getFilenamePrefix() << + R"cpp(SharedTypes; )cpp"; - } - headerFile << R"cpp(#endif // GRAPHQL_DLLEXPORTS + PendingBlankLine pendingSeparator { moduleFile }; + + moduleFile << R"cpp( +export )cpp"; + + NamespaceScope graphqlNamespace { moduleFile, schemaNamespace }; + + pendingSeparator.add(); + + if (!_loader.getEnumTypes().empty()) + { + pendingSeparator.reset(); + + for (const auto& enumType : _loader.getEnumTypes()) + { + moduleFile << R"cpp(using )cpp" << _loader.getSchemaNamespace() << R"cpp(::)cpp" + << enumType.cppType << R"cpp(; +using )cpp" << _loader.getSchemaNamespace() + << R"cpp(::get)cpp" << enumType.cppType << R"cpp(Names; +using )cpp" << _loader.getSchemaNamespace() + << R"cpp(::get)cpp" << enumType.cppType << R"cpp(Values; )cpp"; } } - else + + if (!_loader.getInputTypes().empty()) { - headerFile << R"cpp(std::shared_ptr GetSchema(); + pendingSeparator.reset(); + for (const auto& inputType : _loader.getInputTypes()) + { + moduleFile << R"cpp(using )cpp" << _loader.getSchemaNamespace() << R"cpp(::)cpp" + << inputType.cppType << R"cpp(; )cpp"; + } + + moduleFile << std::endl; } return true; @@ -738,6 +1063,39 @@ void Generator::outputInterfaceDeclaration(std::ostream& headerFile, std::string )cpp"; } +void Generator::outputObjectModule( + std::ostream& moduleFile, std::string_view objectNamespace, std::string_view cppType) const +{ + moduleFile << R"cpp(// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include ")cpp"; + + if (_options.prefixedHeaders) + { + moduleFile << _loader.getFilenamePrefix(); + } + + moduleFile << cppType << R"cpp(Object.h" + +export module GraphQL.)cpp" + << _loader.getFilenamePrefix() << R"cpp(.)cpp" << cppType << R"cpp(Object; + +export namespace )cpp" + << objectNamespace << R"cpp( { + +using object::)cpp" + << cppType << R"cpp(; + +} // namespace )cpp" + << objectNamespace << R"cpp( +)cpp"; +} + void Generator::outputObjectImplements(std::ostream& headerFile, const ObjectType& objectType) const { headerFile << R"cpp(template @@ -859,8 +1217,8 @@ concept endSelectionSet = requires (TImpl impl, const service::SelectionSetParam )cpp"; } -void Generator::outputObjectDeclaration( - std::ostream& headerFile, const ObjectType& objectType, bool isQueryType) const +void Generator::outputObjectDeclaration(std::ostream& headerFile, const ObjectType& objectType, + bool isQueryType, bool isSubscriptionType) const { headerFile << R"cpp(class [[nodiscard("unnecessary construction")]] )cpp" << objectType.cppType << R"cpp( final @@ -918,7 +1276,37 @@ void Generator::outputObjectDeclaration( explicit Model(std::shared_ptr pimpl) noexcept : _pimpl { std::move(pimpl) } { +)cpp"; + + if (isSubscriptionType && !_options.stubs) + { + headerFile << R"cpp( static_assert()cpp"; + + bool firstField = true; + + for (const auto& outputField : objectType.fields) + { + const auto accessorName = SchemaLoader::getOutputCppAccessor(outputField); + + if (!firstField) + { + headerFile << R"cpp( + || )cpp"; + } + + firstField = false; + headerFile << R"cpp(methods::)cpp" << objectType.cppType << R"cpp(Has::)cpp" + << accessorName << R"cpp(WithParams + || methods::)cpp" + << objectType.cppType << R"cpp(Has::)cpp" << accessorName << R"cpp()cpp"; } + + headerFile << R"cpp(, R"msg()cpp" << objectType.cppType + << R"cpp( fields are not implemented)msg"); +)cpp"; + } + + headerFile << R"cpp( } )cpp"; for (const auto& outputField : objectType.fields) @@ -997,7 +1385,7 @@ void Generator::outputObjectDeclaration( } else)cpp"; - if (!_options.stubs) + if (!isSubscriptionType && !_options.stubs) { headerFile << R"cpp( { @@ -1025,7 +1413,7 @@ void Generator::outputObjectDeclaration( headerFile << R"cpp() }; })cpp"; - if (_options.stubs) + if (isSubscriptionType || _options.stubs) { headerFile << R"cpp( else @@ -1169,13 +1557,10 @@ void Generator::outputObjectDeclaration( std::string Generator::getFieldDeclaration(const InputField& inputField) const noexcept { - std::ostringstream output; - - output << R"cpp( )cpp" << _loader.getInputCppType(inputField) << R"cpp( )cpp" - << inputField.cppName << R"cpp(; -)cpp"; - - return output.str(); + return std::format(R"cpp( {} {}; +)cpp", + _loader.getInputCppType(inputField), + inputField.cppName); } std::string Generator::getFieldDeclaration(const OutputField& outputField) const noexcept @@ -1212,65 +1597,37 @@ std::string Generator::getFieldDeclaration(const OutputField& outputField) const std::string Generator::getResolverDeclaration(const OutputField& outputField) const noexcept { - std::ostringstream output; const auto resolverName = SchemaLoader::getOutputCppResolver(outputField); - output << R"cpp( [[nodiscard("unnecessary call")]] service::AwaitableResolver )cpp" - << resolverName << R"cpp((service::ResolverParams&& params) const; -)cpp"; - - return output.str(); + return std::format( + R"cpp( [[nodiscard("unnecessary call")]] service::AwaitableResolver {}(service::ResolverParams&& params) const; +)cpp", + resolverName); } -bool Generator::outputSource() const noexcept +bool Generator::outputSharedTypesSource() const noexcept { - std::ofstream sourceFile(_sourcePath, std::ios_base::trunc); + if (_loader.getEnumTypes().empty() && _loader.getInputTypes().empty()) + { + return false; + } + + std::ofstream sourceFile(_sharedTypesSourcePath, std::ios_base::trunc); sourceFile << R"cpp(// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // WARNING! Do not edit this file manually, your changes will be overwritten. -)cpp"; - - if (!_loader.isIntrospection()) - { - if (_loader.getOperationTypes().empty()) - { - // Normally this would be included by each of the operation object headers. - sourceFile << R"cpp(#include ")cpp" << getHeaderPath() << R"cpp(" -)cpp"; - } - else - { - for (const auto& operation : _loader.getOperationTypes()) - { - sourceFile << R"cpp(#include ")cpp" << operation.cppType << R"cpp(Object.h" -)cpp"; - } - } - - sourceFile << std::endl; - } - - if (_loader.isIntrospection()) - { - sourceFile << R"cpp(#include "graphqlservice/internal/Introspection.h" -)cpp"; - } - else - { - sourceFile << R"cpp(#include "graphqlservice/internal/Schema.h" +#include "graphqlservice/GraphQLService.h" -#include "graphqlservice/introspection/IntrospectionSchema.h" -)cpp"; - } +#include ")cpp" << getSharedTypesHeaderPath() + << R"cpp(" - sourceFile << R"cpp( #include #include +#include #include -#include #include #include #include @@ -1281,43 +1638,41 @@ using namespace std::literals; )cpp"; NamespaceScope graphqlNamespace { sourceFile, "graphql" }; + NamespaceScope serviceNamespace { sourceFile, "service" }; + PendingBlankLine pendingSeparator { sourceFile }; - if (!_loader.getEnumTypes().empty() || !_loader.getInputTypes().empty()) - { - NamespaceScope serviceNamespace { sourceFile, "service" }; - - sourceFile << std::endl; + pendingSeparator.reset(); - for (const auto& enumType : _loader.getEnumTypes()) - { - sourceFile << R"cpp(static const auto s_names)cpp" << enumType.cppType << R"cpp( = )cpp" - << _loader.getSchemaNamespace() << R"cpp(::get)cpp" << enumType.cppType - << R"cpp(Names(); + for (const auto& enumType : _loader.getEnumTypes()) + { + sourceFile << R"cpp(static const auto s_names)cpp" << enumType.cppType << R"cpp( = )cpp" + << _loader.getSchemaNamespace() << R"cpp(::get)cpp" << enumType.cppType + << R"cpp(Names(); static const auto s_values)cpp" - << enumType.cppType << R"cpp( = )cpp" << _loader.getSchemaNamespace() - << R"cpp(::get)cpp" << enumType.cppType << R"cpp(Values(); + << enumType.cppType << R"cpp( = )cpp" << _loader.getSchemaNamespace() + << R"cpp(::get)cpp" << enumType.cppType << R"cpp(Values(); template <> )cpp" << _loader.getSchemaNamespace() - << R"cpp(::)cpp" << enumType.cppType << R"cpp( Argument<)cpp" - << _loader.getSchemaNamespace() << R"cpp(::)cpp" << enumType.cppType - << R"cpp(>::convert(const response::Value& value) + << R"cpp(::)cpp" << enumType.cppType << R"cpp( Argument<)cpp" + << _loader.getSchemaNamespace() << R"cpp(::)cpp" << enumType.cppType + << R"cpp(>::convert(const response::Value& value) { if (!value.maybe_enum()) { throw service::schema_exception { { R"ex(not a valid )cpp" - << enumType.type << R"cpp( value)ex" } }; + << enumType.type << R"cpp( value)ex" } }; } const auto result = internal::sorted_map_lookup( - s_values)cpp" << enumType.cppType - << R"cpp(, + s_values)cpp" + << enumType.cppType << R"cpp(, std::string_view { value.get() }); if (!result) { throw service::schema_exception { { R"ex(not a valid )cpp" - << enumType.type << R"cpp( value)ex" } }; + << enumType.type << R"cpp( value)ex" } }; } return *result; @@ -1325,16 +1680,16 @@ template <> template <> service::AwaitableResolver Result<)cpp" - << _loader.getSchemaNamespace() << R"cpp(::)cpp" << enumType.cppType - << R"cpp(>::convert(service::AwaitableScalar<)cpp" - << _loader.getSchemaNamespace() << R"cpp(::)cpp" << enumType.cppType - << R"cpp(> result, ResolverParams&& params) + << _loader.getSchemaNamespace() << R"cpp(::)cpp" << enumType.cppType + << R"cpp(>::convert(service::AwaitableScalar<)cpp" + << _loader.getSchemaNamespace() << R"cpp(::)cpp" << enumType.cppType + << R"cpp(> result, ResolverParams&& params) { return ModifiedResult<)cpp" - << _loader.getSchemaNamespace() << R"cpp(::)cpp" << enumType.cppType - << R"cpp(>::resolve(std::move(result), std::move(params), + << _loader.getSchemaNamespace() << R"cpp(::)cpp" << enumType.cppType + << R"cpp(>::resolve(std::move(result), std::move(params), []()cpp" << _loader.getSchemaNamespace() - << R"cpp(::)cpp" << enumType.cppType << R"cpp( value, const ResolverParams&) + << R"cpp(::)cpp" << enumType.cppType << R"cpp( value, const ResolverParams&) { const auto idx = static_cast(value); @@ -1345,286 +1700,284 @@ service::AwaitableResolver Result<)cpp" << enumType.type << R"cpp()ex" } }; } - response::Value resolvedResult(response::Type::EnumValue); - - resolvedResult.set(std::string { s_names)cpp" - << enumType.cppType << R"cpp([idx] }); - - return resolvedResult; + return ResolverResult { { response::ValueToken::EnumValue { std::string { s_names)cpp" + << enumType.cppType << R"cpp([idx] } } } }; }); } template <> void Result<)cpp" << _loader.getSchemaNamespace() - << R"cpp(::)cpp" << enumType.cppType - << R"cpp(>::validateScalar(const response::Value& value) + << R"cpp(::)cpp" << enumType.cppType + << R"cpp(>::validateScalar(const response::Value& value) { if (!value.maybe_enum()) { throw service::schema_exception { { R"ex(not a valid )cpp" - << enumType.type << R"cpp( value)ex" } }; + << enumType.type << R"cpp( value)ex" } }; } const auto [itr, itrEnd] = internal::sorted_map_equal_range( - s_values)cpp" << enumType.cppType - << R"cpp(.begin(), - s_values)cpp" << enumType.cppType - << R"cpp(.end(), + s_values)cpp" + << enumType.cppType << R"cpp(.begin(), + s_values)cpp" + << enumType.cppType << R"cpp(.end(), std::string_view { value.get() }); if (itr == itrEnd) { throw service::schema_exception { { R"ex(not a valid )cpp" - << enumType.type << R"cpp( value)ex" } }; + << enumType.type << R"cpp( value)ex" } }; } } )cpp"; - } + } - for (const auto& inputType : _loader.getInputTypes()) - { - bool firstField = true; + for (const auto& inputType : _loader.getInputTypes()) + { + bool firstField = true; - sourceFile << R"cpp(template <> + sourceFile << R"cpp(template <> )cpp" << _loader.getSchemaNamespace() - << R"cpp(::)cpp" << inputType.cppType << R"cpp( Argument<)cpp" - << _loader.getSchemaNamespace() << R"cpp(::)cpp" << inputType.cppType - << R"cpp(>::convert(const response::Value& value) + << R"cpp(::)cpp" << inputType.cppType << R"cpp( Argument<)cpp" + << _loader.getSchemaNamespace() << R"cpp(::)cpp" << inputType.cppType + << R"cpp(>::convert(const response::Value& value) { )cpp"; - for (const auto& inputField : inputType.fields) + for (const auto& inputField : inputType.fields) + { + if (inputField.defaultValue.type() != response::Type::Null) { - if (inputField.defaultValue.type() != response::Type::Null) + if (firstField) { - if (firstField) - { - firstField = false; - sourceFile << R"cpp( const auto defaultValue = []() + firstField = false; + sourceFile << R"cpp( const auto defaultValue = []() { response::Value values(response::Type::Map); response::Value entry; )cpp"; - } + } - sourceFile << getArgumentDefaultValue(0, inputField.defaultValue) - << R"cpp( values.emplace_back(")cpp" << inputField.name - << R"cpp(", std::move(entry)); + sourceFile << getArgumentDefaultValue(0, inputField.defaultValue) + << R"cpp( values.emplace_back(")cpp" << inputField.name + << R"cpp(", std::move(entry)); )cpp"; - } } + } - if (!firstField) - { - sourceFile << R"cpp( + if (!firstField) + { + sourceFile << R"cpp( return values; }(); )cpp"; - } + } - for (const auto& inputField : inputType.fields) - { - sourceFile << getArgumentDeclaration(inputField, "value", "value", "defaultValue"); - } + for (const auto& inputField : inputType.fields) + { + sourceFile << getArgumentDeclaration(inputField, "value", "value", "defaultValue"); + } - if (!inputType.fields.empty()) - { - sourceFile << std::endl; - } + if (!inputType.fields.empty()) + { + sourceFile << std::endl; + } - sourceFile << R"cpp( return )cpp" << _loader.getSchemaNamespace() << R"cpp(::)cpp" - << inputType.cppType << R"cpp( { + sourceFile << R"cpp( return )cpp" << _loader.getSchemaNamespace() << R"cpp(::)cpp" + << inputType.cppType << R"cpp( { )cpp"; - firstField = true; + firstField = true; - for (const auto& inputField : inputType.fields) - { - std::string fieldName(inputField.cppName); + for (const auto& inputField : inputType.fields) + { + std::string fieldName(inputField.cppName); - if (!firstField) - { - sourceFile << R"cpp(, + if (!firstField) + { + sourceFile << R"cpp(, )cpp"; - } + } - const bool shouldMove = SchemaLoader::shouldMoveInputField(inputField); + const bool shouldMove = SchemaLoader::shouldMoveInputField(inputField); - firstField = false; - fieldName[0] = - static_cast(std::toupper(static_cast(fieldName[0]))); + firstField = false; + fieldName[0] = + static_cast(std::toupper(static_cast(fieldName[0]))); - sourceFile << R"cpp( )cpp"; + sourceFile << R"cpp( )cpp"; - if (shouldMove) - { - sourceFile << R"cpp(std::move()cpp"; - } + if (shouldMove) + { + sourceFile << R"cpp(std::move()cpp"; + } - sourceFile << R"cpp(value)cpp" << fieldName; + sourceFile << R"cpp(value)cpp" << fieldName; - if (shouldMove) - { - sourceFile << R"cpp())cpp"; - } + if (shouldMove) + { + sourceFile << R"cpp())cpp"; } + } - sourceFile << R"cpp( + sourceFile << R"cpp( }; } )cpp"; - } - - serviceNamespace.exit(); - sourceFile << std::endl; } - NamespaceScope schemaNamespace { sourceFile, _loader.getSchemaNamespace() }; - std::string_view queryType; + serviceNamespace.exit(); + pendingSeparator.add(); - for (const auto& inputType : _loader.getInputTypes()) + if (!_loader.getInputTypes().empty()) { - sourceFile << std::endl - << inputType.cppType << R"cpp(::)cpp" << inputType.cppType - << R"cpp(() noexcept)cpp"; + pendingSeparator.reset(); - bool firstField = true; + NamespaceScope schemaNamespace { sourceFile, _loader.getSchemaNamespace() }; - for (const auto& inputField : inputType.fields) + for (const auto& inputType : _loader.getInputTypes()) { - sourceFile << R"cpp( + sourceFile << std::endl + << inputType.cppType << R"cpp(::)cpp" << inputType.cppType + << R"cpp(() noexcept)cpp"; + + bool firstField = true; + + for (const auto& inputField : inputType.fields) + { + sourceFile << R"cpp( )cpp" << (firstField ? R"cpp(:)cpp" : R"cpp(,)cpp") - << R"cpp( )cpp" << inputField.cppName << R"cpp( {})cpp"; - firstField = false; - } + << R"cpp( )cpp" << inputField.cppName << R"cpp( {})cpp"; + firstField = false; + } - sourceFile << R"cpp( + sourceFile << R"cpp( { // Explicit definition to prevent ODR violations when LTO is enabled. } )cpp" << inputType.cppType - << R"cpp(::)cpp" << inputType.cppType << R"cpp(()cpp"; + << R"cpp(::)cpp" << inputType.cppType << R"cpp(()cpp"; - firstField = true; + firstField = true; - for (const auto& inputField : inputType.fields) - { - if (!firstField) + for (const auto& inputField : inputType.fields) { - sourceFile << R"cpp(,)cpp"; - } + if (!firstField) + { + sourceFile << R"cpp(,)cpp"; + } - firstField = false; - sourceFile << R"cpp( + firstField = false; + sourceFile << R"cpp( )cpp" << _loader.getInputCppType(inputField) - << R"cpp( )cpp" << inputField.cppName << R"cpp(Arg)cpp"; - } + << R"cpp( )cpp" << inputField.cppName << R"cpp(Arg)cpp"; + } - sourceFile << R"cpp() noexcept + sourceFile << R"cpp() noexcept )cpp"; - firstField = true; + firstField = true; - for (const auto& inputField : inputType.fields) - { - sourceFile << (firstField ? R"cpp( : )cpp" : R"cpp( , )cpp"); - firstField = false; + for (const auto& inputField : inputType.fields) + { + sourceFile << (firstField ? R"cpp( : )cpp" : R"cpp( , )cpp"); + firstField = false; - sourceFile << inputField.cppName << R"cpp( { std::move()cpp" << inputField.cppName - << R"cpp(Arg) } + sourceFile << inputField.cppName << R"cpp( { std::move()cpp" << inputField.cppName + << R"cpp(Arg) } )cpp"; - } + } - sourceFile << R"cpp({ + sourceFile << R"cpp({ } )cpp" << inputType.cppType - << R"cpp(::)cpp" << inputType.cppType << R"cpp((const )cpp" << inputType.cppType - << R"cpp(& other) + << R"cpp(::)cpp" << inputType.cppType << R"cpp((const )cpp" + << inputType.cppType << R"cpp(& other) )cpp"; - firstField = true; - - for (const auto& inputField : inputType.fields) - { - sourceFile << (firstField ? R"cpp( : )cpp" : R"cpp( , )cpp"); - firstField = false; - - sourceFile << inputField.cppName << R"cpp( { service::ModifiedArgument<)cpp" - << _loader.getCppType(inputField.type) << R"cpp(>::duplicate)cpp"; + firstField = true; - if (!inputField.modifiers.empty()) + for (const auto& inputField : inputType.fields) { - bool firstModifier = true; + sourceFile << (firstField ? R"cpp( : )cpp" : R"cpp( , )cpp"); + firstField = false; - for (const auto modifier : inputField.modifiers) + sourceFile << inputField.cppName << R"cpp( { service::ModifiedArgument<)cpp" + << _loader.getCppType(inputField.type) << R"cpp(>::duplicate)cpp"; + + if (!inputField.modifiers.empty()) { - sourceFile << (firstModifier ? R"cpp(<)cpp" : R"cpp(, )cpp"); - firstModifier = false; + bool firstModifier = true; - switch (modifier) + for (const auto modifier : inputField.modifiers) { - case service::TypeModifier::None: - sourceFile << R"cpp(service::TypeModifier::None)cpp"; - break; + sourceFile << (firstModifier ? R"cpp(<)cpp" : R"cpp(, )cpp"); + firstModifier = false; + + switch (modifier) + { + case service::TypeModifier::None: + sourceFile << R"cpp(service::TypeModifier::None)cpp"; + break; - case service::TypeModifier::Nullable: - sourceFile << R"cpp(service::TypeModifier::Nullable)cpp"; - break; + case service::TypeModifier::Nullable: + sourceFile << R"cpp(service::TypeModifier::Nullable)cpp"; + break; - case service::TypeModifier::List: - sourceFile << R"cpp(service::TypeModifier::List)cpp"; - break; + case service::TypeModifier::List: + sourceFile << R"cpp(service::TypeModifier::List)cpp"; + break; + } } - } - sourceFile << R"cpp(>)cpp"; - } + sourceFile << R"cpp(>)cpp"; + } - sourceFile << R"cpp((other.)cpp" << inputField.cppName << R"cpp() } + sourceFile << R"cpp((other.)cpp" << inputField.cppName << R"cpp() } )cpp"; - } + } - sourceFile << R"cpp({ + sourceFile << R"cpp({ } )cpp" << inputType.cppType - << R"cpp(::)cpp" << inputType.cppType << R"cpp(()cpp" << inputType.cppType - << R"cpp(&& other) noexcept + << R"cpp(::)cpp" << inputType.cppType << R"cpp(()cpp" << inputType.cppType + << R"cpp(&& other) noexcept )cpp"; - firstField = true; + firstField = true; - for (const auto& inputField : inputType.fields) - { - sourceFile << (firstField ? R"cpp( : )cpp" : R"cpp( , )cpp"); - firstField = false; + for (const auto& inputField : inputType.fields) + { + sourceFile << (firstField ? R"cpp( : )cpp" : R"cpp( , )cpp"); + firstField = false; - sourceFile << inputField.cppName << R"cpp( { std::move(other.)cpp" << inputField.cppName - << R"cpp() } + sourceFile << inputField.cppName << R"cpp( { std::move(other.)cpp" + << inputField.cppName << R"cpp() } )cpp"; - } + } - sourceFile << R"cpp({ + sourceFile << R"cpp({ } )cpp" << inputType.cppType - << R"cpp(::~)cpp" << inputType.cppType << R"cpp(() + << R"cpp(::~)cpp" << inputType.cppType << R"cpp(() { // Explicit definition to prevent ODR violations when LTO is enabled. } )cpp" << inputType.cppType - << R"cpp(& )cpp" << inputType.cppType << R"cpp(::operator=(const )cpp" - << inputType.cppType << R"cpp(& other) + << R"cpp(& )cpp" << inputType.cppType << R"cpp(::operator=(const )cpp" + << inputType.cppType << R"cpp(& other) { )cpp" << inputType.cppType - << R"cpp( value { other }; + << R"cpp( value { other }; std::swap(*this, value); @@ -1632,36 +1985,97 @@ void Result<)cpp" << _loader.getSchemaNamespace() } )cpp" << inputType.cppType - << R"cpp(& )cpp" << inputType.cppType << R"cpp(::operator=()cpp" - << inputType.cppType << R"cpp(&& other) noexcept + << R"cpp(& )cpp" << inputType.cppType << R"cpp(::operator=()cpp" + << inputType.cppType << R"cpp(&& other) noexcept { )cpp"; - for (const auto& inputField : inputType.fields) - { - sourceFile << R"cpp( )cpp" << inputField.cppName << R"cpp( = std::move(other.)cpp" - << inputField.cppName << R"cpp(); + for (const auto& inputField : inputType.fields) + { + sourceFile << R"cpp( )cpp" << inputField.cppName + << R"cpp( = std::move(other.)cpp" << inputField.cppName << R"cpp(); )cpp"; - } + } - sourceFile << R"cpp( + sourceFile << R"cpp( return *this; } + )cpp"; + } } + return true; +} + +bool Generator::outputSchemaSource() const noexcept +{ + std::ofstream sourceFile(_schemaSourcePath, std::ios_base::trunc); + + sourceFile << R"cpp(// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +)cpp"; + if (!_loader.isIntrospection()) { - for (const auto& operation : _loader.getOperationTypes()) + if (_loader.getOperationTypes().empty()) + { + // Normally this would be included by each of the operation object headers. + sourceFile << R"cpp(#include ")cpp" << getSchemaHeaderPath() << R"cpp(" +)cpp"; + } + else { - if (operation.operation == service::strQuery) + for (const auto& operation : _loader.getOperationTypes()) { - queryType = operation.type; - break; + sourceFile << R"cpp(#include ")cpp"; + + if (_options.prefixedHeaders) + { + sourceFile << _loader.getFilenamePrefix(); + } + + sourceFile << operation.cppType << R"cpp(Object.h" +)cpp"; } } + + sourceFile << std::endl; + } + + if (_loader.isIntrospection()) + { + sourceFile << R"cpp(#include "graphqlservice/internal/Introspection.h" +)cpp"; + } + else + { + sourceFile << R"cpp(#include "graphqlservice/internal/Schema.h" + +#include "graphqlservice/introspection/IntrospectionSchema.h" +)cpp"; } + sourceFile << R"cpp( +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std::literals; + +)cpp"; + + const auto schemaNamespace = std::format(R"cpp(graphql::{})cpp", _loader.getSchemaNamespace()); + NamespaceScope schemaNamespaceScope { sourceFile, schemaNamespace }; + if (!_loader.isIntrospection()) { bool firstOperation = true; @@ -1895,7 +2309,10 @@ Operations::Operations()cpp"; { bool firstValue = true; - sourceFile << R"cpp( type)cpp" << enumType.cppType << R"cpp(->AddEnumValues({ + sourceFile << R"cpp( static const auto s_names)cpp" << enumType.cppType + << R"cpp( = get)cpp" << enumType.cppType << R"cpp(Names(); + type)cpp" << enumType.cppType + << R"cpp(->AddEnumValues({ )cpp"; for (const auto& enumValue : enumType.values) @@ -1907,10 +2324,10 @@ Operations::Operations()cpp"; } firstValue = false; - sourceFile << R"cpp( { service::s_names)cpp" << enumType.cppType - << R"cpp([static_cast()cpp" << _loader.getSchemaNamespace() - << R"cpp(::)cpp" << enumType.cppType << R"cpp(::)cpp" - << enumValue.cppValue << R"cpp()], R"md()cpp"; + sourceFile << R"cpp( { s_names)cpp" << enumType.cppType + << R"cpp([static_cast()cpp" + << _loader.getSchemaNamespace() << R"cpp(::)cpp" << enumType.cppType + << R"cpp(::)cpp" << enumValue.cppValue << R"cpp()], R"md()cpp"; if (!_options.noIntrospection) { @@ -2311,18 +2728,16 @@ service::ResolverMap )cpp" std::map resolvers; - std::transform(objectType.fields.cbegin(), - objectType.fields.cend(), + std::ranges::transform(objectType.fields, std::inserter(resolvers, resolvers.begin()), [](const OutputField& outputField) noexcept { const auto resolverName = SchemaLoader::getOutputCppResolver(outputField); - std::ostringstream output; + auto output = std::format( + R"cpp( {{ R"gql({})gql"sv, [this](service::ResolverParams&& params) {{ return {}(std::move(params)); }} }})cpp", + outputField.name, + resolverName); - output << R"cpp( { R"gql()cpp" << outputField.name - << R"cpp()gql"sv, [this](service::ResolverParams&& params) { return )cpp" - << resolverName << R"cpp((std::move(params)); } })cpp"; - - return std::make_pair(std::string_view { outputField.name }, output.str()); + return std::make_pair(std::string_view { outputField.name }, std::move(output)); }); resolvers["__typename"sv] = @@ -2652,7 +3067,7 @@ void Generator::outputIntrospectionFields( } std::string Generator::getArgumentDefaultValue( - size_t level, const response::Value& defaultValue) const noexcept + std::size_t level, const response::Value& defaultValue) const noexcept { const std::string padding(level, '\t'); std::ostringstream argumentDefaultValue; @@ -2939,7 +3354,7 @@ std::string Generator::getTypeModifiers(const TypeModifierStack& modifiers) cons std::string Generator::getIntrospectionType( std::string_view type, const TypeModifierStack& modifiers) const noexcept { - size_t wrapperCount = 0; + std::size_t wrapperCount = 0; bool nonNull = true; std::ostringstream introspectionType; @@ -3001,7 +3416,7 @@ std::string Generator::getIntrospectionType( introspectionType << R"cpp(schema->LookupType(R"gql()cpp" << type << R"cpp()gql"sv))cpp"; - for (size_t i = 0; i < wrapperCount; ++i) + for (std::size_t i = 0; i < wrapperCount; ++i) { introspectionType << R"cpp())cpp"; } @@ -3014,39 +3429,24 @@ std::vector Generator::outputSeparateFiles() const noexcept const std::filesystem::path headerDir(_headerDir); const std::filesystem::path sourceDir(_sourceDir); std::vector files; - std::string_view queryType; - - for (const auto& operation : _loader.getOperationTypes()) - { - if (operation.operation == service::strQuery) - { - queryType = operation.type; - break; - } - } - - std::ostringstream ossNamespace; - - ossNamespace << R"cpp(graphql::)cpp" << _loader.getSchemaNamespace(); - - const auto schemaNamespace = ossNamespace.str(); - std::ostringstream ossInterfaceNamespace; - ossInterfaceNamespace << schemaNamespace << R"cpp(::object)cpp"; - - const auto objectNamespace = ossInterfaceNamespace.str(); + const auto schemaNamespace = std::format(R"cpp(graphql::{})cpp", _loader.getSchemaNamespace()); + const auto objectNamespace = std::format(R"cpp({}::object)cpp", schemaNamespace); for (const auto& interfaceType : _loader.getInterfaceTypes()) { - const auto headerFilename = std::string(interfaceType.cppType) + "Object.h"; + const auto headerFilename = std::format("{}{}Object.h", + (_options.prefixedHeaders ? _loader.getFilenamePrefix() : std::string_view {}), + interfaceType.cppType); auto headerPath = (headerDir / headerFilename).string(); { std::ofstream headerFile(headerPath, std::ios_base::trunc); - IncludeGuardScope includeGuard { headerFile, headerFilename }; + IncludeGuardScope includeGuard { headerFile, + std::format("{}_{}", _loader.getFilenamePrefix(), headerFilename) }; headerFile << R"cpp(#include ")cpp" - << std::filesystem::path(_headerPath).filename().string() << R"cpp(" + << std::filesystem::path(_schemaHeaderPath).filename().string() << R"cpp(" )cpp"; @@ -3056,11 +3456,21 @@ std::vector Generator::outputSeparateFiles() const noexcept headerFile << std::endl; outputInterfaceDeclaration(headerFile, interfaceType.cppType); headerFile << std::endl; + } - if (_options.verbose) - { - files.push_back(std::move(headerPath)); - } + const auto moduleFilename = std::string(interfaceType.cppType) + "Object.ixx"; + auto modulePath = (headerDir / moduleFilename).string(); + + { + std::ofstream moduleFile(modulePath, std::ios_base::trunc); + + outputObjectModule(moduleFile, objectNamespace, interfaceType.cppType); + } + + if (_options.verbose) + { + files.push_back(std::move(headerPath)); + files.push_back(std::move(modulePath)); } const auto sourceFilename = std::string(interfaceType.cppType) + "Object.cpp"; @@ -3112,15 +3522,18 @@ using namespace std::literals; for (const auto& unionType : _loader.getUnionTypes()) { - const auto headerFilename = std::string(unionType.cppType) + "Object.h"; + const auto headerFilename = std::format("{}{}Object.h", + (_options.prefixedHeaders ? _loader.getFilenamePrefix() : std::string_view {}), + unionType.cppType); auto headerPath = (headerDir / headerFilename).string(); { std::ofstream headerFile(headerPath, std::ios_base::trunc); - IncludeGuardScope includeGuard { headerFile, headerFilename }; + IncludeGuardScope includeGuard { headerFile, + std::format("{}_{}", _loader.getFilenamePrefix(), headerFilename) }; headerFile << R"cpp(#include ")cpp" - << std::filesystem::path(_headerPath).filename().string() << R"cpp(" + << std::filesystem::path(_schemaHeaderPath).filename().string() << R"cpp(" )cpp"; @@ -3132,9 +3545,19 @@ using namespace std::literals; headerFile << std::endl; } + const auto moduleFilename = std::string(unionType.cppType) + "Object.ixx"; + auto modulePath = (headerDir / moduleFilename).string(); + + { + std::ofstream moduleFile(modulePath, std::ios_base::trunc); + + outputObjectModule(moduleFile, objectNamespace, unionType.cppType); + } + if (_options.verbose) { files.push_back(std::move(headerPath)); + files.push_back(std::move(modulePath)); } const auto sourceFilename = std::string(unionType.cppType) + "Object.cpp"; @@ -3186,16 +3609,38 @@ using namespace std::literals; for (const auto& objectType : _loader.getObjectTypes()) { - const bool isQueryType = objectType.type == queryType; - const auto headerFilename = std::string(objectType.cppType) + "Object.h"; + bool isQueryType = false; + bool isSubscriptionType = false; + + for (const auto& operation : _loader.getOperationTypes()) + { + if (objectType.type == operation.type) + { + if (operation.operation == service::strQuery) + { + isQueryType = true; + } + else if (operation.operation == service::strSubscription) + { + isSubscriptionType = true; + } + + break; + } + } + + const auto headerFilename = std::format("{}{}Object.h", + (_options.prefixedHeaders ? _loader.getFilenamePrefix() : std::string_view {}), + objectType.cppType); auto headerPath = (headerDir / headerFilename).string(); { std::ofstream headerFile(headerPath, std::ios_base::trunc); - IncludeGuardScope includeGuard { headerFile, headerFilename }; + IncludeGuardScope includeGuard { headerFile, + std::format("{}_{}", _loader.getFilenamePrefix(), headerFilename) }; headerFile << R"cpp(#include ")cpp" - << std::filesystem::path(_headerPath).filename().string() << R"cpp(" + << std::filesystem::path(_schemaHeaderPath).filename().string() << R"cpp(" )cpp"; @@ -3215,11 +3660,8 @@ using namespace std::literals; } // Output the stub concepts - std::ostringstream ossConceptNamespace; - - ossConceptNamespace << R"cpp(methods::)cpp" << objectType.cppType << R"cpp(Has)cpp"; - - const auto conceptNamespace = ossConceptNamespace.str(); + const auto conceptNamespace = + std::format(R"cpp(methods::{}Has)cpp", objectType.cppType); NamespaceScope stubNamespace { headerFile, conceptNamespace }; outputObjectStubs(headerFile, objectType); @@ -3227,13 +3669,23 @@ using namespace std::literals; // Output the full declaration headerFile << std::endl; - outputObjectDeclaration(headerFile, objectType, isQueryType); + outputObjectDeclaration(headerFile, objectType, isQueryType, isSubscriptionType); headerFile << std::endl; } + const auto moduleFilename = std::string(objectType.cppType) + "Object.ixx"; + auto modulePath = (headerDir / moduleFilename).string(); + + { + std::ofstream moduleFile(modulePath, std::ios_base::trunc); + + outputObjectModule(moduleFile, objectNamespace, objectType.cppType); + } + if (_options.verbose) { files.push_back(std::move(headerPath)); + files.push_back(std::move(modulePath)); } const auto sourceFilename = std::string(objectType.cppType) + "Object.cpp"; @@ -3262,8 +3714,14 @@ using namespace std::literals; case OutputFieldType::Object: if (includedObjects.insert(field.type).second) { - sourceFile << R"cpp(#include ")cpp" - << SchemaLoader::getSafeCppName(field.type) + sourceFile << R"cpp(#include ")cpp"; + + if (_options.prefixedHeaders) + { + sourceFile << _loader.getFilenamePrefix(); + } + + sourceFile << SchemaLoader::getSafeCppName(field.type) << R"cpp(Object.h" )cpp"; } @@ -3300,7 +3758,6 @@ using namespace std::literals; sourceFile << R"cpp( #include #include -#include #include #include @@ -3365,6 +3822,7 @@ int main(int argc, char** argv) bool verbose = false; bool stubs = false; bool noIntrospection = false; + bool prefixedHeaders = false; std::string schemaFileName; std::string filenamePrefix; std::string schemaNamespace; @@ -3391,7 +3849,9 @@ int main(int argc, char** argv) "Unimplemented fields throw runtime exceptions instead of compiler errors")("no-" "introspection", po::bool_switch(&noIntrospection), - "Do not generate support for Introspection"); + "Do not generate support for Introspection")("prefix-headers", + po::bool_switch(&prefixedHeaders), + "Prefix generated object header filenames"); positional.add("schema", 1).add("prefix", 1).add("namespace", 1); internalOptions.add_options()("introspection", po::bool_switch(&buildIntrospection), @@ -3456,6 +3916,7 @@ int main(int argc, char** argv) verbose, // verbose stubs, // stubs noIntrospection, // noIntrospection + prefixedHeaders, // prefixedHeaders }) .Build(); diff --git a/src/SchemaLoader.cpp b/src/SchemaLoader.cpp index 9129ec48..85bbaa63 100644 --- a/src/SchemaLoader.cpp +++ b/src/SchemaLoader.cpp @@ -4,6 +4,7 @@ #include "SchemaLoader.h" #include +#include #include #include #include @@ -77,18 +78,17 @@ void SchemaLoader::validateSchema() { if (s_builtinTypes.find(entry.first) != s_builtinTypes.cend()) { - std::ostringstream error; auto itrPosition = _typePositions.find(entry.first); - - error << "Builtin type overridden: " << entry.first; + auto error = std::format("Builtin type overridden: {}", entry.first); if (itrPosition != _typePositions.cend()) { - error << " line: " << itrPosition->second.line - << " column: " << itrPosition->second.column; + error += std::format(" line: {} column: {}", + itrPosition->second.line, + itrPosition->second.column); } - throw std::runtime_error(error.str()); + throw std::runtime_error(error); } } @@ -148,12 +148,11 @@ void SchemaLoader::validateSchema() { if (_objectNames.find(operation.type) == _objectNames.cend()) { - std::ostringstream error; - - error << "Unknown operation type: " << operation.type - << " operation: " << operation.operation; + const auto error = std::format("Unknown operation type: {} operation: {}", + operation.type, + operation.operation); - throw std::runtime_error(error.str()); + throw std::runtime_error(error); } queryDefined = queryDefined || (operation.operation == service::strQuery); @@ -214,18 +213,18 @@ void SchemaLoader::validateSchema() if (itr == _objectNames.cend()) { - std::ostringstream error; auto itrPosition = _typePositions.find(entry.type); - - error << "Unknown type: " << objectName << " included by: " << entry.type; + auto error = + std::format("Unknown type: {} included by: {}", objectName, entry.type); if (itrPosition != _typePositions.cend()) { - error << " line: " << itrPosition->second.line - << " column: " << itrPosition->second.column; + error += std::format(" line: {} column: {}", + itrPosition->second.line, + itrPosition->second.column); } - throw std::runtime_error(error.str()); + throw std::runtime_error(error); } _objectTypes[itr->second].unions.push_back(entry.type); @@ -264,16 +263,16 @@ void SchemaLoader::fixupOutputFieldList(OutputFieldList& fields, if (itr == _schemaTypes.cend()) { - std::ostringstream error; - - error << "Unknown field type: " << entry.type; + auto error = std::format("Unknown field type: {}", entry.type); if (entry.position) { - error << " line: " << entry.position->line << " column: " << entry.position->column; + error += std::format(" line: {} column: {}", + entry.position->line, + entry.position->column); } - throw std::runtime_error(error.str()); + throw std::runtime_error(error); } switch (itr->second) @@ -300,17 +299,16 @@ void SchemaLoader::fixupOutputFieldList(OutputFieldList& fields, default: { - std::ostringstream error; - - error << "Invalid field type: " << entry.type; + auto error = std::format("Invalid field type: {}", entry.type); if (entry.position) { - error << " line: " << entry.position->line - << " column: " << entry.position->column; + error += std::format(" line: {} column: {}", + entry.position->line, + entry.position->column); } - throw std::runtime_error(error.str()); + throw std::runtime_error(error); } } @@ -331,16 +329,16 @@ void SchemaLoader::fixupInputFieldList(InputFieldList& fields) if (itr == _schemaTypes.cend()) { - std::ostringstream error; - - error << "Unknown argument type: " << entry.type; + auto error = std::format("Unknown argument type: {}", entry.type); if (entry.position) { - error << " line: " << entry.position->line << " column: " << entry.position->column; + error += std::format(" line: {} column: {}", + entry.position->line, + entry.position->column); } - throw std::runtime_error(error.str()); + throw std::runtime_error(error); } switch (itr->second) @@ -359,17 +357,16 @@ void SchemaLoader::fixupInputFieldList(InputFieldList& fields) default: { - std::ostringstream error; - - error << "Invalid argument type: " << entry.type; + auto error = std::format("Invalid argument type: {}", entry.type); if (entry.position) { - error << " line: " << entry.position->line - << " column: " << entry.position->column; + error += std::format(" line: {} column: {}", + entry.position->line, + entry.position->column); } - throw std::runtime_error(error.str()); + throw std::runtime_error(error); } } } @@ -378,24 +375,22 @@ void SchemaLoader::fixupInputFieldList(InputFieldList& fields) void SchemaLoader::reorderInputTypeDependencies() { // Build the dependency list for each input type. - std::for_each(_inputTypes.begin(), _inputTypes.end(), [](InputType& entry) noexcept { - std::for_each(entry.fields.cbegin(), - entry.fields.cend(), - [&entry](const InputField& field) noexcept { - if (field.fieldType == InputFieldType::Input) + std::ranges::for_each(_inputTypes, [](InputType& entry) noexcept { + std::ranges::for_each(entry.fields, [&entry](const InputField& field) noexcept { + if (field.fieldType == InputFieldType::Input) + { + // https://spec.graphql.org/October2021/#sec-Input-Objects.Circular-References + if (!field.modifiers.empty() + && field.modifiers.front() != service::TypeModifier::None) { - // https://spec.graphql.org/October2021/#sec-Input-Objects.Circular-References - if (!field.modifiers.empty() - && field.modifiers.front() != service::TypeModifier::None) - { - entry.declarations.push_back(field.type); - } - else - { - entry.dependencies.insert(field.type); - } + entry.declarations.push_back(field.type); } - }); + else + { + entry.dependencies.insert(field.type); + } + } + }); }); std::unordered_set handled; @@ -418,11 +413,9 @@ void SchemaLoader::reorderInputTypeDependencies() // Check to make sure we made progress. if (itrDependent == itr) { - std::ostringstream error; - - error << "Input object cycle type: " << itr->type; + const auto error = std::format("Input object cycle type: {}", itr->type); - throw std::runtime_error(error.str()); + throw std::runtime_error(error); } if (itrDependent != _inputTypes.end()) @@ -535,12 +528,11 @@ void SchemaLoader::visitDefinition(const peg::ast_node& definition) else { const auto position = definition.begin(); - std::ostringstream error; - - error << "Unexpected executable definition line: " << position.line - << " column: " << position.column; + const auto error = std::format("Unexpected executable definition line: {} column: {}", + position.line, + position.column); - throw std::runtime_error(error.str()); + throw std::runtime_error(error); } } @@ -1288,16 +1280,14 @@ void SchemaLoader::blockReservedName( // https://spec.graphql.org/October2021/#sec-Names.Reserved-Names if (name.size() > 1 && name.substr(0, 2) == R"gql(__)gql"sv) { - std::ostringstream error; - - error << "Names starting with __ are reserved: " << name; + auto error = std::format("Names starting with __ are reserved: {}", name); if (position) { - error << " line: " << position->line << " column: " << position->column; + error += std::format(" line: {} column: {}", position->line, position->column); } - throw std::runtime_error(error.str()); + throw std::runtime_error(error); } } @@ -1308,18 +1298,18 @@ const InterfaceType& SchemaLoader::findInterfaceType( if (itrType == _interfaceNames.cend()) { - std::ostringstream error; const auto itrPosition = _typePositions.find(typeName); - - error << "Unknown interface: " << interfaceName << " implemented by: " << typeName; + auto error = + std::format("Unknown interface: {} implemented by: {}", interfaceName, typeName); if (itrPosition != _typePositions.cend()) { - error << " line: " << itrPosition->second.line - << " column: " << itrPosition->second.column; + error += std::format(" line: {} column: {}", + itrPosition->second.line, + itrPosition->second.column); } - throw std::runtime_error(error.str()); + throw std::runtime_error(error); } return _interfaceTypes[itrType->second]; @@ -1343,24 +1333,24 @@ void SchemaLoader::validateInterfaceFields(std::string_view typeName, if (!unimplemented.empty()) { - std::ostringstream error; const auto itrPosition = _typePositions.find(typeName); - - error << "Missing interface fields type: " << typeName - << " interface: " << interfaceType.type; + auto error = std::format("Missing interface fields type: {} interface: {}", + typeName, + interfaceType.type); if (itrPosition != _typePositions.cend()) { - error << " line: " << itrPosition->second.line - << " column: " << itrPosition->second.column; + error += std::format(" line: {} column: {}", + itrPosition->second.line, + itrPosition->second.column); } for (auto fieldName : unimplemented) { - error << " field: " << fieldName; + error += std::format(" field: {}", fieldName); } - throw std::runtime_error(error.str()); + throw std::runtime_error(error); } } @@ -1383,18 +1373,17 @@ void SchemaLoader::validateTransitiveInterfaces( if (unimplemented.find(typeName) != unimplemented.cend()) { - std::ostringstream error; const auto itrPosition = _typePositions.find(typeName); - - error << "Interface cycle interface: " << typeName; + auto error = std::format("Interface cycle interface: {}", typeName); if (itrPosition != _typePositions.cend()) { - error << " line: " << itrPosition->second.line - << " column: " << itrPosition->second.column; + error += std::format(" line: {} column: {}", + itrPosition->second.line, + itrPosition->second.column); } - throw std::runtime_error(error.str()); + throw std::runtime_error(error); } for (auto entry : interfaces) @@ -1404,23 +1393,22 @@ void SchemaLoader::validateTransitiveInterfaces( if (!unimplemented.empty()) { - std::ostringstream error; const auto itrPosition = _typePositions.find(typeName); - - error << "Missing transitive interface type: " << typeName; + auto error = std::format("Missing transitive interface type: {}", typeName); if (itrPosition != _typePositions.cend()) { - error << " line: " << itrPosition->second.line - << " column: " << itrPosition->second.column; + error += std::format(" line: {} column: {}", + itrPosition->second.line, + itrPosition->second.column); } for (auto interfaceName : unimplemented) { - error << " interface: " << interfaceName; + error += std::format(" interface: {}", interfaceName); } - throw std::runtime_error(error.str()); + throw std::runtime_error(error); } } @@ -1549,13 +1537,13 @@ InputFieldList SchemaLoader::getInputFields(const peg::ast_node::children_t& fie && (field.modifiers.empty() || field.modifiers.front() != service::TypeModifier::Nullable)) { - std::ostringstream error; - - error << "Expected Non-Null default value for field name: " << field.name - << " line: " << defaultValueLocation.line - << " column: " << defaultValueLocation.column; + const auto error = std::format( + "Expected Non-Null default value for field name: {} line: {} column: {}", + field.name, + defaultValueLocation.line, + defaultValueLocation.column); - throw std::runtime_error(error.str()); + throw std::runtime_error(error); } inputFields.push_back(std::move(field)); @@ -1614,7 +1602,7 @@ const tao::graphqlpeg::position& SchemaLoader::getTypePosition(std::string_view return _typePositions.at(type); } -size_t SchemaLoader::getScalarIndex(std::string_view type) const +std::size_t SchemaLoader::getScalarIndex(std::string_view type) const { return _scalarNames.at(type); } @@ -1624,7 +1612,7 @@ const ScalarTypeList& SchemaLoader::getScalarTypes() const noexcept return _scalarTypes; } -size_t SchemaLoader::getEnumIndex(std::string_view type) const +std::size_t SchemaLoader::getEnumIndex(std::string_view type) const { return _enumNames.at(type); } @@ -1634,7 +1622,7 @@ const EnumTypeList& SchemaLoader::getEnumTypes() const noexcept return _enumTypes; } -size_t SchemaLoader::getInputIndex(std::string_view type) const +std::size_t SchemaLoader::getInputIndex(std::string_view type) const { return _inputNames.at(type); } @@ -1644,7 +1632,7 @@ const InputTypeList& SchemaLoader::getInputTypes() const noexcept return _inputTypes; } -size_t SchemaLoader::getUnionIndex(std::string_view type) const +std::size_t SchemaLoader::getUnionIndex(std::string_view type) const { return _unionNames.at(type); } @@ -1654,7 +1642,7 @@ const UnionTypeList& SchemaLoader::getUnionTypes() const noexcept return _unionTypes; } -size_t SchemaLoader::getInterfaceIndex(std::string_view type) const +std::size_t SchemaLoader::getInterfaceIndex(std::string_view type) const { return _interfaceNames.at(type); } @@ -1664,7 +1652,7 @@ const InterfaceTypeList& SchemaLoader::getInterfaceTypes() const noexcept return _interfaceTypes; } -size_t SchemaLoader::getObjectIndex(std::string_view type) const +std::size_t SchemaLoader::getObjectIndex(std::string_view type) const { return _objectNames.at(type); } @@ -1695,9 +1683,9 @@ std::string_view SchemaLoader::getCppType(std::string_view type) const noexcept if (itrBuiltin != s_builtinTypes.cend()) { - if (static_cast(itrBuiltin->second) < s_builtinCppTypes.size()) + if (static_cast(itrBuiltin->second) < s_builtinCppTypes.size()) { - return s_builtinCppTypes[static_cast(itrBuiltin->second)]; + return s_builtinCppTypes[static_cast(itrBuiltin->second)]; } } else @@ -1716,7 +1704,7 @@ std::string_view SchemaLoader::getCppType(std::string_view type) const noexcept std::string SchemaLoader::getInputCppType(const InputField& field) const noexcept { bool nonNull = true; - size_t templateCount = 0; + std::size_t templateCount = 0; std::ostringstream inputType; for (auto modifier : field.modifiers) @@ -1765,7 +1753,7 @@ std::string SchemaLoader::getInputCppType(const InputField& field) const noexcep inputType << getCppType(field.type); - for (size_t i = 0; i < templateCount; ++i) + for (std::size_t i = 0; i < templateCount; ++i) { inputType << R"cpp(>)cpp"; } @@ -1776,7 +1764,7 @@ std::string SchemaLoader::getInputCppType(const InputField& field) const noexcep std::string SchemaLoader::getOutputCppType(const OutputField& field) const noexcept { bool nonNull = true; - size_t templateCount = 0; + std::size_t templateCount = 0; std::ostringstream outputType; switch (field.fieldType) @@ -1855,7 +1843,7 @@ std::string SchemaLoader::getOutputCppType(const OutputField& field) const noexc break; } - for (size_t i = 0; i < templateCount; ++i) + for (std::size_t i = 0; i < templateCount; ++i) { outputType << R"cpp(>)cpp"; } diff --git a/src/SyntaxTree.cpp b/src/SyntaxTree.cpp index 524067ec..f51dc4d3 100644 --- a/src/SyntaxTree.cpp +++ b/src/SyntaxTree.cpp @@ -8,12 +8,13 @@ #include +#include +#include #include #include #include #include #include -#include #include using namespace std::literals; @@ -57,7 +58,7 @@ std::string_view ast_node::unescaped_view() const // Calculate the common indent const auto commonIndent = std::accumulate(lines.cbegin(), lines.cend(), - std::optional {}, + std::optional {}, [](auto value, const auto& line) noexcept { if (line) { @@ -79,7 +80,7 @@ std::string_view ast_node::unescaped_view() const { joined.reserve(std::accumulate(lines.cbegin(), lines.cend(), - size_t {}, + std::size_t {}, [trimIndent](auto value, const auto& line) noexcept { if (line) { @@ -118,8 +119,8 @@ std::string_view ast_node::unescaped_view() const joined.reserve(std::accumulate(children.cbegin(), children.cend(), - size_t(0), - [](size_t total, const std::unique_ptr& child) { + std::size_t(0), + [](std::size_t total, const std::unique_ptr& child) { return total + child->string_view().size(); })); @@ -642,7 +643,7 @@ struct ast_action : nothing struct [[nodiscard("unnecessary construction")]] depth_guard { - explicit depth_guard(size_t & depth) noexcept + explicit depth_guard(std::size_t& depth) noexcept : _depth(depth) { ++_depth; @@ -653,14 +654,14 @@ struct [[nodiscard("unnecessary construction")]] depth_guard --_depth; } - depth_guard(depth_guard &&) noexcept = delete; + depth_guard(depth_guard&&) noexcept = delete; depth_guard(const depth_guard&) = delete; depth_guard& operator=(depth_guard&&) noexcept = delete; depth_guard& operator=(const depth_guard&) = delete; private: - size_t& _depth; + std::size_t& _depth; }; template <> @@ -673,12 +674,11 @@ struct ast_action : maybe_nothing depth_guard guard(in.selectionSetDepth); if (in.selectionSetDepth > in.depthLimit()) { - std::ostringstream oss; + const auto error = std::format("Exceeded nested depth limit: {} for " + "https://spec.graphql.org/October2021/#SelectionSet", + in.depthLimit()); - oss << "Exceeded nested depth limit: " << in.depthLimit() - << " for https://spec.graphql.org/October2021/#SelectionSet"; - - throw parse_error(oss.str(), in); + throw parse_error(error, in); } return tao::graphqlpeg::template match(in, st...); @@ -981,21 +981,21 @@ class [[nodiscard("unnecessary construction")]] depth_limit_input : public Parse { public: template - explicit depth_limit_input(size_t depthLimit, Args && ... args) noexcept + explicit depth_limit_input(std::size_t depthLimit, Args&&... args) noexcept : ParseInput(std::forward(args)...) , _depthLimit(depthLimit) { } - size_t depthLimit() const noexcept + std::size_t depthLimit() const noexcept { return _depthLimit; } - size_t selectionSetDepth = 0; + std::size_t selectionSetDepth = 0; private: - const size_t _depthLimit; + const std::size_t _depthLimit; }; using ast_file = depth_limit_input>; @@ -1018,7 +1018,7 @@ struct [[nodiscard("unnecessary construction")]] ast_input std::variant, ast_string_view> data; }; -ast parseSchemaString(std::string_view input, size_t depthLimit) +ast parseSchemaString(std::string_view input, std::size_t depthLimit) { ast result { std::make_shared( ast_input { ast_string { { input.cbegin(), input.cend() } } }), @@ -1050,7 +1050,7 @@ ast parseSchemaString(std::string_view input, size_t depthLimit) return result; } -ast parseSchemaFile(std::string_view filename, size_t depthLimit) +ast parseSchemaFile(std::string_view filename, std::size_t depthLimit) { ast result; @@ -1081,7 +1081,7 @@ ast parseSchemaFile(std::string_view filename, size_t depthLimit) return result; } -ast parseString(std::string_view input, size_t depthLimit) +ast parseString(std::string_view input, std::size_t depthLimit) { ast result { std::make_shared( ast_input { ast_string { { input.cbegin(), input.cend() } } }), @@ -1114,7 +1114,7 @@ ast parseString(std::string_view input, size_t depthLimit) return result; } -ast parseFile(std::string_view filename, size_t depthLimit) +ast parseFile(std::string_view filename, std::size_t depthLimit) { ast result; @@ -1148,7 +1148,7 @@ ast parseFile(std::string_view filename, size_t depthLimit) } // namespace peg -peg::ast operator""_graphql(const char* text, size_t size) +peg::ast operator"" _graphql(const char* text, std::size_t size) { peg::ast result { std::make_shared( peg::ast_input { peg::ast_string_view { { text, size } } }), diff --git a/src/TaoCppJSONResponse.cpp b/src/TaoCppJSONResponse.cpp new file mode 100644 index 00000000..3616d621 --- /dev/null +++ b/src/TaoCppJSONResponse.cpp @@ -0,0 +1,287 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "graphqlservice/JSONResponse.h" + +#include + +#include +#include +#include +#include +#include + +namespace graphql::response { + +class StreamWriter : public std::enable_shared_from_this +{ +public: + StreamWriter(std::ostream& stream) + : _writer { stream } + { + } + + void add_value(std::shared_ptr&& value) + { + auto writer = std::make_shared(shared_from_this()); + + ValueTokenStream(Value { *value }).visit(writer); + } + + void reserve(std::size_t /* count */) + { + } + + void start_object() + { + _scopeStack.push_back(Scope::Object); + _writer.begin_object(); + } + + void add_member(std::string&& key) + { + _writer.key(key); + } + + void end_object() + { + _writer.end_object(); + _scopeStack.pop_back(); + end_value(); + } + + void start_array() + { + _scopeStack.push_back(Scope::Object); + _writer.begin_array(); + } + + void end_array() + { + _writer.end_array(); + _scopeStack.pop_back(); + end_value(); + } + + void add_null() + { + _writer.null(); + end_value(); + } + + void add_string(std::string&& value) + { + _writer.string(value); + end_value(); + } + + void add_enum(std::string&& value) + { + add_string(std::move(value)); + } + + void add_id(IdType&& value) + { + add_string(value.release()); + } + + void add_bool(bool value) + { + _writer.boolean(value); + end_value(); + } + + void add_int(int value) + { + _writer.number(static_cast(value)); + end_value(); + } + + void add_float(double value) + { + _writer.number(value); + end_value(); + } + + void complete() + { + } + +private: + enum class Scope + { + Array, + Object, + }; + + void end_value() + { + if (_scopeStack.empty()) + { + return; + } + + switch (_scopeStack.back()) + { + case Scope::Array: + _writer.element(); + break; + + case Scope::Object: + _writer.member(); + break; + } + } + + tao::json::events::to_stream _writer; + std::vector _scopeStack; +}; + +std::string toJSON(Value&& response) +{ + std::ostringstream stream; + auto writer = std::make_shared(std::make_shared(stream)); + + ValueTokenStream(std::move(response)).visit(writer); + + return stream.str(); +} + +struct ResponseHandler +{ + ResponseHandler() + { + // Start with a single null value. + _responseStack.push_back({}); + } + + Value getResponse() + { + auto response = std::move(_responseStack.back()); + + _responseStack.pop_back(); + + return response; + } + + void null() + { + setValue(Value()); + } + + void boolean(bool b) + { + setValue(Value(b)); + } + + void number(double d) + { + auto value = Value(Type::Float); + + value.set(std::move(d)); + setValue(std::move(value)); + } + + void number(std::int64_t i) + { + if (i < std::numeric_limits::min() + || i > std::numeric_limits::max()) + { + // https://spec.graphql.org/October2021/#sec-Int + number(static_cast(i)); + } + else + { + static_assert(sizeof(std::int32_t) == sizeof(IntType), + "GraphQL only supports 32-bit signed integers"); + auto value = Value(Type::Int); + + value.set(static_cast(i)); + setValue(std::move(value)); + } + } + + void number(std::uint64_t i) + { + if (i > static_cast(std::numeric_limits::max())) + { + // https://spec.graphql.org/October2021/#sec-Int + number(static_cast(i)); + } + else + { + number(static_cast(i)); + } + } + + void string(std::string&& str) + { + setValue(Value(std::move(str)).from_json()); + } + + void begin_array() + { + _responseStack.push_back(Value(Type::List)); + } + + void element() + { + } + + void end_array() + { + setValue(getResponse()); + } + + void begin_object() + { + _responseStack.push_back(Value(Type::Map)); + } + + void key(std::string&& str) + { + _keyStack.push_back(std::move(str)); + } + + void member() + { + } + + void end_object() + { + setValue(getResponse()); + } + +private: + void setValue(Value&& value) + { + switch (_responseStack.back().type()) + { + case Type::Map: + _responseStack.back().emplace_back(std::move(_keyStack.back()), std::move(value)); + _keyStack.pop_back(); + break; + + case Type::List: + _responseStack.back().emplace_back(std::move(value)); + break; + + default: + _responseStack.back() = std::move(value); + break; + } + } + + std::vector _keyStack; + std::vector _responseStack; +}; + +Value parseJSON(const std::string& json) +{ + ResponseHandler handler; + tao::json::events::from_string(handler, json); + + return handler.getResponse(); +} + +} // namespace graphql::response diff --git a/src/Validation.cpp b/src/Validation.cpp index a95c8624..a065fad1 100644 --- a/src/Validation.cpp +++ b/src/Validation.cpp @@ -9,6 +9,7 @@ #include "graphqlservice/introspection/IntrospectionSchema.h" #include +#include #include #include #include @@ -245,11 +246,9 @@ void ValidateArgumentValueVisitor::visitObjectValue(const peg::ast_node& objectV { // https://spec.graphql.org/October2021/#sec-Input-Object-Field-Uniqueness auto fieldPosition = field->begin(); - std::ostringstream message; + auto message = std::format("Conflicting input field name: {}", name); - message << "Conflicting input field name: " << name; - - _errors.push_back({ message.str(), { fieldPosition.line, fieldPosition.column } }); + _errors.push_back({ std::move(message), { fieldPosition.line, fieldPosition.column } }); continue; } @@ -493,11 +492,9 @@ void ValidateExecutableVisitor::visit(const peg::ast_node& root) { // https://spec.graphql.org/October2021/#sec-Fragment-Name-Uniqueness auto position = fragmentDefinition.begin(); - std::ostringstream error; - - error << "Duplicate fragment name: " << inserted.first->first; + auto error = std::format("Duplicate fragment name: {}", inserted.first->first); - _errors.push_back({ error.str(), { position.line, position.column } }); + _errors.push_back({ std::move(error), { position.line, position.column } }); } }); @@ -517,11 +514,9 @@ void ValidateExecutableVisitor::visit(const peg::ast_node& root) { // https://spec.graphql.org/October2021/#sec-Operation-Name-Uniqueness auto position = operationDefinition.begin(); - std::ostringstream error; - - error << "Duplicate operation name: " << inserted.first->first; + auto error = std::format("Duplicate operation name: {}", inserted.first->first); - _errors.push_back({ error.str(), { position.line, position.column } }); + _errors.push_back({ std::move(error), { position.line, position.column } }); } }); @@ -574,16 +569,14 @@ void ValidateExecutableVisitor::visit(const peg::ast_node& root) unreferencedFragments.erase(name); } - std::transform(unreferencedFragments.begin(), - unreferencedFragments.end(), + std::ranges::transform(unreferencedFragments, std::back_inserter(_errors), [](const auto& fragmentDefinition) noexcept { auto position = fragmentDefinition.second.get().begin(); - std::ostringstream message; + auto message = + std::format("Unused fragment definition name: {}", fragmentDefinition.first); - message << "Unused fragment definition name: " << fragmentDefinition.first; - - return schema_error { message.str(), { position.line, position.column } }; + return schema_error { std::move(message), { position.line, position.column } }; }); } } @@ -619,13 +612,12 @@ void ValidateExecutableVisitor::visitFragmentDefinition(const peg::ast_node& fra // https://spec.graphql.org/October2021/#sec-Fragment-Spread-Type-Existence // https://spec.graphql.org/October2021/#sec-Fragments-On-Composite-Types auto position = typeCondition->begin(); - std::ostringstream message; - - message << (itrType == _types.end() ? "Undefined target type on fragment definition: " - : "Scalar target type on fragment definition: ") - << name << " name: " << innerType; + auto message = std::format("{} target type on fragment definition: {} name: {}", + (itrType == _types.end() ? "Undefined" : "Scalar"), + name, + innerType); - _errors.push_back({ message.str(), { position.line, position.column } }); + _errors.push_back({ std::move(message), { position.line, position.column } }); return; } @@ -657,100 +649,94 @@ void ValidateExecutableVisitor::visitOperationDefinition(const peg::ast_node& op _operationVariables = std::make_optional(); - peg::for_each_child(operationDefinition, - [this, operationName](const peg::ast_node& variable) { - std::string_view variableName; - ValidateArgument variableArgument; + peg::for_each_child< + peg::variable>(operationDefinition, [this, operationName](const peg::ast_node& variable) { + std::string_view variableName; + ValidateArgument variableArgument; - for (const auto& child : variable.children) + for (const auto& child : variable.children) + { + if (child->is_type()) { - if (child->is_type()) + // Skip the $ prefix + variableName = child->string_view().substr(1); + + if (_operationVariables->find(variableName) != _operationVariables->end()) { - // Skip the $ prefix - variableName = child->string_view().substr(1); + // https://spec.graphql.org/October2021/#sec-Variable-Uniqueness + auto position = child->begin(); + auto message = "Conflicting variable"s; - if (_operationVariables->find(variableName) != _operationVariables->end()) + if (!operationName.empty()) { - // https://spec.graphql.org/October2021/#sec-Variable-Uniqueness - auto position = child->begin(); - std::ostringstream message; + message += std::format(" operation: {}", operationName); + } - message << "Conflicting variable"; + message += std::format(" name: {}", variableName); - if (!operationName.empty()) - { - message << " operation: " << operationName; - } + _errors.push_back({ std::move(message), { position.line, position.column } }); + return; + } + } + else if (child->is_type() || child->is_type() + || child->is_type()) + { + ValidateVariableTypeVisitor visitor(_schema, _types); - message << " name: " << variableName; + visitor.visit(*child); - _errors.push_back({ message.str(), { position.line, position.column } }); - return; - } - } - else if (child->is_type() || child->is_type() - || child->is_type()) + if (!visitor.isInputType()) { - ValidateVariableTypeVisitor visitor(_schema, _types); + // https://spec.graphql.org/October2021/#sec-Variables-Are-Input-Types + auto position = child->begin(); + auto message = "Invalid variable type"s; - visitor.visit(*child); - - if (!visitor.isInputType()) + if (!operationName.empty()) { - // https://spec.graphql.org/October2021/#sec-Variables-Are-Input-Types - auto position = child->begin(); - std::ostringstream message; - - message << "Invalid variable type"; - - if (!operationName.empty()) - { - message << " operation: " << operationName; - } - - message << " name: " << variableName; - - _errors.push_back({ message.str(), { position.line, position.column } }); - return; + message += std::format(" operation: {}", operationName); } - variableArgument.type = visitor.getType(); - } - else if (child->is_type()) - { - ValidateArgumentValueVisitor visitor(_errors); - - visitor.visit(*child->children.back()); + message += std::format(" name: {}", variableName); - auto argument = visitor.getArgumentValue(); + _errors.push_back({ std::move(message), { position.line, position.column } }); + return; + } - if (!validateInputValue(false, argument, variableArgument.type)) - { - // https://spec.graphql.org/October2021/#sec-Values-of-Correct-Type - auto position = child->begin(); - std::ostringstream message; + variableArgument.type = visitor.getType(); + } + else if (child->is_type()) + { + ValidateArgumentValueVisitor visitor(_errors); - message << "Incompatible variable default value"; + visitor.visit(*child->children.back()); - if (!operationName.empty()) - { - message << " operation: " << operationName; - } + auto argument = visitor.getArgumentValue(); - message << " name: " << variableName; + if (!validateInputValue(false, argument, variableArgument.type)) + { + // https://spec.graphql.org/October2021/#sec-Values-of-Correct-Type + auto position = child->begin(); + auto message = "Incompatible variable default value"s; - _errors.push_back({ message.str(), { position.line, position.column } }); - return; + if (!operationName.empty()) + { + message += std::format(" operation: {}", operationName); } - variableArgument.defaultValue = true; - variableArgument.nonNullDefaultValue = argument.value != nullptr; + message += std::format(" name: {}", variableName); + + _errors.push_back({ std::move(message), { position.line, position.column } }); + return; } + + variableArgument.defaultValue = true; + variableArgument.nonNullDefaultValue = argument.value != nullptr; } + } - _variableDefinitions.emplace(variableName, variable); - _operationVariables->emplace(variableName, std::move(variableArgument)); - }); + _variableDefinitions.emplace(variableName, variable); + _operationVariables->emplace(variableName, std::move(variableArgument)); + }); peg::on_first_child(operationDefinition, [this, &operationType](const peg::ast_node& child) { @@ -773,11 +759,9 @@ void ValidateExecutableVisitor::visitOperationDefinition(const peg::ast_node& op if (itrType == _operationTypes.end()) { auto position = operationDefinition.begin(); - std::ostringstream error; + auto error = std::format("Unsupported operation type: {}", operationType); - error << "Unsupported operation type: " << operationType; - - _errors.push_back({ error.str(), { position.line, position.column } }); + _errors.push_back({ std::move(error), { position.line, position.column } }); return; } @@ -795,32 +779,28 @@ void ValidateExecutableVisitor::visitOperationDefinition(const peg::ast_node& op { // https://spec.graphql.org/October2021/#sec-Single-root-field auto position = operationDefinition.begin(); - std::ostringstream error; - - error << "Subscription with more than one root field"; + auto error = "Subscription with more than one root field"s; if (!operationName.empty()) { - error << " name: " << operationName; + error += std::format(" name: {}", operationName); } - _errors.push_back({ error.str(), { position.line, position.column } }); + _errors.push_back({ std::move(error), { position.line, position.column } }); } if (_introspectionFieldCount != 0) { // https://spec.graphql.org/October2021/#sec-Single-root-field auto position = operationDefinition.begin(); - std::ostringstream error; - - error << "Subscription with Introspection root field"; + auto error = "Subscription with Introspection root field"s; if (!operationName.empty()) { - error << " name: " << operationName; + error += std::format(" name: {}", operationName); } - _errors.push_back({ error.str(), { position.line, position.column } }); + _errors.push_back({ std::move(error), { position.line, position.column } }); } } @@ -834,11 +814,9 @@ void ValidateExecutableVisitor::visitOperationDefinition(const peg::ast_node& op { // https://spec.graphql.org/October2021/#sec-All-Variables-Used auto position = variable.second.get().begin(); - std::ostringstream error; - - error << "Unused variable name: " << variable.first; + auto error = std::format("Unused variable name: {}", variable.first); - _errors.push_back({ error.str(), { position.line, position.column } }); + _errors.push_back({ std::move(error), { position.line, position.column } }); } } @@ -948,11 +926,9 @@ bool ValidateExecutableVisitor::validateInputValue( if (itrVariable == _operationVariables->end()) { // https://spec.graphql.org/October2021/#sec-All-Variable-Uses-Defined - std::ostringstream message; - - message << "Undefined variable name: " << variable.name; + auto message = std::format("Undefined variable name: {}", variable.name); - _errors.push_back({ message.str(), argument.position }); + _errors.push_back({ std::move(message), argument.position }); return false; } @@ -1045,11 +1021,9 @@ bool ValidateExecutableVisitor::validateInputValue( if (!std::holds_alternative(argument.value->data)) { - std::ostringstream message; + auto message = std::format("Expected Input Object value name: {}", name); - message << "Expected Input Object value name: " << name; - - _errors.push_back({ message.str(), argument.position }); + _errors.push_back({ std::move(message), argument.position }); return false; } @@ -1057,11 +1031,9 @@ bool ValidateExecutableVisitor::validateInputValue( if (itrFields == _inputTypeFields.end()) { - std::ostringstream message; - - message << "Expected Input Object fields name: " << name; + auto message = std::format("Expected Input Object fields name: {}", name); - _errors.push_back({ message.str(), argument.position }); + _errors.push_back({ std::move(message), argument.position }); return false; } @@ -1076,12 +1048,11 @@ bool ValidateExecutableVisitor::validateInputValue( if (itrField == itrFields->second.end()) { // https://spec.graphql.org/October2021/#sec-Input-Object-Field-Names - std::ostringstream message; + auto message = std::format("Undefined Input Object field type: {} name: {}", + name, + entry.first); - message << "Undefined Input Object field type: " << name - << " name: " << entry.first; - - _errors.push_back({ message.str(), entry.second.position }); + _errors.push_back({ std::move(message), entry.second.position }); return false; } @@ -1113,12 +1084,11 @@ bool ValidateExecutableVisitor::validateInputValue( if (!entry.second.type) { - std::ostringstream message; - - message << "Unknown Input Object field type: " << name - << " name: " << entry.first; + auto message = std::format("Unknown Input Object field type: {} name: {}", + name, + entry.first); - _errors.push_back({ message.str(), argument.position }); + _errors.push_back({ std::move(message), argument.position }); return false; } @@ -1127,12 +1097,11 @@ bool ValidateExecutableVisitor::validateInputValue( if (fieldKind == introspection::TypeKind::NON_NULL) { // https://spec.graphql.org/October2021/#sec-Input-Object-Required-Fields - std::ostringstream message; - - message << "Missing Input Object field type: " << name - << " name: " << entry.first; + auto message = std::format("Missing Input Object field type: {} name: {}", + name, + entry.first); - _errors.push_back({ message.str(), argument.position }); + _errors.push_back({ std::move(message), argument.position }); return false; } } @@ -1152,11 +1121,9 @@ bool ValidateExecutableVisitor::validateInputValue( if (!std::holds_alternative(argument.value->data)) { - std::ostringstream message; + auto message = std::format("Expected Enum value name: {}", name); - message << "Expected Enum value name: " << name; - - _errors.push_back({ message.str(), argument.position }); + _errors.push_back({ std::move(message), argument.position }); return false; } @@ -1166,11 +1133,9 @@ bool ValidateExecutableVisitor::validateInputValue( if (itrEnumValues == _enumValues.end() || itrEnumValues->second.find(value) == itrEnumValues->second.end()) { - std::ostringstream message; - - message << "Undefined Enum value type: " << name << " name: " << value; + auto message = std::format("Undefined Enum value type: {} name: {}", name, value); - _errors.push_back({ message.str(), argument.position }); + _errors.push_back({ std::move(message), argument.position }); return false; } @@ -1231,11 +1196,9 @@ bool ValidateExecutableVisitor::validateInputValue( if (_scalarTypes.find(name) == _scalarTypes.end()) { - std::ostringstream message; + auto message = std::format("Undefined Scalar type name: {}", name); - message << "Undefined Scalar type name: " << name; - - _errors.push_back({ message.str(), argument.position }); + _errors.push_back({ std::move(message), argument.position }); return false; } @@ -1398,11 +1361,10 @@ bool ValidateExecutableVisitor::validateVariableType(bool isNonNull, if (variableName != inputName) { // https://spec.graphql.org/October2021/#sec-All-Variable-Usages-are-Allowed - std::ostringstream message; - - message << "Incompatible variable type: " << variableName << " name: " << inputName; + auto message = + std::format("Incompatible variable type: {} name: {}", variableName, inputName); - _errors.push_back({ message.str(), position }); + _errors.push_back({ std::move(message), position }); return false; } @@ -1563,11 +1525,10 @@ void ValidateExecutableVisitor::visitField(const peg::ast_node& field) { // https://spec.graphql.org/October2021/#sec-Leaf-Field-Selections auto position = field.begin(); - std::ostringstream message; + auto message = + std::format("Field on scalar type: {} name: {}", _scopedType->get().name(), name); - message << "Field on scalar type: " << _scopedType->get().name() << " name: " << name; - - _errors.push_back({ message.str(), { position.line, position.column } }); + _errors.push_back({ std::move(message), { position.line, position.column } }); return; } @@ -1591,12 +1552,11 @@ void ValidateExecutableVisitor::visitField(const peg::ast_node& field) { // https://spec.graphql.org/October2021/#sec-Leaf-Field-Selections auto position = field.begin(); - std::ostringstream message; - - message << "Field on union type: " << _scopedType->get().name() - << " name: " << name; + auto message = std::format("Field on union type: {} name: {}", + _scopedType->get().name(), + name); - _errors.push_back({ message.str(), { position.line, position.column } }); + _errors.push_back({ std::move(message), { position.line, position.column } }); return; } @@ -1615,11 +1575,10 @@ void ValidateExecutableVisitor::visitField(const peg::ast_node& field) { // https://spec.graphql.org/October2021/#sec-Field-Selections auto position = field.begin(); - std::ostringstream message; + auto message = + std::format("Undefined field type: {} name: {}", _scopedType->get().name(), name); - message << "Undefined field type: " << _scopedType->get().name() << " name: " << name; - - _errors.push_back({ message.str(), { position.line, position.column } }); + _errors.push_back({ std::move(message), { position.line, position.column } }); return; } @@ -1649,12 +1608,12 @@ void ValidateExecutableVisitor::visitField(const peg::ast_node& field) if (validateArguments.find(argumentName) != validateArguments.end()) { // https://spec.graphql.org/October2021/#sec-Argument-Uniqueness - std::ostringstream message; - - message << "Conflicting argument type: " << _scopedType->get().name() - << " field: " << name << " name: " << argumentName; + auto message = std::format("Conflicting argument type: {} field: {} name: {}", + _scopedType->get().name(), + name, + argumentName); - _errors.push_back({ message.str(), { position.line, position.column } }); + _errors.push_back({ std::move(message), { position.line, position.column } }); continue; } @@ -1687,11 +1646,10 @@ void ValidateExecutableVisitor::visitField(const peg::ast_node& field) { // https://spec.graphql.org/October2021/#sec-Field-Selection-Merging auto position = field.begin(); - std::ostringstream message; - - message << "Conflicting field type: " << _scopedType->get().name() << " name: " << name; + auto message = + std::format("Conflicting field type: {} name: {}", _scopedType->get().name(), name); - _errors.push_back({ message.str(), { position.line, position.column } }); + _errors.push_back({ std::move(message), { position.line, position.column } }); } } @@ -1706,12 +1664,12 @@ void ValidateExecutableVisitor::visitField(const peg::ast_node& field) if (itrArgument == itrField->second.arguments.end()) { // https://spec.graphql.org/October2021/#sec-Argument-Names - std::ostringstream message; + auto message = std::format("Undefined argument type: {} field: {} name: {}", + _scopedType->get().name(), + name, + argumentName); - message << "Undefined argument type: " << _scopedType->get().name() - << " field: " << name << " name: " << argumentName; - - _errors.push_back({ message.str(), argumentLocations[argumentName] }); + _errors.push_back({ std::move(message), argumentLocations[argumentName] }); } } @@ -1728,12 +1686,12 @@ void ValidateExecutableVisitor::visitField(const peg::ast_node& field) argument.second.type)) { // https://spec.graphql.org/October2021/#sec-Values-of-Correct-Type - std::ostringstream message; - - message << "Incompatible argument type: " << _scopedType->get().name() - << " field: " << name << " name: " << argument.first; + auto message = std::format("Incompatible argument type: {} field: {} name: {}", + _scopedType->get().name(), + name, + argument.first); - _errors.push_back({ message.str(), argumentLocations[argument.first] }); + _errors.push_back({ std::move(message), argumentLocations[argument.first] }); } continue; @@ -1750,14 +1708,13 @@ void ValidateExecutableVisitor::visitField(const peg::ast_node& field) { // https://spec.graphql.org/October2021/#sec-Required-Arguments auto position = field.begin(); - std::ostringstream message; + auto message = std::format("{} argument type: {} field: {} name: {}", + (missing ? "Missing" : "Required non-null"), + _scopedType->get().name(), + name, + argument.first); - message << (missing ? "Missing argument type: " - : "Required non-null argument type: ") - << _scopedType->get().name() << " field: " << name - << " name: " << argument.first; - - _errors.push_back({ message.str(), { position.line, position.column } }); + _errors.push_back({ std::move(message), { position.line, position.column } }); } } } @@ -1770,7 +1727,7 @@ void ValidateExecutableVisitor::visitField(const peg::ast_node& field) selection = &child; }); - size_t subFieldCount = 0; + std::size_t subFieldCount = 0; if (selection != nullptr) { @@ -1798,11 +1755,10 @@ void ValidateExecutableVisitor::visitField(const peg::ast_node& field) { // https://spec.graphql.org/October2021/#sec-Leaf-Field-Selections auto position = field.begin(); - std::ostringstream message; - - message << "Missing fields on non-scalar type: " << innerType->get().name(); + auto message = + std::format("Missing fields on non-scalar type: {}", innerType->get().name()); - _errors.push_back({ message.str(), { position.line, position.column } }); + _errors.push_back({ std::move(message), { position.line, position.column } }); return; } @@ -1830,11 +1786,9 @@ void ValidateExecutableVisitor::visitFragmentSpread(const peg::ast_node& fragmen { // https://spec.graphql.org/October2021/#sec-Fragment-spread-target-defined auto position = fragmentSpread.begin(); - std::ostringstream message; - - message << "Undefined fragment spread name: " << name; + auto message = std::format("Undefined fragment spread name: {}", name); - _errors.push_back({ message.str(), { position.line, position.column } }); + _errors.push_back({ std::move(message), { position.line, position.column } }); return; } @@ -1844,11 +1798,9 @@ void ValidateExecutableVisitor::visitFragmentSpread(const peg::ast_node& fragmen { // https://spec.graphql.org/October2021/#sec-Fragment-spreads-must-not-form-cycles auto position = fragmentSpread.begin(); - std::ostringstream message; + auto message = std::format("Cyclic fragment spread name: {}", name); - message << "Cyclic fragment spread name: " << name; - - _errors.push_back({ message.str(), { position.line, position.column } }); + _errors.push_back({ std::move(message), { position.line, position.column } }); } return; @@ -1863,11 +1815,10 @@ void ValidateExecutableVisitor::visitFragmentSpread(const peg::ast_node& fragmen { // https://spec.graphql.org/October2021/#sec-Fragment-spread-is-possible auto position = fragmentSpread.begin(); - std::ostringstream message; - - message << "Incompatible fragment spread target type: " << innerType << " name: " << name; + auto message = + std::format("Incompatible fragment spread target type: {} name: {}", innerType, name); - _errors.push_back({ message.str(), { position.line, position.column } }); + _errors.push_back({ std::move(message), { position.line, position.column } }); return; } @@ -1914,11 +1865,10 @@ void ValidateExecutableVisitor::visitInlineFragment(const peg::ast_node& inlineF if (itrInner == _types.end()) { // https://spec.graphql.org/October2021/#sec-Fragment-Spread-Type-Existence - std::ostringstream message; + auto message = + std::format("Undefined target type on inline fragment name: {}", innerType); - message << "Undefined target type on inline fragment name: " << innerType; - - _errors.push_back({ message.str(), std::move(typeConditionLocation) }); + _errors.push_back({ std::move(message), std::move(typeConditionLocation) }); return; } @@ -1928,14 +1878,11 @@ void ValidateExecutableVisitor::visitInlineFragment(const peg::ast_node& inlineF { // https://spec.graphql.org/October2021/#sec-Fragments-On-Composite-Types // https://spec.graphql.org/October2021/#sec-Fragment-spread-is-possible - std::ostringstream message; - - message << (isScalarType(fragmentType->get().kind()) - ? "Scalar target type on inline fragment name: " - : "Incompatible target type on inline fragment name: ") - << innerType; + auto message = std::format("{} target type on inline fragment name: {}", + (isScalarType(fragmentType->get().kind()) ? "Scalar" : "Incompatible"), + innerType); - _errors.push_back({ message.str(), std::move(typeConditionLocation) }); + _errors.push_back({ std::move(message), std::move(typeConditionLocation) }); return; } } @@ -1972,11 +1919,9 @@ void ValidateExecutableVisitor::visitDirectives( { // https://spec.graphql.org/October2021/#sec-Directives-Are-Defined auto position = directive->begin(); - std::ostringstream message; - - message << "Undefined directive name: " << directiveName; + auto message = std::format("Undefined directive name: {}", directiveName); - _errors.push_back({ message.str(), { position.line, position.column } }); + _errors.push_back({ std::move(message), { position.line, position.column } }); continue; } @@ -1984,11 +1929,9 @@ void ValidateExecutableVisitor::visitDirectives( { // https://spec.graphql.org/October2021/#sec-Directives-Are-Unique-Per-Location auto position = directive->begin(); - std::ostringstream message; + auto message = std::format("Conflicting directive name: {}", directiveName); - message << "Conflicting directive name: " << directiveName; - - _errors.push_back({ message.str(), { position.line, position.column } }); + _errors.push_back({ std::move(message), { position.line, position.column } }); continue; } @@ -1996,45 +1939,43 @@ void ValidateExecutableVisitor::visitDirectives( { // https://spec.graphql.org/October2021/#sec-Directives-Are-In-Valid-Locations auto position = directive->begin(); - std::ostringstream message; - - message << "Unexpected location for directive: " << directiveName; + auto message = std::format("Unexpected location for directive: {}", directiveName); switch (location) { case introspection::DirectiveLocation::QUERY: - message << " name: QUERY"; + message += " name: QUERY"sv; break; case introspection::DirectiveLocation::MUTATION: - message << " name: MUTATION"; + message += " name: MUTATION"sv; break; case introspection::DirectiveLocation::SUBSCRIPTION: - message << " name: SUBSCRIPTION"; + message += " name: SUBSCRIPTION"sv; break; case introspection::DirectiveLocation::FIELD: - message << " name: FIELD"; + message += " name: FIELD"sv; break; case introspection::DirectiveLocation::FRAGMENT_DEFINITION: - message << " name: FRAGMENT_DEFINITION"; + message += " name: FRAGMENT_DEFINITION"sv; break; case introspection::DirectiveLocation::FRAGMENT_SPREAD: - message << " name: FRAGMENT_SPREAD"; + message += " name: FRAGMENT_SPREAD"sv; break; case introspection::DirectiveLocation::INLINE_FRAGMENT: - message << " name: INLINE_FRAGMENT"; + message += " name: INLINE_FRAGMENT"sv; break; default: break; } - _errors.push_back({ message.str(), { position.line, position.column } }); + _errors.push_back({ std::move(message), { position.line, position.column } }); continue; } @@ -2052,12 +1993,12 @@ void ValidateExecutableVisitor::visitDirectives( if (validateArguments.find(argumentName) != validateArguments.end()) { // https://spec.graphql.org/October2021/#sec-Argument-Uniqueness - std::ostringstream message; + auto message = std::format("Conflicting argument directive: {} name: {}", + directiveName, + argumentName); - message << "Conflicting argument directive: " << directiveName - << " name: " << argumentName; - - _errors.push_back({ message.str(), { position.line, position.column } }); + _errors.push_back( + { std::move(message), { position.line, position.column } }); continue; } @@ -2076,12 +2017,11 @@ void ValidateExecutableVisitor::visitDirectives( if (itrArgument == itrDirective->second.arguments.end()) { // https://spec.graphql.org/October2021/#sec-Argument-Names - std::ostringstream message; - - message << "Undefined argument directive: " << directiveName - << " name: " << argumentName; + auto message = std::format("Undefined argument directive: {} name: {}", + directiveName, + argumentName); - _errors.push_back({ message.str(), argumentLocations[argumentName] }); + _errors.push_back({ std::move(message), argumentLocations[argumentName] }); } } @@ -2098,12 +2038,13 @@ void ValidateExecutableVisitor::visitDirectives( argument.second.type)) { // https://spec.graphql.org/October2021/#sec-Values-of-Correct-Type - std::ostringstream message; + auto message = + std::format("Incompatible argument directive: {} name: {}", + directiveName, + argument.first); - message << "Incompatible argument directive: " << directiveName - << " name: " << argument.first; - - _errors.push_back({ message.str(), argumentLocations[argument.first] }); + _errors.push_back( + { std::move(message), argumentLocations[argument.first] }); } continue; @@ -2120,13 +2061,13 @@ void ValidateExecutableVisitor::visitDirectives( { // https://spec.graphql.org/October2021/#sec-Required-Arguments auto position = directive->begin(); - std::ostringstream message; - - message << (missing ? "Missing argument directive: " - : "Required non-null argument directive: ") - << directiveName << " name: " << argument.first; + auto message = std::format("{} argument directive: {} name: {}", + (missing ? "Missing" : "Required non-null"), + directiveName, + argument.first); - _errors.push_back({ message.str(), { position.line, position.column } }); + _errors.push_back( + { std::move(message), { position.line, position.column } }); } } }); diff --git a/src/introspection/CMakeLists.txt b/src/introspection/CMakeLists.txt index 82b8203d..a30faa97 100644 --- a/src/introspection/CMakeLists.txt +++ b/src/introspection/CMakeLists.txt @@ -1,7 +1,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.28) # Normally this would be handled by find_package(cppgraphqlgen CONFIG). include(${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/cppgraphqlgen-functions.cmake) @@ -10,18 +10,14 @@ if(GRAPHQL_UPDATE_SAMPLES) update_graphql_schema_files(introspection schema.introspection.graphql Introspection introspection --introspection) file(GLOB PRIVATE_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/*.h) + file(GLOB PRIVATE_MODULES ${CMAKE_CURRENT_SOURCE_DIR}/*.ixx) add_custom_command( OUTPUT copied_introspection_schema_headers - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PRIVATE_HEADERS} ${CMAKE_CURRENT_SOURCE_DIR}/../../include/graphqlservice/introspection/ + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PRIVATE_HEADERS} ${PRIVATE_MODULES} ${CMAKE_CURRENT_SOURCE_DIR}/../../include/graphqlservice/introspection/ COMMAND ${CMAKE_COMMAND} -E touch copied_introspection_schema_headers - DEPENDS ${PRIVATE_HEADERS} ${CMAKE_CURRENT_SOURCE_DIR}/introspection_schema_files + DEPENDS ${PRIVATE_HEADERS} ${PRIVATE_MODULES} ${CMAKE_CURRENT_SOURCE_DIR}/introspection_schema_files COMMENT "Updating IntrospectionSchema headers") add_custom_target(copy_introspection_schema_headers ALL DEPENDS copied_introspection_schema_headers) endif() - -file(GLOB PUBLIC_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/../../include/graphqlservice/introspection/*.h) -install(FILES ${PUBLIC_HEADERS} - CONFIGURATIONS ${GRAPHQL_INSTALL_CONFIGURATIONS} - DESTINATION ${GRAPHQL_INSTALL_INCLUDE_DIR}/graphqlservice/introspection) diff --git a/src/introspection/DirectiveObject.cpp b/src/introspection/DirectiveObject.cpp index 7f1c19be..4da18e52 100644 --- a/src/introspection/DirectiveObject.cpp +++ b/src/introspection/DirectiveObject.cpp @@ -10,7 +10,6 @@ #include #include -#include #include #include diff --git a/src/introspection/DirectiveObject.h b/src/introspection/DirectiveObject.h index 20a0620e..f9714caf 100644 --- a/src/introspection/DirectiveObject.h +++ b/src/introspection/DirectiveObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef DIRECTIVEOBJECT_H -#define DIRECTIVEOBJECT_H +#ifndef INTROSPECTION_DIRECTIVEOBJECT_H +#define INTROSPECTION_DIRECTIVEOBJECT_H #include "IntrospectionSchema.h" @@ -85,4 +85,4 @@ class [[nodiscard("unnecessary construction")]] Directive final } // namespace graphql::introspection::object -#endif // DIRECTIVEOBJECT_H +#endif // INTROSPECTION_DIRECTIVEOBJECT_H diff --git a/src/introspection/DirectiveObject.ixx b/src/introspection/DirectiveObject.ixx new file mode 100644 index 00000000..8a00c78d --- /dev/null +++ b/src/introspection/DirectiveObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "DirectiveObject.h" + +export module GraphQL.Introspection.DirectiveObject; + +export namespace graphql::introspection::object { + +using object::Directive; + +} // namespace graphql::introspection::object diff --git a/src/introspection/EnumValueObject.cpp b/src/introspection/EnumValueObject.cpp index 85ff0ca5..7cdb6386 100644 --- a/src/introspection/EnumValueObject.cpp +++ b/src/introspection/EnumValueObject.cpp @@ -9,7 +9,6 @@ #include #include -#include #include #include diff --git a/src/introspection/EnumValueObject.h b/src/introspection/EnumValueObject.h index 0ac9e74f..a97c9534 100644 --- a/src/introspection/EnumValueObject.h +++ b/src/introspection/EnumValueObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef ENUMVALUEOBJECT_H -#define ENUMVALUEOBJECT_H +#ifndef INTROSPECTION_ENUMVALUEOBJECT_H +#define INTROSPECTION_ENUMVALUEOBJECT_H #include "IntrospectionSchema.h" @@ -78,4 +78,4 @@ class [[nodiscard("unnecessary construction")]] EnumValue final } // namespace graphql::introspection::object -#endif // ENUMVALUEOBJECT_H +#endif // INTROSPECTION_ENUMVALUEOBJECT_H diff --git a/src/introspection/EnumValueObject.ixx b/src/introspection/EnumValueObject.ixx new file mode 100644 index 00000000..45be5d13 --- /dev/null +++ b/src/introspection/EnumValueObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "EnumValueObject.h" + +export module GraphQL.Introspection.EnumValueObject; + +export namespace graphql::introspection::object { + +using object::EnumValue; + +} // namespace graphql::introspection::object diff --git a/src/introspection/FieldObject.cpp b/src/introspection/FieldObject.cpp index cd50b6b6..4a6dc359 100644 --- a/src/introspection/FieldObject.cpp +++ b/src/introspection/FieldObject.cpp @@ -11,7 +11,6 @@ #include #include -#include #include #include diff --git a/src/introspection/FieldObject.h b/src/introspection/FieldObject.h index f835c5ae..07fba6ae 100644 --- a/src/introspection/FieldObject.h +++ b/src/introspection/FieldObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef FIELDOBJECT_H -#define FIELDOBJECT_H +#ifndef INTROSPECTION_FIELDOBJECT_H +#define INTROSPECTION_FIELDOBJECT_H #include "IntrospectionSchema.h" @@ -92,4 +92,4 @@ class [[nodiscard("unnecessary construction")]] Field final } // namespace graphql::introspection::object -#endif // FIELDOBJECT_H +#endif // INTROSPECTION_FIELDOBJECT_H diff --git a/src/introspection/FieldObject.ixx b/src/introspection/FieldObject.ixx new file mode 100644 index 00000000..aa5c8305 --- /dev/null +++ b/src/introspection/FieldObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "FieldObject.h" + +export module GraphQL.Introspection.FieldObject; + +export namespace graphql::introspection::object { + +using object::Field; + +} // namespace graphql::introspection::object diff --git a/src/introspection/InputValueObject.cpp b/src/introspection/InputValueObject.cpp index daef5e94..40fa9620 100644 --- a/src/introspection/InputValueObject.cpp +++ b/src/introspection/InputValueObject.cpp @@ -10,7 +10,6 @@ #include #include -#include #include #include diff --git a/src/introspection/InputValueObject.h b/src/introspection/InputValueObject.h index 70faa321..8dda3e79 100644 --- a/src/introspection/InputValueObject.h +++ b/src/introspection/InputValueObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef INPUTVALUEOBJECT_H -#define INPUTVALUEOBJECT_H +#ifndef INTROSPECTION_INPUTVALUEOBJECT_H +#define INTROSPECTION_INPUTVALUEOBJECT_H #include "IntrospectionSchema.h" @@ -78,4 +78,4 @@ class [[nodiscard("unnecessary construction")]] InputValue final } // namespace graphql::introspection::object -#endif // INPUTVALUEOBJECT_H +#endif // INTROSPECTION_INPUTVALUEOBJECT_H diff --git a/src/introspection/InputValueObject.ixx b/src/introspection/InputValueObject.ixx new file mode 100644 index 00000000..252760df --- /dev/null +++ b/src/introspection/InputValueObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "InputValueObject.h" + +export module GraphQL.Introspection.InputValueObject; + +export namespace graphql::introspection::object { + +using object::InputValue; + +} // namespace graphql::introspection::object diff --git a/src/introspection/IntrospectionSchema.cpp b/src/introspection/IntrospectionSchema.cpp index b87861cd..5bc857ed 100644 --- a/src/introspection/IntrospectionSchema.cpp +++ b/src/introspection/IntrospectionSchema.cpp @@ -7,8 +7,8 @@ #include #include +#include #include -#include #include #include #include @@ -16,138 +16,7 @@ using namespace std::literals; -namespace graphql { -namespace service { - -static const auto s_namesTypeKind = introspection::getTypeKindNames(); -static const auto s_valuesTypeKind = introspection::getTypeKindValues(); - -template <> -introspection::TypeKind Argument::convert(const response::Value& value) -{ - if (!value.maybe_enum()) - { - throw service::schema_exception { { R"ex(not a valid __TypeKind value)ex" } }; - } - - const auto result = internal::sorted_map_lookup( - s_valuesTypeKind, - std::string_view { value.get() }); - - if (!result) - { - throw service::schema_exception { { R"ex(not a valid __TypeKind value)ex" } }; - } - - return *result; -} - -template <> -service::AwaitableResolver Result::convert(service::AwaitableScalar result, ResolverParams&& params) -{ - return ModifiedResult::resolve(std::move(result), std::move(params), - [](introspection::TypeKind value, const ResolverParams&) - { - const auto idx = static_cast(value); - - if (idx >= s_namesTypeKind.size()) - { - throw service::schema_exception { { R"ex(Enum value out of range for __TypeKind)ex" } }; - } - - response::Value resolvedResult(response::Type::EnumValue); - - resolvedResult.set(std::string { s_namesTypeKind[idx] }); - - return resolvedResult; - }); -} - -template <> -void Result::validateScalar(const response::Value& value) -{ - if (!value.maybe_enum()) - { - throw service::schema_exception { { R"ex(not a valid __TypeKind value)ex" } }; - } - - const auto [itr, itrEnd] = internal::sorted_map_equal_range( - s_valuesTypeKind.begin(), - s_valuesTypeKind.end(), - std::string_view { value.get() }); - - if (itr == itrEnd) - { - throw service::schema_exception { { R"ex(not a valid __TypeKind value)ex" } }; - } -} - -static const auto s_namesDirectiveLocation = introspection::getDirectiveLocationNames(); -static const auto s_valuesDirectiveLocation = introspection::getDirectiveLocationValues(); - -template <> -introspection::DirectiveLocation Argument::convert(const response::Value& value) -{ - if (!value.maybe_enum()) - { - throw service::schema_exception { { R"ex(not a valid __DirectiveLocation value)ex" } }; - } - - const auto result = internal::sorted_map_lookup( - s_valuesDirectiveLocation, - std::string_view { value.get() }); - - if (!result) - { - throw service::schema_exception { { R"ex(not a valid __DirectiveLocation value)ex" } }; - } - - return *result; -} - -template <> -service::AwaitableResolver Result::convert(service::AwaitableScalar result, ResolverParams&& params) -{ - return ModifiedResult::resolve(std::move(result), std::move(params), - [](introspection::DirectiveLocation value, const ResolverParams&) - { - const auto idx = static_cast(value); - - if (idx >= s_namesDirectiveLocation.size()) - { - throw service::schema_exception { { R"ex(Enum value out of range for __DirectiveLocation)ex" } }; - } - - response::Value resolvedResult(response::Type::EnumValue); - - resolvedResult.set(std::string { s_namesDirectiveLocation[idx] }); - - return resolvedResult; - }); -} - -template <> -void Result::validateScalar(const response::Value& value) -{ - if (!value.maybe_enum()) - { - throw service::schema_exception { { R"ex(not a valid __DirectiveLocation value)ex" } }; - } - - const auto [itr, itrEnd] = internal::sorted_map_equal_range( - s_valuesDirectiveLocation.begin(), - s_valuesDirectiveLocation.end(), - std::string_view { value.get() }); - - if (itr == itrEnd) - { - throw service::schema_exception { { R"ex(not a valid __DirectiveLocation value)ex" } }; - } -} - -} // namespace service - -namespace introspection { +namespace graphql::introspection { void AddTypesToSchema(const std::shared_ptr& schema) { @@ -173,36 +42,38 @@ void AddTypesToSchema(const std::shared_ptr& schema) auto typeDirective = schema::ObjectType::Make(R"gql(__Directive)gql"sv, R"md()md"sv); schema->AddType(R"gql(__Directive)gql"sv, typeDirective); + static const auto s_namesTypeKind = getTypeKindNames(); typeTypeKind->AddEnumValues({ - { service::s_namesTypeKind[static_cast(introspection::TypeKind::SCALAR)], R"md()md"sv, std::nullopt }, - { service::s_namesTypeKind[static_cast(introspection::TypeKind::OBJECT)], R"md()md"sv, std::nullopt }, - { service::s_namesTypeKind[static_cast(introspection::TypeKind::INTERFACE)], R"md()md"sv, std::nullopt }, - { service::s_namesTypeKind[static_cast(introspection::TypeKind::UNION)], R"md()md"sv, std::nullopt }, - { service::s_namesTypeKind[static_cast(introspection::TypeKind::ENUM)], R"md()md"sv, std::nullopt }, - { service::s_namesTypeKind[static_cast(introspection::TypeKind::INPUT_OBJECT)], R"md()md"sv, std::nullopt }, - { service::s_namesTypeKind[static_cast(introspection::TypeKind::LIST)], R"md()md"sv, std::nullopt }, - { service::s_namesTypeKind[static_cast(introspection::TypeKind::NON_NULL)], R"md()md"sv, std::nullopt } + { s_namesTypeKind[static_cast(introspection::TypeKind::SCALAR)], R"md()md"sv, std::nullopt }, + { s_namesTypeKind[static_cast(introspection::TypeKind::OBJECT)], R"md()md"sv, std::nullopt }, + { s_namesTypeKind[static_cast(introspection::TypeKind::INTERFACE)], R"md()md"sv, std::nullopt }, + { s_namesTypeKind[static_cast(introspection::TypeKind::UNION)], R"md()md"sv, std::nullopt }, + { s_namesTypeKind[static_cast(introspection::TypeKind::ENUM)], R"md()md"sv, std::nullopt }, + { s_namesTypeKind[static_cast(introspection::TypeKind::INPUT_OBJECT)], R"md()md"sv, std::nullopt }, + { s_namesTypeKind[static_cast(introspection::TypeKind::LIST)], R"md()md"sv, std::nullopt }, + { s_namesTypeKind[static_cast(introspection::TypeKind::NON_NULL)], R"md()md"sv, std::nullopt } }); + static const auto s_namesDirectiveLocation = getDirectiveLocationNames(); typeDirectiveLocation->AddEnumValues({ - { service::s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::QUERY)], R"md()md"sv, std::nullopt }, - { service::s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::MUTATION)], R"md()md"sv, std::nullopt }, - { service::s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::SUBSCRIPTION)], R"md()md"sv, std::nullopt }, - { service::s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::FIELD)], R"md()md"sv, std::nullopt }, - { service::s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::FRAGMENT_DEFINITION)], R"md()md"sv, std::nullopt }, - { service::s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::FRAGMENT_SPREAD)], R"md()md"sv, std::nullopt }, - { service::s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::INLINE_FRAGMENT)], R"md()md"sv, std::nullopt }, - { service::s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::VARIABLE_DEFINITION)], R"md()md"sv, std::nullopt }, - { service::s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::SCHEMA)], R"md()md"sv, std::nullopt }, - { service::s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::SCALAR)], R"md()md"sv, std::nullopt }, - { service::s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::OBJECT)], R"md()md"sv, std::nullopt }, - { service::s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::FIELD_DEFINITION)], R"md()md"sv, std::nullopt }, - { service::s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::ARGUMENT_DEFINITION)], R"md()md"sv, std::nullopt }, - { service::s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::INTERFACE)], R"md()md"sv, std::nullopt }, - { service::s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::UNION)], R"md()md"sv, std::nullopt }, - { service::s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::ENUM)], R"md()md"sv, std::nullopt }, - { service::s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::ENUM_VALUE)], R"md()md"sv, std::nullopt }, - { service::s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::INPUT_OBJECT)], R"md()md"sv, std::nullopt }, - { service::s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::INPUT_FIELD_DEFINITION)], R"md()md"sv, std::nullopt } + { s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::QUERY)], R"md()md"sv, std::nullopt }, + { s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::MUTATION)], R"md()md"sv, std::nullopt }, + { s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::SUBSCRIPTION)], R"md()md"sv, std::nullopt }, + { s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::FIELD)], R"md()md"sv, std::nullopt }, + { s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::FRAGMENT_DEFINITION)], R"md()md"sv, std::nullopt }, + { s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::FRAGMENT_SPREAD)], R"md()md"sv, std::nullopt }, + { s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::INLINE_FRAGMENT)], R"md()md"sv, std::nullopt }, + { s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::VARIABLE_DEFINITION)], R"md()md"sv, std::nullopt }, + { s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::SCHEMA)], R"md()md"sv, std::nullopt }, + { s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::SCALAR)], R"md()md"sv, std::nullopt }, + { s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::OBJECT)], R"md()md"sv, std::nullopt }, + { s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::FIELD_DEFINITION)], R"md()md"sv, std::nullopt }, + { s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::ARGUMENT_DEFINITION)], R"md()md"sv, std::nullopt }, + { s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::INTERFACE)], R"md()md"sv, std::nullopt }, + { s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::UNION)], R"md()md"sv, std::nullopt }, + { s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::ENUM)], R"md()md"sv, std::nullopt }, + { s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::ENUM_VALUE)], R"md()md"sv, std::nullopt }, + { s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::INPUT_OBJECT)], R"md()md"sv, std::nullopt }, + { s_namesDirectiveLocation[static_cast(introspection::DirectiveLocation::INPUT_FIELD_DEFINITION)], R"md()md"sv, std::nullopt } }); AddSchemaDetails(typeSchema, schema); @@ -239,5 +110,4 @@ void AddTypesToSchema(const std::shared_ptr& schema) }, false)); } -} // namespace introspection -} // namespace graphql +} // namespace graphql::introspection diff --git a/src/introspection/IntrospectionSchema.h b/src/introspection/IntrospectionSchema.h index 4adaa9af..b39fe13f 100644 --- a/src/introspection/IntrospectionSchema.h +++ b/src/introspection/IntrospectionSchema.h @@ -8,141 +8,25 @@ #ifndef INTROSPECTIONSCHEMA_H #define INTROSPECTIONSCHEMA_H +#include "graphqlservice/GraphQLResponse.h" +#include "graphqlservice/GraphQLService.h" + +#include "graphqlservice/internal/DllExports.h" +#include "graphqlservice/internal/Version.h" #include "graphqlservice/internal/Schema.h" -// Check if the library version is compatible with schemagen 4.5.0 -static_assert(graphql::internal::MajorVersion == 4, "regenerate with schemagen: major version mismatch"); -static_assert(graphql::internal::MinorVersion == 5, "regenerate with schemagen: minor version mismatch"); +#include "IntrospectionSharedTypes.h" #include #include #include #include -namespace graphql { -namespace introspection { - -enum class TypeKind -{ - SCALAR, - OBJECT, - INTERFACE, - UNION, - ENUM, - INPUT_OBJECT, - LIST, - NON_NULL -}; - -[[nodiscard("unnecessary call")]] constexpr auto getTypeKindNames() noexcept -{ - using namespace std::literals; - - return std::array { - R"gql(SCALAR)gql"sv, - R"gql(OBJECT)gql"sv, - R"gql(INTERFACE)gql"sv, - R"gql(UNION)gql"sv, - R"gql(ENUM)gql"sv, - R"gql(INPUT_OBJECT)gql"sv, - R"gql(LIST)gql"sv, - R"gql(NON_NULL)gql"sv - }; -} - -[[nodiscard("unnecessary call")]] constexpr auto getTypeKindValues() noexcept -{ - using namespace std::literals; - - return std::array, 8> { - std::make_pair(R"gql(ENUM)gql"sv, TypeKind::ENUM), - std::make_pair(R"gql(LIST)gql"sv, TypeKind::LIST), - std::make_pair(R"gql(UNION)gql"sv, TypeKind::UNION), - std::make_pair(R"gql(OBJECT)gql"sv, TypeKind::OBJECT), - std::make_pair(R"gql(SCALAR)gql"sv, TypeKind::SCALAR), - std::make_pair(R"gql(NON_NULL)gql"sv, TypeKind::NON_NULL), - std::make_pair(R"gql(INTERFACE)gql"sv, TypeKind::INTERFACE), - std::make_pair(R"gql(INPUT_OBJECT)gql"sv, TypeKind::INPUT_OBJECT) - }; -} - -enum class DirectiveLocation -{ - QUERY, - MUTATION, - SUBSCRIPTION, - FIELD, - FRAGMENT_DEFINITION, - FRAGMENT_SPREAD, - INLINE_FRAGMENT, - VARIABLE_DEFINITION, - SCHEMA, - SCALAR, - OBJECT, - FIELD_DEFINITION, - ARGUMENT_DEFINITION, - INTERFACE, - UNION, - ENUM, - ENUM_VALUE, - INPUT_OBJECT, - INPUT_FIELD_DEFINITION -}; - -[[nodiscard("unnecessary call")]] constexpr auto getDirectiveLocationNames() noexcept -{ - using namespace std::literals; - - return std::array { - R"gql(QUERY)gql"sv, - R"gql(MUTATION)gql"sv, - R"gql(SUBSCRIPTION)gql"sv, - R"gql(FIELD)gql"sv, - R"gql(FRAGMENT_DEFINITION)gql"sv, - R"gql(FRAGMENT_SPREAD)gql"sv, - R"gql(INLINE_FRAGMENT)gql"sv, - R"gql(VARIABLE_DEFINITION)gql"sv, - R"gql(SCHEMA)gql"sv, - R"gql(SCALAR)gql"sv, - R"gql(OBJECT)gql"sv, - R"gql(FIELD_DEFINITION)gql"sv, - R"gql(ARGUMENT_DEFINITION)gql"sv, - R"gql(INTERFACE)gql"sv, - R"gql(UNION)gql"sv, - R"gql(ENUM)gql"sv, - R"gql(ENUM_VALUE)gql"sv, - R"gql(INPUT_OBJECT)gql"sv, - R"gql(INPUT_FIELD_DEFINITION)gql"sv - }; -} - -[[nodiscard("unnecessary call")]] constexpr auto getDirectiveLocationValues() noexcept -{ - using namespace std::literals; - - return std::array, 19> { - std::make_pair(R"gql(ENUM)gql"sv, DirectiveLocation::ENUM), - std::make_pair(R"gql(FIELD)gql"sv, DirectiveLocation::FIELD), - std::make_pair(R"gql(QUERY)gql"sv, DirectiveLocation::QUERY), - std::make_pair(R"gql(UNION)gql"sv, DirectiveLocation::UNION), - std::make_pair(R"gql(OBJECT)gql"sv, DirectiveLocation::OBJECT), - std::make_pair(R"gql(SCALAR)gql"sv, DirectiveLocation::SCALAR), - std::make_pair(R"gql(SCHEMA)gql"sv, DirectiveLocation::SCHEMA), - std::make_pair(R"gql(MUTATION)gql"sv, DirectiveLocation::MUTATION), - std::make_pair(R"gql(INTERFACE)gql"sv, DirectiveLocation::INTERFACE), - std::make_pair(R"gql(ENUM_VALUE)gql"sv, DirectiveLocation::ENUM_VALUE), - std::make_pair(R"gql(INPUT_OBJECT)gql"sv, DirectiveLocation::INPUT_OBJECT), - std::make_pair(R"gql(SUBSCRIPTION)gql"sv, DirectiveLocation::SUBSCRIPTION), - std::make_pair(R"gql(FRAGMENT_SPREAD)gql"sv, DirectiveLocation::FRAGMENT_SPREAD), - std::make_pair(R"gql(INLINE_FRAGMENT)gql"sv, DirectiveLocation::INLINE_FRAGMENT), - std::make_pair(R"gql(FIELD_DEFINITION)gql"sv, DirectiveLocation::FIELD_DEFINITION), - std::make_pair(R"gql(ARGUMENT_DEFINITION)gql"sv, DirectiveLocation::ARGUMENT_DEFINITION), - std::make_pair(R"gql(FRAGMENT_DEFINITION)gql"sv, DirectiveLocation::FRAGMENT_DEFINITION), - std::make_pair(R"gql(VARIABLE_DEFINITION)gql"sv, DirectiveLocation::VARIABLE_DEFINITION), - std::make_pair(R"gql(INPUT_FIELD_DEFINITION)gql"sv, DirectiveLocation::INPUT_FIELD_DEFINITION) - }; -} +// Check if the library version is compatible with schemagen 5.0.0 +static_assert(graphql::internal::MajorVersion == 5, "regenerate with schemagen: major version mismatch"); +static_assert(graphql::internal::MinorVersion == 0, "regenerate with schemagen: minor version mismatch"); +namespace graphql::introspection { class Schema; class Type; class Field; @@ -170,33 +54,6 @@ void AddDirectiveDetails(const std::shared_ptr& typeDirectiv GRAPHQLSERVICE_EXPORT void AddTypesToSchema(const std::shared_ptr& schema); -} // namespace introspection - -namespace service { - -#ifdef GRAPHQL_DLLEXPORTS -// Export all of the built-in converters -template <> -GRAPHQLSERVICE_EXPORT introspection::TypeKind Argument::convert( - const response::Value& value); -template <> -GRAPHQLSERVICE_EXPORT AwaitableResolver Result::convert( - AwaitableScalar result, ResolverParams&& params); -template <> -GRAPHQLSERVICE_EXPORT void Result::validateScalar( - const response::Value& value); -template <> -GRAPHQLSERVICE_EXPORT introspection::DirectiveLocation Argument::convert( - const response::Value& value); -template <> -GRAPHQLSERVICE_EXPORT AwaitableResolver Result::convert( - AwaitableScalar result, ResolverParams&& params); -template <> -GRAPHQLSERVICE_EXPORT void Result::validateScalar( - const response::Value& value); -#endif // GRAPHQL_DLLEXPORTS - -} // namespace service -} // namespace graphql +} // namespace graphql::introspection #endif // INTROSPECTIONSCHEMA_H diff --git a/src/introspection/IntrospectionSchema.ixx b/src/introspection/IntrospectionSchema.ixx new file mode 100644 index 00000000..8ce51120 --- /dev/null +++ b/src/introspection/IntrospectionSchema.ixx @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "IntrospectionSchema.h" + +export module GraphQL.Introspection.IntrospectionSchema; + +export import GraphQL.Introspection.IntrospectionSharedTypes; + +export import GraphQL.Introspection.SchemaObject; +export import GraphQL.Introspection.TypeObject; +export import GraphQL.Introspection.FieldObject; +export import GraphQL.Introspection.InputValueObject; +export import GraphQL.Introspection.EnumValueObject; +export import GraphQL.Introspection.DirectiveObject; + +export namespace graphql::introspection { + +using introspection::AddSchemaDetails; +using introspection::AddTypeDetails; +using introspection::AddFieldDetails; +using introspection::AddInputValueDetails; +using introspection::AddEnumValueDetails; +using introspection::AddDirectiveDetails; + +using introspection::AddTypesToSchema; + +} // namespace graphql::introspection diff --git a/src/introspection/IntrospectionSharedTypes.cpp b/src/introspection/IntrospectionSharedTypes.cpp new file mode 100644 index 00000000..9457eec6 --- /dev/null +++ b/src/introspection/IntrospectionSharedTypes.cpp @@ -0,0 +1,143 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +#include "graphqlservice/GraphQLService.h" + +#include "IntrospectionSharedTypes.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std::literals; + +namespace graphql { +namespace service { + +static const auto s_namesTypeKind = introspection::getTypeKindNames(); +static const auto s_valuesTypeKind = introspection::getTypeKindValues(); + +template <> +introspection::TypeKind Argument::convert(const response::Value& value) +{ + if (!value.maybe_enum()) + { + throw service::schema_exception { { R"ex(not a valid __TypeKind value)ex" } }; + } + + const auto result = internal::sorted_map_lookup( + s_valuesTypeKind, + std::string_view { value.get() }); + + if (!result) + { + throw service::schema_exception { { R"ex(not a valid __TypeKind value)ex" } }; + } + + return *result; +} + +template <> +service::AwaitableResolver Result::convert(service::AwaitableScalar result, ResolverParams&& params) +{ + return ModifiedResult::resolve(std::move(result), std::move(params), + [](introspection::TypeKind value, const ResolverParams&) + { + const auto idx = static_cast(value); + + if (idx >= s_namesTypeKind.size()) + { + throw service::schema_exception { { R"ex(Enum value out of range for __TypeKind)ex" } }; + } + + return ResolverResult { { response::ValueToken::EnumValue { std::string { s_namesTypeKind[idx] } } } }; + }); +} + +template <> +void Result::validateScalar(const response::Value& value) +{ + if (!value.maybe_enum()) + { + throw service::schema_exception { { R"ex(not a valid __TypeKind value)ex" } }; + } + + const auto [itr, itrEnd] = internal::sorted_map_equal_range( + s_valuesTypeKind.begin(), + s_valuesTypeKind.end(), + std::string_view { value.get() }); + + if (itr == itrEnd) + { + throw service::schema_exception { { R"ex(not a valid __TypeKind value)ex" } }; + } +} + +static const auto s_namesDirectiveLocation = introspection::getDirectiveLocationNames(); +static const auto s_valuesDirectiveLocation = introspection::getDirectiveLocationValues(); + +template <> +introspection::DirectiveLocation Argument::convert(const response::Value& value) +{ + if (!value.maybe_enum()) + { + throw service::schema_exception { { R"ex(not a valid __DirectiveLocation value)ex" } }; + } + + const auto result = internal::sorted_map_lookup( + s_valuesDirectiveLocation, + std::string_view { value.get() }); + + if (!result) + { + throw service::schema_exception { { R"ex(not a valid __DirectiveLocation value)ex" } }; + } + + return *result; +} + +template <> +service::AwaitableResolver Result::convert(service::AwaitableScalar result, ResolverParams&& params) +{ + return ModifiedResult::resolve(std::move(result), std::move(params), + [](introspection::DirectiveLocation value, const ResolverParams&) + { + const auto idx = static_cast(value); + + if (idx >= s_namesDirectiveLocation.size()) + { + throw service::schema_exception { { R"ex(Enum value out of range for __DirectiveLocation)ex" } }; + } + + return ResolverResult { { response::ValueToken::EnumValue { std::string { s_namesDirectiveLocation[idx] } } } }; + }); +} + +template <> +void Result::validateScalar(const response::Value& value) +{ + if (!value.maybe_enum()) + { + throw service::schema_exception { { R"ex(not a valid __DirectiveLocation value)ex" } }; + } + + const auto [itr, itrEnd] = internal::sorted_map_equal_range( + s_valuesDirectiveLocation.begin(), + s_valuesDirectiveLocation.end(), + std::string_view { value.get() }); + + if (itr == itrEnd) + { + throw service::schema_exception { { R"ex(not a valid __DirectiveLocation value)ex" } }; + } +} + +} // namespace service +} // namespace graphql diff --git a/src/introspection/IntrospectionSharedTypes.h b/src/introspection/IntrospectionSharedTypes.h new file mode 100644 index 00000000..9e1d5acb --- /dev/null +++ b/src/introspection/IntrospectionSharedTypes.h @@ -0,0 +1,180 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +#pragma once + +#ifndef INTROSPECTIONSHAREDTYPES_H +#define INTROSPECTIONSHAREDTYPES_H + +#include "graphqlservice/GraphQLResponse.h" + +#include "graphqlservice/internal/DllExports.h" +#include "graphqlservice/internal/Version.h" + +#include +#include +#include +#include +#include +#include + +// Check if the library version is compatible with schemagen 5.0.0 +static_assert(graphql::internal::MajorVersion == 5, "regenerate with schemagen: major version mismatch"); +static_assert(graphql::internal::MinorVersion == 0, "regenerate with schemagen: minor version mismatch"); + +namespace graphql { +namespace introspection { + +enum class TypeKind +{ + SCALAR, + OBJECT, + INTERFACE, + UNION, + ENUM, + INPUT_OBJECT, + LIST, + NON_NULL +}; + +[[nodiscard("unnecessary call")]] constexpr auto getTypeKindNames() noexcept +{ + using namespace std::literals; + + return std::array { + R"gql(SCALAR)gql"sv, + R"gql(OBJECT)gql"sv, + R"gql(INTERFACE)gql"sv, + R"gql(UNION)gql"sv, + R"gql(ENUM)gql"sv, + R"gql(INPUT_OBJECT)gql"sv, + R"gql(LIST)gql"sv, + R"gql(NON_NULL)gql"sv + }; +} + +[[nodiscard("unnecessary call")]] constexpr auto getTypeKindValues() noexcept +{ + using namespace std::literals; + + return std::array, 8> { + std::make_pair(R"gql(ENUM)gql"sv, TypeKind::ENUM), + std::make_pair(R"gql(LIST)gql"sv, TypeKind::LIST), + std::make_pair(R"gql(UNION)gql"sv, TypeKind::UNION), + std::make_pair(R"gql(OBJECT)gql"sv, TypeKind::OBJECT), + std::make_pair(R"gql(SCALAR)gql"sv, TypeKind::SCALAR), + std::make_pair(R"gql(NON_NULL)gql"sv, TypeKind::NON_NULL), + std::make_pair(R"gql(INTERFACE)gql"sv, TypeKind::INTERFACE), + std::make_pair(R"gql(INPUT_OBJECT)gql"sv, TypeKind::INPUT_OBJECT) + }; +} + +enum class DirectiveLocation +{ + QUERY, + MUTATION, + SUBSCRIPTION, + FIELD, + FRAGMENT_DEFINITION, + FRAGMENT_SPREAD, + INLINE_FRAGMENT, + VARIABLE_DEFINITION, + SCHEMA, + SCALAR, + OBJECT, + FIELD_DEFINITION, + ARGUMENT_DEFINITION, + INTERFACE, + UNION, + ENUM, + ENUM_VALUE, + INPUT_OBJECT, + INPUT_FIELD_DEFINITION +}; + +[[nodiscard("unnecessary call")]] constexpr auto getDirectiveLocationNames() noexcept +{ + using namespace std::literals; + + return std::array { + R"gql(QUERY)gql"sv, + R"gql(MUTATION)gql"sv, + R"gql(SUBSCRIPTION)gql"sv, + R"gql(FIELD)gql"sv, + R"gql(FRAGMENT_DEFINITION)gql"sv, + R"gql(FRAGMENT_SPREAD)gql"sv, + R"gql(INLINE_FRAGMENT)gql"sv, + R"gql(VARIABLE_DEFINITION)gql"sv, + R"gql(SCHEMA)gql"sv, + R"gql(SCALAR)gql"sv, + R"gql(OBJECT)gql"sv, + R"gql(FIELD_DEFINITION)gql"sv, + R"gql(ARGUMENT_DEFINITION)gql"sv, + R"gql(INTERFACE)gql"sv, + R"gql(UNION)gql"sv, + R"gql(ENUM)gql"sv, + R"gql(ENUM_VALUE)gql"sv, + R"gql(INPUT_OBJECT)gql"sv, + R"gql(INPUT_FIELD_DEFINITION)gql"sv + }; +} + +[[nodiscard("unnecessary call")]] constexpr auto getDirectiveLocationValues() noexcept +{ + using namespace std::literals; + + return std::array, 19> { + std::make_pair(R"gql(ENUM)gql"sv, DirectiveLocation::ENUM), + std::make_pair(R"gql(FIELD)gql"sv, DirectiveLocation::FIELD), + std::make_pair(R"gql(QUERY)gql"sv, DirectiveLocation::QUERY), + std::make_pair(R"gql(UNION)gql"sv, DirectiveLocation::UNION), + std::make_pair(R"gql(OBJECT)gql"sv, DirectiveLocation::OBJECT), + std::make_pair(R"gql(SCALAR)gql"sv, DirectiveLocation::SCALAR), + std::make_pair(R"gql(SCHEMA)gql"sv, DirectiveLocation::SCHEMA), + std::make_pair(R"gql(MUTATION)gql"sv, DirectiveLocation::MUTATION), + std::make_pair(R"gql(INTERFACE)gql"sv, DirectiveLocation::INTERFACE), + std::make_pair(R"gql(ENUM_VALUE)gql"sv, DirectiveLocation::ENUM_VALUE), + std::make_pair(R"gql(INPUT_OBJECT)gql"sv, DirectiveLocation::INPUT_OBJECT), + std::make_pair(R"gql(SUBSCRIPTION)gql"sv, DirectiveLocation::SUBSCRIPTION), + std::make_pair(R"gql(FRAGMENT_SPREAD)gql"sv, DirectiveLocation::FRAGMENT_SPREAD), + std::make_pair(R"gql(INLINE_FRAGMENT)gql"sv, DirectiveLocation::INLINE_FRAGMENT), + std::make_pair(R"gql(FIELD_DEFINITION)gql"sv, DirectiveLocation::FIELD_DEFINITION), + std::make_pair(R"gql(ARGUMENT_DEFINITION)gql"sv, DirectiveLocation::ARGUMENT_DEFINITION), + std::make_pair(R"gql(FRAGMENT_DEFINITION)gql"sv, DirectiveLocation::FRAGMENT_DEFINITION), + std::make_pair(R"gql(VARIABLE_DEFINITION)gql"sv, DirectiveLocation::VARIABLE_DEFINITION), + std::make_pair(R"gql(INPUT_FIELD_DEFINITION)gql"sv, DirectiveLocation::INPUT_FIELD_DEFINITION) + }; +} + +} // namespace introspection + +namespace service { + +#ifdef GRAPHQL_DLLEXPORTS +// Export all of the built-in converters +template <> +GRAPHQLSERVICE_EXPORT introspection::TypeKind Argument::convert( + const response::Value& value); +template <> +GRAPHQLSERVICE_EXPORT AwaitableResolver Result::convert( + AwaitableScalar result, ResolverParams&& params); +template <> +GRAPHQLSERVICE_EXPORT void Result::validateScalar( + const response::Value& value); +template <> +GRAPHQLSERVICE_EXPORT introspection::DirectiveLocation Argument::convert( + const response::Value& value); +template <> +GRAPHQLSERVICE_EXPORT AwaitableResolver Result::convert( + AwaitableScalar result, ResolverParams&& params); +template <> +GRAPHQLSERVICE_EXPORT void Result::validateScalar( + const response::Value& value); +#endif // GRAPHQL_DLLEXPORTS + +} // namespace service +} // namespace graphql + +#endif // INTROSPECTIONSHAREDTYPES_H diff --git a/src/introspection/IntrospectionSharedTypes.ixx b/src/introspection/IntrospectionSharedTypes.ixx new file mode 100644 index 00000000..60538148 --- /dev/null +++ b/src/introspection/IntrospectionSharedTypes.ixx @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "IntrospectionSharedTypes.h" + +export module GraphQL.Introspection.IntrospectionSharedTypes; + +export namespace graphql::introspection { + +using introspection::TypeKind; +using introspection::getTypeKindNames; +using introspection::getTypeKindValues; + +using introspection::DirectiveLocation; +using introspection::getDirectiveLocationNames; +using introspection::getDirectiveLocationValues; + +} // namespace graphql::introspection diff --git a/src/introspection/SchemaObject.cpp b/src/introspection/SchemaObject.cpp index 49199c42..142e39ec 100644 --- a/src/introspection/SchemaObject.cpp +++ b/src/introspection/SchemaObject.cpp @@ -11,7 +11,6 @@ #include #include -#include #include #include diff --git a/src/introspection/SchemaObject.h b/src/introspection/SchemaObject.h index fd3659c1..53b12b69 100644 --- a/src/introspection/SchemaObject.h +++ b/src/introspection/SchemaObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef SCHEMAOBJECT_H -#define SCHEMAOBJECT_H +#ifndef INTROSPECTION_SCHEMAOBJECT_H +#define INTROSPECTION_SCHEMAOBJECT_H #include "IntrospectionSchema.h" @@ -92,4 +92,4 @@ class [[nodiscard("unnecessary construction")]] Schema final } // namespace graphql::introspection::object -#endif // SCHEMAOBJECT_H +#endif // INTROSPECTION_SCHEMAOBJECT_H diff --git a/src/introspection/SchemaObject.ixx b/src/introspection/SchemaObject.ixx new file mode 100644 index 00000000..56735d85 --- /dev/null +++ b/src/introspection/SchemaObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "SchemaObject.h" + +export module GraphQL.Introspection.SchemaObject; + +export namespace graphql::introspection::object { + +using object::Schema; + +} // namespace graphql::introspection::object diff --git a/src/introspection/TypeObject.cpp b/src/introspection/TypeObject.cpp index e7b13ff8..8c1fafce 100644 --- a/src/introspection/TypeObject.cpp +++ b/src/introspection/TypeObject.cpp @@ -13,7 +13,6 @@ #include #include -#include #include #include diff --git a/src/introspection/TypeObject.h b/src/introspection/TypeObject.h index 3b350e1c..993e6dd7 100644 --- a/src/introspection/TypeObject.h +++ b/src/introspection/TypeObject.h @@ -5,8 +5,8 @@ #pragma once -#ifndef TYPEOBJECT_H -#define TYPEOBJECT_H +#ifndef INTROSPECTION_TYPEOBJECT_H +#define INTROSPECTION_TYPEOBJECT_H #include "IntrospectionSchema.h" @@ -120,4 +120,4 @@ class [[nodiscard("unnecessary construction")]] Type final } // namespace graphql::introspection::object -#endif // TYPEOBJECT_H +#endif // INTROSPECTION_TYPEOBJECT_H diff --git a/src/introspection/TypeObject.ixx b/src/introspection/TypeObject.ixx new file mode 100644 index 00000000..001ccdd6 --- /dev/null +++ b/src/introspection/TypeObject.ixx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// WARNING! Do not edit this file manually, your changes will be overwritten. + +module; + +#include "TypeObject.h" + +export module GraphQL.Introspection.TypeObject; + +export namespace graphql::introspection::object { + +using object::Type; + +} // namespace graphql::introspection::object diff --git a/src/introspection/introspection_schema_files b/src/introspection/introspection_schema_files index fba57600..fac4e1ee 100644 --- a/src/introspection/introspection_schema_files +++ b/src/introspection/introspection_schema_files @@ -1,3 +1,4 @@ +IntrospectionSharedTypes.cpp IntrospectionSchema.cpp SchemaObject.cpp TypeObject.cpp diff --git a/test/AnyScalarTests.cpp b/test/AnyScalarTests.cpp new file mode 100644 index 00000000..2efdb45f --- /dev/null +++ b/test/AnyScalarTests.cpp @@ -0,0 +1,105 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include + +#include "graphqlservice/GraphQLResponse.h" +#include "graphqlservice/JSONResponse.h" + +#include +#include +#include +#include + +using namespace graphql; + +namespace { + +// A toy custom scalar whose C++ representation (a 64-bit integer) can't be expressed with any of +// response::Value's built-in alternatives, since response::IntType is only 32-bit. A hand-written +// resolver can store this directly and hand it off to the token stream via response::AnyScalar, +// without any schema/codegen changes. +struct Int64Scalar +{ + std::int64_t value = 0; +}; + +// Serialize the payload onto the wire as an ordinary JSON string of the decimal value. +response::AnyScalar makeBigInt(std::int64_t value) +{ + return response::AnyScalar { + std::any { Int64Scalar { value } }, + [](const std::any& payload, const std::shared_ptr& visitor) { + const auto& scalar = std::any_cast(payload); + visitor->add_string(std::to_string(scalar.value)); + }, + }; +} + +} // namespace + +TEST(AnyScalarCase, ConstructAndInspect) +{ + auto value = response::Value { makeBigInt(9223372036854775807LL) }; + + ASSERT_TRUE(response::Type::Scalar == value.type()); + ASSERT_TRUE(value.isAny()); +} + +TEST(AnyScalarCase, SerializeToJSON) +{ + auto value = response::Value { makeBigInt(9223372036854775807LL) }; + const auto json = response::toJSON(std::move(value)); + + ASSERT_EQ(R"js("9223372036854775807")js", json); +} + +TEST(AnyScalarCase, SerializeInsideMap) +{ + response::Value map { response::Type::Map }; + map.emplace_back("bigInt", response::Value { makeBigInt(-9223372036854775807LL) }); + + const auto json = response::toJSON(std::move(map)); + + ASSERT_EQ(R"js({"bigInt":"-9223372036854775807"})js", json); +} + +TEST(AnyScalarCase, CopySharesOwnership) +{ + auto original = response::Value { makeBigInt(42) }; + auto copy = response::Value { original }; + + ASSERT_TRUE(copy.isAny()); + // Copies share ownership of the same AnyScalar payload (no deep copy), so they compare equal. + ASSERT_TRUE(original == copy); +} + +TEST(AnyScalarCase, DistinctPayloadsCompareUnequal) +{ + auto lhs = response::Value { makeBigInt(1) }; + auto rhs = response::Value { makeBigInt(1) }; + + // Even with equal logical values, distinct AnyScalar payloads are compared by identity. + ASSERT_FALSE(lhs == rhs); +} + +TEST(AnyScalarCase, ReleaseAny) +{ + auto value = response::Value { makeBigInt(7) }; + auto payload = value.releaseAny(); + + ASSERT_TRUE(static_cast(payload)); + ASSERT_FALSE(value.isAny()); + + const auto& scalar = std::any_cast(payload->value); + ASSERT_EQ(7, scalar.value); +} + +TEST(AnyScalarCase, PlainScalarIsNotAny) +{ + response::Value scalar { response::Type::Scalar }; + scalar.set(response::Value { 5 }); + + ASSERT_TRUE(response::Type::Scalar == scalar.type()); + ASSERT_FALSE(scalar.isAny()); +} diff --git a/test/ArgumentTests.cpp b/test/ArgumentTests.cpp index aa255700..557d4e71 100644 --- a/test/ArgumentTests.cpp +++ b/test/ArgumentTests.cpp @@ -7,6 +7,8 @@ #include "graphqlservice/JSONResponse.h" +#include + using namespace graphql; TEST(ArgumentsCase, ListArgumentStrings) @@ -27,7 +29,7 @@ TEST(ArgumentsCase, ListArgumentStrings) FAIL() << response::toJSON(ex.getErrors()); } - ASSERT_EQ(size_t { 3 }, actual.size()) << "should get 3 entries"; + ASSERT_EQ(std::size_t { 3 }, actual.size()) << "should get 3 entries"; EXPECT_EQ("string1", actual[0]) << "entry should match"; EXPECT_EQ("string2", actual[1]) << "entry should match"; EXPECT_EQ("string3", actual[2]) << "entry should match"; @@ -80,7 +82,7 @@ TEST(ArgumentsCase, ListArgumentStringsNullable) FAIL() << response::toJSON(ex.getErrors()); } - ASSERT_EQ(size_t { 4 }, actual.size()) << "should get 4 entries"; + ASSERT_EQ(std::size_t { 4 }, actual.size()) << "should get 4 entries"; ASSERT_TRUE(actual[0].has_value()) << "should not be null"; EXPECT_EQ("string1", *actual[0]) << "entry should match"; ASSERT_TRUE(actual[1].has_value()) << "should not be null"; @@ -108,11 +110,11 @@ TEST(ArgumentsCase, ListArgumentListArgumentStrings) FAIL() << response::toJSON(ex.getErrors()); } - ASSERT_EQ(size_t { 2 }, actual.size()) << "should get 2 entries"; - ASSERT_EQ(size_t { 2 }, actual[0].size()) << "should get 2 entries"; + ASSERT_EQ(std::size_t { 2 }, actual.size()) << "should get 2 entries"; + ASSERT_EQ(std::size_t { 2 }, actual[0].size()) << "should get 2 entries"; EXPECT_EQ("list1string1", actual[0][0]) << "entry should match"; EXPECT_EQ("list1string2", actual[0][1]) << "entry should match"; - ASSERT_EQ(size_t { 2 }, actual[1].size()) << "should get 2 entries"; + ASSERT_EQ(std::size_t { 2 }, actual[1].size()) << "should get 2 entries"; EXPECT_EQ("list2string1", actual[1][0]) << "entry should match"; EXPECT_EQ("list2string2", actual[1][1]) << "entry should match"; } @@ -136,9 +138,9 @@ TEST(ArgumentsCase, ListArgumentNullableListArgumentStrings) FAIL() << response::toJSON(ex.getErrors()); } - ASSERT_EQ(size_t { 2 }, actual.size()) << "should get 2 entries"; + ASSERT_EQ(std::size_t { 2 }, actual.size()) << "should get 2 entries"; EXPECT_FALSE(actual[0].has_value()) << "should be null"; - ASSERT_EQ(size_t { 2 }, actual[1]->size()) << "should get 2 entries"; + ASSERT_EQ(std::size_t { 2 }, actual[1]->size()) << "should get 2 entries"; EXPECT_EQ("list2string1", (*actual[1])[0]) << "entry should match"; EXPECT_EQ("list2string2", (*actual[1])[1]) << "entry should match"; } @@ -241,7 +243,7 @@ TEST(ArgumentsCase, ScalarArgumentMap) ASSERT_EQ(response::Type::Map, actual.type()) << "should parse the object"; values = actual.release(); - ASSERT_EQ(size_t { 1 }, values.size()) << "should have a single key/value"; + ASSERT_EQ(std::size_t { 1 }, values.size()) << "should have a single key/value"; ASSERT_EQ("foo", values.front().first) << "should match the key"; ASSERT_EQ("bar", values.front().second.get()) << "should match the value"; } @@ -264,7 +266,7 @@ TEST(ArgumentsCase, ScalarArgumentList) ASSERT_EQ(response::Type::List, actual.type()) << "should parse the array"; values = actual.release(); - ASSERT_EQ(size_t { 2 }, values.size()) << "should have 2 values"; + ASSERT_EQ(std::size_t { 2 }, values.size()) << "should have 2 values"; ASSERT_EQ("foo", values.front().get()) << "should match the value"; ASSERT_EQ("bar", values.back().get()) << "should match the value"; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a3b0d52f..e4dda60d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,7 +1,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.28) find_package(GTest MODULE REQUIRED) @@ -14,51 +14,53 @@ target_link_libraries(validation_tests PRIVATE add_bigobj_flag(validation_tests) gtest_add_tests(TARGET validation_tests) -add_executable(today_tests TodayTests.cpp) -target_link_libraries(today_tests PRIVATE - todaygraphql - graphqljson - GTest::GTest - GTest::Main) -add_bigobj_flag(today_tests) -gtest_add_tests(TARGET today_tests) +if(GRAPHQL_BUILD_MODULES) + add_executable(today_tests TodayTests.cpp) + target_link_libraries(today_tests PRIVATE + todaygraphql + graphqljson + GTest::GTest + GTest::Main) + add_bigobj_flag(today_tests) + gtest_add_tests(TARGET today_tests) -add_executable(coroutine_tests CoroutineTests.cpp) -target_link_libraries(coroutine_tests PRIVATE - todaygraphql - graphqljson - GTest::GTest - GTest::Main) -gtest_add_tests(TARGET coroutine_tests) - -add_executable(client_tests ClientTests.cpp) -target_link_libraries(client_tests PRIVATE - todaygraphql - query_client - mutate_client - subscribe_client - GTest::GTest - GTest::Main) -add_bigobj_flag(client_tests) -gtest_add_tests(TARGET client_tests) + add_executable(coroutine_tests CoroutineTests.cpp) + target_link_libraries(coroutine_tests PRIVATE + todaygraphql + graphqljson + GTest::GTest + GTest::Main) + gtest_add_tests(TARGET coroutine_tests) -add_executable(nointrospection_tests NoIntrospectionTests.cpp) -target_link_libraries(nointrospection_tests PRIVATE - todaygraphql_nointrospection - graphqljson - GTest::GTest - GTest::Main) -add_bigobj_flag(nointrospection_tests) -gtest_add_tests(TARGET nointrospection_tests) + add_executable(client_tests ClientTests.cpp) + target_link_libraries(client_tests PRIVATE + todaygraphql + query_client + mutate_client + subscribe_client + GTest::GTest + GTest::Main) + add_bigobj_flag(client_tests) + gtest_add_tests(TARGET client_tests) -add_executable(argument_tests ArgumentTests.cpp) -target_link_libraries(argument_tests PRIVATE - todaygraphql - graphqljson - GTest::GTest - GTest::Main) -add_bigobj_flag(argument_tests) -gtest_add_tests(TARGET argument_tests) + add_executable(nointrospection_tests NoIntrospectionTests.cpp) + target_link_libraries(nointrospection_tests PRIVATE + todaygraphql_nointrospection + graphqljson + GTest::GTest + GTest::Main) + add_bigobj_flag(nointrospection_tests) + gtest_add_tests(TARGET nointrospection_tests) + + add_executable(argument_tests ArgumentTests.cpp) + target_link_libraries(argument_tests PRIVATE + todaygraphql + graphqljson + GTest::GTest + GTest::Main) + add_bigobj_flag(argument_tests) + gtest_add_tests(TARGET argument_tests) +endif() add_executable(pegtl_combined_tests PegtlCombinedTests.cpp) target_link_libraries(pegtl_combined_tests PRIVATE @@ -98,6 +100,16 @@ target_include_directories(response_tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../include) gtest_add_tests(TARGET response_tests) +add_executable(anyscalar_tests AnyScalarTests.cpp) +target_link_libraries(anyscalar_tests PRIVATE + graphqlservice + graphqljson + GTest::GTest + GTest::Main) +target_include_directories(anyscalar_tests PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/../include) +gtest_add_tests(TARGET anyscalar_tests) + if(WIN32 AND BUILD_SHARED_LIBS) add_custom_command(OUTPUT copied_test_dlls COMMAND ${CMAKE_COMMAND} -E copy_if_different @@ -118,10 +130,12 @@ if(WIN32 AND BUILD_SHARED_LIBS) add_custom_target(copy_test_dlls DEPENDS copied_test_dlls) add_dependencies(validation_tests copy_test_dlls) - add_dependencies(today_tests copy_test_dlls) - add_dependencies(client_tests copy_test_dlls) - add_dependencies(nointrospection_tests copy_test_dlls) - add_dependencies(argument_tests copy_test_dlls) + if(GRAPHQL_BUILD_MODULES) + add_dependencies(today_tests copy_test_dlls) + add_dependencies(client_tests copy_test_dlls) + add_dependencies(nointrospection_tests copy_test_dlls) + add_dependencies(argument_tests copy_test_dlls) + endif() add_dependencies(pegtl_combined_tests copy_test_dlls) add_dependencies(pegtl_executable_tests copy_test_dlls) add_dependencies(pegtl_schema_tests copy_test_dlls) diff --git a/test/ClientTests.cpp b/test/ClientTests.cpp index 99835f32..4f674d00 100644 --- a/test/ClientTests.cpp +++ b/test/ClientTests.cpp @@ -3,12 +3,19 @@ #include -#include "MutateClient.h" -#include "QueryClient.h" -#include "SubscribeClient.h" -#include "TodayMock.h" - #include +#include +#include + +import GraphQL.Parse; +import GraphQL.Client; +import GraphQL.Service; + +import GraphQL.Mutate.MutateClient; +import GraphQL.Query.QueryClient; +import GraphQL.Subscribe.SubscribeClient; + +import GraphQL.Today.Mock; using namespace graphql; @@ -28,12 +35,12 @@ class ClientCase : public ::testing::Test } protected: - std::unique_ptr _mockService; + std::shared_ptr _mockService; }; TEST_F(ClientCase, QueryEverything) { - using namespace client::query::Query; + using namespace query::client::query::Query; auto query = GetRequestObject(); @@ -42,20 +49,23 @@ TEST_F(ClientCase, QueryEverything) auto result = _mockService->service ->resolve({ query, {}, std::move(variables), std::launch::async, state }) .get(); - EXPECT_EQ(size_t { 1 }, _mockService->getAppointmentsCount) + EXPECT_EQ(std::size_t { 1 }, _mockService->getAppointmentsCount) << "today service lazy loads the appointments and caches the result"; - EXPECT_EQ(size_t { 1 }, _mockService->getTasksCount) + EXPECT_EQ(std::size_t { 1 }, _mockService->getTasksCount) << "today service lazy loads the tasks and caches the result"; - EXPECT_EQ(size_t { 1 }, _mockService->getUnreadCountsCount) + EXPECT_EQ(std::size_t { 1 }, _mockService->getUnreadCountsCount) << "today service lazy loads the unreadCounts and caches the result"; - EXPECT_EQ(size_t { 1 }, state->appointmentsRequestId) + EXPECT_EQ(std::size_t { 1 }, state->appointmentsRequestId) + << "today service passed the same RequestState"; + EXPECT_EQ(std::size_t { 1 }, state->tasksRequestId) << "today service passed the same RequestState"; - EXPECT_EQ(size_t { 1 }, state->tasksRequestId) << "today service passed the same RequestState"; - EXPECT_EQ(size_t { 1 }, state->unreadCountsRequestId) + EXPECT_EQ(std::size_t { 1 }, state->unreadCountsRequestId) << "today service passed the same RequestState"; - EXPECT_EQ(size_t { 1 }, state->loadAppointmentsCount) << "today service called the loader once"; - EXPECT_EQ(size_t { 1 }, state->loadTasksCount) << "today service called the loader once"; - EXPECT_EQ(size_t { 1 }, state->loadUnreadCountsCount) << "today service called the loader once"; + EXPECT_EQ(std::size_t { 1 }, state->loadAppointmentsCount) + << "today service called the loader once"; + EXPECT_EQ(std::size_t { 1 }, state->loadTasksCount) << "today service called the loader once"; + EXPECT_EQ(std::size_t { 1 }, state->loadUnreadCountsCount) + << "today service called the loader once"; try { @@ -63,10 +73,110 @@ TEST_F(ClientCase, QueryEverything) auto serviceResponse = client::parseServiceResponse(std::move(result)); const auto response = parseResponse(std::move(serviceResponse.data)); - EXPECT_EQ(size_t { 0 }, serviceResponse.errors.size()) << "no errors expected"; + EXPECT_EQ(std::size_t { 0 }, serviceResponse.errors.size()) << "no errors expected"; + + ASSERT_TRUE(response.appointments.edges.has_value()) << "appointments should be set"; + ASSERT_EQ(std::size_t { 1 }, response.appointments.edges->size()) + << "appointments should have 1 entry"; + ASSERT_TRUE((*response.appointments.edges)[0].has_value()) << "edge should be set"; + const auto& appointmentNode = (*response.appointments.edges)[0]->node; + ASSERT_TRUE(appointmentNode.has_value()) << "node should be set"; + EXPECT_EQ(today::getFakeAppointmentId(), appointmentNode->id) + << "id should match in base64 encoding"; + ASSERT_TRUE(appointmentNode->subject.has_value()) << "subject should be set"; + EXPECT_EQ("Lunch?", *(appointmentNode->subject)) << "subject should match"; + ASSERT_TRUE(appointmentNode->when.has_value()) << "when should be set"; + EXPECT_EQ("tomorrow", appointmentNode->when->get()) << "when should match"; + EXPECT_FALSE(appointmentNode->isNow) << "isNow should match"; + EXPECT_EQ("Appointment", appointmentNode->_typename) << "__typename should match"; + + ASSERT_TRUE(response.tasks.edges.has_value()) << "tasks should be set"; + ASSERT_EQ(std::size_t { 1 }, response.tasks.edges->size()) << "tasks should have 1 entry"; + ASSERT_TRUE((*response.tasks.edges)[0].has_value()) << "edge should be set"; + const auto& taskNode = (*response.tasks.edges)[0]->node; + ASSERT_TRUE(taskNode.has_value()) << "node should be set"; + EXPECT_EQ(today::getFakeTaskId(), taskNode->id) << "id should match in base64 encoding"; + ASSERT_TRUE(taskNode->title.has_value()) << "subject should be set"; + EXPECT_EQ("Don't forget", *(taskNode->title)) << "title should match"; + EXPECT_TRUE(taskNode->isComplete) << "isComplete should match"; + EXPECT_EQ("Task", taskNode->_typename) << "__typename should match"; + + ASSERT_TRUE(response.unreadCounts.edges.has_value()) << "unreadCounts should be set"; + ASSERT_EQ(std::size_t { 1 }, response.unreadCounts.edges->size()) + << "unreadCounts should have 1 entry"; + ASSERT_TRUE((*response.unreadCounts.edges)[0].has_value()) << "edge should be set"; + const auto& unreadCountNode = (*response.unreadCounts.edges)[0]->node; + ASSERT_TRUE(unreadCountNode.has_value()) << "node should be set"; + EXPECT_EQ(today::getFakeFolderId(), unreadCountNode->id) + << "id should match in base64 encoding"; + ASSERT_TRUE(unreadCountNode->name.has_value()) << "name should be set"; + EXPECT_EQ("\"Fake\" Inbox", *(unreadCountNode->name)) << "name should match"; + EXPECT_EQ(3, unreadCountNode->unreadCount) << "unreadCount should match"; + EXPECT_EQ("Folder", unreadCountNode->_typename) << "__typename should match"; + + EXPECT_EQ(query::client::query::Query::TaskState::Unassigned, response.testTaskState) + << "testTaskState should match"; + + ASSERT_EQ(std::size_t { 1 }, response.anyType.size()) << "anyType should have 1 entry"; + ASSERT_TRUE(response.anyType[0].has_value()) << "appointment should be set"; + const auto& anyType = *response.anyType[0]; + EXPECT_EQ("Appointment", anyType._typename) << "__typename should match"; + EXPECT_EQ(today::getFakeAppointmentId(), anyType.id) + << "id should match in base64 encoding"; + EXPECT_FALSE(anyType.title.has_value()) << "appointment should not have a title"; + EXPECT_FALSE(anyType.isComplete) << "appointment should not set isComplete"; + ASSERT_TRUE(anyType.subject.has_value()) << "subject should be set"; + EXPECT_EQ("Lunch?", *(anyType.subject)) << "subject should match"; + ASSERT_TRUE(anyType.when.has_value()) << "when should be set"; + EXPECT_EQ("tomorrow", anyType.when->get()) << "when should match"; + EXPECT_FALSE(anyType.isNow) << "isNow should match"; + } + catch (const std::logic_error& ex) + { + FAIL() << ex.what(); + } +} + +TEST_F(ClientCase, QueryEverythingWithVisitor) +{ + using namespace query::client::query::Query; + + auto query = GetRequestObject(); + + response::Value variables(response::Type::Map); + auto state = std::make_shared(1); + auto result = + _mockService->service->visit({ query, {}, std::move(variables), std::launch::async, state }) + .get(); + EXPECT_EQ(std::size_t { 1 }, _mockService->getAppointmentsCount) + << "today service lazy loads the appointments and caches the result"; + EXPECT_EQ(std::size_t { 1 }, _mockService->getTasksCount) + << "today service lazy loads the tasks and caches the result"; + EXPECT_EQ(std::size_t { 1 }, _mockService->getUnreadCountsCount) + << "today service lazy loads the unreadCounts and caches the result"; + EXPECT_EQ(std::size_t { 1 }, state->appointmentsRequestId) + << "today service passed the same RequestState"; + EXPECT_EQ(std::size_t { 1 }, state->tasksRequestId) + << "today service passed the same RequestState"; + EXPECT_EQ(std::size_t { 1 }, state->unreadCountsRequestId) + << "today service passed the same RequestState"; + EXPECT_EQ(std::size_t { 1 }, state->loadAppointmentsCount) + << "today service called the loader once"; + EXPECT_EQ(std::size_t { 1 }, state->loadTasksCount) << "today service called the loader once"; + EXPECT_EQ(std::size_t { 1 }, state->loadUnreadCountsCount) + << "today service called the loader once"; + + try + { + auto visitor = std::make_shared(); + auto responseVisitor = std::make_shared(visitor); + std::move(result.data).visit(responseVisitor); + const auto response = visitor->response(); + + EXPECT_EQ(std::size_t { 0 }, result.errors.size()) << "no errors expected"; ASSERT_TRUE(response.appointments.edges.has_value()) << "appointments should be set"; - ASSERT_EQ(size_t { 1 }, response.appointments.edges->size()) + ASSERT_EQ(std::size_t { 1 }, response.appointments.edges->size()) << "appointments should have 1 entry"; ASSERT_TRUE((*response.appointments.edges)[0].has_value()) << "edge should be set"; const auto& appointmentNode = (*response.appointments.edges)[0]->node; @@ -81,7 +191,7 @@ TEST_F(ClientCase, QueryEverything) EXPECT_EQ("Appointment", appointmentNode->_typename) << "__typename should match"; ASSERT_TRUE(response.tasks.edges.has_value()) << "tasks should be set"; - ASSERT_EQ(size_t { 1 }, response.tasks.edges->size()) << "tasks should have 1 entry"; + ASSERT_EQ(std::size_t { 1 }, response.tasks.edges->size()) << "tasks should have 1 entry"; ASSERT_TRUE((*response.tasks.edges)[0].has_value()) << "edge should be set"; const auto& taskNode = (*response.tasks.edges)[0]->node; ASSERT_TRUE(taskNode.has_value()) << "node should be set"; @@ -92,7 +202,7 @@ TEST_F(ClientCase, QueryEverything) EXPECT_EQ("Task", taskNode->_typename) << "__typename should match"; ASSERT_TRUE(response.unreadCounts.edges.has_value()) << "unreadCounts should be set"; - ASSERT_EQ(size_t { 1 }, response.unreadCounts.edges->size()) + ASSERT_EQ(std::size_t { 1 }, response.unreadCounts.edges->size()) << "unreadCounts should have 1 entry"; ASSERT_TRUE((*response.unreadCounts.edges)[0].has_value()) << "edge should be set"; const auto& unreadCountNode = (*response.unreadCounts.edges)[0]->node; @@ -104,10 +214,10 @@ TEST_F(ClientCase, QueryEverything) EXPECT_EQ(3, unreadCountNode->unreadCount) << "unreadCount should match"; EXPECT_EQ("Folder", unreadCountNode->_typename) << "__typename should match"; - EXPECT_EQ(client::query::Query::TaskState::Unassigned, response.testTaskState) + EXPECT_EQ(query::client::query::Query::TaskState::Unassigned, response.testTaskState) << "testTaskState should match"; - ASSERT_EQ(size_t { 1 }, response.anyType.size()) << "anyType should have 1 entry"; + ASSERT_EQ(std::size_t { 1 }, response.anyType.size()) << "anyType should have 1 entry"; ASSERT_TRUE(response.anyType[0].has_value()) << "appointment should be set"; const auto& anyType = *response.anyType[0]; EXPECT_EQ("Appointment", anyType._typename) << "__typename should match"; @@ -129,14 +239,15 @@ TEST_F(ClientCase, QueryEverything) TEST_F(ClientCase, MutateCompleteTask) { - using namespace client::mutation::CompleteTaskMutation; + using namespace mutate::client::mutation::CompleteTaskMutation; auto query = GetRequestObject(); auto variables = serializeVariables( { std::make_unique(CompleteTaskInput { today::getFakeTaskId(), std::nullopt, std::make_optional(true), - std::make_optional("Hi There!"s) }) }); + std::make_optional("Hi There!"s), + std::vector({true,false}) }) }); auto state = std::make_shared(5); auto result = @@ -148,7 +259,7 @@ TEST_F(ClientCase, MutateCompleteTask) auto serviceResponse = client::parseServiceResponse(std::move(result)); const auto response = parseResponse(std::move(serviceResponse.data)); - EXPECT_EQ(size_t { 0 }, serviceResponse.errors.size()) << "no errors expected"; + EXPECT_EQ(std::size_t { 0 }, serviceResponse.errors.size()) << "no errors expected"; const auto& completedTask = response.completedTask; const auto& task = completedTask.completedTask; @@ -171,7 +282,7 @@ TEST_F(ClientCase, MutateCompleteTask) TEST_F(ClientCase, SubscribeNextAppointmentChangeDefault) { - using namespace client::subscription::TestSubscription; + using namespace subscribe::client::subscription::TestSubscription; auto query = GetRequestObject(); @@ -197,7 +308,7 @@ TEST_F(ClientCase, SubscribeNextAppointmentChangeDefault) auto serviceResponse = client::parseServiceResponse(std::move(result)); const auto response = parseResponse(std::move(serviceResponse.data)); - EXPECT_EQ(size_t { 0 }, serviceResponse.errors.size()) << "no errors expected"; + EXPECT_EQ(std::size_t { 0 }, serviceResponse.errors.size()) << "no errors expected"; const auto& appointmentNode = response.nextAppointment; ASSERT_TRUE(appointmentNode.has_value()) << "should get back a task"; diff --git a/test/CoroutineTests.cpp b/test/CoroutineTests.cpp index 8ba5758c..1fb17f78 100644 --- a/test/CoroutineTests.cpp +++ b/test/CoroutineTests.cpp @@ -3,9 +3,14 @@ #include -#include "TodayMock.h" +#include +#include -#include "graphqlservice/JSONResponse.h" +import GraphQL.Parse; +import GraphQL.JSONResponse; +import GraphQL.Service; + +import GraphQL.Today.Mock; using namespace graphql; @@ -25,7 +30,7 @@ class CoroutineCase : public ::testing::Test } protected: - std::unique_ptr _mockService; + std::shared_ptr _mockService; }; TEST_F(CoroutineCase, QueryEverythingSync) @@ -66,7 +71,7 @@ TEST_F(CoroutineCase, QueryEverythingSync) })"_graphql; response::Value variables(response::Type::Map); auto state = std::make_shared(1); - const auto worker = std::make_shared(); + const auto worker = std::make_shared(); auto result = _mockService->service ->resolve({ query, "Everything"sv, @@ -74,20 +79,23 @@ TEST_F(CoroutineCase, QueryEverythingSync) service::await_async { worker }, state }) .get(); - EXPECT_EQ(size_t { 1 }, _mockService->getAppointmentsCount) + EXPECT_EQ(std::size_t { 1 }, _mockService->getAppointmentsCount) << "today service lazy loads the appointments and caches the result"; - EXPECT_EQ(size_t { 1 }, _mockService->getTasksCount) + EXPECT_EQ(std::size_t { 1 }, _mockService->getTasksCount) << "today service lazy loads the tasks and caches the result"; - EXPECT_EQ(size_t { 1 }, _mockService->getUnreadCountsCount) + EXPECT_EQ(std::size_t { 1 }, _mockService->getUnreadCountsCount) << "today service lazy loads the unreadCounts and caches the result"; - EXPECT_EQ(size_t { 1 }, state->appointmentsRequestId) + EXPECT_EQ(std::size_t { 1 }, state->appointmentsRequestId) + << "today service passed the same RequestState"; + EXPECT_EQ(std::size_t { 1 }, state->tasksRequestId) << "today service passed the same RequestState"; - EXPECT_EQ(size_t { 1 }, state->tasksRequestId) << "today service passed the same RequestState"; - EXPECT_EQ(size_t { 1 }, state->unreadCountsRequestId) + EXPECT_EQ(std::size_t { 1 }, state->unreadCountsRequestId) << "today service passed the same RequestState"; - EXPECT_EQ(size_t { 1 }, state->loadAppointmentsCount) << "today service called the loader once"; - EXPECT_EQ(size_t { 1 }, state->loadTasksCount) << "today service called the loader once"; - EXPECT_EQ(size_t { 1 }, state->loadUnreadCountsCount) << "today service called the loader once"; + EXPECT_EQ(std::size_t { 1 }, state->loadAppointmentsCount) + << "today service called the loader once"; + EXPECT_EQ(std::size_t { 1 }, state->loadTasksCount) << "today service called the loader once"; + EXPECT_EQ(std::size_t { 1 }, state->loadUnreadCountsCount) + << "today service called the loader once"; try { @@ -102,7 +110,7 @@ TEST_F(CoroutineCase, QueryEverythingSync) const auto appointments = service::ScalarArgument::require("appointments", data); const auto appointmentEdges = service::ScalarArgument::require("edges", appointments); - ASSERT_EQ(size_t { 1 }, appointmentEdges.size()) << "appointments should have 1 entry"; + ASSERT_EQ(std::size_t { 1 }, appointmentEdges.size()) << "appointments should have 1 entry"; ASSERT_TRUE(appointmentEdges[0].type() == response::Type::Map) << "appointment should be an object"; const auto appointmentNode = service::ScalarArgument::require("node", appointmentEdges[0]); @@ -121,7 +129,7 @@ TEST_F(CoroutineCase, QueryEverythingSync) const auto tasks = service::ScalarArgument::require("tasks", data); const auto taskEdges = service::ScalarArgument::require("edges", tasks); - ASSERT_EQ(size_t { 1 }, taskEdges.size()) << "tasks should have 1 entry"; + ASSERT_EQ(std::size_t { 1 }, taskEdges.size()) << "tasks should have 1 entry"; ASSERT_TRUE(taskEdges[0].type() == response::Type::Map) << "task should be an object"; const auto taskNode = service::ScalarArgument::require("node", taskEdges[0]); EXPECT_EQ(today::getFakeTaskId(), service::IdArgument::require("id", taskNode)) @@ -136,7 +144,7 @@ TEST_F(CoroutineCase, QueryEverythingSync) const auto unreadCounts = service::ScalarArgument::require("unreadCounts", data); const auto unreadCountEdges = service::ScalarArgument::require("edges", unreadCounts); - ASSERT_EQ(size_t { 1 }, unreadCountEdges.size()) << "unreadCounts should have 1 entry"; + ASSERT_EQ(std::size_t { 1 }, unreadCountEdges.size()) << "unreadCounts should have 1 entry"; ASSERT_TRUE(unreadCountEdges[0].type() == response::Type::Map) << "unreadCount should be an object"; const auto unreadCountNode = service::ScalarArgument::require("node", unreadCountEdges[0]); @@ -201,20 +209,23 @@ TEST_F(CoroutineCase, QueryEverythingQueued) service::await_async { worker }, state }) .get(); - EXPECT_EQ(size_t { 1 }, _mockService->getAppointmentsCount) + EXPECT_EQ(std::size_t { 1 }, _mockService->getAppointmentsCount) << "today service lazy loads the appointments and caches the result"; - EXPECT_EQ(size_t { 1 }, _mockService->getTasksCount) + EXPECT_EQ(std::size_t { 1 }, _mockService->getTasksCount) << "today service lazy loads the tasks and caches the result"; - EXPECT_EQ(size_t { 1 }, _mockService->getUnreadCountsCount) + EXPECT_EQ(std::size_t { 1 }, _mockService->getUnreadCountsCount) << "today service lazy loads the unreadCounts and caches the result"; - EXPECT_EQ(size_t { 2 }, state->appointmentsRequestId) + EXPECT_EQ(std::size_t { 2 }, state->appointmentsRequestId) << "today service passed the same RequestState"; - EXPECT_EQ(size_t { 2 }, state->tasksRequestId) << "today service passed the same RequestState"; - EXPECT_EQ(size_t { 2 }, state->unreadCountsRequestId) + EXPECT_EQ(std::size_t { 2 }, state->tasksRequestId) << "today service passed the same RequestState"; - EXPECT_EQ(size_t { 1 }, state->loadAppointmentsCount) << "today service called the loader once"; - EXPECT_EQ(size_t { 1 }, state->loadTasksCount) << "today service called the loader once"; - EXPECT_EQ(size_t { 1 }, state->loadUnreadCountsCount) << "today service called the loader once"; + EXPECT_EQ(std::size_t { 2 }, state->unreadCountsRequestId) + << "today service passed the same RequestState"; + EXPECT_EQ(std::size_t { 1 }, state->loadAppointmentsCount) + << "today service called the loader once"; + EXPECT_EQ(std::size_t { 1 }, state->loadTasksCount) << "today service called the loader once"; + EXPECT_EQ(std::size_t { 1 }, state->loadUnreadCountsCount) + << "today service called the loader once"; try { @@ -229,7 +240,7 @@ TEST_F(CoroutineCase, QueryEverythingQueued) const auto appointments = service::ScalarArgument::require("appointments", data); const auto appointmentEdges = service::ScalarArgument::require("edges", appointments); - ASSERT_EQ(size_t { 1 }, appointmentEdges.size()) << "appointments should have 1 entry"; + ASSERT_EQ(std::size_t { 1 }, appointmentEdges.size()) << "appointments should have 1 entry"; ASSERT_TRUE(appointmentEdges[0].type() == response::Type::Map) << "appointment should be an object"; const auto appointmentNode = service::ScalarArgument::require("node", appointmentEdges[0]); @@ -248,7 +259,7 @@ TEST_F(CoroutineCase, QueryEverythingQueued) const auto tasks = service::ScalarArgument::require("tasks", data); const auto taskEdges = service::ScalarArgument::require("edges", tasks); - ASSERT_EQ(size_t { 1 }, taskEdges.size()) << "tasks should have 1 entry"; + ASSERT_EQ(std::size_t { 1 }, taskEdges.size()) << "tasks should have 1 entry"; ASSERT_TRUE(taskEdges[0].type() == response::Type::Map) << "task should be an object"; const auto taskNode = service::ScalarArgument::require("node", taskEdges[0]); EXPECT_EQ(today::getFakeTaskId(), service::IdArgument::require("id", taskNode)) @@ -263,7 +274,7 @@ TEST_F(CoroutineCase, QueryEverythingQueued) const auto unreadCounts = service::ScalarArgument::require("unreadCounts", data); const auto unreadCountEdges = service::ScalarArgument::require("edges", unreadCounts); - ASSERT_EQ(size_t { 1 }, unreadCountEdges.size()) << "unreadCounts should have 1 entry"; + ASSERT_EQ(std::size_t { 1 }, unreadCountEdges.size()) << "unreadCounts should have 1 entry"; ASSERT_TRUE(unreadCountEdges[0].type() == response::Type::Map) << "unreadCount should be an object"; const auto unreadCountNode = service::ScalarArgument::require("node", unreadCountEdges[0]); @@ -287,9 +298,6 @@ TEST_F(CoroutineCase, QueryEverythingThreaded) auto query = R"( query Everything { appointments { - pageInfo { - ...PageInfoFields - } edges { node { id @@ -301,9 +309,6 @@ TEST_F(CoroutineCase, QueryEverythingThreaded) } } tasks { - pageInfo { - ...PageInfoFields - } edges { node { id @@ -314,9 +319,6 @@ TEST_F(CoroutineCase, QueryEverythingThreaded) } } unreadCounts { - pageInfo { - ...PageInfoFields - } edges { node { id @@ -326,10 +328,6 @@ TEST_F(CoroutineCase, QueryEverythingThreaded) } } } - } - fragment PageInfoFields on PageInfo { - hasNextPage - hasPreviousPage })"_graphql; response::Value variables(response::Type::Map); auto state = std::make_shared(3); @@ -341,20 +339,23 @@ TEST_F(CoroutineCase, QueryEverythingThreaded) service::await_async { worker }, state }) .get(); - EXPECT_EQ(size_t { 1 }, _mockService->getAppointmentsCount) + EXPECT_EQ(std::size_t { 1 }, _mockService->getAppointmentsCount) << "today service lazy loads the appointments and caches the result"; - EXPECT_EQ(size_t { 1 }, _mockService->getTasksCount) + EXPECT_EQ(std::size_t { 1 }, _mockService->getTasksCount) << "today service lazy loads the tasks and caches the result"; - EXPECT_EQ(size_t { 1 }, _mockService->getUnreadCountsCount) + EXPECT_EQ(std::size_t { 1 }, _mockService->getUnreadCountsCount) << "today service lazy loads the unreadCounts and caches the result"; - EXPECT_EQ(size_t { 3 }, state->appointmentsRequestId) + EXPECT_EQ(std::size_t { 3 }, state->appointmentsRequestId) + << "today service passed the same RequestState"; + EXPECT_EQ(std::size_t { 3 }, state->tasksRequestId) << "today service passed the same RequestState"; - EXPECT_EQ(size_t { 3 }, state->tasksRequestId) << "today service passed the same RequestState"; - EXPECT_EQ(size_t { 3 }, state->unreadCountsRequestId) + EXPECT_EQ(std::size_t { 3 }, state->unreadCountsRequestId) << "today service passed the same RequestState"; - EXPECT_EQ(size_t { 1 }, state->loadAppointmentsCount) << "today service called the loader once"; - EXPECT_EQ(size_t { 1 }, state->loadTasksCount) << "today service called the loader once"; - EXPECT_EQ(size_t { 1 }, state->loadUnreadCountsCount) << "today service called the loader once"; + EXPECT_EQ(std::size_t { 1 }, state->loadAppointmentsCount) + << "today service called the loader once"; + EXPECT_EQ(std::size_t { 1 }, state->loadTasksCount) << "today service called the loader once"; + EXPECT_EQ(std::size_t { 1 }, state->loadUnreadCountsCount) + << "today service called the loader once"; try { @@ -369,7 +370,7 @@ TEST_F(CoroutineCase, QueryEverythingThreaded) const auto appointments = service::ScalarArgument::require("appointments", data); const auto appointmentEdges = service::ScalarArgument::require("edges", appointments); - ASSERT_EQ(size_t { 1 }, appointmentEdges.size()) << "appointments should have 1 entry"; + ASSERT_EQ(std::size_t { 1 }, appointmentEdges.size()) << "appointments should have 1 entry"; ASSERT_TRUE(appointmentEdges[0].type() == response::Type::Map) << "appointment should be an object"; const auto appointmentNode = service::ScalarArgument::require("node", appointmentEdges[0]); @@ -388,7 +389,7 @@ TEST_F(CoroutineCase, QueryEverythingThreaded) const auto tasks = service::ScalarArgument::require("tasks", data); const auto taskEdges = service::ScalarArgument::require("edges", tasks); - ASSERT_EQ(size_t { 1 }, taskEdges.size()) << "tasks should have 1 entry"; + ASSERT_EQ(std::size_t { 1 }, taskEdges.size()) << "tasks should have 1 entry"; ASSERT_TRUE(taskEdges[0].type() == response::Type::Map) << "task should be an object"; const auto taskNode = service::ScalarArgument::require("node", taskEdges[0]); EXPECT_EQ(today::getFakeTaskId(), service::IdArgument::require("id", taskNode)) @@ -403,7 +404,7 @@ TEST_F(CoroutineCase, QueryEverythingThreaded) const auto unreadCounts = service::ScalarArgument::require("unreadCounts", data); const auto unreadCountEdges = service::ScalarArgument::require("edges", unreadCounts); - ASSERT_EQ(size_t { 1 }, unreadCountEdges.size()) << "unreadCounts should have 1 entry"; + ASSERT_EQ(std::size_t { 1 }, unreadCountEdges.size()) << "unreadCounts should have 1 entry"; ASSERT_TRUE(unreadCountEdges[0].type() == response::Type::Map) << "unreadCount should be an object"; const auto unreadCountNode = service::ScalarArgument::require("node", unreadCountEdges[0]); diff --git a/test/NoIntrospectionTests.cpp b/test/NoIntrospectionTests.cpp index 371e807f..2d2a0c85 100644 --- a/test/NoIntrospectionTests.cpp +++ b/test/NoIntrospectionTests.cpp @@ -3,11 +3,15 @@ #include -#include "TodayMock.h" +#include +#include +#include -#include "graphqlservice/JSONResponse.h" +import GraphQL.Parse; +import GraphQL.JSONResponse; +import GraphQL.Service; -#include +import GraphQL.Today.Mock; using namespace graphql; @@ -30,7 +34,7 @@ class NoIntrospectionServiceCase : public ::testing::Test } protected: - std::unique_ptr _mockService; + std::shared_ptr _mockService; }; TEST_F(NoIntrospectionServiceCase, QueryEverything) @@ -75,20 +79,23 @@ TEST_F(NoIntrospectionServiceCase, QueryEverything) _mockService->service ->resolve({ query, "Everything"sv, std::move(variables), std::launch::async, state }) .get(); - EXPECT_EQ(size_t { 1 }, _mockService->getAppointmentsCount) + EXPECT_EQ(std::size_t { 1 }, _mockService->getAppointmentsCount) << "today service lazy loads the appointments and caches the result"; - EXPECT_EQ(size_t { 1 }, _mockService->getTasksCount) + EXPECT_EQ(std::size_t { 1 }, _mockService->getTasksCount) << "today service lazy loads the tasks and caches the result"; - EXPECT_EQ(size_t { 1 }, _mockService->getUnreadCountsCount) + EXPECT_EQ(std::size_t { 1 }, _mockService->getUnreadCountsCount) << "today service lazy loads the unreadCounts and caches the result"; - EXPECT_EQ(size_t { 1 }, state->appointmentsRequestId) + EXPECT_EQ(std::size_t { 1 }, state->appointmentsRequestId) + << "today service passed the same RequestState"; + EXPECT_EQ(std::size_t { 1 }, state->tasksRequestId) << "today service passed the same RequestState"; - EXPECT_EQ(size_t { 1 }, state->tasksRequestId) << "today service passed the same RequestState"; - EXPECT_EQ(size_t { 1 }, state->unreadCountsRequestId) + EXPECT_EQ(std::size_t { 1 }, state->unreadCountsRequestId) << "today service passed the same RequestState"; - EXPECT_EQ(size_t { 1 }, state->loadAppointmentsCount) << "today service called the loader once"; - EXPECT_EQ(size_t { 1 }, state->loadTasksCount) << "today service called the loader once"; - EXPECT_EQ(size_t { 1 }, state->loadUnreadCountsCount) << "today service called the loader once"; + EXPECT_EQ(std::size_t { 1 }, state->loadAppointmentsCount) + << "today service called the loader once"; + EXPECT_EQ(std::size_t { 1 }, state->loadTasksCount) << "today service called the loader once"; + EXPECT_EQ(std::size_t { 1 }, state->loadUnreadCountsCount) + << "today service called the loader once"; try { @@ -103,7 +110,7 @@ TEST_F(NoIntrospectionServiceCase, QueryEverything) const auto appointments = service::ScalarArgument::require("appointments", data); const auto appointmentEdges = service::ScalarArgument::require("edges", appointments); - ASSERT_EQ(size_t { 1 }, appointmentEdges.size()) << "appointments should have 1 entry"; + ASSERT_EQ(std::size_t { 1 }, appointmentEdges.size()) << "appointments should have 1 entry"; ASSERT_TRUE(appointmentEdges[0].type() == response::Type::Map) << "appointment should be an object"; const auto appointmentNode = service::ScalarArgument::require("node", appointmentEdges[0]); @@ -122,7 +129,7 @@ TEST_F(NoIntrospectionServiceCase, QueryEverything) const auto tasks = service::ScalarArgument::require("tasks", data); const auto taskEdges = service::ScalarArgument::require("edges", tasks); - ASSERT_EQ(size_t { 1 }, taskEdges.size()) << "tasks should have 1 entry"; + ASSERT_EQ(std::size_t { 1 }, taskEdges.size()) << "tasks should have 1 entry"; ASSERT_TRUE(taskEdges[0].type() == response::Type::Map) << "task should be an object"; const auto taskNode = service::ScalarArgument::require("node", taskEdges[0]); EXPECT_EQ(today::getFakeTaskId(), service::IdArgument::require("id", taskNode)) @@ -137,7 +144,7 @@ TEST_F(NoIntrospectionServiceCase, QueryEverything) const auto unreadCounts = service::ScalarArgument::require("unreadCounts", data); const auto unreadCountEdges = service::ScalarArgument::require("edges", unreadCounts); - ASSERT_EQ(size_t { 1 }, unreadCountEdges.size()) << "unreadCounts should have 1 entry"; + ASSERT_EQ(std::size_t { 1 }, unreadCountEdges.size()) << "unreadCounts should have 1 entry"; ASSERT_TRUE(unreadCountEdges[0].type() == response::Type::Map) << "unreadCount should be an object"; const auto unreadCountNode = service::ScalarArgument::require("node", unreadCountEdges[0]); diff --git a/test/PegtlCombinedTests.cpp b/test/PegtlCombinedTests.cpp index 170c751f..a4e54521 100644 --- a/test/PegtlCombinedTests.cpp +++ b/test/PegtlCombinedTests.cpp @@ -7,6 +7,8 @@ #include +#include + using namespace graphql; using namespace graphql::peg; @@ -14,6 +16,6 @@ using namespace tao::graphqlpeg; TEST(PegtlCombinedCase, AnalyzeMixedGrammar) { - ASSERT_EQ(size_t { 0 }, analyze(true)) + ASSERT_EQ(std::size_t { 0 }, analyze(true)) << "there shouldn't be any infinite loops in the PEG version of the grammar"; } diff --git a/test/PegtlExecutableTests.cpp b/test/PegtlExecutableTests.cpp index 4dc47120..af14bd4d 100644 --- a/test/PegtlExecutableTests.cpp +++ b/test/PegtlExecutableTests.cpp @@ -9,6 +9,8 @@ #include +#include + using namespace graphql; using namespace graphql::peg; @@ -138,7 +140,7 @@ TEST(PegtlExecutableCase, ParseVariableDefaultEmptyList) TEST(PegtlExecutableCase, AnalyzeExecutableGrammar) { - ASSERT_EQ(size_t { 0 }, analyze(true)) + ASSERT_EQ(std::size_t { 0 }, analyze(true)) << "there shouldn't be any infinite loops in the PEG version of the grammar"; } @@ -248,8 +250,7 @@ TEST(PegtlExecutableCase, ParseFloatWithFractionalAndExponentialParts) TEST(PegtlExecutableCase, ParseIgnoreUnicodeBOM) { - memory_input<> input("query { \xEF\xBB\xBF __typename }", - "ParseIgnoreUnicodeBOM"); + memory_input<> input("query { \xEF\xBB\xBF __typename }", "ParseIgnoreUnicodeBOM"); const bool result = parse(input); diff --git a/test/PegtlSchemaTests.cpp b/test/PegtlSchemaTests.cpp index 330a6d72..24e621fd 100644 --- a/test/PegtlSchemaTests.cpp +++ b/test/PegtlSchemaTests.cpp @@ -7,6 +7,8 @@ #include +#include + using namespace graphql; using namespace graphql::peg; @@ -219,6 +221,6 @@ TEST(PegtlSchemaCase, ParseTodaySchema) TEST(PegtlSchemaCase, AnalyzeSchemaGrammar) { - ASSERT_EQ(size_t { 0 }, analyze(true)) + ASSERT_EQ(std::size_t { 0 }, analyze(true)) << "there shouldn't be any infinite loops in the PEG version of the grammar"; } diff --git a/test/ResponseTests.cpp b/test/ResponseTests.cpp index 4327186f..9da7d6a0 100644 --- a/test/ResponseTests.cpp +++ b/test/ResponseTests.cpp @@ -5,6 +5,8 @@ #include "graphqlservice/GraphQLResponse.h" +#include + using namespace graphql; TEST(ResponseCase, ValueConstructorFromStringLiteral) @@ -22,7 +24,7 @@ TEST(ResponseCase, IdTypeCompareEqual) std::string_view fakeIdString { "fakeId" }; response::IdType result(fakeIdString.size()); - std::copy(fakeIdString.cbegin(), fakeIdString.cend(), result.begin()); + std::ranges::copy(fakeIdString, result.begin()); return response::IdType { std::move(result) }; }(); diff --git a/test/TodayTests.cpp b/test/TodayTests.cpp index 95ed9414..fe3e5c6b 100644 --- a/test/TodayTests.cpp +++ b/test/TodayTests.cpp @@ -3,11 +3,15 @@ #include -#include "TodayMock.h" +#include +#include +#include -#include "graphqlservice/JSONResponse.h" +import GraphQL.Parse; +import GraphQL.JSONResponse; +import GraphQL.Service; -#include +import GraphQL.Today.Mock; using namespace graphql; @@ -27,7 +31,7 @@ class TodayServiceCase : public ::testing::Test } protected: - std::unique_ptr _mockService; + std::shared_ptr _mockService; }; TEST_F(TodayServiceCase, QueryEverything) @@ -72,20 +76,23 @@ TEST_F(TodayServiceCase, QueryEverything) _mockService->service ->resolve({ query, "Everything"sv, std::move(variables), std::launch::async, state }) .get(); - EXPECT_EQ(size_t { 1 }, _mockService->getAppointmentsCount) + EXPECT_EQ(std::size_t { 1 }, _mockService->getAppointmentsCount) << "today service lazy loads the appointments and caches the result"; - EXPECT_EQ(size_t { 1 }, _mockService->getTasksCount) + EXPECT_EQ(std::size_t { 1 }, _mockService->getTasksCount) << "today service lazy loads the tasks and caches the result"; - EXPECT_EQ(size_t { 1 }, _mockService->getUnreadCountsCount) + EXPECT_EQ(std::size_t { 1 }, _mockService->getUnreadCountsCount) << "today service lazy loads the unreadCounts and caches the result"; - EXPECT_EQ(size_t { 1 }, state->appointmentsRequestId) + EXPECT_EQ(std::size_t { 1 }, state->appointmentsRequestId) + << "today service passed the same RequestState"; + EXPECT_EQ(std::size_t { 1 }, state->tasksRequestId) << "today service passed the same RequestState"; - EXPECT_EQ(size_t { 1 }, state->tasksRequestId) << "today service passed the same RequestState"; - EXPECT_EQ(size_t { 1 }, state->unreadCountsRequestId) + EXPECT_EQ(std::size_t { 1 }, state->unreadCountsRequestId) << "today service passed the same RequestState"; - EXPECT_EQ(size_t { 1 }, state->loadAppointmentsCount) << "today service called the loader once"; - EXPECT_EQ(size_t { 1 }, state->loadTasksCount) << "today service called the loader once"; - EXPECT_EQ(size_t { 1 }, state->loadUnreadCountsCount) << "today service called the loader once"; + EXPECT_EQ(std::size_t { 1 }, state->loadAppointmentsCount) + << "today service called the loader once"; + EXPECT_EQ(std::size_t { 1 }, state->loadTasksCount) << "today service called the loader once"; + EXPECT_EQ(std::size_t { 1 }, state->loadUnreadCountsCount) + << "today service called the loader once"; try { @@ -100,7 +107,7 @@ TEST_F(TodayServiceCase, QueryEverything) const auto appointments = service::ScalarArgument::require("appointments", data); const auto appointmentEdges = service::ScalarArgument::require("edges", appointments); - ASSERT_EQ(size_t { 1 }, appointmentEdges.size()) << "appointments should have 1 entry"; + ASSERT_EQ(std::size_t { 1 }, appointmentEdges.size()) << "appointments should have 1 entry"; ASSERT_TRUE(appointmentEdges[0].type() == response::Type::Map) << "appointment should be an object"; const auto appointmentNode = service::ScalarArgument::require("node", appointmentEdges[0]); @@ -119,7 +126,7 @@ TEST_F(TodayServiceCase, QueryEverything) const auto tasks = service::ScalarArgument::require("tasks", data); const auto taskEdges = service::ScalarArgument::require("edges", tasks); - ASSERT_EQ(size_t { 1 }, taskEdges.size()) << "tasks should have 1 entry"; + ASSERT_EQ(std::size_t { 1 }, taskEdges.size()) << "tasks should have 1 entry"; ASSERT_TRUE(taskEdges[0].type() == response::Type::Map) << "task should be an object"; const auto taskNode = service::ScalarArgument::require("node", taskEdges[0]); EXPECT_EQ(today::getFakeTaskId(), service::IdArgument::require("id", taskNode)) @@ -134,7 +141,7 @@ TEST_F(TodayServiceCase, QueryEverything) const auto unreadCounts = service::ScalarArgument::require("unreadCounts", data); const auto unreadCountEdges = service::ScalarArgument::require("edges", unreadCounts); - ASSERT_EQ(size_t { 1 }, unreadCountEdges.size()) << "unreadCounts should have 1 entry"; + ASSERT_EQ(std::size_t { 1 }, unreadCountEdges.size()) << "unreadCounts should have 1 entry"; ASSERT_TRUE(unreadCountEdges[0].type() == response::Type::Map) << "unreadCount should be an object"; const auto unreadCountNode = service::ScalarArgument::require("node", unreadCountEdges[0]); @@ -171,20 +178,21 @@ TEST_F(TodayServiceCase, QueryAppointments) auto state = std::make_shared(2); auto result = _mockService->service->resolve({ query, {}, std::move(variables), {}, state }).get(); - EXPECT_EQ(size_t { 1 }, _mockService->getAppointmentsCount) + EXPECT_EQ(std::size_t { 1 }, _mockService->getAppointmentsCount) << "today service lazy loads the appointments and caches the result"; - EXPECT_GE(size_t { 1 }, _mockService->getTasksCount) + EXPECT_GE(std::size_t { 1 }, _mockService->getTasksCount) << "today service lazy loads the tasks and caches the result"; - EXPECT_GE(size_t { 1 }, _mockService->getUnreadCountsCount) + EXPECT_GE(std::size_t { 1 }, _mockService->getUnreadCountsCount) << "today service lazy loads the unreadCounts and caches the result"; - EXPECT_EQ(size_t { 2 }, state->appointmentsRequestId) + EXPECT_EQ(std::size_t { 2 }, state->appointmentsRequestId) << "today service passed the same RequestState"; - EXPECT_EQ(size_t { 0 }, state->tasksRequestId) << "today service did not call the loader"; - EXPECT_EQ(size_t { 0 }, state->unreadCountsRequestId) + EXPECT_EQ(std::size_t { 0 }, state->tasksRequestId) << "today service did not call the loader"; + EXPECT_EQ(std::size_t { 0 }, state->unreadCountsRequestId) << "today service did not call the loader"; - EXPECT_EQ(size_t { 1 }, state->loadAppointmentsCount) << "today service called the loader once"; - EXPECT_EQ(size_t { 0 }, state->loadTasksCount) << "today service did not call the loader"; - EXPECT_EQ(size_t { 0 }, state->loadUnreadCountsCount) + EXPECT_EQ(std::size_t { 1 }, state->loadAppointmentsCount) + << "today service called the loader once"; + EXPECT_EQ(std::size_t { 0 }, state->loadTasksCount) << "today service did not call the loader"; + EXPECT_EQ(std::size_t { 0 }, state->loadUnreadCountsCount) << "today service did not call the loader"; try @@ -200,7 +208,7 @@ TEST_F(TodayServiceCase, QueryAppointments) const auto appointments = service::ScalarArgument::require("appointments", data); const auto appointmentEdges = service::ScalarArgument::require("edges", appointments); - ASSERT_EQ(size_t { 1 }, appointmentEdges.size()) << "appointments should have 1 entry"; + ASSERT_EQ(std::size_t { 1 }, appointmentEdges.size()) << "appointments should have 1 entry"; ASSERT_TRUE(appointmentEdges[0].type() == response::Type::Map) << "appointment should be an object"; const auto appointmentNode = service::ScalarArgument::require("node", appointmentEdges[0]); @@ -239,20 +247,21 @@ TEST_F(TodayServiceCase, QueryAppointmentsWithForceError) auto state = std::make_shared(2); auto result = _mockService->service->resolve({ query, {}, std::move(variables), {}, state }).get(); - EXPECT_EQ(size_t { 1 }, _mockService->getAppointmentsCount) + EXPECT_EQ(std::size_t { 1 }, _mockService->getAppointmentsCount) << "today service lazy loads the appointments and caches the result"; - EXPECT_GE(size_t { 1 }, _mockService->getTasksCount) + EXPECT_GE(std::size_t { 1 }, _mockService->getTasksCount) << "today service lazy loads the tasks and caches the result"; - EXPECT_GE(size_t { 1 }, _mockService->getUnreadCountsCount) + EXPECT_GE(std::size_t { 1 }, _mockService->getUnreadCountsCount) << "today service lazy loads the unreadCounts and caches the result"; - EXPECT_EQ(size_t { 2 }, state->appointmentsRequestId) + EXPECT_EQ(std::size_t { 2 }, state->appointmentsRequestId) << "today service passed the same RequestState"; - EXPECT_EQ(size_t { 0 }, state->tasksRequestId) << "today service did not call the loader"; - EXPECT_EQ(size_t { 0 }, state->unreadCountsRequestId) + EXPECT_EQ(std::size_t { 0 }, state->tasksRequestId) << "today service did not call the loader"; + EXPECT_EQ(std::size_t { 0 }, state->unreadCountsRequestId) << "today service did not call the loader"; - EXPECT_EQ(size_t { 1 }, state->loadAppointmentsCount) << "today service called the loader once"; - EXPECT_EQ(size_t { 0 }, state->loadTasksCount) << "today service did not call the loader"; - EXPECT_EQ(size_t { 0 }, state->loadUnreadCountsCount) + EXPECT_EQ(std::size_t { 1 }, state->loadAppointmentsCount) + << "today service called the loader once"; + EXPECT_EQ(std::size_t { 0 }, state->loadTasksCount) << "today service did not call the loader"; + EXPECT_EQ(std::size_t { 0 }, state->loadUnreadCountsCount) << "today service did not call the loader"; try @@ -275,7 +284,7 @@ TEST_F(TodayServiceCase, QueryAppointmentsWithForceError) const auto appointments = service::ScalarArgument::require("appointments", data); const auto appointmentEdges = service::ScalarArgument::require("edges", appointments); - ASSERT_EQ(size_t { 1 }, appointmentEdges.size()) << "appointments should have 1 entry"; + ASSERT_EQ(std::size_t { 1 }, appointmentEdges.size()) << "appointments should have 1 entry"; ASSERT_TRUE(appointmentEdges[0].type() == response::Type::Map) << "appointment should be an object"; const auto appointmentNode = service::ScalarArgument::require("node", appointmentEdges[0]); @@ -315,20 +324,21 @@ TEST_F(TodayServiceCase, QueryAppointmentsWithForceErrorAsync) auto result = _mockService->service ->resolve({ query, {}, std::move(variables), std::launch::async, state }) .get(); - EXPECT_EQ(size_t { 1 }, _mockService->getAppointmentsCount) + EXPECT_EQ(std::size_t { 1 }, _mockService->getAppointmentsCount) << "today service lazy loads the appointments and caches the result"; - EXPECT_GE(size_t { 1 }, _mockService->getTasksCount) + EXPECT_GE(std::size_t { 1 }, _mockService->getTasksCount) << "today service lazy loads the tasks and caches the result"; - EXPECT_GE(size_t { 1 }, _mockService->getUnreadCountsCount) + EXPECT_GE(std::size_t { 1 }, _mockService->getUnreadCountsCount) << "today service lazy loads the unreadCounts and caches the result"; - EXPECT_EQ(size_t { 2 }, state->appointmentsRequestId) + EXPECT_EQ(std::size_t { 2 }, state->appointmentsRequestId) << "today service passed the same RequestState"; - EXPECT_EQ(size_t { 0 }, state->tasksRequestId) << "today service did not call the loader"; - EXPECT_EQ(size_t { 0 }, state->unreadCountsRequestId) + EXPECT_EQ(std::size_t { 0 }, state->tasksRequestId) << "today service did not call the loader"; + EXPECT_EQ(std::size_t { 0 }, state->unreadCountsRequestId) << "today service did not call the loader"; - EXPECT_EQ(size_t { 1 }, state->loadAppointmentsCount) << "today service called the loader once"; - EXPECT_EQ(size_t { 0 }, state->loadTasksCount) << "today service did not call the loader"; - EXPECT_EQ(size_t { 0 }, state->loadUnreadCountsCount) + EXPECT_EQ(std::size_t { 1 }, state->loadAppointmentsCount) + << "today service called the loader once"; + EXPECT_EQ(std::size_t { 0 }, state->loadTasksCount) << "today service did not call the loader"; + EXPECT_EQ(std::size_t { 0 }, state->loadUnreadCountsCount) << "today service did not call the loader"; try @@ -351,7 +361,7 @@ TEST_F(TodayServiceCase, QueryAppointmentsWithForceErrorAsync) const auto appointments = service::ScalarArgument::require("appointments", data); const auto appointmentEdges = service::ScalarArgument::require("edges", appointments); - ASSERT_EQ(size_t { 1 }, appointmentEdges.size()) << "appointments should have 1 entry"; + ASSERT_EQ(std::size_t { 1 }, appointmentEdges.size()) << "appointments should have 1 entry"; ASSERT_TRUE(appointmentEdges[0].type() == response::Type::Map) << "appointment should be an object"; const auto appointmentNode = service::ScalarArgument::require("node", appointmentEdges[0]); @@ -388,21 +398,22 @@ TEST_F(TodayServiceCase, QueryTasks) auto state = std::make_shared(3); auto result = _mockService->service->resolve({ query, {}, std::move(variables), {}, state }).get(); - EXPECT_GE(size_t { 1 }, _mockService->getAppointmentsCount) + EXPECT_GE(std::size_t { 1 }, _mockService->getAppointmentsCount) << "today service lazy loads the appointments and caches the result"; - EXPECT_EQ(size_t { 1 }, _mockService->getTasksCount) + EXPECT_EQ(std::size_t { 1 }, _mockService->getTasksCount) << "today service lazy loads the tasks and caches the result"; - EXPECT_GE(size_t { 1 }, _mockService->getUnreadCountsCount) + EXPECT_GE(std::size_t { 1 }, _mockService->getUnreadCountsCount) << "today service lazy loads the unreadCounts and caches the result"; - EXPECT_EQ(size_t { 0 }, state->appointmentsRequestId) + EXPECT_EQ(std::size_t { 0 }, state->appointmentsRequestId) << "today service did not call the loader"; - EXPECT_EQ(size_t { 3 }, state->tasksRequestId) << "today service passed the same RequestState"; - EXPECT_EQ(size_t { 0 }, state->unreadCountsRequestId) + EXPECT_EQ(std::size_t { 3 }, state->tasksRequestId) + << "today service passed the same RequestState"; + EXPECT_EQ(std::size_t { 0 }, state->unreadCountsRequestId) << "today service did not call the loader"; - EXPECT_EQ(size_t { 0 }, state->loadAppointmentsCount) + EXPECT_EQ(std::size_t { 0 }, state->loadAppointmentsCount) << "today service did not call the loader"; - EXPECT_EQ(size_t { 1 }, state->loadTasksCount) << "today service called the loader once"; - EXPECT_EQ(size_t { 0 }, state->loadUnreadCountsCount) + EXPECT_EQ(std::size_t { 1 }, state->loadTasksCount) << "today service called the loader once"; + EXPECT_EQ(std::size_t { 0 }, state->loadUnreadCountsCount) << "today service did not call the loader"; try @@ -418,7 +429,7 @@ TEST_F(TodayServiceCase, QueryTasks) const auto tasks = service::ScalarArgument::require("tasks", data); const auto taskEdges = service::ScalarArgument::require("edges", tasks); - ASSERT_EQ(size_t { 1 }, taskEdges.size()) << "tasks should have 1 entry"; + ASSERT_EQ(std::size_t { 1 }, taskEdges.size()) << "tasks should have 1 entry"; ASSERT_TRUE(taskEdges[0].type() == response::Type::Map) << "task should be an object"; const auto taskNode = service::ScalarArgument::require("node", taskEdges[0]); EXPECT_EQ(today::getFakeTaskId(), service::IdArgument::require("taskId", taskNode)) @@ -451,21 +462,22 @@ TEST_F(TodayServiceCase, QueryUnreadCounts) auto state = std::make_shared(4); auto result = _mockService->service->resolve({ query, {}, std::move(variables), {}, state }).get(); - EXPECT_GE(size_t { 1 }, _mockService->getAppointmentsCount) + EXPECT_GE(std::size_t { 1 }, _mockService->getAppointmentsCount) << "today service lazy loads the appointments and caches the result"; - EXPECT_GE(size_t { 1 }, _mockService->getTasksCount) + EXPECT_GE(std::size_t { 1 }, _mockService->getTasksCount) << "today service lazy loads the tasks and caches the result"; - EXPECT_EQ(size_t { 1 }, _mockService->getUnreadCountsCount) + EXPECT_EQ(std::size_t { 1 }, _mockService->getUnreadCountsCount) << "today service lazy loads the unreadCounts and caches the result"; - EXPECT_EQ(size_t { 0 }, state->appointmentsRequestId) + EXPECT_EQ(std::size_t { 0 }, state->appointmentsRequestId) << "today service did not call the loader"; - EXPECT_EQ(size_t { 0 }, state->tasksRequestId) << "today service did not call the loader"; - EXPECT_EQ(size_t { 4 }, state->unreadCountsRequestId) + EXPECT_EQ(std::size_t { 0 }, state->tasksRequestId) << "today service did not call the loader"; + EXPECT_EQ(std::size_t { 4 }, state->unreadCountsRequestId) << "today service passed the same RequestState"; - EXPECT_EQ(size_t { 0 }, state->loadAppointmentsCount) + EXPECT_EQ(std::size_t { 0 }, state->loadAppointmentsCount) << "today service did not call the loader"; - EXPECT_EQ(size_t { 0 }, state->loadTasksCount) << "today service did not call the loader"; - EXPECT_EQ(size_t { 1 }, state->loadUnreadCountsCount) << "today service called the loader once"; + EXPECT_EQ(std::size_t { 0 }, state->loadTasksCount) << "today service did not call the loader"; + EXPECT_EQ(std::size_t { 1 }, state->loadUnreadCountsCount) + << "today service called the loader once"; try { @@ -480,7 +492,7 @@ TEST_F(TodayServiceCase, QueryUnreadCounts) const auto unreadCounts = service::ScalarArgument::require("unreadCounts", data); const auto unreadCountEdges = service::ScalarArgument::require("edges", unreadCounts); - ASSERT_EQ(size_t { 1 }, unreadCountEdges.size()) << "unreadCounts should have 1 entry"; + ASSERT_EQ(std::size_t { 1 }, unreadCountEdges.size()) << "unreadCounts should have 1 entry"; ASSERT_TRUE(unreadCountEdges[0].type() == response::Type::Map) << "unreadCount should be an object"; const auto unreadCountNode = service::ScalarArgument::require("node", unreadCountEdges[0]); @@ -615,7 +627,8 @@ TEST_F(TodayServiceCase, SubscribeNextAppointmentChangeOverride) auto subscriptionObject = std::make_shared( [](const std::shared_ptr& state) -> std::shared_ptr { - EXPECT_EQ(size_t { 7 }, std::static_pointer_cast(state)->requestId) + EXPECT_EQ(std::size_t { 7 }, + std::static_pointer_cast(state)->requestId) << "should pass the RequestState to the subscription resolvers"; return std::make_shared( response::IdType(today::getFakeAppointmentId()), @@ -963,7 +976,7 @@ TEST_F(TodayServiceCase, NestedFragmentDirectives) capturedParams.pop(); const auto params1 = std::move(capturedParams.top()); capturedParams.pop(); - ASSERT_EQ(size_t { 1 }, params1.operationDirectives.size()) + ASSERT_EQ(std::size_t { 1 }, params1.operationDirectives.size()) << "missing operation directive"; const auto itrQueryTag1 = params1.operationDirectives.cbegin(); ASSERT_TRUE(itrQueryTag1->first == "queryTag"sv) << "missing required directive"; @@ -972,18 +985,19 @@ TEST_F(TodayServiceCase, NestedFragmentDirectives) const auto fragmentDefinitionCount1 = params1.fragmentDefinitionDirectives.size(); const auto fragmentSpreadCount1 = params1.fragmentSpreadDirectives.size(); const auto inlineFragmentCount1 = params1.inlineFragmentDirectives.size(); - ASSERT_EQ(size_t { 1 }, params1.fieldDirectives.size()) << "missing operation directive"; + ASSERT_EQ(std::size_t { 1 }, params1.fieldDirectives.size()) + << "missing operation directive"; const auto itrFieldTag1 = params1.fieldDirectives.cbegin(); ASSERT_TRUE(itrFieldTag1->first == "fieldTag"sv) << "missing required directive"; const auto& fieldTag1 = itrFieldTag1->second; const auto field1 = service::StringArgument::require("field", fieldTag1); - ASSERT_EQ(size_t { 1 }, params2.operationDirectives.size()) + ASSERT_EQ(std::size_t { 1 }, params2.operationDirectives.size()) << "missing operation directive"; const auto itrQueryTag2 = params2.operationDirectives.cbegin(); ASSERT_TRUE(itrQueryTag2->first == "queryTag"sv) << "missing required directive"; const auto& queryTag2 = itrQueryTag2->second; const auto query2 = service::StringArgument::require("query", queryTag2); - ASSERT_EQ(size_t { 1 }, params2.fragmentDefinitionDirectives.size()) + ASSERT_EQ(std::size_t { 1 }, params2.fragmentDefinitionDirectives.size()) << "missing fragment definition directive"; const auto itrFragmentDefinitionTag2 = params2.fragmentDefinitionDirectives.cbegin(); ASSERT_TRUE(itrFragmentDefinitionTag2->first == "fragmentDefinitionTag"sv) @@ -991,7 +1005,7 @@ TEST_F(TodayServiceCase, NestedFragmentDirectives) const auto& fragmentDefinitionTag2 = itrFragmentDefinitionTag2->second; const auto fragmentDefinition2 = service::StringArgument::require("fragmentDefinition", fragmentDefinitionTag2); - ASSERT_EQ(size_t { 1 }, params2.fragmentSpreadDirectives.size()) + ASSERT_EQ(std::size_t { 1 }, params2.fragmentSpreadDirectives.size()) << "missing fragment spread directive"; const auto itrFragmentSpreadTag2 = params2.fragmentSpreadDirectives.cbegin(); ASSERT_TRUE(itrFragmentSpreadTag2->first == "fragmentSpreadTag"sv) @@ -1000,18 +1014,18 @@ TEST_F(TodayServiceCase, NestedFragmentDirectives) const auto fragmentSpread2 = service::StringArgument::require("fragmentSpread", fragmentSpreadTag2); const auto inlineFragmentCount2 = params2.inlineFragmentDirectives.size(); - ASSERT_EQ(size_t { 1 }, params2.fieldDirectives.size()) << "missing field directive"; + ASSERT_EQ(std::size_t { 1 }, params2.fieldDirectives.size()) << "missing field directive"; const auto itrFieldTag2 = params2.fieldDirectives.cbegin(); ASSERT_TRUE(itrFieldTag2->first == "fieldTag"sv) << "missing field directive"; const auto& fieldTag2 = itrFieldTag2->second; const auto field2 = service::StringArgument::require("field", fieldTag2); - ASSERT_EQ(size_t { 1 }, params3.operationDirectives.size()) + ASSERT_EQ(std::size_t { 1 }, params3.operationDirectives.size()) << "missing operation directive"; const auto itrQueryTag3 = params3.operationDirectives.cbegin(); ASSERT_TRUE(itrQueryTag3->first == "queryTag"sv) << "missing required directive"; const auto& queryTag3 = itrQueryTag3->second; const auto query3 = service::StringArgument::require("query", queryTag3); - ASSERT_EQ(size_t { 1 }, params3.fragmentDefinitionDirectives.size()) + ASSERT_EQ(std::size_t { 1 }, params3.fragmentDefinitionDirectives.size()) << "missing fragment definition directive"; const auto itrFragmentDefinitionTag3 = params3.fragmentDefinitionDirectives.cbegin(); ASSERT_TRUE(itrFragmentDefinitionTag3->first == "fragmentDefinitionTag"sv) @@ -1019,7 +1033,7 @@ TEST_F(TodayServiceCase, NestedFragmentDirectives) const auto& fragmentDefinitionTag3 = itrFragmentDefinitionTag3->second; const auto fragmentDefinition3 = service::StringArgument::require("fragmentDefinition", fragmentDefinitionTag3); - ASSERT_EQ(size_t { 1 }, params3.fragmentSpreadDirectives.size()) + ASSERT_EQ(std::size_t { 1 }, params3.fragmentSpreadDirectives.size()) << "missing fragment spread directive"; const auto itrFragmentSpreadTag3 = params3.fragmentSpreadDirectives.cbegin(); ASSERT_TRUE(itrFragmentSpreadTag3->first == "fragmentSpreadTag"sv) @@ -1027,19 +1041,19 @@ TEST_F(TodayServiceCase, NestedFragmentDirectives) const auto& fragmentSpreadTag3 = itrFragmentSpreadTag3->second; const auto fragmentSpread3 = service::StringArgument::require("fragmentSpread", fragmentSpreadTag3); - ASSERT_EQ(size_t { 1 }, params3.inlineFragmentDirectives.size()) + ASSERT_EQ(std::size_t { 1 }, params3.inlineFragmentDirectives.size()) << "missing inline fragment directive"; const auto itrInlineFragmentTag3 = params3.inlineFragmentDirectives.cbegin(); ASSERT_TRUE(itrInlineFragmentTag3->first == "inlineFragmentTag"sv); const auto& inlineFragmentTag3 = itrInlineFragmentTag3->second; const auto inlineFragment3 = service::StringArgument::require("inlineFragment", inlineFragmentTag3); - ASSERT_EQ(size_t { 1 }, params3.fieldDirectives.size()) << "missing field directive"; + ASSERT_EQ(std::size_t { 1 }, params3.fieldDirectives.size()) << "missing field directive"; const auto itrFieldTag3 = params3.fieldDirectives.cbegin(); ASSERT_TRUE(itrFieldTag3->first == "fieldTag"sv) << "missing field directive"; const auto& fieldTag3 = itrFieldTag3->second; const auto field3 = service::StringArgument::require("field", fieldTag3); - ASSERT_EQ(size_t { 1 }, params4.operationDirectives.size()) + ASSERT_EQ(std::size_t { 1 }, params4.operationDirectives.size()) << "missing operation directive"; const auto itrQueryTag4 = params4.operationDirectives.cbegin(); ASSERT_TRUE(itrQueryTag4->first == "queryTag"sv) << "missing required directive"; @@ -1047,19 +1061,20 @@ TEST_F(TodayServiceCase, NestedFragmentDirectives) const auto query4 = service::StringArgument::require("query", queryTag4); const auto fragmentDefinitionCount4 = params4.fragmentDefinitionDirectives.size(); const auto fragmentSpreadCount4 = params4.fragmentSpreadDirectives.size(); - ASSERT_EQ(size_t { 1 }, params4.inlineFragmentDirectives.size()) + ASSERT_EQ(std::size_t { 1 }, params4.inlineFragmentDirectives.size()) << "missing inline fragment directive"; const auto itrInlineFragmentTag4 = params4.inlineFragmentDirectives.cbegin(); ASSERT_TRUE(itrInlineFragmentTag4->first == "inlineFragmentTag"sv); const auto& inlineFragmentTag4 = itrInlineFragmentTag4->second; const auto inlineFragment4 = service::StringArgument::require("inlineFragment", inlineFragmentTag4); - ASSERT_EQ(size_t { 3 }, params4.fieldDirectives.size()) << "missing field directive"; + ASSERT_EQ(std::size_t { 3 }, params4.fieldDirectives.size()) << "missing field directive"; const auto itrRepeatable1 = params4.fieldDirectives.cbegin(); ASSERT_TRUE(itrRepeatable1->first == "repeatableOnField"sv) << "missing field directive"; EXPECT_TRUE(response::Type::Map == itrRepeatable1->second.type()) << "unexpected arguments type directive"; - EXPECT_EQ(size_t { 0 }, itrRepeatable1->second.size()) << "extra arguments on directive"; + EXPECT_EQ(std::size_t { 0 }, itrRepeatable1->second.size()) + << "extra arguments on directive"; const auto itrFieldTag4 = itrRepeatable1 + 1; ASSERT_TRUE(itrFieldTag4->first == "fieldTag"sv) << "missing field directive"; const auto& fieldTag4 = itrFieldTag4->second; @@ -1067,7 +1082,8 @@ TEST_F(TodayServiceCase, NestedFragmentDirectives) ASSERT_TRUE(itrRepeatable2->first == "repeatableOnField"sv) << "missing field directive"; EXPECT_TRUE(response::Type::Map == itrRepeatable2->second.type()) << "unexpected arguments type directive"; - EXPECT_EQ(size_t { 0 }, itrRepeatable2->second.size()) << "extra arguments on directive"; + EXPECT_EQ(std::size_t { 0 }, itrRepeatable2->second.size()) + << "extra arguments on directive"; const auto field4 = service::StringArgument::require("field", fieldTag4); ASSERT_EQ(1, depth1); @@ -1076,16 +1092,16 @@ TEST_F(TodayServiceCase, NestedFragmentDirectives) ASSERT_EQ(4, depth4); ASSERT_TRUE(capturedParams.empty()); ASSERT_EQ("nested", query1) << "remember the operation directives"; - ASSERT_EQ(size_t { 0 }, fragmentDefinitionCount1); - ASSERT_EQ(size_t { 0 }, fragmentSpreadCount1); - ASSERT_EQ(size_t { 0 }, inlineFragmentCount1); + ASSERT_EQ(std::size_t { 0 }, fragmentDefinitionCount1); + ASSERT_EQ(std::size_t { 0 }, fragmentSpreadCount1); + ASSERT_EQ(std::size_t { 0 }, inlineFragmentCount1); ASSERT_EQ("nested1", field1) << "remember the field directives"; ASSERT_EQ("nested", query2) << "remember the operation directives"; ASSERT_EQ("fragmentDefinition1", fragmentDefinition2) << "remember the directives from the fragment definition"; ASSERT_EQ("fragmentSpread1", fragmentSpread2) << "remember the directives from the fragment spread"; - ASSERT_EQ(size_t { 0 }, inlineFragmentCount2); + ASSERT_EQ(std::size_t { 0 }, inlineFragmentCount2); ASSERT_EQ("nested2", field2) << "remember the field directives"; ASSERT_EQ("nested", query3) << "remember the operation directives"; ASSERT_EQ("fragmentDefinition2", fragmentDefinition3) @@ -1096,10 +1112,10 @@ TEST_F(TodayServiceCase, NestedFragmentDirectives) << "remember the directives from the inline fragment"; ASSERT_EQ("nested3", field3) << "remember the field directives"; ASSERT_EQ("nested", query4) << "remember the operation directives"; - ASSERT_EQ(size_t { 0 }, fragmentDefinitionCount4) + ASSERT_EQ(std::size_t { 0 }, fragmentDefinitionCount4) << "traversing a field to a nested object SelectionSet resets the fragment " "directives"; - ASSERT_EQ(size_t { 0 }, fragmentSpreadCount4) + ASSERT_EQ(std::size_t { 0 }, fragmentSpreadCount4) << "traversing a field to a nested object SelectionSet resets the fragment " "directives"; ASSERT_EQ("inlineFragment5", inlineFragment4) @@ -1128,20 +1144,21 @@ TEST_F(TodayServiceCase, QueryAppointmentsById) auto state = std::make_shared(12); auto result = _mockService->service->resolve({ query, {}, std::move(variables), {}, state }).get(); - EXPECT_EQ(size_t { 1 }, _mockService->getAppointmentsCount) + EXPECT_EQ(std::size_t { 1 }, _mockService->getAppointmentsCount) << "today service lazy loads the appointments and caches the result"; - EXPECT_GE(size_t { 1 }, _mockService->getTasksCount) + EXPECT_GE(std::size_t { 1 }, _mockService->getTasksCount) << "today service lazy loads the tasks and caches the result"; - EXPECT_GE(size_t { 1 }, _mockService->getUnreadCountsCount) + EXPECT_GE(std::size_t { 1 }, _mockService->getUnreadCountsCount) << "today service lazy loads the unreadCounts and caches the result"; - EXPECT_EQ(size_t { 12 }, state->appointmentsRequestId) + EXPECT_EQ(std::size_t { 12 }, state->appointmentsRequestId) << "today service passed the same RequestState"; - EXPECT_EQ(size_t { 0 }, state->tasksRequestId) << "today service did not call the loader"; - EXPECT_EQ(size_t { 0 }, state->unreadCountsRequestId) + EXPECT_EQ(std::size_t { 0 }, state->tasksRequestId) << "today service did not call the loader"; + EXPECT_EQ(std::size_t { 0 }, state->unreadCountsRequestId) << "today service did not call the loader"; - EXPECT_EQ(size_t { 1 }, state->loadAppointmentsCount) << "today service called the loader once"; - EXPECT_EQ(size_t { 0 }, state->loadTasksCount) << "today service did not call the loader"; - EXPECT_EQ(size_t { 0 }, state->loadUnreadCountsCount) + EXPECT_EQ(std::size_t { 1 }, state->loadAppointmentsCount) + << "today service called the loader once"; + EXPECT_EQ(std::size_t { 0 }, state->loadTasksCount) << "today service did not call the loader"; + EXPECT_EQ(std::size_t { 0 }, state->loadUnreadCountsCount) << "today service did not call the loader"; try @@ -1156,7 +1173,7 @@ TEST_F(TodayServiceCase, QueryAppointmentsById) const auto appointmentsById = service::ScalarArgument::require("appointmentsById", data); - ASSERT_EQ(size_t { 1 }, appointmentsById.size()); + ASSERT_EQ(std::size_t { 1 }, appointmentsById.size()); const auto& appointmentEntry = appointmentsById.front(); EXPECT_EQ(today::getFakeAppointmentId(), service::IdArgument::require("appointmentId", appointmentEntry)) @@ -1186,7 +1203,7 @@ TEST_F(TodayServiceCase, UnimplementedFieldError) ASSERT_TRUE(result.type() == response::Type::Map); const auto& errors = result["errors"]; ASSERT_TRUE(errors.type() == response::Type::List); - ASSERT_EQ(size_t { 1 }, errors.size()); + ASSERT_EQ(std::size_t { 1 }, errors.size()); response::Value error { errors[0] }; ASSERT_TRUE(error.type() == response::Type::Map); ASSERT_EQ( @@ -1219,7 +1236,7 @@ TEST_F(TodayServiceCase, SubscribeNodeChangeMatchingId) const std::shared_ptr& state, response::IdType&& idArg) -> std::shared_ptr { EXPECT_EQ(expectedContext, resolverContext); - EXPECT_EQ(size_t { 13 }, + EXPECT_EQ(std::size_t { 13 }, std::static_pointer_cast(state)->requestId) << "should pass the RequestState to the subscription resolvers"; EXPECT_EQ(today::getFakeTaskId(), idArg); @@ -1366,7 +1383,7 @@ TEST_F(TodayServiceCase, SubscribeNodeChangeFuzzyComparator) const response::IdType fuzzyId { 'f', 'a', 'k' }; EXPECT_EQ(expectedContext, resolverContext); - EXPECT_EQ(size_t { 14 }, + EXPECT_EQ(std::size_t { 14 }, std::static_pointer_cast(state)->requestId) << "should pass the RequestState to the subscription resolvers"; EXPECT_EQ(fuzzyId, idArg); @@ -1514,7 +1531,7 @@ TEST_F(TodayServiceCase, SubscribeNodeChangeMatchingVariable) const std::shared_ptr& state, response::IdType&& idArg) -> std::shared_ptr { EXPECT_EQ(expectedContext, resolverContext); - EXPECT_EQ(size_t { 14 }, + EXPECT_EQ(std::size_t { 14 }, std::static_pointer_cast(state)->requestId) << "should pass the RequestState to the subscription resolvers"; EXPECT_EQ(today::getFakeTaskId(), idArg); @@ -1587,20 +1604,21 @@ TEST_F(TodayServiceCase, DeferredQueryAppointmentsById) auto state = std::make_shared(15); auto result = _mockService->service->resolve({ query, {}, std::move(variables), {}, state }).get(); - EXPECT_EQ(size_t { 1 }, _mockService->getAppointmentsCount) + EXPECT_EQ(std::size_t { 1 }, _mockService->getAppointmentsCount) << "today service lazy loads the appointments and caches the result"; - EXPECT_GE(size_t { 1 }, _mockService->getTasksCount) + EXPECT_GE(std::size_t { 1 }, _mockService->getTasksCount) << "today service lazy loads the tasks and caches the result"; - EXPECT_GE(size_t { 1 }, _mockService->getUnreadCountsCount) + EXPECT_GE(std::size_t { 1 }, _mockService->getUnreadCountsCount) << "today service lazy loads the unreadCounts and caches the result"; - EXPECT_EQ(size_t { 15 }, state->appointmentsRequestId) + EXPECT_EQ(std::size_t { 15 }, state->appointmentsRequestId) << "today service passed the same RequestState"; - EXPECT_EQ(size_t { 0 }, state->tasksRequestId) << "today service did not call the loader"; - EXPECT_EQ(size_t { 0 }, state->unreadCountsRequestId) + EXPECT_EQ(std::size_t { 0 }, state->tasksRequestId) << "today service did not call the loader"; + EXPECT_EQ(std::size_t { 0 }, state->unreadCountsRequestId) << "today service did not call the loader"; - EXPECT_EQ(size_t { 1 }, state->loadAppointmentsCount) << "today service called the loader once"; - EXPECT_EQ(size_t { 0 }, state->loadTasksCount) << "today service did not call the loader"; - EXPECT_EQ(size_t { 0 }, state->loadUnreadCountsCount) + EXPECT_EQ(std::size_t { 1 }, state->loadAppointmentsCount) + << "today service called the loader once"; + EXPECT_EQ(std::size_t { 0 }, state->loadTasksCount) << "today service did not call the loader"; + EXPECT_EQ(std::size_t { 0 }, state->loadUnreadCountsCount) << "today service did not call the loader"; try @@ -1615,7 +1633,7 @@ TEST_F(TodayServiceCase, DeferredQueryAppointmentsById) const auto appointmentsById = service::ScalarArgument::require("appointmentsById", data); - ASSERT_EQ(size_t { 1 }, appointmentsById.size()); + ASSERT_EQ(std::size_t { 1 }, appointmentsById.size()); const auto& appointmentEntry = appointmentsById.front(); EXPECT_EQ(today::getFakeAppointmentId(), service::IdArgument::require("appointmentId", appointmentEntry)) @@ -1650,20 +1668,21 @@ TEST_F(TodayServiceCase, NonBlockingQueryAppointmentsById) auto result = _mockService->service ->resolve({ query, {}, std::move(variables), std::launch::async, state }) .get(); - EXPECT_EQ(size_t { 1 }, _mockService->getAppointmentsCount) + EXPECT_EQ(std::size_t { 1 }, _mockService->getAppointmentsCount) << "today service lazy loads the appointments and caches the result"; - EXPECT_GE(size_t { 1 }, _mockService->getTasksCount) + EXPECT_GE(std::size_t { 1 }, _mockService->getTasksCount) << "today service lazy loads the tasks and caches the result"; - EXPECT_GE(size_t { 1 }, _mockService->getUnreadCountsCount) + EXPECT_GE(std::size_t { 1 }, _mockService->getUnreadCountsCount) << "today service lazy loads the unreadCounts and caches the result"; - EXPECT_EQ(size_t { 16 }, state->appointmentsRequestId) + EXPECT_EQ(std::size_t { 16 }, state->appointmentsRequestId) << "today service passed the same RequestState"; - EXPECT_EQ(size_t { 0 }, state->tasksRequestId) << "today service did not call the loader"; - EXPECT_EQ(size_t { 0 }, state->unreadCountsRequestId) + EXPECT_EQ(std::size_t { 0 }, state->tasksRequestId) << "today service did not call the loader"; + EXPECT_EQ(std::size_t { 0 }, state->unreadCountsRequestId) << "today service did not call the loader"; - EXPECT_EQ(size_t { 1 }, state->loadAppointmentsCount) << "today service called the loader once"; - EXPECT_EQ(size_t { 0 }, state->loadTasksCount) << "today service did not call the loader"; - EXPECT_EQ(size_t { 0 }, state->loadUnreadCountsCount) + EXPECT_EQ(std::size_t { 1 }, state->loadAppointmentsCount) + << "today service called the loader once"; + EXPECT_EQ(std::size_t { 0 }, state->loadTasksCount) << "today service did not call the loader"; + EXPECT_EQ(std::size_t { 0 }, state->loadUnreadCountsCount) << "today service did not call the loader"; try @@ -1678,7 +1697,7 @@ TEST_F(TodayServiceCase, NonBlockingQueryAppointmentsById) const auto appointmentsById = service::ScalarArgument::require("appointmentsById", data); - ASSERT_EQ(size_t { 1 }, appointmentsById.size()); + ASSERT_EQ(std::size_t { 1 }, appointmentsById.size()); const auto& appointmentEntry = appointmentsById.front(); EXPECT_EQ(today::getFakeAppointmentId(), service::IdArgument::require("appointmentId", appointmentEntry)) @@ -1868,20 +1887,21 @@ TEST_F(TodayServiceCase, QueryAppointmentsThroughUnionTypeFragment) auto state = std::make_shared(20); auto result = _mockService->service->resolve({ query, {}, std::move(variables), {}, state }).get(); - EXPECT_EQ(size_t { 1 }, _mockService->getAppointmentsCount) + EXPECT_EQ(std::size_t { 1 }, _mockService->getAppointmentsCount) << "today service lazy loads the appointments and caches the result"; - EXPECT_GE(size_t { 1 }, _mockService->getTasksCount) + EXPECT_GE(std::size_t { 1 }, _mockService->getTasksCount) << "today service lazy loads the tasks and caches the result"; - EXPECT_GE(size_t { 1 }, _mockService->getUnreadCountsCount) + EXPECT_GE(std::size_t { 1 }, _mockService->getUnreadCountsCount) << "today service lazy loads the unreadCounts and caches the result"; - EXPECT_EQ(size_t { 20 }, state->appointmentsRequestId) + EXPECT_EQ(std::size_t { 20 }, state->appointmentsRequestId) << "today service passed the same RequestState"; - EXPECT_EQ(size_t { 0 }, state->tasksRequestId) << "today service did not call the loader"; - EXPECT_EQ(size_t { 0 }, state->unreadCountsRequestId) + EXPECT_EQ(std::size_t { 0 }, state->tasksRequestId) << "today service did not call the loader"; + EXPECT_EQ(std::size_t { 0 }, state->unreadCountsRequestId) << "today service did not call the loader"; - EXPECT_EQ(size_t { 1 }, state->loadAppointmentsCount) << "today service called the loader once"; - EXPECT_EQ(size_t { 0 }, state->loadTasksCount) << "today service did not call the loader"; - EXPECT_EQ(size_t { 0 }, state->loadUnreadCountsCount) + EXPECT_EQ(std::size_t { 1 }, state->loadAppointmentsCount) + << "today service called the loader once"; + EXPECT_EQ(std::size_t { 0 }, state->loadTasksCount) << "today service did not call the loader"; + EXPECT_EQ(std::size_t { 0 }, state->loadUnreadCountsCount) << "today service did not call the loader"; try @@ -1897,7 +1917,7 @@ TEST_F(TodayServiceCase, QueryAppointmentsThroughUnionTypeFragment) const auto appointments = service::ScalarArgument::require("appointments", data); const auto appointmentEdges = service::ScalarArgument::require("edges", appointments); - ASSERT_EQ(size_t { 1 }, appointmentEdges.size()) << "appointments should have 1 entry"; + ASSERT_EQ(std::size_t { 1 }, appointmentEdges.size()) << "appointments should have 1 entry"; ASSERT_TRUE(appointmentEdges[0].type() == response::Type::Map) << "appointment should be an object"; const auto appointmentNode = service::ScalarArgument::require("node", appointmentEdges[0]); diff --git a/test/ValidationTests.cpp b/test/ValidationTests.cpp index 06af8b05..de1cb550 100644 --- a/test/ValidationTests.cpp +++ b/test/ValidationTests.cpp @@ -8,6 +8,7 @@ #include "graphqlservice/JSONResponse.h" #include +#include using namespace graphql; @@ -50,7 +51,7 @@ TEST_F(ValidationExamplesCase, CounterExample102) auto errors = service::buildErrorValues(_service->validate(query)).release(); - ASSERT_EQ(errors.size(), size_t { 2 }); + ASSERT_EQ(errors.size(), std::size_t { 2 }); EXPECT_EQ( R"js({"message":"Undefined field type: Dog name: color","locations":[{"line":4,"column":5}]})js", response::toJSON(std::move(errors[0]))) @@ -102,7 +103,7 @@ TEST_F(ValidationExamplesCase, CounterExample104) auto errors = service::buildErrorValues(_service->validate(query)).release(); - ASSERT_EQ(errors.size(), size_t { 1 }); + ASSERT_EQ(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Duplicate operation name: getName","locations":[{"line":7,"column":3}]})js", response::toJSON(std::move(errors[0]))) @@ -127,7 +128,7 @@ TEST_F(ValidationExamplesCase, CounterExample105) auto errors = service::buildErrorValues(_service->validate(query)).release(); - ASSERT_EQ(errors.size(), size_t { 1 }); + ASSERT_EQ(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Duplicate operation name: dogOperation","locations":[{"line":7,"column":3}]})js", response::toJSON(std::move(errors[0]))) @@ -168,7 +169,7 @@ TEST_F(ValidationExamplesCase, CounterExample107) auto errors = service::buildErrorValues(_service->validate(query)).release(); - ASSERT_EQ(errors.size(), size_t { 1 }); + ASSERT_EQ(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Anonymous operation not alone","locations":[{"line":1,"column":1}]})js", response::toJSON(std::move(errors[0]))) @@ -223,7 +224,7 @@ TEST_F(ValidationExamplesCase, CounterExample110) auto errors = service::buildErrorValues(_service->validate(query)).release(); - ASSERT_EQ(errors.size(), size_t { 1 }); + ASSERT_EQ(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Subscription with more than one root field name: sub","locations":[{"line":1,"column":1}]})js", response::toJSON(std::move(errors[0]))) @@ -248,7 +249,7 @@ TEST_F(ValidationExamplesCase, CounterExample111) auto errors = service::buildErrorValues(_service->validate(query)).release(); - ASSERT_EQ(errors.size(), size_t { 1 }); + ASSERT_EQ(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Subscription with more than one root field name: sub","locations":[{"line":1,"column":1}]})js", response::toJSON(std::move(errors[0]))) @@ -265,7 +266,7 @@ TEST_F(ValidationExamplesCase, CounterExample112) auto errors = service::buildErrorValues(_service->validate(query)).release(); - ASSERT_EQ(errors.size(), size_t { 1 }); + ASSERT_EQ(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Subscription with Introspection root field name: sub","locations":[{"line":1,"column":1}]})js", response::toJSON(std::move(errors[0]))) @@ -286,8 +287,8 @@ TEST_F(ValidationExamplesCase, CounterExample113) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 4 }) << "2 undefined fields + 2 unused fragments"; - ASSERT_GE(errors.size(), size_t { 2 }); + EXPECT_EQ(errors.size(), std::size_t { 4 }) << "2 undefined fields + 2 unused fragments"; + ASSERT_GE(errors.size(), std::size_t { 2 }); EXPECT_EQ( R"js({"message":"Undefined field type: Dog name: meowVolume","locations":[{"line":2,"column":4}]})js", response::toJSON(std::move(errors[0]))) @@ -326,8 +327,8 @@ TEST_F(ValidationExamplesCase, CounterExample115) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 2 }) << "1 undefined field + 1 unused fragment"; - ASSERT_GE(errors.size(), size_t { 1 }); + EXPECT_EQ(errors.size(), std::size_t { 2 }) << "1 undefined field + 1 unused fragment"; + ASSERT_GE(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Undefined field type: Pet name: nickname","locations":[{"line":2,"column":4}]})js", response::toJSON(std::move(errors[0]))) @@ -369,8 +370,8 @@ TEST_F(ValidationExamplesCase, CounterExample117) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 3 }) << "2 undefined fields + 1 unused fragment"; - ASSERT_GE(errors.size(), size_t { 2 }); + EXPECT_EQ(errors.size(), std::size_t { 3 }) << "2 undefined fields + 1 unused fragment"; + ASSERT_GE(errors.size(), std::size_t { 2 }); EXPECT_EQ( R"js({"message":"Field on union type: CatOrDog name: name","locations":[{"line":2,"column":4}]})js", response::toJSON(std::move(errors[0]))) @@ -417,8 +418,8 @@ TEST_F(ValidationExamplesCase, CounterExample119) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 2 }) << "1 conflicting field + 1 unused fragment"; - ASSERT_GE(errors.size(), size_t { 1 }); + EXPECT_EQ(errors.size(), std::size_t { 2 }) << "1 conflicting field + 1 unused fragment"; + ASSERT_GE(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Conflicting field type: Dog name: name","locations":[{"line":3,"column":4}]})js", response::toJSON(std::move(errors[0]))) @@ -481,9 +482,9 @@ TEST_F(ValidationExamplesCase, CounterExample121) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 9 }) + EXPECT_EQ(errors.size(), std::size_t { 9 }) << "4 conflicting fields + 1 missing argument + 4 unused fragments"; - ASSERT_GE(errors.size(), size_t { 4 }); + ASSERT_GE(errors.size(), std::size_t { 4 }); EXPECT_EQ( R"js({"message":"Conflicting field type: Dog name: doesKnowCommand","locations":[{"line":3,"column":4}]})js", response::toJSON(std::move(errors[0]))) @@ -550,8 +551,8 @@ TEST_F(ValidationExamplesCase, CounterExample123) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 2 }) << "1 conflicting field + 1 unused fragment"; - ASSERT_GE(errors.size(), size_t { 1 }); + EXPECT_EQ(errors.size(), std::size_t { 2 }) << "1 conflicting field + 1 unused fragment"; + ASSERT_GE(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Conflicting field type: Cat name: meowVolume","locations":[{"line":6,"column":5}]})js", response::toJSON(std::move(errors[0]))) @@ -588,8 +589,8 @@ TEST_F(ValidationExamplesCase, CounterExample125) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 2 }) << "1 invalid field + 1 unused fragment"; - ASSERT_GE(errors.size(), size_t { 1 }); + EXPECT_EQ(errors.size(), std::size_t { 2 }) << "1 invalid field + 1 unused fragment"; + ASSERT_GE(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Field on scalar type: Int name: sinceWhen","locations":[{"line":3,"column":5}]})js", response::toJSON(std::move(errors[0]))) @@ -639,7 +640,7 @@ TEST_F(ValidationExamplesCase, CounterExample127) auto errors = service::buildErrorValues(_service->validate(query)).release(); - ASSERT_EQ(errors.size(), size_t { 3 }) << "3 invalid fields"; + ASSERT_EQ(errors.size(), std::size_t { 3 }) << "3 invalid fields"; EXPECT_EQ( R"js({"message":"Missing fields on non-scalar type: Human","locations":[{"line":2,"column":4}]})js", response::toJSON(std::move(errors[0]))) @@ -687,9 +688,9 @@ TEST_F(ValidationExamplesCase, CounterExample129) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 3 }) + EXPECT_EQ(errors.size(), std::size_t { 3 }) << "1 undefined argument + 1 missing argument + 1 unused fragment"; - ASSERT_GE(errors.size(), size_t { 1 }); + ASSERT_GE(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Undefined argument type: Dog field: doesKnowCommand name: command","locations":[{"line":2,"column":20}]})js", response::toJSON(std::move(errors[0]))) @@ -706,9 +707,9 @@ TEST_F(ValidationExamplesCase, CounterExample130) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 3 }) + EXPECT_EQ(errors.size(), std::size_t { 3 }) << "1 undefined argument + 1 missing argument + 1 unused fragment"; - ASSERT_GE(errors.size(), size_t { 1 }); + ASSERT_GE(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Undefined argument directive: include name: unless","locations":[{"line":2,"column":48}]})js", response::toJSON(std::move(errors[0]))) @@ -808,8 +809,8 @@ TEST_F(ValidationExamplesCase, CounterExample135) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 2 }) << "1 missing argument + 1 unused fragment"; - ASSERT_GE(errors.size(), size_t { 1 }); + EXPECT_EQ(errors.size(), std::size_t { 2 }) << "1 missing argument + 1 unused fragment"; + ASSERT_GE(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Missing argument type: Arguments field: nonNullBooleanArgField name: nonNullBooleanArg","locations":[{"line":2,"column":4}]})js", response::toJSON(std::move(errors[0]))) @@ -826,8 +827,8 @@ TEST_F(ValidationExamplesCase, CounterExample136) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 2 }) << "1 missing argument + 1 unused fragment"; - ASSERT_GE(errors.size(), size_t { 1 }); + EXPECT_EQ(errors.size(), std::size_t { 2 }) << "1 missing argument + 1 unused fragment"; + ASSERT_GE(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Required non-null argument type: Arguments field: nonNullBooleanArgField name: nonNullBooleanArg","locations":[{"line":2,"column":4}]})js", response::toJSON(std::move(errors[0]))) @@ -881,8 +882,8 @@ TEST_F(ValidationExamplesCase, CounterExample138) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 1 }) << "1 duplicate fragment"; - ASSERT_GE(errors.size(), size_t { 1 }); + EXPECT_EQ(errors.size(), std::size_t { 1 }) << "1 duplicate fragment"; + ASSERT_GE(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Duplicate fragment name: fragmentOne","locations":[{"line":11,"column":3}]})js", response::toJSON(std::move(errors[0]))) @@ -937,8 +938,8 @@ TEST_F(ValidationExamplesCase, CounterExample140) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 4 }) << "2 not existing types + 2 unused fragments"; - ASSERT_GE(errors.size(), size_t { 2 }); + EXPECT_EQ(errors.size(), std::size_t { 4 }) << "2 not existing types + 2 unused fragments"; + ASSERT_GE(errors.size(), std::size_t { 2 }); EXPECT_EQ( R"js({"message":"Undefined target type on fragment definition: notOnExistingType name: NotInSchema","locations":[{"line":1,"column":28}]})js", response::toJSON(std::move(errors[0]))) @@ -995,8 +996,8 @@ TEST_F(ValidationExamplesCase, CounterExample142) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 4 }) << "2 not existing types + 2 unused fragments"; - ASSERT_GE(errors.size(), size_t { 2 }); + EXPECT_EQ(errors.size(), std::size_t { 4 }) << "2 not existing types + 2 unused fragments"; + ASSERT_GE(errors.size(), std::size_t { 2 }); EXPECT_EQ( R"js({"message":"Scalar target type on fragment definition: fragOnScalar name: Int","locations":[{"line":1,"column":23}]})js", response::toJSON(std::move(errors[0]))) @@ -1023,8 +1024,8 @@ TEST_F(ValidationExamplesCase, CounterExample143) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 1 }) << "1 unused fragment"; - ASSERT_GE(errors.size(), size_t { 1 }); + EXPECT_EQ(errors.size(), std::size_t { 1 }) << "1 unused fragment"; + ASSERT_GE(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Unused fragment definition name: nameFragment","locations":[{"line":1,"column":1}]})js", response::toJSON(std::move(errors[0]))) @@ -1043,8 +1044,8 @@ TEST_F(ValidationExamplesCase, CounterExample144) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 2 }) << "1 undefined fragment + 1 missing field"; - ASSERT_GE(errors.size(), size_t { 1 }); + EXPECT_EQ(errors.size(), std::size_t { 2 }) << "1 undefined fragment + 1 missing field"; + ASSERT_GE(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Undefined fragment spread name: undefinedFragment","locations":[{"line":3,"column":8}]})js", response::toJSON(std::move(errors[0]))) @@ -1073,8 +1074,8 @@ TEST_F(ValidationExamplesCase, CounterExample145) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 2 }) << "2 cyclic fragments"; - ASSERT_GE(errors.size(), size_t { 2 }); + EXPECT_EQ(errors.size(), std::size_t { 2 }) << "2 cyclic fragments"; + ASSERT_GE(errors.size(), std::size_t { 2 }); EXPECT_EQ( R"js({"message":"Cyclic fragment spread name: nameFragment","locations":[{"line":14,"column":7}]})js", response::toJSON(std::move(errors[0]))) @@ -1134,8 +1135,8 @@ TEST_F(ValidationExamplesCase, CounterExample147) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 2 }) << "2 cyclic fragments"; - ASSERT_GE(errors.size(), size_t { 2 }); + EXPECT_EQ(errors.size(), std::size_t { 2 }) << "2 cyclic fragments"; + ASSERT_GE(errors.size(), std::size_t { 2 }); EXPECT_EQ( R"js({"message":"Cyclic fragment spread name: dogFragment","locations":[{"line":19,"column":8}]})js", response::toJSON(std::move(errors[0]))) @@ -1178,8 +1179,8 @@ TEST_F(ValidationExamplesCase, CounterExample149) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 2 }) << "1 incompatible type + 1 unused fragment"; - ASSERT_GE(errors.size(), size_t { 1 }); + EXPECT_EQ(errors.size(), std::size_t { 2 }) << "1 incompatible type + 1 unused fragment"; + ASSERT_GE(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Incompatible target type on inline fragment name: Cat","locations":[{"line":2,"column":8}]})js", response::toJSON(std::move(errors[0]))) @@ -1278,8 +1279,8 @@ TEST_F(ValidationExamplesCase, CounterExample153) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 4 }) << "2 incompatible type + 2 unused fragments"; - ASSERT_GE(errors.size(), size_t { 2 }); + EXPECT_EQ(errors.size(), std::size_t { 4 }) << "2 incompatible type + 2 unused fragments"; + ASSERT_GE(errors.size(), std::size_t { 2 }); EXPECT_EQ( R"js({"message":"Incompatible target type on inline fragment name: Dog","locations":[{"line":2,"column":8}]})js", response::toJSON(std::move(errors[0]))) @@ -1328,8 +1329,8 @@ TEST_F(ValidationExamplesCase, CounterExample155) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 3 }) << "1 incompatible type + 2 unused fragments"; - ASSERT_GE(errors.size(), size_t { 1 }); + EXPECT_EQ(errors.size(), std::size_t { 3 }) << "1 incompatible type + 2 unused fragments"; + ASSERT_GE(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Incompatible fragment spread target type: Sentient name: sentientFragment","locations":[{"line":2,"column":7}]})js", response::toJSON(std::move(errors[0]))) @@ -1402,9 +1403,9 @@ TEST_F(ValidationExamplesCase, CounterExample158) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 5 }) + EXPECT_EQ(errors.size(), std::size_t { 5 }) << "2 expected values + 2 incompatible arguments + 1 unused fragment"; - ASSERT_GE(errors.size(), size_t { 4 }); + ASSERT_GE(errors.size(), std::size_t { 4 }); EXPECT_EQ(R"js({"message":"Expected Int value","locations":[{"line":2,"column":24}]})js", response::toJSON(std::move(errors[0]))) << "error should match"; @@ -1447,8 +1448,8 @@ TEST_F(ValidationExamplesCase, CounterExample160) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 2 }) << "1 undefined field + 1 incompatible argument"; - ASSERT_GE(errors.size(), size_t { 2 }); + EXPECT_EQ(errors.size(), std::size_t { 2 }) << "1 undefined field + 1 incompatible argument"; + ASSERT_GE(errors.size(), std::size_t { 2 }); EXPECT_EQ( R"js({"message":"Undefined Input Object field type: ComplexInput name: favoriteCookieFlavor","locations":[{"line":2,"column":45}]})js", response::toJSON(std::move(errors[0]))) @@ -1471,8 +1472,8 @@ TEST_F(ValidationExamplesCase, CounterExample161) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 1 }) << "1 conflicting field"; - ASSERT_GE(errors.size(), size_t { 1 }); + EXPECT_EQ(errors.size(), std::size_t { 1 }) << "1 conflicting field"; + ASSERT_GE(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Conflicting input field name: name","locations":[{"line":2,"column":37}]})js", response::toJSON(std::move(errors[0]))) @@ -1491,8 +1492,8 @@ TEST_F(ValidationExamplesCase, CounterExample162) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 1 }) << "1 unexpected location"; - ASSERT_GE(errors.size(), size_t { 1 }); + EXPECT_EQ(errors.size(), std::size_t { 1 }) << "1 unexpected location"; + ASSERT_GE(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Unexpected location for directive: skip name: QUERY","locations":[{"line":1,"column":7}]})js", response::toJSON(std::move(errors[0]))) @@ -1511,8 +1512,8 @@ TEST_F(ValidationExamplesCase, CounterExample163) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 2 }) << "1 conflicting directive + 1 unused variable"; - ASSERT_GE(errors.size(), size_t { 1 }); + EXPECT_EQ(errors.size(), std::size_t { 2 }) << "1 conflicting directive + 1 unused variable"; + ASSERT_GE(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Conflicting directive name: skip","locations":[{"line":2,"column":24}]})js", response::toJSON(std::move(errors[0]))) @@ -1548,8 +1549,8 @@ TEST_F(ValidationExamplesCase, CounterExample165) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 1 }) << "1 conflicting variable"; - ASSERT_GE(errors.size(), size_t { 1 }); + EXPECT_EQ(errors.size(), std::size_t { 1 }) << "1 conflicting variable"; + ASSERT_GE(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Conflicting variable operation: houseTrainedQuery name: atOtherHomes","locations":[{"line":1,"column":49}]})js", response::toJSON(std::move(errors[0]))) @@ -1646,8 +1647,8 @@ TEST_F(ValidationExamplesCase, CounterExample169) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 4 }) << "4 invalid variable types"; - ASSERT_GE(errors.size(), size_t { 4 }); + EXPECT_EQ(errors.size(), std::size_t { 4 }) << "4 invalid variable types"; + ASSERT_GE(errors.size(), std::size_t { 4 }); EXPECT_EQ( R"js({"message":"Invalid variable type operation: takesCat name: cat","locations":[{"line":1,"column":22}]})js", response::toJSON(std::move(errors[0]))) @@ -1692,8 +1693,8 @@ TEST_F(ValidationExamplesCase, CounterExample171) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 2 }) << "1 undefined variable + 1 incompatible argument"; - ASSERT_GE(errors.size(), size_t { 1 }); + EXPECT_EQ(errors.size(), std::size_t { 2 }) << "1 undefined variable + 1 incompatible argument"; + ASSERT_GE(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Undefined variable name: atOtherHomes","locations":[{"line":3,"column":34}]})js", response::toJSON(std::move(errors[0]))) @@ -1734,8 +1735,8 @@ TEST_F(ValidationExamplesCase, CounterExample173) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 2 }) << "1 undefined variable + 1 incompatible argument"; - ASSERT_GE(errors.size(), size_t { 1 }); + EXPECT_EQ(errors.size(), std::size_t { 2 }) << "1 undefined variable + 1 incompatible argument"; + ASSERT_GE(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Undefined variable name: atOtherHomes","locations":[{"line":8,"column":33}]})js", response::toJSON(std::move(errors[0]))) @@ -1762,8 +1763,8 @@ TEST_F(ValidationExamplesCase, CounterExample174) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 2 }) << "1 undefined variable + 1 incompatible argument"; - ASSERT_GE(errors.size(), size_t { 1 }); + EXPECT_EQ(errors.size(), std::size_t { 2 }) << "1 undefined variable + 1 incompatible argument"; + ASSERT_GE(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Undefined variable name: atOtherHomes","locations":[{"line":12,"column":33}]})js", response::toJSON(std::move(errors[0]))) @@ -1816,8 +1817,8 @@ TEST_F(ValidationExamplesCase, CounterExample176) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 2 }) << "1 undefined variable + 1 incompatible argument"; - ASSERT_GE(errors.size(), size_t { 1 }); + EXPECT_EQ(errors.size(), std::size_t { 2 }) << "1 undefined variable + 1 incompatible argument"; + ASSERT_GE(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Undefined variable name: atOtherHomes","locations":[{"line":14,"column":33}]})js", response::toJSON(std::move(errors[0]))) @@ -1836,8 +1837,8 @@ TEST_F(ValidationExamplesCase, CounterExample177) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 1 }) << "1 unused variable"; - ASSERT_GE(errors.size(), size_t { 1 }); + EXPECT_EQ(errors.size(), std::size_t { 1 }) << "1 unused variable"; + ASSERT_GE(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Unused variable name: atOtherHomes","locations":[{"line":1,"column":22}]})js", response::toJSON(std::move(errors[0]))) @@ -1878,8 +1879,8 @@ TEST_F(ValidationExamplesCase, CounterExample179) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 1 }) << "1 unused variable"; - ASSERT_GE(errors.size(), size_t { 1 }); + EXPECT_EQ(errors.size(), std::size_t { 1 }) << "1 unused variable"; + ASSERT_GE(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Unused variable name: atOtherHomes","locations":[{"line":1,"column":37}]})js", response::toJSON(std::move(errors[0]))) @@ -1908,8 +1909,8 @@ TEST_F(ValidationExamplesCase, CounterExample180) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 1 }) << "1 unused variable"; - ASSERT_GE(errors.size(), size_t { 1 }); + EXPECT_EQ(errors.size(), std::size_t { 1 }) << "1 unused variable"; + ASSERT_GE(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Unused variable name: extra","locations":[{"line":7,"column":51}]})js", response::toJSON(std::move(errors[0]))) @@ -1928,8 +1929,9 @@ TEST_F(ValidationExamplesCase, CounterExample181) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 2 }) << "1 incompatible variable + 1 incompatible argument"; - ASSERT_GE(errors.size(), size_t { 1 }); + EXPECT_EQ(errors.size(), std::size_t { 2 }) + << "1 incompatible variable + 1 incompatible argument"; + ASSERT_GE(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Incompatible variable type: Int name: Boolean","locations":[{"line":3,"column":33}]})js", response::toJSON(std::move(errors[0]))) @@ -1948,8 +1950,9 @@ TEST_F(ValidationExamplesCase, CounterExample182) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 2 }) << "1 incompatible variable + 1 incompatible argument"; - ASSERT_GE(errors.size(), size_t { 1 }); + EXPECT_EQ(errors.size(), std::size_t { 2 }) + << "1 incompatible variable + 1 incompatible argument"; + ASSERT_GE(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Expected Scalar variable type","locations":[{"line":3,"column":33}]})js", response::toJSON(std::move(errors[0]))) @@ -1968,8 +1971,9 @@ TEST_F(ValidationExamplesCase, CounterExample183) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 2 }) << "1 incompatible variable + 1 incompatible argument"; - ASSERT_GE(errors.size(), size_t { 1 }); + EXPECT_EQ(errors.size(), std::size_t { 2 }) + << "1 incompatible variable + 1 incompatible argument"; + ASSERT_GE(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Expected Non-Null variable type","locations":[{"line":3,"column":47}]})js", response::toJSON(std::move(errors[0]))) @@ -2002,8 +2006,9 @@ TEST_F(ValidationExamplesCase, CounterExample185) auto errors = service::buildErrorValues(_service->validate(query)).release(); - EXPECT_EQ(errors.size(), size_t { 2 }) << "1 incompatible variable + 1 incompatible argument"; - ASSERT_GE(errors.size(), size_t { 1 }); + EXPECT_EQ(errors.size(), std::size_t { 2 }) + << "1 incompatible variable + 1 incompatible argument"; + ASSERT_GE(errors.size(), std::size_t { 1 }); EXPECT_EQ( R"js({"message":"Expected Non-Null variable type","locations":[{"line":3,"column":52}]})js", response::toJSON(std::move(errors[0]))) diff --git a/vcpkg b/vcpkg new file mode 160000 index 00000000..7aeffc91 --- /dev/null +++ b/vcpkg @@ -0,0 +1 @@ +Subproject commit 7aeffc91033ad35cc4e2c152f213a866ec6c11ac diff --git a/vcpkg.json b/vcpkg.json index 7c62b562..5ed83019 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -20,8 +20,14 @@ "gtest" ] }, + "taocpp-json": { + "description": "Build the graphqljson library with taocpp-json.", + "dependencies": [ + "taocpp-json" + ] + }, "rapidjson": { - "description": "Build the graphqljson library with RapidJSON.", + "description": "Build the graphqljson library with rapidjson.", "dependencies": [ "rapidjson" ]