Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More GraphQL Work #1936

Merged
merged 110 commits into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from 108 commits
Commits
Show all changes
110 commits
Select commit Hold shift + click to select a range
9ac92d1
Further GraphQL API Implementation
Cyberboss Sep 11, 2024
ed2de22
More GraphQL development
Cyberboss Sep 11, 2024
74fa26f
Implement baby's first pagination on `User`s
Cyberboss Sep 11, 2024
b2fa1c0
Fix VS complaining about a duplicate type name
Cyberboss Sep 11, 2024
99e6220
Fix issues with Swagger generation
Cyberboss Sep 11, 2024
a5b4afb
Add a basic GQL errored login test
Cyberboss Sep 11, 2024
edd41aa
Apply code suggestion
Cyberboss Sep 11, 2024
84f5ee7
Properly setup GraphQL `IUserName` for TGS user
Cyberboss Sep 11, 2024
ede12a6
Enable @defer
Cyberboss Sep 11, 2024
673bfcb
Upgrade to HotChocolate 14 RC. Setup DataLoaders, Filtering, and Sort…
Cyberboss Sep 12, 2024
a2ed580
Add missing dependency comment
Cyberboss Sep 12, 2024
ba5d5e4
Fix build warnings
Cyberboss Sep 12, 2024
d3168f9
NotFound/Gone don't throw for GraphQL AuthorityInvokers
Cyberboss Sep 12, 2024
27e668d
Move API specific `IAuthorityInvoker`s out of Core namespace
Cyberboss Sep 12, 2024
e2abe41
Fix a null referencing issue
Cyberboss Sep 12, 2024
a96c6dc
Split `AuthorityInvoker` into GraphQL and REST
Cyberboss Sep 12, 2024
194f53d
Document a warning suppression
Cyberboss Sep 12, 2024
429ca72
Merge branch 'dev' into MoreGraphQL
Cyberboss Sep 12, 2024
12ba7d4
Cleanup where the RemoteGateway exception is thrown
Cyberboss Sep 12, 2024
e218853
Merge branch 'PrivateKeySharing' into MoreGraphQL
Cyberboss Sep 13, 2024
d94892c
`AddQueryFieldToMutationPayloads`
Cyberboss Sep 13, 2024
6494f35
Simplify a `new` expression
Cyberboss Sep 13, 2024
0744c88
Fix semver type specification
Cyberboss Sep 13, 2024
8765916
Merge branch 'dev' into MoreGraphQL
Cyberboss Sep 13, 2024
68c6ba3
Fix @authorize directives not showing up.
Cyberboss Sep 13, 2024
69a107e
.csproj cleanup
Cyberboss Sep 13, 2024
d143411
Disable the cost analyzer
Cyberboss Sep 14, 2024
30f3bd5
API surface restructuring for GraphQL. Lockdown some API information …
Cyberboss Sep 14, 2024
dcedf3a
Implement OAuthConnections loading for GraphQL
Cyberboss Sep 14, 2024
886a166
Give SwarmNode a specific `NodeId` field and unmangle `Identifier`
Cyberboss Sep 14, 2024
614a299
Cleanup `Program`. Add bypass for update path/host watchdog args
Cyberboss Sep 14, 2024
0f5841a
Fix RawRequestTests assertions
Cyberboss Sep 14, 2024
0c34ff1
Apply code suggestions
Cyberboss Sep 14, 2024
a8d8ee8
Node auth cleanup + some user group implementation
Cyberboss Sep 14, 2024
308a08d
More UserGroup querying work
Cyberboss Sep 14, 2024
ff262b2
Improve `LoginPayload`
Cyberboss Sep 14, 2024
f6c0929
Fix UTF file BOM thingy
Cyberboss Sep 14, 2024
6e4aa50
Implement `UserGroup.UsersQueryable`
Cyberboss Sep 14, 2024
a131fea
Changing port allocation tactics for the millionth time
Cyberboss Sep 14, 2024
0166adc
Do not catch `ArgumentException`s in this retry loop
Cyberboss Sep 15, 2024
0c0bf28
Do not enable BananaCakePop without `HostApiDocumentation`
Cyberboss Sep 15, 2024
90d0d09
Link GraphQL API documentation
Cyberboss Sep 15, 2024
269476c
Revert "Disable the cost analyzer"
Cyberboss Sep 15, 2024
bca2a22
Apply code suggestion
Cyberboss Sep 15, 2024
73b038c
Correct some documentation comments
Cyberboss Sep 15, 2024
c420b63
Implement permission set querying and authority
Cyberboss Sep 15, 2024
bfe12f0
Do not enforce cost limits when debugging
Cyberboss Sep 15, 2024
547fb9d
Use IUserGroupAuthority functions in the controller
Cyberboss Sep 15, 2024
f9587f4
Fix UserGroupController permissions declaration
Cyberboss Sep 15, 2024
3c85d7d
Remove unused GQL field
Cyberboss Sep 15, 2024
1bd6286
Fix some naming issues with Connections
Cyberboss Sep 15, 2024
5b135fb
Makes sense to have recursive lookups here. Cost analyzer should be p…
Cyberboss Sep 15, 2024
998a635
Having `PermissionSet` as a `Node` causes complex issues with mutations
Cyberboss Sep 15, 2024
8c9904f
Correct a documentation comment
Cyberboss Sep 15, 2024
f796969
Setup skeleton create user mutations
Cyberboss Sep 15, 2024
3ffbb74
Better support NoContent results in authorities
Cyberboss Sep 15, 2024
9019396
Clean up GraphQL username retrieval
Cyberboss Sep 15, 2024
0160d17
Implement GraphQL user creation, `UserAuthority.Update`, and make con…
Cyberboss Sep 15, 2024
b802596
Apply a bunch of code suggestions
Cyberboss Sep 15, 2024
8e030ed
Some code cleanup regarding port allocation
Cyberboss Sep 15, 2024
c9c24a5
Flesh out GraphQL client
Cyberboss Sep 18, 2024
a313797
Apply code suggestions
Cyberboss Sep 18, 2024
55c1795
Implement remaining user mutations
Cyberboss Sep 18, 2024
fa2fb00
Skeleton UserGroup mutations
Cyberboss Sep 18, 2024
cc5ddc2
Allow null permission sets and enabled status on user/group creation
Cyberboss Sep 19, 2024
5f0fa33
Fix invalid AuthenticationContexts being able to be dispatched to GQL…
Cyberboss Sep 19, 2024
f37883a
Hopefully fix issues with auth pipeline for good
Cyberboss Sep 20, 2024
c6f30f7
Fix a CA1506 warning
Cyberboss Sep 21, 2024
2223063
Apply code suggestion
Cyberboss Sep 21, 2024
f9218e6
Fix naming of rights flags fields
Cyberboss Sep 21, 2024
07a317e
Apply some code suggestions
Cyberboss Sep 21, 2024
8139422
Fix documentation comments for `IGraphQLAuthorityInvoker` params
Cyberboss Sep 21, 2024
8ccfd68
Note about how `updateUsers` can remove users from groups
Cyberboss Sep 21, 2024
25a0e49
Document user group mutations
Cyberboss Sep 21, 2024
2c3542a
Remember the basics:
Cyberboss Sep 21, 2024
af21ba5
Implement user group mutations
Cyberboss Sep 21, 2024
36694ec
Simplify REST paginated result transformations
Cyberboss Sep 21, 2024
dc03964
Fix some logging placeholders
Cyberboss Sep 21, 2024
2ba4740
Fix the EFCore warning for unordered queryables. Will need to revisit…
Cyberboss Sep 22, 2024
4ca97d2
Rework the 0-length password on create with OAuth scenario to be a li…
Cyberboss Sep 22, 2024
1d7e12d
Fix BOM
Cyberboss Sep 22, 2024
eb2a5c0
Apply code suggestion
Cyberboss Sep 22, 2024
3fc08cc
Fix cyclomatic complexity warning
Cyberboss Sep 22, 2024
6b276f6
Breakup `UserMutations` more to mitigate potential bad requests
Cyberboss Sep 22, 2024
00467fd
Primary constructors cannot have readonly parameters. Not worth using.
Cyberboss Sep 22, 2024
b92af3f
Make page sizes consistent across REST/GQL
Cyberboss Sep 22, 2024
da15190
Fix issues with `RightsTypeInterceptor`
Cyberboss Sep 22, 2024
92b007d
Correct bad argument assertions
Cyberboss Sep 22, 2024
8b09eb7
Implement SxS GraphQL users tests
Cyberboss Sep 22, 2024
3c2ba81
Add missing newline
Cyberboss Sep 22, 2024
d7c5374
Fold `TokenValidator` into `AuthenticationContextFactory`
Cyberboss Sep 23, 2024
d0d2db5
Implement basic session invalidation subscriptions
Cyberboss Sep 24, 2024
af60f4f
Fix warnings, test build errors, and disable IDE0290
Cyberboss Sep 24, 2024
0b87147
Fix issue with GQL auth and no roles
Cyberboss Sep 24, 2024
e14d998
Correct GQL Authorize policies
Cyberboss Sep 24, 2024
1931b25
Fix build warning
Cyberboss Sep 24, 2024
f38a21f
Move to saner namespace
Cyberboss Sep 24, 2024
14bc1ac
Fix Linux live tests user count being off
Cyberboss Sep 24, 2024
e5f08c3
Split up which jobs `TGS_TEST_GRAPHQL` is set in more
Cyberboss Sep 24, 2024
5faf6ee
Add missing subscription `CancellationToken`
Cyberboss Sep 24, 2024
1cb77e1
Add `User` subscriptions without event senders
Cyberboss Sep 24, 2024
89770a6
Don't bring database models into the GraphQL type system
Cyberboss Sep 24, 2024
4ccc773
Make extension method for authentication errors
Cyberboss Sep 24, 2024
6de91eb
Very basic subscription tests and client support
Cyberboss Sep 24, 2024
0b966c6
Fixup `UserSubscriptions`
Cyberboss Sep 25, 2024
36ee7d7
Implement user subscription topic sending
Cyberboss Sep 25, 2024
33c2e53
Update HotChocolate
Cyberboss Sep 25, 2024
333e896
User subscription tests
Cyberboss Sep 25, 2024
b36ede9
Work around a race condition
Cyberboss Sep 25, 2024
a979c49
Workaround for https://github.com/ChilliCream/graphql-platform/issues…
Cyberboss Sep 26, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/ci-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,6 @@ jobs:
configuration: ["Debug", "Release"]
env:
TGS_TELEMETRY_KEY_FILE: C:/tgs_telemetry_key.txt
TGS_TEST_GRAPHQL: true
runs-on: windows-latest
steps:
- name: Setup dotnet
Expand Down Expand Up @@ -627,6 +626,7 @@ jobs:
run: |
echo "TGS_TEST_DATABASE_TYPE=PostgresSql" >> $GITHUB_ENV
echo "TGS_TEST_CONNECTION_STRING=Application Name=tgstation-server;Host=127.0.0.1;Username=$USER;Database=TGS__${{ matrix.watchdog-type }}_${{ matrix.configuration }}" >> $GITHUB_ENV
echo "TGS_TEST_GRAPHQL=true" >> $GITHUB_ENV

