Skip to content

Commit

Permalink
Merge pull request #1954 from tgstation/GraphQLGitHub [RESTDeploy][GQ…
Browse files Browse the repository at this point in the history
…LDeploy]

Initial GraphQL API release. Also releasing REST API v10.10
  • Loading branch information
Cyberboss authored Oct 6, 2024
2 parents e82b1a6 + 11de859 commit 0c01d9f
Show file tree
Hide file tree
Showing 39 changed files with 423 additions and 117 deletions.
9 changes: 5 additions & 4 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.

Expand Down
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.)
124 changes: 114 additions & 10 deletions .github/workflows/ci-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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: |
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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 }}

Expand All @@ -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 GraphQL Schema
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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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 }}
Expand All @@ -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:
Expand Down
3 changes: 2 additions & 1 deletion build/Version.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
<PropertyGroup>
<TgsCoreVersion>6.10.0</TgsCoreVersion>
<TgsConfigVersion>5.3.0</TgsConfigVersion>
<TgsApiVersion>10.10.0</TgsApiVersion>
<TgsRestVersion>10.10.0</TgsRestVersion>
<TgsGraphQLVersion>0.1.0</TgsGraphQLVersion>
<TgsCommonLibraryVersion>7.0.0</TgsCommonLibraryVersion>
<TgsApiLibraryVersion>16.0.0</TgsApiLibraryVersion>
<TgsClientVersion>19.0.0</TgsClientVersion>
Expand Down
2 changes: 1 addition & 1 deletion src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion src/Tgstation.Server.Api/ApiHeaders.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<OAuthProvider>(oauthProviderString, out var oauthProvider))
if (Enum.TryParse<OAuthProvider>(oauthProviderString, true, out var oauthProvider))
OAuthProvider = oauthProvider;
else
AddError(HeaderErrorTypes.OAuthProvider, "Invalid OAuth provider!");
Expand Down
2 changes: 1 addition & 1 deletion src/Tgstation.Server.Api/Tgstation.Server.Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<Target Name="ApplyApiVersionAttribute" BeforeTargets="CoreCompile">
<ItemGroup>
<AssemblyAttributes Include="Tgstation.Server.Api.Properties.ApiVersionAttribute">
<_Parameter1>$(TgsApiVersion)</_Parameter1>
<_Parameter1>$(TgsRestVersion)</_Parameter1>
</AssemblyAttributes>
</ItemGroup>

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,7 @@ query UnauthenticatedServerInformation {
currentNode {
gateway {
information {
majorApiVersion
oAuthProviderInfos {
key
value {
clientId
redirectUri
serverUrl
}
}
majorGraphQLApiVersion
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

<PropertyGroup>
<TargetFramework>$(TgsFrameworkVersion)</TargetFramework>
<Version>$(TgsApiVersion)</Version>
<!-- Opting to not version this because as far as I can tell, it'll only be used locally -->
<Nullable>enable</Nullable>
</PropertyGroup>

Expand Down
6 changes: 3 additions & 3 deletions src/Tgstation.Server.Host/Authority/IUserAuthority.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ public interface IUserAuthority : IAuthority
ValueTask<AuthorityResponse<User>> GetId(long id, bool includeJoins, bool allowSystemUser, CancellationToken cancellationToken);

/// <summary>
/// Gets the <see cref="OAuthConnection"/>s for the <see cref="User"/> with a given <paramref name="userId"/>.
/// Gets the <see cref="GraphQL.Types.OAuth.OAuthConnection"/>s for the <see cref="User"/> with a given <paramref name="userId"/>.
/// </summary>
/// <param name="userId">The <see cref="EntityId.Id"/> of the <see cref="User"/>.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in an <see cref="global::System.Array"/> of <see cref="GraphQL.Types.OAuthConnection"/> <see cref="AuthorityResponse{TResult}"/>.</returns>
ValueTask<AuthorityResponse<GraphQL.Types.OAuthConnection[]>> OAuthConnections(long userId, CancellationToken cancellationToken);
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in an <see cref="global::System.Array"/> of <see cref="GraphQL.Types.OAuth.OAuthConnection"/> <see cref="AuthorityResponse{TResult}"/>.</returns>
ValueTask<AuthorityResponse<GraphQL.Types.OAuth.OAuthConnection[]>> OAuthConnections(long userId, CancellationToken cancellationToken);

/// <summary>
/// Gets all registered <see cref="User"/>s.
Expand Down
8 changes: 4 additions & 4 deletions src/Tgstation.Server.Host/Authority/UserAuthority.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public static Task<Dictionary<long, User>> GetUsers(
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in a <see cref="Dictionary{TKey, TValue}"/> of the requested <see cref="User"/>s.</returns>
[DataLoader]
public static async ValueTask<ILookup<long, GraphQL.Types.OAuthConnection>> GetOAuthConnections(
public static async ValueTask<ILookup<long, GraphQL.Types.OAuth.OAuthConnection>> GetOAuthConnections(
IReadOnlyList<long> userIds,
IDatabaseContext databaseContext,
CancellationToken cancellationToken)
Expand All @@ -118,7 +118,7 @@ public static Task<Dictionary<long, User>> 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));
}

/// <summary>
Expand Down Expand Up @@ -285,8 +285,8 @@ public IQueryable<User> Queryable(bool includeJoins)
=> Queryable(includeJoins, false);

/// <inheritdoc />
public async ValueTask<AuthorityResponse<GraphQL.Types.OAuthConnection[]>> OAuthConnections(long userId, CancellationToken cancellationToken)
=> new AuthorityResponse<GraphQL.Types.OAuthConnection[]>(
public async ValueTask<AuthorityResponse<GraphQL.Types.OAuth.OAuthConnection[]>> OAuthConnections(long userId, CancellationToken cancellationToken)
=> new AuthorityResponse<GraphQL.Types.OAuth.OAuthConnection[]>(
await oAuthConnectionsDataLoader.LoadRequiredAsync(userId, cancellationToken));

/// <inheritdoc />
Expand Down
4 changes: 2 additions & 2 deletions src/Tgstation.Server.Host/Core/Application.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading

0 comments on commit 0c01d9f

Please sign in to comment.