From 97f2a504acd734545e67e594a04031340b849e1c Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Sun, 24 May 2020 13:30:51 -0400 Subject: [PATCH 1/6] Add libssl1.0.0 to native deps list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ea66abd2b8..f5dc9af2ad 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ The following dependencies are required to run tgstation-server on Linux alongsi - libc6-i386 - libstdc++6:i386 +- libssl1.0.0 - gcc-multilib (Only on 64-bit systems) Note that tgstation-server has only ever been tested on Linux via it's [docker environment](build/Dockerfile#L22). If you are having trouble with something in a native installation, or figure out a required workaround, please contact project maintainers so this documentation may be better updated. From 823dab7b854a6c01cadb8c89b9436b230ee47440 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Sun, 24 May 2020 16:42:31 -0400 Subject: [PATCH 2/6] Port client/api libraries to .netstandard 2.1 - Enable nullable references - Client v7, API v6.4.1 --- build/Version.props | 4 +-- src/Tgstation.Server.Api/ApiHeaders.cs | 14 ++++---- .../Models/Administration.cs | 6 ++-- src/Tgstation.Server.Api/Models/Byond.cs | 4 +-- src/Tgstation.Server.Api/Models/ChatBot.cs | 15 ++++---- .../Models/ChatChannel.cs | 4 +-- src/Tgstation.Server.Api/Models/CompileJob.cs | 6 ++-- .../Models/ConfigurationFile.cs | 10 +++--- .../Models/DiscordConnectionStringBuilder.cs | 4 +-- .../Models/DreamDaemon.cs | 4 +-- src/Tgstation.Server.Api/Models/DreamMaker.cs | 2 +- .../Models/ErrorCodeExtensions.cs | 7 ++-- .../Models/ErrorMessage.cs | 11 ++++-- src/Tgstation.Server.Api/Models/Instance.cs | 6 ++-- .../Models/Internal/ChatBot.cs | 21 +++++------ .../Models/Internal/CompileJob.cs | 6 ++-- .../Models/Internal/Job.cs | 4 +-- .../Models/Internal/RepositorySettings.cs | 8 ++--- .../Models/Internal/RevisionInformation.cs | 4 +-- .../Models/Internal/ServerInformation.cs | 2 +- .../Models/Internal/TestMergeBase.cs | 8 ++--- .../Models/Internal/User.cs | 4 +-- .../Models/IrcConnectionStringBuilder.cs | 6 ++-- src/Tgstation.Server.Api/Models/Job.cs | 4 +-- src/Tgstation.Server.Api/Models/Repository.cs | 16 ++++----- .../Models/RevisionInformation.cs | 6 ++-- .../Models/ServerInformation.cs | 6 ++-- src/Tgstation.Server.Api/Models/TestMerge.cs | 2 +- .../Models/TestMergeParameters.cs | 4 +-- src/Tgstation.Server.Api/Models/Token.cs | 2 +- src/Tgstation.Server.Api/Models/User.cs | 6 ++-- src/Tgstation.Server.Api/Models/UserUpdate.cs | 2 +- .../Tgstation.Server.Api.csproj | 20 +++++------ src/Tgstation.Server.Client/ApiClient.cs | 6 ++-- .../ApiConflictException.cs | 2 +- src/Tgstation.Server.Client/ApiException.cs | 6 ++-- .../ClientException.cs | 2 +- .../Components/ConfigurationClient.cs | 5 ++- .../Components/InstanceUserClient.cs | 7 +++- .../ConflictException.cs | 2 +- .../IServerClientFactory.cs | 19 +++++++--- .../MethodNotSupportedException.cs | 2 +- .../RateLimitException.cs | 2 +- src/Tgstation.Server.Client/ServerClient.cs | 2 +- .../ServerClientFactory.cs | 35 ++++++++++++++----- .../ServerErrorException.cs | 2 +- .../Tgstation.Server.Client.csproj | 25 +++++-------- .../UnauthorizedException.cs | 2 +- .../VersionMismatchException.cs | 2 +- .../Tgstation.Server.Host.csproj | 11 ++---- .../Tgstation.Server.Tests/IntegrationTest.cs | 6 ++-- 51 files changed, 192 insertions(+), 174 deletions(-) diff --git a/build/Version.props b/build/Version.props index b09d27300e..ab5b742297 100644 --- a/build/Version.props +++ b/build/Version.props @@ -3,8 +3,8 @@ 4.2.5 - 6.4.0 - 6.3.0 + 6.4.1 + 7.0.0 5.2.1 0.4.0 1.1.0 diff --git a/src/Tgstation.Server.Api/ApiHeaders.cs b/src/Tgstation.Server.Api/ApiHeaders.cs index 2ee152dc56..69d6fa32e6 100644 --- a/src/Tgstation.Server.Api/ApiHeaders.cs +++ b/src/Tgstation.Server.Api/ApiHeaders.cs @@ -69,12 +69,12 @@ namespace Tgstation.Server.Api /// /// The client's user agent as a if valid /// - public ProductHeaderValue UserAgent => ProductInfoHeaderValue.TryParse(RawUserAgent, out var userAgent) ? userAgent.Product : null; + public ProductHeaderValue? UserAgent => ProductInfoHeaderValue.TryParse(RawUserAgent, out var userAgent) ? userAgent.Product : null; /// /// The client's raw user agent /// - public string RawUserAgent { get; } + public string? RawUserAgent { get; } /// /// The client's API version @@ -84,17 +84,17 @@ namespace Tgstation.Server.Api /// /// The client's JWT /// - public string Token { get; } + public string? Token { get; } /// /// The client's username /// - public string Username { get; } + public string? Username { get; } /// /// The client's password /// - public string Password { get; } + public string? Password { get; } /// /// If the header uses password or JWT authentication @@ -236,7 +236,7 @@ namespace Tgstation.Server.Api /// The value of /// The value of /// The value of - ApiHeaders(ProductHeaderValue userAgent, string token, string username, string password) + ApiHeaders(ProductHeaderValue userAgent, string? token, string? username, string? password) { RawUserAgent = userAgent?.ToString(); Token = token; @@ -274,7 +274,7 @@ namespace Tgstation.Server.Api headers.UserAgent.Add(new ProductInfoHeaderValue(UserAgent)); headers.Add(ApiVersionHeader, new ProductHeaderValue(AssemblyName.Name, ApiVersion.ToString()).ToString()); - instanceId = instanceId ?? InstanceId; + instanceId ??= InstanceId; if (instanceId.HasValue) headers.Add(InstanceIdHeader, instanceId.ToString()); } diff --git a/src/Tgstation.Server.Api/Models/Administration.cs b/src/Tgstation.Server.Api/Models/Administration.cs index f325862bbe..9febaa977a 100644 --- a/src/Tgstation.Server.Api/Models/Administration.cs +++ b/src/Tgstation.Server.Api/Models/Administration.cs @@ -15,16 +15,16 @@ namespace Tgstation.Server.Api.Models /// /// The GitHub repository the server is built to recieve updates from /// - public Uri TrackedRepositoryUrl { get; set; } + public Uri? TrackedRepositoryUrl { get; set; } /// /// The latest available version of the Tgstation.Server.Host assembly from the upstream repository. If is higher than 's the update cannot be applied due to API changes /// - public Version LatestVersion { get; set; } + public Version? LatestVersion { get; set; } /// /// Changes the version of Tgstation.Server.Host to the given version from the upstream repository /// - public Version NewVersion { get; set; } + public Version? NewVersion { get; set; } } } diff --git a/src/Tgstation.Server.Api/Models/Byond.cs b/src/Tgstation.Server.Api/Models/Byond.cs index 6ad465fbf6..d5322db8c0 100644 --- a/src/Tgstation.Server.Api/Models/Byond.cs +++ b/src/Tgstation.Server.Api/Models/Byond.cs @@ -10,11 +10,11 @@ namespace Tgstation.Server.Api.Models /// /// The of the installation used for new compiles. Will be if the user does not have permission to view it or there is no BYOND version installed. Only considers the and numbers. /// - public Version Version { get; set; } + public Version? Version { get; set; } /// /// The being used to install a new /// - public Job InstallJob { get; set; } + public Job? InstallJob { get; set; } } } diff --git a/src/Tgstation.Server.Api/Models/ChatBot.cs b/src/Tgstation.Server.Api/Models/ChatBot.cs index afa8aab001..c0034d32bb 100644 --- a/src/Tgstation.Server.Api/Models/ChatBot.cs +++ b/src/Tgstation.Server.Api/Models/ChatBot.cs @@ -10,7 +10,7 @@ namespace Tgstation.Server.Api.Models /// /// Channels the Discord bot should listen/announce in /// - public ICollection Channels { get; set; } + public ICollection? Channels { get; set; } /// /// Validates are correct for the @@ -20,15 +20,12 @@ namespace Tgstation.Server.Api.Models { if (!Provider.HasValue) return true; - switch (Provider.Value) + return Provider.Value switch { - case ChatProvider.Discord: - return Channels?.Select(x => x.DiscordChannelId.HasValue && x.IrcChannel == null).All(x => x) ?? true; - case ChatProvider.Irc: - return Channels?.Select(x => !x.DiscordChannelId.HasValue && x.IrcChannel != null).All(x => x) ?? true; - default: - throw new InvalidOperationException("Invalid provider type!"); - } + ChatProvider.Discord => Channels?.Select(x => x.DiscordChannelId.HasValue && x.IrcChannel == null).All(x => x) ?? true, + ChatProvider.Irc => Channels?.Select(x => !x.DiscordChannelId.HasValue && x.IrcChannel != null).All(x => x) ?? true, + _ => throw new InvalidOperationException("Invalid provider type!"), + }; } } } diff --git a/src/Tgstation.Server.Api/Models/ChatChannel.cs b/src/Tgstation.Server.Api/Models/ChatChannel.cs index 0039e9bc20..805fa1fbbb 100644 --- a/src/Tgstation.Server.Api/Models/ChatChannel.cs +++ b/src/Tgstation.Server.Api/Models/ChatChannel.cs @@ -12,7 +12,7 @@ namespace Tgstation.Server.Api.Models /// If multiple copies of the same channel with different keys are added to the server, the one that will be used is undefined. /// [StringLength(Limits.MaximumIndexableStringLength)] - public string IrcChannel { get; set; } + public string? IrcChannel { get; set; } /// /// The Discord channel ID @@ -41,6 +41,6 @@ namespace Tgstation.Server.Api.Models /// A custom tag users can define to group channels together /// [StringLength(Limits.MaximumStringLength)] - public string Tag { get; set; } + public string? Tag { get; set; } } } diff --git a/src/Tgstation.Server.Api/Models/CompileJob.cs b/src/Tgstation.Server.Api/Models/CompileJob.cs index 44a3b7baad..964bc9dad7 100644 --- a/src/Tgstation.Server.Api/Models/CompileJob.cs +++ b/src/Tgstation.Server.Api/Models/CompileJob.cs @@ -8,16 +8,16 @@ namespace Tgstation.Server.Api.Models /// /// The relating to this job /// - public Job Job { get; set; } + public Job? Job { get; set; } /// /// Git revision the compiler ran on. Not modifiable /// - public RevisionInformation RevisionInformation { get; set; } + public RevisionInformation? RevisionInformation { get; set; } /// /// The the was made with /// - public Version ByondVersion { get; set; } + public Version? ByondVersion { get; set; } } } diff --git a/src/Tgstation.Server.Api/Models/ConfigurationFile.cs b/src/Tgstation.Server.Api/Models/ConfigurationFile.cs index 01d5380881..5703586515 100644 --- a/src/Tgstation.Server.Api/Models/ConfigurationFile.cs +++ b/src/Tgstation.Server.Api/Models/ConfigurationFile.cs @@ -11,7 +11,7 @@ namespace Tgstation.Server.Api.Models /// The path to the file /// [StringLength(Limits.MaximumStringLength)] - public string Path { get; set; } + public string? Path { get; set; } /// /// If access to the file was denied for the operation @@ -26,13 +26,13 @@ namespace Tgstation.Server.Api.Models /// /// The MD5 hash of the file when last read by the user. If this doesn't match during update actions, the write will be denied with /// - public string LastReadHash { get; set; } + public string? LastReadHash { get; set; } /// /// The content of the . Will be if is or during listing and write operations /// -#pragma warning disable CA1819 // Properties should not return arrays - public byte[] Content { get; set; } -#pragma warning restore CA1819 // Properties should not return arrays +#pragma warning disable CA1819, SA1011 // Properties should not return arrays, Closing square bracket should be followed by a space + public byte[]? Content { get; set; } +#pragma warning restore CA1819, SA1011 // Properties should not return arrays, Closing square bracket should be followed by a space } } diff --git a/src/Tgstation.Server.Api/Models/DiscordConnectionStringBuilder.cs b/src/Tgstation.Server.Api/Models/DiscordConnectionStringBuilder.cs index a919e18380..2fe677ea02 100644 --- a/src/Tgstation.Server.Api/Models/DiscordConnectionStringBuilder.cs +++ b/src/Tgstation.Server.Api/Models/DiscordConnectionStringBuilder.cs @@ -15,7 +15,7 @@ namespace Tgstation.Server.Api.Models /// The Discord bot token /// /// See https://discordapp.com/developers/docs/topics/oauth2#bots - public string BotToken { get; set; } + public string? BotToken { get; set; } /// /// Construct a @@ -32,6 +32,6 @@ namespace Tgstation.Server.Api.Models } /// - public override string ToString() => BotToken; + public override string ToString() => BotToken ?? "(null)"; } } \ No newline at end of file diff --git a/src/Tgstation.Server.Api/Models/DreamDaemon.cs b/src/Tgstation.Server.Api/Models/DreamDaemon.cs index 7360918bd6..38e29363e0 100644 --- a/src/Tgstation.Server.Api/Models/DreamDaemon.cs +++ b/src/Tgstation.Server.Api/Models/DreamDaemon.cs @@ -11,12 +11,12 @@ namespace Tgstation.Server.Api.Models /// /// The live revision /// - public CompileJob ActiveCompileJob { get; set; } + public CompileJob? ActiveCompileJob { get; set; } /// /// The next revision to go live /// - public CompileJob StagedCompileJob { get; set; } + public CompileJob? StagedCompileJob { get; set; } /// /// The current status of diff --git a/src/Tgstation.Server.Api/Models/DreamMaker.cs b/src/Tgstation.Server.Api/Models/DreamMaker.cs index 5b07d0b33d..9055a56d31 100644 --- a/src/Tgstation.Server.Api/Models/DreamMaker.cs +++ b/src/Tgstation.Server.Api/Models/DreamMaker.cs @@ -11,7 +11,7 @@ namespace Tgstation.Server.Api.Models /// The .dme file tries to compile with without the extension /// [StringLength(Limits.MaximumStringLength)] - public string ProjectName { get; set; } + public string? ProjectName { get; set; } /// /// The port used during compilation to validate the DMAPI diff --git a/src/Tgstation.Server.Api/Models/ErrorCodeExtensions.cs b/src/Tgstation.Server.Api/Models/ErrorCodeExtensions.cs index adabefbb7c..a69f18e483 100644 --- a/src/Tgstation.Server.Api/Models/ErrorCodeExtensions.cs +++ b/src/Tgstation.Server.Api/Models/ErrorCodeExtensions.cs @@ -1,4 +1,5 @@ -using System.ComponentModel; +using System.Collections.Generic; +using System.ComponentModel; using System.Linq; namespace Tgstation.Server.Api.Models @@ -13,9 +14,9 @@ namespace Tgstation.Server.Api.Models /// /// The to describe. /// A description of the on success, on failure. - public static string Describe(this ErrorCode errorCode) + public static string? Describe(this ErrorCode errorCode) { - var attributes = (DescriptionAttribute[])typeof(ErrorCode) + var attributes = (IEnumerable?)typeof(ErrorCode) .GetField(errorCode.ToString()) ?.GetCustomAttributes(typeof(DescriptionAttribute), false); diff --git a/src/Tgstation.Server.Api/Models/ErrorMessage.cs b/src/Tgstation.Server.Api/Models/ErrorMessage.cs index 37e190fc2f..099c49cdf7 100644 --- a/src/Tgstation.Server.Api/Models/ErrorMessage.cs +++ b/src/Tgstation.Server.Api/Models/ErrorMessage.cs @@ -12,18 +12,18 @@ namespace Tgstation.Server.Api.Models /// The version of the API the server is using /// [Required] - public Version ServerApiVersion { get; set; } + public Version? ServerApiVersion { get; set; } /// /// A human readable description of the error /// [Required] - public string Message { get; set; } + public string? Message { get; set; } /// /// Additional data associated with the error message. /// - public string AdditionalData { get; set; } + public string? AdditionalData { get; set; } /// /// The of the . @@ -31,6 +31,11 @@ namespace Tgstation.Server.Api.Models [EnumDataType(typeof(ErrorCode))] public ErrorCode ErrorCode { get; set; } + /// + /// Initializes a new instance of the . + /// + public ErrorMessage() { } + /// /// Initializes a new instance of the . /// diff --git a/src/Tgstation.Server.Api/Models/Instance.cs b/src/Tgstation.Server.Api/Models/Instance.cs index b8d5afb013..d3f1a78115 100644 --- a/src/Tgstation.Server.Api/Models/Instance.cs +++ b/src/Tgstation.Server.Api/Models/Instance.cs @@ -18,13 +18,13 @@ namespace Tgstation.Server.Api.Models /// [Required] [StringLength(Limits.MaximumStringLength)] - public string Name { get; set; } + public string? Name { get; set; } /// /// The path to where the is located. Can only be changed while the is offline. Must not exist when the instance is created /// [Required] - public string Path { get; set; } + public string? Path { get; set; } /// /// If the is online @@ -56,7 +56,7 @@ namespace Tgstation.Server.Api.Models /// /// Due to how s are children of s but moving one requires the to be offline, interactions with this are performed in a non-standard fashion. The is read by querying the again (either via list or ID lookup) and cancelled by making any sort of update to the . Once the comes back it can be queried like a normal job [NotMapped] - public Job MoveJob { get; set; } + public Job? MoveJob { get; set; } /// /// Create a clone of the essential metadata diff --git a/src/Tgstation.Server.Api/Models/Internal/ChatBot.cs b/src/Tgstation.Server.Api/Models/Internal/ChatBot.cs index 44ae5e5c4d..5db8be2eda 100644 --- a/src/Tgstation.Server.Api/Models/Internal/ChatBot.cs +++ b/src/Tgstation.Server.Api/Models/Internal/ChatBot.cs @@ -18,7 +18,7 @@ namespace Tgstation.Server.Api.Models.Internal /// [Required] [StringLength(Limits.MaximumIndexableStringLength)] - public string Name { get; set; } + public string? Name { get; set; } /// /// If the connection is enabled @@ -50,25 +50,22 @@ namespace Tgstation.Server.Api.Models.Internal /// [Required] [StringLength(Limits.MaximumStringLength)] - public string ConnectionString { get; set; } + public string? ConnectionString { get; set; } /// /// Get the which maps to the . /// /// A for the . - public ChatConnectionStringBuilder CreateConnectionStringBuilder() + public ChatConnectionStringBuilder? CreateConnectionStringBuilder() { if (ConnectionString == null) return null; - switch (Provider) + return Provider switch { - case ChatProvider.Discord: - return new DiscordConnectionStringBuilder(ConnectionString); - case ChatProvider.Irc: - return new IrcConnectionStringBuilder(ConnectionString); - default: - throw new InvalidOperationException("Invalid Provider!"); - } + ChatProvider.Discord => new DiscordConnectionStringBuilder(ConnectionString), + ChatProvider.Irc => new IrcConnectionStringBuilder(ConnectionString), + _ => throw new InvalidOperationException("Invalid Provider!"), + }; } /// @@ -77,7 +74,7 @@ namespace Tgstation.Server.Api.Models.Internal /// The optional . public void SetConnectionStringBuilder(ChatConnectionStringBuilder stringBuilder) { - ConnectionString = stringBuilder?.ToString(); + ConnectionString = stringBuilder?.ToString() ?? throw new ArgumentNullException(nameof(stringBuilder)); } } } diff --git a/src/Tgstation.Server.Api/Models/Internal/CompileJob.cs b/src/Tgstation.Server.Api/Models/Internal/CompileJob.cs index a39532bd3c..87f4bc13cd 100644 --- a/src/Tgstation.Server.Api/Models/Internal/CompileJob.cs +++ b/src/Tgstation.Server.Api/Models/Internal/CompileJob.cs @@ -13,13 +13,13 @@ namespace Tgstation.Server.Api.Models.Internal /// The .dme file used for compilation /// [Required] - public string DmeName { get; set; } + public string? DmeName { get; set; } /// /// Textual output of DM /// [Required] - public string Output { get; set; } + public string? Output { get; set; } /// /// The Game folder the results were compiled into @@ -37,6 +37,6 @@ namespace Tgstation.Server.Api.Models.Internal /// The DMAPI . /// [NotMapped] - public virtual Version DMApiVersion { get; set; } + public virtual Version? DMApiVersion { get; set; } } } diff --git a/src/Tgstation.Server.Api/Models/Internal/Job.cs b/src/Tgstation.Server.Api/Models/Internal/Job.cs index 3e5656e56e..ac3b00f1b8 100644 --- a/src/Tgstation.Server.Api/Models/Internal/Job.cs +++ b/src/Tgstation.Server.Api/Models/Internal/Job.cs @@ -13,7 +13,7 @@ namespace Tgstation.Server.Api.Models.Internal /// English description of the /// [Required] - public string Description { get; set; } + public string? Description { get; set; } /// /// The associated with the if any. @@ -23,7 +23,7 @@ namespace Tgstation.Server.Api.Models.Internal /// /// Details of any exceptions caught during the /// - public string ExceptionDetails { get; set; } + public string? ExceptionDetails { get; set; } /// /// When the was started diff --git a/src/Tgstation.Server.Api/Models/Internal/RepositorySettings.cs b/src/Tgstation.Server.Api/Models/Internal/RepositorySettings.cs index e392669537..b211010351 100644 --- a/src/Tgstation.Server.Api/Models/Internal/RepositorySettings.cs +++ b/src/Tgstation.Server.Api/Models/Internal/RepositorySettings.cs @@ -12,7 +12,7 @@ namespace Tgstation.Server.Api.Models.Internal /// [Required] [StringLength(Limits.MaximumStringLength)] - public string CommitterName { get; set; } + public string? CommitterName { get; set; } /// /// The e-mail of the committer @@ -20,19 +20,19 @@ namespace Tgstation.Server.Api.Models.Internal [Required] [StringLength(Limits.MaximumStringLength)] [EmailAddress] - public string CommitterEmail { get; set; } + public string? CommitterEmail { get; set; } /// /// The username to access the git repository with /// [StringLength(Limits.MaximumStringLength)] - public string AccessUser { get; set; } + public string? AccessUser { get; set; } /// /// The token/password to access the git repository with /// [StringLength(Limits.MaximumStringLength)] - public string AccessToken { get; set; } + public string? AccessToken { get; set; } /// /// If commits created from testmerges are pushed to the remote diff --git a/src/Tgstation.Server.Api/Models/Internal/RevisionInformation.cs b/src/Tgstation.Server.Api/Models/Internal/RevisionInformation.cs index 312f5812d1..efdb763aa2 100644 --- a/src/Tgstation.Server.Api/Models/Internal/RevisionInformation.cs +++ b/src/Tgstation.Server.Api/Models/Internal/RevisionInformation.cs @@ -12,13 +12,13 @@ namespace Tgstation.Server.Api.Models.Internal /// [Required] [StringLength(40)] - public string CommitSha { get; set; } + public string? CommitSha { get; set; } /// /// The sha of the most recent remote commit /// [Required] [StringLength(40)] - public string OriginCommitSha { get; set; } + public string? OriginCommitSha { get; set; } } } diff --git a/src/Tgstation.Server.Api/Models/Internal/ServerInformation.cs b/src/Tgstation.Server.Api/Models/Internal/ServerInformation.cs index 101877c917..0c6768e53a 100644 --- a/src/Tgstation.Server.Api/Models/Internal/ServerInformation.cs +++ b/src/Tgstation.Server.Api/Models/Internal/ServerInformation.cs @@ -25,6 +25,6 @@ namespace Tgstation.Server.Api.Models.Internal /// /// Limits the locations instances may be created or attached from. /// - public ICollection ValidInstancePaths { get; set; } + public ICollection? ValidInstancePaths { get; set; } } } diff --git a/src/Tgstation.Server.Api/Models/Internal/TestMergeBase.cs b/src/Tgstation.Server.Api/Models/Internal/TestMergeBase.cs index 76dbe54ce2..c105e643f7 100644 --- a/src/Tgstation.Server.Api/Models/Internal/TestMergeBase.cs +++ b/src/Tgstation.Server.Api/Models/Internal/TestMergeBase.cs @@ -12,27 +12,27 @@ namespace Tgstation.Server.Api.Models.Internal /// The title of the pull request /// [Required] - public string TitleAtMerge { get; set; } + public string? TitleAtMerge { get; set; } /// /// The body of the pull request /// [Required] - public string BodyAtMerge { get; set; } + public string? BodyAtMerge { get; set; } /// /// The URL of the pull request /// [Required] #pragma warning disable CA1056 // Uri properties should not be strings - public string Url { get; set; } + public string? Url { get; set; } #pragma warning restore CA1056 // Uri properties should not be strings /// /// The author of the pull request /// [Required] - public string Author { get; set; } + public string? Author { get; set; } /// /// Construct a diff --git a/src/Tgstation.Server.Api/Models/Internal/User.cs b/src/Tgstation.Server.Api/Models/Internal/User.cs index 35bd7c5e3d..e50b23afde 100644 --- a/src/Tgstation.Server.Api/Models/Internal/User.cs +++ b/src/Tgstation.Server.Api/Models/Internal/User.cs @@ -31,14 +31,14 @@ namespace Tgstation.Server.Api.Models.Internal /// The SID/UID of the on Windows/POSIX respectively /// // No need for StringLength as the server MUST validate it. - public string SystemIdentifier { get; set; } + public string? SystemIdentifier { get; set; } /// /// The name of the /// [Required] [StringLength(Limits.MaximumStringLength)] - public string Name { get; set; } + public string? Name { get; set; } /// /// The for the diff --git a/src/Tgstation.Server.Api/Models/IrcConnectionStringBuilder.cs b/src/Tgstation.Server.Api/Models/IrcConnectionStringBuilder.cs index 8f4534f0c5..4661742d12 100644 --- a/src/Tgstation.Server.Api/Models/IrcConnectionStringBuilder.cs +++ b/src/Tgstation.Server.Api/Models/IrcConnectionStringBuilder.cs @@ -16,7 +16,7 @@ namespace Tgstation.Server.Api.Models /// /// The IP address or URL of the IRC server /// - public string Address { get; set; } + public string? Address { get; set; } /// /// The port the server runs on @@ -26,7 +26,7 @@ namespace Tgstation.Server.Api.Models /// /// The nickname for the bot to use /// - public string Nickname { get; set; } + public string? Nickname { get; set; } /// /// If the connection should be made using SSL @@ -41,7 +41,7 @@ namespace Tgstation.Server.Api.Models /// /// The optional password to use /// - public string Password { get; set; } + public string? Password { get; set; } /// /// Construct an diff --git a/src/Tgstation.Server.Api/Models/Job.cs b/src/Tgstation.Server.Api/Models/Job.cs index ad4e1372ab..9869be84ab 100644 --- a/src/Tgstation.Server.Api/Models/Job.cs +++ b/src/Tgstation.Server.Api/Models/Job.cs @@ -8,12 +8,12 @@ /// /// The that started the job /// - public User StartedBy { get; set; } + public User? StartedBy { get; set; } /// /// The that cancelled the job /// - public User CancelledBy { get; set; } + public User? CancelledBy { get; set; } /// /// Optional progress between 0 and 100 inclusive diff --git a/src/Tgstation.Server.Api/Models/Repository.cs b/src/Tgstation.Server.Api/Models/Repository.cs index ee50701502..26a3e1fa46 100644 --- a/src/Tgstation.Server.Api/Models/Repository.cs +++ b/src/Tgstation.Server.Api/Models/Repository.cs @@ -10,32 +10,32 @@ namespace Tgstation.Server.Api.Models /// /// The origin URL. If , the does not exist /// - public string Origin { get; set; } + public string? Origin { get; set; } /// /// The commit HEAD should point to. Not populated in responses, use instead for retrieval /// - public string CheckoutSha { get; set; } + public string? CheckoutSha { get; set; } /// /// The current for the /// - public RevisionInformation RevisionInformation { get; set; } + public RevisionInformation? RevisionInformation { get; set; } /// /// If the repository was cloned from GitHub.com this will be set with the owner of the repository /// - public string GitHubOwner { get; set; } + public string? GitHubOwner { get; set; } /// /// If the repository was cloned from GitHub.com this will be set with the name of the repository /// - public string GitHubName { get; set; } + public string? GitHubName { get; set; } /// /// The started by the if any /// - public Job ActiveJob { get; set; } + public Job? ActiveJob { get; set; } /// /// Do the equivalent of a git pull. Will attempt to merge unless is also specified in which case a hard reset will be performed after checking out @@ -45,11 +45,11 @@ namespace Tgstation.Server.Api.Models /// /// The branch or tag HEAD points to /// - public string Reference { get; set; } + public string? Reference { get; set; } /// /// for new s. Note that merges that conflict will not be performed /// - public List NewTestMerges { get; set; } + public ICollection? NewTestMerges { get; set; } } } \ No newline at end of file diff --git a/src/Tgstation.Server.Api/Models/RevisionInformation.cs b/src/Tgstation.Server.Api/Models/RevisionInformation.cs index 9fbe61578a..1d05a22755 100644 --- a/src/Tgstation.Server.Api/Models/RevisionInformation.cs +++ b/src/Tgstation.Server.Api/Models/RevisionInformation.cs @@ -8,16 +8,16 @@ namespace Tgstation.Server.Api.Models /// /// The that was created with this /// - public TestMerge PrimaryTestMerge { get; set; } + public TestMerge? PrimaryTestMerge { get; set; } /// /// The s active in the /// - public ICollection ActiveTestMerges { get; set; } + public ICollection? ActiveTestMerges { get; set; } /// /// The s made from the /// - public ICollection CompileJobs { get; set; } + public ICollection? CompileJobs { get; set; } } } diff --git a/src/Tgstation.Server.Api/Models/ServerInformation.cs b/src/Tgstation.Server.Api/Models/ServerInformation.cs index 081671e850..26d9cbc0fd 100644 --- a/src/Tgstation.Server.Api/Models/ServerInformation.cs +++ b/src/Tgstation.Server.Api/Models/ServerInformation.cs @@ -10,16 +10,16 @@ namespace Tgstation.Server.Api.Models /// /// The version of the host /// - public Version Version { get; set; } + public Version? Version { get; set; } /// /// The version of the host /// - public Version ApiVersion { get; set; } + public Version? ApiVersion { get; set; } /// /// The DMAPI version of the host. /// - public Version DMApiVersion { get; set; } + public Version? DMApiVersion { get; set; } } } diff --git a/src/Tgstation.Server.Api/Models/TestMerge.cs b/src/Tgstation.Server.Api/Models/TestMerge.cs index d448504e2d..f9cdbcea75 100644 --- a/src/Tgstation.Server.Api/Models/TestMerge.cs +++ b/src/Tgstation.Server.Api/Models/TestMerge.cs @@ -6,6 +6,6 @@ /// /// The who created the /// - public User MergedBy { get; set; } + public User? MergedBy { get; set; } } } \ No newline at end of file diff --git a/src/Tgstation.Server.Api/Models/TestMergeParameters.cs b/src/Tgstation.Server.Api/Models/TestMergeParameters.cs index 09fe947a57..ba68c72e7e 100644 --- a/src/Tgstation.Server.Api/Models/TestMergeParameters.cs +++ b/src/Tgstation.Server.Api/Models/TestMergeParameters.cs @@ -17,12 +17,12 @@ namespace Tgstation.Server.Api.Models /// [Required] [StringLength(40)] - public string PullRequestRevision { get; set; } + public string? PullRequestRevision { get; set; } /// /// Optional comment about the test /// [StringLength(Limits.MaximumStringLength)] - public string Comment { get; set; } + public string? Comment { get; set; } } } \ No newline at end of file diff --git a/src/Tgstation.Server.Api/Models/Token.cs b/src/Tgstation.Server.Api/Models/Token.cs index bc0010be5d..4f73c1c28e 100644 --- a/src/Tgstation.Server.Api/Models/Token.cs +++ b/src/Tgstation.Server.Api/Models/Token.cs @@ -10,7 +10,7 @@ namespace Tgstation.Server.Api.Models /// /// The value of the JWT /// - public string Bearer { get; set; } + public string? Bearer { get; set; } /// /// When the expires diff --git a/src/Tgstation.Server.Api/Models/User.cs b/src/Tgstation.Server.Api/Models/User.cs index 3382bba8f5..e6f50acb27 100644 --- a/src/Tgstation.Server.Api/Models/User.cs +++ b/src/Tgstation.Server.Api/Models/User.cs @@ -6,16 +6,16 @@ /// /// The name of the default admin user /// - public const string AdminName = "Admin"; + public static readonly string AdminName = "Admin"; /// /// The default admin password /// - public const string DefaultAdminPassword = "ISolemlySwearToDeleteTheDataDirectory"; + public static readonly string DefaultAdminPassword = "ISolemlySwearToDeleteTheDataDirectory"; /// /// The who created this /// - public User CreatedBy { get; set; } + public User? CreatedBy { get; set; } } } \ No newline at end of file diff --git a/src/Tgstation.Server.Api/Models/UserUpdate.cs b/src/Tgstation.Server.Api/Models/UserUpdate.cs index 6e93926be8..dc35f3de24 100644 --- a/src/Tgstation.Server.Api/Models/UserUpdate.cs +++ b/src/Tgstation.Server.Api/Models/UserUpdate.cs @@ -11,6 +11,6 @@ namespace Tgstation.Server.Api.Models /// Cleartext password of the /// [Required] - public string Password { get; set; } + public string? Password { get; set; } } } diff --git a/src/Tgstation.Server.Api/Tgstation.Server.Api.csproj b/src/Tgstation.Server.Api/Tgstation.Server.Api.csproj index e8e39bc692..de34a2b556 100644 --- a/src/Tgstation.Server.Api/Tgstation.Server.Api.csproj +++ b/src/Tgstation.Server.Api/Tgstation.Server.Api.csproj @@ -2,15 +2,16 @@ - netstandard2.0 + netstandard2.1 Full + $(TgsApiVersion) true Cyberboss - /tg/station + /tg/station 13 API definitions for tgstation-server + https://tgstation.github.io/tgstation-server LICENSE tgs.png - https://tgstation.github.io/tgstation-server Git https://github.com/tgstation/tgstation-server 2018 @@ -18,21 +19,16 @@ See https://github.com/tgstation/tgstation-server/releases/tag/api-v$(TgsApiVersion) true snupkg - $(TgsApiVersion) ../../build/analyzers.ruleset latest + enable + bin\$(Configuration)\netstandard2.1\Tgstation.Server.Api.xml + CA1028 - + true - bin\Release\netstandard2.1\Tgstation.Server.Api.xml - 1701;1702;CA1028 - - - - 1701;1702;CA1028 - bin\Debug\netstandard2.1\Tgstation.Server.Api.xml diff --git a/src/Tgstation.Server.Client/ApiClient.cs b/src/Tgstation.Server.Client/ApiClient.cs index d15629ea32..d4026b7679 100644 --- a/src/Tgstation.Server.Client/ApiClient.cs +++ b/src/Tgstation.Server.Client/ApiClient.cs @@ -57,7 +57,7 @@ namespace Tgstation.Server.Client static void HandleBadResponse(HttpResponseMessage response, string json) { - ErrorMessage errorMessage = null; + ErrorMessage? errorMessage = null; try { // check if json serializes to an error message @@ -124,7 +124,7 @@ namespace Tgstation.Server.Client /// The optional for the request /// The for the operation /// A resulting in the response on success - async Task RunRequest(string route, object body, HttpMethod method, long? instanceId, CancellationToken cancellationToken) + async Task RunRequest(string route, object? body, HttpMethod method, long? instanceId, CancellationToken cancellationToken) { if (route == null) throw new ArgumentNullException(nameof(route)); @@ -162,7 +162,7 @@ namespace Tgstation.Server.Client try { - return JsonConvert.DeserializeObject(json, serializerSettings); + return JsonConvert.DeserializeObject(json, serializerSettings) !; } catch (JsonException) { diff --git a/src/Tgstation.Server.Client/ApiConflictException.cs b/src/Tgstation.Server.Client/ApiConflictException.cs index e107b9bc25..53611af3b6 100644 --- a/src/Tgstation.Server.Client/ApiConflictException.cs +++ b/src/Tgstation.Server.Client/ApiConflictException.cs @@ -14,7 +14,7 @@ namespace Tgstation.Server.Client /// /// The for the /// The for the - public ApiConflictException(ErrorMessage errorMessage, HttpResponseMessage responseMessage) : base(errorMessage, responseMessage) + public ApiConflictException(ErrorMessage? errorMessage, HttpResponseMessage responseMessage) : base(errorMessage, responseMessage) { } /// diff --git a/src/Tgstation.Server.Client/ApiException.cs b/src/Tgstation.Server.Client/ApiException.cs index 91cb2c7ad0..00b103b80b 100644 --- a/src/Tgstation.Server.Client/ApiException.cs +++ b/src/Tgstation.Server.Client/ApiException.cs @@ -12,12 +12,12 @@ namespace Tgstation.Server.Client /// /// The of the server's API. /// - public Version ServerApiVersion { get; } + public Version? ServerApiVersion { get; } /// /// Additional error data from the server. /// - public string AdditionalServerData { get; } + public string? AdditionalServerData { get; } /// /// The API if applicable. @@ -29,7 +29,7 @@ namespace Tgstation.Server.Client /// /// The returned from the API. /// The . - protected ApiException(ErrorMessage errorMessage, HttpResponseMessage responseMessage) : base( + protected ApiException(ErrorMessage? errorMessage, HttpResponseMessage responseMessage) : base( responseMessage, errorMessage?.Message ?? $"HTTP {responseMessage.StatusCode}. Unknown API error, ErrorMessage payload not present!") { diff --git a/src/Tgstation.Server.Client/ClientException.cs b/src/Tgstation.Server.Client/ClientException.cs index 19e86fff87..92926f26e5 100644 --- a/src/Tgstation.Server.Client/ClientException.cs +++ b/src/Tgstation.Server.Client/ClientException.cs @@ -12,7 +12,7 @@ namespace Tgstation.Server.Client /// /// The of the /// - public HttpResponseMessage ResponseMessage { get; } + public HttpResponseMessage? ResponseMessage { get; } /// /// Initialize a new instance of the . diff --git a/src/Tgstation.Server.Client/Components/ConfigurationClient.cs b/src/Tgstation.Server.Client/Components/ConfigurationClient.cs index 4063e73963..31e0c2dd20 100644 --- a/src/Tgstation.Server.Client/Components/ConfigurationClient.cs +++ b/src/Tgstation.Server.Client/Components/ConfigurationClient.cs @@ -59,7 +59,10 @@ namespace Tgstation.Server.Client.Components { if (file == null) throw new ArgumentNullException(nameof(file)); - return apiClient.Read(Routes.ConfigurationFile + SanitizeGetPath(file.Path), instance.Id, cancellationToken); + return apiClient.Read( + Routes.ConfigurationFile + SanitizeGetPath(file.Path ?? throw new ArgumentException("file.Path should not be null!", nameof(file))), + instance.Id, + cancellationToken); } /// diff --git a/src/Tgstation.Server.Client/Components/InstanceUserClient.cs b/src/Tgstation.Server.Client/Components/InstanceUserClient.cs index cc9757f00b..b71b45359f 100644 --- a/src/Tgstation.Server.Client/Components/InstanceUserClient.cs +++ b/src/Tgstation.Server.Client/Components/InstanceUserClient.cs @@ -35,7 +35,12 @@ namespace Tgstation.Server.Client.Components public Task Create(InstanceUser instanceUser, CancellationToken cancellationToken) => apiClient.Create(Routes.InstanceUser, instanceUser ?? throw new ArgumentNullException(nameof(instanceUser)), instance.Id, cancellationToken); /// - public Task Delete(InstanceUser instanceUser, CancellationToken cancellationToken) => apiClient.Delete(Routes.SetID(Routes.InstanceUser, instanceUser.UserId.Value), instance.Id, cancellationToken); + public Task Delete(InstanceUser instanceUser, CancellationToken cancellationToken) => apiClient.Delete( + Routes.SetID( + Routes.InstanceUser, + instanceUser.UserId ?? throw new ArgumentException("Missing instanceUser.UserId!", nameof(instanceUser))), + instance.Id, + cancellationToken); /// public Task Read(CancellationToken cancellationToken) => apiClient.Read(Routes.InstanceUser, instance.Id, cancellationToken); diff --git a/src/Tgstation.Server.Client/ConflictException.cs b/src/Tgstation.Server.Client/ConflictException.cs index 930b3b8a3d..d8ce94379d 100644 --- a/src/Tgstation.Server.Client/ConflictException.cs +++ b/src/Tgstation.Server.Client/ConflictException.cs @@ -14,7 +14,7 @@ namespace Tgstation.Server.Client /// /// The for the . /// The for the . - public ConflictException(ErrorMessage errorMessage, HttpResponseMessage responseMessage) : base(errorMessage, responseMessage) + public ConflictException(ErrorMessage? errorMessage, HttpResponseMessage responseMessage) : base(errorMessage, responseMessage) { } /// diff --git a/src/Tgstation.Server.Client/IServerClientFactory.cs b/src/Tgstation.Server.Client/IServerClientFactory.cs index 32816de376..a3de3cb8d6 100644 --- a/src/Tgstation.Server.Client/IServerClientFactory.cs +++ b/src/Tgstation.Server.Client/IServerClientFactory.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api.Models; @@ -16,18 +17,26 @@ namespace Tgstation.Server.Client /// The URL to access TGS /// The username to for the /// The password for the - /// The representing timeout for the connection - /// The for the operation + /// Optional initial s to add to the . + /// Optional representing timeout for the connection + /// Optional for the operation /// A resulting in a new - Task CreateServerClient(Uri host, string username, string password, TimeSpan timeout = default, CancellationToken cancellationToken = default); + Task CreateFromLogin( + Uri host, + string username, + string password, + IEnumerable? requestLoggers = null, + TimeSpan? timeout = null, + CancellationToken cancellationToken = default); /// /// Create a /// /// The URL to access TGS /// The to access the API with - /// The representing timeout for the connection /// A new - IServerClient CreateServerClient(Uri host, Token token, TimeSpan timeout = default); + IServerClient CreateFromToken( + Uri host, + Token token); } } diff --git a/src/Tgstation.Server.Client/MethodNotSupportedException.cs b/src/Tgstation.Server.Client/MethodNotSupportedException.cs index 75d1248ba2..8c837d29ab 100644 --- a/src/Tgstation.Server.Client/MethodNotSupportedException.cs +++ b/src/Tgstation.Server.Client/MethodNotSupportedException.cs @@ -14,7 +14,7 @@ namespace Tgstation.Server.Client /// /// The for the . /// The for the . - public MethodNotSupportedException(ErrorMessage errorMessage, HttpResponseMessage responseMessage) : base(errorMessage, responseMessage) + public MethodNotSupportedException(ErrorMessage? errorMessage, HttpResponseMessage responseMessage) : base(errorMessage, responseMessage) { } /// diff --git a/src/Tgstation.Server.Client/RateLimitException.cs b/src/Tgstation.Server.Client/RateLimitException.cs index 5bd72ccbf3..b6968576b7 100644 --- a/src/Tgstation.Server.Client/RateLimitException.cs +++ b/src/Tgstation.Server.Client/RateLimitException.cs @@ -21,7 +21,7 @@ namespace Tgstation.Server.Client /// /// The for the . /// The for the . - public RateLimitException(ErrorMessage errorMessage, HttpResponseMessage responseMessage) : base(errorMessage, responseMessage) + public RateLimitException(ErrorMessage? errorMessage, HttpResponseMessage responseMessage) : base(errorMessage, responseMessage) { if (!responseMessage.Headers.TryGetValues(HeaderNames.RetryAfter, out var values)) return; diff --git a/src/Tgstation.Server.Client/ServerClient.cs b/src/Tgstation.Server.Client/ServerClient.cs index 4eac7213cc..950b890094 100644 --- a/src/Tgstation.Server.Client/ServerClient.cs +++ b/src/Tgstation.Server.Client/ServerClient.cs @@ -19,7 +19,7 @@ namespace Tgstation.Server.Client set { token = value ?? throw new InvalidOperationException("Cannot set a null Token!"); - apiClient.Headers = new ApiHeaders(apiClient.Headers.UserAgent, token.Bearer); + apiClient.Headers = new ApiHeaders(apiClient.Headers.UserAgent!, token.Bearer!); } } diff --git a/src/Tgstation.Server.Client/ServerClientFactory.cs b/src/Tgstation.Server.Client/ServerClientFactory.cs index 62eec5644c..225b437d06 100644 --- a/src/Tgstation.Server.Client/ServerClientFactory.cs +++ b/src/Tgstation.Server.Client/ServerClientFactory.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; @@ -30,7 +32,13 @@ namespace Tgstation.Server.Client } /// - public async Task CreateServerClient(Uri host, string username, string password, TimeSpan timeout, CancellationToken cancellationToken) + public async Task CreateFromLogin( + Uri host, + string username, + string password, + IEnumerable? requestLoggers = null, + TimeSpan? timeout = null, + CancellationToken cancellationToken = default) { if (host == null) throw new ArgumentNullException(nameof(host)); @@ -39,28 +47,37 @@ namespace Tgstation.Server.Client if (password == null) throw new ArgumentNullException(nameof(password)); + requestLoggers ??= Enumerable.Empty(); + Token token; using (var api = ApiClientFactory.CreateApiClient(host, new ApiHeaders(productHeaderValue, username, password))) { - if (timeout != default) - api.Timeout = timeout; + foreach (var requestLogger in requestLoggers) + api.AddRequestLogger(requestLogger); + + if (timeout.HasValue) + api.Timeout = timeout.Value; token = await api.Update(Routes.Root, cancellationToken).ConfigureAwait(false); } - return CreateServerClient(host, token, timeout); + var client = CreateFromToken(host, token); + if (timeout.HasValue) + client.Timeout = timeout.Value; + + return client; } /// - public IServerClient CreateServerClient(Uri host, Token token, TimeSpan timeout) + public IServerClient CreateFromToken(Uri host, Token token) { if (host == null) throw new ArgumentNullException(nameof(host)); if (token == null) throw new ArgumentNullException(nameof(token)); - var result = new ServerClient(ApiClientFactory.CreateApiClient(host, new ApiHeaders(productHeaderValue, token.Bearer)), token); - if (timeout != default) - result.Timeout = timeout; - return result; + if (token.Bearer == null) + throw new ArgumentException("token.Bearer should not be null!", nameof(token)); + + return new ServerClient(ApiClientFactory.CreateApiClient(host, new ApiHeaders(productHeaderValue, token.Bearer)), token); } } } diff --git a/src/Tgstation.Server.Client/ServerErrorException.cs b/src/Tgstation.Server.Client/ServerErrorException.cs index 4ce9b31f59..31f6ec2837 100644 --- a/src/Tgstation.Server.Client/ServerErrorException.cs +++ b/src/Tgstation.Server.Client/ServerErrorException.cs @@ -19,7 +19,7 @@ namespace Tgstation.Server.Client /// /// The for the /// The for the - public ServerErrorException(ErrorMessage errorMessage, HttpResponseMessage responseMessage) : base(errorMessage, responseMessage) + public ServerErrorException(ErrorMessage? errorMessage, HttpResponseMessage responseMessage) : base(errorMessage, responseMessage) { } diff --git a/src/Tgstation.Server.Client/Tgstation.Server.Client.csproj b/src/Tgstation.Server.Client/Tgstation.Server.Client.csproj index 181a06bbd2..40d7a56503 100644 --- a/src/Tgstation.Server.Client/Tgstation.Server.Client.csproj +++ b/src/Tgstation.Server.Client/Tgstation.Server.Client.csproj @@ -2,41 +2,34 @@ - netstandard2.0 + netstandard2.1 Full $(TgsClientVersion) true Cyberboss /tg/station 13 - Client library for tgstation-server https://tgstation.github.io/tgstation-server - https://github.com/tgstation/tgstation-server - Git - en-CA LICENSE tgs.png + Git + https://github.com/tgstation/tgstation-server + 2018 json web api tgstation-server tgstation ss13 byond client - Updated to API version $(TgsApiVersion) + .Net Standard 2.1, allow login requests to be logged. true snupkg - 2018 ../../build/analyzers.ruleset latest + enable + bin\$(Configuration)\netstandard2.1\Tgstation.Server.Client.xml - + true - bin\Release\netstandard2.1\Tgstation.Server.Client.xml - 1701;1702 - - - 1701;1702 - bin\Debug\netstandard2.1\Tgstation.Server.Client.xml - - + diff --git a/src/Tgstation.Server.Client/UnauthorizedException.cs b/src/Tgstation.Server.Client/UnauthorizedException.cs index 8f43faeeec..206f0b39b6 100644 --- a/src/Tgstation.Server.Client/UnauthorizedException.cs +++ b/src/Tgstation.Server.Client/UnauthorizedException.cs @@ -14,7 +14,7 @@ namespace Tgstation.Server.Client /// /// The returned by the API. /// The . - public UnauthorizedException(ErrorMessage errorMessage, HttpResponseMessage responseMessage) : base(errorMessage, responseMessage) + public UnauthorizedException(ErrorMessage? errorMessage, HttpResponseMessage responseMessage) : base(errorMessage, responseMessage) { } /// diff --git a/src/Tgstation.Server.Client/VersionMismatchException.cs b/src/Tgstation.Server.Client/VersionMismatchException.cs index 17206eb0de..b1ea09bb3f 100644 --- a/src/Tgstation.Server.Client/VersionMismatchException.cs +++ b/src/Tgstation.Server.Client/VersionMismatchException.cs @@ -14,7 +14,7 @@ namespace Tgstation.Server.Client /// /// The for the . /// The for the . - public VersionMismatchException(ErrorMessage errorMessage, HttpResponseMessage responseMessage) : base(errorMessage, responseMessage) + public VersionMismatchException(ErrorMessage? errorMessage, HttpResponseMessage responseMessage) : base(errorMessage, responseMessage) { } diff --git a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj index 9177220c50..5b9e627a09 100644 --- a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj +++ b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj @@ -9,18 +9,13 @@ ../../build/analyzers.ruleset false true + bin\$(Configuration)\netcoreapp3.1\Tgstation.Server.Host.xml + API1000 - + true - bin\Release\netcoreapp2.1\Tgstation.Server.Host.xml - API1000 - - - - API1000 - bin\Debug\netcoreapp2.1\Tgstation.Server.Host.xml diff --git a/tests/Tgstation.Server.Tests/IntegrationTest.cs b/tests/Tgstation.Server.Tests/IntegrationTest.cs index 90d1096dac..e32ed5e505 100644 --- a/tests/Tgstation.Server.Tests/IntegrationTest.cs +++ b/tests/Tgstation.Server.Tests/IntegrationTest.cs @@ -43,7 +43,7 @@ namespace Tgstation.Server.Tests { try { - adminClient = await clientFactory.CreateServerClient(server.Url, User.AdminName, User.DefaultAdminPassword).ConfigureAwait(false); + adminClient = await clientFactory.CreateFromLogin(server.Url, User.AdminName, User.DefaultAdminPassword).ConfigureAwait(false); break; } catch (HttpRequestException) @@ -144,7 +144,7 @@ namespace Tgstation.Server.Tests { try { - return await clientFactory.CreateServerClient(server.Url, User.AdminName, User.DefaultAdminPassword).ConfigureAwait(false); + return await clientFactory.CreateFromLogin(server.Url, User.AdminName, User.DefaultAdminPassword).ConfigureAwait(false); } catch (HttpRequestException) { @@ -193,7 +193,7 @@ namespace Tgstation.Server.Tests Bearer = adminClient.Token.Bearer + '0' }; - var badClient = clientFactory.CreateServerClient(server.Url, newToken); + var badClient = clientFactory.CreateFromToken(server.Url, newToken); await Assert.ThrowsExceptionAsync(() => badClient.Version(cancellationToken)).ConfigureAwait(false); var adminTest = new AdministrationTest(adminClient.Administration).Run(cancellationToken); From d93860b8dcb9a003df89b03e097c8212af38ebe4 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Sun, 24 May 2020 16:42:44 -0400 Subject: [PATCH 3/6] No more production experimental watchdogs --- .../Components/InstanceManager.cs | 15 +++++++++++++++ src/Tgstation.Server.Host/Setup/SetupWizard.cs | 5 +---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/InstanceManager.cs b/src/Tgstation.Server.Host/Components/InstanceManager.cs index 60ee6c3faf..3e61925a27 100644 --- a/src/Tgstation.Server.Host/Components/InstanceManager.cs +++ b/src/Tgstation.Server.Host/Components/InstanceManager.cs @@ -1,13 +1,16 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Host.Components.Interop; using Tgstation.Server.Host.Components.Interop.Bridge; +using Tgstation.Server.Host.Configuration; using Tgstation.Server.Host.Core; using Tgstation.Server.Host.Database; using Tgstation.Server.Host.IO; @@ -78,6 +81,11 @@ namespace Tgstation.Server.Host.Components /// readonly IDictionary bridgeHandlers; + /// + /// The for the . + /// + readonly GeneralConfiguration generalConfiguration; + /// /// The for . /// @@ -104,6 +112,7 @@ namespace Tgstation.Server.Host.Components /// The value of /// The value of . /// The value of . + /// The containing the value of . /// The value of public InstanceManager( IInstanceFactory instanceFactory, @@ -114,6 +123,7 @@ namespace Tgstation.Server.Host.Components IServerControl serverControl, ISystemIdentityFactory systemIdentityFactory, IAsyncDelayer asyncDelayer, + IOptions generalConfigurationOptions, ILogger logger) { this.instanceFactory = instanceFactory ?? throw new ArgumentNullException(nameof(instanceFactory)); @@ -124,6 +134,7 @@ namespace Tgstation.Server.Host.Components this.serverControl = serverControl ?? throw new ArgumentNullException(nameof(serverControl)); this.systemIdentityFactory = systemIdentityFactory ?? throw new ArgumentNullException(nameof(systemIdentityFactory)); this.asyncDelayer = asyncDelayer ?? throw new ArgumentNullException(nameof(asyncDelayer)); + generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions)); this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); serverControl.RegisterForRestart(this); @@ -319,6 +330,10 @@ namespace Tgstation.Server.Host.Components /// private void CheckSystemCompatibility() { + if (generalConfiguration.UseExperimentalWatchdog && !Debugger.IsAttached) + throw new InvalidOperationException( + "The experimental watchdog is currently non-functional! Please disable the '{nameof(generalConfiguration.UseExperimentalWatchdog)}' option in your configuration!"); + using var systemIdentity = systemIdentityFactory.GetCurrent(); if (!systemIdentity.CanCreateSymlinks) throw new InvalidOperationException("The user running tgstation-server cannot create symlinks! Please try running as an administrative user!"); diff --git a/src/Tgstation.Server.Host/Setup/SetupWizard.cs b/src/Tgstation.Server.Host/Setup/SetupWizard.cs index adaa75906d..779f20493d 100644 --- a/src/Tgstation.Server.Host/Setup/SetupWizard.cs +++ b/src/Tgstation.Server.Host/Setup/SetupWizard.cs @@ -556,10 +556,7 @@ namespace Tgstation.Server.Host.Setup if (String.IsNullOrWhiteSpace(newGeneralConfiguration.GitHubAccessToken)) newGeneralConfiguration.GitHubAccessToken = null; - newGeneralConfiguration.UseExperimentalWatchdog = await PromptYesNo("Use the experimental watchdog (NOT RECOMMENDED)? (y/n): ", cancellationToken).ConfigureAwait(false); - - newGeneralConfiguration.UseBasicWatchdogOnWindows = true; - + // newGeneralConfiguration.UseExperimentalWatchdog = await PromptYesNo("Use the experimental watchdog (NOT RECOMMENDED)? (y/n): ", cancellationToken).ConfigureAwait(false); return newGeneralConfiguration; } From eb1816a1696f1a704094420ba9424756b388ab38 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Sun, 24 May 2020 17:09:58 -0400 Subject: [PATCH 4/6] Fix a log message --- .../Components/Repository/Repository.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/Repository/Repository.cs b/src/Tgstation.Server.Host/Components/Repository/Repository.cs index 4a6aef276e..8afac9fe04 100644 --- a/src/Tgstation.Server.Host/Components/Repository/Repository.cs +++ b/src/Tgstation.Server.Host/Components/Repository/Repository.cs @@ -265,12 +265,12 @@ namespace Tgstation.Server.Host.Components.Repository if (progressReporter == null) throw new ArgumentNullException(nameof(progressReporter)); - logger.LogDebug("Begin AddTestMerge: #{0} at {1} ({4}) by <{2} ({3})>", + logger.LogDebug("Begin AddTestMerge: #{0} at {1} ({2}) by <{3} ({4})>", testMergeParameters.Number, testMergeParameters.PullRequestRevision?.Substring(0, 7), + testMergeParameters.Comment, committerName, - committerEmail, - testMergeParameters.Comment); + committerEmail); if (!IsGitHubRepository) throw new InvalidOperationException("Test merging is only available on GitHub hosted origin repositories!"); From 745cd6bb7c715519d66862e8d5eb56fd316e9f73 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Sun, 24 May 2020 17:10:18 -0400 Subject: [PATCH 5/6] Fix database update when test merging --- .../Controllers/RepositoryController.cs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/Tgstation.Server.Host/Controllers/RepositoryController.cs b/src/Tgstation.Server.Host/Controllers/RepositoryController.cs index 03c5eed46e..de272d9d78 100644 --- a/src/Tgstation.Server.Host/Controllers/RepositoryController.cs +++ b/src/Tgstation.Server.Host/Controllers/RepositoryController.cs @@ -491,6 +491,7 @@ namespace Tgstation.Server.Host.Controllers async databaseContext => { databaseContext.Instances.Attach(attachedInstance); + var previousRevInfo = lastRevisionInfo; var needsUpdate = await LoadRevisionInformation( repo, databaseContext, @@ -516,13 +517,14 @@ namespace Tgstation.Server.Host.Controllers testMergeToAdd.MergedBy = mergedBy; + lastRevisionInfo.ActiveTestMerges.AddRange(previousRevInfo.ActiveTestMerges); lastRevisionInfo.ActiveTestMerges.Add(new RevInfoTestMerge { TestMerge = testMergeToAdd }); lastRevisionInfo.PrimaryTestMerge = testMergeToAdd; - databaseContext.Users.Attach(testMergeToAdd.MergedBy); + needsUpdate = true; } if (needsUpdate) @@ -532,12 +534,7 @@ namespace Tgstation.Server.Host.Controllers await CallLoadRevInfo().ConfigureAwait(false); // apply new rev info, tracking applied test merges - async Task UpdateRevInfo(Models.TestMerge testMergeToAdd = null) - { - var last = lastRevisionInfo; - await CallLoadRevInfo(testMergeToAdd, last.OriginCommitSha).ConfigureAwait(false); - lastRevisionInfo.ActiveTestMerges.AddRange(last.ActiveTestMerges); - } + Task UpdateRevInfo(Models.TestMerge testMergeToAdd = null) => CallLoadRevInfo(testMergeToAdd, lastRevisionInfo.OriginCommitSha); try { @@ -814,7 +811,7 @@ namespace Tgstation.Server.Host.Controllers doneSteps = 0; numSteps = 2; - // the stuff didn't make it into the db, forget what we've done and abort + // Forget what we've done and abort await repo.CheckoutObject(startReference ?? startSha, NextProgressReporter(), default).ConfigureAwait(false); if (startReference != null && repo.Head != startSha) await repo.ResetToSha(startSha, NextProgressReporter(), default).ConfigureAwait(false); From 5d00e23a977356cc7c634dcf3d381decf4cb8e1e Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Sun, 24 May 2020 17:57:28 -0400 Subject: [PATCH 6/6] Add auto refresh functionality to IServerClient --- src/Tgstation.Server.Client/ApiClient.cs | 102 ++++++++++++++---- .../ApiClientFactory.cs | 2 +- .../IApiClientFactory.cs | 3 +- .../IServerClientFactory.cs | 2 + .../ServerClientFactory.cs | 10 +- .../TestApiClient.cs | 4 +- .../Setup/TestSetupWizard.cs | 5 +- .../Tgstation.Server.Tests/IntegrationTest.cs | 2 +- 8 files changed, 95 insertions(+), 35 deletions(-) diff --git a/src/Tgstation.Server.Client/ApiClient.cs b/src/Tgstation.Server.Client/ApiClient.cs index d4026b7679..4fcdc1e981 100644 --- a/src/Tgstation.Server.Client/ApiClient.cs +++ b/src/Tgstation.Server.Client/ApiClient.cs @@ -44,6 +44,16 @@ namespace Tgstation.Server.Client /// readonly List requestLoggers; + /// + /// Backing field for + /// + readonly ApiHeaders? tokenRefreshHeaders; + + /// + /// The for refreshes. + /// + readonly SemaphoreSlim semaphoreSlim; + /// /// Backing field for /// @@ -101,18 +111,25 @@ namespace Tgstation.Server.Client /// /// The value of /// The value of - /// The value of - public ApiClient(IHttpClient httpClient, Uri url, ApiHeaders apiHeaders) + /// The value of + /// The value of + public ApiClient(IHttpClient httpClient, Uri url, ApiHeaders apiHeaders, ApiHeaders? tokenRefreshHeaders) { this.httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); Url = url ?? throw new ArgumentNullException(nameof(url)); headers = apiHeaders ?? throw new ArgumentNullException(nameof(apiHeaders)); + this.tokenRefreshHeaders = tokenRefreshHeaders; requestLoggers = new List(); + semaphoreSlim = new SemaphoreSlim(1); } /// - public void Dispose() => httpClient.Dispose(); + public void Dispose() + { + httpClient.Dispose(); + semaphoreSlim.Dispose(); + } /// /// Main request method @@ -122,9 +139,10 @@ namespace Tgstation.Server.Client /// The body of the request /// The method of the request /// The optional for the request + /// If this is a token refresh operation. /// The for the operation /// A resulting in the response on success - async Task RunRequest(string route, object? body, HttpMethod method, long? instanceId, CancellationToken cancellationToken) + async Task RunRequest(string route, object? body, HttpMethod method, long? instanceId, bool tokenRefresh, CancellationToken cancellationToken) { if (route == null) throw new ArgumentNullException(nameof(route)); @@ -141,11 +159,25 @@ namespace Tgstation.Server.Client if (body != null) request.Content = new StringContent(JsonConvert.SerializeObject(body, serializerSettings), Encoding.UTF8, ApiHeaders.ApplicationJson); - headers.SetRequestHeaders(request.Headers, instanceId); + var headersToUse = tokenRefresh ? tokenRefreshHeaders! : headers; + headersToUse.SetRequestHeaders(request.Headers, instanceId); - await Task.WhenAll(requestLoggers.Select(x => x.LogRequest(request, cancellationToken))).ConfigureAwait(false); + // This is meant to be a gate against token refresh operations + await semaphoreSlim.WaitAsync(cancellationToken).ConfigureAwait(false); + if(!tokenRefresh) + semaphoreSlim.Release(); - response = await httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false); + try + { + await Task.WhenAll(requestLoggers.Select(x => x.LogRequest(request, cancellationToken))).ConfigureAwait(false); + + response = await httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false); + } + finally + { + if (tokenRefresh) + semaphoreSlim.Release(); + } } using (response) @@ -155,7 +187,13 @@ namespace Tgstation.Server.Client var json = await response.Content.ReadAsStringAsync().ConfigureAwait(false); if (!response.IsSuccessStatusCode) + { + if (!tokenRefresh + && response.StatusCode == HttpStatusCode.Unauthorized + && await RefreshToken(cancellationToken).ConfigureAwait(false)) + return await RunRequest(route, body, method, instanceId, false, cancellationToken).ConfigureAwait(false); HandleBadResponse(response, json); + } if (String.IsNullOrWhiteSpace(json)) json = JsonConvert.SerializeObject(new object()); @@ -171,50 +209,68 @@ namespace Tgstation.Server.Client } } - /// - public Task Create(string route, CancellationToken cancellationToken) => RunRequest(route, new object(), HttpMethod.Put, null, cancellationToken); + async Task RefreshToken(CancellationToken cancellationToken) + { + if (tokenRefreshHeaders == null) + return false; + + try + { + var token = await RunRequest(Routes.Root, null, HttpMethod.Post, null, true, cancellationToken); + headers = new ApiHeaders(headers.UserAgent!, token.Bearer!); + } + catch (ClientException) + { + return false; + } + + return true; + } /// - public Task Read(string route, CancellationToken cancellationToken) => RunRequest(route, null, HttpMethod.Get, null, cancellationToken); + public Task Create(string route, CancellationToken cancellationToken) => RunRequest(route, new object(), HttpMethod.Put, null, false, cancellationToken); /// - public Task Update(string route, CancellationToken cancellationToken) => RunRequest(route, new object(), HttpMethod.Post, null, cancellationToken); + public Task Read(string route, CancellationToken cancellationToken) => RunRequest(route, null, HttpMethod.Get, null, false, cancellationToken); /// - public Task Update(string route, TBody body, CancellationToken cancellationToken) => RunRequest(route, body, HttpMethod.Post, null, cancellationToken); + public Task Update(string route, CancellationToken cancellationToken) => RunRequest(route, new object(), HttpMethod.Post, null, false, cancellationToken); /// - public Task Update(string route, TBody body, CancellationToken cancellationToken) => RunRequest(route, body, HttpMethod.Post, null, cancellationToken); + public Task Update(string route, TBody body, CancellationToken cancellationToken) => RunRequest(route, body, HttpMethod.Post, null, false, cancellationToken); /// - public Task Create(string route, TBody body, CancellationToken cancellationToken) => RunRequest(route, body, HttpMethod.Put, null, cancellationToken); + public Task Update(string route, TBody body, CancellationToken cancellationToken) => RunRequest(route, body, HttpMethod.Post, null, false, cancellationToken); /// - public Task Delete(string route, CancellationToken cancellationToken) => RunRequest(route, null, HttpMethod.Delete, null, cancellationToken); + public Task Create(string route, TBody body, CancellationToken cancellationToken) => RunRequest(route, body, HttpMethod.Put, null, false, cancellationToken); /// - public Task Create(string route, TBody body, long instanceId, CancellationToken cancellationToken) => RunRequest(route, body, HttpMethod.Put, instanceId, cancellationToken); + public Task Delete(string route, CancellationToken cancellationToken) => RunRequest(route, null, HttpMethod.Delete, null, false, cancellationToken); /// - public Task Read(string route, long instanceId, CancellationToken cancellationToken) => RunRequest(route, null, HttpMethod.Get, instanceId, cancellationToken); + public Task Create(string route, TBody body, long instanceId, CancellationToken cancellationToken) => RunRequest(route, body, HttpMethod.Put, instanceId, false, cancellationToken); /// - public Task Update(string route, TBody body, long instanceId, CancellationToken cancellationToken) => RunRequest(route, body, HttpMethod.Post, instanceId, cancellationToken); + public Task Read(string route, long instanceId, CancellationToken cancellationToken) => RunRequest(route, null, HttpMethod.Get, instanceId, false, cancellationToken); /// - public Task Delete(string route, long instanceId, CancellationToken cancellationToken) => RunRequest(route, null, HttpMethod.Delete, instanceId, cancellationToken); + public Task Update(string route, TBody body, long instanceId, CancellationToken cancellationToken) => RunRequest(route, body, HttpMethod.Post, instanceId, false, cancellationToken); /// - public Task Delete(string route, TBody body, long instanceId, CancellationToken cancellationToken) => RunRequest(route, body, HttpMethod.Delete, instanceId, cancellationToken); + public Task Delete(string route, long instanceId, CancellationToken cancellationToken) => RunRequest(route, null, HttpMethod.Delete, instanceId, false, cancellationToken); /// - public Task Delete(string route, long instanceId, CancellationToken cancellationToken) => RunRequest(route, null, HttpMethod.Delete, instanceId, cancellationToken); + public Task Delete(string route, TBody body, long instanceId, CancellationToken cancellationToken) => RunRequest(route, body, HttpMethod.Delete, instanceId, false, cancellationToken); /// - public Task Create(string route, long instanceId, CancellationToken cancellationToken) => RunRequest(route, new object(), HttpMethod.Put, instanceId, cancellationToken); + public Task Delete(string route, long instanceId, CancellationToken cancellationToken) => RunRequest(route, null, HttpMethod.Delete, instanceId, false, cancellationToken); /// - public Task Patch(string route, long instanceId, CancellationToken cancellationToken) => RunRequest(route, new object(), new HttpMethod("PATCH"), instanceId, cancellationToken); + public Task Create(string route, long instanceId, CancellationToken cancellationToken) => RunRequest(route, new object(), HttpMethod.Put, instanceId, false, cancellationToken); + + /// + public Task Patch(string route, long instanceId, CancellationToken cancellationToken) => RunRequest(route, new object(), HttpMethod.Patch, instanceId, false, cancellationToken); /// public void AddRequestLogger(IRequestLogger requestLogger) => requestLoggers.Add(requestLogger ?? throw new ArgumentNullException(nameof(requestLogger))); diff --git a/src/Tgstation.Server.Client/ApiClientFactory.cs b/src/Tgstation.Server.Client/ApiClientFactory.cs index 0b686b77c2..d3f137baeb 100644 --- a/src/Tgstation.Server.Client/ApiClientFactory.cs +++ b/src/Tgstation.Server.Client/ApiClientFactory.cs @@ -7,6 +7,6 @@ namespace Tgstation.Server.Client sealed class ApiClientFactory : IApiClientFactory { /// - public IApiClient CreateApiClient(Uri url, ApiHeaders apiHeaders) => new ApiClient(new HttpClient(), url, apiHeaders); + public IApiClient CreateApiClient(Uri url, ApiHeaders apiHeaders, ApiHeaders? tokenRefreshHeaders) => new ApiClient(new HttpClient(), url, apiHeaders, tokenRefreshHeaders); } } \ No newline at end of file diff --git a/src/Tgstation.Server.Client/IApiClientFactory.cs b/src/Tgstation.Server.Client/IApiClientFactory.cs index b8b5f91f96..3cc76b8d1f 100644 --- a/src/Tgstation.Server.Client/IApiClientFactory.cs +++ b/src/Tgstation.Server.Client/IApiClientFactory.cs @@ -13,7 +13,8 @@ namespace Tgstation.Server.Client /// /// The base /// The for the + /// The to use to generate a new . /// A new - IApiClient CreateApiClient(Uri url, ApiHeaders apiHeaders); + IApiClient CreateApiClient(Uri url, ApiHeaders apiHeaders, ApiHeaders? tokenRefreshHeaders); } } diff --git a/src/Tgstation.Server.Client/IServerClientFactory.cs b/src/Tgstation.Server.Client/IServerClientFactory.cs index a3de3cb8d6..83c8905f64 100644 --- a/src/Tgstation.Server.Client/IServerClientFactory.cs +++ b/src/Tgstation.Server.Client/IServerClientFactory.cs @@ -19,6 +19,7 @@ namespace Tgstation.Server.Client /// The password for the /// Optional initial s to add to the . /// Optional representing timeout for the connection + /// Attempt to refresh the received when it expires or becomes invalid. and will be stored in memory if this is . /// Optional for the operation /// A resulting in a new Task CreateFromLogin( @@ -27,6 +28,7 @@ namespace Tgstation.Server.Client string password, IEnumerable? requestLoggers = null, TimeSpan? timeout = null, + bool attemptLoginRefresh = true, CancellationToken cancellationToken = default); /// diff --git a/src/Tgstation.Server.Client/ServerClientFactory.cs b/src/Tgstation.Server.Client/ServerClientFactory.cs index 225b437d06..9dc883cca1 100644 --- a/src/Tgstation.Server.Client/ServerClientFactory.cs +++ b/src/Tgstation.Server.Client/ServerClientFactory.cs @@ -38,6 +38,7 @@ namespace Tgstation.Server.Client string password, IEnumerable? requestLoggers = null, TimeSpan? timeout = null, + bool attemptRefreshLogin = true, CancellationToken cancellationToken = default) { if (host == null) @@ -50,7 +51,8 @@ namespace Tgstation.Server.Client requestLoggers ??= Enumerable.Empty(); Token token; - using (var api = ApiClientFactory.CreateApiClient(host, new ApiHeaders(productHeaderValue, username, password))) + var loginHeaders = new ApiHeaders(productHeaderValue, username, password); + using (var api = ApiClientFactory.CreateApiClient(host, loginHeaders, null)) { foreach (var requestLogger in requestLoggers) api.AddRequestLogger(requestLogger); @@ -64,7 +66,9 @@ namespace Tgstation.Server.Client if (timeout.HasValue) client.Timeout = timeout.Value; - return client; + var apiHeaders = new ApiHeaders(productHeaderValue, token.Bearer!); + + return new ServerClient(ApiClientFactory.CreateApiClient(host, apiHeaders, loginHeaders), token); } /// @@ -77,7 +81,7 @@ namespace Tgstation.Server.Client if (token.Bearer == null) throw new ArgumentException("token.Bearer should not be null!", nameof(token)); - return new ServerClient(ApiClientFactory.CreateApiClient(host, new ApiHeaders(productHeaderValue, token.Bearer)), token); + return new ServerClient(ApiClientFactory.CreateApiClient(host, new ApiHeaders(productHeaderValue, token.Bearer), null), token); } } } diff --git a/tests/Tgstation.Server.Client.Tests/TestApiClient.cs b/tests/Tgstation.Server.Client.Tests/TestApiClient.cs index 3266d176fd..29486a7535 100644 --- a/tests/Tgstation.Server.Client.Tests/TestApiClient.cs +++ b/tests/Tgstation.Server.Client.Tests/TestApiClient.cs @@ -39,7 +39,7 @@ namespace Tgstation.Server.Client.Tests var httpClient = new Mock(); httpClient.Setup(x => x.SendAsync(It.IsNotNull(), It.IsAny())).Returns(Task.FromResult(response)); - var client = new ApiClient(httpClient.Object, new Uri("http://fake.com"), new ApiHeaders(new ProductHeaderValue("fake"), "fake")); + var client = new ApiClient(httpClient.Object, new Uri("http://fake.com"), new ApiHeaders(new ProductHeaderValue("fake"), "fake"), null); var result = await client.Read(Routes.Byond, default).ConfigureAwait(false); Assert.AreEqual(sample.Version, result.Version); @@ -64,7 +64,7 @@ namespace Tgstation.Server.Client.Tests var httpClient = new Mock(); httpClient.Setup(x => x.SendAsync(It.IsNotNull(), It.IsAny())).Returns(Task.FromResult(response)); - var client = new ApiClient(httpClient.Object, new Uri("http://fake.com"), new ApiHeaders(new ProductHeaderValue("fake"), "fake")); + var client = new ApiClient(httpClient.Object, new Uri("http://fake.com"), new ApiHeaders(new ProductHeaderValue("fake"), "fake"), null); await Assert.ThrowsExceptionAsync(() => client.Read(Routes.Byond, default)).ConfigureAwait(false); } diff --git a/tests/Tgstation.Server.Host.Tests/Setup/TestSetupWizard.cs b/tests/Tgstation.Server.Host.Tests/Setup/TestSetupWizard.cs index 847a81fb49..8d9d760faa 100644 --- a/tests/Tgstation.Server.Host.Tests/Setup/TestSetupWizard.cs +++ b/tests/Tgstation.Server.Host.Tests/Setup/TestSetupWizard.cs @@ -86,7 +86,7 @@ namespace Tgstation.Server.Host.Setup.Tests var mockFailCommand = new Mock(); mockFailCommand.Setup(x => x.ExecuteNonQueryAsync(It.IsAny())).Throws(new Exception()).Verifiable(); - void SetDbCommandCreator(Mock mock, Func creator) => mock.Protected().Setup("CreateDbCommand").Returns(creator).Verifiable(); + static void SetDbCommandCreator(Mock mock, Func creator) => mock.Protected().Setup("CreateDbCommand").Returns(creator).Verifiable(); var mockGoodDbConnection = new Mock(); mockGoodDbConnection.Setup(x => x.OpenAsync(It.IsAny())).Returns(Task.CompletedTask).Verifiable(); @@ -144,7 +144,6 @@ namespace Tgstation.Server.Host.Setup.Tests "-27", "5000", "fake token", - "y", //logging config "no", //cp config @@ -167,7 +166,6 @@ namespace Tgstation.Server.Host.Setup.Tests String.Empty, String.Empty, "n", - "y", //logging config "y", "not actually verified because lol mocks /../!@#$%^&*()/..///.", @@ -191,7 +189,6 @@ namespace Tgstation.Server.Host.Setup.Tests String.Empty, String.Empty, "y", - "y", "will faile", String.Empty, String.Empty, diff --git a/tests/Tgstation.Server.Tests/IntegrationTest.cs b/tests/Tgstation.Server.Tests/IntegrationTest.cs index e32ed5e505..523b101a15 100644 --- a/tests/Tgstation.Server.Tests/IntegrationTest.cs +++ b/tests/Tgstation.Server.Tests/IntegrationTest.cs @@ -144,7 +144,7 @@ namespace Tgstation.Server.Tests { try { - return await clientFactory.CreateFromLogin(server.Url, User.AdminName, User.DefaultAdminPassword).ConfigureAwait(false); + return await clientFactory.CreateFromLogin(server.Url, User.AdminName, User.DefaultAdminPassword, attemptLoginRefresh: false).ConfigureAwait(false); } catch (HttpRequestException) {