- name: Setup MariaDB
uses: ankane/setup-mariadb@v1
Expand All @@ -638,6 +638,7 @@ jobs:
run: |
echo "TGS_TEST_DATABASE_TYPE=MariaDB" >> $GITHUB_ENV
echo "TGS_TEST_CONNECTION_STRING=Server=127.0.0.1;uid=root;database=tgs__${{ matrix.watchdog-type }}_${{ matrix.configuration }}" >> $GITHUB_ENV
echo "TGS_TEST_GRAPHQL=true" >> $GITHUB_ENV

- name: Setup MySQL
uses: ankane/setup-mysql@v1
Expand All @@ -657,6 +658,7 @@ jobs:
TGS_CONNSTRING_VALUE="Server=(localdb)\MSSQLLocalDB;Encrypt=false;Integrated Security=true;Initial Catalog=TGS_${{ matrix.watchdog-type }}_${{ matrix.configuration }};Application Name=tgstation-server"
echo "TGS_TEST_CONNECTION_STRING=$(echo $TGS_CONNSTRING_VALUE)" >> $GITHUB_ENV
echo "TGS_TEST_DATABASE_TYPE=SqlServer" >> $GITHUB_ENV
echo "TGS_TEST_GRAPHQL=true" >> $GITHUB_ENV

