From 449e3ad2828f44da8fcecf4d369b254a2002ec15 Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Sun, 6 Oct 2024 10:09:23 -0400 Subject: [PATCH 01/10] Make authorized gateway information fields properly non-nullable --- .../GraphQL/Types/GatewayInformation.cs | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/Tgstation.Server.Host/GraphQL/Types/GatewayInformation.cs b/src/Tgstation.Server.Host/GraphQL/Types/GatewayInformation.cs index aefc2562bb..a4fc74bd1d 100644 --- a/src/Tgstation.Server.Host/GraphQL/Types/GatewayInformation.cs +++ b/src/Tgstation.Server.Host/GraphQL/Types/GatewayInformation.cs @@ -1,5 +1,6 @@ -using System; +using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using HotChocolate; @@ -28,7 +29,7 @@ public sealed class GatewayInformation /// The containing the . /// A specifying the minimumn valid password length for TGS users. [TgsGraphQLAuthorize(AdministrationRights.WriteUsers | AdministrationRights.EditOwnPassword)] - public uint? MinimumPasswordLength( + public uint MinimumPasswordLength( [Service] IOptionsSnapshot generalConfigurationOptions) { ArgumentNullException.ThrowIfNull(generalConfigurationOptions); @@ -41,7 +42,7 @@ public sealed class GatewayInformation /// The containing the . /// A specifying the maximum allowed attached instances for the . [TgsGraphQLAuthorize(InstanceManagerRights.Create)] - public uint? InstanceLimit( + public uint InstanceLimit( [Service] IOptionsSnapshot generalConfigurationOptions) { ArgumentNullException.ThrowIfNull(generalConfigurationOptions); @@ -55,7 +56,7 @@ public sealed class GatewayInformation /// A specifying the maximum allowed registered users for the . /// This limit only applies to user creation attempts made via the current . [TgsGraphQLAuthorize(AdministrationRights.WriteUsers)] - public uint? UserLimit( + public uint UserLimit( [Service] IOptionsSnapshot generalConfigurationOptions) { ArgumentNullException.ThrowIfNull(generalConfigurationOptions); @@ -69,7 +70,7 @@ public sealed class GatewayInformation /// A specifying the maximum allowed registered s for the . /// This limit only applies to creation attempts made via the current . [TgsGraphQLAuthorize(AdministrationRights.WriteUsers)] - public uint? UserGroupLimit( + public uint UserGroupLimit( [Service] IOptionsSnapshot generalConfigurationOptions) { ArgumentNullException.ThrowIfNull(generalConfigurationOptions); @@ -95,7 +96,7 @@ public sealed class GatewayInformation /// The to use. /// if the runs on a Windows operating system, otherwise. [TgsGraphQLAuthorize] - public bool? WindowsHost( + public bool WindowsHost( [Service] IPlatformIdentifier platformIdentifier) { ArgumentNullException.ThrowIfNull(platformIdentifier); @@ -106,7 +107,7 @@ public sealed class GatewayInformation /// Gets the swarm protocol . /// [TgsGraphQLAuthorize] - public Version? SwarmProtocolVersion => global::System.Version.Parse(MasterVersionsAttribute.Instance.RawSwarmProtocolVersion); + public Version SwarmProtocolVersion => global::System.Version.Parse(MasterVersionsAttribute.Instance.RawSwarmProtocolVersion); /// /// Gets the of tgstation-server the is running. @@ -114,7 +115,7 @@ public sealed class GatewayInformation /// The to use. /// The of tgstation-server the is running. [TgsGraphQLAuthorize] - public Version? Version( + public Version Version( [Service] IAssemblyInformationProvider assemblyInformationProvider) { ArgumentNullException.ThrowIfNull(assemblyInformationProvider); @@ -130,13 +131,13 @@ public sealed class GatewayInformation /// Gets the HTTP API of the . /// [TgsGraphQLAuthorize] - public Version? ApiVersion => ApiHeaders.Version; + public Version ApiVersion => ApiHeaders.Version; /// /// Gets the DMAPI interop the uses. /// [TgsGraphQLAuthorize] - public Version? DMApiVersion => DMApiConstants.InteropVersion; + public Version DMApiVersion => DMApiConstants.InteropVersion; /// /// Gets the information needed to perform open authentication with the . From e240d864291ec82cd0403e422d4f23675948997a Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Sun, 6 Oct 2024 10:49:32 -0400 Subject: [PATCH 02/10] Pull together all the metadata, documentation, tooling, etc to start pre-releasing the GraphQL API --- .github/CONTRIBUTING.md | 9 +- .github/PULL_REQUEST_TEMPLATE.md | 2 +- .github/workflows/ci-pipeline.yml | 124 ++++++++++++++++-- build/Version.props | 3 +- src/README.md | 2 +- .../Tgstation.Server.Api.csproj | 2 +- .../UnauthenticatedServerInformation.graphql | 2 +- .../Tgstation.Server.Client.GraphQL.csproj | 2 +- src/Tgstation.Server.Host/GraphQL/Mutation.cs | 6 + .../GraphQL/Mutations/UserGroupMutations.cs | 1 + .../GraphQL/Mutations/UserMutations.cs | 1 + src/Tgstation.Server.Host/GraphQL/Query.cs | 3 + .../GraphQL/Subscription.cs | 6 + .../Subscriptions/UserSubscriptions.cs | 1 + .../GraphQL/Types/GatewayInformation.cs | 15 ++- .../Properties/MasterVersionsAttribute.cs | 14 +- src/Tgstation.Server.Host/README.md | 4 +- .../Tgstation.Server.Host.csproj | 1 + src/Tgstation.Server.Host/appsettings.yml | 4 +- .../Live/TestLiveServer.cs | 19 ++- tests/Tgstation.Server.Tests/TestVersions.cs | 15 ++- .../Component.cs | 3 +- .../Tgstation.Server.ReleaseNotes/Program.cs | 24 ++-- 23 files changed, 209 insertions(+), 54 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index e1eca34032..f116b2259d 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -191,7 +191,7 @@ This prevents nesting levels from getting deeper then they need to be. - Some terminology to help understand the architecture: - An instance can be thought of as a separate server. It has a separate directory, repository, set of byond installations, etc... The only thing shared amongst instances is API surface, users, global configuration, the active tgstation-server version, and the host machine. - - API refers to the HTTP API unless otherwise specified. + - API refers to the REST API unless otherwise specified. - The entirety of server functionality resides in the host (Tgstation.Server.Host) project. - A Component is a service running in tgstation-server to help with instance functionality. These can only be communicated with via the HTTP or DM APIs. - There is a difference between Watchdog and Host Watchdog. The former monitors DreamDaemon uptime, the latter handles updating tgstation-server. @@ -247,7 +247,7 @@ Warning: You may need to temporarily set valid MySql credentials in [MySqlDesign OAuth providers are hardcoded but it is fairly easy to add new ones. The flow doesn't need to be strict OAuth either (r.e. /tg/ forums). Follow the following steps: -1. Add the name to the [Tgstation.Server.Api.Models.OAuthProviders](../src/Tgstation.Server.Api/Models/OAuthProviders.cs) enum (Also necessitates a minor HTTP API version bump). +1. Add the name to the [Tgstation.Server.Api.Models.OAuthProviders](../src/Tgstation.Server.Api/Models/OAuthProviders.cs) enum (Also necessitates a minor API version bump to the HTTP APIs (REST/GraphQL)). 1. Create an implementation of [IOAuthValidator](../src/Tgstation.Server.Host/Security/OAuth/IOAuthValidator.cs). - Most providers can simply override the [GenericOAuthValidator](../src/Tgstation.Server.Host/Security/OAuth/GenericOAuthValidator.cs). 1. Construct the implementation in the [OAuthProviders](../src/Tgstation.Server.Host/Security/OAuth/OAuthProviders.cs) class. @@ -275,7 +275,8 @@ Major changes should be committed to the `VX` branch created when the time for a We have several subcomponent APIs we ship with the core server that have their own versions. -- HTTP API +- REST API +- GraphQL API - DreamMaker API - Interop API - Configuration File @@ -308,7 +309,7 @@ Word commit names descriptively. Only submit work through pull requests (With th At the time of this writing, the repository is configured to automate much of the deployment/release process. -When the new API, client, or DMAPI is ready to be released, update the `Version.props` file appropriately and merge the pull request with the text `[APIDeploy]`, `[NuGetDeploy]`, or `[DMDeploy]` respectively in the commit message (or all three!). The release will be published automatically. +When the new API, client, or DMAPI is ready to be released, update the `Version.props` file appropriately and merge the pull request with the text `[RESTDeploy]`, `[GQLDeploy]`, `[NugetDeploy]`, or `[DMDeploy]` respectively in the commit message (or all three!). The release will be published automatically. That step should be taken for the latest API and client before releasing the core version that uses them if applicable. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index a71922c1df..518a6e5b61 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -16,7 +16,7 @@ They will be amalgamated together in the end. Categories are used by [the release notes tool](../tools/Tgstation.Server.ReleaseNotes) to generate formatted changelists used in releases. The default category is Core. Only one category may be specified for a 🆑 block. -Valid categories are Core, DreamMaker API, HTTP API, Host Watchdog, Web Control Panel, Configuration, Nuget: Api, Nuget: Client, and Nuget: Common. +Valid categories are Core, DreamMaker API, REST API, GraphQL API, Host Watchdog, Web Control Panel, Configuration, Nuget: Api, Nuget: Client, and Nuget: Common. /🆑 [Why]: # (If this does not close or work on an existing GitHub issue, please add a short description [two lines down] of why you think these changes would benefit the server. If you can't justify it in words, it might not be worth adding.) diff --git a/.github/workflows/ci-pipeline.yml b/.github/workflows/ci-pipeline.yml index afef6086dd..4c27487241 100644 --- a/.github/workflows/ci-pipeline.yml +++ b/.github/workflows/ci-pipeline.yml @@ -10,7 +10,8 @@ # - Checks commit tags for deployment intents # - Deploys DreamMaker API zip [DMDeploy] (dev/master) # - Deploys Nuget Packages [NugetDeploy] (dev/master) -# - Deploys HTTP API swagger.json [APIDeploy] (dev/master) +# - Deploys HTTP API swagger.json [RESTDeploy] (dev/master) +# - Deploys GraphQL API schema.graphql [GQLDeploy] (dev/master) # - Deploys tgstation-server [TGSDeploy] (master) # - GitHub Releases: https://github.com/tgstation/tgstation-server/releases # - Docker: https://hub.docker.com/r/tgstation/server @@ -741,6 +742,13 @@ jobs: name: openapi-spec path: C:/tgs_api.json + - name: Store GraphQL Schema + if: ${{ matrix.configuration == 'Release' && matrix.watchdog-type == 'Advanced' && matrix.database-type == 'SqlServer' }} + uses: actions/upload-artifact@v4 + with: + name: graphql-schema + path: ./artifacts/tgs-api.graphql + - name: Package Server Service if: ${{ matrix.configuration == 'Release' && matrix.watchdog-type == 'Basic' && matrix.database-type == 'PostgresSql' }} run: | @@ -1611,11 +1619,11 @@ jobs: - name: GitHub Requires at Least One Step for a Job run: exit 0 - deploy-http: - name: Deploy HTTP API + deploy-rest: + name: Deploy REST API needs: deployment-gate runs-on: windows-latest - if: contains(github.event.head_commit.message, '[APIDeploy]') + if: contains(github.event.head_commit.message, '[RESTDeploy]') steps: - name: Setup dotnet uses: actions/setup-dotnet@v4 @@ -1630,7 +1638,7 @@ jobs: shell: powershell run: | [XML]$versionXML = Get-Content build/Version.props - $apiVersion = $versionXML.Project.PropertyGroup.TgsApiVersion + $apiVersion = $versionXML.Project.PropertyGroup.TgsRestVersion echo "TGS_API_VERSION=$apiVersion" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append - name: Retrieve OpenAPI Spec @@ -1661,7 +1669,7 @@ jobs: - name: Generate Release Notes env: TGS_RELEASE_NOTES_TOKEN: ${{ steps.app-token-generation.outputs.token }} - run: dotnet release_notes_bins/Tgstation.Server.ReleaseNotes.dll ${{ env.TGS_API_VERSION }} --httpapi + run: dotnet release_notes_bins/Tgstation.Server.ReleaseNotes.dll ${{ env.TGS_API_VERSION }} --restapi - name: Create GitHub Release uses: actions/create-release@v1 @@ -1670,7 +1678,7 @@ jobs: GITHUB_TOKEN: ${{ steps.app-token-generation.outputs.token }} with: tag_name: api-v${{ env.TGS_API_VERSION }} - release_name: tgstation-server API v${{ env.TGS_API_VERSION }} + release_name: tgstation-server REST API v${{ env.TGS_API_VERSION }} body_path: release_notes.md commitish: ${{ github.event.head_commit.id }} @@ -1684,6 +1692,86 @@ jobs: asset_name: swagger.json asset_content_type: application/json + deploy-gql: + name: Deploy GraphQL API + needs: deployment-gate + runs-on: windows-latest + if: contains(github.event.head_commit.message, '[GQLDeploy]') + steps: + - name: Setup dotnet + uses: actions/setup-dotnet@v4 + with: + dotnet-version: ${{ env.TGS_DOTNET_VERSION }}.0.x + dotnet-quality: ${{ env.TGS_DOTNET_QUALITY }} + + - name: Checkout + uses: actions/checkout@v4 + + - name: Parse API version + shell: powershell + run: | + [XML]$versionXML = Get-Content build/Version.props + $apiVersion = $versionXML.Project.PropertyGroup.TgsGraphQLVersion + echo "TGS_API_VERSION=$apiVersion" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append + [Version]$parsedVersion = $apiVersion + if ($parsedVersion.Major -eq 0) { + echo "TGS_GRAPHQL_PRERELEASE=true" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append + } else { + echo "TGS_GRAPHQL_PRERELEASE=false" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append + } + + - name: Retrieve GraphQL Schema + uses: actions/download-artifact@v4 + with: + name: graphql-schema + path: schema + + - name: Grab Most Recent Changelog + shell: powershell + run: | + $ProgressPreference = 'SilentlyContinue' + Invoke-WebRequest -Uri https://raw.githubusercontent.com/tgstation/tgstation-server/gh-pages/changelog.yml -OutFile changelog.yml + + - name: Retrieve ReleaseNotes Binaries + uses: actions/download-artifact@v4 + with: + name: release_notes_bins + path: release_notes_bins + + - name: Generate App Token + id: app-token-generation + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + + - name: Generate Release Notes + env: + TGS_RELEASE_NOTES_TOKEN: ${{ steps.app-token-generation.outputs.token }} + run: dotnet release_notes_bins/Tgstation.Server.ReleaseNotes.dll ${{ env.TGS_API_VERSION }} --graphqlapi + + - name: Create GitHub Release + uses: actions/create-release@v1 + id: create_release + env: + GITHUB_TOKEN: ${{ steps.app-token-generation.outputs.token }} + with: + tag_name: graphql-v${{ env.TGS_API_VERSION }} + release_name: tgstation-server GraphQL API v${{ env.TGS_API_VERSION }} + body_path: release_notes.md + commitish: ${{ github.event.head_commit.id }} + prerelease: ${{ env.TGS_GRAPHQL_PRERELEASE }} + + - name: Upload OpenApi Spec + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ steps.app-token-generation.outputs.token }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./schema/tgs-api.graphql + asset_name: tgs-api.graphql + asset_content_type: text/plain + deploy-dm: name: Deploy DreamMaker API needs: deployment-gate @@ -1818,7 +1906,7 @@ jobs: ensure-release: name: Ensure TGS Release is Latest GitHub Release - needs: [deploy-dm, deploy-http] + needs: [deploy-dm, deploy-rest, deploy-gql] runs-on: ubuntu-latest if: (!(cancelled() || failure())) && (!contains(github.event.head_commit.message, '[TGSDeploy]')) && (needs.deploy-dm.result == 'success' || needs.deploy-http.result == 'success') steps: @@ -1848,7 +1936,7 @@ jobs: deploy-tgs: name: Deploy TGS - needs: [deploy-dm, deploy-http, deployment-gate] + needs: [deploy-dm, deploy-rest, deploy-gql, deployment-gate] runs-on: windows-latest if: (!(cancelled() || failure())) && github.event.ref == 'refs/heads/master' && contains(github.event.head_commit.message, '[TGSDeploy]') && needs.deployment-gate.result == 'success' env: @@ -1940,6 +2028,12 @@ jobs: name: openapi-spec path: swagger + - name: Retrieve GraphQL Schema + uses: actions/download-artifact@v4 + with: + name: graphql-schema + path: schema + - name: Retrieve Debian Packaging Archive uses: actions/download-artifact@v4 with: @@ -2035,7 +2129,7 @@ jobs: asset_name: DMAPI.zip asset_content_type: application/zip - - name: Upload OpenApi Spec Artifact + - name: Upload REST API Artifact uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ steps.app-token-generation.outputs.token }} @@ -2045,6 +2139,16 @@ jobs: asset_name: swagger.json asset_content_type: application/json + - name: Upload GraphQL API Artifact + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ steps.app-token-generation.outputs.token }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./schema/tgs-api.graphql + asset_name: tgs-api.graphql + asset_content_type: text/plain + - name: Upload Server Update Package Artifact uses: actions/upload-release-asset@v1 env: diff --git a/build/Version.props b/build/Version.props index 9c784ed4d7..1e304d4fb6 100644 --- a/build/Version.props +++ b/build/Version.props @@ -5,7 +5,8 @@ 6.10.0 5.2.0 - 10.10.0 + 10.10.0 + 0.1.0 7.0.0 16.0.0 19.0.0 diff --git a/src/README.md b/src/README.md index 76cb6bdf5d..c05d859bf2 100644 --- a/src/README.md +++ b/src/README.md @@ -5,7 +5,7 @@ This is a series of README.md files aimed at directing people around the codebas - To explore the server code navigate to [Tgstation.Server.Host](./Tgstation.Server.Host). - To explore the DreamMaker public API navigate to [DMAPI/tgs.dm](./DMAPI/tgs.dm). - To explore the DMAPI internals, navigate to [DMAPI/tgs](./DMAPI/tgs). -- To explore the HTTP API definitions navigate to [Tgstation.Server.Api](./Tgstation.Server.Api). +- To explore the REST API definitions navigate to [Tgstation.Server.Api](./Tgstation.Server.Api). - To explore the C# client code navigate to [Tgstation.Server.Client](./Tgstation.Server.Client). [Tgstation.Server.Host.Watchdog](./Tgstation.Server.Host.Watchdog), [Tgstation.Server.Host.Service](./Tgstation.Server.Host.Service), and [Tgstation.Server.Host.Console](./Tgstation.Server.Host.Console) are related to the Service/Console runners which have the simple task of executing Tgstation.Server.Host and updating it when requested. diff --git a/src/Tgstation.Server.Api/Tgstation.Server.Api.csproj b/src/Tgstation.Server.Api/Tgstation.Server.Api.csproj index 2617a2a566..129035ad21 100644 --- a/src/Tgstation.Server.Api/Tgstation.Server.Api.csproj +++ b/src/Tgstation.Server.Api/Tgstation.Server.Api.csproj @@ -14,7 +14,7 @@ - <_Parameter1>$(TgsApiVersion) + <_Parameter1>$(TgsRestVersion) diff --git a/src/Tgstation.Server.Client.GraphQL/GQL/Queries/UnauthenticatedServerInformation.graphql b/src/Tgstation.Server.Client.GraphQL/GQL/Queries/UnauthenticatedServerInformation.graphql index 44bff278f0..d2a45ce425 100644 --- a/src/Tgstation.Server.Client.GraphQL/GQL/Queries/UnauthenticatedServerInformation.graphql +++ b/src/Tgstation.Server.Client.GraphQL/GQL/Queries/UnauthenticatedServerInformation.graphql @@ -3,7 +3,7 @@ query UnauthenticatedServerInformation { currentNode { gateway { information { - majorApiVersion + majorGraphQLApiVersion oAuthProviderInfos { key value { diff --git a/src/Tgstation.Server.Client.GraphQL/Tgstation.Server.Client.GraphQL.csproj b/src/Tgstation.Server.Client.GraphQL/Tgstation.Server.Client.GraphQL.csproj index 4c1e5d1557..f0fa5de21f 100644 --- a/src/Tgstation.Server.Client.GraphQL/Tgstation.Server.Client.GraphQL.csproj +++ b/src/Tgstation.Server.Client.GraphQL/Tgstation.Server.Client.GraphQL.csproj @@ -3,7 +3,7 @@ $(TgsFrameworkVersion) - $(TgsApiVersion) + enable diff --git a/src/Tgstation.Server.Host/GraphQL/Mutation.cs b/src/Tgstation.Server.Host/GraphQL/Mutation.cs index 4b68aa9b89..5e6fa1d10c 100644 --- a/src/Tgstation.Server.Host/GraphQL/Mutation.cs +++ b/src/Tgstation.Server.Host/GraphQL/Mutation.cs @@ -14,8 +14,14 @@ namespace Tgstation.Server.Host.GraphQL /// Root type for GraphQL mutations. /// /// Intentionally left mostly empty, use type extensions to properly scope operations to domains. + [GraphQLDescription(GraphQLDescription)] public sealed class Mutation { + /// + /// Description to show on the type. + /// + public const string GraphQLDescription = "Root Mutation type"; + /// /// Generate a JWT for authenticating with server. This is the only operation that accepts and required basic authentication. /// diff --git a/src/Tgstation.Server.Host/GraphQL/Mutations/UserGroupMutations.cs b/src/Tgstation.Server.Host/GraphQL/Mutations/UserGroupMutations.cs index 92ef77d288..7f27d8ea76 100644 --- a/src/Tgstation.Server.Host/GraphQL/Mutations/UserGroupMutations.cs +++ b/src/Tgstation.Server.Host/GraphQL/Mutations/UserGroupMutations.cs @@ -18,6 +18,7 @@ namespace Tgstation.Server.Host.GraphQL.Mutations /// related s. /// [ExtendObjectType(typeof(Mutation))] + [GraphQLDescription(Mutation.GraphQLDescription)] public sealed class UserGroupMutations { /// diff --git a/src/Tgstation.Server.Host/GraphQL/Mutations/UserMutations.cs b/src/Tgstation.Server.Host/GraphQL/Mutations/UserMutations.cs index 64c671a7cb..5c51fd8bc3 100644 --- a/src/Tgstation.Server.Host/GraphQL/Mutations/UserMutations.cs +++ b/src/Tgstation.Server.Host/GraphQL/Mutations/UserMutations.cs @@ -22,6 +22,7 @@ namespace Tgstation.Server.Host.GraphQL.Mutations /// related s. /// [ExtendObjectType(typeof(Mutation))] + [GraphQLDescription(Mutation.GraphQLDescription)] public sealed class UserMutations { /// diff --git a/src/Tgstation.Server.Host/GraphQL/Query.cs b/src/Tgstation.Server.Host/GraphQL/Query.cs index ca9fed764f..660040d41d 100644 --- a/src/Tgstation.Server.Host/GraphQL/Query.cs +++ b/src/Tgstation.Server.Host/GraphQL/Query.cs @@ -1,10 +1,13 @@ #pragma warning disable CA1724 // Dumb conflict with Microsoft.EntityFrameworkCore.Query +using HotChocolate; + namespace Tgstation.Server.Host.GraphQL { /// /// GraphQL query . /// + [GraphQLDescription("Root Query type")] public sealed class Query { /// diff --git a/src/Tgstation.Server.Host/GraphQL/Subscription.cs b/src/Tgstation.Server.Host/GraphQL/Subscription.cs index 114a7622ad..170dceb49a 100644 --- a/src/Tgstation.Server.Host/GraphQL/Subscription.cs +++ b/src/Tgstation.Server.Host/GraphQL/Subscription.cs @@ -16,8 +16,14 @@ namespace Tgstation.Server.Host.GraphQL /// Root type for GraphQL subscriptions. /// /// Intentionally left mostly empty, use type extensions to properly scope operations to domains. + [GraphQLDescription(GraphQLDescription)] public sealed class Subscription { + /// + /// Description to show on the type. + /// + public const string GraphQLDescription = "Root Subscription type. Note that subscriptions are performed over Server Sent events."; + /// /// Gets the topic name for the login session represented by a given . /// diff --git a/src/Tgstation.Server.Host/GraphQL/Subscriptions/UserSubscriptions.cs b/src/Tgstation.Server.Host/GraphQL/Subscriptions/UserSubscriptions.cs index 2369df8ab4..35be564c52 100644 --- a/src/Tgstation.Server.Host/GraphQL/Subscriptions/UserSubscriptions.cs +++ b/src/Tgstation.Server.Host/GraphQL/Subscriptions/UserSubscriptions.cs @@ -18,6 +18,7 @@ namespace Tgstation.Server.Host.GraphQL.Subscriptions /// Subscriptions for . /// [ExtendObjectType(typeof(Subscription))] + [GraphQLDescription(Subscription.GraphQLDescription)] public sealed class UserSubscriptions { /// diff --git a/src/Tgstation.Server.Host/GraphQL/Types/GatewayInformation.cs b/src/Tgstation.Server.Host/GraphQL/Types/GatewayInformation.cs index a4fc74bd1d..727e38c5f1 100644 --- a/src/Tgstation.Server.Host/GraphQL/Types/GatewayInformation.cs +++ b/src/Tgstation.Server.Host/GraphQL/Types/GatewayInformation.cs @@ -1,6 +1,5 @@ -using System; +using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using HotChocolate; @@ -123,12 +122,18 @@ public Version Version( } /// - /// Gets the major HTTP API number of the . + /// Gets the major GraphQL API number of the . /// - public int MajorApiVersion => ApiHeaders.Version.Major; + public int MajorGraphQLApiVersion => GraphQLApiVersion.Major; /// - /// Gets the HTTP API of the . + /// Gets the GraphQL API of the . + /// + [TgsGraphQLAuthorize] + public Version GraphQLApiVersion => global::System.Version.Parse(MasterVersionsAttribute.Instance.RawGraphQLVersion); + + /// + /// Gets the REST API of the . /// [TgsGraphQLAuthorize] public Version ApiVersion => ApiHeaders.Version; diff --git a/src/Tgstation.Server.Host/Properties/MasterVersionsAttribute.cs b/src/Tgstation.Server.Host/Properties/MasterVersionsAttribute.cs index 8212c4ffee..efef0144bc 100644 --- a/src/Tgstation.Server.Host/Properties/MasterVersionsAttribute.cs +++ b/src/Tgstation.Server.Host/Properties/MasterVersionsAttribute.cs @@ -32,7 +32,7 @@ sealed class MasterVersionsAttribute : Attribute public string RawWebpanelVersion { get; } /// - /// The of the control panel version built. + /// The of the host watchdog that was built alongside this TGS version. /// public string RawHostWatchdogVersion { get; } @@ -42,10 +42,15 @@ sealed class MasterVersionsAttribute : Attribute public string RawMariaDBRedistVersion { get; } /// - /// The of the MariaDB server bundled with TGS installs. + /// The of the TGS swarm protocol. /// public string RawSwarmProtocolVersion { get; } + /// + /// The of the TGS GraphQL API. + /// + public string RawGraphQLVersion { get; } + /// /// Initializes a new instance of the class. /// @@ -55,13 +60,15 @@ sealed class MasterVersionsAttribute : Attribute /// The value of . /// The value of . /// The value of . + /// The value of . public MasterVersionsAttribute( string rawConfigurationVersion, string rawInteropVersion, string rawWebpanelVersion, string rawHostWatchdogVersion, string rawMariaDBRedistVersion, - string rawSwarmProtocolVersion) + string rawSwarmProtocolVersion, + string rawGraphQLVersion) { RawConfigurationVersion = rawConfigurationVersion ?? throw new ArgumentNullException(nameof(rawConfigurationVersion)); RawInteropVersion = rawInteropVersion ?? throw new ArgumentNullException(nameof(rawInteropVersion)); @@ -69,6 +76,7 @@ public MasterVersionsAttribute( RawHostWatchdogVersion = rawHostWatchdogVersion ?? throw new ArgumentNullException(nameof(rawHostWatchdogVersion)); RawMariaDBRedistVersion = rawMariaDBRedistVersion ?? throw new ArgumentNullException(nameof(rawMariaDBRedistVersion)); RawSwarmProtocolVersion = rawSwarmProtocolVersion ?? throw new ArgumentNullException(nameof(rawSwarmProtocolVersion)); + RawGraphQLVersion = rawGraphQLVersion ?? throw new ArgumentNullException(nameof(rawGraphQLVersion)); } } } diff --git a/src/Tgstation.Server.Host/README.md b/src/Tgstation.Server.Host/README.md index 1f8df0feee..9dd86b2d63 100644 --- a/src/Tgstation.Server.Host/README.md +++ b/src/Tgstation.Server.Host/README.md @@ -23,13 +23,15 @@ Server startup can be a bit complicated so here's a walkthrough Here's a breakdown of things in this directory - [.config](./.config) contains the dotnet-tools.json. At the time of writing, this is only used to set the version of the [dotnet ef tools](https://docs.microsoft.com/en-us/ef/core/miscellaneous/cli/) used to create database migrations. +- [Authority](./Authority) is code that contains the buisiness logic that bridges the HTTP APIs with component code and the database. - [ClientApp](./ClientApp) contains scripts to build and deploy the web control panel with TGS. - [Components](./Components) is where the bulk of the TGS implementation lives. - [Configuration](./Configuration) contains classes that partly make up the configuration yaml files (i.e. [appsettings.yml](./appsettings.yml)). -- [Controllers](./Controllers) is where HTTP API code lives and bridges it with component code. +- [Controllers](./Controllers) is where REST API code lives. - [Core](./Core) contains [Application.cs](./Core/Application.cs) and other classes related to the overrarching server process. - [Database](./Database) contains all database related code. - [Extensions](./Extensions) contains helper functions implemented as C# extension methods. +- [GraphQL](./GraqhQL) is where GraphQL API code lives. - [IO](./IO) contains classes related to interacting with the filesystem. - [Jobs](./Jobs) contains the job manager code. - [Models](./Models) contains ORM models used to interact with the database. diff --git a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj index 22798e4f13..1a6ec0e2e7 100644 --- a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj +++ b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj @@ -57,6 +57,7 @@ <_Parameter4>$(TgsHostWatchdogVersion) <_Parameter5>$(TgsMariaDBRedistVersion) <_Parameter6>$(TgsSwarmProtocolVersion) + <_Parameter7>$(TgsGraphQLVersion) diff --git a/src/Tgstation.Server.Host/appsettings.yml b/src/Tgstation.Server.Host/appsettings.yml index 353104e6de..b965a326ed 100644 --- a/src/Tgstation.Server.Host/appsettings.yml +++ b/src/Tgstation.Server.Host/appsettings.yml @@ -13,7 +13,7 @@ General: UserGroupLimit: 25 # Maximum number of allowed groups InstanceLimit: 10 # Maximum number of allowed instances ValidInstancePaths: # An array of directories instances may be created in (either directly or as a subdirectory). null removes the restriction - HostApiDocumentation: false # Make HTTP API documentation available at /api/doc/tgs_api.json + HostApiDocumentation: false # Make HTTP API documentation available at /api/doc/tgs_api.json and /api/graphql SkipAddingByondFirewallException: false # Windows Only: Prevent running netsh.exe to add a firewall exception for installed engine binaries DeploymentDirectoryCopyTasksPerCore: 100 # Maximum number of concurrent file copy operations PER available CPU core OpenDreamGitUrl: https://github.com/OpenDreamProject/OpenDream # The repository to retrieve OpenDream from @@ -40,7 +40,7 @@ Logging: Microsoft: Warning # Level of stdout verbosity from Microsoft dependecies (i.e. webserver, database object relational mapper, etc...). Can be one of Trace, Debug, Information, Warning, Error, or Critical ControlPanel: Enable: false # If the web control panel is hosted alongside the server - Channel: https://tgstation.github.io/tgstation-server-webpanel/api/${Major}.${Minor}.${Patch} # Channel for live web control panel updates. The tokens ${Major}, ${Minor}, and ${Patch} are substituted for the version of the HTTP API TGS was built with + Channel: https://tgstation.github.io/tgstation-server-webpanel/api/${Major}.${Minor}.${Patch} # Channel for live web control panel updates. The tokens ${Major}, ${Minor}, and ${Patch} are substituted for the version of the REST API TGS was built with AllowAnyOrigin: false # Enable the `Access-Control-Allow-Origin: *` header for HTTP responses AllowedOrigins: [] # Explict list of origins for `Access-Control-Allow-Origin:` HTTP header. Ignored if AllowAnyOrigin is true PublicPath: # Used if TGS is not hosted at the website root. If TGS is hosted under `https://domain.com/tgs`, this should be `/tgs` diff --git a/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs b/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs index ea2bf9311f..dec3a1a96d 100644 --- a/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs +++ b/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs @@ -1407,16 +1407,15 @@ await unAuthedMultiClient.ExecuteReadOnlyConfirmEquivalence( gqlClient => gqlClient.UnauthenticatedServerInformation.ExecuteAsync(cancellationToken), (restServerInfo, gqlServerInfo) => { - var result = restServerInfo.ApiVersion.Major == gqlServerInfo.Swarm.CurrentNode.Gateway.Information.MajorApiVersion - && (restServerInfo.OAuthProviderInfos == gqlServerInfo.Swarm.CurrentNode.Gateway.Information.OAuthProviderInfos - || restServerInfo.OAuthProviderInfos.All(kvp => - { - var info = gqlServerInfo.Swarm.CurrentNode.Gateway.Information.OAuthProviderInfos.FirstOrDefault(x => (int)x.Key == (int)kvp.Key); - return info != null - && info.Value.ServerUrl == kvp.Value.ServerUrl - && info.Value.ClientId == kvp.Value.ClientId - && info.Value.RedirectUri == kvp.Value.RedirectUri; - })); + var result = restServerInfo.OAuthProviderInfos == gqlServerInfo.Swarm.CurrentNode.Gateway.Information.OAuthProviderInfos + || restServerInfo.OAuthProviderInfos.All(kvp => + { + var info = gqlServerInfo.Swarm.CurrentNode.Gateway.Information.OAuthProviderInfos.FirstOrDefault(x => (int)x.Key == (int)kvp.Key); + return info != null + && info.Value.ServerUrl == kvp.Value.ServerUrl + && info.Value.ClientId == kvp.Value.ClientId + && info.Value.RedirectUri == kvp.Value.RedirectUri; + }); return result; }, diff --git a/tests/Tgstation.Server.Tests/TestVersions.cs b/tests/Tgstation.Server.Tests/TestVersions.cs index 6da26320aa..a0809b78f6 100644 --- a/tests/Tgstation.Server.Tests/TestVersions.cs +++ b/tests/Tgstation.Server.Tests/TestVersions.cs @@ -32,7 +32,7 @@ using Tgstation.Server.Host.System; using Tgstation.Server.Api.Models; using Tgstation.Server.Tests.Live; -using Tgstation.Server.Host.Utils; +using Tgstation.Server.Host.Properties; namespace Tgstation.Server.Tests { @@ -75,14 +75,23 @@ public void TestConfigVersion() } [TestMethod] - public void TestApiVersion() + public void TestRestVersion() { - var versionString = versionsPropertyGroup.Element(xmlNamespace + "TgsApiVersion").Value; + var versionString = versionsPropertyGroup.Element(xmlNamespace + "TgsRestVersion").Value; Assert.IsNotNull(versionString); Assert.IsTrue(Version.TryParse(versionString, out var expected)); Assert.AreEqual(expected, ApiHeaders.Version); } + [TestMethod] + public void TestGraphQLVersion() + { + var versionString = versionsPropertyGroup.Element(xmlNamespace + "TgsGraphQLVersion").Value; + Assert.IsNotNull(versionString); + Assert.IsTrue(Version.TryParse(versionString, out var expected)); + Assert.AreEqual(expected, Version.Parse(MasterVersionsAttribute.Instance.RawGraphQLVersion)); + } + [TestMethod] public void TestApiLibraryVersion() { diff --git a/tools/Tgstation.Server.ReleaseNotes/Component.cs b/tools/Tgstation.Server.ReleaseNotes/Component.cs index d1baa3fcda..b983aebc87 100644 --- a/tools/Tgstation.Server.ReleaseNotes/Component.cs +++ b/tools/Tgstation.Server.ReleaseNotes/Component.cs @@ -6,7 +6,8 @@ enum Component Core, HostWatchdog, WebControlPanel, - HttpApi, + RestApi, + GraphQLApi, DreamMakerApi, InteropApi, NugetCommon, diff --git a/tools/Tgstation.Server.ReleaseNotes/Program.cs b/tools/Tgstation.Server.ReleaseNotes/Program.cs index 801d10e779..3e64ff565b 100644 --- a/tools/Tgstation.Server.ReleaseNotes/Program.cs +++ b/tools/Tgstation.Server.ReleaseNotes/Program.cs @@ -91,8 +91,11 @@ static async Task Main(string[] args) case "--NO-CLOSE": doNotCloseMilestone = true; break; - case "--HTTPAPI": - componentRelease = Component.HttpApi; + case "--RESTAPI": + componentRelease = Component.RestApi; + break; + case "--GRAPHQLAPI": + componentRelease = Component.GraphQLApi; break; case "--INTEROPAPI": componentRelease = Component.InteropApi; @@ -245,7 +248,8 @@ static async Task Main(string[] args) return 10; } - var apiVersion = Version.Parse(versionsPropertyGroup.Element(xmlNamespace + "TgsApiVersion").Value); + var restVersion = Version.Parse(versionsPropertyGroup.Element(xmlNamespace + "TgsRestVersion").Value); + var graphQLVersion = Version.Parse(versionsPropertyGroup.Element(xmlNamespace + "TgsGraphQLVersion").Value); var configVersion = Version.Parse(versionsPropertyGroup.Element(xmlNamespace + "TgsConfigVersion").Value); var dmApiVersion = Version.Parse(versionsPropertyGroup.Element(xmlNamespace + "TgsDmapiVersion").Value); var interopVersion = Version.Parse(versionsPropertyGroup.Element(xmlNamespace + "TgsInteropVersion").Value); @@ -255,7 +259,7 @@ static async Task Main(string[] args) if (webControlVersion.Major == 0) postControlPanelMessage = true; - prefix = $"Please refer to the [README](https://github.com/tgstation/tgstation-server#setup) for setup instructions. Full changelog can be found [here](https://raw.githubusercontent.com/tgstation/tgstation-server/gh-pages/changelog.yml).{Environment.NewLine}{Environment.NewLine}#### Component Versions\nCore: {coreVersion}\nConfiguration: {configVersion}\nHTTP API: {apiVersion}\nDreamMaker API: {dmApiVersion} (Interop: {interopVersion})\n[Web Control Panel](https://github.com/tgstation/tgstation-server-webpanel): {webControlVersion}\nHost Watchdog: {hostWatchdogVersion}"; + prefix = $"Please refer to the [README](https://github.com/tgstation/tgstation-server#setup) for setup instructions. Full changelog can be found [here](https://raw.githubusercontent.com/tgstation/tgstation-server/gh-pages/changelog.yml).{Environment.NewLine}{Environment.NewLine}#### Component Versions\nCore: {coreVersion}\nConfiguration: {configVersion}\nREST API: {restVersion}\nGraphQL API{(graphQLVersion.Major < 1 ? " (Pre-release)" : String.Empty)}: {graphQLVersion}\nDreamMaker API: {dmApiVersion} (Interop: {interopVersion})\n[Web Control Panel](https://github.com/tgstation/tgstation-server-webpanel): {webControlVersion}\nHost Watchdog: {hostWatchdogVersion}"; var newNotes = new StringBuilder(prefix); if (postControlPanelMessage) @@ -409,7 +413,7 @@ async ValueTask DeleteMilestone(Milestone milestoneToDelete, int moveToMilestone var componentVersionDict = new Dictionary { { Component.Configuration, configVersion }, - { Component.HttpApi, apiVersion }, + { Component.RestApi, restVersion }, { Component.DreamMakerApi, dmApiVersion }, { Component.InteropApi, interopVersion }, { Component.WebControlPanel, webControlVersion }, @@ -508,7 +512,7 @@ async ValueTask DeleteMilestone(Milestone milestoneToDelete, int moveToMilestone static string GetComponentDisplayName(Component component, bool debian) => component switch { - Component.HttpApi => debian ? "the HTTP API" : "HTTP API", + Component.RestApi => debian ? "the REST API" : "REST API", Component.InteropApi => debian ? "the Interop API" : "Interop API", Component.Configuration => debian ? "the TGS configuration" : "**Configuration**", Component.DreamMakerApi => debian ? "the DreamMaker API" : "DreamMaker API", @@ -607,7 +611,8 @@ Version Parse(string elemName, bool controlPanel = false) var dict = new Dictionary { { Component.Core, Parse("TgsCoreVersion") }, - { Component.HttpApi, Parse("TgsApiVersion") }, + { Component.RestApi, Parse("TgsRestVersion") }, + { Component.GraphQLApi, Parse("TgsGraphQLVersion") }, { Component.DreamMakerApi, Parse("TgsDmapiVersion") }, }; @@ -720,7 +725,8 @@ async Task CommitNotes(Component component, List notes) component = targetComponent.ToUpperInvariant() switch { "**CONFIGURATION**" or "CONFIGURATION" or "CONFIG" => Component.Configuration, - "HTTP API" => Component.HttpApi, + "HTTP API" or "REST API" => Component.RestApi, + "GQL API" or "GRAPHQL API" or "GQL" or "GRAPHQL" => Component.GraphQLApi, "WEB CONTROL PANEL" => Component.WebControlPanel, "DMAPI" or "DREAMMAKER API" => Component.DreamMakerApi, "INTEROP API" => Component.InteropApi, @@ -1161,7 +1167,7 @@ static async Task GenerateNotes(IGitHubClient client, Dictionary> { - { Component.HttpApi, releases + { Component.RestApi, releases .Where(x => x.TagName.StartsWith("api-v")) .Select(x => Version.Parse(x.TagName[5..])) .OrderBy(x => x) From b2dcec3fdcf4b132516603c4b88378dbd67a48d8 Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Sun, 6 Oct 2024 10:54:53 -0400 Subject: [PATCH 03/10] Correct an actions step name --- .github/workflows/ci-pipeline.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-pipeline.yml b/.github/workflows/ci-pipeline.yml index 4c27487241..cc4afdcb38 100644 --- a/.github/workflows/ci-pipeline.yml +++ b/.github/workflows/ci-pipeline.yml @@ -1762,7 +1762,7 @@ jobs: commitish: ${{ github.event.head_commit.id }} prerelease: ${{ env.TGS_GRAPHQL_PRERELEASE }} - - name: Upload OpenApi Spec + - name: Upload GraphQL Schema uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ steps.app-token-generation.outputs.token }} From 5e86d0c7a4bfa78e2738e4b82d52a84cee385ac1 Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Sun, 6 Oct 2024 10:59:11 -0400 Subject: [PATCH 04/10] Fix some GraphQL holes in release notes --- tools/Tgstation.Server.ReleaseNotes/Program.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/tools/Tgstation.Server.ReleaseNotes/Program.cs b/tools/Tgstation.Server.ReleaseNotes/Program.cs index 3e64ff565b..fd231463d2 100644 --- a/tools/Tgstation.Server.ReleaseNotes/Program.cs +++ b/tools/Tgstation.Server.ReleaseNotes/Program.cs @@ -414,6 +414,7 @@ async ValueTask DeleteMilestone(Milestone milestoneToDelete, int moveToMilestone { { Component.Configuration, configVersion }, { Component.RestApi, restVersion }, + { Component.GraphQLApi, graphQLVersion }, { Component.DreamMakerApi, dmApiVersion }, { Component.InteropApi, interopVersion }, { Component.WebControlPanel, webControlVersion }, @@ -513,6 +514,7 @@ async ValueTask DeleteMilestone(Milestone milestoneToDelete, int moveToMilestone static string GetComponentDisplayName(Component component, bool debian) => component switch { Component.RestApi => debian ? "the REST API" : "REST API", + Component.GraphQLApi => debian ? "the GraphQL API" : "GraphQL API", Component.InteropApi => debian ? "the Interop API" : "Interop API", Component.Configuration => debian ? "the TGS configuration" : "**Configuration**", Component.DreamMakerApi => debian ? "the DreamMaker API" : "DreamMaker API", @@ -1166,15 +1168,23 @@ static async Task GenerateNotes(IGitHubClient client, Dictionary> { { Component.RestApi, releases - .Where(x => x.TagName.StartsWith("api-v")) - .Select(x => Version.Parse(x.TagName[5..])) + .Where(x => x.TagName.StartsWith(ApiTagPrefix)) + .Select(x => Version.Parse(x.TagName[ApiTagPrefix.Length..])) + .OrderBy(x => x) + .ToHashSet() }, + { Component.GraphQLApi, releases + .Where(x => x.TagName.StartsWith(GraphQLTagPrefix)) + .Select(x => Version.Parse(x.TagName[GraphQLTagPrefix.Length..])) .OrderBy(x => x) .ToHashSet() }, { Component.DreamMakerApi, releases - .Where(x => x.TagName.StartsWith("dmapi-v")) - .Select(x => Version.Parse(x.TagName[7..])) + .Where(x => x.TagName.StartsWith(DMApiTagPrefix)) + .Select(x => Version.Parse(x.TagName[DMApiTagPrefix.Length..])) .OrderBy(x => x) .ToHashSet() }, { Component.NugetCommon, await nugetCommonVersions }, From 8f734af21089b220ef4217a8ca4b918eff149ddd Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Sun, 6 Oct 2024 11:21:22 -0400 Subject: [PATCH 05/10] Fix changelog.yml deserialization --- tools/Tgstation.Server.ReleaseNotes/Component.cs | 6 ++++-- tools/Tgstation.Server.ReleaseNotes/Program.cs | 12 ++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/tools/Tgstation.Server.ReleaseNotes/Component.cs b/tools/Tgstation.Server.ReleaseNotes/Component.cs index b983aebc87..ac2d1aa84b 100644 --- a/tools/Tgstation.Server.ReleaseNotes/Component.cs +++ b/tools/Tgstation.Server.ReleaseNotes/Component.cs @@ -1,4 +1,6 @@ -namespace Tgstation.Server.ReleaseNotes +using YamlDotNet.Serialization; + +namespace Tgstation.Server.ReleaseNotes { enum Component { @@ -6,7 +8,7 @@ enum Component Core, HostWatchdog, WebControlPanel, - RestApi, + HttpApi, // can't be properly renamed due to changelog GraphQLApi, DreamMakerApi, InteropApi, diff --git a/tools/Tgstation.Server.ReleaseNotes/Program.cs b/tools/Tgstation.Server.ReleaseNotes/Program.cs index fd231463d2..bd2f868e20 100644 --- a/tools/Tgstation.Server.ReleaseNotes/Program.cs +++ b/tools/Tgstation.Server.ReleaseNotes/Program.cs @@ -92,7 +92,7 @@ static async Task Main(string[] args) doNotCloseMilestone = true; break; case "--RESTAPI": - componentRelease = Component.RestApi; + componentRelease = Component.HttpApi; break; case "--GRAPHQLAPI": componentRelease = Component.GraphQLApi; @@ -413,7 +413,7 @@ async ValueTask DeleteMilestone(Milestone milestoneToDelete, int moveToMilestone var componentVersionDict = new Dictionary { { Component.Configuration, configVersion }, - { Component.RestApi, restVersion }, + { Component.HttpApi, restVersion }, { Component.GraphQLApi, graphQLVersion }, { Component.DreamMakerApi, dmApiVersion }, { Component.InteropApi, interopVersion }, @@ -513,7 +513,7 @@ async ValueTask DeleteMilestone(Milestone milestoneToDelete, int moveToMilestone static string GetComponentDisplayName(Component component, bool debian) => component switch { - Component.RestApi => debian ? "the REST API" : "REST API", + Component.HttpApi => debian ? "the REST API" : "REST API", Component.GraphQLApi => debian ? "the GraphQL API" : "GraphQL API", Component.InteropApi => debian ? "the Interop API" : "Interop API", Component.Configuration => debian ? "the TGS configuration" : "**Configuration**", @@ -613,7 +613,7 @@ Version Parse(string elemName, bool controlPanel = false) var dict = new Dictionary { { Component.Core, Parse("TgsCoreVersion") }, - { Component.RestApi, Parse("TgsRestVersion") }, + { Component.HttpApi, Parse("TgsRestVersion") }, { Component.GraphQLApi, Parse("TgsGraphQLVersion") }, { Component.DreamMakerApi, Parse("TgsDmapiVersion") }, }; @@ -727,7 +727,7 @@ async Task CommitNotes(Component component, List notes) component = targetComponent.ToUpperInvariant() switch { "**CONFIGURATION**" or "CONFIGURATION" or "CONFIG" => Component.Configuration, - "HTTP API" or "REST API" => Component.RestApi, + "HTTP API" or "REST API" => Component.HttpApi, "GQL API" or "GRAPHQL API" or "GQL" or "GRAPHQL" => Component.GraphQLApi, "WEB CONTROL PANEL" => Component.WebControlPanel, "DMAPI" or "DREAMMAKER API" => Component.DreamMakerApi, @@ -1172,7 +1172,7 @@ static async Task GenerateNotes(IGitHubClient client, Dictionary> { - { Component.RestApi, releases + { Component.HttpApi, releases .Where(x => x.TagName.StartsWith(ApiTagPrefix)) .Select(x => Version.Parse(x.TagName[ApiTagPrefix.Length..])) .OrderBy(x => x) From 86a69715817ae382dc154b08b74ef699efdcd936 Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Sun, 6 Oct 2024 13:24:59 -0400 Subject: [PATCH 06/10] Reorganize GraphQL types and make OAuthProviders stronger --- .../Authority/IUserAuthority.cs | 5 +- .../Authority/UserAuthority.cs | 8 +-- src/Tgstation.Server.Host/Core/Application.cs | 4 +- .../Interceptors/RightsTypeInterceptor.cs | 4 +- .../Mutations/Payloads/LoginPayload.cs | 2 +- .../GraphQL/Mutations/UserMutations.cs | 1 + .../GraphQL/{Types => }/Scalars/JwtType.cs | 2 +- .../GraphQL/{Types => }/Scalars/SemverType.cs | 3 +- .../GraphQL/Types/GatewayInformation.cs | 7 +- .../Types/OAuth/BasicOAuthProviderInfo.cs | 28 ++++++++ .../Types/OAuth/FullOAuthProviderInfo.cs | 29 ++++++++ .../Types/{ => OAuth}/OAuthConnection.cs | 3 +- .../GraphQL/Types/OAuth/OAuthProviderInfos.cs | 66 +++++++++++++++++++ .../Types/OAuth/RedirectOAuthProviderInfo.cs | 27 ++++++++ .../Types/OAuth/ServerUrlOAuthProviderInfo.cs | 27 ++++++++ .../GraphQL/Types/User.cs | 1 + 16 files changed, 198 insertions(+), 19 deletions(-) rename src/Tgstation.Server.Host/GraphQL/{Types => }/Interceptors/RightsTypeInterceptor.cs (97%) rename src/Tgstation.Server.Host/GraphQL/{Types => }/Scalars/JwtType.cs (94%) rename src/Tgstation.Server.Host/GraphQL/{Types => }/Scalars/SemverType.cs (97%) create mode 100644 src/Tgstation.Server.Host/GraphQL/Types/OAuth/BasicOAuthProviderInfo.cs create mode 100644 src/Tgstation.Server.Host/GraphQL/Types/OAuth/FullOAuthProviderInfo.cs rename src/Tgstation.Server.Host/GraphQL/Types/{ => OAuth}/OAuthConnection.cs (94%) create mode 100644 src/Tgstation.Server.Host/GraphQL/Types/OAuth/OAuthProviderInfos.cs create mode 100644 src/Tgstation.Server.Host/GraphQL/Types/OAuth/RedirectOAuthProviderInfo.cs create mode 100644 src/Tgstation.Server.Host/GraphQL/Types/OAuth/ServerUrlOAuthProviderInfo.cs diff --git a/src/Tgstation.Server.Host/Authority/IUserAuthority.cs b/src/Tgstation.Server.Host/Authority/IUserAuthority.cs index b0a67681cd..4b67a3e55b 100644 --- a/src/Tgstation.Server.Host/Authority/IUserAuthority.cs +++ b/src/Tgstation.Server.Host/Authority/IUserAuthority.cs @@ -6,6 +6,7 @@ using Tgstation.Server.Api.Models.Request; using Tgstation.Server.Api.Rights; using Tgstation.Server.Host.Authority.Core; +using Tgstation.Server.Host.GraphQL.Types.OAuth; using Tgstation.Server.Host.Models; using Tgstation.Server.Host.Security; @@ -40,8 +41,8 @@ public interface IUserAuthority : IAuthority /// /// The of the . /// The for the operation. - /// A resulting in an of . - ValueTask> OAuthConnections(long userId, CancellationToken cancellationToken); + /// A resulting in an of . + ValueTask> OAuthConnections(long userId, CancellationToken cancellationToken); /// /// Gets all registered s. diff --git a/src/Tgstation.Server.Host/Authority/UserAuthority.cs b/src/Tgstation.Server.Host/Authority/UserAuthority.cs index 753a180d30..686eb10f47 100644 --- a/src/Tgstation.Server.Host/Authority/UserAuthority.cs +++ b/src/Tgstation.Server.Host/Authority/UserAuthority.cs @@ -102,7 +102,7 @@ public static Task> GetUsers( /// The for the operation. /// A resulting in a of the requested s. [DataLoader] - public static async ValueTask> GetOAuthConnections( + public static async ValueTask> GetOAuthConnections( IReadOnlyList userIds, IDatabaseContext databaseContext, CancellationToken cancellationToken) @@ -118,7 +118,7 @@ public static Task> GetUsers( return list.ToLookup( oauthConnection => oauthConnection.UserId, - x => new GraphQL.Types.OAuthConnection(x.ExternalUserId!, x.Provider)); + x => new GraphQL.Types.OAuth.OAuthConnection(x.ExternalUserId!, x.Provider)); } /// @@ -285,8 +285,8 @@ public IQueryable Queryable(bool includeJoins) => Queryable(includeJoins, false); /// - public async ValueTask> OAuthConnections(long userId, CancellationToken cancellationToken) - => new AuthorityResponse( + public async ValueTask> OAuthConnections(long userId, CancellationToken cancellationToken) + => new AuthorityResponse( await oAuthConnectionsDataLoader.LoadRequiredAsync(userId, cancellationToken)); /// diff --git a/src/Tgstation.Server.Host/Core/Application.cs b/src/Tgstation.Server.Host/Core/Application.cs index aa7753c05c..3a310a7ec9 100644 --- a/src/Tgstation.Server.Host/Core/Application.cs +++ b/src/Tgstation.Server.Host/Core/Application.cs @@ -57,10 +57,10 @@ using Tgstation.Server.Host.Database; using Tgstation.Server.Host.Extensions; using Tgstation.Server.Host.GraphQL; +using Tgstation.Server.Host.GraphQL.Interceptors; +using Tgstation.Server.Host.GraphQL.Scalars; using Tgstation.Server.Host.GraphQL.Subscriptions; using Tgstation.Server.Host.GraphQL.Types; -using Tgstation.Server.Host.GraphQL.Types.Interceptors; -using Tgstation.Server.Host.GraphQL.Types.Scalars; using Tgstation.Server.Host.IO; using Tgstation.Server.Host.Jobs; using Tgstation.Server.Host.Properties; diff --git a/src/Tgstation.Server.Host/GraphQL/Types/Interceptors/RightsTypeInterceptor.cs b/src/Tgstation.Server.Host/GraphQL/Interceptors/RightsTypeInterceptor.cs similarity index 97% rename from src/Tgstation.Server.Host/GraphQL/Types/Interceptors/RightsTypeInterceptor.cs rename to src/Tgstation.Server.Host/GraphQL/Interceptors/RightsTypeInterceptor.cs index 538c32b3ad..5fea9058e3 100644 --- a/src/Tgstation.Server.Host/GraphQL/Types/Interceptors/RightsTypeInterceptor.cs +++ b/src/Tgstation.Server.Host/GraphQL/Interceptors/RightsTypeInterceptor.cs @@ -6,10 +6,10 @@ using HotChocolate.Configuration; using HotChocolate.Types; using HotChocolate.Types.Descriptors.Definitions; - using Tgstation.Server.Api.Rights; +using Tgstation.Server.Host.GraphQL.Types; -namespace Tgstation.Server.Host.GraphQL.Types.Interceptors +namespace Tgstation.Server.Host.GraphQL.Interceptors { /// /// Fixes the names used for the default flags types in API rights. diff --git a/src/Tgstation.Server.Host/GraphQL/Mutations/Payloads/LoginPayload.cs b/src/Tgstation.Server.Host/GraphQL/Mutations/Payloads/LoginPayload.cs index 8d5cd5e683..407ebab3b7 100644 --- a/src/Tgstation.Server.Host/GraphQL/Mutations/Payloads/LoginPayload.cs +++ b/src/Tgstation.Server.Host/GraphQL/Mutations/Payloads/LoginPayload.cs @@ -1,6 +1,6 @@ using HotChocolate; using Tgstation.Server.Api.Models.Response; -using Tgstation.Server.Host.GraphQL.Types.Scalars; +using Tgstation.Server.Host.GraphQL.Scalars; using Tgstation.Server.Host.Models; namespace Tgstation.Server.Host.GraphQL.Mutations.Payloads diff --git a/src/Tgstation.Server.Host/GraphQL/Mutations/UserMutations.cs b/src/Tgstation.Server.Host/GraphQL/Mutations/UserMutations.cs index 5c51fd8bc3..77fc7e3445 100644 --- a/src/Tgstation.Server.Host/GraphQL/Mutations/UserMutations.cs +++ b/src/Tgstation.Server.Host/GraphQL/Mutations/UserMutations.cs @@ -13,6 +13,7 @@ using Tgstation.Server.Host.Authority; using Tgstation.Server.Host.GraphQL.Mutations.Payloads; using Tgstation.Server.Host.GraphQL.Types; +using Tgstation.Server.Host.GraphQL.Types.OAuth; using Tgstation.Server.Host.Models.Transformers; using Tgstation.Server.Host.Security; diff --git a/src/Tgstation.Server.Host/GraphQL/Types/Scalars/JwtType.cs b/src/Tgstation.Server.Host/GraphQL/Scalars/JwtType.cs similarity index 94% rename from src/Tgstation.Server.Host/GraphQL/Types/Scalars/JwtType.cs rename to src/Tgstation.Server.Host/GraphQL/Scalars/JwtType.cs index 47f10ccde3..170a744a42 100644 --- a/src/Tgstation.Server.Host/GraphQL/Types/Scalars/JwtType.cs +++ b/src/Tgstation.Server.Host/GraphQL/Scalars/JwtType.cs @@ -3,7 +3,7 @@ using HotChocolate.Language; using HotChocolate.Types; -namespace Tgstation.Server.Host.GraphQL.Types.Scalars +namespace Tgstation.Server.Host.GraphQL.Scalars { /// /// A for encoded JSON Web Tokens. diff --git a/src/Tgstation.Server.Host/GraphQL/Types/Scalars/SemverType.cs b/src/Tgstation.Server.Host/GraphQL/Scalars/SemverType.cs similarity index 97% rename from src/Tgstation.Server.Host/GraphQL/Types/Scalars/SemverType.cs rename to src/Tgstation.Server.Host/GraphQL/Scalars/SemverType.cs index dce31b4751..de8c1e530a 100644 --- a/src/Tgstation.Server.Host/GraphQL/Types/Scalars/SemverType.cs +++ b/src/Tgstation.Server.Host/GraphQL/Scalars/SemverType.cs @@ -2,10 +2,9 @@ using HotChocolate.Language; using HotChocolate.Types; - using Tgstation.Server.Common.Extensions; -namespace Tgstation.Server.Host.GraphQL.Types.Scalars +namespace Tgstation.Server.Host.GraphQL.Scalars { /// /// A for semantic s. diff --git a/src/Tgstation.Server.Host/GraphQL/Types/GatewayInformation.cs b/src/Tgstation.Server.Host/GraphQL/Types/GatewayInformation.cs index 727e38c5f1..9a1aee31cf 100644 --- a/src/Tgstation.Server.Host/GraphQL/Types/GatewayInformation.cs +++ b/src/Tgstation.Server.Host/GraphQL/Types/GatewayInformation.cs @@ -10,6 +10,7 @@ using Tgstation.Server.Api.Rights; using Tgstation.Server.Host.Components.Interop; using Tgstation.Server.Host.Configuration; +using Tgstation.Server.Host.GraphQL.Types.OAuth; using Tgstation.Server.Host.Properties; using Tgstation.Server.Host.Security; using Tgstation.Server.Host.Security.OAuth; @@ -148,12 +149,12 @@ public Version Version( /// Gets the information needed to perform open authentication with the . /// /// The to use. - /// A map of enabled s to their . - public IReadOnlyDictionary OAuthProviderInfos( + /// A map of enabled s to their . + public OAuthProviderInfos OAuthProviderInfos( [Service] IOAuthProviders oAuthProviders) { ArgumentNullException.ThrowIfNull(oAuthProviders); - return oAuthProviders.ProviderInfos(); + return new OAuthProviderInfos(oAuthProviders); } } } diff --git a/src/Tgstation.Server.Host/GraphQL/Types/OAuth/BasicOAuthProviderInfo.cs b/src/Tgstation.Server.Host/GraphQL/Types/OAuth/BasicOAuthProviderInfo.cs new file mode 100644 index 0000000000..f503d7adc8 --- /dev/null +++ b/src/Tgstation.Server.Host/GraphQL/Types/OAuth/BasicOAuthProviderInfo.cs @@ -0,0 +1,28 @@ +using System; + +using Tgstation.Server.Api.Models; + +namespace Tgstation.Server.Host.GraphQL.Types.OAuth +{ + /// + /// Basic OAuth provider info. + /// + public class BasicOAuthProviderInfo + { + /// + /// The client ID. + /// + public string ClientID { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The to build from. + public BasicOAuthProviderInfo(OAuthProviderInfo providerInfo) + { + ArgumentNullException.ThrowIfNull(providerInfo); + + ClientID = providerInfo.ClientId ?? throw new InvalidOperationException("ClientID not set!"); + } + } +} diff --git a/src/Tgstation.Server.Host/GraphQL/Types/OAuth/FullOAuthProviderInfo.cs b/src/Tgstation.Server.Host/GraphQL/Types/OAuth/FullOAuthProviderInfo.cs new file mode 100644 index 0000000000..cd750f665b --- /dev/null +++ b/src/Tgstation.Server.Host/GraphQL/Types/OAuth/FullOAuthProviderInfo.cs @@ -0,0 +1,29 @@ +using System; + +using Tgstation.Server.Api.Models; + +namespace Tgstation.Server.Host.GraphQL.Types.OAuth +{ + /// + /// OAuth provider info with a and . + /// + public sealed class FullOAuthProviderInfo : RedirectOAuthProviderInfo + { + /// + /// The remote service URL. + /// + public Uri ServerUrl { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The to build from. + public FullOAuthProviderInfo(OAuthProviderInfo providerInfo) + : base(providerInfo) + { + ArgumentNullException.ThrowIfNull(providerInfo); + + ServerUrl = providerInfo.ServerUrl ?? throw new InvalidOperationException("Missing OAuthProviderInfo ServerUrl!"); + } + } +} diff --git a/src/Tgstation.Server.Host/GraphQL/Types/OAuthConnection.cs b/src/Tgstation.Server.Host/GraphQL/Types/OAuth/OAuthConnection.cs similarity index 94% rename from src/Tgstation.Server.Host/GraphQL/Types/OAuthConnection.cs rename to src/Tgstation.Server.Host/GraphQL/Types/OAuth/OAuthConnection.cs index 7b0732fe92..cae66dec1a 100644 --- a/src/Tgstation.Server.Host/GraphQL/Types/OAuthConnection.cs +++ b/src/Tgstation.Server.Host/GraphQL/Types/OAuth/OAuthConnection.cs @@ -1,8 +1,7 @@ using System; - using Tgstation.Server.Api.Models; -namespace Tgstation.Server.Host.GraphQL.Types +namespace Tgstation.Server.Host.GraphQL.Types.OAuth { /// /// Represents a valid OAuth connection. diff --git a/src/Tgstation.Server.Host/GraphQL/Types/OAuth/OAuthProviderInfos.cs b/src/Tgstation.Server.Host/GraphQL/Types/OAuth/OAuthProviderInfos.cs new file mode 100644 index 0000000000..da9bffe4b3 --- /dev/null +++ b/src/Tgstation.Server.Host/GraphQL/Types/OAuth/OAuthProviderInfos.cs @@ -0,0 +1,66 @@ +using System; + +using Tgstation.Server.Api.Models; +using Tgstation.Server.Host.Security.OAuth; + +namespace Tgstation.Server.Host.GraphQL.Types.OAuth +{ + /// + /// Description of configured OAuth services. + /// + public sealed class OAuthProviderInfos + { + /// + /// https://discord.com. + /// + public BasicOAuthProviderInfo? Discord { get; } + + /// + /// https://github.com. + /// + public RedirectOAuthProviderInfo? GitHub { get; } + + /// + /// https://tgstation13.org. + /// + public RedirectOAuthProviderInfo? TGForums { get; } + + /// + /// https://www.keycloak.org. + /// + public FullOAuthProviderInfo? Keycloak { get; } + + /// + /// https://invisioncommunity.com. + /// + public FullOAuthProviderInfo? InvisionCommunity { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The to get data from. + public OAuthProviderInfos(IOAuthProviders oAuthProviders) + { + ArgumentNullException.ThrowIfNull(oAuthProviders); + + var dic = oAuthProviders.ProviderInfos(); + + TProviderInfo? TryBuild(OAuthProvider oAuthProvider, Func contructor) + where TProviderInfo : BasicOAuthProviderInfo + { + if (dic.TryGetValue(oAuthProvider, out var providerInfo)) + { + return contructor(providerInfo); + } + + return null; + } + + Discord = TryBuild(OAuthProvider.Discord, info => new BasicOAuthProviderInfo(info)); + GitHub = TryBuild(OAuthProvider.GitHub, info => new RedirectOAuthProviderInfo(info)); + TGForums = TryBuild(OAuthProvider.TGForums, info => new RedirectOAuthProviderInfo(info)); + Keycloak = TryBuild(OAuthProvider.Keycloak, info => new FullOAuthProviderInfo(info)); + InvisionCommunity = TryBuild(OAuthProvider.InvisionCommunity, info => new FullOAuthProviderInfo(info)); + } + } +} diff --git a/src/Tgstation.Server.Host/GraphQL/Types/OAuth/RedirectOAuthProviderInfo.cs b/src/Tgstation.Server.Host/GraphQL/Types/OAuth/RedirectOAuthProviderInfo.cs new file mode 100644 index 0000000000..ec0b00f6a0 --- /dev/null +++ b/src/Tgstation.Server.Host/GraphQL/Types/OAuth/RedirectOAuthProviderInfo.cs @@ -0,0 +1,27 @@ +using System; + +using Tgstation.Server.Api.Models; + +namespace Tgstation.Server.Host.GraphQL.Types.OAuth +{ + /// + /// OAuth provider info with a . + /// + public class RedirectOAuthProviderInfo : BasicOAuthProviderInfo + { + /// + /// The authentication server URL. + /// + public Uri RedirectUri { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The to build from. + public RedirectOAuthProviderInfo(OAuthProviderInfo providerInfo) + : base(providerInfo) + { + RedirectUri = providerInfo!.RedirectUri ?? throw new InvalidOperationException("RedirectUri not set!"); + } + } +} diff --git a/src/Tgstation.Server.Host/GraphQL/Types/OAuth/ServerUrlOAuthProviderInfo.cs b/src/Tgstation.Server.Host/GraphQL/Types/OAuth/ServerUrlOAuthProviderInfo.cs new file mode 100644 index 0000000000..2add1c2bae --- /dev/null +++ b/src/Tgstation.Server.Host/GraphQL/Types/OAuth/ServerUrlOAuthProviderInfo.cs @@ -0,0 +1,27 @@ +using System; + +using Tgstation.Server.Api.Models; + +namespace Tgstation.Server.Host.GraphQL.Types.OAuth +{ + /// + /// OAuth provider info with a . + /// + public sealed class ServerUrlOAuthProviderInfo : BasicOAuthProviderInfo + { + /// + /// The remote service URL. + /// + public Uri ServerUrl { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The to build from. + public ServerUrlOAuthProviderInfo(OAuthProviderInfo providerInfo) + : base(providerInfo) + { + ServerUrl = providerInfo!.ServerUrl ?? throw new InvalidOperationException("ServerUrl not set!"); + } + } +} diff --git a/src/Tgstation.Server.Host/GraphQL/Types/User.cs b/src/Tgstation.Server.Host/GraphQL/Types/User.cs index 7e993fd358..721fbe9eef 100644 --- a/src/Tgstation.Server.Host/GraphQL/Types/User.cs +++ b/src/Tgstation.Server.Host/GraphQL/Types/User.cs @@ -7,6 +7,7 @@ using Tgstation.Server.Host.Authority; using Tgstation.Server.Host.GraphQL.Interfaces; +using Tgstation.Server.Host.GraphQL.Types.OAuth; using Tgstation.Server.Host.Models.Transformers; using Tgstation.Server.Host.Security; From 8699ad16201be403e020bca01b398e49743be4ed Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Sun, 6 Oct 2024 13:26:02 -0400 Subject: [PATCH 07/10] Fix build warnings --- src/Tgstation.Server.Host/Authority/IUserAuthority.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Tgstation.Server.Host/Authority/IUserAuthority.cs b/src/Tgstation.Server.Host/Authority/IUserAuthority.cs index 4b67a3e55b..9e2a37da59 100644 --- a/src/Tgstation.Server.Host/Authority/IUserAuthority.cs +++ b/src/Tgstation.Server.Host/Authority/IUserAuthority.cs @@ -6,7 +6,6 @@ using Tgstation.Server.Api.Models.Request; using Tgstation.Server.Api.Rights; using Tgstation.Server.Host.Authority.Core; -using Tgstation.Server.Host.GraphQL.Types.OAuth; using Tgstation.Server.Host.Models; using Tgstation.Server.Host.Security; @@ -37,11 +36,11 @@ public interface IUserAuthority : IAuthority ValueTask> GetId(long id, bool includeJoins, bool allowSystemUser, CancellationToken cancellationToken); /// - /// Gets the s for the with a given . + /// Gets the s for the with a given . /// /// The of the . /// The for the operation. - /// A resulting in an of . + /// A resulting in an of . ValueTask> OAuthConnections(long userId, CancellationToken cancellationToken); /// From dab44e6a645bb5da2242289e59ecdb7883345ad1 Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Sun, 6 Oct 2024 13:46:53 -0400 Subject: [PATCH 08/10] Fix linter issue --- src/Tgstation.Server.Host/GraphQL/Types/OAuth/OAuthConnection.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Tgstation.Server.Host/GraphQL/Types/OAuth/OAuthConnection.cs b/src/Tgstation.Server.Host/GraphQL/Types/OAuth/OAuthConnection.cs index cae66dec1a..b073446c66 100644 --- a/src/Tgstation.Server.Host/GraphQL/Types/OAuth/OAuthConnection.cs +++ b/src/Tgstation.Server.Host/GraphQL/Types/OAuth/OAuthConnection.cs @@ -1,4 +1,5 @@ using System; + using Tgstation.Server.Api.Models; namespace Tgstation.Server.Host.GraphQL.Types.OAuth From 674276ea665b1ed9276d11b8de0713f81503a200 Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Sun, 6 Oct 2024 13:47:11 -0400 Subject: [PATCH 09/10] Case insensitive parse for OAuthConnections --- src/Tgstation.Server.Api/ApiHeaders.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tgstation.Server.Api/ApiHeaders.cs b/src/Tgstation.Server.Api/ApiHeaders.cs index acc673bf07..b023727bed 100644 --- a/src/Tgstation.Server.Api/ApiHeaders.cs +++ b/src/Tgstation.Server.Api/ApiHeaders.cs @@ -269,7 +269,7 @@ void AddError(HeaderErrorTypes headerType, string message) if (requestHeaders.Headers.TryGetValue(OAuthProviderHeader, out StringValues oauthProviderValues)) { var oauthProviderString = oauthProviderValues.First(); - if (Enum.TryParse(oauthProviderString, out var oauthProvider)) + if (Enum.TryParse(oauthProviderString, true, out var oauthProvider)) OAuthProvider = oauthProvider; else AddError(HeaderErrorTypes.OAuthProvider, "Invalid OAuth provider!"); From 11de85915c6566b05d54fd38ba5370706c7146e4 Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Sun, 6 Oct 2024 15:38:13 -0400 Subject: [PATCH 10/10] Fix build --- .../GQL/Queries/OAuthInformation.graphql | 18 ------------------ .../UnauthenticatedServerInformation.graphql | 8 -------- .../Live/TestLiveServer.cs | 17 +---------------- 3 files changed, 1 insertion(+), 42 deletions(-) delete mode 100644 src/Tgstation.Server.Client.GraphQL/GQL/Queries/OAuthInformation.graphql diff --git a/src/Tgstation.Server.Client.GraphQL/GQL/Queries/OAuthInformation.graphql b/src/Tgstation.Server.Client.GraphQL/GQL/Queries/OAuthInformation.graphql deleted file mode 100644 index e32a409b60..0000000000 --- a/src/Tgstation.Server.Client.GraphQL/GQL/Queries/OAuthInformation.graphql +++ /dev/null @@ -1,18 +0,0 @@ -query OAuthInformation { - swarm { - currentNode { - gateway { - information { - oAuthProviderInfos { - key - value { - clientId - redirectUri - serverUrl - } - } - } - } - } - } -} diff --git a/src/Tgstation.Server.Client.GraphQL/GQL/Queries/UnauthenticatedServerInformation.graphql b/src/Tgstation.Server.Client.GraphQL/GQL/Queries/UnauthenticatedServerInformation.graphql index d2a45ce425..be535b84a0 100644 --- a/src/Tgstation.Server.Client.GraphQL/GQL/Queries/UnauthenticatedServerInformation.graphql +++ b/src/Tgstation.Server.Client.GraphQL/GQL/Queries/UnauthenticatedServerInformation.graphql @@ -4,14 +4,6 @@ query UnauthenticatedServerInformation { gateway { information { majorGraphQLApiVersion - oAuthProviderInfos { - key - value { - clientId - redirectUri - serverUrl - } - } } } } diff --git a/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs b/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs index dec3a1a96d..2092e0819f 100644 --- a/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs +++ b/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs @@ -1402,23 +1402,8 @@ async Task TestTgsInternal(bool openDreamOnly, CancellationToken hardCancellatio // test getting server info var unAuthedMultiClient = new MultiServerClient(firstAdminRestClient, unauthenticatedGraphQLClient); - await unAuthedMultiClient.ExecuteReadOnlyConfirmEquivalence( - restClient => restClient.ServerInformation(cancellationToken), + await unauthenticatedGraphQLClient.RunQueryEnsureNoErrors( gqlClient => gqlClient.UnauthenticatedServerInformation.ExecuteAsync(cancellationToken), - (restServerInfo, gqlServerInfo) => - { - var result = restServerInfo.OAuthProviderInfos == gqlServerInfo.Swarm.CurrentNode.Gateway.Information.OAuthProviderInfos - || restServerInfo.OAuthProviderInfos.All(kvp => - { - var info = gqlServerInfo.Swarm.CurrentNode.Gateway.Information.OAuthProviderInfos.FirstOrDefault(x => (int)x.Key == (int)kvp.Key); - return info != null - && info.Value.ServerUrl == kvp.Value.ServerUrl - && info.Value.ClientId == kvp.Value.ClientId - && info.Value.RedirectUri == kvp.Value.RedirectUri; - }); - - return result; - }, cancellationToken); var testObserver = new HoldLastObserver>();