- name: Checkout (Branch)
uses: actions/checkout@v4
Expand Down Expand Up @@ -855,6 +857,7 @@ jobs:
run: |
echo "TGS_TEST_DATABASE_TYPE=Sqlite" >> $GITHUB_ENV
echo "TGS_TEST_CONNECTION_STRING=Data Source=TGS_${{ matrix.watchdog-type }}_${{ matrix.configuration }}.sqlite3;Mode=ReadWriteCreate" >> $GITHUB_ENV
echo "TGS_TEST_GRAPHQL=true" >> $GITHUB_ENV

- name: Set PostgresSql Connection Info
if: ${{ matrix.database-type == 'PostgresSql' }}
Expand All @@ -874,6 +877,7 @@ jobs:
echo "TGS_TEST_DATABASE_TYPE=MySql" >> $GITHUB_ENV
echo "TGS_TEST_CONNECTION_STRING=Server=127.0.0.1;Port=3307;uid=root;pwd=mysql;database=tgs__${{ matrix.watchdog-type }}_${{ matrix.configuration }}" >> $GITHUB_ENV
echo "Database__ServerVersion=5.7.31" >> $GITHUB_ENV
echo "TGS_TEST_GRAPHQL=true" >> $GITHUB_ENV

- name: Set General__UseBasicWatchdog
if: ${{ matrix.watchdog-type == 'Basic' }}
Expand Down
6 changes: 3 additions & 3 deletions build/Version.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
<PropertyGroup>
<TgsCoreVersion>6.10.0</TgsCoreVersion>
<TgsConfigVersion>5.2.0</TgsConfigVersion>
<TgsApiVersion>10.9.0</TgsApiVersion>
<TgsApiVersion>10.10.0</TgsApiVersion>
<TgsCommonLibraryVersion>7.0.0</TgsCommonLibraryVersion>
<TgsApiLibraryVersion>15.0.0</TgsApiLibraryVersion>
<TgsClientVersion>18.0.0</TgsClientVersion>
<TgsApiLibraryVersion>16.0.0</TgsApiLibraryVersion>
<TgsClientVersion>19.0.0</TgsClientVersion>
<TgsDmapiVersion>7.3.0</TgsDmapiVersion>
<TgsInteropVersion>5.10.0</TgsInteropVersion>
<TgsHostWatchdogVersion>1.5.0</TgsHostWatchdogVersion>
Expand Down
3 changes: 1 addition & 2 deletions build/analyzers.ruleset
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="myrules" Description="My rule set" ToolsVersion="17.0">
<Rules AnalyzerId="AsyncUsageAnalyzers" RuleNamespace="AsyncUsageAnalyzers">
<Rule Id="UseConfigureAwait" Action="Warning" />
</Rules>
<Rules AnalyzerId="Microsoft.Analyzers.ManagedCodeAnalysis" RuleNamespace="Microsoft.Rules.Managed">
<Rule Id="CA1000" Action="Warning" />
<Rule Id="CA1001" Action="Warning" />
<Rule Id="CA1002" Action="None" />
<Rule Id="CA1003" Action="Warning" />
<Rule Id="CA1004" Action="Warning" />
<Rule Id="CA1005" Action="Warning" />
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 @@ -76,7 +76,7 @@ public sealed class ApiHeaders
/// <summary>
/// A <see cref="char"/> <see cref="Array"/> containing the ':' <see cref="char"/>.
/// </summary>
static readonly char[] ColonSeparator = new char[] { ':' };
static readonly char[] ColonSeparator = [':'];

/// <summary>
/// The instance <see cref="EntityId.Id"/> being accessed.
Expand Down
6 changes: 6 additions & 0 deletions src/Tgstation.Server.Api/Models/ErrorCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -663,5 +663,11 @@ public enum ErrorCode : uint
/// </summary>
[Description("Provided repository username doesn't match the user of the corresponding access token!")]
RepoTokenUsernameMismatch,

/// <summary>
/// Attempted to make a cross swarm server request using the GraphQL API.
/// </summary>
[Description("GraphQL swarm remote gateways not implemented!")]
RemoteGatewaysNotImplemented,
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,6 @@ public abstract class ServerInformationBase
/// Limits the locations instances may be created or attached from.
/// </summary>
[ResponseOptions]
public ICollection<string>? ValidInstancePaths { get; set; }
public List<string>? ValidInstancePaths { get; set; }
}
}
9 changes: 1 addition & 8 deletions src/Tgstation.Server.Api/Models/Internal/SwarmServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Tgstation.Server.Api.Models.Internal
/// <summary>
/// Information about a server in the swarm.
/// </summary>
public abstract class SwarmServer : IEquatable<SwarmServer>
public abstract class SwarmServer
{
/// <summary>
/// The public address of the server.
Expand Down Expand Up @@ -47,12 +47,5 @@ protected SwarmServer(SwarmServer copy)
PublicAddress = copy.PublicAddress;
Identifier = copy.Identifier;
}

/// <inheritdoc />
public bool Equals(SwarmServer other)
=> other != null
&& other.Identifier == Identifier
&& other.PublicAddress == PublicAddress
&& other.Address == Address;
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
using System;

using Tgstation.Server.Api.Models.Response;
using Tgstation.Server.Api.Models.Response;

namespace Tgstation.Server.Api.Models.Internal
{
/// <summary>
/// Represents information about a running <see cref="SwarmServer"/>.
/// </summary>
public class SwarmServerInformation : SwarmServer, IEquatable<SwarmServerInformation>
public class SwarmServerInformation : SwarmServer
{
/// <summary>
/// If the <see cref="SwarmServerResponse"/> is the controller.
Expand All @@ -30,10 +28,5 @@ public SwarmServerInformation(SwarmServerInformation copy)
{
Controller = copy.Controller;
}

/// <inheritdoc />
public bool Equals(SwarmServerInformation other)
=> base.Equals(other)
&& other.Controller == Controller;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Tgstation.Server.Api.Models.Response
/// <summary>
/// Represents basic server information.
/// </summary>
public sealed class ServerInformationResponse : Internal.LocalServerInformation
public sealed class ServerInformationResponse : Internal.ServerInformationBase
{
/// <summary>
/// The version of the host.
Expand All @@ -23,6 +23,16 @@ public sealed class ServerInformationResponse : Internal.LocalServerInformation
/// </summary>
public Version? DMApiVersion { get; set; }

/// <summary>
/// If the server is running on a windows operating system.
/// </summary>
public bool WindowsHost { get; set; }

/// <summary>
/// Map of <see cref="OAuthProvider"/> to the <see cref="OAuthProviderInfo"/> for them.
/// </summary>
public Dictionary<OAuthProvider, OAuthProviderInfo>? OAuthProviderInfos { get; set; }

/// <summary>
/// If there is a server update in progress.
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion src/Tgstation.Server.Api/Models/UserName.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public override string? Name
/// <typeparam name="TResultType">The child of <see cref="UserName"/> to create.</typeparam>
/// <returns>A new <typeparamref name="TResultType"/> copied from <see langword="this"/>.</returns>
protected virtual TResultType CreateUserName<TResultType>()
where TResultType : UserName, new() => new TResultType
where TResultType : UserName, new() => new()
{
Id = Id,
Name = Name,
Expand Down
16 changes: 5 additions & 11 deletions src/Tgstation.Server.Api/Rights/RightsHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,13 @@ public static RightsType TypeToRight<TRight>()
/// </summary>
/// <typeparam name="TRight">The <see cref="RightsType"/>.</typeparam>
/// <param name="right">The <typeparamref name="TRight"/>.</param>
/// <returns>A <see cref="string"/> representing the claim role name.</returns>
public static string RoleNames<TRight>(TRight right)
/// <returns>Am <see cref="IEnumerable{T}"/> of <see cref="string"/>s representing the claim role names.</returns>
public static IEnumerable<string> RoleNames<TRight>(TRight right)
where TRight : Enum
{
IEnumerable<string> GetRoleNames()
{
foreach (Enum rightValue in Enum.GetValues(right.GetType()))
if (Convert.ToInt32(rightValue, CultureInfo.InvariantCulture) != 0 && right.HasFlag(rightValue))
yield return String.Concat(typeof(TRight).Name, '.', rightValue.ToString());
}

var names = GetRoleNames();
return String.Join(",", names);
foreach (Enum rightValue in Enum.GetValues(right.GetType()))
if (Convert.ToInt32(rightValue, CultureInfo.InvariantCulture) != 0 && right.HasFlag(rightValue))
yield return String.Concat(typeof(TRight).Name, '.', rightValue.ToString());
}

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion src/Tgstation.Server.Client.GraphQL/.graphqlrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"transportProfiles": [
{
"default": "Http",
"subscription": "WebSocket"
"subscription": "Http"
}
]
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
using System;
using System.Net.Http.Headers;
using System.Threading.Tasks;

using Microsoft.Extensions.Logging;

using StrawberryShake;

using Tgstation.Server.Common.Extensions;

namespace Tgstation.Server.Client.GraphQL
{
/// <inheritdoc cref="IAuthenticatedGraphQLServerClient" />
sealed class AuthenticatedGraphQLServerClient : GraphQLServerClient, IAuthenticatedGraphQLServerClient
{
/// <inheritdoc />
public ITransferClient TransferClient => restClient!.Transfer;

/// <summary>
/// A <see cref="Func{T, TResult}"/> that takes a bearer token as input and outputs a <see cref="ITransferClient"/> that uses it.
/// </summary>
readonly Func<string, IRestServerClient>? getRestClientForToken;

/// <summary>
/// The current <see cref="IRestServerClient"/>.
/// </summary>
IRestServerClient? restClient;

/// <summary>
/// Initializes a new instance of the <see cref="AuthenticatedGraphQLServerClient"/> class.
/// </summary>
/// <param name="graphQLClient">The <see cref="IGraphQLClient"/> to use.</param>
/// <param name="serviceProvider">The <see cref="IAsyncDisposable"/> to use.</param>
/// <param name="logger">The <see cref="ILogger"/> to use.</param>
/// <param name="restClient">The value of <see cref="restClient"/>.</param>
public AuthenticatedGraphQLServerClient(
IGraphQLClient graphQLClient,
IAsyncDisposable serviceProvider,
ILogger<GraphQLServerClient> logger,
IRestServerClient restClient)
: base(graphQLClient, serviceProvider, logger)
{
this.restClient = restClient ?? throw new ArgumentNullException(nameof(restClient));
}

/// <summary>
/// Initializes a new instance of the <see cref="AuthenticatedGraphQLServerClient"/> class.
/// </summary>
/// <param name="graphQLClient">The <see cref="IGraphQLClient"/> to use.</param>
/// <param name="serviceProvider">The <see cref="IAsyncDisposable"/> to use.</param>
/// <param name="logger">The <see cref="ILogger"/> to use.</param>
/// <param name="setAuthenticationHeader">The <see cref="Action{T}"/> to call to set the async local <see cref="AuthenticationHeaderValue"/> for requests.</param>
/// <param name="basicCredentialsHeader">The basic <see cref="AuthenticationHeaderValue"/> to use for reauthentication.</param>
/// <param name="loginResult">The <see cref="ILoginResult"/> <see cref="IOperationResult{TResultData}"/> containing the initial JWT to use.</param>
/// <param name="getRestClientForToken">The value of <see cref="getRestClientForToken"/>.</param>
public AuthenticatedGraphQLServerClient(
IGraphQLClient graphQLClient,
IAsyncDisposable serviceProvider,
ILogger<GraphQLServerClient> logger,
Action<AuthenticationHeaderValue> setAuthenticationHeader,
AuthenticationHeaderValue? basicCredentialsHeader,
IOperationResult<ILoginResult> loginResult,
Func<string, IRestServerClient> getRestClientForToken)
: base(
graphQLClient,
serviceProvider,
logger,
setAuthenticationHeader,
basicCredentialsHeader,
loginResult)
{
this.getRestClientForToken = getRestClientForToken ?? throw new ArgumentNullException(nameof(getRestClientForToken));
restClient = getRestClientForToken(loginResult.Data!.Login.Bearer!.EncodedToken);
}

/// <inheritdoc />
public sealed override ValueTask DisposeAsync()
#pragma warning disable CA2012 // Use ValueTasks correctly
=> ValueTaskExtensions.WhenAll(
base.DisposeAsync(),
restClient!.DisposeAsync());
#pragma warning restore CA2012 // Use ValueTasks correctly

/// <inheritdoc />
protected sealed override async ValueTask<AuthenticationHeaderValue> CreateUpdatedAuthenticationHeader(string bearer)
{
var baseTask = base.CreateUpdatedAuthenticationHeader(bearer);
if (restClient != null)
await restClient.DisposeAsync().ConfigureAwait(false);

if (getRestClientForToken != null)
restClient = getRestClientForToken(bearer);

return await baseTask.ConfigureAwait(false);
}
}
}
51 changes: 51 additions & 0 deletions src/Tgstation.Server.Client.GraphQL/AuthenticationException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System;

namespace Tgstation.Server.Client.GraphQL
{
/// <summary>
/// <see cref="Exception"/> thrown when automatic <see cref="IGraphQLServerClient"/> authentication fails.
/// </summary>
public sealed class AuthenticationException : Exception
{
/// <summary>
/// The <see cref="ILogin_Login_Errors_ErrorMessageError"/>.
/// </summary>
public ILogin_Login_Errors_ErrorMessageError? ErrorMessage { get; }

/// <summary>
/// Initializes a new instance of the <see cref="AuthenticationException"/> class.
/// </summary>
/// <param name="errorMessage">The value of <see cref="ErrorMessage"/>.</param>
public AuthenticationException(ILogin_Login_Errors_ErrorMessageError errorMessage)
: base(errorMessage?.Message)
{
ErrorMessage = errorMessage ?? throw new ArgumentNullException(nameof(errorMessage));
}

/// <summary>
/// Initializes a new instance of the <see cref="AuthenticationException"/> class.
/// </summary>
public AuthenticationException()
{
}

/// <summary>
/// Initializes a new instance of the <see cref="AuthenticationException"/> class.
/// </summary>
/// <param name="message">The <see cref="Exception.Message"/>.</param>
public AuthenticationException(string message)
: base(message)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="AuthenticationException"/> class.
/// </summary>
/// <param name="message">The <see cref="Exception.Message"/>.</param>
/// <param name="innerException">The <see cref="Exception.InnerException"/>.</param>
public AuthenticationException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}
Loading
Loading