diff --git a/build/Version.props b/build/Version.props index 9e803532ab..14e8e4286c 100644 --- a/build/Version.props +++ b/build/Version.props @@ -3,14 +3,14 @@ - 4.9.2 + 4.10.0 3.0.0 - 8.3.0 - 9.2.0 + 9.0.0 + 9.0.0 + 10.0.0 6.0.2 5.3.0 1.1.1 1.2.0 - diff --git a/src/Tgstation.Server.Api/ApiHeaders.cs b/src/Tgstation.Server.Api/ApiHeaders.cs index 9ec9c575b1..aa44cc9c84 100644 --- a/src/Tgstation.Server.Api/ApiHeaders.cs +++ b/src/Tgstation.Server.Api/ApiHeaders.cs @@ -9,6 +9,7 @@ using System.Net.Mime; using System.Reflection; using System.Text; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Properties; namespace Tgstation.Server.Api { @@ -55,7 +56,7 @@ namespace Tgstation.Server.Api /// /// Get the version of the the caller is using /// - public static readonly Version Version = AssemblyName.Version.Semver(); + public static readonly Version Version = Version.Parse(ApiVersionAttribute.Instance.RawApiVersion); /// /// The instance being accessed diff --git a/src/Tgstation.Server.Api/DefaultCredentials.cs b/src/Tgstation.Server.Api/DefaultCredentials.cs new file mode 100644 index 0000000000..d816f24739 --- /dev/null +++ b/src/Tgstation.Server.Api/DefaultCredentials.cs @@ -0,0 +1,18 @@ +namespace Tgstation.Server.Api +{ + /// + /// Represents initial credentials used by the server. + /// + public static class DefaultCredentials + { + /// + /// The name of the default admin user + /// + public static readonly string AdminUserName = "Admin"; + + /// + /// The default admin password + /// + public static readonly string DefaultAdminUserPassword = "ISolemlySwearToDeleteTheDataDirectory"; + } +} diff --git a/src/Tgstation.Server.Api/Models/Byond.cs b/src/Tgstation.Server.Api/Models/Byond.cs deleted file mode 100644 index 6241e28948..0000000000 --- a/src/Tgstation.Server.Api/Models/Byond.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; - -namespace Tgstation.Server.Api.Models -{ - /// - /// Represents a BYOND installation. is used to upload custom BYOND version zip files, though must still be set. - /// - public sealed class Byond : FileTicketResult - { - /// - /// 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; } - - /// - /// The being used to install a new - /// - public Job? InstallJob { get; set; } - - /// - /// If a custom BYOND version is to be uploaded. - /// - public bool? UploadCustomZip { get; set; } - } -} diff --git a/src/Tgstation.Server.Api/Models/ChatChannel.cs b/src/Tgstation.Server.Api/Models/ChatChannel.cs index b3490556eb..549c9678b4 100644 --- a/src/Tgstation.Server.Api/Models/ChatChannel.cs +++ b/src/Tgstation.Server.Api/Models/ChatChannel.cs @@ -11,12 +11,14 @@ namespace Tgstation.Server.Api.Models /// The IRC channel name. Also potentially contains the channel passsword (if separated by a colon). /// If multiple copies of the same channel with different keys are added to the server, the one that will be used is undefined. /// + [ResponseOptions] [StringLength(Limits.MaximumIndexableStringLength, MinimumLength = 1)] public string? IrcChannel { get; set; } /// /// The Discord channel ID /// + [ResponseOptions] public ulong? DiscordChannelId { get; set; } /// @@ -40,6 +42,7 @@ namespace Tgstation.Server.Api.Models /// /// A custom tag users can define to group channels together /// + [ResponseOptions] [StringLength(Limits.MaximumStringLength)] public string? Tag { get; set; } } diff --git a/src/Tgstation.Server.Api/Models/DreamDaemon.cs b/src/Tgstation.Server.Api/Models/DreamDaemon.cs deleted file mode 100644 index d7d0567fe4..0000000000 --- a/src/Tgstation.Server.Api/Models/DreamDaemon.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Tgstation.Server.Api.Models.Internal; - -namespace Tgstation.Server.Api.Models -{ - /// - /// Represents an instance of BYOND's DreamDaemon game server. Create action starts the server. Delete action shuts down the server - /// - public sealed class DreamDaemon : DreamDaemonSettings - { - /// - /// The live revision - /// - public CompileJob? ActiveCompileJob { get; set; } - - /// - /// The next revision to go live - /// - public CompileJob? StagedCompileJob { get; set; } - - /// - /// The current . - /// - [EnumDataType(typeof(WatchdogStatus))] - public WatchdogStatus? Status { get; set; } - - /// - /// The current of . May be downgraded due to requirements of - /// - [EnumDataType(typeof(DreamDaemonSecurity))] - public DreamDaemonSecurity? CurrentSecurity { get; set; } - - /// - /// The port the running instance is set to - /// - public ushort? CurrentPort { get; set; } - - /// - /// The webclient status the running instance is set to - /// - public bool? CurrentAllowWebclient { get; set; } - - /// - /// If the server is undergoing a soft reset. This may be automatically set by changes to other fields - /// - public bool? SoftRestart { get; set; } - - /// - /// If the server is undergoing a soft shutdown - /// - public bool? SoftShutdown { get; set; } - - /// - /// If a dump of the active DreamDaemon executable should be created. - /// - public bool? CreateDump { get; set; } - } -} diff --git a/src/Tgstation.Server.Api/Models/EntityId.cs b/src/Tgstation.Server.Api/Models/EntityId.cs index 85d5cd0487..a33d3f294c 100644 --- a/src/Tgstation.Server.Api/Models/EntityId.cs +++ b/src/Tgstation.Server.Api/Models/EntityId.cs @@ -1,13 +1,15 @@ -namespace Tgstation.Server.Api.Models +namespace Tgstation.Server.Api.Models { /// - /// Common base of s, s, and s. + /// Common base of entities with IDs. /// public class EntityId { /// /// The ID of the entity. /// - public long Id { get; set; } + [RequestOptions(FieldPresence.Required)] + [RequestOptions(FieldPresence.Ignored, PutOnly = true)] + public virtual long? Id { get; set; } } } diff --git a/src/Tgstation.Server.Api/Models/ErrorCode.cs b/src/Tgstation.Server.Api/Models/ErrorCode.cs index 60e03b909d..311afec346 100644 --- a/src/Tgstation.Server.Api/Models/ErrorCode.cs +++ b/src/Tgstation.Server.Api/Models/ErrorCode.cs @@ -4,7 +4,7 @@ using System.ComponentModel; namespace Tgstation.Server.Api.Models { /// - /// Types of s that the API may return. + /// Types of s that the API may return. /// /// Entries marked with the are no longer in use but kept for reference. public enum ErrorCode : uint @@ -40,7 +40,7 @@ namespace Tgstation.Server.Api.Models BadHeaders, /// - /// Attempted to request a with an existing . + /// Attempted to request a with an existing . /// [Description("Cannot generate a bearer token using a bearer token!")] TokenWithToken, @@ -76,43 +76,43 @@ namespace Tgstation.Server.Api.Models ServerUpdateInProgress, /// - /// Attempted to change something other than the capitalization of a . + /// Attempted to change something other than the capitalization of a for a user. /// [Description("Can only change the capitalization of a user's name!")] UserNameChange, /// - /// Attempted to change a . + /// Attempted to change a . /// [Description("Cannot change a user's systemIdentifier!")] UserSidChange, /// - /// Attempted to create a with a and . + /// Attempted to create a user with a and . /// [Description("A user cannot have both a name and systemIdentifier!")] UserMismatchNameSid, /// - /// Attempted to create a with a and . + /// Attempted to create a user with a and . /// [Description("A user cannot have both a password and systemIdentifier!")] UserMismatchPasswordSid, /// - /// The given length was less than the server's configured minimum. + /// The given length was less than the server's configured minimum. /// [Description("The given password is less than the server's configured minimum password length!")] UserPasswordLength, /// - /// Attempted to create a with a ':' in the . + /// Attempted to create a user with a ':' character in the . /// [Description("User names cannot contain the ':' character!")] UserColonInName, /// - /// Attempted to create a with a or whitespace . + /// Attempted to create a user with a or whitespace . /// [Description("User's name is missing or invalid whitespace!")] UserMissingName, @@ -148,7 +148,7 @@ namespace Tgstation.Server.Api.Models InstanceLimitReached, /// - /// Attempted to create an with a whitespace . + /// Attempted to create an with a whitespace . /// [Description("Instance names cannot be whitespace!")] InstanceWhitespaceName, @@ -166,7 +166,7 @@ namespace Tgstation.Server.Api.Models RequiresPosixSystemIdentity, /// - /// A was updated. + /// A was updated. /// [Description("This existing file hash does not match, the file has beeen updated!")] ConfigurationFileUpdated, @@ -178,10 +178,11 @@ namespace Tgstation.Server.Api.Models ConfigurationDirectoryNotEmpty, /// - /// Tried to clone a repository with a missing property. + /// Currently unused. /// - [Description("Cannot clone repository with missing origin field!")] - RepoMissingOrigin, + [Obsolete("Unused", true)] + [Description("Unknown error code.")] + UnusedErrorCode1, /// /// One of and is set while the other isn't. @@ -214,25 +215,26 @@ namespace Tgstation.Server.Api.Models RepoMissing, /// - /// Attempted to and set at the same time. + /// Attempted to and set at the same time. /// [Description("Cannot checkoutSha and set reference at the same time!")] RepoMismatchShaAndReference, /// - /// Attempted to and at the same time. + /// Attempted to and at the same time. /// [Description("Cannot checkoutSha and updateFromOrigin at the same time!")] RepoMismatchShaAndUpdate, /// - /// Attempted to change the origin of an existing repository. + /// Currently unused. /// - [Description("Cannot change the origin of an existing repository, delete and recreate it instead!")] - RepoCantChangeOrigin, + [Obsolete("Unused", true)] + [Description("Unknown error code.")] + UnusedErrorCode2, /// - /// contained duplicate s. + /// contained duplicate s. /// [Description("The same test merge was present more than once or is already merged!")] RepoDuplicateTestMerge, @@ -262,43 +264,44 @@ namespace Tgstation.Server.Api.Models ApiInvalidPageOrPageSize, /// - /// A requested 's data does not match with its . + /// A requested 's data does not match with its . /// [Description("One or more of the channels for one or more of the provided chat bots do not have the correct channel data for their provider!")] ChatBotWrongChannelType, /// - /// was whitespace. + /// was whitespace. /// [Description("A chat bot's connection string cannot be whitespace!")] ChatBotWhitespaceConnectionString, /// - /// was whitespace. + /// Chat bot was whitespace. /// [Description("A chat bot's name cannot be whitespace!")] ChatBotWhitespaceName, /// - /// was during creation. + /// was during creation. /// [Description("Missing chat bot provider!")] ChatBotProviderMissing, /// - /// Tried to edit membership using . + /// Currently unused. /// - [Description("The " + Routes.UserGroup + " endpoint cannot edit group members. Please update each member user individually.")] - UserGroupControllerCantEditMembers, + [Obsolete("Unused", true)] + [Description("Unknown error code.")] + UnusedErrorCode3, /// - /// Attempted to add a when at or above the or it was set to something lower than the existing amount of . + /// Attempted to add a chat bot when at or above the or it was set to something lower than the existing amount of chat bots. /// [Description("Performing this operation would violate the instance's configured chatBotLimit!")] ChatBotMax, /// - /// Attempted to configure a with more s than the configured limit + /// Attempted to configure a chat bot with more s than the configured . /// [Description("Set amount of chatChannels exceeds the configured channelLimit!")] ChatBotMaxChannels, @@ -334,7 +337,7 @@ namespace Tgstation.Server.Api.Models DreamMakerInvalidValidation, /// - /// Tried to remove the last for a passwordless . + /// Tried to remove the last for a passwordless user. /// [Description("This user is passwordless and removing their oAuthConnections would leave them with no authentication method!")] CannotRemoveLastAuthenticationOption, @@ -364,19 +367,19 @@ namespace Tgstation.Server.Api.Models DreamMakerCompileJobInProgress, /// - /// Missing settings in database. + /// Missing in database. /// [Description("Could not retrieve DreamDaemon settings from the database!")] InstanceMissingDreamDaemonSettings, /// - /// Missing settings in database. + /// Missing in database. /// [Description("Could not retrieve DreamMaker settings from the database!")] InstanceMissingDreamMakerSettings, /// - /// Missing settings in database. + /// Missing in database. /// [Description("Could not retrieve Repository settings from the database!")] InstanceMissingRepositorySettings, @@ -400,7 +403,7 @@ namespace Tgstation.Server.Api.Models RepoCannotAuthenticate, /// - /// Cannot perform operation while not on a . + /// Cannot perform operation while not on a . /// [Description("This git operation requires the repository HEAD to currently be on a tracked reference!")] RepoReferenceRequired, @@ -412,7 +415,7 @@ namespace Tgstation.Server.Api.Models WatchdogRunning, /// - /// Attempted to start the watchdog with a corrupted . + /// Attempted to start the watchdog with a corrupted . /// [Description("Cannot launch active compile job as it is missing or corrupted!")] WatchdogCompileJobCorrupted, @@ -436,7 +439,7 @@ namespace Tgstation.Server.Api.Models RepoUnsupportedTestMergeRemote, /// - /// Either or was in one when it should have been the other. + /// Either or was in one when it should have been the other. /// [Description("The value set for checkoutSha or reference should be in the other field!")] RepoSwappedShaOrReference, @@ -448,7 +451,7 @@ namespace Tgstation.Server.Api.Models RepoMergeConflict, /// - /// The current does not track a remote reference. + /// The current does not track a remote reference. /// [Description("The repository's current reference is unsuitable for this operation as it does not track a remote reference!")] RepoReferenceNotTracking, @@ -460,13 +463,13 @@ namespace Tgstation.Server.Api.Models RepoTestMergeConflict, /// - /// Attempted to create an instance outside of the . + /// Attempted to create an instance outside of the . /// [Description("The new instance's path is not under a white-listed path.")] InstanceNotAtWhitelistedPath, /// - /// Attempted to make a DreamDaemon update with both and set. + /// Attempted to make a DreamDaemon update with both and set. /// [Description("Cannot set both softShutdown and softReboot at once!")] DreamDaemonDoubleSoft, @@ -580,7 +583,7 @@ namespace Tgstation.Server.Api.Models PortNotAvailable, /// - /// Attempted to set for the admin user. + /// Attempted to set for the admin user. /// [Description("The admin user cannot use OAuth connections!")] AdminUserCannotOAuth, @@ -592,31 +595,31 @@ namespace Tgstation.Server.Api.Models OAuthProviderDisabled, /// - /// A requiring a file upload did not receive it before timing out. + /// A job requiring a file upload did not receive it before timing out. /// [Description("The job did not receive a required upload before timing out!")] FileUploadExpired, /// - /// Tried to update a to have both a and + /// Tried to update a user to have both a and /// [Description("A user may not have both a permissionSet and group!")] UserGroupAndPermissionSet, /// - /// Tried to delete a non-empty . + /// Tried to delete a non-empty user group. /// [Description("Cannot delete the user group as it is not empty!")] UserGroupNotEmpty, /// - /// Attempted to create an but the configured limit has been reached. + /// Attempted to create a user but the configured limit has been reached. /// [Description("The user cannot be created because the configured limit has been reached!")] UserLimitReached, /// - /// Attempted to create an but the configured limit has been reached. + /// Attempted to create a user group but the configured limit has been reached. /// [Description("The user group cannot be created because the configured limit has been reached!")] UserGroupLimitReached, diff --git a/src/Tgstation.Server.Api/Models/FieldPresence.cs b/src/Tgstation.Server.Api/Models/FieldPresence.cs new file mode 100644 index 0000000000..4817b8965a --- /dev/null +++ b/src/Tgstation.Server.Api/Models/FieldPresence.cs @@ -0,0 +1,23 @@ +namespace Tgstation.Server.Api.Models +{ + /// + /// Indicates whether a request field is or . + /// + public enum FieldPresence + { + /// + /// The field is optional + /// + Optional, + + /// + /// The field is required. + /// + Required, + + /// + /// The field is ignored or should not appear. + /// + Ignored + } +} diff --git a/src/Tgstation.Server.Api/Models/ConfigurationFile.cs b/src/Tgstation.Server.Api/Models/IConfigurationFile.cs similarity index 59% rename from src/Tgstation.Server.Api/Models/ConfigurationFile.cs rename to src/Tgstation.Server.Api/Models/IConfigurationFile.cs index 7d5e0168e9..a1c59c0449 100644 --- a/src/Tgstation.Server.Api/Models/ConfigurationFile.cs +++ b/src/Tgstation.Server.Api/Models/IConfigurationFile.cs @@ -5,27 +5,18 @@ namespace Tgstation.Server.Api.Models /// /// Represents a game configuration file. Create and delete actions uncerimonuously overwrite/delete files /// - public sealed class ConfigurationFile : FileTicketResult + public interface IConfigurationFile { /// - /// The path to the file + /// The path to the file. /// [StringLength(Limits.MaximumStringLength)] public string? Path { get; set; } - /// - /// If access to the file was denied for the operation - /// - public bool? AccessDenied { get; set; } - - /// - /// If represents a directory - /// - public bool? IsDirectory { get; set; } - /// /// 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 /// + [ResponseOptions] public string? LastReadHash { get; set; } } } diff --git a/src/Tgstation.Server.Api/Models/Instance.cs b/src/Tgstation.Server.Api/Models/Instance.cs index 60856b07ea..2f5ad89701 100644 --- a/src/Tgstation.Server.Api/Models/Instance.cs +++ b/src/Tgstation.Server.Api/Models/Instance.cs @@ -1,34 +1,28 @@ -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; namespace Tgstation.Server.Api.Models { /// /// Metadata about a server instance /// - public class Instance : EntityId + public abstract class Instance : NamedEntity { - /// - /// The name of the - /// - [Required] - [StringLength(Limits.MaximumStringLength)] - 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] + [RequestOptions(FieldPresence.Required, PutOnly = true)] public string? Path { get; set; } /// /// If the is online /// [Required] + [RequestOptions(FieldPresence.Ignored, PutOnly = true)] public bool? Online { get; set; } /// - /// If can be used on the + /// If s can be used on the /// [Required] [EnumDataType(typeof(ConfigurationType))] @@ -41,30 +35,9 @@ namespace Tgstation.Server.Api.Models public uint? AutoUpdateInterval { get; set; } /// - /// The maximum number of s the may contain. + /// The maximum number of chat bots the may contain. /// [Required] public ushort? ChatBotLimit { get; set; } - - /// - /// The representing a change of - /// - /// 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; } - - /// - /// Create a clone of the essential metadata - /// - /// A clone of the essential metadata - public Instance CloneMetadata() => new Instance - { - Id = Id, - Name = Name, - Path = Path, - Online = Online, - ConfigurationType = ConfigurationType, - AutoUpdateInterval = AutoUpdateInterval - }; } } diff --git a/src/Tgstation.Server.Api/Models/ChatBot.cs b/src/Tgstation.Server.Api/Models/Internal/ChatBotApiBase.cs similarity index 76% rename from src/Tgstation.Server.Api/Models/ChatBot.cs rename to src/Tgstation.Server.Api/Models/Internal/ChatBotApiBase.cs index c0034d32bb..0ec152ab06 100644 --- a/src/Tgstation.Server.Api/Models/ChatBot.cs +++ b/src/Tgstation.Server.Api/Models/Internal/ChatBotApiBase.cs @@ -1,11 +1,11 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; -namespace Tgstation.Server.Api.Models +namespace Tgstation.Server.Api.Models.Internal { /// - public sealed class ChatBot : Internal.ChatBot + public abstract class ChatBotApiBase : ChatBotSettings { /// /// Channels the Discord bot should listen/announce in @@ -13,9 +13,9 @@ namespace Tgstation.Server.Api.Models public ICollection? Channels { get; set; } /// - /// Validates are correct for the + /// Validates are correct for the /// - /// if the are valid for the , otherwise + /// if the are valid for the , otherwise public bool ValidateProviderChannelTypes() { if (!Provider.HasValue) diff --git a/src/Tgstation.Server.Api/Models/Internal/ChatBot.cs b/src/Tgstation.Server.Api/Models/Internal/ChatBotSettings.cs similarity index 85% rename from src/Tgstation.Server.Api/Models/Internal/ChatBot.cs rename to src/Tgstation.Server.Api/Models/Internal/ChatBotSettings.cs index 626a526b6d..0a29dd84ae 100644 --- a/src/Tgstation.Server.Api/Models/Internal/ChatBot.cs +++ b/src/Tgstation.Server.Api/Models/Internal/ChatBotSettings.cs @@ -6,15 +6,8 @@ namespace Tgstation.Server.Api.Models.Internal /// /// Manage the server chat bots /// - public class ChatBot : EntityId + public abstract class ChatBotSettings : NamedEntity { - /// - /// The name of the connection - /// - [Required] - [StringLength(Limits.MaximumIndexableStringLength, MinimumLength = 1)] - public string? Name { get; set; } - /// /// If the connection is enabled /// @@ -28,7 +21,7 @@ namespace Tgstation.Server.Api.Models.Internal public uint? ReconnectionInterval { get; set; } /// - /// The maximum number of s the may contain. + /// The maximum number of s the may contain. /// [Required] public ushort? ChannelLimit { get; set; } @@ -37,6 +30,7 @@ namespace Tgstation.Server.Api.Models.Internal /// The used for the connection /// [Required] + [RequestOptions(FieldPresence.Required, PutOnly = true)] [EnumDataType(typeof(ChatProvider))] public ChatProvider? Provider { get; set; } @@ -44,13 +38,15 @@ namespace Tgstation.Server.Api.Models.Internal /// The information used to connect to the /// [Required] + [RequestOptions(FieldPresence.Required, PutOnly = true)] + [ResponseOptions] [StringLength(Limits.MaximumStringLength)] public string? ConnectionString { get; set; } /// /// Get the which maps to the . /// - /// A for the . + /// A for the . public ChatConnectionStringBuilder? CreateConnectionStringBuilder() { if (ConnectionString == null) @@ -64,7 +60,7 @@ namespace Tgstation.Server.Api.Models.Internal } /// - /// Set the for the . Also updates the . + /// Set the for the . Also updates the . /// /// The optional . public void SetConnectionStringBuilder(ChatConnectionStringBuilder stringBuilder) diff --git a/src/Tgstation.Server.Api/Models/Internal/ChatConnectionStringBuilder.cs b/src/Tgstation.Server.Api/Models/Internal/ChatConnectionStringBuilder.cs index 51d8025f73..36c1f202aa 100644 --- a/src/Tgstation.Server.Api/Models/Internal/ChatConnectionStringBuilder.cs +++ b/src/Tgstation.Server.Api/Models/Internal/ChatConnectionStringBuilder.cs @@ -1,19 +1,19 @@ -namespace Tgstation.Server.Api.Models.Internal +namespace Tgstation.Server.Api.Models.Internal { /// - /// Helper for building s + /// Helper for building s /// public abstract class ChatConnectionStringBuilder { /// - /// If the evaluates to a valid + /// If the evaluates to a valid /// public abstract bool Valid { get; } /// - /// Gets the associated with the + /// Gets the associated with the /// - /// The associated with the + /// The associated with the public abstract override string ToString(); } -} \ No newline at end of file +} diff --git a/src/Tgstation.Server.Api/Models/Internal/CompileJob.cs b/src/Tgstation.Server.Api/Models/Internal/CompileJob.cs index e57bc17784..425d63eec2 100644 --- a/src/Tgstation.Server.Api/Models/Internal/CompileJob.cs +++ b/src/Tgstation.Server.Api/Models/Internal/CompileJob.cs @@ -5,9 +5,9 @@ using System.ComponentModel.DataAnnotations.Schema; namespace Tgstation.Server.Api.Models.Internal { /// - /// Represents a run of + /// Represents a deployment run. /// - public class CompileJob : EntityId + public abstract class CompileJob : EntityId { /// /// The .dme file used for compilation @@ -28,14 +28,16 @@ namespace Tgstation.Server.Api.Models.Internal public Guid? DirectoryName { get; set; } /// - /// The minimum required to run the 's output + /// The minimum required to run the 's output. /// + [ResponseOptions] public DreamDaemonSecurity? MinimumSecurityLevel { get; set; } /// /// The DMAPI . /// [NotMapped] + [ResponseOptions] public virtual Version? DMApiVersion { get; set; } } } diff --git a/src/Tgstation.Server.Api/Models/Internal/DreamDaemonApiBase.cs b/src/Tgstation.Server.Api/Models/Internal/DreamDaemonApiBase.cs new file mode 100644 index 0000000000..4f31e79760 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Internal/DreamDaemonApiBase.cs @@ -0,0 +1,20 @@ +namespace Tgstation.Server.Api.Models.Internal +{ + /// + /// Base class for DreamDaemon API models. + /// + public abstract class DreamDaemonApiBase : DreamDaemonSettings + { + /// + /// If the server is undergoing a soft reset. This may be automatically set by changes to other fields + /// + [ResponseOptions] + public bool? SoftRestart { get; set; } + + /// + /// If the server is undergoing a soft shutdown + /// + [ResponseOptions] + public bool? SoftShutdown { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/Internal/DreamDaemonLaunchParameters.cs b/src/Tgstation.Server.Api/Models/Internal/DreamDaemonLaunchParameters.cs index f57e215e8a..9ef1614383 100644 --- a/src/Tgstation.Server.Api/Models/Internal/DreamDaemonLaunchParameters.cs +++ b/src/Tgstation.Server.Api/Models/Internal/DreamDaemonLaunchParameters.cs @@ -12,19 +12,22 @@ namespace Tgstation.Server.Api.Models.Internal /// If the BYOND web client can be used to connect to the game server /// [Required] + [ResponseOptions] public bool? AllowWebClient { get; set; } /// - /// The level of + /// The level of DreamDaemon. /// [Required] + [ResponseOptions] [EnumDataType(typeof(DreamDaemonSecurity))] public DreamDaemonSecurity? SecurityLevel { get; set; } /// - /// The first port uses. This should be the publically advertised port + /// The port DreamDaemon uses. This should be publically accessible. /// [Required] + [ResponseOptions] [Range(1, UInt16.MaxValue)] public ushort? Port { get; set; } @@ -32,6 +35,7 @@ namespace Tgstation.Server.Api.Models.Internal /// The DreamDaemon startup timeout in seconds /// [Required] + [ResponseOptions] [Range(1, UInt32.MaxValue)] public uint? StartupTimeout { get; set; } @@ -39,12 +43,14 @@ namespace Tgstation.Server.Api.Models.Internal /// The number of seconds between each watchdog heartbeat. 0 disables. /// [Required] + [ResponseOptions] public uint? HeartbeatSeconds { get; set; } /// /// The timeout for sending and receiving BYOND topics in milliseconds. /// [Required] + [ResponseOptions] [Range(1, UInt32.MaxValue)] public uint? TopicRequestTimeout { get; set; } @@ -52,6 +58,7 @@ namespace Tgstation.Server.Api.Models.Internal /// Parameters string for DreamDaemon. /// [Required] + [ResponseOptions] [StringLength(Limits.MaximumStringLength)] public string? AdditionalParameters { get; set; } diff --git a/src/Tgstation.Server.Api/Models/Internal/DreamDaemonSettings.cs b/src/Tgstation.Server.Api/Models/Internal/DreamDaemonSettings.cs index fb90af9a8c..5d00f5852b 100644 --- a/src/Tgstation.Server.Api/Models/Internal/DreamDaemonSettings.cs +++ b/src/Tgstation.Server.Api/Models/Internal/DreamDaemonSettings.cs @@ -1,16 +1,17 @@ -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; namespace Tgstation.Server.Api.Models.Internal { /// - /// Configurable settings for + /// Configurable settings for DreamDaemon. /// public class DreamDaemonSettings : DreamDaemonLaunchParameters { /// - /// If starts when it's starts + /// If the watchdog starts when it's starts /// [Required] + [ResponseOptions] public bool? AutoStart { get; set; } } } diff --git a/src/Tgstation.Server.Api/Models/DreamMaker.cs b/src/Tgstation.Server.Api/Models/Internal/DreamMakerSettings.cs similarity index 77% rename from src/Tgstation.Server.Api/Models/DreamMaker.cs rename to src/Tgstation.Server.Api/Models/Internal/DreamMakerSettings.cs index 9d1ca2aac3..32655f3574 100644 --- a/src/Tgstation.Server.Api/Models/DreamMaker.cs +++ b/src/Tgstation.Server.Api/Models/Internal/DreamMakerSettings.cs @@ -1,16 +1,17 @@ -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; -namespace Tgstation.Server.Api.Models +namespace Tgstation.Server.Api.Models.Internal { /// /// Represents the state of the DreamMaker compiler. Create action starts a new compile. Delete action cancels the current compile /// - public class DreamMaker + public abstract class DreamMakerSettings { /// - /// The .dme file tries to compile with without the extension + /// The name of the .dme file the server tries to compile with without the extension. /// [StringLength(Limits.MaximumStringLength)] + [ResponseOptions] public string? ProjectName { get; set; } /// diff --git a/src/Tgstation.Server.Api/Models/InstancePermissionSet.cs b/src/Tgstation.Server.Api/Models/Internal/InstancePermissionSet.cs similarity index 93% rename from src/Tgstation.Server.Api/Models/InstancePermissionSet.cs rename to src/Tgstation.Server.Api/Models/Internal/InstancePermissionSet.cs index eb1a5b80f9..ff2c24fa4a 100644 --- a/src/Tgstation.Server.Api/Models/InstancePermissionSet.cs +++ b/src/Tgstation.Server.Api/Models/Internal/InstancePermissionSet.cs @@ -1,16 +1,17 @@ using System.ComponentModel.DataAnnotations; using Tgstation.Server.Api.Rights; -namespace Tgstation.Server.Api.Models +namespace Tgstation.Server.Api.Models.Internal { /// /// Represents a s permissions in an /// - public class InstancePermissionSet + public abstract class InstancePermissionSet { /// /// The of the the belongs to /// + [RequestOptions(FieldPresence.Required)] public long PermissionSetId { get; set; } /// diff --git a/src/Tgstation.Server.Api/Models/Internal/Job.cs b/src/Tgstation.Server.Api/Models/Internal/Job.cs index ac3b00f1b8..254be63dc3 100644 --- a/src/Tgstation.Server.Api/Models/Internal/Job.cs +++ b/src/Tgstation.Server.Api/Models/Internal/Job.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel.DataAnnotations; using Tgstation.Server.Api.Rights; @@ -18,11 +18,13 @@ namespace Tgstation.Server.Api.Models.Internal /// /// The associated with the if any. /// + [ResponseOptions] public ErrorCode? ErrorCode { get; set; } /// /// Details of any exceptions caught during the /// + [ResponseOptions] public string? ExceptionDetails { get; set; } /// @@ -34,6 +36,7 @@ namespace Tgstation.Server.Api.Models.Internal /// /// When the stopped /// + [ResponseOptions] public DateTimeOffset? StoppedAt { get; set; } /// @@ -45,11 +48,13 @@ namespace Tgstation.Server.Api.Models.Internal /// /// The of if it can be cancelled /// + [ResponseOptions] public RightsType? CancelRightsType { get; set; } /// /// The required to cancel the /// + [ResponseOptions] public ulong? CancelRight { get; set; } } -} \ No newline at end of file +} diff --git a/src/Tgstation.Server.Api/Models/Internal/RepositoryApiBase.cs b/src/Tgstation.Server.Api/Models/Internal/RepositoryApiBase.cs new file mode 100644 index 0000000000..2a52068888 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Internal/RepositoryApiBase.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; + +namespace Tgstation.Server.Api.Models.Internal +{ + /// + /// Base for repository models. + /// + public class RepositoryApiBase : RepositorySettings + { + /// + /// The branch or tag HEAD points to. + /// + [StringLength(Limits.MaximumStringLength)] + [ResponseOptions] + public string? Reference { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/Internal/RepositorySettings.cs b/src/Tgstation.Server.Api/Models/Internal/RepositorySettings.cs index 4a852a901b..de0ff3ff23 100644 --- a/src/Tgstation.Server.Api/Models/Internal/RepositorySettings.cs +++ b/src/Tgstation.Server.Api/Models/Internal/RepositorySettings.cs @@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations; namespace Tgstation.Server.Api.Models.Internal { /// - /// Represents configurable settings for a + /// Represents configurable settings for a git repository. /// public class RepositorySettings { @@ -26,12 +26,14 @@ namespace Tgstation.Server.Api.Models.Internal /// The username to access the git repository with /// [StringLength(Limits.MaximumStringLength)] + [ResponseOptions] public string? AccessUser { get; set; } /// /// The token/password to access the git repository with /// [StringLength(Limits.MaximumStringLength)] + [ResponseOptions(Presence = FieldPresence.Ignored)] public string? AccessToken { get; set; } /// diff --git a/src/Tgstation.Server.Api/Models/Internal/ServerInformation.cs b/src/Tgstation.Server.Api/Models/Internal/ServerInformationBase.cs similarity index 75% rename from src/Tgstation.Server.Api/Models/Internal/ServerInformation.cs rename to src/Tgstation.Server.Api/Models/Internal/ServerInformationBase.cs index 21f4ed26eb..f1c486214c 100644 --- a/src/Tgstation.Server.Api/Models/Internal/ServerInformation.cs +++ b/src/Tgstation.Server.Api/Models/Internal/ServerInformationBase.cs @@ -3,9 +3,9 @@ using System.Collections.Generic; namespace Tgstation.Server.Api.Models.Internal { /// - /// Base class for . + /// Base class for . /// - public abstract class ServerInformation + public abstract class ServerInformationBase { /// /// Minimum length of database user passwords. @@ -18,18 +18,19 @@ namespace Tgstation.Server.Api.Models.Internal public uint InstanceLimit { get; set; } /// - /// The maximum number of s allowed. + /// The maximum number of users allowed. /// public uint UserLimit { get; set; } /// - /// The maximum number of s allowed. + /// The maximum number of user groups allowed. /// public uint UserGroupLimit { get; set; } /// /// Limits the locations instances may be created or attached from. /// + [ResponseOptions] public ICollection? ValidInstancePaths { get; set; } } } diff --git a/src/Tgstation.Server.Api/Models/Internal/ServerUpdateRequest.cs b/src/Tgstation.Server.Api/Models/Internal/ServerUpdateRequest.cs new file mode 100644 index 0000000000..1090f47f16 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Internal/ServerUpdateRequest.cs @@ -0,0 +1,7 @@ +namespace Tgstation.Server.Api.Models.Request +{ + /// + public sealed class ServerUpdateRequest : ServerUpdate + { + } +} diff --git a/src/Tgstation.Server.Api/Models/Internal/TestMerge.cs b/src/Tgstation.Server.Api/Models/Internal/TestMergeApiBase.cs similarity index 69% rename from src/Tgstation.Server.Api/Models/Internal/TestMerge.cs rename to src/Tgstation.Server.Api/Models/Internal/TestMergeApiBase.cs index 76ee553023..4c2556d4e6 100644 --- a/src/Tgstation.Server.Api/Models/Internal/TestMerge.cs +++ b/src/Tgstation.Server.Api/Models/Internal/TestMergeApiBase.cs @@ -6,15 +6,15 @@ namespace Tgstation.Server.Api.Models.Internal /// /// Represents a test merge of a remote "pull request". /// - public class TestMerge : TestMergeBase + public class TestMergeApiBase : TestMergeModelBase { /// - /// The ID of the + /// The ID of the /// public long Id { get; set; } /// - /// When the was created + /// When the was created /// [Required] public DateTimeOffset MergedAt { get; set; } diff --git a/src/Tgstation.Server.Api/Models/Internal/TestMergeBase.cs b/src/Tgstation.Server.Api/Models/Internal/TestMergeModelBase.cs similarity index 76% rename from src/Tgstation.Server.Api/Models/Internal/TestMergeBase.cs rename to src/Tgstation.Server.Api/Models/Internal/TestMergeModelBase.cs index c8b87ee4d2..fadd01d1e2 100644 --- a/src/Tgstation.Server.Api/Models/Internal/TestMergeBase.cs +++ b/src/Tgstation.Server.Api/Models/Internal/TestMergeModelBase.cs @@ -6,7 +6,7 @@ namespace Tgstation.Server.Api.Models.Internal /// /// Layer of test merge data required internally /// - public abstract class TestMergeBase : TestMergeParameters + public abstract class TestMergeModelBase : TestMergeParameters { /// /// The title of the test merge source. @@ -35,15 +35,15 @@ namespace Tgstation.Server.Api.Models.Internal public string? Author { get; set; } /// - /// Construct a + /// Construct a /// - protected TestMergeBase() { } + protected TestMergeModelBase() { } /// - /// Construct a from a + /// Construct a from a /// - /// The to copy data from - protected TestMergeBase(TestMergeBase copy) + /// The to copy data from + protected TestMergeModelBase(TestMergeModelBase copy) { if (copy == null) throw new ArgumentNullException(nameof(copy)); diff --git a/src/Tgstation.Server.Api/Models/Internal/UserApiBase.cs b/src/Tgstation.Server.Api/Models/Internal/UserApiBase.cs new file mode 100644 index 0000000000..ab1d096417 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Internal/UserApiBase.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; + +namespace Tgstation.Server.Api.Models.Internal +{ + /// + public abstract class UserApiBase : UserModelBase + { + /// + /// List of s associated with the user. + /// + public ICollection? OAuthConnections { get; set; } + + /// + /// The directly associated with the user. + /// + [ResponseOptions] + public PermissionSet? PermissionSet { get; set; } + + /// + /// The asociated with the user, if any. + /// + [ResponseOptions] + public UserGroup? Group { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/Internal/UserBase.cs b/src/Tgstation.Server.Api/Models/Internal/UserBase.cs deleted file mode 100644 index a2301ddd08..0000000000 --- a/src/Tgstation.Server.Api/Models/Internal/UserBase.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace Tgstation.Server.Api.Models.Internal -{ - /// - /// Base class for . - /// - public class UserBase - { - /// - /// The ID of the - /// - [Required] - public long? Id { get; set; } - - /// - /// The name of the - /// - [Required] - [StringLength(Limits.MaximumIndexableStringLength, MinimumLength = 1)] - public string? Name { get; set; } - } -} diff --git a/src/Tgstation.Server.Api/Models/Internal/UserGroup.cs b/src/Tgstation.Server.Api/Models/Internal/UserGroup.cs index 25c9520ba1..11fc1b5e89 100644 --- a/src/Tgstation.Server.Api/Models/Internal/UserGroup.cs +++ b/src/Tgstation.Server.Api/Models/Internal/UserGroup.cs @@ -1,9 +1,9 @@ namespace Tgstation.Server.Api.Models.Internal { /// - /// Represents a group of s. + /// Represents a group of users. /// - public class UserGroup : UserGroupBase + public class UserGroup : NamedEntity { /// /// The of the . diff --git a/src/Tgstation.Server.Api/Models/Internal/UserGroupBase.cs b/src/Tgstation.Server.Api/Models/Internal/UserGroupBase.cs deleted file mode 100644 index 0f144dc95d..0000000000 --- a/src/Tgstation.Server.Api/Models/Internal/UserGroupBase.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace Tgstation.Server.Api.Models.Internal -{ - /// - /// Base class for . - /// - public abstract class UserGroupBase : EntityId - { - /// - /// The name of the . - /// - [Required] - [StringLength(Limits.MaximumIndexableStringLength, MinimumLength = 1)] - public string? Name { get; set; } - } -} diff --git a/src/Tgstation.Server.Api/Models/Internal/User.cs b/src/Tgstation.Server.Api/Models/Internal/UserModelBase.cs similarity index 55% rename from src/Tgstation.Server.Api/Models/Internal/User.cs rename to src/Tgstation.Server.Api/Models/Internal/UserModelBase.cs index ac37d2e28f..f30e5f84d3 100644 --- a/src/Tgstation.Server.Api/Models/Internal/User.cs +++ b/src/Tgstation.Server.Api/Models/Internal/UserModelBase.cs @@ -4,25 +4,27 @@ using System.ComponentModel.DataAnnotations; namespace Tgstation.Server.Api.Models.Internal { /// - /// Represents a server + /// Represents a server user. /// - public abstract class User : UserBase + public abstract class UserModelBase : UserName { /// - /// If the is enabled since users cannot be deleted. System users cannot be disabled + /// If the is enabled since users cannot be deleted. System users cannot be disabled /// [Required] public bool? Enabled { get; set; } /// - /// When the was created + /// When the was created /// [Required] + [RequestOptions(FieldPresence.Ignored)] public DateTimeOffset? CreatedAt { get; set; } /// - /// The SID/UID of the on Windows/POSIX respectively + /// The SID/UID of the on Windows/POSIX respectively /// + [ResponseOptions] [StringLength(Limits.MaximumIndexableStringLength, MinimumLength = 1)] public string? SystemIdentifier { get; set; } } diff --git a/src/Tgstation.Server.Api/Models/Job.cs b/src/Tgstation.Server.Api/Models/Job.cs deleted file mode 100644 index 9869be84ab..0000000000 --- a/src/Tgstation.Server.Api/Models/Job.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Tgstation.Server.Api.Models -{ - /// - /// Represents a long running job on the server. Model is read-only, updates attempt to cancel the job - /// - public sealed class Job : Internal.Job - { - /// - /// The that started the job - /// - public User? StartedBy { get; set; } - - /// - /// The that cancelled the job - /// - public User? CancelledBy { get; set; } - - /// - /// Optional progress between 0 and 100 inclusive - /// - public int? Progress { get; set; } - } -} diff --git a/src/Tgstation.Server.Api/Models/Limits.cs b/src/Tgstation.Server.Api/Models/Limits.cs index 6acd8a35bf..d485dc42ce 100644 --- a/src/Tgstation.Server.Api/Models/Limits.cs +++ b/src/Tgstation.Server.Api/Models/Limits.cs @@ -13,7 +13,7 @@ namespace Tgstation.Server.Api.Models public const int MaximumStringLength = 10000; /// - /// Length limit for s. + /// Length limit for s. /// public const int MaximumIndexableStringLength = 100; diff --git a/src/Tgstation.Server.Api/Models/NamedEntity.cs b/src/Tgstation.Server.Api/Models/NamedEntity.cs new file mode 100644 index 0000000000..42dc14d95c --- /dev/null +++ b/src/Tgstation.Server.Api/Models/NamedEntity.cs @@ -0,0 +1,18 @@ +using System.ComponentModel.DataAnnotations; + +namespace Tgstation.Server.Api.Models +{ + /// + /// Base class for named entities. + /// + public abstract class NamedEntity : EntityId + { + /// + /// The name of the entity represented by the . + /// + [Required] + [RequestOptions(FieldPresence.Required, PutOnly = true)] + [StringLength(Limits.MaximumIndexableStringLength, MinimumLength = 1)] + public virtual string? Name { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/OAuthConnection.cs b/src/Tgstation.Server.Api/Models/OAuthConnection.cs index 678846bbe9..1e6072feb1 100644 --- a/src/Tgstation.Server.Api/Models/OAuthConnection.cs +++ b/src/Tgstation.Server.Api/Models/OAuthConnection.cs @@ -18,6 +18,7 @@ namespace Tgstation.Server.Api.Models /// The ID of the user in the . /// [Required] + [RequestOptions(FieldPresence.Required)] [StringLength(Limits.MaximumIndexableStringLength, MinimumLength = 1)] public string? ExternalUserId { get; set; } } diff --git a/src/Tgstation.Server.Api/Models/OAuthProviderInfo.cs b/src/Tgstation.Server.Api/Models/OAuthProviderInfo.cs index 6163e445f4..61dd8f04d7 100644 --- a/src/Tgstation.Server.Api/Models/OAuthProviderInfo.cs +++ b/src/Tgstation.Server.Api/Models/OAuthProviderInfo.cs @@ -20,6 +20,7 @@ namespace Tgstation.Server.Api.Models /// /// The server URL. /// + [ResponseOptions] public Uri? ServerUrl { get; set; } } } diff --git a/src/Tgstation.Server.Api/Models/PermissionSet.cs b/src/Tgstation.Server.Api/Models/PermissionSet.cs index a84ae1d27b..0ffb6801f2 100644 --- a/src/Tgstation.Server.Api/Models/PermissionSet.cs +++ b/src/Tgstation.Server.Api/Models/PermissionSet.cs @@ -6,22 +6,24 @@ namespace Tgstation.Server.Api.Models /// /// Represents a set of server permissions. /// - public class PermissionSet + public class PermissionSet : EntityId { - /// - /// The ID of the . - /// - [Required] - public long? Id { get; set; } + /// + [RequestOptions(FieldPresence.Ignored)] + public override long? Id + { + get => base.Id; + set => base.Id = value; + } /// - /// The for the + /// The for the user. /// [Required] public AdministrationRights? AdministrationRights { get; set; } /// - /// The for the + /// The for the user. /// [Required] public InstanceManagerRights? InstanceManagerRights { get; set; } diff --git a/src/Tgstation.Server.Api/Models/Repository.cs b/src/Tgstation.Server.Api/Models/Repository.cs deleted file mode 100644 index a88dfe1c54..0000000000 --- a/src/Tgstation.Server.Api/Models/Repository.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using Tgstation.Server.Api.Models.Internal; - -namespace Tgstation.Server.Api.Models -{ - /// - /// Represents a git repository - /// - public sealed class Repository : RepositorySettings, IGitRemoteInformation - { - /// - /// The origin URL. If , the does not exist - /// - public Uri? Origin { get; set; } - - /// - /// If submodules should be recursively cloned. - /// - public bool? RecurseSubmodules { get; set; } - - /// - /// The commit HEAD should point to. Not populated in responses, use instead for retrieval - /// - [StringLength(Limits.MaximumCommitShaLength)] - public string? CheckoutSha { get; set; } - - /// - /// The current for the - /// - public RevisionInformation? RevisionInformation { get; set; } - - /// - public RemoteGitProvider? RemoteGitProvider { get; set; } - - /// - public string? RemoteRepositoryOwner { get; set; } - - /// - public string? RemoteRepositoryName { get; set; } - - /// - /// The started by the if any - /// - 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 - /// - public bool? UpdateFromOrigin { get; set; } - - /// - /// The branch or tag HEAD points to - /// - [StringLength(Limits.MaximumStringLength)] - public string? Reference { get; set; } - - /// - /// for new s. Note that merges that conflict will not be performed - /// - public ICollection? NewTestMerges { get; set; } - } -} diff --git a/src/Tgstation.Server.Api/Models/Request/ByondVersionRequest.cs b/src/Tgstation.Server.Api/Models/Request/ByondVersionRequest.cs new file mode 100644 index 0000000000..a7406a93f2 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Request/ByondVersionRequest.cs @@ -0,0 +1,21 @@ +using System; + +namespace Tgstation.Server.Api.Models.Request +{ + /// + /// A request to install a BYOND . + /// + public sealed class ByondVersionRequest + { + /// + /// The BYOND version to install. + /// + [RequestOptions(FieldPresence.Required)] + public Version? Version { get; set; } + + /// + /// If a custom BYOND version is to be uploaded. + /// + public bool? UploadCustomZip { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/Request/ChatBotCreateRequest.cs b/src/Tgstation.Server.Api/Models/Request/ChatBotCreateRequest.cs new file mode 100644 index 0000000000..f3097a7107 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Request/ChatBotCreateRequest.cs @@ -0,0 +1,11 @@ +using Tgstation.Server.Api.Models.Internal; + +namespace Tgstation.Server.Api.Models.Request +{ + /// + /// Represents a request to update a chat bot. + /// + public sealed class ChatBotCreateRequest : ChatBotApiBase + { + } +} diff --git a/src/Tgstation.Server.Api/Models/Request/ChatBotUpdateRequest.cs b/src/Tgstation.Server.Api/Models/Request/ChatBotUpdateRequest.cs new file mode 100644 index 0000000000..e53c35fdbe --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Request/ChatBotUpdateRequest.cs @@ -0,0 +1,11 @@ +using Tgstation.Server.Api.Models.Internal; + +namespace Tgstation.Server.Api.Models.Request +{ + /// + /// Represents a request to update a chat bot. + /// + public class ChatBotUpdateRequest : ChatBotApiBase + { + } +} diff --git a/src/Tgstation.Server.Api/Models/Request/ConfigurationFileRequest.cs b/src/Tgstation.Server.Api/Models/Request/ConfigurationFileRequest.cs new file mode 100644 index 0000000000..37892f3484 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Request/ConfigurationFileRequest.cs @@ -0,0 +1,15 @@ +namespace Tgstation.Server.Api.Models.Request +{ + /// + /// Represents a request to update a configuration file. + /// + public sealed class ConfigurationFileRequest : IConfigurationFile + { + /// + [RequestOptions(FieldPresence.Required)] + public string? Path { get; set; } + + /// + public string? LastReadHash { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/Request/DreamDaemonRequest.cs b/src/Tgstation.Server.Api/Models/Request/DreamDaemonRequest.cs new file mode 100644 index 0000000000..eb2c729924 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Request/DreamDaemonRequest.cs @@ -0,0 +1,11 @@ +using Tgstation.Server.Api.Models.Internal; + +namespace Tgstation.Server.Api.Models.Request +{ + /// + /// A request to update . + /// + public sealed class DreamDaemonRequest : DreamDaemonApiBase + { + } +} diff --git a/src/Tgstation.Server.Api/Models/Request/DreamMakerRequest.cs b/src/Tgstation.Server.Api/Models/Request/DreamMakerRequest.cs new file mode 100644 index 0000000000..3523f01ab1 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Request/DreamMakerRequest.cs @@ -0,0 +1,11 @@ +using Tgstation.Server.Api.Models.Internal; + +namespace Tgstation.Server.Api.Models.Request +{ + /// + /// A request to the DreamMaker controller. + /// + public sealed class DreamMakerRequest : DreamMakerSettings + { + } +} diff --git a/src/Tgstation.Server.Api/Models/Request/InstanceCreateRequest.cs b/src/Tgstation.Server.Api/Models/Request/InstanceCreateRequest.cs new file mode 100644 index 0000000000..6194fe16cb --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Request/InstanceCreateRequest.cs @@ -0,0 +1,9 @@ +namespace Tgstation.Server.Api.Models.Request +{ + /// + /// A request to create an . + /// + public sealed class InstanceCreateRequest : Instance + { + } +} diff --git a/src/Tgstation.Server.Api/Models/Request/InstancePermissionSetRequest.cs b/src/Tgstation.Server.Api/Models/Request/InstancePermissionSetRequest.cs new file mode 100644 index 0000000000..46d1781b00 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Request/InstancePermissionSetRequest.cs @@ -0,0 +1,11 @@ +using Tgstation.Server.Api.Models.Internal; + +namespace Tgstation.Server.Api.Models.Request +{ + /// + /// A request to update an instance permission set. + /// + public sealed class InstancePermissionSetRequest : InstancePermissionSet + { + } +} diff --git a/src/Tgstation.Server.Api/Models/Request/InstanceUpdateRequest.cs b/src/Tgstation.Server.Api/Models/Request/InstanceUpdateRequest.cs new file mode 100644 index 0000000000..49b8ac697e --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Request/InstanceUpdateRequest.cs @@ -0,0 +1,9 @@ +namespace Tgstation.Server.Api.Models.Request +{ + /// + /// A request to update an . + /// + public class InstanceUpdateRequest : Instance + { + } +} diff --git a/src/Tgstation.Server.Api/Models/Request/RepositoryCreateRequest.cs b/src/Tgstation.Server.Api/Models/Request/RepositoryCreateRequest.cs new file mode 100644 index 0000000000..902fb2f896 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Request/RepositoryCreateRequest.cs @@ -0,0 +1,22 @@ +using System; +using Tgstation.Server.Api.Models.Internal; + +namespace Tgstation.Server.Api.Models.Request +{ + /// + /// Represents a request to clone the repository. + /// + public sealed class RepositoryCreateRequest : RepositoryApiBase + { + /// + /// The origin URL to clone. + /// + [RequestOptions(FieldPresence.Required)] + public Uri? Origin { get; set; } + + /// + /// If submodules should be recursively cloned. + /// + public bool? RecurseSubmodules { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/Request/RepositoryUpdateRequest.cs b/src/Tgstation.Server.Api/Models/Request/RepositoryUpdateRequest.cs new file mode 100644 index 0000000000..a9cd63f3e1 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Request/RepositoryUpdateRequest.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using Tgstation.Server.Api.Models.Internal; + +namespace Tgstation.Server.Api.Models.Request +{ + /// + /// Represents a request to change the repository. + /// + public sealed class RepositoryUpdateRequest : RepositoryApiBase + { + /// + /// The commit HEAD should point to. + /// + [StringLength(Limits.MaximumCommitShaLength)] + public string? CheckoutSha { 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 + /// + public bool? UpdateFromOrigin { get; set; } + + /// + /// for new s. Note that merges that conflict will not be performed + /// + public ICollection? NewTestMerges { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/Request/UserCreateRequest.cs b/src/Tgstation.Server.Api/Models/Request/UserCreateRequest.cs new file mode 100644 index 0000000000..697021e6fd --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Request/UserCreateRequest.cs @@ -0,0 +1,10 @@ +namespace Tgstation.Server.Api.Models.Request +{ + /// + /// For creating a user. + /// + #pragma warning disable CA1501 + public sealed class UserCreateRequest : UserUpdateRequest + { + } +} diff --git a/src/Tgstation.Server.Api/Models/Request/UserGroupCreateRequest.cs b/src/Tgstation.Server.Api/Models/Request/UserGroupCreateRequest.cs new file mode 100644 index 0000000000..0022a1f037 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Request/UserGroupCreateRequest.cs @@ -0,0 +1,9 @@ +namespace Tgstation.Server.Api.Models.Request +{ + /// + /// Request to create a user group. + /// + public sealed class UserGroupCreateRequest : UserGroupUpdateRequest + { + } +} diff --git a/src/Tgstation.Server.Api/Models/Request/UserGroupUpdateRequest.cs b/src/Tgstation.Server.Api/Models/Request/UserGroupUpdateRequest.cs new file mode 100644 index 0000000000..cbd8d7d595 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Request/UserGroupUpdateRequest.cs @@ -0,0 +1,11 @@ +using Tgstation.Server.Api.Models.Internal; + +namespace Tgstation.Server.Api.Models.Request +{ + /// + /// Request to update a user group. + /// + public class UserGroupUpdateRequest : UserGroup + { + } +} diff --git a/src/Tgstation.Server.Api/Models/Request/UserUpdateRequest.cs b/src/Tgstation.Server.Api/Models/Request/UserUpdateRequest.cs new file mode 100644 index 0000000000..6513a546cf --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Request/UserUpdateRequest.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; +using Tgstation.Server.Api.Models.Internal; + +namespace Tgstation.Server.Api.Models.Request +{ + /// + /// For editing a given user. + /// + public class UserUpdateRequest : UserApiBase + { + /// + /// Cleartext password of the user. + /// + [Required] + public string? Password { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/RequestOptionsAttribute.cs b/src/Tgstation.Server.Api/Models/RequestOptionsAttribute.cs new file mode 100644 index 0000000000..26184bd666 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/RequestOptionsAttribute.cs @@ -0,0 +1,30 @@ +using System; + +namespace Tgstation.Server.Api.Models +{ + /// + /// Indicates the for fields in models. + /// + [AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = false)] + public sealed class RequestOptionsAttribute : Attribute + { + /// + /// The . + /// + public FieldPresence Presence { get; } + + /// + /// If this only applies to HTTP PUT requests with the model. + /// + public bool PutOnly { get; set; } + + /// + /// Initializes a new instance of the . + /// + /// The value of . + public RequestOptionsAttribute(FieldPresence presence) + { + Presence = presence; + } + } +} diff --git a/src/Tgstation.Server.Api/Models/Administration.cs b/src/Tgstation.Server.Api/Models/Response/AdministrationResponse.cs similarity index 57% rename from src/Tgstation.Server.Api/Models/Administration.cs rename to src/Tgstation.Server.Api/Models/Response/AdministrationResponse.cs index 918930f30a..301d87976f 100644 --- a/src/Tgstation.Server.Api/Models/Administration.cs +++ b/src/Tgstation.Server.Api/Models/Response/AdministrationResponse.cs @@ -1,11 +1,11 @@ using System; -namespace Tgstation.Server.Api.Models +namespace Tgstation.Server.Api.Models.Response { /// /// Represents administrative server information /// - public sealed class Administration + public sealed class AdministrationResponse { /// /// The GitHub repository the server is built to recieve updates from @@ -13,13 +13,8 @@ namespace Tgstation.Server.Api.Models 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 + /// The latest available version of the Tgstation.Server.Host assembly from the upstream repository. If is not equal to 4 the update cannot be applied due to API changes /// 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; } } } diff --git a/src/Tgstation.Server.Api/Models/Response/ByondInstallResponse.cs b/src/Tgstation.Server.Api/Models/Response/ByondInstallResponse.cs new file mode 100644 index 0000000000..3cebc0a6a6 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Response/ByondInstallResponse.cs @@ -0,0 +1,24 @@ +using System; + +namespace Tgstation.Server.Api.Models.Response +{ + /// + /// Represents a BYOND installation. is used to upload custom BYOND version zip files, though must still be set. + /// + public sealed class ByondInstallResponse : FileTicketResponse + { + /// + /// The being used to install a new . + /// + [ResponseOptions] + public JobResponse? InstallJob { get; set; } + + /// + [ResponseOptions] + public override string? FileTicket + { + get => base.FileTicket; + set => base.FileTicket = value; + } + } +} diff --git a/src/Tgstation.Server.Api/Models/Response/ByondResponse.cs b/src/Tgstation.Server.Api/Models/Response/ByondResponse.cs new file mode 100644 index 0000000000..e54af87526 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Response/ByondResponse.cs @@ -0,0 +1,16 @@ +using System; + +namespace Tgstation.Server.Api.Models.Response +{ + /// + /// Represents an installed BYOND . + /// + public sealed class ByondResponse + { + /// + /// The installed BYOND . If there is no BYOND version installed. Only considers the and numbers. + /// + [ResponseOptions] + public Version? Version { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/Response/ChatBotResponse.cs b/src/Tgstation.Server.Api/Models/Response/ChatBotResponse.cs new file mode 100644 index 0000000000..716f8deb80 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Response/ChatBotResponse.cs @@ -0,0 +1,11 @@ +using Tgstation.Server.Api.Models.Internal; + +namespace Tgstation.Server.Api.Models.Response +{ + /// + /// Represents a chat bot response. + /// + public sealed class ChatBotResponse : ChatBotApiBase + { + } +} diff --git a/src/Tgstation.Server.Api/Models/CompileJob.cs b/src/Tgstation.Server.Api/Models/Response/CompileJobResponse.cs similarity index 55% rename from src/Tgstation.Server.Api/Models/CompileJob.cs rename to src/Tgstation.Server.Api/Models/Response/CompileJobResponse.cs index d5a2c12d09..0739e58347 100644 --- a/src/Tgstation.Server.Api/Models/CompileJob.cs +++ b/src/Tgstation.Server.Api/Models/Response/CompileJobResponse.cs @@ -1,22 +1,22 @@ using System; -namespace Tgstation.Server.Api.Models +namespace Tgstation.Server.Api.Models.Response { /// - public sealed class CompileJob : Internal.CompileJob + public sealed class CompileJobResponse : Internal.CompileJob { /// - /// The relating to this job + /// The relating to this job. /// - public Job? Job { get; set; } + public JobResponse? Job { get; set; } /// - /// Git revision the compiler ran on. Not modifiable + /// Git revision the compiler ran on. /// public RevisionInformation? RevisionInformation { get; set; } /// - /// The the was made with + /// The the was made with. /// public Version? ByondVersion { get; set; } diff --git a/src/Tgstation.Server.Api/Models/Response/ConfigurationFileResponse.cs b/src/Tgstation.Server.Api/Models/Response/ConfigurationFileResponse.cs new file mode 100644 index 0000000000..062c0de625 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Response/ConfigurationFileResponse.cs @@ -0,0 +1,26 @@ +namespace Tgstation.Server.Api.Models.Response +{ + /// + /// Response when reading configuration files. + /// + public sealed class ConfigurationFileResponse : FileTicketResponse, IConfigurationFile + { + /// + public string? Path { get; set; } + + /// + [ResponseOptions] + public string? LastReadHash { get; set; } + + /// + /// If represents a directory + /// + public bool? IsDirectory { get; set; } + + /// + /// If access to the file was denied for the operation + /// + [ResponseOptions] + public bool? AccessDenied { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/Response/DreamDaemonResponse.cs b/src/Tgstation.Server.Api/Models/Response/DreamDaemonResponse.cs new file mode 100644 index 0000000000..2fcbaa6189 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Response/DreamDaemonResponse.cs @@ -0,0 +1,49 @@ +using System.ComponentModel.DataAnnotations; +using Tgstation.Server.Api.Models.Internal; + +namespace Tgstation.Server.Api.Models.Response +{ + /// + /// Represents an instance of BYOND's DreamDaemon game server. Create action starts the server. Delete action shuts down the server + /// + public sealed class DreamDaemonResponse : DreamDaemonApiBase + { + /// + /// The live revision. + /// + [ResponseOptions] + public CompileJobResponse? ActiveCompileJob { get; set; } + + /// + /// The next revision to go live. + /// + [ResponseOptions] + public CompileJobResponse? StagedCompileJob { get; set; } + + /// + /// The current . + /// + [EnumDataType(typeof(WatchdogStatus))] + [ResponseOptions] + public WatchdogStatus? Status { get; set; } + + /// + /// The current of . May be downgraded due to requirements of + /// + [EnumDataType(typeof(DreamDaemonSecurity))] + [ResponseOptions] + public DreamDaemonSecurity? CurrentSecurity { get; set; } + + /// + /// The port the running instance is set to + /// + [ResponseOptions] + public ushort? CurrentPort { get; set; } + + /// + /// The webclient status the running instance is set to + /// + [ResponseOptions] + public bool? CurrentAllowWebclient { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/Response/DreamMakerResponse.cs b/src/Tgstation.Server.Api/Models/Response/DreamMakerResponse.cs new file mode 100644 index 0000000000..d114c7a43e --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Response/DreamMakerResponse.cs @@ -0,0 +1,11 @@ +using Tgstation.Server.Api.Models.Internal; + +namespace Tgstation.Server.Api.Models.Response +{ + /// + /// A request to the DreamMaker controller. + /// + public sealed class DreamMakerResponse : DreamMakerSettings + { + } +} diff --git a/src/Tgstation.Server.Api/Models/ErrorMessage.cs b/src/Tgstation.Server.Api/Models/Response/ErrorMessageResponse.cs similarity index 63% rename from src/Tgstation.Server.Api/Models/ErrorMessage.cs rename to src/Tgstation.Server.Api/Models/Response/ErrorMessageResponse.cs index 099c49cdf7..7cb35319e9 100644 --- a/src/Tgstation.Server.Api/Models/ErrorMessage.cs +++ b/src/Tgstation.Server.Api/Models/Response/ErrorMessageResponse.cs @@ -1,46 +1,45 @@ -using System; +using System; using System.ComponentModel.DataAnnotations; -namespace Tgstation.Server.Api.Models +namespace Tgstation.Server.Api.Models.Response { /// /// Represents an error message returned by the server /// - public sealed class ErrorMessage + public sealed class ErrorMessageResponse { /// /// The version of the API the server is using /// - [Required] public Version? ServerApiVersion { get; set; } /// /// A human readable description of the error /// - [Required] public string? Message { get; set; } /// /// Additional data associated with the error message. /// + [ResponseOptions] public string? AdditionalData { get; set; } /// - /// The of the . + /// The of the . /// [EnumDataType(typeof(ErrorCode))] public ErrorCode ErrorCode { get; set; } /// - /// Initializes a new instance of the . + /// Initializes a new instance of the . /// - public ErrorMessage() { } + public ErrorMessageResponse() { } /// - /// Initializes a new instance of the . + /// Initializes a new instance of the . /// - /// The . - public ErrorMessage(ErrorCode errorCode) + /// The . + public ErrorMessageResponse(ErrorCode errorCode) { ErrorCode = errorCode; Message = errorCode.Describe(); diff --git a/src/Tgstation.Server.Api/Models/FileTicketResult.cs b/src/Tgstation.Server.Api/Models/Response/FileTicketResponse.cs similarity index 61% rename from src/Tgstation.Server.Api/Models/FileTicketResult.cs rename to src/Tgstation.Server.Api/Models/Response/FileTicketResponse.cs index 87d22a78b2..1e94f65b5e 100644 --- a/src/Tgstation.Server.Api/Models/FileTicketResult.cs +++ b/src/Tgstation.Server.Api/Models/Response/FileTicketResponse.cs @@ -1,13 +1,13 @@ -namespace Tgstation.Server.Api.Models +namespace Tgstation.Server.Api.Models.Response { /// /// Response for when file transfers are necessary. /// - public class FileTicketResult + public class FileTicketResponse { /// /// The ticket to use to access the controller. /// - public string? FileTicket { get; set; } + public virtual string? FileTicket { get; set; } } } diff --git a/src/Tgstation.Server.Api/Models/Response/InstancePermissionSetResponse.cs b/src/Tgstation.Server.Api/Models/Response/InstancePermissionSetResponse.cs new file mode 100644 index 0000000000..811fef574e --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Response/InstancePermissionSetResponse.cs @@ -0,0 +1,11 @@ +using Tgstation.Server.Api.Models.Internal; + +namespace Tgstation.Server.Api.Models.Response +{ + /// + /// A response containing an instance permission set. + /// + public sealed class InstancePermissionSetResponse : InstancePermissionSet + { + } +} diff --git a/src/Tgstation.Server.Api/Models/Response/InstanceResponse.cs b/src/Tgstation.Server.Api/Models/Response/InstanceResponse.cs new file mode 100644 index 0000000000..9b5e9dd03d --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Response/InstanceResponse.cs @@ -0,0 +1,15 @@ +namespace Tgstation.Server.Api.Models.Response +{ + /// + /// Server response for s. + /// + public sealed class InstanceResponse : Instance + { + /// + /// The representing a change of . + /// + /// 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. + [ResponseOptions] + public JobResponse? MoveJob { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/Response/JobResponse.cs b/src/Tgstation.Server.Api/Models/Response/JobResponse.cs new file mode 100644 index 0000000000..38613ef26f --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Response/JobResponse.cs @@ -0,0 +1,25 @@ +namespace Tgstation.Server.Api.Models.Response +{ + /// + /// Represents a long running job on the server. Model is read-only, updates attempt to cancel the job + /// + public sealed class JobResponse : Internal.Job + { + /// + /// The that started the job + /// + public UserName? StartedBy { get; set; } + + /// + /// The that cancelled the job + /// + [ResponseOptions] + public UserName? CancelledBy { get; set; } + + /// + /// Optional progress between 0 and 100 inclusive + /// + [ResponseOptions] + public int? Progress { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/LogFile.cs b/src/Tgstation.Server.Api/Models/Response/LogFileResponse.cs similarity index 69% rename from src/Tgstation.Server.Api/Models/LogFile.cs rename to src/Tgstation.Server.Api/Models/Response/LogFileResponse.cs index 873528fcb4..1bfa2f5030 100644 --- a/src/Tgstation.Server.Api/Models/LogFile.cs +++ b/src/Tgstation.Server.Api/Models/Response/LogFileResponse.cs @@ -1,16 +1,16 @@ using System; -namespace Tgstation.Server.Api.Models +namespace Tgstation.Server.Api.Models.Response { /// /// Represents a server log file. /// - public sealed class LogFile : FileTicketResult + public sealed class LogFileResponse : FileTicketResponse { /// /// The name of the log file. /// - public string Name { get; set; } = String.Empty; + public string? Name { get; set; } /// /// The of when the log file was modified. diff --git a/src/Tgstation.Server.Api/Models/Paginated.cs b/src/Tgstation.Server.Api/Models/Response/PaginatedResponse.cs similarity index 87% rename from src/Tgstation.Server.Api/Models/Paginated.cs rename to src/Tgstation.Server.Api/Models/Response/PaginatedResponse.cs index ba4e187494..fba3c698a6 100644 --- a/src/Tgstation.Server.Api/Models/Paginated.cs +++ b/src/Tgstation.Server.Api/Models/Response/PaginatedResponse.cs @@ -1,13 +1,13 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -namespace Tgstation.Server.Api.Models +namespace Tgstation.Server.Api.Models.Response { /// /// Represents a paginated set of models. /// /// The of the returned model. - public sealed class Paginated + public sealed class PaginatedResponse { /// /// The of the returned s. diff --git a/src/Tgstation.Server.Api/Models/Response/RepositoryResponse.cs b/src/Tgstation.Server.Api/Models/Response/RepositoryResponse.cs new file mode 100644 index 0000000000..14a9fe2f73 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Response/RepositoryResponse.cs @@ -0,0 +1,41 @@ +using System; +using Tgstation.Server.Api.Models.Internal; + +namespace Tgstation.Server.Api.Models.Response +{ + /// + /// Represents a git repository + /// + public sealed class RepositoryResponse : RepositoryApiBase, IGitRemoteInformation + { + /// + /// The origin URL. If , the git repository does not currently exist on the server. + /// + [ResponseOptions] + public Uri? Origin { get; set; } + + /// + /// The current . + /// + [ResponseOptions] + public RevisionInformation? RevisionInformation { get; set; } + + /// + [ResponseOptions] + public RemoteGitProvider? RemoteGitProvider { get; set; } + + /// + [ResponseOptions] + public string? RemoteRepositoryOwner { get; set; } + + /// + [ResponseOptions] + public string? RemoteRepositoryName { get; set; } + + /// + /// The started by the request, if any. + /// + [ResponseOptions] + public JobResponse? ActiveJob { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/ServerInformation.cs b/src/Tgstation.Server.Api/Models/Response/ServerInformationResponse.cs similarity index 81% rename from src/Tgstation.Server.Api/Models/ServerInformation.cs rename to src/Tgstation.Server.Api/Models/Response/ServerInformationResponse.cs index ed0509d76b..0efe4dc4bc 100644 --- a/src/Tgstation.Server.Api/Models/ServerInformation.cs +++ b/src/Tgstation.Server.Api/Models/Response/ServerInformationResponse.cs @@ -1,12 +1,12 @@ using System; using System.Collections.Generic; -namespace Tgstation.Server.Api.Models +namespace Tgstation.Server.Api.Models.Response { /// /// Represents basic server information. /// - public sealed class ServerInformation : Internal.ServerInformation + public sealed class ServerInformationResponse : Internal.ServerInformationBase { /// /// The version of the host @@ -34,9 +34,10 @@ namespace Tgstation.Server.Api.Models public bool UpdateInProgress { get; set; } /// - /// A of connected s. + /// A of connected s. /// - public ICollection? SwarmServers { get; set; } + [ResponseOptions] + public ICollection? SwarmServers { get; set; } /// /// Map of to the for them. diff --git a/src/Tgstation.Server.Api/Models/Response/ServerUpdateResponse.cs b/src/Tgstation.Server.Api/Models/Response/ServerUpdateResponse.cs new file mode 100644 index 0000000000..a3ff1e7159 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Response/ServerUpdateResponse.cs @@ -0,0 +1,7 @@ +namespace Tgstation.Server.Api.Models.Response +{ + /// + public sealed class ServerUpdateResponse : ServerUpdate + { + } +} diff --git a/src/Tgstation.Server.Api/Models/Response/SwarmServerResponse.cs b/src/Tgstation.Server.Api/Models/Response/SwarmServerResponse.cs new file mode 100644 index 0000000000..1e11ad9b45 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Response/SwarmServerResponse.cs @@ -0,0 +1,13 @@ +using Tgstation.Server.Api.Models.Internal; + +namespace Tgstation.Server.Api.Models.Response +{ + /// + public sealed class SwarmServerResponse : SwarmServer + { + /// + /// If the is the controller. + /// + public bool Controller { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/Token.cs b/src/Tgstation.Server.Api/Models/Response/TokenResponse.cs similarity index 64% rename from src/Tgstation.Server.Api/Models/Token.cs rename to src/Tgstation.Server.Api/Models/Response/TokenResponse.cs index 4f73c1c28e..21a96b5388 100644 --- a/src/Tgstation.Server.Api/Models/Token.cs +++ b/src/Tgstation.Server.Api/Models/Response/TokenResponse.cs @@ -1,11 +1,11 @@ -using System; +using System; -namespace Tgstation.Server.Api.Models +namespace Tgstation.Server.Api.Models.Response { /// /// Represents a JWT returned by the API /// - public sealed class Token + public sealed class TokenResponse { /// /// The value of the JWT @@ -13,7 +13,7 @@ namespace Tgstation.Server.Api.Models public string? Bearer { get; set; } /// - /// When the expires + /// When the expires /// public DateTimeOffset ExpiresAt { get; set; } } diff --git a/src/Tgstation.Server.Api/Models/Response/UserGroupResponse.cs b/src/Tgstation.Server.Api/Models/Response/UserGroupResponse.cs new file mode 100644 index 0000000000..d72f9eeaae --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Response/UserGroupResponse.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using Tgstation.Server.Api.Models.Internal; + +namespace Tgstation.Server.Api.Models.Response +{ + /// + public sealed class UserGroupResponse : UserGroup + { + /// + /// The s the has. + /// + public ICollection? Users { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/Response/UserResponse.cs b/src/Tgstation.Server.Api/Models/Response/UserResponse.cs new file mode 100644 index 0000000000..09b0f03c8f --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Response/UserResponse.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations; +using Tgstation.Server.Api.Models.Internal; + +namespace Tgstation.Server.Api.Models.Response +{ + /// + public class UserResponse : UserApiBase + { + /// + /// The who created this + /// + [Required] + public UserName? CreatedBy { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/ResponseOptionsAttribute.cs b/src/Tgstation.Server.Api/Models/ResponseOptionsAttribute.cs new file mode 100644 index 0000000000..a8c42e0f4c --- /dev/null +++ b/src/Tgstation.Server.Api/Models/ResponseOptionsAttribute.cs @@ -0,0 +1,16 @@ +using System; + +namespace Tgstation.Server.Api.Models +{ + /// + /// Indicates the response of API fields. Changes it from to by default. + /// + [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] + public sealed class ResponseOptionsAttribute : Attribute + { + /// + /// The . + /// + public FieldPresence Presence { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/RevisionInformation.cs b/src/Tgstation.Server.Api/Models/RevisionInformation.cs index 8e115fb922..4c47bde70c 100644 --- a/src/Tgstation.Server.Api/Models/RevisionInformation.cs +++ b/src/Tgstation.Server.Api/Models/RevisionInformation.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace Tgstation.Server.Api.Models { @@ -8,6 +8,7 @@ namespace Tgstation.Server.Api.Models /// /// The that was created with this /// + [ResponseOptions] public TestMerge? PrimaryTestMerge { get; set; } /// @@ -16,7 +17,7 @@ namespace Tgstation.Server.Api.Models public ICollection? ActiveTestMerges { get; set; } /// - /// The s made from the + /// The s made from the /// public ICollection? CompileJobs { get; set; } } diff --git a/src/Tgstation.Server.Api/Models/ServerUpdate.cs b/src/Tgstation.Server.Api/Models/ServerUpdate.cs new file mode 100644 index 0000000000..6af9e3251e --- /dev/null +++ b/src/Tgstation.Server.Api/Models/ServerUpdate.cs @@ -0,0 +1,16 @@ +using System; + +namespace Tgstation.Server.Api.Models +{ + /// + /// Represents a request to update TGS. + /// + public abstract class ServerUpdate + { + /// + /// Changes the version of tgstation-server to the given version from the upstream repository. + /// + [RequestOptions(FieldPresence.Required)] + public Version? NewVersion { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/SwarmServer.cs b/src/Tgstation.Server.Api/Models/SwarmServer.cs deleted file mode 100644 index 392d055518..0000000000 --- a/src/Tgstation.Server.Api/Models/SwarmServer.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Tgstation.Server.Api.Models -{ - /// - public sealed class SwarmServer : Internal.SwarmServer - { - /// - /// If the is the controller. - /// - public bool Controller { get; set; } - } -} diff --git a/src/Tgstation.Server.Api/Models/TestMerge.cs b/src/Tgstation.Server.Api/Models/TestMerge.cs index f9cdbcea75..5ab802f96f 100644 --- a/src/Tgstation.Server.Api/Models/TestMerge.cs +++ b/src/Tgstation.Server.Api/Models/TestMerge.cs @@ -1,11 +1,13 @@ -namespace Tgstation.Server.Api.Models +using Tgstation.Server.Api.Models.Internal; + +namespace Tgstation.Server.Api.Models { /// - public sealed class TestMerge : Internal.TestMerge + public sealed class TestMerge : TestMergeApiBase { /// - /// The who created the + /// The of the user who created the . /// - public User? MergedBy { get; set; } + public UserName? 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 fbac15c974..f779a42d98 100644 --- a/src/Tgstation.Server.Api/Models/TestMergeParameters.cs +++ b/src/Tgstation.Server.Api/Models/TestMergeParameters.cs @@ -22,6 +22,7 @@ namespace Tgstation.Server.Api.Models /// /// Optional comment about the test /// + [ResponseOptions] [StringLength(Limits.MaximumStringLength)] public string? Comment { get; set; } } diff --git a/src/Tgstation.Server.Api/Models/User.cs b/src/Tgstation.Server.Api/Models/User.cs deleted file mode 100644 index 729e81383e..0000000000 --- a/src/Tgstation.Server.Api/Models/User.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; - -namespace Tgstation.Server.Api.Models -{ - /// - public class User : Internal.User - { - /// - /// The name of the default admin user - /// - public static readonly string AdminName = "Admin"; - - /// - /// The default admin password - /// - public static readonly string DefaultAdminPassword = "ISolemlySwearToDeleteTheDataDirectory"; - - /// - /// The who created this - /// - [Required] - public Internal.UserBase? CreatedBy { get; set; } - - /// - /// List of s associated with the . - /// - public ICollection? OAuthConnections { get; set; } - - /// - /// The directly associated with the . - /// - public PermissionSet? PermissionSet { get; set; } - - /// - /// The asociated with the , if any. - /// - public Internal.UserGroup? Group { get; set; } - } -} diff --git a/src/Tgstation.Server.Api/Models/UserGroup.cs b/src/Tgstation.Server.Api/Models/UserGroup.cs deleted file mode 100644 index cc9394395a..0000000000 --- a/src/Tgstation.Server.Api/Models/UserGroup.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Collections.Generic; - -namespace Tgstation.Server.Api.Models -{ - /// - public sealed class UserGroup : Internal.UserGroup - { - /// - /// The s the has. - /// - public ICollection? Users { get; set; } - } -} diff --git a/src/Tgstation.Server.Api/Models/UserName.cs b/src/Tgstation.Server.Api/Models/UserName.cs new file mode 100644 index 0000000000..8044489a2d --- /dev/null +++ b/src/Tgstation.Server.Api/Models/UserName.cs @@ -0,0 +1,33 @@ +namespace Tgstation.Server.Api.Models +{ + /// + /// Base class for user names. + /// + public class UserName : NamedEntity + { + /// + [RequestOptions(FieldPresence.Optional)] + public override string? Name + { + get => base.Name; + set => base.Name = value; + } + + /// + /// Create a copy of the as a given . + /// + /// The child of to create. + /// A new copied from . + protected virtual TResultType CreateUserName() where TResultType : UserName, new() => new TResultType + { + Id = Id, + Name = Name + }; + + /// + /// Create a copy of the . + /// + /// A new copied from . + public UserName CreateUserName() => CreateUserName(); + } +} diff --git a/src/Tgstation.Server.Api/Models/UserUpdate.cs b/src/Tgstation.Server.Api/Models/UserUpdate.cs deleted file mode 100644 index dc35f3de24..0000000000 --- a/src/Tgstation.Server.Api/Models/UserUpdate.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace Tgstation.Server.Api.Models -{ - /// - /// For editing a given . Will never be returned by the API - /// - public sealed class UserUpdate : User - { - /// - /// Cleartext password of the - /// - [Required] - public string? Password { get; set; } - } -} diff --git a/src/Tgstation.Server.Api/Properties/ApiVersionAttribute.cs b/src/Tgstation.Server.Api/Properties/ApiVersionAttribute.cs new file mode 100644 index 0000000000..87de47ad51 --- /dev/null +++ b/src/Tgstation.Server.Api/Properties/ApiVersionAttribute.cs @@ -0,0 +1,34 @@ +using System; +using System.Reflection; + +namespace Tgstation.Server.Api.Properties +{ + /// + /// Attribute for bringing in the HTTP API version from MSBuild. + /// + [AttributeUsage(AttributeTargets.Assembly)] + sealed class ApiVersionAttribute : Attribute + { + /// + /// Return the 's instance of the . + /// + public static ApiVersionAttribute Instance => Assembly + .GetExecutingAssembly() + .GetCustomAttribute(); + + /// + /// The of the TGS API definition. + /// + public string RawApiVersion { get; } + + /// + /// Initializes a new instance of the . + /// + /// The value of . + public ApiVersionAttribute( + string rawApiVersion) + { + RawApiVersion = rawApiVersion ?? throw new ArgumentNullException(nameof(rawApiVersion)); + } + } +} diff --git a/src/Tgstation.Server.Api/Rights/AdministrationRights.cs b/src/Tgstation.Server.Api/Rights/AdministrationRights.cs index f134c7d1c9..d0747d9f45 100644 --- a/src/Tgstation.Server.Api/Rights/AdministrationRights.cs +++ b/src/Tgstation.Server.Api/Rights/AdministrationRights.cs @@ -3,7 +3,7 @@ using System; namespace Tgstation.Server.Api.Rights { /// - /// Rights for + /// Administration rights for the server. /// [Flags] public enum AdministrationRights : ulong @@ -14,7 +14,7 @@ namespace Tgstation.Server.Api.Rights None = 0, /// - /// User has complete control over creating/editing s and s (and deleting in the case of the latter). + /// User has complete control over creating/editing s and s (and deleting in the case of the latter). /// WriteUsers = 1, @@ -24,7 +24,7 @@ namespace Tgstation.Server.Api.Rights RestartHost = 2, /// - /// User can read and upgrade/downgrade TGS through the API. + /// User can read and upgrade/downgrade TGS through the API. /// ChangeVersion = 4, @@ -39,12 +39,12 @@ namespace Tgstation.Server.Api.Rights ReadUsers = 16, /// - /// User can list and download s. + /// User can list and download s. /// DownloadLogs = 32, /// - /// User can modify their own . + /// User can modify their own . /// EditOwnOAuthConnections = 64, } diff --git a/src/Tgstation.Server.Api/Rights/ByondRights.cs b/src/Tgstation.Server.Api/Rights/ByondRights.cs index 3e54887103..100d64f2b7 100644 --- a/src/Tgstation.Server.Api/Rights/ByondRights.cs +++ b/src/Tgstation.Server.Api/Rights/ByondRights.cs @@ -3,7 +3,7 @@ using System; namespace Tgstation.Server.Api.Rights { /// - /// Rights for + /// Rights for BYOND version management. /// [Flags] public enum ByondRights : ulong diff --git a/src/Tgstation.Server.Api/Rights/ChatBotRights.cs b/src/Tgstation.Server.Api/Rights/ChatBotRights.cs index b0bf961ed2..9deee84290 100644 --- a/src/Tgstation.Server.Api/Rights/ChatBotRights.cs +++ b/src/Tgstation.Server.Api/Rights/ChatBotRights.cs @@ -3,7 +3,7 @@ using System; namespace Tgstation.Server.Api.Rights { /// - /// Rights for + /// Rights for chat bots. /// [Flags] public enum ChatBotRights : ulong @@ -14,57 +14,57 @@ namespace Tgstation.Server.Api.Rights None = 0, /// - /// User can change . + /// User can change . /// WriteEnabled = 1, /// - /// User can change . + /// User can change . /// WriteProvider = 2, /// - /// User can change . + /// User can change . /// WriteChannels = 4, /// - /// User can change + /// User can change /// WriteConnectionString = 8, /// - /// User can read requires the permission. + /// User can read requires the permission. /// ReadConnectionString = 16, /// - /// User can read all properties except + /// User can read all chat bot properties except /// Read = 32, /// - /// User can create new s. + /// User can create new chat bots. /// Create = 64, /// - /// User can delete s. + /// User can delete chat bots. /// Delete = 128, /// - /// User can change . + /// User can change . /// WriteName = 256, /// - /// User can change . + /// User can change . /// WriteReconnectionInterval = 512, /// - /// User can change . + /// User can change . /// WriteChannelLimit = 1024, } diff --git a/src/Tgstation.Server.Api/Rights/ConfigurationRights.cs b/src/Tgstation.Server.Api/Rights/ConfigurationRights.cs index bc93c814e0..48a192fdcd 100644 --- a/src/Tgstation.Server.Api/Rights/ConfigurationRights.cs +++ b/src/Tgstation.Server.Api/Rights/ConfigurationRights.cs @@ -3,7 +3,7 @@ using System; namespace Tgstation.Server.Api.Rights { /// - /// Rights for + /// Rights for s. /// [Flags] public enum ConfigurationRights : ulong diff --git a/src/Tgstation.Server.Api/Rights/DreamDaemonRights.cs b/src/Tgstation.Server.Api/Rights/DreamDaemonRights.cs index 0547c1400b..ad69c359c7 100644 --- a/src/Tgstation.Server.Api/Rights/DreamDaemonRights.cs +++ b/src/Tgstation.Server.Api/Rights/DreamDaemonRights.cs @@ -3,7 +3,7 @@ using System; namespace Tgstation.Server.Api.Rights { /// - /// Rights for + /// Rights for managing DreamDaemon. /// [Flags] public enum DreamDaemonRights : ulong @@ -14,7 +14,7 @@ namespace Tgstation.Server.Api.Rights None = 0, /// - /// User can read and + /// User can read and /// ReadRevision = 1, @@ -34,7 +34,7 @@ namespace Tgstation.Server.Api.Rights SetSecurity = 8, /// - /// User can read every propery of except and . + /// User can read every property of except and . /// ReadMetadata = 16, @@ -44,12 +44,12 @@ namespace Tgstation.Server.Api.Rights SetWebClient = 32, /// - /// User can change . + /// User can change . /// SoftRestart = 64, /// - /// User can change . + /// User can change . /// SoftShutdown = 128, diff --git a/src/Tgstation.Server.Api/Rights/DreamMakerRights.cs b/src/Tgstation.Server.Api/Rights/DreamMakerRights.cs index 44d67495f5..680a609859 100644 --- a/src/Tgstation.Server.Api/Rights/DreamMakerRights.cs +++ b/src/Tgstation.Server.Api/Rights/DreamMakerRights.cs @@ -3,7 +3,7 @@ using System; namespace Tgstation.Server.Api.Rights { /// - /// Rights for + /// Rights for deployment. /// [Flags] public enum DreamMakerRights : ulong @@ -14,7 +14,7 @@ namespace Tgstation.Server.Api.Rights None = 0, /// - /// User may read all properties of . + /// User may read all properties of . /// Read = 1, @@ -29,27 +29,27 @@ namespace Tgstation.Server.Api.Rights CancelCompile = 4, /// - /// User may modify . + /// User may modify . /// SetDme = 8, /// - /// User may modify . + /// User may modify . /// SetApiValidationPort = 16, /// - /// User may list and read all s. + /// User may list and read all s. /// CompileJobs = 32, /// - /// User may modify . + /// User may modify . /// SetSecurityLevel = 64, /// - /// User may modify . + /// User may modify . /// SetApiValidationRequirement = 128, } diff --git a/src/Tgstation.Server.Api/Rights/InstanceManagerRights.cs b/src/Tgstation.Server.Api/Rights/InstanceManagerRights.cs index b8700b9e30..e438734d90 100644 --- a/src/Tgstation.Server.Api/Rights/InstanceManagerRights.cs +++ b/src/Tgstation.Server.Api/Rights/InstanceManagerRights.cs @@ -14,7 +14,7 @@ namespace Tgstation.Server.Api.Rights None = 0, /// - /// User can view s which they have an for. + /// User can view s which they have an for. /// Read = 1, @@ -64,7 +64,7 @@ namespace Tgstation.Server.Api.Rights SetChatBotLimit = 512, /// - /// User can give themselves or their group full rights on ALL instances. + /// User can give themselves or their group full on ALL instances. /// GrantPermissions = 1024, } diff --git a/src/Tgstation.Server.Api/Rights/InstancePermissionSetRights.cs b/src/Tgstation.Server.Api/Rights/InstancePermissionSetRights.cs index 285411f32c..6b2dc5814d 100644 --- a/src/Tgstation.Server.Api/Rights/InstancePermissionSetRights.cs +++ b/src/Tgstation.Server.Api/Rights/InstancePermissionSetRights.cs @@ -14,17 +14,17 @@ namespace Tgstation.Server.Api.Rights None = 0, /// - /// Allow read access to all s in the . + /// Allow read access to all s in the . /// Read = 1, /// - /// Allow write and delete access to all for the . + /// Allow write and delete access to all for the . /// Write = 2, /// - /// Allow adding additional s to the . + /// Allow adding additional s to the . /// Create = 4 } diff --git a/src/Tgstation.Server.Api/Rights/RepositoryRights.cs b/src/Tgstation.Server.Api/Rights/RepositoryRights.cs index a65ca17e0a..4d0d452287 100644 --- a/src/Tgstation.Server.Api/Rights/RepositoryRights.cs +++ b/src/Tgstation.Server.Api/Rights/RepositoryRights.cs @@ -3,7 +3,7 @@ using System; namespace Tgstation.Server.Api.Rights { /// - /// Rights for a + /// Rights for the git repository. /// [Flags] public enum RepositoryRights : ulong @@ -19,12 +19,12 @@ namespace Tgstation.Server.Api.Rights CancelPendingChanges = 1, /// - /// User may clone the if it does not exist. + /// User may clone the repository if it does not exist. /// SetOrigin = 2, /// - /// User may directly checkout a git SHA that the 's HEAD will point to. + /// User may directly checkout a git SHA that the repository's HEAD will point to. /// SetSha = 4, @@ -54,12 +54,12 @@ namespace Tgstation.Server.Api.Rights ChangeCredentials = 128, /// - /// User may set to another git branch or tag (not a SHA). + /// User may set to another git branch or tag (not a SHA). /// SetReference = 256, /// - /// User may read all fields in the with the exception of . + /// User may read repository information. /// Read = 512, @@ -69,7 +69,7 @@ namespace Tgstation.Server.Api.Rights ChangeAutoUpdateSettings = 1024, /// - /// User may delete the and allow it to be cloned again. + /// User may delete the repository and allow it to be cloned again. /// Delete = 2048, diff --git a/src/Tgstation.Server.Api/Routes.cs b/src/Tgstation.Server.Api/Routes.cs index 99784db093..7825f1bb5d 100644 --- a/src/Tgstation.Server.Api/Routes.cs +++ b/src/Tgstation.Server.Api/Routes.cs @@ -14,44 +14,44 @@ namespace Tgstation.Server.Api public const string Root = "/"; /// - /// The controller + /// The server administration controller. /// - public const string Administration = Root + nameof(Models.Administration); + public const string Administration = Root + "Administration"; /// - /// The controller + /// The endpoint to download server logs. /// public const string Logs = Administration + "/Logs"; /// - /// The controller + /// The user controller. /// - public const string User = Root + nameof(Models.User); + public const string User = Root + "User"; /// - /// The controller. + /// The user group controller. /// - public const string UserGroup = Root + nameof(Models.UserGroup); + public const string UserGroup = Root + "UserGroup"; /// - /// The controller + /// The controller. /// - public const string InstanceManager = Root + nameof(Models.Instance); + public const string InstanceManager = Root + "Instance"; /// - /// The controller + /// The BYOND controller. /// - public const string Byond = Root + nameof(Models.Byond); + public const string Byond = Root + "Byond"; /// - /// The controller + /// The git repository controller. /// - public const string Repository = Root + nameof(Models.Repository); + public const string Repository = Root + "Repository"; /// - /// The controller + /// The DreamDaemon controller /// - public const string DreamDaemon = Root + nameof(Models.DreamDaemon); + public const string DreamDaemon = Root + "DreamDaemon"; /// /// For accessing DD diagnostics @@ -59,12 +59,12 @@ namespace Tgstation.Server.Api public const string Diagnostics = DreamDaemon + "/Diagnostics"; /// - /// The controller + /// The configuration controller /// public const string Configuration = Root + "Config"; /// - /// To be paired with for accessing s + /// To be paired with for accessing s. /// public const string File = "File"; @@ -74,24 +74,24 @@ namespace Tgstation.Server.Api public const string ConfigurationFile = Configuration + "/" + File; /// - /// The controller + /// The instance permission set controller. /// - public const string InstancePermissionSet = Root + nameof(Models.InstancePermissionSet); + public const string InstancePermissionSet = Root + "InstancePermissionSet"; /// - /// The controller + /// The chat bot controller /// public const string Chat = Root + "Chat"; /// - /// The controller + /// The deployment controller /// - public const string DreamMaker = Root + nameof(Models.DreamMaker); + public const string DreamMaker = Root + "DreamMaker"; /// - /// The controller + /// The jobs controller /// - public const string Jobs = Root + nameof(Models.Job); + public const string Jobs = Root + "Job"; /// /// The transfer controller. @@ -119,7 +119,7 @@ namespace Tgstation.Server.Api public static string ListRoute(string route) => String.Format(CultureInfo.InvariantCulture, "{0}/{1}", route, List); /// - /// Sanitize a path for use in a GET . + /// Sanitize a path for use in a GET . /// /// The path to sanitize. /// The sanitized path. diff --git a/src/Tgstation.Server.Api/Tgstation.Server.Api.csproj b/src/Tgstation.Server.Api/Tgstation.Server.Api.csproj index 933cb4cb6c..6a2f206421 100644 --- a/src/Tgstation.Server.Api/Tgstation.Server.Api.csproj +++ b/src/Tgstation.Server.Api/Tgstation.Server.Api.csproj @@ -1,10 +1,10 @@ - + netstandard2.1 Full - $(TgsApiVersion) + $(TgsApiLibraryVersion) true Cyberboss /tg/station 13 @@ -32,6 +32,19 @@ + + + + <_Parameter1>$(TgsApiVersion) + + + + + + + + + diff --git a/src/Tgstation.Server.Client/AdministrationClient.cs b/src/Tgstation.Server.Client/AdministrationClient.cs index e941337ebf..ea044ba0bd 100644 --- a/src/Tgstation.Server.Client/AdministrationClient.cs +++ b/src/Tgstation.Server.Client/AdministrationClient.cs @@ -5,7 +5,8 @@ using System.Threading; using System.Threading.Tasks; using System.Web; using Tgstation.Server.Api; -using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client { @@ -21,22 +22,22 @@ namespace Tgstation.Server.Client { } /// - public Task Read(CancellationToken cancellationToken) => ApiClient.Read(Routes.Administration, cancellationToken); + public Task Read(CancellationToken cancellationToken) => ApiClient.Read(Routes.Administration, cancellationToken); /// - public Task Update(Administration administration, CancellationToken cancellationToken) => ApiClient.Update(Routes.Administration, administration ?? throw new ArgumentNullException(nameof(administration)), cancellationToken); + public Task Update(ServerUpdateRequest updateRequest, CancellationToken cancellationToken) => ApiClient.Update(Routes.Administration, updateRequest ?? throw new ArgumentNullException(nameof(updateRequest)), cancellationToken); /// public Task Restart(CancellationToken cancellationToken) => ApiClient.Delete(Routes.Administration, cancellationToken); /// - public Task> ListLogs(PaginationSettings? paginationSettings, CancellationToken cancellationToken) - => ReadPaged(paginationSettings, Routes.Logs, null, cancellationToken); + public Task> ListLogs(PaginationSettings? paginationSettings, CancellationToken cancellationToken) + => ReadPaged(paginationSettings, Routes.Logs, null, cancellationToken); /// - public async Task> GetLog(LogFile logFile, CancellationToken cancellationToken) + public async Task> GetLog(LogFileResponse logFile, CancellationToken cancellationToken) { - var resultFile = await ApiClient.Read( + var resultFile = await ApiClient.Read( Routes.Logs + Routes.SanitizeGetPath( HttpUtility.UrlEncode( logFile?.Name ?? throw new ArgumentNullException(nameof(logFile)))), diff --git a/src/Tgstation.Server.Client/ApiClient.cs b/src/Tgstation.Server.Client/ApiClient.cs index 7c8460b27e..f1afee6b3f 100644 --- a/src/Tgstation.Server.Client/ApiClient.cs +++ b/src/Tgstation.Server.Client/ApiClient.cs @@ -15,6 +15,7 @@ using System.Threading.Tasks; using System.Web; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client { @@ -54,7 +55,7 @@ namespace Tgstation.Server.Client readonly ApiHeaders? tokenRefreshHeaders; /// - /// The for refreshes. + /// The for refreshes. /// readonly SemaphoreSlim semaphoreSlim; @@ -71,11 +72,11 @@ namespace Tgstation.Server.Client static void HandleBadResponse(HttpResponseMessage response, string json) { - ErrorMessage? errorMessage = null; + ErrorMessageResponse? errorMessage = null; try { // check if json serializes to an error message - errorMessage = JsonConvert.DeserializeObject(json, GetSerializerSettings()); + errorMessage = JsonConvert.DeserializeObject(json, GetSerializerSettings()); } catch (JsonException) { } @@ -271,7 +272,7 @@ namespace Tgstation.Server.Client if (startingToken != headers.Token) return true; - var token = await RunRequest(Routes.Root, new object(), HttpMethod.Post, null, true, cancellationToken); + var token = await RunRequest(Routes.Root, new object(), HttpMethod.Post, null, true, cancellationToken); headers = new ApiHeaders(headers.UserAgent!, token.Bearer!); } catch (ClientException) @@ -338,7 +339,7 @@ namespace Tgstation.Server.Client public void AddRequestLogger(IRequestLogger requestLogger) => requestLoggers.Add(requestLogger ?? throw new ArgumentNullException(nameof(requestLogger))); /// - public Task Download(FileTicketResult ticket, CancellationToken cancellationToken) + public Task Download(FileTicketResponse ticket, CancellationToken cancellationToken) { if (ticket == null) throw new ArgumentNullException(nameof(ticket)); @@ -353,7 +354,7 @@ namespace Tgstation.Server.Client } /// - public async Task Upload(FileTicketResult ticket, Stream? uploadStream, CancellationToken cancellationToken) + public async Task Upload(FileTicketResponse ticket, Stream? uploadStream, CancellationToken cancellationToken) { if (ticket == null) throw new ArgumentNullException(nameof(ticket)); diff --git a/src/Tgstation.Server.Client/ApiConflictException.cs b/src/Tgstation.Server.Client/ApiConflictException.cs index 53611af3b6..bfd1b1e51d 100644 --- a/src/Tgstation.Server.Client/ApiConflictException.cs +++ b/src/Tgstation.Server.Client/ApiConflictException.cs @@ -1,6 +1,6 @@ -using System; +using System; using System.Net.Http; -using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client { @@ -12,9 +12,9 @@ namespace Tgstation.Server.Client /// /// Construct an using an /// - /// The for the + /// The for the /// The for the - public ApiConflictException(ErrorMessage? errorMessage, HttpResponseMessage responseMessage) : base(errorMessage, responseMessage) + public ApiConflictException(ErrorMessageResponse? errorMessage, HttpResponseMessage responseMessage) : base(errorMessage, responseMessage) { } /// @@ -35,4 +35,4 @@ namespace Tgstation.Server.Client /// The inner for the base public ApiConflictException(string message, Exception innerException) : base(message, innerException) { } } -} \ No newline at end of file +} diff --git a/src/Tgstation.Server.Client/ApiException.cs b/src/Tgstation.Server.Client/ApiException.cs index 5dc4e1ab6b..d8f24addac 100644 --- a/src/Tgstation.Server.Client/ApiException.cs +++ b/src/Tgstation.Server.Client/ApiException.cs @@ -1,6 +1,7 @@ using System; using System.Net.Http; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client { @@ -27,9 +28,9 @@ namespace Tgstation.Server.Client /// /// Initialize a new instance of the . /// - /// The returned from the API. + /// The returned from the API. /// The . - protected ApiException(ErrorMessage? errorMessage, HttpResponseMessage responseMessage) : base( + protected ApiException(ErrorMessageResponse? errorMessage, HttpResponseMessage responseMessage) : base( responseMessage, errorMessage?.Message ?? $"HTTP {responseMessage?.StatusCode ?? throw new ArgumentNullException(nameof(responseMessage))}. Unknown API error, ErrorMessage payload not present!") { diff --git a/src/Tgstation.Server.Client/Components/ByondClient.cs b/src/Tgstation.Server.Client/Components/ByondClient.cs index 0a1847b35a..9f6bf84823 100644 --- a/src/Tgstation.Server.Client/Components/ByondClient.cs +++ b/src/Tgstation.Server.Client/Components/ByondClient.cs @@ -5,6 +5,8 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client.Components { @@ -28,23 +30,28 @@ namespace Tgstation.Server.Client.Components } /// - public Task ActiveVersion(CancellationToken cancellationToken) => ApiClient.Read(Routes.Byond, instance.Id, cancellationToken); + public Task ActiveVersion(CancellationToken cancellationToken) => ApiClient.Read(Routes.Byond, instance.Id!.Value, cancellationToken); /// - public Task> InstalledVersions(PaginationSettings? paginationSettings, CancellationToken cancellationToken) - => ReadPaged(paginationSettings, Routes.ListRoute(Routes.Byond), instance.Id, cancellationToken); + public Task> InstalledVersions(PaginationSettings? paginationSettings, CancellationToken cancellationToken) + => ReadPaged(paginationSettings, Routes.ListRoute(Routes.Byond), instance.Id, cancellationToken); /// - public async Task SetActiveVersion(Byond byond, Stream zipFileStream, CancellationToken cancellationToken) + public async Task SetActiveVersion(ByondVersionRequest installRequest, Stream zipFileStream, CancellationToken cancellationToken) { - var result = await ApiClient.Update( + if (installRequest == null) + throw new ArgumentNullException(nameof(installRequest)); + if (installRequest.UploadCustomZip == true && zipFileStream == null) + throw new ArgumentNullException(nameof(zipFileStream)); + + var result = await ApiClient.Update( Routes.Byond, - byond ?? throw new ArgumentNullException(nameof(byond)), - instance.Id, + installRequest, + instance.Id!.Value, cancellationToken) .ConfigureAwait(false); - if (byond.UploadCustomZip == true) + if (installRequest.UploadCustomZip == true) await ApiClient.Upload(result, zipFileStream, cancellationToken).ConfigureAwait(false); return result; diff --git a/src/Tgstation.Server.Client/Components/ChatBotsClient.cs b/src/Tgstation.Server.Client/Components/ChatBotsClient.cs index fa58da1c8a..a417bfb2ea 100644 --- a/src/Tgstation.Server.Client/Components/ChatBotsClient.cs +++ b/src/Tgstation.Server.Client/Components/ChatBotsClient.cs @@ -4,6 +4,8 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client.Components { @@ -27,19 +29,19 @@ namespace Tgstation.Server.Client.Components } /// - public Task Create(ChatBot settings, CancellationToken cancellationToken) => ApiClient.Create(Routes.Chat, settings ?? throw new ArgumentNullException(nameof(settings)), instance.Id, cancellationToken); + public Task Create(ChatBotCreateRequest settings, CancellationToken cancellationToken) => ApiClient.Create(Routes.Chat, settings ?? throw new ArgumentNullException(nameof(settings)), instance.Id!.Value, cancellationToken); /// - public Task Delete(ChatBot settings, CancellationToken cancellationToken) => ApiClient.Delete(Routes.SetID(Routes.Chat, settings?.Id ?? throw new ArgumentNullException(nameof(settings))), instance.Id, cancellationToken); + public Task Delete(EntityId settingsId, CancellationToken cancellationToken) => ApiClient.Delete(Routes.SetID(Routes.Chat, settingsId?.Id ?? throw new ArgumentNullException(nameof(settingsId))), instance.Id!.Value, cancellationToken); /// - public Task> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken) - => ReadPaged(paginationSettings, Routes.ListRoute(Routes.Chat), instance.Id, cancellationToken); + public Task> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken) + => ReadPaged(paginationSettings, Routes.ListRoute(Routes.Chat), instance.Id!.Value, cancellationToken); /// - public Task Update(ChatBot settings, CancellationToken cancellationToken) => ApiClient.Update(Routes.Chat, settings ?? throw new ArgumentNullException(nameof(settings)), instance.Id, cancellationToken); + public Task Update(ChatBotUpdateRequest settings, CancellationToken cancellationToken) => ApiClient.Update(Routes.Chat, settings ?? throw new ArgumentNullException(nameof(settings)), instance.Id!.Value, cancellationToken); /// - public Task GetId(ChatBot settings, CancellationToken cancellationToken) => ApiClient.Read(Routes.SetID(Routes.Chat, (settings ?? throw new ArgumentNullException(nameof(settings))).Id), instance.Id, cancellationToken); + public Task GetId(EntityId settingsId, CancellationToken cancellationToken) => ApiClient.Read(Routes.SetID(Routes.Chat, settingsId?.Id ?? throw new ArgumentNullException(nameof(settingsId))), instance.Id!.Value, cancellationToken); } } diff --git a/src/Tgstation.Server.Client/Components/ConfigurationClient.cs b/src/Tgstation.Server.Client/Components/ConfigurationClient.cs index e0fdb69db6..364095c035 100644 --- a/src/Tgstation.Server.Client/Components/ConfigurationClient.cs +++ b/src/Tgstation.Server.Client/Components/ConfigurationClient.cs @@ -5,6 +5,8 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client.Components { @@ -28,30 +30,30 @@ namespace Tgstation.Server.Client.Components } /// - public Task DeleteEmptyDirectory(ConfigurationFile directory, CancellationToken cancellationToken) => ApiClient.Delete(Routes.Configuration, directory, instance.Id, cancellationToken); + public Task DeleteEmptyDirectory(IConfigurationFile directory, CancellationToken cancellationToken) => ApiClient.Delete(Routes.Configuration, directory, instance.Id!.Value, cancellationToken); /// - public Task CreateDirectory(ConfigurationFile directory, CancellationToken cancellationToken) => ApiClient.Create(Routes.Configuration, directory, instance.Id, cancellationToken); + public Task CreateDirectory(IConfigurationFile directory, CancellationToken cancellationToken) => ApiClient.Create(Routes.Configuration, directory, instance.Id!.Value, cancellationToken); /// - public Task> List( + public Task> List( PaginationSettings? paginationSettings, string directory, CancellationToken cancellationToken) - => ReadPaged( + => ReadPaged( paginationSettings, Routes.ListRoute(Routes.Configuration) + Routes.SanitizeGetPath(directory), - instance.Id, + instance.Id!.Value, cancellationToken); /// - public async Task> Read(ConfigurationFile file, CancellationToken cancellationToken) + public async Task> Read(IConfigurationFile file, CancellationToken cancellationToken) { if (file == null) throw new ArgumentNullException(nameof(file)); - var configFile = await ApiClient.Read( + var configFile = await ApiClient.Read( Routes.ConfigurationFile + Routes.SanitizeGetPath(file.Path ?? throw new ArgumentException("file.Path should not be null!", nameof(file))), - instance.Id, + instance.Id!.Value, cancellationToken) .ConfigureAwait(false); var downloadStream = await ApiClient.Download(configFile, cancellationToken).ConfigureAwait(false); @@ -67,7 +69,7 @@ namespace Tgstation.Server.Client.Components } /// - public async Task Write(ConfigurationFile file, Stream uploadStream, CancellationToken cancellationToken) + public async Task Write(ConfigurationFileRequest file, Stream uploadStream, CancellationToken cancellationToken) { long initialStreamPosition = 0; MemoryStream? memoryStream = null; @@ -78,10 +80,10 @@ namespace Tgstation.Server.Client.Components using (memoryStream) { - var configFileTask = ApiClient.Update( + var configFileTask = ApiClient.Update( Routes.Configuration, file ?? throw new ArgumentNullException(nameof(file)), - instance.Id, + instance.Id!.Value, cancellationToken); if (memoryStream != null) diff --git a/src/Tgstation.Server.Client/Components/DreamDaemonClient.cs b/src/Tgstation.Server.Client/Components/DreamDaemonClient.cs index 566c8058fe..0acd56fca3 100644 --- a/src/Tgstation.Server.Client/Components/DreamDaemonClient.cs +++ b/src/Tgstation.Server.Client/Components/DreamDaemonClient.cs @@ -1,8 +1,10 @@ -using System; +using System; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client.Components { @@ -31,21 +33,21 @@ namespace Tgstation.Server.Client.Components } /// - public Task Shutdown(CancellationToken cancellationToken) => apiClient.Delete(Routes.DreamDaemon, instance.Id, cancellationToken); + public Task Shutdown(CancellationToken cancellationToken) => apiClient.Delete(Routes.DreamDaemon, instance.Id!.Value, cancellationToken); /// - public Task Start(CancellationToken cancellationToken) => apiClient.Create(Routes.DreamDaemon, instance.Id, cancellationToken); + public Task Start(CancellationToken cancellationToken) => apiClient.Create(Routes.DreamDaemon, instance.Id!.Value, cancellationToken); /// - public Task Restart(CancellationToken cancellationToken) => apiClient.Patch(Routes.DreamDaemon, instance.Id, cancellationToken); + public Task Restart(CancellationToken cancellationToken) => apiClient.Patch(Routes.DreamDaemon, instance.Id!.Value, cancellationToken); /// - public Task Read(CancellationToken cancellationToken) => apiClient.Read(Routes.DreamDaemon, instance.Id, cancellationToken); + public Task Read(CancellationToken cancellationToken) => apiClient.Read(Routes.DreamDaemon, instance.Id!.Value, cancellationToken); /// - public Task Update(DreamDaemon dreamDaemon, CancellationToken cancellationToken) => apiClient.Update(Routes.DreamDaemon, dreamDaemon ?? throw new ArgumentNullException(nameof(dreamDaemon)), instance.Id, cancellationToken); + public Task Update(DreamDaemonRequest dreamDaemon, CancellationToken cancellationToken) => apiClient.Update(Routes.DreamDaemon, dreamDaemon ?? throw new ArgumentNullException(nameof(dreamDaemon)), instance.Id!.Value, cancellationToken); /// - public Task CreateDump(CancellationToken cancellationToken) => apiClient.Patch(Routes.Diagnostics, instance.Id, cancellationToken); + public Task CreateDump(CancellationToken cancellationToken) => apiClient.Patch(Routes.Diagnostics, instance.Id!.Value, cancellationToken); } -} \ No newline at end of file +} diff --git a/src/Tgstation.Server.Client/Components/DreamMakerClient.cs b/src/Tgstation.Server.Client/Components/DreamMakerClient.cs index aa551322b5..251a3b8ab6 100644 --- a/src/Tgstation.Server.Client/Components/DreamMakerClient.cs +++ b/src/Tgstation.Server.Client/Components/DreamMakerClient.cs @@ -4,6 +4,8 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client.Components { @@ -27,19 +29,19 @@ namespace Tgstation.Server.Client.Components } /// - public Task Compile(CancellationToken cancellationToken) => ApiClient.Create(Routes.DreamMaker, instance.Id, cancellationToken); + public Task Compile(CancellationToken cancellationToken) => ApiClient.Create(Routes.DreamMaker, instance.Id!.Value, cancellationToken); /// - public Task GetCompileJob(EntityId compileJob, CancellationToken cancellationToken) => ApiClient.Read(Routes.SetID(Routes.DreamMaker, compileJob?.Id ?? throw new ArgumentNullException(nameof(compileJob))), instance.Id, cancellationToken); + public Task GetCompileJob(EntityId compileJob, CancellationToken cancellationToken) => ApiClient.Read(Routes.SetID(Routes.DreamMaker, compileJob?.Id ?? throw new ArgumentNullException(nameof(compileJob))), instance.Id!.Value, cancellationToken); /// - public Task> ListCompileJobs(PaginationSettings? paginationSettings, CancellationToken cancellationToken) - => ReadPaged(paginationSettings, Routes.ListRoute(Routes.DreamMaker), instance.Id, cancellationToken); + public Task> ListCompileJobs(PaginationSettings? paginationSettings, CancellationToken cancellationToken) + => ReadPaged(paginationSettings, Routes.ListRoute(Routes.DreamMaker), instance.Id!.Value, cancellationToken); /// - public Task Read(CancellationToken cancellationToken) => ApiClient.Read(Routes.DreamMaker, instance.Id, cancellationToken); + public Task Read(CancellationToken cancellationToken) => ApiClient.Read(Routes.DreamMaker, instance.Id!.Value, cancellationToken); /// - public Task Update(DreamMaker dreamMaker, CancellationToken cancellationToken) => ApiClient.Update(Routes.DreamMaker, dreamMaker ?? throw new ArgumentNullException(nameof(dreamMaker)), instance.Id, cancellationToken); + public Task Update(DreamMakerRequest dreamMaker, CancellationToken cancellationToken) => ApiClient.Update(Routes.DreamMaker, dreamMaker ?? throw new ArgumentNullException(nameof(dreamMaker)), instance.Id!.Value, cancellationToken); } } diff --git a/src/Tgstation.Server.Client/Components/IByondClient.cs b/src/Tgstation.Server.Client/Components/IByondClient.cs index ee1befa5c7..2dbf33ab9d 100644 --- a/src/Tgstation.Server.Client/Components/IByondClient.cs +++ b/src/Tgstation.Server.Client/Components/IByondClient.cs @@ -2,37 +2,38 @@ using System.Collections.Generic; using System.IO; using System.Threading; using System.Threading.Tasks; -using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client.Components { /// - /// For managing the installation + /// For managing the installation /// public interface IByondClient { /// - /// Get the active information + /// Get the active information /// /// The for the operation - /// A resulting in the active information - Task ActiveVersion(CancellationToken cancellationToken); + /// A resulting in the active information + Task ActiveVersion(CancellationToken cancellationToken); /// - /// Get all installed s + /// Get all installed s /// /// The optional for the operation. /// The for the operation - /// A resulting in an of installed s - Task> InstalledVersions(PaginationSettings? paginationSettings, CancellationToken cancellationToken); + /// A resulting in an of installed s + Task> InstalledVersions(PaginationSettings? paginationSettings, CancellationToken cancellationToken); /// - /// Updates the information + /// Updates the information /// - /// The information to update - /// The for the .zip file if is . + /// The . + /// The for the .zip file if is . /// The for the operation - /// A resulting in the updated information - Task SetActiveVersion(Byond byond, Stream zipFileStream, CancellationToken cancellationToken); + /// A resulting in the updated information + Task SetActiveVersion(ByondVersionRequest installRequest, Stream zipFileStream, CancellationToken cancellationToken); } } diff --git a/src/Tgstation.Server.Client/Components/IChatBotsClient.cs b/src/Tgstation.Server.Client/Components/IChatBotsClient.cs index 92485c0800..f38bd310d5 100644 --- a/src/Tgstation.Server.Client/Components/IChatBotsClient.cs +++ b/src/Tgstation.Server.Client/Components/IChatBotsClient.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client.Components { @@ -11,43 +13,43 @@ namespace Tgstation.Server.Client.Components public interface IChatBotsClient { /// - /// List the s + /// List the chat bots. /// /// The optional for the operation. /// The for the operation - /// A resulting in a of the of the server - Task> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken); + /// A resulting in a of the s. + Task> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken); /// - /// Create a + /// Create a chat bot. /// - /// The to create + /// The . /// The for the operation - /// A resulting in the new - Task Create(ChatBot settings, CancellationToken cancellationToken); + /// A resulting in the of the newly created chat bot. + Task Create(ChatBotCreateRequest settings, CancellationToken cancellationToken); /// - /// Updates a 's setttings + /// Updates a chat bot's setttings. /// - /// The to update + /// The . /// The for the operation - /// A resulting in the updated - Task Update(ChatBot settings, CancellationToken cancellationToken); + /// A resulting in the updated chat bot's . + Task Update(ChatBotUpdateRequest settings, CancellationToken cancellationToken); /// - /// Get a 's setttings + /// Get a specific chat bot's settings. /// - /// The to get + /// The of the chat bot to get. /// The for the operation - /// A resulting in the - Task GetId(ChatBot settings, CancellationToken cancellationToken); + /// A resulting in the . + Task GetId(EntityId settingsId, CancellationToken cancellationToken); /// - /// Delete a + /// Delete a chat bot. /// - /// The to delete + /// The of the chat bot to delete. /// The for the operation /// A representing the running operation - Task Delete(ChatBot settings, CancellationToken cancellationToken); + Task Delete(EntityId settingsId, CancellationToken cancellationToken); } } diff --git a/src/Tgstation.Server.Client/Components/IConfigurationClient.cs b/src/Tgstation.Server.Client/Components/IConfigurationClient.cs index 9e022fe084..4813807330 100644 --- a/src/Tgstation.Server.Client/Components/IConfigurationClient.cs +++ b/src/Tgstation.Server.Client/Components/IConfigurationClient.cs @@ -4,11 +4,13 @@ using System.IO; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client.Components { /// - /// For managing files + /// For managing s. /// public interface IConfigurationClient { @@ -18,43 +20,43 @@ namespace Tgstation.Server.Client.Components /// The optional for the operation. /// The path to the directory to list files in /// The for the operation - /// A of s in the - Task> List( + /// A of s in the . + Task> List( PaginationSettings? paginationSettings, string directory, CancellationToken cancellationToken); /// - /// Read a file + /// Read a . /// - /// The file to read + /// The file to read /// The for the operation - /// A resulting in a containing the and downloaded . - Task> Read(ConfigurationFile file, CancellationToken cancellationToken); + /// A resulting in a containing the and downloaded . + Task> Read(IConfigurationFile file, CancellationToken cancellationToken); /// - /// Overwrite a file + /// Overwrite a . /// - /// The file to write + /// The . /// The of uploaded data. If , a delete will be attempted. /// The for the operation - /// A resulting in the new . - Task Write(ConfigurationFile file, Stream uploadStream, CancellationToken cancellationToken); + /// A resulting in the new . + Task Write(ConfigurationFileRequest file, Stream uploadStream, CancellationToken cancellationToken); /// /// Delete an empty /// - /// The representing the directory to delete + /// The representing the directory to delete /// The for the operation /// A representing the running operation - Task DeleteEmptyDirectory(ConfigurationFile directory, CancellationToken cancellationToken); + Task DeleteEmptyDirectory(IConfigurationFile directory, CancellationToken cancellationToken); /// /// Creates an empty /// - /// The representing the directory to create + /// The representing the directory to create /// The for the operation - /// A resulting in the new - Task CreateDirectory(ConfigurationFile directory, CancellationToken cancellationToken); + /// A resulting in the new + Task CreateDirectory(IConfigurationFile directory, CancellationToken cancellationToken); } } diff --git a/src/Tgstation.Server.Client/Components/IDreamDaemonClient.cs b/src/Tgstation.Server.Client/Components/IDreamDaemonClient.cs index f5b78966bb..fb67e9c709 100644 --- a/src/Tgstation.Server.Client/Components/IDreamDaemonClient.cs +++ b/src/Tgstation.Server.Client/Components/IDreamDaemonClient.cs @@ -1,55 +1,56 @@ -using System.Threading; +using System.Threading; using System.Threading.Tasks; -using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client.Components { /// - /// For managing + /// For managing /// public interface IDreamDaemonClient { /// - /// Get the represented by the + /// Get the represented by the /// /// The for the operation - /// A resulting in the information - Task Read(CancellationToken cancellationToken); + /// A resulting in the information + Task Read(CancellationToken cancellationToken); /// - /// Start + /// Start /// /// The for the operation - /// A resulting in the of the running operation - Task Start(CancellationToken cancellationToken); + /// A resulting in the of the running operation + Task Start(CancellationToken cancellationToken); /// - /// Restart + /// Restart /// /// The for the operation - /// A resulting in the of the running operation - Task Restart(CancellationToken cancellationToken); + /// A resulting in the of the running operation + Task Restart(CancellationToken cancellationToken); /// - /// Shutdown + /// Shutdown /// /// The for the operation - /// A resulting in the information + /// A resulting in the information Task Shutdown(CancellationToken cancellationToken); /// - /// Update . This may trigger + /// Update . This may trigger a /// - /// The to update + /// The . /// The for the operation - /// A resulting in the information - Task Update(DreamDaemon dreamDaemon, CancellationToken cancellationToken); + /// A resulting in the information + Task Update(DreamDaemonRequest dreamDaemon, CancellationToken cancellationToken); /// /// Start a job to create a process dump of the active DreamDaemon executable. /// /// The for the operation. - /// A resulting in the of the running operation. - Task CreateDump(CancellationToken cancellationToken); + /// A resulting in the of the running operation. + Task CreateDump(CancellationToken cancellationToken); } } diff --git a/src/Tgstation.Server.Client/Components/IDreamMakerClient.cs b/src/Tgstation.Server.Client/Components/IDreamMakerClient.cs index 9c45f65e33..ccffd7a11d 100644 --- a/src/Tgstation.Server.Client/Components/IDreamMakerClient.cs +++ b/src/Tgstation.Server.Client/Components/IDreamMakerClient.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client.Components { @@ -11,41 +13,41 @@ namespace Tgstation.Server.Client.Components public interface IDreamMakerClient { /// - /// Get the information + /// Get the . /// /// The for the operation - /// A resulting in the information - Task Read(CancellationToken cancellationToken); + /// A resulting in the . + Task Read(CancellationToken cancellationToken); /// - /// Updates the setttings + /// Updates the . /// - /// The to update + /// The to update /// The for the operation /// A representing the running operation - Task Update(DreamMaker dreamMaker, CancellationToken cancellationToken); + Task Update(DreamMakerRequest dreamMaker, CancellationToken cancellationToken); /// /// Compile the current repository revision /// /// The for the operation - /// A resulting in the for the compile - Task Compile(CancellationToken cancellationToken); + /// A resulting in the for the compile + Task Compile(CancellationToken cancellationToken); /// - /// Gets the s for the instance + /// Gets the s for the instance. /// /// The optional for the operation. /// The for the operation - /// A resulting in a of s. - Task> ListCompileJobs(PaginationSettings? paginationSettings, CancellationToken cancellationToken); + /// A resulting in a of s. + Task> ListCompileJobs(PaginationSettings? paginationSettings, CancellationToken cancellationToken); /// /// Get a /// - /// The to get + /// The 's to get /// The for the operation - /// A resulting in the - Task GetCompileJob(EntityId compileJob, CancellationToken cancellationToken); + /// A resulting in the . + Task GetCompileJob(EntityId compileJob, CancellationToken cancellationToken); } } diff --git a/src/Tgstation.Server.Client/Components/IInstanceClient.cs b/src/Tgstation.Server.Client/Components/IInstanceClient.cs index 63ea36d530..0e0b503f28 100644 --- a/src/Tgstation.Server.Client/Components/IInstanceClient.cs +++ b/src/Tgstation.Server.Client/Components/IInstanceClient.cs @@ -3,7 +3,7 @@ using Tgstation.Server.Api.Models; namespace Tgstation.Server.Client.Components { /// - /// For managing a single + /// For managing a single . /// public interface IInstanceClient { diff --git a/src/Tgstation.Server.Client/Components/IInstancePermissionSetClient.cs b/src/Tgstation.Server.Client/Components/IInstancePermissionSetClient.cs index 570c7b6034..17febd87fd 100644 --- a/src/Tgstation.Server.Client/Components/IInstancePermissionSetClient.cs +++ b/src/Tgstation.Server.Client/Components/IInstancePermissionSetClient.cs @@ -1,58 +1,60 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Internal; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client.Components { /// - /// For managing s + /// For managing instance permission sets. /// public interface IInstancePermissionSetClient { /// - /// Get the associated with the logged on user + /// Get the instance permission sets associated with the logged on user. /// /// The for the operation - /// A resulting in the associated with the logged on user - Task Read(CancellationToken cancellationToken); + /// A resulting in the associated with the logged on user + Task Read(CancellationToken cancellationToken); /// /// Get a specific /// - /// The to get + /// The . /// The for the operation /// A resulting in the requested - Task GetId(InstancePermissionSet instancePermissionSet, CancellationToken cancellationToken); + Task GetId(InstancePermissionSet instancePermissionSet, CancellationToken cancellationToken); /// - /// Get the s in the + /// Get the instance permission sets in the instance. /// /// The optional for the operation. /// The for the operation - /// A resulting in a of s in the instance - Task> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken); + /// A resulting in a of s in the instance. + Task> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken); /// /// Update a /// - /// The to update + /// The . /// The for the operation /// A representing the running operation - Task Update(InstancePermissionSet instancePermissionSet, CancellationToken cancellationToken); + Task Update(InstancePermissionSetRequest instancePermissionSet, CancellationToken cancellationToken); /// /// Create a /// - /// The to create + /// The . /// The for the operation - /// A reulting in the new - Task Create(InstancePermissionSet instancePermissionSet, CancellationToken cancellationToken); + /// A reulting in the new . + Task Create(InstancePermissionSetRequest instancePermissionSet, CancellationToken cancellationToken); /// /// Delete a /// - /// The to delete + /// The to delete /// The for the operation /// A representing the running operation Task Delete(InstancePermissionSet instancePermissionSet, CancellationToken cancellationToken); diff --git a/src/Tgstation.Server.Client/Components/IJobsClient.cs b/src/Tgstation.Server.Client/Components/IJobsClient.cs index b3d800b51b..79702efcf3 100644 --- a/src/Tgstation.Server.Client/Components/IJobsClient.cs +++ b/src/Tgstation.Server.Client/Components/IJobsClient.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client.Components { @@ -11,35 +12,35 @@ namespace Tgstation.Server.Client.Components public interface IJobsClient { /// - /// List the s in the + /// List the s in the /// /// The optional for the operation. /// The for the operation - /// A resulting in a of the s in the - Task> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken); + /// A resulting in a of the s in the + Task> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken); /// - /// List the active s in the + /// List the active s in the /// /// The optional for the operation. /// The for the operation - /// A resulting in a of the active s in the - Task> ListActive(PaginationSettings? paginationSettings, CancellationToken cancellationToken); + /// A resulting in a of the active s in the + Task> ListActive(PaginationSettings? paginationSettings, CancellationToken cancellationToken); /// /// Get a /// - /// The 's to get + /// The 's to get /// The for the operation - /// A resulting in the - Task GetId(EntityId job, CancellationToken cancellationToken); + /// A resulting in the + Task GetId(EntityId job, CancellationToken cancellationToken); /// /// Cancels a /// - /// The to cancel + /// The to cancel /// The for the operation /// A representing the running operation - Task Cancel(Job job, CancellationToken cancellationToken); + Task Cancel(JobResponse job, CancellationToken cancellationToken); } } diff --git a/src/Tgstation.Server.Client/Components/IRepositoryClient.cs b/src/Tgstation.Server.Client/Components/IRepositoryClient.cs index daecf62b2e..645567bb64 100644 --- a/src/Tgstation.Server.Client/Components/IRepositoryClient.cs +++ b/src/Tgstation.Server.Client/Components/IRepositoryClient.cs @@ -1,42 +1,43 @@ -using System.Threading; +using System.Threading; using System.Threading.Tasks; -using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client.Components { /// - /// For managing the + /// For managing the git repository. /// public interface IRepositoryClient { /// - /// Get the represented by the + /// Get the repository's current status. /// /// The for the operation - /// A resulting in the - Task Read(CancellationToken cancellationToken); + /// A resulting in the . + Task Read(CancellationToken cancellationToken); /// - /// Update the + /// Update the repository. /// - /// The to update + /// The . /// The for the operation - /// A resulting in the updated - Task Update(Repository repository, CancellationToken cancellationToken); + /// A resulting in the . + Task Update(RepositoryUpdateRequest repository, CancellationToken cancellationToken); /// /// Clones a /// - /// The to clone + /// The . /// The for the operation - /// A resulting in the updated - Task Clone(Repository repository, CancellationToken cancellationToken); + /// A resulting in the / + Task Clone(RepositoryCreateRequest repository, CancellationToken cancellationToken); /// - /// Deletes the + /// Deletes the repository. /// /// The for the operation - /// A resulting in the updated - Task Delete(CancellationToken cancellationToken); + /// A resulting in the . + Task Delete(CancellationToken cancellationToken); } } diff --git a/src/Tgstation.Server.Client/Components/InstanceClient.cs b/src/Tgstation.Server.Client/Components/InstanceClient.cs index 1f854ff680..fda79d9220 100644 --- a/src/Tgstation.Server.Client/Components/InstanceClient.cs +++ b/src/Tgstation.Server.Client/Components/InstanceClient.cs @@ -1,4 +1,4 @@ -using System; +using System; using Tgstation.Server.Api.Models; namespace Tgstation.Server.Client.Components @@ -42,6 +42,8 @@ namespace Tgstation.Server.Client.Components { if (apiClient == null) throw new ArgumentNullException(nameof(apiClient)); + if (!instance.Id.HasValue) + throw new ArgumentException("Instance missing ID!", nameof(instance)); Metadata = instance ?? throw new ArgumentNullException(nameof(instance)); @@ -55,4 +57,4 @@ namespace Tgstation.Server.Client.Components Jobs = new JobsClient(apiClient, instance); } } -} \ No newline at end of file +} diff --git a/src/Tgstation.Server.Client/Components/InstancePermissionSetClient.cs b/src/Tgstation.Server.Client/Components/InstancePermissionSetClient.cs index a202fe285a..29005fa921 100644 --- a/src/Tgstation.Server.Client/Components/InstancePermissionSetClient.cs +++ b/src/Tgstation.Server.Client/Components/InstancePermissionSetClient.cs @@ -4,6 +4,9 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Internal; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client.Components { @@ -27,31 +30,31 @@ namespace Tgstation.Server.Client.Components } /// - public Task Create(InstancePermissionSet instancePermissionSet, CancellationToken cancellationToken) => ApiClient.Create(Routes.InstancePermissionSet, instancePermissionSet ?? throw new ArgumentNullException(nameof(instancePermissionSet)), instance.Id, cancellationToken); + public Task Create(InstancePermissionSetRequest instancePermissionSet, CancellationToken cancellationToken) => ApiClient.Create(Routes.InstancePermissionSet, instancePermissionSet ?? throw new ArgumentNullException(nameof(instancePermissionSet)), instance.Id!.Value, cancellationToken); /// public Task Delete(InstancePermissionSet instancePermissionSet, CancellationToken cancellationToken) => ApiClient.Delete( Routes.SetID( Routes.InstancePermissionSet, instancePermissionSet.PermissionSetId), - instance.Id, + instance.Id!.Value, cancellationToken); /// - public Task Read(CancellationToken cancellationToken) => ApiClient.Read(Routes.InstancePermissionSet, instance.Id, cancellationToken); + public Task Read(CancellationToken cancellationToken) => ApiClient.Read(Routes.InstancePermissionSet, instance.Id!.Value, cancellationToken); /// - public Task Update(InstancePermissionSet instancePermissionSet, CancellationToken cancellationToken) => ApiClient.Update(Routes.InstancePermissionSet, instancePermissionSet ?? throw new ArgumentNullException(nameof(instancePermissionSet)), instance.Id, cancellationToken); + public Task Update(InstancePermissionSetRequest instancePermissionSet, CancellationToken cancellationToken) => ApiClient.Update(Routes.InstancePermissionSet, instancePermissionSet ?? throw new ArgumentNullException(nameof(instancePermissionSet)), instance.Id!.Value, cancellationToken); /// - public Task> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken) - => ReadPaged( + public Task> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken) + => ReadPaged( paginationSettings, Routes.ListRoute(Routes.InstancePermissionSet), instance.Id, cancellationToken); /// - public Task GetId(InstancePermissionSet instancePermissionSet, CancellationToken cancellationToken) => ApiClient.Read(Routes.SetID(Routes.InstancePermissionSet, instancePermissionSet?.PermissionSetId ?? throw new ArgumentNullException(nameof(instancePermissionSet))), instance.Id, cancellationToken); + public Task GetId(InstancePermissionSet instancePermissionSet, CancellationToken cancellationToken) => ApiClient.Read(Routes.SetID(Routes.InstancePermissionSet, instancePermissionSet?.PermissionSetId ?? throw new ArgumentNullException(nameof(instancePermissionSet))), instance.Id!.Value, cancellationToken); } } diff --git a/src/Tgstation.Server.Client/Components/JobsClient.cs b/src/Tgstation.Server.Client/Components/JobsClient.cs index cf6a8875ec..c03bdc25af 100644 --- a/src/Tgstation.Server.Client/Components/JobsClient.cs +++ b/src/Tgstation.Server.Client/Components/JobsClient.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client.Components { @@ -27,17 +28,17 @@ namespace Tgstation.Server.Client.Components } /// - public Task Cancel(Job job, CancellationToken cancellationToken) => ApiClient.Delete(Routes.SetID(Routes.Jobs, job?.Id ?? throw new ArgumentNullException(nameof(job))), instance.Id, cancellationToken); + public Task Cancel(JobResponse job, CancellationToken cancellationToken) => ApiClient.Delete(Routes.SetID(Routes.Jobs, job?.Id ?? throw new ArgumentNullException(nameof(job))), instance.Id!.Value, cancellationToken); /// - public Task> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken) - => ReadPaged(paginationSettings, Routes.ListRoute(Routes.Jobs), instance.Id, cancellationToken); + public Task> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken) + => ReadPaged(paginationSettings, Routes.ListRoute(Routes.Jobs), instance.Id!.Value, cancellationToken); /// - public Task> ListActive(PaginationSettings? paginationSettings, CancellationToken cancellationToken) - => ReadPaged(paginationSettings, Routes.Jobs, instance.Id, cancellationToken); + public Task> ListActive(PaginationSettings? paginationSettings, CancellationToken cancellationToken) + => ReadPaged(paginationSettings, Routes.Jobs, instance.Id!.Value, cancellationToken); /// - public Task GetId(EntityId job, CancellationToken cancellationToken) => ApiClient.Read(Routes.SetID(Routes.Jobs, job?.Id ?? throw new ArgumentNullException(nameof(job))), instance.Id, cancellationToken); + public Task GetId(EntityId job, CancellationToken cancellationToken) => ApiClient.Read(Routes.SetID(Routes.Jobs, job?.Id ?? throw new ArgumentNullException(nameof(job))), instance.Id!.Value, cancellationToken); } } diff --git a/src/Tgstation.Server.Client/Components/RepositoryClient.cs b/src/Tgstation.Server.Client/Components/RepositoryClient.cs index 97d4cdfec5..9ab222b4e7 100644 --- a/src/Tgstation.Server.Client/Components/RepositoryClient.cs +++ b/src/Tgstation.Server.Client/Components/RepositoryClient.cs @@ -1,8 +1,10 @@ -using System; +using System; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client.Components { @@ -31,15 +33,15 @@ namespace Tgstation.Server.Client.Components } /// - public Task Clone(Repository repository, CancellationToken cancellationToken) => apiClient.Create(Routes.Repository, repository, instance.Id, cancellationToken); + public Task Clone(RepositoryCreateRequest repository, CancellationToken cancellationToken) => apiClient.Create(Routes.Repository, repository, instance.Id!.Value, cancellationToken); /// - public Task Delete(CancellationToken cancellationToken) => apiClient.Delete(Routes.Repository, instance.Id, cancellationToken); + public Task Delete(CancellationToken cancellationToken) => apiClient.Delete(Routes.Repository, instance.Id!.Value, cancellationToken); /// - public Task Read(CancellationToken cancellationToken) => apiClient.Read(Routes.Repository, instance.Id, cancellationToken); + public Task Read(CancellationToken cancellationToken) => apiClient.Read(Routes.Repository, instance.Id!.Value, cancellationToken); /// - public Task Update(Repository repository, CancellationToken cancellationToken) => apiClient.Update(Routes.Repository, repository ?? throw new ArgumentNullException(nameof(repository)), instance.Id, cancellationToken); + public Task Update(RepositoryUpdateRequest repository, CancellationToken cancellationToken) => apiClient.Update(Routes.Repository, repository ?? throw new ArgumentNullException(nameof(repository)), instance.Id!.Value, cancellationToken); } -} \ No newline at end of file +} diff --git a/src/Tgstation.Server.Client/ConflictException.cs b/src/Tgstation.Server.Client/ConflictException.cs index d8ce94379d..45d2e19a3c 100644 --- a/src/Tgstation.Server.Client/ConflictException.cs +++ b/src/Tgstation.Server.Client/ConflictException.cs @@ -1,6 +1,6 @@ -using System; +using System; using System.Net.Http; -using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client { @@ -12,9 +12,9 @@ namespace Tgstation.Server.Client /// /// Initialize a new instance of the . /// - /// The for the . + /// The for the . /// The for the . - public ConflictException(ErrorMessage? errorMessage, HttpResponseMessage responseMessage) : base(errorMessage, responseMessage) + public ConflictException(ErrorMessageResponse? errorMessage, HttpResponseMessage responseMessage) : base(errorMessage, responseMessage) { } /// @@ -35,4 +35,4 @@ namespace Tgstation.Server.Client /// The inner for the base public ConflictException(string message, Exception innerException) : base(message, innerException) { } } -} \ No newline at end of file +} diff --git a/src/Tgstation.Server.Client/IAdministrationClient.cs b/src/Tgstation.Server.Client/IAdministrationClient.cs index b8abccfc91..1ddfb5d890 100644 --- a/src/Tgstation.Server.Client/IAdministrationClient.cs +++ b/src/Tgstation.Server.Client/IAdministrationClient.cs @@ -3,7 +3,8 @@ using System.Collections.Generic; using System.IO; using System.Threading; using System.Threading.Tasks; -using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client { @@ -13,19 +14,19 @@ namespace Tgstation.Server.Client public interface IAdministrationClient { /// - /// Get the represented by the + /// Get the represented by the /// /// The for the operation - /// A resulting in the represented by the - Task Read(CancellationToken cancellationToken); + /// A resulting in the represented by the + Task Read(CancellationToken cancellationToken); /// - /// Updates the setttings + /// Updates the setttings /// - /// The to update + /// The . /// The for the operation - /// A representing the running operation - Task Update(Administration administration, CancellationToken cancellationToken); + /// A resulting in the echoed . + Task Update(ServerUpdateRequest updateRequest, CancellationToken cancellationToken); /// /// Restarts the TGS server @@ -39,15 +40,15 @@ namespace Tgstation.Server.Client /// /// The optional for the operation. /// The for the operation - /// A resulting in an of metadata. - Task> ListLogs(PaginationSettings? paginationSettings, CancellationToken cancellationToken); + /// A resulting in an of metadata. + Task> ListLogs(PaginationSettings? paginationSettings, CancellationToken cancellationToken); /// /// Download a given . /// - /// The to download. + /// The to download. /// The for the operation - /// A resulting a containing the downloaded and associated . - Task> GetLog(LogFile logFile, CancellationToken cancellationToken); + /// A resulting a containing the downloaded and associated . + Task> GetLog(LogFileResponse logFile, CancellationToken cancellationToken); } } diff --git a/src/Tgstation.Server.Client/IApiClient.cs b/src/Tgstation.Server.Client/IApiClient.cs index 47b93e84d7..d76772f0c6 100644 --- a/src/Tgstation.Server.Client/IApiClient.cs +++ b/src/Tgstation.Server.Client/IApiClient.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client { @@ -205,18 +206,18 @@ namespace Tgstation.Server.Client /// /// Downloads a file for a given . /// - /// The to download. + /// The to download. /// The for the operation. /// A resulting in the downloaded . - Task Download(FileTicketResult ticket, CancellationToken cancellationToken); + Task Download(FileTicketResponse ticket, CancellationToken cancellationToken); /// /// Uploads a given for a given . /// - /// The to download. + /// The to download. /// The to upload. represents an empty file. /// The for the operation. /// A representing the running operation. - Task Upload(FileTicketResult ticket, Stream? uploadStream, CancellationToken cancellationToken); + Task Upload(FileTicketResponse ticket, Stream? uploadStream, CancellationToken cancellationToken); } } diff --git a/src/Tgstation.Server.Client/IApiClientFactory.cs b/src/Tgstation.Server.Client/IApiClientFactory.cs index 3cc76b8d1f..d95b12ea32 100644 --- a/src/Tgstation.Server.Client/IApiClientFactory.cs +++ b/src/Tgstation.Server.Client/IApiClientFactory.cs @@ -1,4 +1,4 @@ -using System; +using System; using Tgstation.Server.Api; namespace Tgstation.Server.Client @@ -13,7 +13,7 @@ namespace Tgstation.Server.Client /// /// The base /// The for the - /// The to use to generate a new . + /// The to use to generate a new . /// A new IApiClient CreateApiClient(Uri url, ApiHeaders apiHeaders, ApiHeaders? tokenRefreshHeaders); } diff --git a/src/Tgstation.Server.Client/IInstanceManagerClient.cs b/src/Tgstation.Server.Client/IInstanceManagerClient.cs index 4e53f8f32b..644a311840 100644 --- a/src/Tgstation.Server.Client/IInstanceManagerClient.cs +++ b/src/Tgstation.Server.Client/IInstanceManagerClient.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Client.Components; namespace Tgstation.Server.Client @@ -17,23 +19,23 @@ namespace Tgstation.Server.Client /// The optional for the operation. /// The for the operation /// A resulting in a of all s the user can view - Task> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken); + Task> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken); /// /// Create or attach an /// - /// The to create. will be ignored + /// The . /// The for the operation /// A resulting in the created or attached - Task CreateOrAttach(Instance instance, CancellationToken cancellationToken); + Task CreateOrAttach(InstanceCreateRequest instance, CancellationToken cancellationToken); /// /// Relocates, renamed, and/or on/offlines an /// - /// The to update + /// The . /// The for the operation /// A resulting in the updated - Task Update(Instance instance, CancellationToken cancellationToken); + Task Update(InstanceUpdateRequest instance, CancellationToken cancellationToken); /// /// Get a specific @@ -41,28 +43,28 @@ namespace Tgstation.Server.Client /// The to get /// The for the operation /// A resulting in the - Task GetId(Instance instance, CancellationToken cancellationToken); + Task GetId(EntityId instance, CancellationToken cancellationToken); /// /// Deletes an /// - /// The to delete + /// The of the to delete /// The for the operation /// A representing the running operation - Task Detach(Instance instance, CancellationToken cancellationToken); + Task Detach(EntityId instance, CancellationToken cancellationToken); /// /// Gives the user full permissions on an . /// - /// The to grant permissions on. + /// The of the to grant permissions on. /// The for the operation. /// A representing the running operation. - Task GrantPermissions(Instance instance, CancellationToken cancellationToken); + Task GrantPermissions(EntityId instance, CancellationToken cancellationToken); /// /// Create an for a given /// - /// The to create an for + /// The of the to create an for /// A new IInstanceClient CreateClient(Instance instance); } diff --git a/src/Tgstation.Server.Client/IServerClient.cs b/src/Tgstation.Server.Client/IServerClient.cs index 048d7eb53e..9939530351 100644 --- a/src/Tgstation.Server.Client/IServerClient.cs +++ b/src/Tgstation.Server.Client/IServerClient.cs @@ -1,7 +1,7 @@ using System; using System.Threading; using System.Threading.Tasks; -using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client { @@ -18,7 +18,7 @@ namespace Tgstation.Server.Client /// /// The used to access the server /// - Token Token { get; set; } + TokenResponse Token { get; set; } /// /// The connection timeout @@ -46,11 +46,11 @@ namespace Tgstation.Server.Client IUserGroupsClient Groups { get; } /// - /// The of the + /// The of the /// /// The for the operation - /// A resulting in the of the target server - Task ServerInformation(CancellationToken cancellationToken); + /// A resulting in the of the target server + Task ServerInformation(CancellationToken cancellationToken); /// /// Adds a to the request pipeline diff --git a/src/Tgstation.Server.Client/IServerClientFactory.cs b/src/Tgstation.Server.Client/IServerClientFactory.cs index 83c8905f64..cbbbc60f63 100644 --- a/src/Tgstation.Server.Client/IServerClientFactory.cs +++ b/src/Tgstation.Server.Client/IServerClientFactory.cs @@ -1,8 +1,8 @@ -using System; +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client { @@ -19,7 +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 . + /// 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( @@ -35,10 +35,10 @@ namespace Tgstation.Server.Client /// Create a /// /// The URL to access TGS - /// The to access the API with + /// The to access the API with /// A new IServerClient CreateFromToken( Uri host, - Token token); + TokenResponse token); } } diff --git a/src/Tgstation.Server.Client/IUserGroupsClient.cs b/src/Tgstation.Server.Client/IUserGroupsClient.cs index 02141cad54..d2c360eb30 100644 --- a/src/Tgstation.Server.Client/IUserGroupsClient.cs +++ b/src/Tgstation.Server.Client/IUserGroupsClient.cs @@ -2,50 +2,52 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client { /// - /// For managing s. + /// For managing s. /// public interface IUserGroupsClient { /// /// Get a specific . /// - /// The of the to get. + /// The of the user group to get. /// The for the operation. /// A resulting in the requested . - Task GetId(EntityId group, CancellationToken cancellationToken); + Task GetId(EntityId group, CancellationToken cancellationToken); /// - /// List all s. + /// List all s. /// /// The optional for the operation. /// The for the operation. - /// A resulting in a of all s. - Task> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken); + /// A resulting in a of all s. + Task> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken); /// /// Create a new . /// - /// The to create. + /// The . /// The for the operation. - /// The new . - Task Create(UserGroup group, CancellationToken cancellationToken); + /// The new . + Task Create(UserGroupCreateRequest group, CancellationToken cancellationToken); /// /// Update a . /// - /// The updated . + /// The . /// The for the operation. - /// The updated . - Task Update(UserGroup group, CancellationToken cancellationToken); + /// The updated . + Task Update(UserGroupUpdateRequest group, CancellationToken cancellationToken); /// /// Deletes a . /// - /// The of the to delete. + /// The of the user group to delete. /// The for the operation. /// A representing the running operation. Task Delete(EntityId group, CancellationToken cancellationToken); diff --git a/src/Tgstation.Server.Client/IUsersClient.cs b/src/Tgstation.Server.Client/IUsersClient.cs index 0328188210..fdc8a54b81 100644 --- a/src/Tgstation.Server.Client/IUsersClient.cs +++ b/src/Tgstation.Server.Client/IUsersClient.cs @@ -2,11 +2,13 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client { /// - /// For managing s + /// For managing s /// public interface IUsersClient { @@ -14,39 +16,39 @@ namespace Tgstation.Server.Client /// Read the current user's information and general rights /// /// The for the operation - /// A resulting in the current - Task Read(CancellationToken cancellationToken); + /// A resulting in the current + Task Read(CancellationToken cancellationToken); /// /// Get a specific /// - /// The to get. + /// The of the to get. /// The for the operation /// A resulting in the requested - Task GetId(Api.Models.Internal.UserBase user, CancellationToken cancellationToken); + Task GetId(EntityId user, CancellationToken cancellationToken); /// - /// List all s + /// List all s /// /// The optional for the operation. /// The for the operation - /// A resulting in a of all s - Task> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken); + /// A resulting in a of all s + Task> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken); /// /// Create a new /// - /// The used to create the new + /// The . /// The for the operation - /// The new - Task Create(UserUpdate user, CancellationToken cancellationToken); + /// The new + Task Create(UserCreateRequest user, CancellationToken cancellationToken); /// /// Update a /// - /// The used to update the + /// The used to update the /// The for the operation - /// The updated - Task Update(UserUpdate user, CancellationToken cancellationToken); + /// The updated + Task Update(UserUpdateRequest user, CancellationToken cancellationToken); } } diff --git a/src/Tgstation.Server.Client/InstanceManagerClient.cs b/src/Tgstation.Server.Client/InstanceManagerClient.cs index 910f09896e..089cd3bcb7 100644 --- a/src/Tgstation.Server.Client/InstanceManagerClient.cs +++ b/src/Tgstation.Server.Client/InstanceManagerClient.cs @@ -4,6 +4,8 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Client.Components; namespace Tgstation.Server.Client @@ -20,25 +22,36 @@ namespace Tgstation.Server.Client { } /// - public Task CreateOrAttach(Instance instance, CancellationToken cancellationToken) => ApiClient.Create(Routes.InstanceManager, instance ?? throw new ArgumentNullException(nameof(instance)), cancellationToken); + public Task CreateOrAttach(InstanceCreateRequest instance, CancellationToken cancellationToken) => ApiClient.Create(Routes.InstanceManager, instance ?? throw new ArgumentNullException(nameof(instance)), cancellationToken); /// - public Task Detach(Instance instance, CancellationToken cancellationToken) => ApiClient.Delete(Routes.SetID(Routes.InstanceManager, instance?.Id ?? throw new ArgumentNullException(nameof(instance))), cancellationToken); + public Task Detach(EntityId instance, CancellationToken cancellationToken) => ApiClient.Delete(Routes.SetID(Routes.InstanceManager, instance?.Id ?? throw new ArgumentNullException(nameof(instance))), cancellationToken); /// - public Task> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken) - => ReadPaged(paginationSettings, Routes.ListRoute(Routes.InstanceManager), null, cancellationToken); + public Task> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken) + => ReadPaged(paginationSettings, Routes.ListRoute(Routes.InstanceManager), null, cancellationToken); /// - public Task Update(Instance instance, CancellationToken cancellationToken) => ApiClient.Update(Routes.InstanceManager, instance ?? throw new ArgumentNullException(nameof(instance)), cancellationToken); + public Task Update(InstanceUpdateRequest instance, CancellationToken cancellationToken) => ApiClient.Update(Routes.InstanceManager, instance ?? throw new ArgumentNullException(nameof(instance)), cancellationToken); /// - public Task GetId(Instance instance, CancellationToken cancellationToken) => ApiClient.Read(Routes.SetID(Routes.InstanceManager, instance?.Id ?? throw new ArgumentNullException(nameof(instance))), cancellationToken); + public Task GetId(EntityId instance, CancellationToken cancellationToken) => ApiClient.Read(Routes.SetID(Routes.InstanceManager, instance?.Id ?? throw new ArgumentNullException(nameof(instance))), cancellationToken); /// - public Task GrantPermissions(Instance instance, CancellationToken cancellationToken) => ApiClient.Patch(Routes.SetID(Routes.InstanceManager, instance?.Id ?? throw new ArgumentNullException(nameof(instance))), cancellationToken); + public Task GrantPermissions(EntityId instance, CancellationToken cancellationToken) => ApiClient.Patch(Routes.SetID(Routes.InstanceManager, instance?.Id ?? throw new ArgumentNullException(nameof(instance))), cancellationToken); /// - public IInstanceClient CreateClient(Instance instance) => new InstanceClient(ApiClient, instance); + public IInstanceClient CreateClient(Instance instance) + { + if (instance == null) + throw new ArgumentNullException(nameof(instance)); + + if (!instance.Id.HasValue) + throw new ArgumentException("Instance missing Id!", nameof(instance)); + + return new InstanceClient( + ApiClient, + instance); + } } } diff --git a/src/Tgstation.Server.Client/MethodNotSupportedException.cs b/src/Tgstation.Server.Client/MethodNotSupportedException.cs index 88774c4053..f19dcdab0e 100644 --- a/src/Tgstation.Server.Client/MethodNotSupportedException.cs +++ b/src/Tgstation.Server.Client/MethodNotSupportedException.cs @@ -1,6 +1,6 @@ using System; using System.Net.Http; -using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client { @@ -12,9 +12,9 @@ namespace Tgstation.Server.Client /// /// Initialize a new instance of the . /// - /// The for the . + /// The for the . /// The for the . - public MethodNotSupportedException(ErrorMessage? errorMessage, HttpResponseMessage responseMessage) : base(errorMessage, responseMessage) + public MethodNotSupportedException(ErrorMessageResponse? errorMessage, HttpResponseMessage responseMessage) : base(errorMessage, responseMessage) { } /// diff --git a/src/Tgstation.Server.Client/PaginatedClient.cs b/src/Tgstation.Server.Client/PaginatedClient.cs index 970129e0ec..df8f023d36 100644 --- a/src/Tgstation.Server.Client/PaginatedClient.cs +++ b/src/Tgstation.Server.Client/PaginatedClient.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client { @@ -72,12 +73,12 @@ namespace Tgstation.Server.Client } } - Task> GetPage() => instanceId.HasValue - ? ApiClient.Read>( + Task> GetPage() => instanceId.HasValue + ? ApiClient.Read>( String.Format(CultureInfo.InvariantCulture, routeFormatter, currentPage), instanceId.Value, cancellationToken) - : ApiClient.Read>( + : ApiClient.Read>( String.Format(CultureInfo.InvariantCulture, routeFormatter, currentPage), cancellationToken); diff --git a/src/Tgstation.Server.Client/RateLimitException.cs b/src/Tgstation.Server.Client/RateLimitException.cs index acef0d769e..39df1df3ad 100644 --- a/src/Tgstation.Server.Client/RateLimitException.cs +++ b/src/Tgstation.Server.Client/RateLimitException.cs @@ -2,7 +2,7 @@ using Microsoft.Net.Http.Headers; using System; using System.Linq; using System.Net.Http; -using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client { @@ -19,9 +19,9 @@ namespace Tgstation.Server.Client /// /// Initialize a new instance of the . /// - /// The for the . + /// The for the . /// The for the . - public RateLimitException(ErrorMessage? errorMessage, HttpResponseMessage responseMessage) : base(errorMessage, responseMessage) + public RateLimitException(ErrorMessageResponse? errorMessage, HttpResponseMessage responseMessage) : base(errorMessage, responseMessage) { if (responseMessage == null) throw new ArgumentNullException(nameof(responseMessage)); diff --git a/src/Tgstation.Server.Client/ServerClient.cs b/src/Tgstation.Server.Client/ServerClient.cs index e1b8756558..283519241f 100644 --- a/src/Tgstation.Server.Client/ServerClient.cs +++ b/src/Tgstation.Server.Client/ServerClient.cs @@ -2,7 +2,7 @@ using System; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; -using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client { @@ -13,7 +13,7 @@ namespace Tgstation.Server.Client public Uri Url => apiClient.Url; /// - public Token Token + public TokenResponse Token { get => token; set @@ -50,14 +50,14 @@ namespace Tgstation.Server.Client /// /// Backing field for /// - Token token; + TokenResponse token; /// /// Construct a /// /// The value of /// The value of - public ServerClient(IApiClient apiClient, Token token) + public ServerClient(IApiClient apiClient, TokenResponse token) { this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient)); this.token = token ?? throw new ArgumentNullException(nameof(token)); @@ -75,7 +75,7 @@ namespace Tgstation.Server.Client public void Dispose() => apiClient.Dispose(); /// - public Task ServerInformation(CancellationToken cancellationToken) => apiClient.Read(Routes.Root, cancellationToken); + public Task ServerInformation(CancellationToken cancellationToken) => apiClient.Read(Routes.Root, cancellationToken); /// public void AddRequestLogger(IRequestLogger requestLogger) => apiClient.AddRequestLogger(requestLogger); diff --git a/src/Tgstation.Server.Client/ServerClientFactory.cs b/src/Tgstation.Server.Client/ServerClientFactory.cs index 5dd86c3189..4cd28528fa 100644 --- a/src/Tgstation.Server.Client/ServerClientFactory.cs +++ b/src/Tgstation.Server.Client/ServerClientFactory.cs @@ -5,7 +5,7 @@ using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; -using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client { @@ -50,7 +50,7 @@ namespace Tgstation.Server.Client requestLoggers ??= Enumerable.Empty(); - Token token; + TokenResponse token; var loginHeaders = new ApiHeaders(productHeaderValue, username, password); using (var api = ApiClientFactory.CreateApiClient(host, loginHeaders, null)) { @@ -59,7 +59,7 @@ namespace Tgstation.Server.Client if (timeout.HasValue) api.Timeout = timeout.Value; - token = await api.Update(Routes.Root, cancellationToken).ConfigureAwait(false); + token = await api.Update(Routes.Root, cancellationToken).ConfigureAwait(false); } if (!attemptLoginRefresh) @@ -78,7 +78,7 @@ namespace Tgstation.Server.Client } /// - public IServerClient CreateFromToken(Uri host, Token token) + public IServerClient CreateFromToken(Uri host, TokenResponse token) { if (host == null) throw new ArgumentNullException(nameof(host)); diff --git a/src/Tgstation.Server.Client/ServerErrorException.cs b/src/Tgstation.Server.Client/ServerErrorException.cs index c2feecdeaa..ea6bb98357 100644 --- a/src/Tgstation.Server.Client/ServerErrorException.cs +++ b/src/Tgstation.Server.Client/ServerErrorException.cs @@ -1,6 +1,6 @@ using System; using System.Net.Http; -using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client { @@ -17,9 +17,9 @@ namespace Tgstation.Server.Client /// /// Construct an with a given /// - /// The for the + /// The for the /// The for the - public ServerErrorException(ErrorMessage? errorMessage, HttpResponseMessage responseMessage) : base(errorMessage, responseMessage) + public ServerErrorException(ErrorMessageResponse? errorMessage, HttpResponseMessage responseMessage) : base(errorMessage, responseMessage) { } diff --git a/src/Tgstation.Server.Client/UnauthorizedException.cs b/src/Tgstation.Server.Client/UnauthorizedException.cs index 07228d299b..8b1356f1c5 100644 --- a/src/Tgstation.Server.Client/UnauthorizedException.cs +++ b/src/Tgstation.Server.Client/UnauthorizedException.cs @@ -1,6 +1,6 @@ using System; using System.Net.Http; -using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client { @@ -12,9 +12,9 @@ namespace Tgstation.Server.Client /// /// Construct an /// - /// The returned by the API. + /// The returned by the API. /// The . - public UnauthorizedException(ErrorMessage? errorMessage, HttpResponseMessage responseMessage) : base(errorMessage, responseMessage) + public UnauthorizedException(ErrorMessageResponse? errorMessage, HttpResponseMessage responseMessage) : base(errorMessage, responseMessage) { } /// diff --git a/src/Tgstation.Server.Client/UserGroupsClient.cs b/src/Tgstation.Server.Client/UserGroupsClient.cs index dadb68c7b4..85c9d799ed 100644 --- a/src/Tgstation.Server.Client/UserGroupsClient.cs +++ b/src/Tgstation.Server.Client/UserGroupsClient.cs @@ -4,6 +4,8 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client { @@ -20,17 +22,17 @@ namespace Tgstation.Server.Client } /// - public Task Create(UserGroup group, CancellationToken cancellationToken) => ApiClient.Create(Routes.UserGroup, group ?? throw new ArgumentNullException(nameof(group)), cancellationToken); + public Task Create(UserGroupCreateRequest group, CancellationToken cancellationToken) => ApiClient.Create(Routes.UserGroup, group ?? throw new ArgumentNullException(nameof(group)), cancellationToken); /// - public Task GetId(EntityId group, CancellationToken cancellationToken) => ApiClient.Read(Routes.SetID(Routes.UserGroup, group?.Id ?? throw new ArgumentNullException(nameof(group))), cancellationToken); + public Task GetId(EntityId group, CancellationToken cancellationToken) => ApiClient.Read(Routes.SetID(Routes.UserGroup, group?.Id ?? throw new ArgumentNullException(nameof(group))), cancellationToken); /// - public Task> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken) - => ReadPaged(paginationSettings, Routes.ListRoute(Routes.UserGroup), null, cancellationToken); + public Task> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken) + => ReadPaged(paginationSettings, Routes.ListRoute(Routes.UserGroup), null, cancellationToken); /// - public Task Update(UserGroup group, CancellationToken cancellationToken) => ApiClient.Update(Routes.UserGroup, group ?? throw new ArgumentNullException(nameof(group)), cancellationToken); + public Task Update(UserGroupUpdateRequest group, CancellationToken cancellationToken) => ApiClient.Update(Routes.UserGroup, group ?? throw new ArgumentNullException(nameof(group)), cancellationToken); /// public Task Delete(EntityId group, CancellationToken cancellationToken) => ApiClient.Delete(Routes.SetID(Routes.UserGroup, group?.Id ?? throw new ArgumentNullException(nameof(group))), cancellationToken); diff --git a/src/Tgstation.Server.Client/UsersClient.cs b/src/Tgstation.Server.Client/UsersClient.cs index a8b9168cf9..def6266506 100644 --- a/src/Tgstation.Server.Client/UsersClient.cs +++ b/src/Tgstation.Server.Client/UsersClient.cs @@ -4,6 +4,8 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client { @@ -20,19 +22,19 @@ namespace Tgstation.Server.Client } /// - public Task Create(UserUpdate user, CancellationToken cancellationToken) => ApiClient.Create(Routes.User, user ?? throw new ArgumentNullException(nameof(user)), cancellationToken); + public Task Create(UserCreateRequest user, CancellationToken cancellationToken) => ApiClient.Create(Routes.User, user ?? throw new ArgumentNullException(nameof(user)), cancellationToken); /// - public Task GetId(Api.Models.Internal.UserBase user, CancellationToken cancellationToken) => ApiClient.Read(Routes.SetID(Routes.User, user?.Id ?? throw new ArgumentNullException(nameof(user))), cancellationToken); + public Task GetId(EntityId user, CancellationToken cancellationToken) => ApiClient.Read(Routes.SetID(Routes.User, user?.Id ?? throw new ArgumentNullException(nameof(user))), cancellationToken); /// - public Task> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken) - => ReadPaged(paginationSettings, Routes.ListRoute(Routes.User), null, cancellationToken); + public Task> List(PaginationSettings? paginationSettings, CancellationToken cancellationToken) + => ReadPaged(paginationSettings, Routes.ListRoute(Routes.User), null, cancellationToken); /// - public Task Read(CancellationToken cancellationToken) => ApiClient.Read(Routes.User, cancellationToken); + public Task Read(CancellationToken cancellationToken) => ApiClient.Read(Routes.User, cancellationToken); /// - public Task Update(UserUpdate user, CancellationToken cancellationToken) => ApiClient.Update(Routes.User, user ?? throw new ArgumentNullException(nameof(user)), cancellationToken); + public Task Update(UserUpdateRequest user, CancellationToken cancellationToken) => ApiClient.Update(Routes.User, user ?? throw new ArgumentNullException(nameof(user)), cancellationToken); } } diff --git a/src/Tgstation.Server.Client/VersionMismatchException.cs b/src/Tgstation.Server.Client/VersionMismatchException.cs index b1ea09bb3f..9fe74c47ee 100644 --- a/src/Tgstation.Server.Client/VersionMismatchException.cs +++ b/src/Tgstation.Server.Client/VersionMismatchException.cs @@ -1,6 +1,6 @@ -using System; +using System; using System.Net.Http; -using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client { @@ -12,9 +12,9 @@ namespace Tgstation.Server.Client /// /// Initialize a new instance of the . /// - /// The for the . + /// The for the . /// The for the . - public VersionMismatchException(ErrorMessage? errorMessage, HttpResponseMessage responseMessage) : base(errorMessage, responseMessage) + public VersionMismatchException(ErrorMessageResponse? errorMessage, HttpResponseMessage responseMessage) : base(errorMessage, responseMessage) { } @@ -36,4 +36,4 @@ namespace Tgstation.Server.Client /// The inner for the base public VersionMismatchException(string message, Exception innerException) : base(message, innerException) { } } -} \ No newline at end of file +} diff --git a/src/Tgstation.Server.Host/Components/Chat/ChatManager.cs b/src/Tgstation.Server.Host/Components/Chat/ChatManager.cs index dac908b48b..b5f1773526 100644 --- a/src/Tgstation.Server.Host/Components/Chat/ChatManager.cs +++ b/src/Tgstation.Server.Host/Components/Chat/ChatManager.cs @@ -67,7 +67,7 @@ namespace Tgstation.Server.Host.Components.Chat readonly IDictionary builtinCommands; /// - /// Map of s in use, keyed by + /// Map of s in use, keyed by /// readonly IDictionary providers; @@ -117,7 +117,7 @@ namespace Tgstation.Server.Host.Components.Chat Task messageSendTask; /// - /// The that completes when s change + /// The that completes when s change /// TaskCompletionSource connectionsUpdated; @@ -570,9 +570,9 @@ namespace Tgstation.Server.Host.Components.Chat lock (providers) { // raw settings changes forces a rebuild of the provider - if (providers.TryGetValue(newSettings.Id, out provider)) + if (providers.TryGetValue(newSettings.Id.Value, out provider)) { - providers.Remove(newSettings.Id); + providers.Remove(newSettings.Id.Value); disconnectTask = DisconnectProvider(provider); } else @@ -580,7 +580,7 @@ namespace Tgstation.Server.Host.Components.Chat if (newSettings.Enabled.Value) { provider = providerFactory.CreateProvider(newSettings); - providers.Add(newSettings.Id, provider); + providers.Add(newSettings.Id.Value, provider); } } @@ -748,7 +748,7 @@ namespace Tgstation.Server.Host.Components.Chat builtinCommands.Add(I.Name.ToUpperInvariant(), I); var initialChatBots = activeChatBots.ToList(); await Task.WhenAll(initialChatBots.Select(x => ChangeSettings(x, cancellationToken))).ConfigureAwait(false); - await Task.WhenAll(initialChatBots.Select(x => ChangeChannels(x.Id, x.Channels, cancellationToken))).ConfigureAwait(false); + await Task.WhenAll(initialChatBots.Select(x => ChangeChannels(x.Id.Value, x.Channels, cancellationToken))).ConfigureAwait(false); initialProviderConnectionsTask = InitialConnection(); chatHandler = MonitorMessages(handlerCts.Token); } diff --git a/src/Tgstation.Server.Host/Components/Chat/IChatManager.cs b/src/Tgstation.Server.Host/Components/Chat/IChatManager.cs index 6a72c0e09d..5bd0117ab1 100644 --- a/src/Tgstation.Server.Host/Components/Chat/IChatManager.cs +++ b/src/Tgstation.Server.Host/Components/Chat/IChatManager.cs @@ -23,7 +23,7 @@ namespace Tgstation.Server.Host.Components.Chat /// /// The new /// The for the operation - /// A representing the running operation. Will complete immediately if the property of is + /// A representing the running operation. Will complete immediately if the property of is Task ChangeSettings(Models.ChatBot newSettings, CancellationToken cancellationToken); /// diff --git a/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs b/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs index 4b60ad50fb..1817808d27 100644 --- a/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs +++ b/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs @@ -80,7 +80,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers IJobManager jobManager, IAssemblyInformationProvider assemblyInformationProvider, ILogger logger, - Models.ChatBot chatBot) + ChatBot chatBot) : base(jobManager, logger, chatBot) { this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider)); @@ -371,21 +371,23 @@ namespace Tgstation.Server.Host.Components.Chat.Providers } } - /// - public override async Task> SendUpdateMessage( + /// + /// Create a of s for a discord update embed. + /// + /// The of the deployment. + /// The BYOND of the deployment. + /// The repository GitHub owner, if any. + /// The repository GitHub name, if any. + /// if the local deployment commit was pushed to the remote repository. + /// A new of s to use. + static List BuildUpdateEmbedFields( Models.RevisionInformation revisionInformation, Version byondVersion, - DateTimeOffset? estimatedCompletionTime, string gitHubOwner, string gitHubRepo, - ulong channelId, - bool localCommitPushed, - CancellationToken cancellationToken) + bool localCommitPushed) { bool gitHub = gitHubOwner != null && gitHubRepo != null; - - localCommitPushed |= revisionInformation.CommitSha == revisionInformation.OriginCommitSha; - var fields = new List { new EmbedFieldBuilder @@ -420,6 +422,23 @@ namespace Tgstation.Server.Host.Components.Chat.Providers Value = $"[{x.TitleAtMerge}]({x.Url}) by _[@{x.Author}](https://github.com/{x.Author})_{Environment.NewLine}Commit: [{x.TargetCommitSha.Substring(0, 7)}](https://github.com/{gitHubOwner}/{gitHubRepo}/commit/{x.TargetCommitSha}){(String.IsNullOrWhiteSpace(x.Comment) ? String.Empty : $"{Environment.NewLine}_**{x.Comment}**_")}" })); + return fields; + } + + /// + public override async Task> SendUpdateMessage( + Models.RevisionInformation revisionInformation, + Version byondVersion, + DateTimeOffset? estimatedCompletionTime, + string gitHubOwner, + string gitHubRepo, + ulong channelId, + bool localCommitPushed, + CancellationToken cancellationToken) + { + localCommitPushed |= revisionInformation.CommitSha == revisionInformation.OriginCommitSha; + + var fields = BuildUpdateEmbedFields(revisionInformation, byondVersion, gitHubOwner, gitHubRepo, localCommitPushed); var builder = new EmbedBuilder { Author = new EmbedAuthorBuilder diff --git a/src/Tgstation.Server.Host/Components/Deployment/DmbFactory.cs b/src/Tgstation.Server.Host/Components/Deployment/DmbFactory.cs index 874de6b9a6..8dbf155b5f 100644 --- a/src/Tgstation.Server.Host/Components/Deployment/DmbFactory.cs +++ b/src/Tgstation.Server.Host/Components/Deployment/DmbFactory.cs @@ -136,15 +136,15 @@ namespace Tgstation.Server.Host.Components.Deployment } lock (jobLockCounts) - if (!jobLockCounts.TryGetValue(job.Id, out var currentVal) || currentVal == 1) + if (!jobLockCounts.TryGetValue(job.Id.Value, out var currentVal) || currentVal == 1) { - jobLockCounts.Remove(job.Id); + jobLockCounts.Remove(job.Id.Value); logger.LogDebug("Cleaning lock-free compile job {0} => {1}", job.Id, job.DirectoryName); cleanupTask = HandleCleanup(); } else { - var decremented = --jobLockCounts[job.Id]; + var decremented = --jobLockCounts[job.Id.Value]; logger.LogTrace("Compile job {0} lock count now: {1}", job.Id, decremented); } } @@ -193,7 +193,7 @@ namespace Tgstation.Server.Host.Components.Deployment lock (jobLockCounts) { var jobId = nextDmbProvider.CompileJob.Id; - var incremented = jobLockCounts[jobId] += lockCount; + var incremented = jobLockCounts[jobId.Value] += lockCount; logger.LogTrace("Compile job {0} lock count now: {1}", jobId, incremented); return nextDmbProvider; } @@ -324,13 +324,13 @@ namespace Tgstation.Server.Host.Components.Deployment lock (jobLockCounts) { - if (!jobLockCounts.TryGetValue(compileJob.Id, out int value)) + if (!jobLockCounts.TryGetValue(compileJob.Id.Value, out int value)) { value = 1; - jobLockCounts.Add(compileJob.Id, 1); + jobLockCounts.Add(compileJob.Id.Value, 1); } else - jobLockCounts[compileJob.Id] = ++value; + jobLockCounts[compileJob.Id.Value] = ++value; providerSubmitted = true; @@ -366,7 +366,7 @@ namespace Tgstation.Server.Host.Components.Deployment .AsQueryable() .Where( x => x.Job.Instance.Id == metadata.Id - && jobIdsToSkip.Contains(x.Id)) + && jobIdsToSkip.Contains(x.Id.Value)) .Select(x => x.DirectoryName.Value) .ToListAsync(cancellationToken) .ConfigureAwait(false)) diff --git a/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs b/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs index 04b80d802c..a567614202 100644 --- a/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs +++ b/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs @@ -400,7 +400,7 @@ namespace Tgstation.Server.Host.Components.Deployment /// Executes and populate a given /// /// The to run and populate - /// The settings to use + /// The to use /// The to use /// The to use /// The to use. @@ -409,7 +409,7 @@ namespace Tgstation.Server.Host.Components.Deployment /// A representing the running operation async Task RunCompileJob( Models.CompileJob job, - Api.Models.DreamMaker dreamMakerSettings, + Api.Models.Internal.DreamMakerSettings dreamMakerSettings, IByondExecutableLock byondLock, IRepository repository, IRemoteDeploymentManager remoteDeploymentManager, @@ -549,7 +549,7 @@ namespace Tgstation.Server.Host.Components.Deployment TimeSpan? averageSpan = null; Models.RepositorySettings repositorySettings = null; Models.DreamDaemonSettings ddSettings = null; - DreamMakerSettings dreamMakerSettings = null; + Models.DreamMakerSettings dreamMakerSettings = null; IRepository repo = null; IRemoteDeploymentManager remoteDeploymentManager = null; Models.RevisionInformation revInfo = null; @@ -787,7 +787,7 @@ namespace Tgstation.Server.Host.Components.Deployment async Task Compile( Models.RevisionInformation revisionInformation, - Api.Models.DreamMaker dreamMakerSettings, + Api.Models.Internal.DreamMakerSettings dreamMakerSettings, uint apiValidateTimeout, IRepository repository, IRemoteDeploymentManager remoteDeploymentManager, diff --git a/src/Tgstation.Server.Host/Components/Deployment/Remote/IRemoteDeploymentManagerFactory.cs b/src/Tgstation.Server.Host/Components/Deployment/Remote/IRemoteDeploymentManagerFactory.cs index 9ab84ef261..daa6c5a95c 100644 --- a/src/Tgstation.Server.Host/Components/Deployment/Remote/IRemoteDeploymentManagerFactory.cs +++ b/src/Tgstation.Server.Host/Components/Deployment/Remote/IRemoteDeploymentManagerFactory.cs @@ -19,7 +19,7 @@ namespace Tgstation.Server.Host.Components.Deployment.Remote /// Create a for a given . /// /// Current metadata. - /// The containing the to use. + /// The containing the to use. /// A new . IRemoteDeploymentManager CreateRemoteDeploymentManager(Api.Models.Instance metadata, Models.CompileJob compileJob); } diff --git a/src/Tgstation.Server.Host/Components/IInstanceManager.cs b/src/Tgstation.Server.Host/Components/IInstanceManager.cs index 1731be40cf..d4922d783c 100644 --- a/src/Tgstation.Server.Host/Components/IInstanceManager.cs +++ b/src/Tgstation.Server.Host/Components/IInstanceManager.cs @@ -1,4 +1,4 @@ -using System.Threading; +using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Host.Components.Interop.Bridge; using Tgstation.Server.Host.Models; @@ -20,7 +20,7 @@ namespace Tgstation.Server.Host.Components /// /// The of the desired /// The associated with the given if it is online, otherwise. - IInstanceReference GetInstanceReference(Models.Instance metadata); + IInstanceReference GetInstanceReference(Api.Models.Instance metadata); /// /// Online an diff --git a/src/Tgstation.Server.Host/Components/IRenameNotifyee.cs b/src/Tgstation.Server.Host/Components/IRenameNotifyee.cs index d40afc0023..c6de7b32f1 100644 --- a/src/Tgstation.Server.Host/Components/IRenameNotifyee.cs +++ b/src/Tgstation.Server.Host/Components/IRenameNotifyee.cs @@ -1,4 +1,4 @@ -using System.Threading; +using System.Threading; using System.Threading.Tasks; namespace Tgstation.Server.Host.Components @@ -11,7 +11,7 @@ namespace Tgstation.Server.Host.Components /// /// Called when the owning is renamed. /// - /// The new . + /// The new . /// The for the operation. /// A representing the running operation. Task InstanceRenamed(string newInstanceName, CancellationToken cancellationToken); diff --git a/src/Tgstation.Server.Host/Components/InstanceFactory.cs b/src/Tgstation.Server.Host/Components/InstanceFactory.cs index cd04474fb4..e2c0a5afda 100644 --- a/src/Tgstation.Server.Host/Components/InstanceFactory.cs +++ b/src/Tgstation.Server.Host/Components/InstanceFactory.cs @@ -273,14 +273,14 @@ namespace Tgstation.Server.Host.Components serverPortProvider, loggerFactory, loggerFactory.CreateLogger(), - metadata.CloneMetadata()); + metadata); var dmbFactory = new DmbFactory( databaseContextFactory, gameIoManager, remoteDeploymentManagerFactory, loggerFactory.CreateLogger(), - metadata.CloneMetadata()); + metadata); try { var reattachInfoHandler = new SessionPersistor( @@ -288,7 +288,7 @@ namespace Tgstation.Server.Host.Components dmbFactory, processExecutor, loggerFactory.CreateLogger(), - metadata.CloneMetadata()); + metadata); var watchdog = watchdogFactory.CreateWatchdog( chatManager, dmbFactory, @@ -298,7 +298,7 @@ namespace Tgstation.Server.Host.Components diagnosticsIOManager, eventConsumer, remoteDeploymentManagerFactory, - metadata.CloneMetadata(), + metadata, metadata.DreamDaemonSettings); eventConsumer.SetWatchdog(watchdog); commandFactory.SetWatchdog(watchdog); @@ -317,10 +317,10 @@ namespace Tgstation.Server.Host.Components repoManager, remoteDeploymentManagerFactory, loggerFactory.CreateLogger(), - metadata.CloneMetadata()); + metadata); instance = new Instance( - metadata.CloneMetadata(), + metadata, repoManager, byond, dreamMaker, diff --git a/src/Tgstation.Server.Host/Components/InstanceManager.cs b/src/Tgstation.Server.Host/Components/InstanceManager.cs index f4526b2e09..dd2455d3c9 100644 --- a/src/Tgstation.Server.Host/Components/InstanceManager.cs +++ b/src/Tgstation.Server.Host/Components/InstanceManager.cs @@ -194,14 +194,14 @@ namespace Tgstation.Server.Host.Components } /// - public IInstanceReference GetInstanceReference(Models.Instance metadata) + public IInstanceReference GetInstanceReference(Api.Models.Instance metadata) { if (metadata == null) throw new ArgumentNullException(nameof(metadata)); lock (instances) { - if (!instances.TryGetValue(metadata.Id, out var instance)) + if (!instances.TryGetValue(metadata.Id.Value, out var instance)) { logger.LogTrace("Cannot reference instance {0} as it is not online or on this node!", metadata.Id); return null; @@ -289,13 +289,13 @@ namespace Tgstation.Server.Host.Components InstanceContainer container; lock (instances) { - if (!instances.TryGetValue(metadata.Id, out container)) + if (!instances.TryGetValue(metadata.Id.Value, out container)) { logger.LogDebug("Not offlining removed instance {0}", metadata.Id); return; } - instances.Remove(metadata.Id); + instances.Remove(metadata.Id.Value); } try @@ -339,7 +339,7 @@ namespace Tgstation.Server.Host.Components using var lockContext = await SemaphoreSlimContext.Lock(instanceStateChangeSemaphore, cancellationToken).ConfigureAwait(false); lock (instances) - if (instances.ContainsKey(metadata.Id)) + if (instances.ContainsKey(metadata.Id.Value)) { logger.LogDebug("Aborting instance creation due to it seemingly already being online"); return; @@ -354,7 +354,7 @@ namespace Tgstation.Server.Host.Components try { lock (instances) - instances.Add(metadata.Id, new InstanceContainer(instance)); + instances.Add(metadata.Id.Value, new InstanceContainer(instance)); } catch (Exception ex) { @@ -543,7 +543,7 @@ namespace Tgstation.Server.Host.Components { lock (instances) { - instances.TryGetValue(metadata.Id, out var container); + instances.TryGetValue(metadata.Id.Value, out var container); return container?.Instance; } } diff --git a/src/Tgstation.Server.Host/Components/Interop/Bridge/RuntimeInformation.cs b/src/Tgstation.Server.Host/Components/Interop/Bridge/RuntimeInformation.cs index a54398a512..ffa2f09022 100644 --- a/src/Tgstation.Server.Host/Components/Interop/Bridge/RuntimeInformation.cs +++ b/src/Tgstation.Server.Host/Components/Interop/Bridge/RuntimeInformation.cs @@ -29,7 +29,7 @@ namespace Tgstation.Server.Host.Components.Interop.Bridge public bool ApiValidateOnly { get; } /// - /// The of the owner at the time of launch + /// The of the owner at the time of launch /// public string InstanceName { get; set; } diff --git a/src/Tgstation.Server.Host/Components/Interop/Bridge/TestMergeInformation.cs b/src/Tgstation.Server.Host/Components/Interop/Bridge/TestMergeInformation.cs index d3dc664503..4ec7088ba2 100644 --- a/src/Tgstation.Server.Host/Components/Interop/Bridge/TestMergeInformation.cs +++ b/src/Tgstation.Server.Host/Components/Interop/Bridge/TestMergeInformation.cs @@ -7,7 +7,7 @@ namespace Tgstation.Server.Host.Components.Interop.Bridge /// /// This model mirrors /datum/tgs_revision_information/test_merge /// - public sealed class TestMergeInformation : TestMergeBase + public sealed class TestMergeInformation : TestMergeModelBase { /// /// The unix time of when the test merge was applied diff --git a/src/Tgstation.Server.Host/Components/Interop/Topic/TopicParameters.cs b/src/Tgstation.Server.Host/Components/Interop/Topic/TopicParameters.cs index cab487c410..4752434fb2 100644 --- a/src/Tgstation.Server.Host/Components/Interop/Topic/TopicParameters.cs +++ b/src/Tgstation.Server.Host/Components/Interop/Topic/TopicParameters.cs @@ -1,4 +1,4 @@ -using System; +using System; using Tgstation.Server.Host.Components.Session; namespace Tgstation.Server.Host.Components.Interop.Topic @@ -35,7 +35,7 @@ namespace Tgstation.Server.Host.Components.Interop.Topic public RebootState? NewRebootState { get; } /// - /// The new for requests. + /// The new for requests. /// public string NewInstanceName { get; } diff --git a/src/Tgstation.Server.Host/Components/Session/SessionPersistor.cs b/src/Tgstation.Server.Host/Components/Session/SessionPersistor.cs index a0513fad10..0a96bef1e3 100644 --- a/src/Tgstation.Server.Host/Components/Session/SessionPersistor.cs +++ b/src/Tgstation.Server.Host/Components/Session/SessionPersistor.cs @@ -79,7 +79,7 @@ namespace Tgstation.Server.Host.Components.Session var dbReattachInfo = new Models.ReattachInformation { AccessIdentifier = reattachInformation.AccessIdentifier, - CompileJobId = reattachInformation.Dmb.CompileJob.Id, + CompileJobId = reattachInformation.Dmb.CompileJob.Id.Value, Port = reattachInformation.Port, ProcessId = reattachInformation.ProcessId, RebootState = reattachInformation.RebootState, diff --git a/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs b/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs index 427636e6b3..6c03ceae3b 100644 --- a/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs +++ b/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs @@ -9,6 +9,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Host.Components.Events; using Tgstation.Server.Host.Core; using Tgstation.Server.Host.IO; @@ -214,7 +215,7 @@ namespace Tgstation.Server.Host.Components.StaticFiles } /// - public async Task> ListDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken) + public async Task> ListDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken) { await EnsureDirectories(cancellationToken).ConfigureAwait(false); var path = ValidateConfigRelativePath(configurationRelativePath); @@ -222,14 +223,14 @@ namespace Tgstation.Server.Host.Components.StaticFiles if (configurationRelativePath == null) configurationRelativePath = "/"; - List result = new List(); + List result = new List(); void ListImpl() { var enumerator = synchronousIOManager.GetDirectories(path, cancellationToken); try { - result.AddRange(enumerator.Select(x => new ConfigurationFile + result.AddRange(enumerator.Select(x => new ConfigurationFileResponse { IsDirectory = true, Path = ioManager.ConcatPath(configurationRelativePath, x), @@ -243,7 +244,7 @@ namespace Tgstation.Server.Host.Components.StaticFiles } enumerator = synchronousIOManager.GetFiles(path, cancellationToken); - result.AddRange(enumerator.Select(x => new ConfigurationFile + result.AddRange(enumerator.Select(x => new ConfigurationFileResponse { IsDirectory = false, Path = ioManager.ConcatPath(configurationRelativePath, x), @@ -260,12 +261,12 @@ namespace Tgstation.Server.Host.Components.StaticFiles } /// - public async Task Read(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken) + public async Task Read(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken) { await EnsureDirectories(cancellationToken).ConfigureAwait(false); var path = ValidateConfigRelativePath(configurationRelativePath); - ConfigurationFile result = null; + ConfigurationFileResponse result = null; void ReadImpl() { @@ -316,7 +317,7 @@ namespace Tgstation.Server.Host.Components.StaticFiles path, false)); - result = new ConfigurationFile + result = new ConfigurationFileResponse { FileTicket = fileTicket.FileTicket, IsDirectory = false, @@ -333,19 +334,20 @@ namespace Tgstation.Server.Host.Components.StaticFiles { isDirectory = synchronousIOManager.IsDirectory(path); } - catch + catch(Exception ex) { + logger.LogDebug(ex, "IsDirectory exception!"); isDirectory = false; } - result = new ConfigurationFile + result = new ConfigurationFileResponse { Path = configurationRelativePath }; if (!isDirectory) result.AccessDenied = true; - else - result.IsDirectory = true; + + result.IsDirectory = isDirectory; } } @@ -429,12 +431,12 @@ namespace Tgstation.Server.Host.Components.StaticFiles } /// - public async Task Write(string configurationRelativePath, ISystemIdentity systemIdentity, string previousHash, CancellationToken cancellationToken) + public async Task Write(string configurationRelativePath, ISystemIdentity systemIdentity, string previousHash, CancellationToken cancellationToken) { await EnsureDirectories(cancellationToken).ConfigureAwait(false); var path = ValidateConfigRelativePath(configurationRelativePath); - ConfigurationFile result = null; + ConfigurationFileResponse result = null; void WriteImpl() { @@ -462,7 +464,7 @@ namespace Tgstation.Server.Host.Components.StaticFiles await systemIdentity.RunImpersonated(WriteCallback, cancellationToken).ConfigureAwait(false); if (!success) - fileTicket.SetErrorMessage(new ErrorMessage(ErrorCode.ConfigurationFileUpdated) + fileTicket.SetErrorMessage(new ErrorMessageResponse(ErrorCode.ConfigurationFileUpdated) { AdditionalData = fileHash }); @@ -471,7 +473,7 @@ namespace Tgstation.Server.Host.Components.StaticFiles } } - result = new ConfigurationFile + result = new ConfigurationFileResponse { FileTicket = fileTicket.Ticket.FileTicket, LastReadHash = previousHash, @@ -491,19 +493,20 @@ namespace Tgstation.Server.Host.Components.StaticFiles { isDirectory = synchronousIOManager.IsDirectory(path); } - catch + catch(Exception ex) { + logger.LogDebug(ex, "IsDirectory exception!"); isDirectory = false; } - result = new ConfigurationFile + result = new ConfigurationFileResponse { Path = configurationRelativePath }; if (!isDirectory) result.AccessDenied = true; - else - result.IsDirectory = true; + + result.IsDirectory = isDirectory; } } diff --git a/src/Tgstation.Server.Host/Components/StaticFiles/IConfiguration.cs b/src/Tgstation.Server.Host/Components/StaticFiles/IConfiguration.cs index 34a42d299e..59439fae24 100644 --- a/src/Tgstation.Server.Host/Components/StaticFiles/IConfiguration.cs +++ b/src/Tgstation.Server.Host/Components/StaticFiles/IConfiguration.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Host.Components.Events; using Tgstation.Server.Host.Security; @@ -32,13 +33,13 @@ namespace Tgstation.Server.Host.Components.StaticFiles Task SymlinkStaticFilesTo(string destination, CancellationToken cancellationToken); /// - /// Get for all items in a given + /// Get s for all items in a given /// /// The relative path in the Configuration directory /// The for the operation. If , the operation will be performed as the user of the /// The for the operation - /// A resulting in the s for the items in the directory. and will both be - Task> ListDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken); + /// A resulting in the s for the items in the directory. and will both be + Task> ListDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken); /// /// Reads a given @@ -46,8 +47,8 @@ namespace Tgstation.Server.Host.Components.StaticFiles /// The relative path in the Configuration directory /// The for the operation. If , the operation will be performed as the user of the /// The for the operation - /// A resulting in the of the file - Task Read(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken); + /// A resulting in the of the file + Task Read(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken); /// /// Create an empty directory at @@ -74,7 +75,7 @@ namespace Tgstation.Server.Host.Components.StaticFiles /// The for the operation. If , the operation will be performed as the user of the /// The hash any existing file must match in order for the write to succeed /// The for the operation. Usage may result in partial writes - /// A resulting in the updated or if the write failed due to conflicts - Task Write(string configurationRelativePath, ISystemIdentity systemIdentity, string previousHash, CancellationToken cancellationToken); + /// A resulting in the updated or if the write failed due to conflicts + Task Write(string configurationRelativePath, ISystemIdentity systemIdentity, string previousHash, CancellationToken cancellationToken); } } diff --git a/src/Tgstation.Server.Host/Configuration/GeneralConfiguration.cs b/src/Tgstation.Server.Host/Configuration/GeneralConfiguration.cs index 766e4d4cd4..c6ea672e48 100644 --- a/src/Tgstation.Server.Host/Configuration/GeneralConfiguration.cs +++ b/src/Tgstation.Server.Host/Configuration/GeneralConfiguration.cs @@ -11,7 +11,7 @@ namespace Tgstation.Server.Host.Configuration /// /// General configuration options /// - public sealed class GeneralConfiguration : ServerInformation + public sealed class GeneralConfiguration : ServerInformationBase { /// /// The key for the the resides in @@ -29,22 +29,22 @@ namespace Tgstation.Server.Host.Configuration public static readonly Version CurrentConfigVersion = Version.Parse(MasterVersionsAttribute.Instance.RawConfigurationVersion); /// - /// The default value for . + /// The default value for . /// const uint DefaultMinimumPasswordLength = 15; /// - /// The default value for . + /// The default value for . /// const uint DefaultInstanceLimit = 10; /// - /// The default value for . + /// The default value for . /// const uint DefaultUserLimit = 100; /// - /// The default value for . + /// The default value for . /// const uint DefaultUserGroupLimit = 25; diff --git a/src/Tgstation.Server.Host/Configuration/SecurityConfiguration.cs b/src/Tgstation.Server.Host/Configuration/SecurityConfiguration.cs index 1957a3b56d..9f6c598952 100644 --- a/src/Tgstation.Server.Host/Configuration/SecurityConfiguration.cs +++ b/src/Tgstation.Server.Host/Configuration/SecurityConfiguration.cs @@ -34,12 +34,12 @@ namespace Tgstation.Server.Host.Configuration const uint DefaultTokenSigningKeyByteAmount = 256; /// - /// Amount of minutes until s generated from passwords expire. + /// Amount of minutes until s generated from passwords expire. /// public uint TokenExpiryMinutes { get; set; } = DefaultTokenExpiryMinutes; /// - /// Amount of minutes to skew the clock for validation. + /// Amount of minutes to skew the clock for validation. /// public uint TokenClockSkewMinutes { get; set; } = DefaultTokenClockSkewMinutes; @@ -49,7 +49,7 @@ namespace Tgstation.Server.Host.Configuration public uint TokenSigningKeyByteCount { get; set; } = DefaultTokenSigningKeyByteAmount; /// - /// Amount of minutes until s generated from OAuth logins expire. + /// Amount of minutes until s generated from OAuth logins expire. /// public uint OAuthTokenExpiryMinutes { get; set; } = DefaultOAuthTokenExpiryMinutes; diff --git a/src/Tgstation.Server.Host/Controllers/AdministrationController.cs b/src/Tgstation.Server.Host/Controllers/AdministrationController.cs index 5e3e1b3889..6fa5369700 100644 --- a/src/Tgstation.Server.Host/Controllers/AdministrationController.cs +++ b/src/Tgstation.Server.Host/Controllers/AdministrationController.cs @@ -11,6 +11,8 @@ using System.Threading.Tasks; using System.Web; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Api.Rights; using Tgstation.Server.Host.Configuration; using Tgstation.Server.Host.Core; @@ -24,7 +26,7 @@ using Tgstation.Server.Host.Transfer; namespace Tgstation.Server.Host.Controllers { /// - /// for purposes + /// for purposes /// [Route(Routes.Administration)] public sealed class AdministrationController : ApiController @@ -122,18 +124,18 @@ namespace Tgstation.Server.Host.Controllers } /// - /// Get server information. + /// Get server information. /// /// The for the operation. /// A resulting in the for the operation. - /// Retrieved data successfully. + /// Retrieved data successfully. /// The GitHub API rate limit was hit. See response header Retry-After. /// A GitHub API error occurred. See error message for details. [HttpGet] [TgsAuthorize(AdministrationRights.ChangeVersion)] - [ProducesResponseType(typeof(Administration), 200)] - [ProducesResponseType(typeof(ErrorMessage), 424)] - [ProducesResponseType(typeof(ErrorMessage), 429)] + [ProducesResponseType(typeof(AdministrationResponse), 200)] + [ProducesResponseType(typeof(ErrorMessageResponse), 424)] + [ProducesResponseType(typeof(ErrorMessageResponse), 429)] public async Task Read(CancellationToken cancellationToken) { try @@ -169,7 +171,7 @@ namespace Tgstation.Server.Host.Controllers Logger.LogWarning(e, "Not found exception while retrieving upstream repository info!"); } - return Json(new Administration + return Json(new AdministrationResponse { LatestVersion = greatestVersion, TrackedRepositoryUrl = repoUrl, @@ -182,7 +184,7 @@ namespace Tgstation.Server.Host.Controllers catch (ApiException e) { Logger.LogWarning(e, OctokitException); - return StatusCode(HttpStatusCode.FailedDependency, new ErrorMessage(ErrorCode.RemoteApiError) + return StatusCode(HttpStatusCode.FailedDependency, new ErrorMessageResponse(ErrorCode.RemoteApiError) { AdditionalData = e.Message }); @@ -192,7 +194,7 @@ namespace Tgstation.Server.Host.Controllers /// /// Attempt to perform a server upgrade. /// - /// The model containing the to update to. + /// The . /// The for the operation. /// A resulting in the for the operation. /// Update has been started successfully. @@ -202,27 +204,27 @@ namespace Tgstation.Server.Host.Controllers /// A GitHub API error occurred. [HttpPost] [TgsAuthorize(AdministrationRights.ChangeVersion)] - [ProducesResponseType(typeof(Administration), 202)] - [ProducesResponseType(typeof(ErrorMessage), 410)] - [ProducesResponseType(typeof(ErrorMessage), 422)] - [ProducesResponseType(typeof(ErrorMessage), 424)] - [ProducesResponseType(typeof(ErrorMessage), 429)] - public async Task Update([FromBody] Administration model, CancellationToken cancellationToken) + [ProducesResponseType(typeof(ServerUpdateResponse), 202)] + [ProducesResponseType(typeof(ErrorMessageResponse), 410)] + [ProducesResponseType(typeof(ErrorMessageResponse), 422)] + [ProducesResponseType(typeof(ErrorMessageResponse), 424)] + [ProducesResponseType(typeof(ErrorMessageResponse), 429)] + public async Task Update([FromBody] ServerUpdateRequest model, CancellationToken cancellationToken) { if (model == null) throw new ArgumentNullException(nameof(model)); if (model.NewVersion == null) - return BadRequest(new ErrorMessage(ErrorCode.ModelValidationFailure) + return BadRequest(new ErrorMessageResponse(ErrorCode.ModelValidationFailure) { AdditionalData = "newVersion is required!" }); if (model.NewVersion.Major != assemblyInformationProvider.Version.Major) - return BadRequest(new ErrorMessage(ErrorCode.CannotChangeServerSuite)); + return BadRequest(new ErrorMessageResponse(ErrorCode.CannotChangeServerSuite)); if (!serverControl.WatchdogPresent) - return UnprocessableEntity(new ErrorMessage(ErrorCode.MissingHostWatchdog)); + return UnprocessableEntity(new ErrorMessageResponse(ErrorCode.MissingHostWatchdog)); try { @@ -231,9 +233,9 @@ namespace Tgstation.Server.Host.Controllers return Gone(); if (updateResult == ServerUpdateResult.UpdateInProgress) - return BadRequest(new ErrorMessage(ErrorCode.ServerUpdateInProgress)); + return BadRequest(new ErrorMessageResponse(ErrorCode.ServerUpdateInProgress)); - return Accepted(new Administration + return Accepted(new ServerUpdateResponse { NewVersion = model.NewVersion }); @@ -245,7 +247,7 @@ namespace Tgstation.Server.Host.Controllers catch (ApiException e) { Logger.LogWarning(e, OctokitException); - return StatusCode(HttpStatusCode.FailedDependency, new ErrorMessage(ErrorCode.RemoteApiError) + return StatusCode(HttpStatusCode.FailedDependency, new ErrorMessageResponse(ErrorCode.RemoteApiError) { AdditionalData = e.Message }); @@ -261,7 +263,7 @@ namespace Tgstation.Server.Host.Controllers [HttpDelete] [TgsAuthorize(AdministrationRights.RestartHost)] [ProducesResponseType(204)] - [ProducesResponseType(typeof(ErrorMessage), 422)] + [ProducesResponseType(typeof(ErrorMessageResponse), 422)] public async Task Delete() { try @@ -269,7 +271,7 @@ namespace Tgstation.Server.Host.Controllers if (!serverControl.WatchdogPresent) { Logger.LogDebug("Restart request failed due to lack of host watchdog!"); - return UnprocessableEntity(new ErrorMessage(ErrorCode.MissingHostWatchdog)); + return UnprocessableEntity(new ErrorMessageResponse(ErrorCode.MissingHostWatchdog)); } await serverControl.Restart().ConfigureAwait(false); @@ -282,7 +284,7 @@ namespace Tgstation.Server.Host.Controllers } /// - /// List s present. + /// List s present. /// /// The current page. /// The page size. @@ -292,8 +294,8 @@ namespace Tgstation.Server.Host.Controllers /// An IO error occurred while listing. [HttpGet(Routes.Logs)] [TgsAuthorize(AdministrationRights.DownloadLogs)] - [ProducesResponseType(typeof(Paginated), 200)] - [ProducesResponseType(typeof(ErrorMessage), 409)] + [ProducesResponseType(typeof(PaginatedResponse), 200)] + [ProducesResponseType(typeof(ErrorMessageResponse), 409)] public Task ListLogs([FromQuery] int? page, [FromQuery] int? pageSize, CancellationToken cancellationToken) => Paginated( async () => @@ -303,7 +305,7 @@ namespace Tgstation.Server.Host.Controllers { var files = await ioManager.GetFiles(path, cancellationToken).ConfigureAwait(false); var tasks = files.Select( - async file => new LogFile + async file => new LogFileResponse { Name = ioManager.GetFileName(file), LastModified = await ioManager @@ -316,7 +318,7 @@ namespace Tgstation.Server.Host.Controllers await Task.WhenAll(tasks).ConfigureAwait(false); - return new PaginatableResult( + return new PaginatableResult( tasks .AsQueryable() .Select(x => x.Result) @@ -324,8 +326,8 @@ namespace Tgstation.Server.Host.Controllers } catch (IOException ex) { - return new PaginatableResult( - Conflict(new ErrorMessage(ErrorCode.IOError) + return new PaginatableResult( + Conflict(new ErrorMessageResponse(ErrorCode.IOError) { AdditionalData = ex.ToString() })); @@ -337,17 +339,17 @@ namespace Tgstation.Server.Host.Controllers cancellationToken); /// - /// Download a . + /// Download a . /// /// The path to download. /// The for the operation. /// A resulting in the of the request. - /// Downloaded successfully. + /// Downloaded successfully. /// An IO error occurred while downloading. [HttpGet(Routes.Logs + "/{*path}")] [TgsAuthorize(AdministrationRights.DownloadLogs)] - [ProducesResponseType(typeof(LogFile), 200)] - [ProducesResponseType(typeof(ErrorMessage), 409)] + [ProducesResponseType(typeof(LogFileResponse), 200)] + [ProducesResponseType(typeof(ErrorMessageResponse), 409)] public async Task GetLog(string path, CancellationToken cancellationToken) { if (path == null) @@ -374,7 +376,7 @@ namespace Tgstation.Server.Host.Controllers var readTask = ioManager.ReadAllBytes(fullPath, cancellationToken); - return Ok(new LogFile + return Ok(new LogFileResponse { Name = path, LastModified = await ioManager.GetLastModified(fullPath, cancellationToken).ConfigureAwait(false), @@ -383,7 +385,7 @@ namespace Tgstation.Server.Host.Controllers } catch (IOException ex) { - return Conflict(new ErrorMessage(ErrorCode.IOError) + return Conflict(new ErrorMessageResponse(ErrorCode.IOError) { AdditionalData = ex.ToString() }); diff --git a/src/Tgstation.Server.Host/Controllers/ApiController.cs b/src/Tgstation.Server.Host/Controllers/ApiController.cs index beaae1e95d..659f7da2e8 100644 --- a/src/Tgstation.Server.Host/Controllers/ApiController.cs +++ b/src/Tgstation.Server.Host/Controllers/ApiController.cs @@ -17,6 +17,7 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Host.Database; using Tgstation.Server.Host.Models; using Tgstation.Server.Host.Security; @@ -96,19 +97,19 @@ namespace Tgstation.Server.Host.Controllers /// Generic 410 response. /// /// An with . - protected ObjectResult Gone() => StatusCode(HttpStatusCode.Gone, new ErrorMessage(ErrorCode.ResourceNotPresent)); + protected ObjectResult Gone() => StatusCode(HttpStatusCode.Gone, new ErrorMessageResponse(ErrorCode.ResourceNotPresent)); /// /// Generic 404 response. /// /// An with . - protected new NotFoundObjectResult NotFound() => NotFound(new ErrorMessage(ErrorCode.ResourceNeverPresent)); + protected new NotFoundObjectResult NotFound() => NotFound(new ErrorMessageResponse(ErrorCode.ResourceNeverPresent)); /// /// Generic 501 response. /// /// An with . - protected ObjectResult RequiresPosixSystemIdentity() => StatusCode(HttpStatusCode.NotImplemented, new ErrorMessage(ErrorCode.RequiresPosixSystemIdentity)); + protected ObjectResult RequiresPosixSystemIdentity() => StatusCode(HttpStatusCode.NotImplemented, new ErrorMessageResponse(ErrorCode.RequiresPosixSystemIdentity)); /// /// Strongly type calls to . @@ -121,7 +122,7 @@ namespace Tgstation.Server.Host.Controllers /// Strongly type calls to . /// /// The . - /// The accompanying payload. + /// The accompanying payload. /// A with the given . protected ObjectResult StatusCode(HttpStatusCode statusCode, object errorMessage) => StatusCode((int)statusCode, errorMessage); @@ -145,7 +146,7 @@ namespace Tgstation.Server.Host.Controllers Logger.LogWarning(rateLimitException, "Exceeded GitHub rate limit!"); var secondsString = Math.Ceiling((rateLimitException.Reset - DateTimeOffset.UtcNow).TotalSeconds).ToString(CultureInfo.InvariantCulture); Response.Headers.Add(HeaderNames.RetryAfter, secondsString); - return StatusCode(HttpStatusCode.TooManyRequests, new ErrorMessage(ErrorCode.GitHubApiRateLimit)); + return StatusCode(HttpStatusCode.TooManyRequests, new ErrorMessageResponse(ErrorCode.GitHubApiRateLimit)); } /// @@ -174,7 +175,7 @@ namespace Tgstation.Server.Host.Controllers headersException = ex; } - var errorMessage = new ErrorMessage(ErrorCode.BadHeaders) + var errorMessage = new ErrorMessageResponse(ErrorCode.BadHeaders) { AdditionalData = headersException.Message }; @@ -210,7 +211,7 @@ namespace Tgstation.Server.Host.Controllers { await StatusCode( HttpStatusCode.UpgradeRequired, - new ErrorMessage(ErrorCode.ApiMismatch)) + new ErrorMessageResponse(ErrorCode.ApiMismatch)) .ExecuteResultAsync(context) .ConfigureAwait(false); return; @@ -249,7 +250,7 @@ namespace Tgstation.Server.Host.Controllers if (errorMessages.Any()) { await BadRequest( - new ErrorMessage(ErrorCode.ModelValidationFailure) + new ErrorMessageResponse(ErrorCode.ModelValidationFailure) { AdditionalData = String.Join(Environment.NewLine, errorMessages) }) @@ -303,7 +304,7 @@ namespace Tgstation.Server.Host.Controllers Action resultTransformer, int? pageQuery, int? pageSizeQuery, - CancellationToken cancellationToken) => PaginatedImpl( + CancellationToken cancellationToken) => PaginatedImpl( queryGenerator, resultTransformer, pageQuery, @@ -316,19 +317,19 @@ namespace Tgstation.Server.Host.Controllers /// The of model being generated. /// The of model being returned. /// A resulting in a resulting in the generated . - /// An to transform the s after being queried. + /// An to transform the s after being queried. /// The requested page from the query. /// The requested page size from the query. /// The for the operation. /// A resulting in the for the operation. protected Task Paginated( Func>> queryGenerator, - Action resultTransformer, + Action resultTransformer, int? pageQuery, int? pageSizeQuery, CancellationToken cancellationToken) where TModel : IApiTransformable - => PaginatedImpl( + => PaginatedImpl( queryGenerator, resultTransformer, pageQuery, @@ -341,14 +342,14 @@ namespace Tgstation.Server.Host.Controllers /// The of model being generated. If different from , must implement for . /// The of model being returned. /// A resulting in a resulting in the generated . - /// An to transform the s after being queried. + /// An to transform the s after being queried. /// The requested page from the query. /// The requested page size from the query. /// The for the operation. /// A resulting in the for the operation. async Task PaginatedImpl( Func>> queryGenerator, - Action resultTransformer, + Action resultTransformer, int? pageQuery, int? pageSizeQuery, CancellationToken cancellationToken) @@ -357,11 +358,11 @@ namespace Tgstation.Server.Host.Controllers throw new ArgumentNullException(nameof(queryGenerator)); if (pageQuery <= 0 || pageSizeQuery <= 0) - return BadRequest(new ErrorMessage(ErrorCode.ApiInvalidPageOrPageSize)); + return BadRequest(new ErrorMessageResponse(ErrorCode.ApiInvalidPageOrPageSize)); var pageSize = pageSizeQuery ?? DefaultPageSize; if (pageSize > MaximumPageSize) - return BadRequest(new ErrorMessage(ErrorCode.ApiPageTooLarge) + return BadRequest(new ErrorMessageResponse(ErrorCode.ApiPageTooLarge) { AdditionalData = $"Maximum page size: {MaximumPageSize}" }); @@ -392,10 +393,6 @@ namespace Tgstation.Server.Host.Controllers pagedResults = queriedResults.ToList(); } - if (resultTransformer != null) - foreach (var I in pagedResults) - resultTransformer(I); - ICollection finalResults; if (typeof(TModel) == typeof(TResultModel)) finalResults = (List)(object)pagedResults; // clearly a safe cast @@ -405,8 +402,12 @@ namespace Tgstation.Server.Host.Controllers .Select(x => x.ToApi()) .ToList(); + if (resultTransformer != null) + foreach (var I in finalResults) + resultTransformer(I); + return Json( - new Paginated + new PaginatedResponse { Content = finalResults, PageSize = pageSize, diff --git a/src/Tgstation.Server.Host/Controllers/BridgeController.cs b/src/Tgstation.Server.Host/Controllers/BridgeController.cs index 8bb613ff4d..d9397874b4 100644 --- a/src/Tgstation.Server.Host/Controllers/BridgeController.cs +++ b/src/Tgstation.Server.Host/Controllers/BridgeController.cs @@ -19,6 +19,7 @@ namespace Tgstation.Server.Host.Controllers [Route("/Bridge")] [Produces(MediaTypeNames.Application.Json)] [ApiController] + [ApiExplorerSettings(IgnoreApi = true)] public class BridgeController : Controller { /// diff --git a/src/Tgstation.Server.Host/Controllers/ByondController.cs b/src/Tgstation.Server.Host/Controllers/ByondController.cs index 5ae09e1c2b..26d14a7033 100644 --- a/src/Tgstation.Server.Host/Controllers/ByondController.cs +++ b/src/Tgstation.Server.Host/Controllers/ByondController.cs @@ -7,6 +7,8 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Api.Rights; using Tgstation.Server.Host.Components; using Tgstation.Server.Host.Database; @@ -18,7 +20,7 @@ using Tgstation.Server.Host.Transfer; namespace Tgstation.Server.Host.Controllers { /// - /// Controller for managing s + /// Controller for managing BYOND installations. /// [Route(Routes.Byond)] public sealed class ByondController : InstanceRequiredController @@ -60,23 +62,23 @@ namespace Tgstation.Server.Host.Controllers } /// - /// Gets the active version. + /// Gets the active . /// /// A resulting in the for the operation. /// Retrieved version information successfully. [HttpGet] [TgsAuthorize(ByondRights.ReadActive)] - [ProducesResponseType(typeof(Api.Models.Byond), 200)] + [ProducesResponseType(typeof(ByondResponse), 200)] public Task Read() => WithComponentInstance(instance => Task.FromResult( - Json(new Api.Models.Byond + Json(new ByondResponse { Version = instance.ByondManager.ActiveVersion }))); /// - /// Lists installed versions. + /// Lists installed s. /// /// The current page. /// The page size. @@ -85,16 +87,16 @@ namespace Tgstation.Server.Host.Controllers /// Retrieved version information successfully. [HttpGet(Routes.List)] [TgsAuthorize(ByondRights.ListInstalled)] - [ProducesResponseType(typeof(Paginated), 200)] + [ProducesResponseType(typeof(PaginatedResponse), 200)] public Task List([FromQuery] int? page, [FromQuery] int? pageSize, CancellationToken cancellationToken) => WithComponentInstance( instance => Paginated( () => Task.FromResult( - new PaginatableResult( + new PaginatableResult( instance .ByondManager .InstalledVersions - .Select(x => new Api.Models.Byond + .Select(x => new ByondResponse { Version = x }) @@ -108,17 +110,17 @@ namespace Tgstation.Server.Host.Controllers /// /// Changes the active BYOND version to the one specified in a given . /// - /// The to switch to. + /// The to switch to. /// The for the operation. /// A resulting in the for the operation. /// Switched active version successfully. - /// Created to install and switch active version successfully. + /// Created to install and switch active version successfully. [HttpPost] [TgsAuthorize(ByondRights.InstallOfficialOrChangeActiveVersion | ByondRights.InstallCustomVersion)] - [ProducesResponseType(typeof(Api.Models.Byond), 200)] - [ProducesResponseType(typeof(Api.Models.Byond), 202)] + [ProducesResponseType(typeof(ByondInstallResponse), 200)] + [ProducesResponseType(typeof(ByondInstallResponse), 202)] #pragma warning disable CA1506 // TODO: Decomplexify - public async Task Update([FromBody] Api.Models.Byond model, CancellationToken cancellationToken) + public async Task Update([FromBody] ByondVersionRequest model, CancellationToken cancellationToken) #pragma warning restore CA1506 { if (model == null) @@ -129,7 +131,7 @@ namespace Tgstation.Server.Host.Controllers if (model.Version == null || model.Version.Revision != -1 || (uploadingZip && model.Version.Build > 0)) - return BadRequest(new ErrorMessage(ErrorCode.ModelValidationFailure)); + return BadRequest(new ErrorMessageResponse(ErrorCode.ModelValidationFailure)); var userByondRights = AuthenticationContext.InstancePermissionSet.ByondRights.Value; if ((!userByondRights.HasFlag(ByondRights.InstallOfficialOrChangeActiveVersion) && !uploadingZip) @@ -137,7 +139,7 @@ namespace Tgstation.Server.Host.Controllers return Forbid(); // remove cruff fields - var result = new Api.Models.Byond(); + var result = new ByondInstallResponse(); return await WithComponentInstance( async instance => { @@ -152,7 +154,7 @@ namespace Tgstation.Server.Host.Controllers await byondManager.ChangeVersion(model.Version, null, cancellationToken).ConfigureAwait(false); } else if (model.Version.Build > 0) - return BadRequest(new ErrorMessage(ErrorCode.ByondNonExistentCustomVersion)); + return BadRequest(new ErrorMessageResponse(ErrorCode.ByondNonExistentCustomVersion)); else { var installingVersion = model.Version.Build <= 0 @@ -166,7 +168,7 @@ namespace Tgstation.Server.Host.Controllers Instance.Id); // run the install through the job manager - var job = new Models.Job + var job = new Job { Description = $"Install {(!uploadingZip ? String.Empty : "custom ")}BYOND version {model.Version.Major}.{model.Version.Minor}", StartedBy = AuthenticationContext.User, @@ -225,8 +227,6 @@ namespace Tgstation.Server.Host.Controllers } } - if ((AuthenticationContext.GetRight(RightsType.Byond) & (ulong)ByondRights.ReadActive) != 0) - result.Version = byondManager.ActiveVersion; return result.InstallJob != null ? (IActionResult)Accepted(result) : Json(result); }) .ConfigureAwait(false); diff --git a/src/Tgstation.Server.Host/Controllers/ChatController.cs b/src/Tgstation.Server.Host/Controllers/ChatController.cs index 771aebf7e7..d349de7b13 100644 --- a/src/Tgstation.Server.Host/Controllers/ChatController.cs +++ b/src/Tgstation.Server.Host/Controllers/ChatController.cs @@ -11,6 +11,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Internal; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Api.Rights; using Tgstation.Server.Host.Components; using Tgstation.Server.Host.Database; @@ -21,7 +24,7 @@ using Z.EntityFramework.Plus; namespace Tgstation.Server.Host.Controllers { /// - /// for managing s + /// for managing s /// [Route(Routes.Chat)] #pragma warning disable CA1506 // TODO: Decomplexify @@ -65,14 +68,14 @@ namespace Tgstation.Server.Host.Controllers /// /// Create a new chat bot . /// - /// The to create. + /// The . /// The for the operation. /// A resulting in the for the operation. - /// Created chat bot successfully. + /// Created successfully. [HttpPut] [TgsAuthorize(ChatBotRights.Create)] - [ProducesResponseType(typeof(Api.Models.ChatBot), 201)] - public async Task Create([FromBody] Api.Models.ChatBot model, CancellationToken cancellationToken) + [ProducesResponseType(typeof(ChatBotResponse), 201)] + public async Task Create([FromBody] ChatBotCreateRequest model, CancellationToken cancellationToken) { if (model == null) throw new ArgumentNullException(nameof(model)); @@ -89,19 +92,19 @@ namespace Tgstation.Server.Host.Controllers .ConfigureAwait(false); if (countOfExistingBotsInInstance >= Instance.ChatBotLimit.Value) - return Conflict(new ErrorMessage(ErrorCode.ChatBotMax)); + return Conflict(new ErrorMessageResponse(ErrorCode.ChatBotMax)); model.Enabled ??= false; model.ReconnectionInterval ??= 1; // try to update das db first - var dbModel = new Models.ChatBot + var dbModel = new ChatBot { Name = model.Name, ConnectionString = model.ConnectionString, Enabled = model.Enabled, Channels = model.Channels?.Select(x => ConvertApiChatChannel(x)).ToList() ?? new List(), // important that this isn't null - InstanceId = Instance.Id, + InstanceId = Instance.Id.Value, Provider = model.Provider, ReconnectionInterval = model.ReconnectionInterval, ChannelLimit = model.ChannelLimit @@ -119,7 +122,7 @@ namespace Tgstation.Server.Host.Controllers await instance.Chat.ChangeSettings(dbModel, cancellationToken).ConfigureAwait(false); if (dbModel.Channels.Count > 0) - await instance.Chat.ChangeChannels(dbModel.Id, dbModel.Channels, cancellationToken).ConfigureAwait(false); + await instance.Chat.ChangeChannels(dbModel.Id.Value, dbModel.Channels, cancellationToken).ConfigureAwait(false); } catch { @@ -128,7 +131,7 @@ namespace Tgstation.Server.Host.Controllers // DCTx2: Operations must always run await DatabaseContext.Save(default).ConfigureAwait(false); - await instance.Chat.DeleteConnection(dbModel.Id, default).ConfigureAwait(false); + await instance.Chat.DeleteConnection(dbModel.Id.Value, default).ConfigureAwait(false); throw; } @@ -139,7 +142,7 @@ namespace Tgstation.Server.Host.Controllers } /// - /// Delete a . + /// Delete a . /// /// The to delete. /// The for the operation. @@ -166,7 +169,7 @@ namespace Tgstation.Server.Host.Controllers ?? NoContent(); /// - /// List s. + /// List s. /// /// The current page. /// The page size. @@ -175,13 +178,13 @@ namespace Tgstation.Server.Host.Controllers /// Listed chat bots successfully. [HttpGet(Routes.List)] [TgsAuthorize(ChatBotRights.Read)] - [ProducesResponseType(typeof(Paginated), 200)] + [ProducesResponseType(typeof(PaginatedResponse), 200)] public Task List([FromQuery] int? page, [FromQuery] int? pageSize, CancellationToken cancellationToken) { var connectionStrings = (AuthenticationContext.GetRight(RightsType.ChatBots) & (ulong)ChatBotRights.ReadConnectionString) != 0; - return Paginated( + return Paginated( () => Task.FromResult( - new PaginatableResult( + new PaginatableResult( DatabaseContext .ChatBots .AsQueryable() @@ -199,17 +202,17 @@ namespace Tgstation.Server.Host.Controllers } /// - /// Get a specific . + /// Get a specific . /// /// The to retrieve. /// The for the operation. /// A resulting in the for the operation. - /// Retrieved successfully. - /// The with the given ID does not exist in this instance. + /// Retrieved successfully. + /// The with the given ID does not exist in this instance. [HttpGet("{id}")] [TgsAuthorize(ChatBotRights.Read)] - [ProducesResponseType(typeof(Api.Models.ChatBot), 200)] - [ProducesResponseType(typeof(ErrorMessage), 410)] + [ProducesResponseType(typeof(ChatBotResponse), 200)] + [ProducesResponseType(typeof(ErrorMessageResponse), 410)] public async Task GetId(long id, CancellationToken cancellationToken) { var query = DatabaseContext.ChatBots @@ -232,19 +235,19 @@ namespace Tgstation.Server.Host.Controllers /// /// Updates a chat bot . /// - /// The update to apply. + /// The . /// The for the operation. /// A resulting in the for the operation. /// Update applied successfully. - /// Update applied successfully. not returned based on user permissions. - /// The with the given ID does not exist in this instance. + /// Update applied successfully. not returned based on user permissions. + /// The with the given ID does not exist in this instance. [HttpPost] [TgsAuthorize(ChatBotRights.WriteChannels | ChatBotRights.WriteConnectionString | ChatBotRights.WriteEnabled | ChatBotRights.WriteName | ChatBotRights.WriteProvider)] - [ProducesResponseType(typeof(Api.Models.ChatBot), 200)] + [ProducesResponseType(typeof(ChatBotResponse), 200)] [ProducesResponseType(204)] - [ProducesResponseType(typeof(ErrorMessage), 410)] + [ProducesResponseType(typeof(ErrorMessageResponse), 410)] #pragma warning disable CA1502, CA1506 // TODO: Decomplexify - public async Task Update([FromBody] Api.Models.ChatBot model, CancellationToken cancellationToken) + public async Task Update([FromBody] ChatBotUpdateRequest model, CancellationToken cancellationToken) #pragma warning restore CA1502, CA1506 { if (model == null) @@ -268,7 +271,7 @@ namespace Tgstation.Server.Host.Controllers if ((model.Channels?.Count ?? current.Channels.Count) > (model.ChannelLimit ?? current.ChannelLimit.Value)) { // 400 or 409 depends on if the client sent both - var errorMessage = new ErrorMessage(ErrorCode.ChatBotMaxChannels); + var errorMessage = new ErrorMessageResponse(ErrorCode.ChatBotMaxChannels); if (model.Channels != null && model.ChannelLimit.HasValue) return BadRequest(errorMessage); return Conflict(errorMessage); @@ -278,7 +281,7 @@ namespace Tgstation.Server.Host.Controllers bool anySettingsModified = false; - bool CheckModified(Expression> expression, ChatBotRights requiredRight) + bool CheckModified(Expression> expression, ChatBotRights requiredRight) { var memberSelectorExpression = (MemberExpression)expression.Body; var property = (PropertyInfo)memberSelectorExpression.Member; @@ -329,7 +332,7 @@ namespace Tgstation.Server.Host.Controllers await chat.ChangeSettings(current, cancellationToken).ConfigureAwait(false); // have to rebuild the thing first if (model.Channels != null || anySettingsModified) - await chat.ChangeChannels(current.Id, current.Channels, cancellationToken).ConfigureAwait(false); + await chat.ChangeChannels(current.Id.Value, current.Channels, cancellationToken).ConfigureAwait(false); return null; }) @@ -350,29 +353,29 @@ namespace Tgstation.Server.Host.Controllers /// /// Perform some basic validation of a given . /// - /// The to validate. + /// The to validate. /// If the is being created. /// An to respond with or . - private IActionResult StandardModelChecks(Api.Models.ChatBot model, bool forCreation) + private IActionResult StandardModelChecks(ChatBotApiBase model, bool forCreation) { if (model.ReconnectionInterval == 0) throw new InvalidOperationException("RecconnectionInterval cannot be zero!"); if (forCreation && !model.Provider.HasValue) - return BadRequest(new ErrorMessage(ErrorCode.ChatBotProviderMissing)); + return BadRequest(new ErrorMessageResponse(ErrorCode.ChatBotProviderMissing)); if (model.Name != null && String.IsNullOrWhiteSpace(model.Name)) - return BadRequest(new ErrorMessage(ErrorCode.ChatBotWhitespaceName)); + return BadRequest(new ErrorMessageResponse(ErrorCode.ChatBotWhitespaceName)); if (model.ConnectionString != null && String.IsNullOrWhiteSpace(model.ConnectionString)) - return BadRequest(new ErrorMessage(ErrorCode.ChatBotWhitespaceConnectionString)); + return BadRequest(new ErrorMessageResponse(ErrorCode.ChatBotWhitespaceConnectionString)); if (!model.ValidateProviderChannelTypes()) - return BadRequest(new ErrorMessage(ErrorCode.ChatBotWrongChannelType)); + return BadRequest(new ErrorMessageResponse(ErrorCode.ChatBotWrongChannelType)); - var defaultMaxChannels = (ulong)Math.Max(Models.ChatBot.DefaultChannelLimit, model.Channels?.Count ?? 0); + var defaultMaxChannels = (ulong)Math.Max(ChatBot.DefaultChannelLimit, model.Channels?.Count ?? 0); if (defaultMaxChannels > UInt16.MaxValue) - return BadRequest(new ErrorMessage(ErrorCode.ChatBotMaxChannels)); + return BadRequest(new ErrorMessageResponse(ErrorCode.ChatBotMaxChannels)); if (forCreation) model.ChannelLimit ??= (ushort)defaultMaxChannels; diff --git a/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs b/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs index a26ae11eb4..8dd9456219 100644 --- a/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs +++ b/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs @@ -7,6 +7,8 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Api.Rights; using Tgstation.Server.Host.Components; using Tgstation.Server.Host.Database; @@ -17,7 +19,7 @@ using Tgstation.Server.Host.Security; namespace Tgstation.Server.Host.Controllers { /// - /// The for s + /// The for s /// [Route(Routes.Configuration)] public sealed class ConfigurationController : InstanceRequiredController @@ -73,16 +75,16 @@ namespace Tgstation.Server.Host.Controllers /// /// Write to a configuration file. /// - /// The representing the file. + /// The representing the file. /// The for the operation. /// A resulting in the for the operation. /// File updated successfully. /// File upload ticket created successfully. [HttpPost] [TgsAuthorize(ConfigurationRights.Write)] - [ProducesResponseType(typeof(ConfigurationFile), 200)] - [ProducesResponseType(typeof(ConfigurationFile), 202)] - public async Task Update([FromBody] ConfigurationFile model, CancellationToken cancellationToken) + [ProducesResponseType(typeof(ConfigurationFileResponse), 200)] + [ProducesResponseType(typeof(ConfigurationFileResponse), 202)] + public async Task Update([FromBody] ConfigurationFileRequest model, CancellationToken cancellationToken) { if (model == null) throw new ArgumentNullException(nameof(model)); @@ -110,7 +112,7 @@ namespace Tgstation.Server.Host.Controllers catch(IOException e) { Logger.LogInformation("IOException while updating file {0}: {1}", model.Path, e); - return Conflict(new ErrorMessage(ErrorCode.IOError) + return Conflict(new ErrorMessageResponse(ErrorCode.IOError) { AdditionalData = e.Message }); @@ -131,8 +133,8 @@ namespace Tgstation.Server.Host.Controllers /// File does not currently exist. [HttpGet(Routes.File + "/{*filePath}")] [TgsAuthorize(ConfigurationRights.Read)] - [ProducesResponseType(typeof(ConfigurationFile), 200)] - [ProducesResponseType(typeof(ErrorMessage), 410)] + [ProducesResponseType(typeof(ConfigurationFileResponse), 200)] + [ProducesResponseType(typeof(ErrorMessageResponse), 410)] public async Task File(string filePath, CancellationToken cancellationToken) { if (ForbidDueToModeConflicts(filePath, out var systemIdentity)) @@ -157,7 +159,7 @@ namespace Tgstation.Server.Host.Controllers catch (IOException e) { Logger.LogInformation("IOException while reading file {0}: {1}", filePath, e); - return Conflict(new ErrorMessage(ErrorCode.IOError) + return Conflict(new ErrorMessageResponse(ErrorCode.IOError) { AdditionalData = e.Message }); @@ -180,8 +182,8 @@ namespace Tgstation.Server.Host.Controllers /// Directory does not currently exist. [HttpGet(Routes.List + "/{*directoryPath}")] [TgsAuthorize(ConfigurationRights.List)] - [ProducesResponseType(typeof(Paginated), 200)] - [ProducesResponseType(typeof(ErrorMessage), 410)] + [ProducesResponseType(typeof(PaginatedResponse), 200)] + [ProducesResponseType(typeof(ErrorMessageResponse), 410)] public Task Directory( string directoryPath, [FromQuery] int? page, @@ -192,7 +194,7 @@ namespace Tgstation.Server.Host.Controllers async () => { if (ForbidDueToModeConflicts(directoryPath, out var systemIdentity)) - return new PaginatableResult( + return new PaginatableResult( Forbid()); try @@ -202,21 +204,21 @@ namespace Tgstation.Server.Host.Controllers .ListDirectory(directoryPath, systemIdentity, cancellationToken) .ConfigureAwait(false); if (result == null) - return new PaginatableResult(Gone()); + return new PaginatableResult(Gone()); - return new PaginatableResult( + return new PaginatableResult( result .AsQueryable() .OrderBy(x => x.Path)); } catch (NotImplementedException) { - return new PaginatableResult( + return new PaginatableResult( RequiresPosixSystemIdentity()); } catch (UnauthorizedAccessException) { - return new PaginatableResult( + return new PaginatableResult( Forbid()); } }, @@ -234,7 +236,7 @@ namespace Tgstation.Server.Host.Controllers /// A resulting in the for the operation. [HttpGet(Routes.List)] [TgsAuthorize(ConfigurationRights.List)] - [ProducesResponseType(typeof(Paginated), 200)] + [ProducesResponseType(typeof(PaginatedResponse), 200)] public Task List( [FromQuery] int? page, [FromQuery] int? pageSize, @@ -243,16 +245,16 @@ namespace Tgstation.Server.Host.Controllers /// /// Create a configuration directory. /// - /// The representing the directory. + /// The representing the directory. /// The for the operation. /// A resulting in the for the operation. /// Directory already exists. /// Directory created successfully. [HttpPut] [TgsAuthorize(ConfigurationRights.Write)] - [ProducesResponseType(typeof(ConfigurationFile), 200)] - [ProducesResponseType(typeof(ConfigurationFile), 201)] - public async Task Create([FromBody] ConfigurationFile model, CancellationToken cancellationToken) + [ProducesResponseType(typeof(ConfigurationFileResponse), 200)] + [ProducesResponseType(typeof(ConfigurationFileResponse), 201)] + public async Task CreateDirectory([FromBody] ConfigurationFileRequest model, CancellationToken cancellationToken) { if (model == null) throw new ArgumentNullException(nameof(model)); @@ -262,20 +264,25 @@ namespace Tgstation.Server.Host.Controllers try { - model.IsDirectory = true; + var resultModel = new ConfigurationFileResponse + { + IsDirectory = true, + Path = model.Path + }; + return await WithComponentInstance( async instance => await instance - .Configuration - .CreateDirectory(model.Path, systemIdentity, cancellationToken) - .ConfigureAwait(false) - ? (IActionResult)Json(model) - : Created(model)) + .Configuration + .CreateDirectory(model.Path, systemIdentity, cancellationToken) + .ConfigureAwait(false) + ? (IActionResult)Json(resultModel) + : Created(resultModel)) .ConfigureAwait(false); } catch (IOException e) { Logger.LogInformation("IOException while creating directory {0}: {1}", model.Path, e); - return Conflict(new ErrorMessage(ErrorCode.IOError) + return Conflict(new ErrorMessageResponse(ErrorCode.IOError) { Message = e.Message }); @@ -293,20 +300,20 @@ namespace Tgstation.Server.Host.Controllers /// /// Deletes an empty /// - /// A representing the path to the directory to delete + /// A representing the path to the directory to delete /// The for the operation /// A resulting in the of the operation /// Empty directory deleted successfully. [HttpDelete] [TgsAuthorize(ConfigurationRights.Delete)] [ProducesResponseType(204)] - public async Task Delete([FromBody] ConfigurationFile directory, CancellationToken cancellationToken) + public async Task DeleteDirectory([FromBody] ConfigurationFileRequest directory, CancellationToken cancellationToken) { if (directory == null) throw new ArgumentNullException(nameof(directory)); if (directory.Path == null) - return BadRequest(new ErrorMessage(ErrorCode.ModelValidationFailure)); + return BadRequest(new ErrorMessageResponse(ErrorCode.ModelValidationFailure)); if (ForbidDueToModeConflicts(directory.Path, out var systemIdentity)) return Forbid(); @@ -319,7 +326,7 @@ namespace Tgstation.Server.Host.Controllers .DeleteDirectory(directory.Path, systemIdentity, cancellationToken) .ConfigureAwait(false) ? (IActionResult)NoContent() - : Conflict(new ErrorMessage(ErrorCode.ConfigurationDirectoryNotEmpty))) + : Conflict(new ErrorMessageResponse(ErrorCode.ConfigurationDirectoryNotEmpty))) .ConfigureAwait(false); } catch (NotImplementedException) diff --git a/src/Tgstation.Server.Host/Controllers/ControlPanelController.cs b/src/Tgstation.Server.Host/Controllers/ControlPanelController.cs index 71de870e2c..b632f7e690 100644 --- a/src/Tgstation.Server.Host/Controllers/ControlPanelController.cs +++ b/src/Tgstation.Server.Host/Controllers/ControlPanelController.cs @@ -17,6 +17,7 @@ namespace Tgstation.Server.Host.Controllers /// Controller for the web control panel. /// [Route(Application.ControlPanelRoute)] + [ApiExplorerSettings(IgnoreApi = true)] public class ControlPanelController : Controller { /// diff --git a/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs b/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs index 41ffc0228d..dbbf353758 100644 --- a/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs +++ b/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs @@ -9,6 +9,7 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Api.Rights; using Tgstation.Server.Host.Components; using Tgstation.Server.Host.Components.Session; @@ -21,7 +22,7 @@ using Tgstation.Server.Host.Security; namespace Tgstation.Server.Host.Controllers { /// - /// for managing the + /// for managing the /// [Route(Routes.DreamDaemon)] public sealed class DreamDaemonController : InstanceRequiredController @@ -67,17 +68,17 @@ namespace Tgstation.Server.Host.Controllers /// /// The for the operation. /// A resulting in the of the operation. - /// to launch the watchdog started successfully. + /// Watchdog launch started successfully. [HttpPut] [TgsAuthorize(DreamDaemonRights.Start)] - [ProducesResponseType(typeof(Api.Models.Job), 202)] + [ProducesResponseType(typeof(JobResponse), 202)] public Task Create(CancellationToken cancellationToken) => WithComponentInstance(async instance => { if (instance.Watchdog.Status != WatchdogStatus.Offline) - return Conflict(new ErrorMessage(ErrorCode.WatchdogRunning)); + return Conflict(new ErrorMessageResponse(ErrorCode.WatchdogRunning)); - var job = new Models.Job + var job = new Job { Description = "Launch DreamDaemon", CancelRight = (ulong)DreamDaemonRights.Shutdown, @@ -98,12 +99,12 @@ namespace Tgstation.Server.Host.Controllers /// /// The for the operation. /// A resulting in the of the operation. - /// Read information successfully. + /// Read information successfully. /// The database entity for the requested instance could not be retrieved. The instance was likely detached. [HttpGet] [TgsAuthorize(DreamDaemonRights.ReadMetadata | DreamDaemonRights.ReadRevision)] - [ProducesResponseType(typeof(DreamDaemon), 200)] - [ProducesResponseType(typeof(ErrorMessage), 410)] + [ProducesResponseType(typeof(DreamDaemonResponse), 200)] + [ProducesResponseType(typeof(ErrorMessageResponse), 410)] public Task Read(CancellationToken cancellationToken) => ReadImpl(null, cancellationToken); /// @@ -133,7 +134,7 @@ namespace Tgstation.Server.Host.Controllers return Gone(); } - var result = new DreamDaemon(); + var result = new DreamDaemonResponse(); if (metadata) { var alphaActive = dd.AlphaIsActive; @@ -188,7 +189,7 @@ namespace Tgstation.Server.Host.Controllers /// /// Update watchdog settings to be applied at next server reboot. /// - /// The updated settings. + /// The updated settings. /// The for the operation. /// A resulting in the of the operation. /// Settings applied successfully. @@ -205,17 +206,17 @@ namespace Tgstation.Server.Host.Controllers | DreamDaemonRights.SetStartupTimeout | DreamDaemonRights.SetHeartbeatInterval | DreamDaemonRights.SetTopicTimeout)] - [ProducesResponseType(typeof(DreamDaemon), 200)] - [ProducesResponseType(typeof(ErrorMessage), 410)] + [ProducesResponseType(typeof(DreamDaemonResponse), 200)] + [ProducesResponseType(typeof(ErrorMessageResponse), 410)] #pragma warning disable CA1502 // TODO: Decomplexify #pragma warning disable CA1506 - public async Task Update([FromBody] DreamDaemon model, CancellationToken cancellationToken) + public async Task Update([FromBody] DreamDaemonResponse model, CancellationToken cancellationToken) { if (model == null) throw new ArgumentNullException(nameof(model)); if (model.SoftShutdown == true && model.SoftRestart == true) - return BadRequest(new ErrorMessage(ErrorCode.DreamDaemonDoubleSoft)); + return BadRequest(new ErrorMessageResponse(ErrorCode.DreamDaemonDoubleSoft)); // alias for changing DD settings var current = await DatabaseContext @@ -238,7 +239,7 @@ namespace Tgstation.Server.Host.Controllers cancellationToken) .ConfigureAwait(false); if (verifiedPort != model.Port) - return Conflict(new ErrorMessage(ErrorCode.PortNotAvailable)); + return Conflict(new ErrorMessageResponse(ErrorCode.PortNotAvailable)); } var userRights = (DreamDaemonRights)AuthenticationContext.GetRight(RightsType.DreamDaemon); @@ -298,18 +299,18 @@ namespace Tgstation.Server.Host.Controllers #pragma warning restore CA1502 /// - /// Creates a to restart the Watchdog. It will not start if it wasn't already running. + /// Creates a to restart the Watchdog. It will not start if it wasn't already running. /// /// The for the operation /// A resulting in the of the request - /// Restart started successfully. + /// Restart started successfully. [HttpPatch] [TgsAuthorize(DreamDaemonRights.Restart)] - [ProducesResponseType(typeof(Api.Models.Job), 202)] + [ProducesResponseType(typeof(JobResponse), 202)] public Task Restart(CancellationToken cancellationToken) => WithComponentInstance(async instance => { - var job = new Models.Job + var job = new Job { Instance = Instance, CancelRightsType = RightsType.DreamDaemon, @@ -321,7 +322,7 @@ namespace Tgstation.Server.Host.Controllers var watchdog = instance.Watchdog; if (watchdog.Status == WatchdogStatus.Offline) - return Conflict(new ErrorMessage(ErrorCode.WatchdogNotRunning)); + return Conflict(new ErrorMessageResponse(ErrorCode.WatchdogNotRunning)); await jobManager.RegisterOperation( job, @@ -332,18 +333,18 @@ namespace Tgstation.Server.Host.Controllers }); /// - /// Creates a to generate a DreamDaemon process dump. + /// Creates a to generate a DreamDaemon process dump. /// /// The for the operation /// A resulting in the of the request - /// Dump started successfully. + /// Dump started successfully. [HttpPatch(Routes.Diagnostics)] [TgsAuthorize(DreamDaemonRights.CreateDump)] - [ProducesResponseType(typeof(Api.Models.Job), 202)] + [ProducesResponseType(typeof(JobResponse), 202)] public Task CreateDump(CancellationToken cancellationToken) => WithComponentInstance(async instance => { - var job = new Models.Job + var job = new Job { Instance = Instance, CancelRightsType = RightsType.DreamDaemon, @@ -355,7 +356,7 @@ namespace Tgstation.Server.Host.Controllers var watchdog = instance.Watchdog; if (watchdog.Status == WatchdogStatus.Offline) - return Conflict(new ErrorMessage(ErrorCode.WatchdogNotRunning)); + return Conflict(new ErrorMessageResponse(ErrorCode.WatchdogNotRunning)); await jobManager.RegisterOperation( job, diff --git a/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs b/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs index 40223ad242..e142c3f0ed 100644 --- a/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs +++ b/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs @@ -7,6 +7,8 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Api.Rights; using Tgstation.Server.Host.Components; using Tgstation.Server.Host.Core; @@ -61,14 +63,14 @@ namespace Tgstation.Server.Host.Controllers } /// - /// Read current status. + /// Read current deployment settings. /// /// The for the operation. /// A resulting in the of the request. - /// Read status successfully. + /// Read deployment settings successfully. [HttpGet] [TgsAuthorize(DreamMakerRights.Read)] - [ProducesResponseType(typeof(DreamMaker), 200)] + [ProducesResponseType(typeof(DreamMakerResponse), 200)] public async Task Read(CancellationToken cancellationToken) { var dreamMakerSettings = await DatabaseContext @@ -81,17 +83,17 @@ namespace Tgstation.Server.Host.Controllers } /// - /// Get a specified by a given . + /// Get a specified by a given . /// /// The . /// The for the operation. /// A resulting in the of the request. - /// retrieved successfully. - /// Specified ID does not exist in this instance. + /// retrieved successfully. + /// Specified ID does not exist in this instance. [HttpGet("{id}")] [TgsAuthorize(DreamMakerRights.CompileJobs)] - [ProducesResponseType(typeof(Api.Models.CompileJob), 200)] - [ProducesResponseType(typeof(ErrorMessage), 404)] + [ProducesResponseType(typeof(CompileJobResponse), 200)] + [ProducesResponseType(typeof(ErrorMessageResponse), 404)] public async Task GetId(long id, CancellationToken cancellationToken) { var compileJob = await BaseCompileJobsQuery() @@ -106,7 +108,7 @@ namespace Tgstation.Server.Host.Controllers /// Base query for pulling in all required fields. /// /// An of with all the inclusions. - IQueryable BaseCompileJobsQuery() => DatabaseContext + IQueryable BaseCompileJobsQuery() => DatabaseContext .CompileJobs .AsQueryable() .Include(x => x.Job) @@ -121,7 +123,7 @@ namespace Tgstation.Server.Host.Controllers .Where(x => x.Job.Instance.Id == Instance.Id); /// - /// List all s for the instance. + /// List all s for the instance. /// /// The current page. /// The page size. @@ -130,11 +132,11 @@ namespace Tgstation.Server.Host.Controllers /// Retrieved s successfully. [HttpGet(Routes.List)] [TgsAuthorize(DreamMakerRights.CompileJobs)] - [ProducesResponseType(typeof(Paginated), 200)] + [ProducesResponseType(typeof(PaginatedResponse), 200)] public Task List([FromQuery] int? page, [FromQuery] int? pageSize, CancellationToken cancellationToken) - => Paginated( + => Paginated( () => Task.FromResult( - new PaginatableResult( + new PaginatableResult( BaseCompileJobsQuery() .OrderByDescending(x => x.Job.StoppedAt))), null, @@ -147,13 +149,13 @@ namespace Tgstation.Server.Host.Controllers /// /// The for the operation. /// A resulting in the of the request. - /// Created deployment successfully. + /// Created deployment successfully. [HttpPut] [TgsAuthorize(DreamMakerRights.Compile)] - [ProducesResponseType(typeof(Api.Models.Job), 202)] + [ProducesResponseType(typeof(JobResponse), 202)] public async Task Create(CancellationToken cancellationToken) { - var job = new Models.Job + var job = new Job { Description = "Compile active repository code", StartedBy = AuthenticationContext.User, @@ -174,11 +176,11 @@ namespace Tgstation.Server.Host.Controllers /// /// Update deployment settings. /// - /// The updated settings. + /// The . /// The for the operation. /// A resulting in the of the request. - /// Changes applied successfully. The updated settings will be returned. - /// Changes applied successfully. The updated settings will be not be returned due to permissions. + /// Changes applied successfully. The updated will be returned. + /// Changes applied successfully. The updated will be not be returned due to permissions. /// The database entity for the requested instance could not be retrieved. The instance was likely detached. [HttpPost] [TgsAuthorize( @@ -186,10 +188,10 @@ namespace Tgstation.Server.Host.Controllers | DreamMakerRights.SetApiValidationPort | DreamMakerRights.SetSecurityLevel | DreamMakerRights.SetApiValidationRequirement)] - [ProducesResponseType(typeof(DreamMaker), 200)] + [ProducesResponseType(typeof(DreamMakerResponse), 200)] [ProducesResponseType(204)] - [ProducesResponseType(typeof(ErrorMessage), 410)] - public async Task Update([FromBody] DreamMaker model, CancellationToken cancellationToken) + [ProducesResponseType(typeof(ErrorMessageResponse), 410)] + public async Task Update([FromBody] DreamMakerRequest model, CancellationToken cancellationToken) { if (model == null) throw new ArgumentNullException(nameof(model)); @@ -230,7 +232,7 @@ namespace Tgstation.Server.Host.Controllers cancellationToken) .ConfigureAwait(false); if (verifiedPort != model.ApiValidationPort) - return Conflict(new ErrorMessage(ErrorCode.PortNotAvailable)); + return Conflict(new ErrorMessageResponse(ErrorCode.PortNotAvailable)); hostModel.ApiValidationPort = model.ApiValidationPort; } diff --git a/src/Tgstation.Server.Host/Controllers/HomeController.cs b/src/Tgstation.Server.Host/Controllers/HomeController.cs index 00389c54e4..3dcdc1a4d4 100644 --- a/src/Tgstation.Server.Host/Controllers/HomeController.cs +++ b/src/Tgstation.Server.Host/Controllers/HomeController.cs @@ -14,6 +14,7 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Host.Components.Interop; using Tgstation.Server.Host.Configuration; using Tgstation.Server.Host.Core; @@ -152,12 +153,12 @@ namespace Tgstation.Server.Host.Controllers /// /// The for the operation. /// - /// A resuting in the containing of the if a properly authenticated API request, the web control panel if on a browser and enabled, otherwise. + /// A resuting in the containing of the if a properly authenticated API request, the web control panel if on a browser and enabled, otherwise. /// - /// retrieved successfully. + /// retrieved successfully. [HttpGet] [AllowAnonymous] - [ProducesResponseType(typeof(ServerInformation), 200)] + [ProducesResponseType(typeof(ServerInformationResponse), 200)] #pragma warning disable CA1506 public async Task Home(CancellationToken cancellationToken) { @@ -186,7 +187,7 @@ namespace Tgstation.Server.Host.Controllers if (!headers.Compatible()) return StatusCode( HttpStatusCode.UpgradeRequired, - new ErrorMessage(ErrorCode.ApiMismatch)); + new ErrorMessageResponse(ErrorCode.ApiMismatch)); } catch (HeadersException) { @@ -194,7 +195,7 @@ namespace Tgstation.Server.Host.Controllers } } - return Json(new ServerInformation + return Json(new ServerInformationResponse { Version = assemblyInformationProvider.Version, ApiVersion = ApiHeaders.Version, @@ -217,13 +218,13 @@ namespace Tgstation.Server.Host.Controllers /// /// The for the operation /// A resulting in the of the operation - /// User logged in and generated successfully. + /// User logged in and generated successfully. /// User authentication failed. /// User authenticated but is disabled by an administrator. /// OAuth authentication failed due to rate limiting. [HttpPost] - [ProducesResponseType(typeof(Token), 200)] - [ProducesResponseType(typeof(ErrorMessage), 429)] + [ProducesResponseType(typeof(TokenResponse), 200)] + [ProducesResponseType(typeof(ErrorMessageResponse), 429)] #pragma warning disable CA1506 // TODO: Decomplexify public async Task CreateToken(CancellationToken cancellationToken) { @@ -234,7 +235,7 @@ namespace Tgstation.Server.Host.Controllers } if (ApiHeaders.IsTokenAuthentication) - return BadRequest(new ErrorMessage(ErrorCode.TokenWithToken)); + return BadRequest(new ErrorMessageResponse(ErrorCode.TokenWithToken)); var oAuthLogin = ApiHeaders.OAuthProvider.HasValue; @@ -264,7 +265,7 @@ namespace Tgstation.Server.Host.Controllers .GetValidator(oAuthProvider); if (validator == null) - return BadRequest(new ErrorMessage(ErrorCode.OAuthProviderDisabled)); + return BadRequest(new ErrorMessageResponse(ErrorCode.OAuthProviderDisabled)); externalUserId = await validator .ValidateResponseCode(ApiHeaders.Token, cancellationToken) diff --git a/src/Tgstation.Server.Host/Controllers/InstanceController.cs b/src/Tgstation.Server.Host/Controllers/InstanceController.cs index 85f88e14ed..b13aaa2c3b 100644 --- a/src/Tgstation.Server.Host/Controllers/InstanceController.cs +++ b/src/Tgstation.Server.Host/Controllers/InstanceController.cs @@ -12,6 +12,8 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Api.Rights; using Tgstation.Server.Host.Components; using Tgstation.Server.Host.Configuration; @@ -38,7 +40,7 @@ namespace Tgstation.Server.Host.Controllers public const string InstanceAttachFileName = "TGS4_ALLOW_INSTANCE_ATTACH"; /// - /// Prefix for move s. + /// Prefix for move s. /// const string MoveInstanceJobPrefix = "Move instance ID "; @@ -116,7 +118,13 @@ namespace Tgstation.Server.Host.Controllers swarmConfiguration = swarmConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(swarmConfigurationOptions)); } - async Task CreateDefaultInstance(Api.Models.Instance initialSettings, CancellationToken cancellationToken) + /// + /// Creates a default from . + /// + /// The . + /// The for the operation. + /// A resulting in the new or if ports could not be allocated. + async Task CreateDefaultInstance(InstanceCreateRequest initialSettings, CancellationToken cancellationToken) { var ddPort = await portAllocator.GetAvailablePort(1, false, cancellationToken).ConfigureAwait(false); if (!ddPort.HasValue) @@ -177,7 +185,7 @@ namespace Tgstation.Server.Host.Controllers PostTestMergeComment = false, CreateGitHubDeployments = false }, - InstancePermissionSets = new List // give this user full privileges on the instance + InstancePermissionSets = new List // give this user full privileges on the instance { InstanceAdminPermissionSet(null) }, @@ -197,10 +205,15 @@ namespace Tgstation.Server.Host.Controllers return path; } - Models.InstancePermissionSet InstanceAdminPermissionSet(Models.InstancePermissionSet permissionSetToModify) + /// + /// Generate an with full rights. + /// + /// An optional existing to update. + /// or a new with full rights. + InstancePermissionSet InstanceAdminPermissionSet(InstancePermissionSet permissionSetToModify) { if (permissionSetToModify == null) - permissionSetToModify = new Models.InstancePermissionSet() + permissionSetToModify = new InstancePermissionSet() { PermissionSetId = AuthenticationContext.PermissionSet.Id.Value }; @@ -217,22 +230,22 @@ namespace Tgstation.Server.Host.Controllers /// /// Create or attach an . /// - /// The settings. + /// The . /// The for the operation. /// A resulting in the of the request. /// Instance attached successfully. /// Instance created successfully. [HttpPut] [TgsAuthorize(InstanceManagerRights.Create)] - [ProducesResponseType(typeof(Api.Models.Instance), 200)] - [ProducesResponseType(typeof(Api.Models.Instance), 201)] - public async Task Create([FromBody] Api.Models.Instance model, CancellationToken cancellationToken) + [ProducesResponseType(typeof(InstanceResponse), 200)] + [ProducesResponseType(typeof(InstanceResponse), 201)] + public async Task Create([FromBody] InstanceCreateRequest model, CancellationToken cancellationToken) { if (model == null) throw new ArgumentNullException(nameof(model)); if (String.IsNullOrWhiteSpace(model.Name)) - return BadRequest(new ErrorMessage(ErrorCode.InstanceWhitespaceName)); + return BadRequest(new ErrorMessageResponse(ErrorCode.InstanceWhitespaceName)); var unNormalizedPath = model.Path; var targetInstancePath = NormalizePath(unNormalizedPath); @@ -253,7 +266,7 @@ namespace Tgstation.Server.Host.Controllers } if (InstanceIsChildOf(installationDirectoryPath)) - return Conflict(new ErrorMessage(ErrorCode.InstanceAtConflictingPath)); + return Conflict(new ErrorMessageResponse(ErrorCode.InstanceAtConflictingPath)); // Validate it's not a child of any other instance IActionResult earlyOut = null; @@ -275,9 +288,9 @@ namespace Tgstation.Server.Host.Controllers otherInstance => { if (++countOfOtherInstances >= generalConfiguration.InstanceLimit) - earlyOut ??= Conflict(new ErrorMessage(ErrorCode.InstanceLimitReached)); + earlyOut ??= Conflict(new ErrorMessageResponse(ErrorCode.InstanceLimitReached)); else if (InstanceIsChildOf(otherInstance.Path)) - earlyOut ??= Conflict(new ErrorMessage(ErrorCode.InstanceAtConflictingPath)); + earlyOut ??= Conflict(new ErrorMessageResponse(ErrorCode.InstanceAtConflictingPath)); if (earlyOut != null && !newCancellationToken.IsCancellationRequested) cts.Cancel(); @@ -298,7 +311,7 @@ namespace Tgstation.Server.Host.Controllers if (!(generalConfiguration.ValidInstancePaths? .Select(path => NormalizePath(path)) .Any(path => InstanceIsChildOf(path)) ?? true)) - return BadRequest(new ErrorMessage(ErrorCode.InstanceNotAtWhitelistedPath)); + return BadRequest(new ErrorMessageResponse(ErrorCode.InstanceNotAtWhitelistedPath)); async Task DirExistsAndIsNotEmpty() { @@ -318,13 +331,13 @@ namespace Tgstation.Server.Host.Controllers bool attached = false; if (await ioManager.FileExists(model.Path, cancellationToken).ConfigureAwait(false) || await dirExistsTask.ConfigureAwait(false)) if (!await ioManager.FileExists(ioManager.ConcatPath(model.Path, InstanceAttachFileName), cancellationToken).ConfigureAwait(false)) - return Conflict(new ErrorMessage(ErrorCode.InstanceAtExistingPath)); + return Conflict(new ErrorMessageResponse(ErrorCode.InstanceAtExistingPath)); else attached = true; var newInstance = await CreateDefaultInstance(model, cancellationToken).ConfigureAwait(false); if (newInstance == null) - return Conflict(new ErrorMessage(ErrorCode.NoPortsAvailable)); + return Conflict(new ErrorMessageResponse(ErrorCode.NoPortsAvailable)); DatabaseContext.Instances.Add(newInstance); try @@ -349,7 +362,7 @@ namespace Tgstation.Server.Host.Controllers } catch (IOException e) { - return Conflict(new ErrorMessage(ErrorCode.IOError) + return Conflict(new ErrorMessageResponse(ErrorCode.IOError) { AdditionalData = e.Message }); @@ -372,7 +385,7 @@ namespace Tgstation.Server.Host.Controllers [HttpDelete("{id}")] [TgsAuthorize(InstanceManagerRights.Delete)] [ProducesResponseType(204)] - [ProducesResponseType(typeof(ErrorMessage), 410)] + [ProducesResponseType(typeof(ErrorMessageResponse), 410)] public async Task Delete(long id, CancellationToken cancellationToken) { var originalModel = await DatabaseContext @@ -383,7 +396,7 @@ namespace Tgstation.Server.Host.Controllers if (originalModel == default) return Gone(); if (originalModel.Online.Value) - return Conflict(new ErrorMessage(ErrorCode.InstanceDetachOnline)); + return Conflict(new ErrorMessageResponse(ErrorCode.InstanceDetachOnline)); DatabaseContext.Instances.Remove(originalModel); @@ -414,11 +427,11 @@ namespace Tgstation.Server.Host.Controllers /// The database entity for the requested instance could not be retrieved. The instance was likely detached. [HttpPost] [TgsAuthorize(InstanceManagerRights.Relocate | InstanceManagerRights.Rename | InstanceManagerRights.SetAutoUpdate | InstanceManagerRights.SetConfiguration | InstanceManagerRights.SetOnline | InstanceManagerRights.SetChatBotLimit)] - [ProducesResponseType(typeof(Api.Models.Instance), 200)] - [ProducesResponseType(typeof(Api.Models.Instance), 202)] - [ProducesResponseType(typeof(ErrorMessage), 410)] + [ProducesResponseType(typeof(InstanceResponse), 200)] + [ProducesResponseType(typeof(InstanceResponse), 202)] + [ProducesResponseType(typeof(ErrorMessageResponse), 410)] #pragma warning disable CA1502 // TODO: Decomplexify - public async Task Update([FromBody] Api.Models.Instance model, CancellationToken cancellationToken) + public async Task Update([FromBody] InstanceUpdateRequest model, CancellationToken cancellationToken) { if (model == null) throw new ArgumentNullException(nameof(model)); @@ -433,7 +446,7 @@ namespace Tgstation.Server.Host.Controllers #pragma warning disable CA1310 // Specify StringComparison Where(x => !x.StoppedAt.HasValue && x.Description.StartsWith(MoveInstanceJobPrefix)) #pragma warning restore CA1310 // Specify StringComparison - .Select(x => new Models.Job + .Select(x => new Job { Id = x.Id }).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); @@ -485,11 +498,11 @@ namespace Tgstation.Server.Host.Controllers if (!userRights.HasFlag(InstanceManagerRights.Relocate)) return Forbid(); if (originalModel.Online.Value && model.Online != true) - return Conflict(new ErrorMessage(ErrorCode.InstanceRelocateOnline)); + return Conflict(new ErrorMessageResponse(ErrorCode.InstanceRelocateOnline)); var dirExistsTask = ioManager.DirectoryExists(model.Path, cancellationToken); if (await ioManager.FileExists(model.Path, cancellationToken).ConfigureAwait(false) || await dirExistsTask.ConfigureAwait(false)) - return Conflict(new ErrorMessage(ErrorCode.InstanceAtExistingPath)); + return Conflict(new ErrorMessageResponse(ErrorCode.InstanceAtExistingPath)); originalModelPath = originalModel.Path; originalModel.Path = rawPath; @@ -517,7 +530,7 @@ namespace Tgstation.Server.Host.Controllers .ConfigureAwait(false); if (countOfExistingChatBots > model.ChatBotLimit.Value) - return Conflict(new ErrorMessage(ErrorCode.ChatBotMax)); + return Conflict(new ErrorMessageResponse(ErrorCode.ChatBotMax)); } await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); @@ -556,7 +569,7 @@ namespace Tgstation.Server.Host.Controllers throw; } - var api = (AuthenticationContext.GetRight(RightsType.InstanceManager) & (ulong)InstanceManagerRights.Read) != 0 ? originalModel.ToApi() : new Api.Models.Instance + var api = (AuthenticationContext.GetRight(RightsType.InstanceManager) & (ulong)InstanceManagerRights.Read) != 0 ? originalModel.ToApi() : new InstanceResponse { Id = originalModel.Id }; @@ -564,7 +577,7 @@ namespace Tgstation.Server.Host.Controllers var moving = originalModelPath != null; if (moving) { - var job = new Models.Job + var job = new Job { Description = $"{MoveInstanceJobPrefix}{originalModel.Id} from {originalModelPath} to {rawPath}", Instance = originalModel, @@ -603,7 +616,7 @@ namespace Tgstation.Server.Host.Controllers /// Retrieved s successfully. [HttpGet(Routes.List)] [TgsAuthorize(InstanceManagerRights.List | InstanceManagerRights.Read)] - [ProducesResponseType(typeof(Paginated), 200)] + [ProducesResponseType(typeof(PaginatedResponse), 200)] public async Task List( [FromQuery] int? page, [FromQuery] int? pageSize, @@ -641,7 +654,7 @@ namespace Tgstation.Server.Host.Controllers .ConfigureAwait(false); var needsUpdate = false; - var result = await Paginated( + var result = await Paginated( () => Task.FromResult( new PaginatableResult( GetBaseQuery() @@ -672,8 +685,8 @@ namespace Tgstation.Server.Host.Controllers /// The database entity for the requested instance could not be retrieved. The instance was likely detached. [HttpGet("{id}")] [TgsAuthorize(InstanceManagerRights.List | InstanceManagerRights.Read)] - [ProducesResponseType(typeof(Api.Models.Instance), 200)] - [ProducesResponseType(typeof(ErrorMessage), 410)] + [ProducesResponseType(typeof(InstanceResponse), 200)] + [ProducesResponseType(typeof(ErrorMessageResponse), 410)] public async Task GetId(long id, CancellationToken cancellationToken) { var cantList = !AuthenticationContext.PermissionSet.InstanceManagerRights.Value.HasFlag(InstanceManagerRights.List); @@ -730,7 +743,7 @@ namespace Tgstation.Server.Host.Controllers [HttpPatch("{id}")] [TgsAuthorize(InstanceManagerRights.GrantPermissions)] [ProducesResponseType(204)] - [ProducesResponseType(typeof(ErrorMessage), 410)] + [ProducesResponseType(typeof(ErrorMessageResponse), 410)] public async Task GrantPermissions(long id, CancellationToken cancellationToken) { IQueryable BaseQuery() => DatabaseContext diff --git a/src/Tgstation.Server.Host/Controllers/InstancePermissionSetController.cs b/src/Tgstation.Server.Host/Controllers/InstancePermissionSetController.cs index b89c17380f..2414b8fa50 100644 --- a/src/Tgstation.Server.Host/Controllers/InstancePermissionSetController.cs +++ b/src/Tgstation.Server.Host/Controllers/InstancePermissionSetController.cs @@ -7,6 +7,8 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Api.Rights; using Tgstation.Server.Host.Components; using Tgstation.Server.Host.Database; @@ -42,19 +44,19 @@ namespace Tgstation.Server.Host.Controllers { } /// - /// Create an . + /// Create an . /// - /// The to create. + /// The . /// The for the operation. /// A resulting in the of the request. - /// created successfully. + /// created successfully. /// The does not exist. [HttpPut] [TgsAuthorize(InstancePermissionSetRights.Create)] - [ProducesResponseType(typeof(Api.Models.InstancePermissionSet), 201)] - [ProducesResponseType(typeof(ErrorMessage), 410)] + [ProducesResponseType(typeof(InstancePermissionSetResponse), 201)] + [ProducesResponseType(typeof(ErrorMessageResponse), 410)] #pragma warning disable CA1506 - public async Task Create([FromBody] Api.Models.InstancePermissionSet model, CancellationToken cancellationToken) + public async Task Create([FromBody] InstancePermissionSetRequest model, CancellationToken cancellationToken) { if (model == null) throw new ArgumentNullException(nameof(model)); @@ -87,7 +89,7 @@ namespace Tgstation.Server.Host.Controllers return Forbid(); } - var dbUser = new Models.InstancePermissionSet + var dbUser = new InstancePermissionSet { ByondRights = RightsHelper.Clamp(model.ByondRights ?? ByondRights.None), ChatBotRights = RightsHelper.Clamp(model.ChatBotRights ?? ChatBotRights.None), @@ -97,7 +99,7 @@ namespace Tgstation.Server.Host.Controllers RepositoryRights = RightsHelper.Clamp(model.RepositoryRights ?? RepositoryRights.None), InstancePermissionSetRights = RightsHelper.Clamp(model.InstancePermissionSetRights ?? InstancePermissionSetRights.None), PermissionSetId = model.PermissionSetId, - InstanceId = Instance.Id + InstanceId = Instance.Id.Value, }; DatabaseContext.InstancePermissionSets.Add(dbUser); @@ -108,19 +110,19 @@ namespace Tgstation.Server.Host.Controllers #pragma warning restore CA1506 /// - /// Update the permissions for an . + /// Update the permissions for an . /// - /// The updated . + /// The . /// The for the operation. /// A resulting in the of the request. - /// updated successfully. - /// The requested does not currently exist. + /// updated successfully. + /// The requested does not currently exist. [HttpPost] [TgsAuthorize(InstancePermissionSetRights.Write)] - [ProducesResponseType(typeof(Api.Models.InstancePermissionSet), 200)] - [ProducesResponseType(typeof(ErrorMessage), 410)] + [ProducesResponseType(typeof(InstancePermissionSetResponse), 200)] + [ProducesResponseType(typeof(ErrorMessageResponse), 410)] #pragma warning disable CA1506 // TODO: Decomplexify - public async Task Update([FromBody] Api.Models.InstancePermissionSet model, CancellationToken cancellationToken) + public async Task Update([FromBody] InstancePermissionSetRequest model, CancellationToken cancellationToken) { if (model == null) throw new ArgumentNullException(nameof(model)); @@ -150,37 +152,37 @@ namespace Tgstation.Server.Host.Controllers return Json( showFullPermissionSet ? originalPermissionSet.ToApi() - : new Api.Models.InstancePermissionSet + : new InstancePermissionSetResponse { PermissionSetId = originalPermissionSet.PermissionSetId }); } #pragma warning restore CA1506 /// - /// Read the active . + /// Read the active . /// /// The of the request. - /// retrieved successfully. + /// retrieved successfully. [HttpGet] [TgsAuthorize] - [ProducesResponseType(typeof(Api.Models.InstancePermissionSet), 200)] + [ProducesResponseType(typeof(InstancePermissionSetResponse), 200)] public IActionResult Read() => Json(AuthenticationContext.InstancePermissionSet.ToApi()); /// - /// Lists s for the instance. + /// Lists s for the instance. /// /// The current page. /// The page size. /// The for the operation. /// A resulting in the of the request. - /// Retrieved s successfully. + /// Retrieved s successfully. [HttpGet(Routes.List)] [TgsAuthorize(InstancePermissionSetRights.Read)] - [ProducesResponseType(typeof(Paginated), 200)] + [ProducesResponseType(typeof(PaginatedResponse), 200)] public Task List([FromQuery] int? page, [FromQuery] int? pageSize, CancellationToken cancellationToken) - => Paginated( + => Paginated( () => Task.FromResult( - new PaginatableResult( + new PaginatableResult( DatabaseContext .Instances .AsQueryable() @@ -193,17 +195,17 @@ namespace Tgstation.Server.Host.Controllers cancellationToken); /// - /// Gets a specific . + /// Gets a specific . /// - /// The . + /// The . /// The for the operation. /// A resulting in the of the request. - /// Retrieve successfully. - /// The requested does not currently exist. + /// Retrieve successfully. + /// The requested does not currently exist. [HttpGet("{id}")] [TgsAuthorize(InstancePermissionSetRights.Read)] - [ProducesResponseType(typeof(Api.Models.InstancePermissionSet), 200)] - [ProducesResponseType(typeof(ErrorMessage), 410)] + [ProducesResponseType(typeof(InstancePermissionSetResponse), 200)] + [ProducesResponseType(typeof(ErrorMessageResponse), 410)] public async Task GetId(long id, CancellationToken cancellationToken) { // this functions as userId @@ -221,17 +223,17 @@ namespace Tgstation.Server.Host.Controllers } /// - /// Delete an . + /// Delete an . /// - /// The to delete. + /// The to delete. /// The for the operation. /// A resulting in the of the request. - /// Target deleted. - /// Target or no longer exists. + /// Target deleted. + /// Target or no longer exists. [HttpDelete("{id}")] [TgsAuthorize(InstancePermissionSetRights.Write)] [ProducesResponseType(204)] - [ProducesResponseType(typeof(ErrorMessage), 410)] + [ProducesResponseType(typeof(ErrorMessageResponse), 410)] public async Task Delete(long id, CancellationToken cancellationToken) { var numDeleted = await DatabaseContext diff --git a/src/Tgstation.Server.Host/Controllers/InstanceRequiredController.cs b/src/Tgstation.Server.Host/Controllers/InstanceRequiredController.cs index 1005c5fcb2..7ed3b9ab75 100644 --- a/src/Tgstation.Server.Host/Controllers/InstanceRequiredController.cs +++ b/src/Tgstation.Server.Host/Controllers/InstanceRequiredController.cs @@ -5,6 +5,7 @@ using System; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Host.Components; using Tgstation.Server.Host.Database; using Tgstation.Server.Host.Security; @@ -46,7 +47,7 @@ namespace Tgstation.Server.Host.Controllers protected override async Task ValidateRequest(CancellationToken cancellationToken) { if (!ApiHeaders.InstanceId.HasValue) - return BadRequest(new ErrorMessage(ErrorCode.InstanceHeaderRequired)); + return BadRequest(new ErrorMessageResponse(ErrorCode.InstanceHeaderRequired)); if (AuthenticationContext.InstancePermissionSet == null) return Forbid(); @@ -55,7 +56,7 @@ namespace Tgstation.Server.Host.Controllers using var instanceReferenceCheck = instanceManager.GetInstanceReference(Instance); if (instanceReferenceCheck == null) - return Conflict(new ErrorMessage(ErrorCode.InstanceOffline)); + return Conflict(new ErrorMessageResponse(ErrorCode.InstanceOffline)); return null; } @@ -74,7 +75,7 @@ namespace Tgstation.Server.Host.Controllers using (LogContext.PushProperty("InstanceReference", instanceReference.Uid)) { if (instanceReference == null) - return Conflict(new ErrorMessage(ErrorCode.InstanceOffline)); + return Conflict(new ErrorMessageResponse(ErrorCode.InstanceOffline)); return await action(instanceReference).ConfigureAwait(false); } } @@ -84,9 +85,9 @@ namespace Tgstation.Server.Host.Controllers /// /// The to use. /// The to use. - /// The to check. + /// The to check. /// if an unsaved DB update was made, otherwise. - public static bool ValidateInstanceOnlineStatus(IInstanceManager instanceManager, ILogger logger, Models.Instance metadata) + public static bool ValidateInstanceOnlineStatus(IInstanceManager instanceManager, ILogger logger, Api.Models.Instance metadata) { if (instanceManager == null) throw new ArgumentNullException(nameof(instanceManager)); diff --git a/src/Tgstation.Server.Host/Controllers/JobController.cs b/src/Tgstation.Server.Host/Controllers/JobController.cs index 62f657d9e3..95b7b95634 100644 --- a/src/Tgstation.Server.Host/Controllers/JobController.cs +++ b/src/Tgstation.Server.Host/Controllers/JobController.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Host.Components; using Tgstation.Server.Host.Database; using Tgstation.Server.Host.Jobs; @@ -16,7 +17,7 @@ using Tgstation.Server.Host.Security; namespace Tgstation.Server.Host.Controllers { /// - /// for s + /// for s /// [Route(Routes.Jobs)] public sealed class JobController : InstanceRequiredController @@ -50,20 +51,20 @@ namespace Tgstation.Server.Host.Controllers } /// - /// Get active s for the instance. + /// Get active s for the instance. /// /// The current page. /// The page size. /// The for the operation. /// A resulting in the of the request. - /// Retrieved active s successfully. + /// Retrieved active s successfully. [HttpGet] [TgsAuthorize] - [ProducesResponseType(typeof(Paginated), 200)] + [ProducesResponseType(typeof(PaginatedResponse), 200)] public Task Read([FromQuery] int? page, [FromQuery] int? pageSize, CancellationToken cancellationToken) - => Paginated( + => Paginated( () => Task.FromResult( - new PaginatableResult( + new PaginatableResult( DatabaseContext .Jobs .AsQueryable() @@ -76,20 +77,20 @@ namespace Tgstation.Server.Host.Controllers cancellationToken); /// - /// List all s for the instance in reverse creation order. + /// List all for the instance in reverse creation order. /// /// The current page. /// The page size. /// The for the operation. /// A resulting in the of the request. - /// Retrieved s successfully. + /// Retrieved s successfully. [HttpGet(Routes.List)] [TgsAuthorize] - [ProducesResponseType(typeof(Paginated), 200)] + [ProducesResponseType(typeof(PaginatedResponse), 200)] public Task List([FromQuery] int? page, [FromQuery] int? pageSize, CancellationToken cancellationToken) - => Paginated( + => Paginated( () => Task.FromResult( - new PaginatableResult( + new PaginatableResult( DatabaseContext .Jobs .AsQueryable() @@ -102,18 +103,18 @@ namespace Tgstation.Server.Host.Controllers cancellationToken); /// - /// Cancel a running . + /// Cancel a running . /// - /// The of the to cancel. + /// The of the to cancel. /// The for the operation. /// A resulting in the of the request. - /// cancellation requested successfully. - /// does not exist in this instance. - /// could not be found in the job manager. Has it already completed? + /// cancellation requested successfully. + /// does not exist in this instance. + /// could not be found in the job manager. Has it already completed? [HttpDelete("{id}")] [TgsAuthorize] - [ProducesResponseType(typeof(Api.Models.Job), 202)] - [ProducesResponseType(typeof(ErrorMessage), 404)] + [ProducesResponseType(typeof(JobResponse), 202)] + [ProducesResponseType(typeof(ErrorMessageResponse), 404)] public async Task Delete(long id, CancellationToken cancellationToken) { // don't care if an instance post or not at this point @@ -127,7 +128,7 @@ namespace Tgstation.Server.Host.Controllers return NotFound(); if (job.StoppedAt != null) - return Conflict(new ErrorMessage(ErrorCode.JobStopped)); + return Conflict(new ErrorMessageResponse(ErrorCode.JobStopped)); if (job.CancelRight.HasValue && job.CancelRightsType.HasValue && (AuthenticationContext.GetRight(job.CancelRightsType.Value) & job.CancelRight.Value) == 0) return Forbid(); @@ -137,17 +138,17 @@ namespace Tgstation.Server.Host.Controllers } /// - /// Get a specific . + /// Get a specific . /// - /// The of the to retrieve. + /// The of the to retrieve. /// The for the operation. /// A resulting in the of the request. - /// Retrieved successfully. - /// does not exist in this instance. + /// Retrieved successfully. + /// does not exist in this instance. [HttpGet("{id}")] [TgsAuthorize] - [ProducesResponseType(typeof(Api.Models.Job), 200)] - [ProducesResponseType(typeof(ErrorMessage), 404)] + [ProducesResponseType(typeof(JobResponse), 200)] + [ProducesResponseType(typeof(ErrorMessageResponse), 404)] public async Task GetId(long id, CancellationToken cancellationToken) { var job = await DatabaseContext diff --git a/src/Tgstation.Server.Host/Controllers/RepositoryController.cs b/src/Tgstation.Server.Host/Controllers/RepositoryController.cs index b342d9e1ee..9e3b9faeab 100644 --- a/src/Tgstation.Server.Host/Controllers/RepositoryController.cs +++ b/src/Tgstation.Server.Host/Controllers/RepositoryController.cs @@ -11,6 +11,8 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Api.Rights; using Tgstation.Server.Host.Components; using Tgstation.Server.Host.Core; @@ -22,7 +24,7 @@ using Tgstation.Server.Host.Security; namespace Tgstation.Server.Host.Controllers { /// - /// for managing the s + /// for managing the git repository. /// [Route(Routes.Repository)] #pragma warning disable CA1506 // TODO: Decomplexify @@ -92,7 +94,7 @@ namespace Tgstation.Server.Host.Controllers Instance = instance, CommitSha = repoSha, Timestamp = await repository.TimestampCommit(repoSha, cancellationToken).ConfigureAwait(false), - CompileJobs = new List(), + CompileJobs = new List(), ActiveTestMerges = new List() // non null vals for api returns }; @@ -111,7 +113,7 @@ namespace Tgstation.Server.Host.Controllers return needsDbUpdate; } - async Task PopulateApi(Repository model, Components.Repository.IRepository repository, IDatabaseContext databaseContext, Models.Instance instance, CancellationToken cancellationToken) + async Task PopulateApi(RepositoryResponse model, Components.Repository.IRepository repository, IDatabaseContext databaseContext, Models.Instance instance, CancellationToken cancellationToken) { model.RemoteGitProvider = repository.RemoteGitProvider; model.RemoteRepositoryOwner = repository.RemoteRepositoryOwner; @@ -130,22 +132,22 @@ namespace Tgstation.Server.Host.Controllers /// /// Begin cloning the repository if it doesn't exist. /// - /// Initial settings. + /// The . /// The for the operation. /// A resulting in the of the request. - /// The was created successfully and the to clone it has begun. + /// The repository was created successfully and the to clone it has begun. /// The database entity for the requested instance could not be retrieved. The instance was likely detached. [HttpPut] [TgsAuthorize(RepositoryRights.SetOrigin)] - [ProducesResponseType(typeof(Repository), 201)] - [ProducesResponseType(typeof(ErrorMessage), 410)] - public async Task Create([FromBody] Repository model, CancellationToken cancellationToken) + [ProducesResponseType(typeof(RepositoryResponse), 201)] + [ProducesResponseType(typeof(ErrorMessageResponse), 410)] + public async Task Create([FromBody] RepositoryCreateRequest model, CancellationToken cancellationToken) { if (model == null) throw new ArgumentNullException(nameof(model)); if (model.Origin == null) - return BadRequest(ErrorCode.RepoMissingOrigin); + return BadRequest(ErrorCode.ModelValidationFailure); if (model.AccessUser == null ^ model.AccessToken == null) return BadRequest(ErrorCode.RepoMismatchUserAndAccessToken); @@ -171,18 +173,18 @@ namespace Tgstation.Server.Host.Controllers var repoManager = instance.RepositoryManager; if (repoManager.CloneInProgress) - return Conflict(new ErrorMessage(ErrorCode.RepoCloning)); + return Conflict(new ErrorMessageResponse(ErrorCode.RepoCloning)); if (repoManager.InUse) - return Conflict(new ErrorMessage(ErrorCode.RepoBusy)); + return Conflict(new ErrorMessageResponse(ErrorCode.RepoBusy)); using var repo = await repoManager.LoadRepository(cancellationToken).ConfigureAwait(false); // clone conflict if (repo != null) - return Conflict(new ErrorMessage(ErrorCode.RepoExists)); + return Conflict(new ErrorMessageResponse(ErrorCode.RepoExists)); - var job = new Models.Job + var job = new Job { Description = String.Format(CultureInfo.InvariantCulture, "Clone branch {1} of repository {0}", origin, cloneBranch ?? "master"), StartedBy = AuthenticationContext.User, @@ -229,7 +231,7 @@ namespace Tgstation.Server.Host.Controllers } /// - /// Delete the . + /// Delete the repository. /// /// The for the operation /// A resulting in the of the operation @@ -237,8 +239,8 @@ namespace Tgstation.Server.Host.Controllers /// The database entity for the requested instance could not be retrieved. The instance was likely detached. [HttpDelete] [TgsAuthorize(RepositoryRights.Delete)] - [ProducesResponseType(typeof(Repository), 202)] - [ProducesResponseType(typeof(ErrorMessage), 410)] + [ProducesResponseType(typeof(RepositoryResponse), 202)] + [ProducesResponseType(typeof(ErrorMessageResponse), 410)] public async Task Delete(CancellationToken cancellationToken) { var currentModel = await DatabaseContext @@ -256,9 +258,9 @@ namespace Tgstation.Server.Host.Controllers await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); - Logger.LogInformation("Instance {0} repository delete initiated by user {1}", Instance.Id, AuthenticationContext.User.Id); + Logger.LogInformation("Instance {0} repository delete initiated by user {1}", Instance.Id, AuthenticationContext.User.Id.Value); - var job = new Models.Job + var job = new Job { Description = "Delete repository", StartedBy = AuthenticationContext.User, @@ -275,18 +277,18 @@ namespace Tgstation.Server.Host.Controllers } /// - /// Get status. + /// Get the repository's status. /// /// The for the operation. /// A resulting in the of the operation. - /// Retrieved the settings successfully. - /// Retrieved the settings successfully, though they did not previously exist. + /// Retrieved the repository settings successfully. + /// Retrieved the repository settings successfully, though they did not previously exist. /// The database entity for the requested instance could not be retrieved. The instance was likely detached. [HttpGet] [TgsAuthorize(RepositoryRights.Read)] - [ProducesResponseType(typeof(Repository), 200)] - [ProducesResponseType(typeof(Repository), 201)] - [ProducesResponseType(typeof(ErrorMessage), 410)] + [ProducesResponseType(typeof(RepositoryResponse), 200)] + [ProducesResponseType(typeof(RepositoryResponse), 201)] + [ProducesResponseType(typeof(RepositoryResponse), 410)] public async Task Read(CancellationToken cancellationToken) { var currentModel = await DatabaseContext @@ -307,10 +309,10 @@ namespace Tgstation.Server.Host.Controllers var repoManager = instance.RepositoryManager; if (repoManager.CloneInProgress) - return Conflict(new ErrorMessage(ErrorCode.RepoCloning)); + return Conflict(new ErrorMessageResponse(ErrorCode.RepoCloning)); if (repoManager.InUse) - return Conflict(new ErrorMessage(ErrorCode.RepoBusy)); + return Conflict(new ErrorMessageResponse(ErrorCode.RepoBusy)); using var repo = await repoManager.LoadRepository(cancellationToken).ConfigureAwait(false); if (repo != null && await PopulateApi(api, repo, DatabaseContext, Instance, cancellationToken).ConfigureAwait(false)) @@ -326,42 +328,42 @@ namespace Tgstation.Server.Host.Controllers } /// - /// Perform updats to the . + /// Perform updates to the repository. /// - /// The updated . + /// The . /// The for the operation. /// A resulting in the of the operation. - /// Updated the settings successfully. - /// Updated the settings successfully and a was created to make the requested git changes. + /// Updated the repository settings successfully. + /// Updated the repository settings successfully and a was created to make the requested git changes. /// The database entity for the requested instance could not be retrieved. The instance was likely detached. [HttpPost] [TgsAuthorize(RepositoryRights.ChangeAutoUpdateSettings | RepositoryRights.ChangeCommitter | RepositoryRights.ChangeCredentials | RepositoryRights.ChangeTestMergeCommits | RepositoryRights.MergePullRequest | RepositoryRights.SetReference | RepositoryRights.SetSha | RepositoryRights.UpdateBranch)] - [ProducesResponseType(typeof(Repository), 200)] - [ProducesResponseType(typeof(Repository), 202)] - [ProducesResponseType(typeof(ErrorMessage), 410)] + [ProducesResponseType(typeof(RepositoryResponse), 200)] + [ProducesResponseType(typeof(RepositoryResponse), 202)] + [ProducesResponseType(typeof(ErrorMessageResponse), 410)] #pragma warning disable CA1502, CA1505 // TODO: Decomplexify - public async Task Update([FromBody] Repository model, CancellationToken cancellationToken) + public async Task Update([FromBody] RepositoryUpdateRequest model, CancellationToken cancellationToken) { if (model == null) throw new ArgumentNullException(nameof(model)); if (model.AccessUser == null ^ model.AccessToken == null) - return BadRequest(new ErrorMessage(ErrorCode.RepoMismatchUserAndAccessToken)); + return BadRequest(new ErrorMessageResponse(ErrorCode.RepoMismatchUserAndAccessToken)); if (model.CheckoutSha != null && model.Reference != null) - return BadRequest(new ErrorMessage(ErrorCode.RepoMismatchShaAndReference)); + return BadRequest(new ErrorMessageResponse(ErrorCode.RepoMismatchShaAndReference)); if (model.CheckoutSha != null && model.UpdateFromOrigin == true) - return BadRequest(new ErrorMessage(ErrorCode.RepoMismatchShaAndUpdate)); + return BadRequest(new ErrorMessageResponse(ErrorCode.RepoMismatchShaAndUpdate)); if (model.NewTestMerges?.Any(x => model.NewTestMerges.Any(y => x != y && x.Number == y.Number)) == true) - return BadRequest(new ErrorMessage(ErrorCode.RepoDuplicateTestMerge)); + return BadRequest(new ErrorMessageResponse(ErrorCode.RepoDuplicateTestMerge)); if (model.CommitterName?.Length == 0) - return BadRequest(new ErrorMessage(ErrorCode.RepoWhitespaceCommitterName)); + return BadRequest(new ErrorMessageResponse(ErrorCode.RepoWhitespaceCommitterName)); if (model.CommitterEmail?.Length == 0) - return BadRequest(new ErrorMessage(ErrorCode.RepoWhitespaceCommitterEmail)); + return BadRequest(new ErrorMessageResponse(ErrorCode.RepoWhitespaceCommitterEmail)); var newTestMerges = model.NewTestMerges != null && model.NewTestMerges.Count > 0; var userRights = (RepositoryRights)AuthenticationContext.GetRight(RightsType.Repository); @@ -415,7 +417,7 @@ namespace Tgstation.Server.Host.Controllers var canRead = userRights.HasFlag(RepositoryRights.Read); - var api = canRead ? currentModel.ToApi() : new Repository(); + var api = canRead ? currentModel.ToApi() : new RepositoryResponse(); if (canRead) { var earlyOut = await WithComponentInstance( @@ -423,19 +425,16 @@ namespace Tgstation.Server.Host.Controllers { var repoManager = instance.RepositoryManager; if (repoManager.CloneInProgress) - return Conflict(new ErrorMessage(ErrorCode.RepoCloning)); + return Conflict(new ErrorMessageResponse(ErrorCode.RepoCloning)); if (repoManager.InUse) - return Conflict(new ErrorMessage(ErrorCode.RepoBusy)); + return Conflict(new ErrorMessageResponse(ErrorCode.RepoBusy)); using var repo = await repoManager.LoadRepository(cancellationToken).ConfigureAwait(false); if (repo == null) - return Conflict(new ErrorMessage(ErrorCode.RepoMissing)); + return Conflict(new ErrorMessageResponse(ErrorCode.RepoMissing)); await PopulateApi(api, repo, DatabaseContext, Instance, cancellationToken).ConfigureAwait(false); - if (model.Origin != null && model.Origin != repo.Origin) - return BadRequest(new ErrorMessage(ErrorCode.RepoCantChangeOrigin)); - return null; }) .ConfigureAwait(false); @@ -537,7 +536,7 @@ namespace Tgstation.Server.Host.Controllers var mergedBy = databaseContext.Users.Local.FirstOrDefault(x => x.Id == AuthenticationContext.User.Id); if (mergedBy == default) { - mergedBy = new Models.User + mergedBy = new User { Id = AuthenticationContext.User.Id }; @@ -884,7 +883,7 @@ namespace Tgstation.Server.Host.Controllers } } - var job = new Models.Job + var job = new Job { Description = description, StartedBy = AuthenticationContext.User, diff --git a/src/Tgstation.Server.Host/Controllers/SwarmController.cs b/src/Tgstation.Server.Host/Controllers/SwarmController.cs index 473df47a38..18659b6972 100644 --- a/src/Tgstation.Server.Host/Controllers/SwarmController.cs +++ b/src/Tgstation.Server.Host/Controllers/SwarmController.cs @@ -21,6 +21,7 @@ namespace Tgstation.Server.Host.Controllers [Route(SwarmConstants.ControllerRoute)] [Produces(MediaTypeNames.Application.Json)] [ApiController] + [ApiExplorerSettings(IgnoreApi = true)] public sealed class SwarmController : Controller { /// diff --git a/src/Tgstation.Server.Host/Controllers/TransferController.cs b/src/Tgstation.Server.Host/Controllers/TransferController.cs index 26521a7159..edaed2ac8a 100644 --- a/src/Tgstation.Server.Host/Controllers/TransferController.cs +++ b/src/Tgstation.Server.Host/Controllers/TransferController.cs @@ -11,6 +11,7 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Host.Database; using Tgstation.Server.Host.Security; using Tgstation.Server.Host.Transfer; @@ -53,7 +54,7 @@ namespace Tgstation.Server.Host.Controllers /// /// Downloads a file with a given . /// - /// The for the download. + /// The for the download. /// The for the operation. /// A resulting in the of the method. /// Started streaming download successfully. @@ -61,20 +62,20 @@ namespace Tgstation.Server.Host.Controllers [TgsAuthorize] [HttpGet] [ProducesResponseType(200, Type = typeof(LimitedFileStreamResult))] - [ProducesResponseType(410, Type = typeof(ErrorMessage))] + [ProducesResponseType(410, Type = typeof(ErrorMessageResponse))] public async Task Download([Required, FromQuery] string ticket, CancellationToken cancellationToken) { if (ticket == null) - return BadRequest(new ErrorMessage(ErrorCode.ModelValidationFailure)); + return BadRequest(new ErrorMessageResponse(ErrorCode.ModelValidationFailure)); var streamAccept = new MediaTypeHeaderValue(MediaTypeNames.Application.Octet); if (!Request.GetTypedHeaders().Accept.Any(x => streamAccept.IsSubsetOf(x))) - return StatusCode(HttpStatusCode.NotAcceptable, new ErrorMessage(ErrorCode.BadHeaders) + return StatusCode(HttpStatusCode.NotAcceptable, new ErrorMessageResponse(ErrorCode.BadHeaders) { AdditionalData = $"File downloads must accept both {MediaTypeNames.Application.Octet} and {MediaTypeNames.Application.Json}!" }); - var fileTicketResult = new FileTicketResult + var fileTicketResult = new FileTicketResponse { FileTicket = ticket }; @@ -101,7 +102,7 @@ namespace Tgstation.Server.Host.Controllers /// /// Uploads a file with a given . /// - /// The for the upload. + /// The for the upload. /// The for the operation. /// A resulting in the of the method. /// Uploaded file successfully. @@ -110,13 +111,13 @@ namespace Tgstation.Server.Host.Controllers [TgsAuthorize] [HttpPut] [ProducesResponseType(204)] - [ProducesResponseType(410, Type = typeof(ErrorMessage))] + [ProducesResponseType(410, Type = typeof(ErrorMessageResponse))] public async Task Upload([Required, FromQuery] string ticket, CancellationToken cancellationToken) { if (ticket == null) - return BadRequest(new ErrorMessage(ErrorCode.ModelValidationFailure)); + return BadRequest(new ErrorMessageResponse(ErrorCode.ModelValidationFailure)); - var fileTicketResult = new FileTicketResult + var fileTicketResult = new FileTicketResponse { FileTicket = ticket }; diff --git a/src/Tgstation.Server.Host/Controllers/UserController.cs b/src/Tgstation.Server.Host/Controllers/UserController.cs index 1b00ef4422..00dcc3667b 100644 --- a/src/Tgstation.Server.Host/Controllers/UserController.cs +++ b/src/Tgstation.Server.Host/Controllers/UserController.cs @@ -9,6 +9,8 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Api.Rights; using Tgstation.Server.Host.Configuration; using Tgstation.Server.Host.Database; @@ -66,20 +68,20 @@ namespace Tgstation.Server.Host.Controllers } /// - /// Check if a given has a valid specified. + /// Check if a given has a valid specified. /// - /// The to check. - /// If this is a new . + /// The to check. + /// If this is a new . /// if is valid, a otherwise. - BadRequestObjectResult CheckValidName(UserUpdate model, bool newUser) + BadRequestObjectResult CheckValidName(UserUpdateRequest model, bool newUser) { var userInvalidWithNullName = newUser && model.Name == null && model.SystemIdentifier == null; if (userInvalidWithNullName || (model.Name != null && String.IsNullOrWhiteSpace(model.Name))) - return BadRequest(new ErrorMessage(ErrorCode.UserMissingName)); + return BadRequest(new ErrorMessageResponse(ErrorCode.UserMissingName)); model.Name = model.Name?.Trim(); if (model.Name != null && model.Name.Contains(':', StringComparison.InvariantCulture)) - return BadRequest(new ErrorMessage(ErrorCode.UserColonInName)); + return BadRequest(new ErrorMessageResponse(ErrorCode.UserColonInName)); return null; } @@ -88,13 +90,13 @@ namespace Tgstation.Server.Host.Controllers /// /// The user to update. /// The new password. - /// If this is for a new . + /// If this is for a new . /// on success, if is too short. - BadRequestObjectResult TrySetPassword(Models.User dbUser, string newPassword, bool newUser) + BadRequestObjectResult TrySetPassword(User dbUser, string newPassword, bool newUser) { newPassword ??= String.Empty; if (newPassword.Length < generalConfiguration.MinimumPasswordLength) - return BadRequest(new ErrorMessage(ErrorCode.UserPasswordLength) + return BadRequest(new ErrorMessageResponse(ErrorCode.UserPasswordLength) { AdditionalData = $"Required password length: {generalConfiguration.MinimumPasswordLength}" }); @@ -103,38 +105,38 @@ namespace Tgstation.Server.Host.Controllers } /// - /// Create a new . + /// Create a new . /// - /// The to create. + /// The . /// The for the operation. /// A resulting in the of the operation. - /// created successfully. + /// created successfully. /// The requested system identifier could not be found. [HttpPut] [TgsAuthorize(AdministrationRights.WriteUsers)] - [ProducesResponseType(typeof(Api.Models.User), 201)] + [ProducesResponseType(typeof(UserResponse), 201)] #pragma warning disable CA1502, CA1506 - public async Task Create([FromBody] UserUpdate model, CancellationToken cancellationToken) + public async Task Create([FromBody] UserCreateRequest model, CancellationToken cancellationToken) { if (model == null) throw new ArgumentNullException(nameof(model)); if (model.OAuthConnections?.Any(x => x == null) == true) - return BadRequest(new ErrorMessage(ErrorCode.ModelValidationFailure)); + return BadRequest(new ErrorMessageResponse(ErrorCode.ModelValidationFailure)); if ((model.Password != null && model.SystemIdentifier != null) || (model.Password == null && model.SystemIdentifier == null && model.OAuthConnections?.Any() != true)) - return BadRequest(new ErrorMessage(ErrorCode.UserMismatchPasswordSid)); + return BadRequest(new ErrorMessageResponse(ErrorCode.UserMismatchPasswordSid)); if (model.Group != null && model.PermissionSet != null) - return BadRequest(new ErrorMessage(ErrorCode.UserGroupAndPermissionSet)); + return BadRequest(new ErrorMessageResponse(ErrorCode.UserGroupAndPermissionSet)); model.Name = model.Name?.Trim(); if (model.Name?.Length == 0) model.Name = null; if (!(model.Name == null ^ model.SystemIdentifier == null)) - return BadRequest(new ErrorMessage(ErrorCode.UserMismatchNameSid)); + return BadRequest(new ErrorMessageResponse(ErrorCode.UserMismatchNameSid)); var fail = CheckValidName(model, true); if (fail != null) @@ -146,7 +148,7 @@ namespace Tgstation.Server.Host.Controllers .CountAsync(cancellationToken) .ConfigureAwait(false); if (totalUsers >= generalConfiguration.UserLimit) - return Conflict(new ErrorMessage(ErrorCode.UserLimitReached)); + return Conflict(new ErrorMessageResponse(ErrorCode.UserLimitReached)); var dbUser = await CreateNewUserFromModel(model, cancellationToken).ConfigureAwait(false); if (dbUser == null) @@ -180,36 +182,38 @@ namespace Tgstation.Server.Host.Controllers Logger.LogInformation("Created new user {0} ({1})", dbUser.Name, dbUser.Id); - return Created(dbUser.ToApi(true)); + return Created(dbUser.ToApi()); } #pragma warning restore CA1502, CA1506 /// - /// Update a . + /// Update a . /// - /// The to update. + /// The to update. /// The for the operation. /// A resulting in the of the operation. - /// updated successfully. - /// Requested does not exist. - /// Requested does not exist. + /// updated successfully. + /// updated successfully. Not returned due to lack of permissions. + /// Requested does not exist. + /// Requested does not exist. [HttpPost] [TgsAuthorize(AdministrationRights.WriteUsers | AdministrationRights.EditOwnPassword | AdministrationRights.EditOwnOAuthConnections)] - [ProducesResponseType(typeof(Api.Models.User), 200)] - [ProducesResponseType(typeof(ErrorMessage), 404)] - [ProducesResponseType(typeof(ErrorMessage), 410)] + [ProducesResponseType(typeof(UserResponse), 200)] + [ProducesResponseType(204)] + [ProducesResponseType(typeof(ErrorMessageResponse), 404)] + [ProducesResponseType(typeof(ErrorMessageResponse), 410)] #pragma warning disable CA1502 // TODO: Decomplexify #pragma warning disable CA1506 - public async Task Update([FromBody] UserUpdate model, CancellationToken cancellationToken) + public async Task Update([FromBody] UserUpdateRequest model, CancellationToken cancellationToken) { if (model == null) throw new ArgumentNullException(nameof(model)); if (!model.Id.HasValue || model.OAuthConnections?.Any(x => x == null) == true) - return BadRequest(new ErrorMessage(ErrorCode.ModelValidationFailure)); + return BadRequest(new ErrorMessageResponse(ErrorCode.ModelValidationFailure)); if (model.Group != null && model.PermissionSet != null) - return BadRequest(new ErrorMessage(ErrorCode.UserGroupAndPermissionSet)); + return BadRequest(new ErrorMessageResponse(ErrorCode.UserGroupAndPermissionSet)); var callerAdministrationRights = (AdministrationRights)AuthenticationContext.GetRight(RightsType.Administration); var canEditAllUsers = callerAdministrationRights.HasFlag(AdministrationRights.WriteUsers); @@ -248,7 +252,7 @@ namespace Tgstation.Server.Host.Controllers return Forbid(); if (model.SystemIdentifier != null && model.SystemIdentifier != originalUser.SystemIdentifier) - return BadRequest(new ErrorMessage(ErrorCode.UserSidChange)); + return BadRequest(new ErrorMessageResponse(ErrorCode.UserSidChange)); if (model.Password != null) { @@ -258,7 +262,7 @@ namespace Tgstation.Server.Host.Controllers } if (model.Name != null && Models.User.CanonicalizeName(model.Name) != originalUser.CanonicalName) - return BadRequest(new ErrorMessage(ErrorCode.UserNameChange)); + return BadRequest(new ErrorMessageResponse(ErrorCode.UserNameChange)); if (model.Enabled.HasValue) { @@ -272,11 +276,11 @@ namespace Tgstation.Server.Host.Controllers && (model.OAuthConnections.Count != originalUser.OAuthConnections.Count || !model.OAuthConnections.All(x => originalUser.OAuthConnections.Any(y => y.Provider == x.Provider && y.ExternalUserId == x.ExternalUserId)))) { - if (originalUser.CanonicalName == Models.User.CanonicalizeName(Api.Models.User.AdminName)) - return BadRequest(new ErrorMessage(ErrorCode.AdminUserCannotOAuth)); + if (originalUser.CanonicalName == Models.User.CanonicalizeName(DefaultCredentials.AdminUserName)) + return BadRequest(new ErrorMessageResponse(ErrorCode.AdminUserCannotOAuth)); if (model.OAuthConnections.Count == 0 && originalUser.PasswordHash == null && originalUser.SystemIdentifier == null) - return BadRequest(new ErrorMessage(ErrorCode.CannotRemoveLastAuthenticationOption)); + return BadRequest(new ErrorMessageResponse(ErrorCode.CannotRemoveLastAuthenticationOption)); originalUser.OAuthConnections.Clear(); foreach (var updatedConnection in model.OAuthConnections) @@ -334,43 +338,40 @@ namespace Tgstation.Server.Host.Controllers Logger.LogInformation("Updated user {0} ({1})", originalUser.Name, originalUser.Id); // return id only if not a self update and cannot read users - return Json( - AuthenticationContext.User.Id == originalUser.Id - || callerAdministrationRights.HasFlag(AdministrationRights.ReadUsers) - ? originalUser.ToApi(true) - : new Api.Models.User - { - Id = originalUser.Id - }); + var canReadBack = AuthenticationContext.User.Id == originalUser.Id + || callerAdministrationRights.HasFlag(AdministrationRights.ReadUsers); + return canReadBack + ? (IActionResult)Json(originalUser.ToApi()) + : NoContent(); } #pragma warning restore CA1506 #pragma warning restore CA1502 /// - /// Get information about the current . + /// Get information about the current . /// /// The of the operation. - /// The was retrieved successfully. + /// The was retrieved successfully. [HttpGet] [TgsAuthorize] - [ProducesResponseType(typeof(Api.Models.User), 200)] - public IActionResult Read() => Json(AuthenticationContext.User.ToApi(true)); + [ProducesResponseType(typeof(UserResponse), 200)] + public IActionResult Read() => Json(AuthenticationContext.User.ToApi()); /// - /// List all s in the server. + /// List all s in the server. /// /// The current page. /// The page size. /// The for the operation. /// A resulting in the of the operation. - /// Retrieved s successfully. + /// Retrieved s successfully. [HttpGet(Routes.List)] [TgsAuthorize(AdministrationRights.ReadUsers)] - [ProducesResponseType(typeof(Paginated), 200)] + [ProducesResponseType(typeof(PaginatedResponse), 200)] public Task List([FromQuery] int? page, [FromQuery] int? pageSize, CancellationToken cancellationToken) - => Paginated( + => Paginated( () => Task.FromResult( - new PaginatableResult( + new PaginatableResult( DatabaseContext .Users .AsQueryable() @@ -387,17 +388,17 @@ namespace Tgstation.Server.Host.Controllers cancellationToken); /// - /// Get a specific . + /// Get a specific . /// - /// The to retrieve. + /// The to retrieve. /// The for the operation. /// A resulting in the of the operation. - /// The was retrieved successfully. - /// The does not exist. + /// The was retrieved successfully. + /// The does not exist. [HttpGet("{id}")] [TgsAuthorize] - [ProducesResponseType(typeof(Api.Models.User), 200)] - [ProducesResponseType(typeof(ErrorMessage), 404)] + [ProducesResponseType(typeof(UserResponse), 200)] + [ProducesResponseType(typeof(ErrorMessageResponse), 404)] public async Task GetId(long id, CancellationToken cancellationToken) { if (id == AuthenticationContext.User.Id) @@ -421,19 +422,19 @@ namespace Tgstation.Server.Host.Controllers if (user.CanonicalName == Models.User.CanonicalizeName(Models.User.TgsSystemUserName)) return Forbid(); - return Json(user.ToApi(true)); + return Json(user.ToApi()); } /// /// Creates a new from a given . /// - /// The to use as a template. + /// The to use as a template. /// The for the operation. /// A resulting in a new on success, if the requested did not exist. - async Task CreateNewUserFromModel(Api.Models.User model, CancellationToken cancellationToken) + async Task CreateNewUserFromModel(Api.Models.Internal.UserApiBase model, CancellationToken cancellationToken) { Models.PermissionSet permissionSet = null; - Models.UserGroup group = null; + UserGroup group = null; if (model.Group != null) group = await DatabaseContext .Groups @@ -449,7 +450,7 @@ namespace Tgstation.Server.Host.Controllers InstanceManagerRights = model.PermissionSet?.InstanceManagerRights ?? InstanceManagerRights.None, }; - return new Models.User + return new User { CreatedAt = DateTimeOffset.UtcNow, CreatedBy = AuthenticationContext.User, diff --git a/src/Tgstation.Server.Host/Controllers/UserGroupController.cs b/src/Tgstation.Server.Host/Controllers/UserGroupController.cs index a01c97be7f..d2e3cf8c7e 100644 --- a/src/Tgstation.Server.Host/Controllers/UserGroupController.cs +++ b/src/Tgstation.Server.Host/Controllers/UserGroupController.cs @@ -8,16 +8,19 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Api.Rights; using Tgstation.Server.Host.Configuration; using Tgstation.Server.Host.Database; +using Tgstation.Server.Host.Models; using Tgstation.Server.Host.Security; using Z.EntityFramework.Plus; namespace Tgstation.Server.Host.Controllers { /// - /// for managing s. + /// for managing s. /// [Route(Routes.UserGroup)] public class UserGroupController : ApiController @@ -51,20 +54,20 @@ namespace Tgstation.Server.Host.Controllers /// /// Create a new . /// - /// The to create. + /// The . /// The for the operation. /// A resulting in the of the operation. /// created successfully. [HttpPut] [TgsAuthorize(AdministrationRights.WriteUsers)] - [ProducesResponseType(typeof(UserGroup), 201)] - public async Task Create([FromBody] UserGroup model, CancellationToken cancellationToken) + [ProducesResponseType(typeof(UserGroupResponse), 201)] + public async Task Create([FromBody] UserGroupCreateRequest model, CancellationToken cancellationToken) { if (model == null) throw new ArgumentNullException(nameof(model)); if (model.Name == null) - return BadRequest(new ErrorMessage(ErrorCode.ModelValidationFailure)); + return BadRequest(new ErrorMessageResponse(ErrorCode.ModelValidationFailure)); var totalGroups = await DatabaseContext .Groups @@ -72,7 +75,7 @@ namespace Tgstation.Server.Host.Controllers .CountAsync(cancellationToken) .ConfigureAwait(false); if (totalGroups >= generalConfiguration.UserGroupLimit) - return Conflict(new ErrorMessage(ErrorCode.UserGroupLimitReached)); + return Conflict(new ErrorMessageResponse(ErrorCode.UserGroupLimitReached)); var permissionSet = new Models.PermissionSet { @@ -80,7 +83,7 @@ namespace Tgstation.Server.Host.Controllers InstanceManagerRights = model.PermissionSet?.InstanceManagerRights ?? InstanceManagerRights.None }; - var dbGroup = new Models.UserGroup + var dbGroup = new UserGroup { Name = model.Name, PermissionSet = permissionSet, @@ -96,24 +99,19 @@ namespace Tgstation.Server.Host.Controllers /// /// Update a . /// - /// The to update. + /// The . /// The for the operation. /// A resulting in the of the operation. /// updated successfully. /// The requested does not currently exist. [HttpPost] [TgsAuthorize(AdministrationRights.WriteUsers)] - [ProducesResponseType(typeof(UserGroup), 200)] - public async Task Update([FromBody] UserGroup model, CancellationToken cancellationToken) + [ProducesResponseType(typeof(UserGroupResponse), 200)] + public async Task Update([FromBody] UserGroupUpdateRequest model, CancellationToken cancellationToken) { if (model == null) throw new ArgumentNullException(nameof(model)); - // For my sanity, I'm not allowing user management here - // Use the UserController for that - if (model.Users != null) - return BadRequest(new ErrorMessage(ErrorCode.UserGroupControllerCantEditMembers)); - var currentGroup = await DatabaseContext .Groups .AsQueryable() @@ -137,7 +135,7 @@ namespace Tgstation.Server.Host.Controllers await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); if (!AuthenticationContext.PermissionSet.AdministrationRights.Value.HasFlag(AdministrationRights.ReadUsers)) - return Json(new UserGroup + return Json(new UserGroupResponse { Id = currentGroup.Id }); @@ -148,15 +146,15 @@ namespace Tgstation.Server.Host.Controllers /// /// Gets a specific . /// - /// The of the . + /// The of the . /// The for the operation. /// A resulting in the of the request. /// Retrieve successfully. /// The requested does not currently exist. [HttpGet("{id}")] [TgsAuthorize(AdministrationRights.ReadUsers)] - [ProducesResponseType(typeof(UserGroup), 200)] - [ProducesResponseType(typeof(ErrorMessage), 410)] + [ProducesResponseType(typeof(UserGroupResponse), 200)] + [ProducesResponseType(typeof(ErrorMessageResponse), 410)] public async Task GetId(long id, CancellationToken cancellationToken) { // this functions as userId @@ -183,11 +181,11 @@ namespace Tgstation.Server.Host.Controllers /// Retrieved s successfully. [HttpGet(Routes.List)] [TgsAuthorize(AdministrationRights.ReadUsers)] - [ProducesResponseType(typeof(Paginated), 200)] + [ProducesResponseType(typeof(PaginatedResponse), 200)] public Task List([FromQuery] int? page, [FromQuery] int? pageSize, CancellationToken cancellationToken) - => Paginated( + => Paginated( () => Task.FromResult( - new PaginatableResult( + new PaginatableResult( DatabaseContext .Groups .AsQueryable() @@ -200,7 +198,7 @@ namespace Tgstation.Server.Host.Controllers cancellationToken); /// - /// Delete an . + /// Delete a . /// /// The of the to delete. /// The for the operation. @@ -211,8 +209,8 @@ namespace Tgstation.Server.Host.Controllers [HttpDelete("{id}")] [TgsAuthorize(AdministrationRights.WriteUsers)] [ProducesResponseType(204)] - [ProducesResponseType(typeof(ErrorMessage), 409)] - [ProducesResponseType(typeof(ErrorMessage), 410)] + [ProducesResponseType(typeof(ErrorMessageResponse), 409)] + [ProducesResponseType(typeof(ErrorMessageResponse), 410)] public async Task Delete(long id, CancellationToken cancellationToken) { var numDeleted = await DatabaseContext @@ -234,7 +232,7 @@ namespace Tgstation.Server.Host.Controllers .ConfigureAwait(false); return groupExists - ? Conflict(new ErrorMessage(ErrorCode.UserGroupNotEmpty)) + ? Conflict(new ErrorMessageResponse(ErrorCode.UserGroupNotEmpty)) : Gone(); } } diff --git a/src/Tgstation.Server.Host/Core/SwaggerConfiguration.cs b/src/Tgstation.Server.Host/Core/SwaggerConfiguration.cs index eec48ee126..1effb9b96a 100644 --- a/src/Tgstation.Server.Host/Core/SwaggerConfiguration.cs +++ b/src/Tgstation.Server.Host/Core/SwaggerConfiguration.cs @@ -9,8 +9,10 @@ using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Mime; +using System.Reflection; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Host.Controllers; namespace Tgstation.Server.Host.Core @@ -18,7 +20,7 @@ namespace Tgstation.Server.Host.Core /// /// Implements various filters for . /// - sealed class SwaggerConfiguration : IOperationFilter, IDocumentFilter, ISchemaFilter + sealed class SwaggerConfiguration : IOperationFilter, IDocumentFilter, ISchemaFilter, IRequestBodyFilter { /// /// The name for password authentication. @@ -47,7 +49,7 @@ namespace Tgstation.Server.Host.Core { Reference = new OpenApiReference { - Id = nameof(ErrorMessage), + Id = nameof(ErrorMessageResponse), Type = ReferenceType.Schema } } @@ -120,6 +122,22 @@ namespace Tgstation.Server.Host.Core }); } + /// + /// Generates the OpenAPI schema ID for a given . + /// + /// The to generate a schema ID for. + /// The generated schema ID for . + static string GenerateSchemaId(Type type) + { + if (type == typeof(UserName)) + return "ShallowUserResponse"; + + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(PaginatedResponse<>)) + return $"Paginated{type.GenericTypeArguments.First().Name}"; + + return type.Name; + } + /// /// Configure the swagger settings. /// @@ -158,19 +176,9 @@ namespace Tgstation.Server.Host.Core swaggerGenOptions.OperationFilter(); swaggerGenOptions.DocumentFilter(); swaggerGenOptions.SchemaFilter(); + swaggerGenOptions.RequestBodyFilter(); - swaggerGenOptions.CustomSchemaIds(type => - { - if (type == typeof(Api.Models.Internal.UserBase)) - return "ShallowUser"; - if (type == typeof(Api.Models.Internal.UserGroup)) - return "ShallowUserGroup"; - - if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Paginated<>)) - return $"Paginated{type.GenericTypeArguments.First().Name}"; - - return type.Name; - }); + swaggerGenOptions.CustomSchemaIds(GenerateSchemaId); swaggerGenOptions.AddSecurityDefinition(PasswordSecuritySchemeId, new OpenApiSecurityScheme { @@ -208,11 +216,6 @@ namespace Tgstation.Server.Host.Core operation.OperationId = $"{context.MethodInfo.DeclaringType.Name}.{context.MethodInfo.Name}"; - // request bodies are never nullable - var bodySchemas = operation.RequestBody?.Content.Select(x => x.Value.Schema) ?? Enumerable.Empty(); - foreach (var bodySchema in bodySchemas) - bodySchema.Nullable = false; - var authAttributes = context .MethodInfo .DeclaringType @@ -380,24 +383,12 @@ namespace Tgstation.Server.Host.Core Schema = productHeaderSchema }); - var pathsToRemove = new List(); - var filteredControllers = new string[] - { - nameof(BridgeController), - nameof(ControlPanelController), - nameof(SwarmController), - }; - + var allSchemas = context + .SchemaRepository + .Schemas; foreach (var path in swaggerDoc.Paths) foreach (var operation in path.Value.Operations.Select(x => x.Value)) { - if (filteredControllers.Any( - x => operation.OperationId.StartsWith(x, StringComparison.Ordinal))) - { - pathsToRemove.Add(path.Key); - continue; - } - operation.Parameters.Insert(0, new OpenApiParameter { Reference = new OpenApiReference @@ -417,12 +408,120 @@ namespace Tgstation.Server.Host.Core }); } - foreach (var filteredPath in pathsToRemove) - swaggerDoc.Paths.Remove(filteredPath); - AddDefaultResponses(swaggerDoc); } + /// + /// Applies the , , and to of a given . + /// + /// The root . + /// The current . + static void ApplyAttributesForRootSchema(OpenApiSchema rootSchema, SchemaFilterContext context) + { + // tune up the descendants + rootSchema.Nullable = false; + var rootSchemaId = GenerateSchemaId(context.Type); + var rootRequestSchema = rootSchemaId.EndsWith("Request", StringComparison.Ordinal); + var rootResponseSchema = rootSchemaId.EndsWith("Response", StringComparison.Ordinal); + var isPutRequest = rootSchemaId.EndsWith("CreateRequest", StringComparison.Ordinal); + + Tuple> GetTypeFromKvp(Type currentType, KeyValuePair kvp, IDictionary schemaDictionary) + { + var propertyInfo = currentType + .GetProperties() + .Single(x => x.Name.Equals(kvp.Key, StringComparison.OrdinalIgnoreCase)); + + return Tuple.Create( + propertyInfo, + kvp.Key, + kvp.Value, + schemaDictionary); + } + + var subSchemaStack = new Stack>>( + rootSchema.Properties.Select(x => GetTypeFromKvp(context.Type, x, rootSchema.Properties))); + + while (subSchemaStack.Count > 0) + { + var tuple = subSchemaStack.Pop(); + var subSchema = tuple.Item3; + + if (subSchema.Reference != null) + // can't mangle references per request + continue; + + var subSchemaPropertyInfo = tuple.Item1; + + if (subSchema.Properties != null + && !subSchemaPropertyInfo + .PropertyType + .GetInterfaces() + .Any(x => x == typeof(IEnumerable))) + foreach (var kvp in subSchema.Properties) + subSchemaStack.Push(GetTypeFromKvp(subSchemaPropertyInfo.PropertyType, kvp, subSchema.Properties)); + + var attributes = subSchemaPropertyInfo + .GetCustomAttributes(); + var responsePresence = attributes + .OfType() + .FirstOrDefault() + ?.Presence + ?? FieldPresence.Required; + var requestOptions = attributes + .OfType() + .OrderBy(x => x.PutOnly) // Process PUTs last + .ToList(); + + if (requestOptions.Any() && requestOptions.All(x => x.Presence == FieldPresence.Ignored && !x.PutOnly)) + subSchema.ReadOnly = true; + + var subSchemaId = tuple.Item2; + var subSchemaOwningDictionary = tuple.Item4; + if (rootResponseSchema) + { + subSchema.Nullable = responsePresence == FieldPresence.Optional; + if (responsePresence == FieldPresence.Ignored) + subSchemaOwningDictionary.Remove(subSchemaId); + } + else if (rootRequestSchema) + { + subSchema.Nullable = true; + var lastOptionWasIgnored = false; + foreach (var requestOption in requestOptions) + { + var validForThisRequest = !requestOption.PutOnly || isPutRequest; + if (!validForThisRequest) + continue; + + lastOptionWasIgnored = false; + switch (requestOption.Presence) + { + case FieldPresence.Ignored: + lastOptionWasIgnored = true; + break; + case FieldPresence.Optional: + subSchema.Nullable = true; + break; + case FieldPresence.Required: + subSchema.Nullable = false; + break; + default: + throw new InvalidOperationException($"Invalid FieldPresence: {requestOption.Presence}!"); + } + } + + if (lastOptionWasIgnored) + subSchemaOwningDictionary.Remove(subSchemaId); + } + else if (responsePresence == FieldPresence.Required + && requestOptions.All(x => x.Presence == FieldPresence.Required && !x.PutOnly)) + subSchema.Nullable = false; + + // otherwise, we have to assume it's a shared schema + // use what Swagger thinks the nullability is by default + } + } + /// public void Apply(OpenApiSchema schema, SchemaFilterContext context) { @@ -434,19 +533,29 @@ namespace Tgstation.Server.Host.Core // Nothing is required schema.Required.Clear(); - // Could be nullable type, make sure to get the right one - Type nonNullableType = context.Type.IsConstructedGenericType - ? context.Type.GenericTypeArguments.First() - : context.Type; - - if (nonNullableType != context.Type - && !context.Type.GetInterfaces().Any(x => x == typeof(IEnumerable))) - schema.Nullable = true; + if (context.MemberInfo == null) + ApplyAttributesForRootSchema(schema, context); if (!schema.Enum?.Any() ?? false) return; - OpenApiEnumVarNamesExtension.Apply(schema, nonNullableType); + // Could be nullable type, make sure to get the right one + Type firstGenericArgumentOrType = context.Type.IsConstructedGenericType + ? context.Type.GenericTypeArguments.First() + : context.Type; + + OpenApiEnumVarNamesExtension.Apply(schema, firstGenericArgumentOrType); + } + + /// + public void Apply(OpenApiRequestBody requestBody, RequestBodyFilterContext context) + { + if (requestBody == null) + throw new ArgumentNullException(nameof(requestBody)); + if (context == null) + throw new ArgumentNullException(nameof(context)); + + requestBody.Required = true; } } } diff --git a/src/Tgstation.Server.Host/Database/DatabaseContext.cs b/src/Tgstation.Server.Host/Database/DatabaseContext.cs index 7992a475cc..819afa7af5 100644 --- a/src/Tgstation.Server.Host/Database/DatabaseContext.cs +++ b/src/Tgstation.Server.Host/Database/DatabaseContext.cs @@ -377,17 +377,17 @@ namespace Tgstation.Server.Host.Database /// /// Used by unit tests to remind us to setup the correct MSSQL migration downgrades. /// - internal static readonly Type MSLatestMigration = typeof(MSAddRevInfoTimestamp); + internal static readonly Type MSLatestMigration = typeof(MSTruncateInstanceNames); /// /// Used by unit tests to remind us to setup the correct MYSQL migration downgrades. /// - internal static readonly Type MYLatestMigration = typeof(MYAddRevInfoTimestamp); + internal static readonly Type MYLatestMigration = typeof(MYTruncateInstanceNames); /// /// Used by unit tests to remind us to setup the correct PostgresSQL migration downgrades. /// - internal static readonly Type PGLatestMigration = typeof(PGAddRevInfoTimestamp); + internal static readonly Type PGLatestMigration = typeof(PGTruncateInstanceNames); /// /// Used by unit tests to remind us to setup the correct SQLite migration downgrades. @@ -420,6 +420,15 @@ namespace Tgstation.Server.Host.Database // Update this with new migrations as they are made string targetMigration = null; + if (targetVersion < new Version(4, 10, 0)) + targetMigration = currentDatabaseType switch + { + DatabaseType.MySql => nameof(MSAddRevInfoTimestamp), + DatabaseType.PostgresSql => nameof(PGAddRevInfoTimestamp), + DatabaseType.SqlServer => nameof(MSAddRevInfoTimestamp), + DatabaseType.Sqlite => nameof(SLAddRevInfoTimestamp), + _ => throw new ArgumentException($"Invalid DatabaseType: {currentDatabaseType}", nameof(currentDatabaseType)), + }; if (targetVersion < new Version(4, 8, 0)) targetMigration = currentDatabaseType switch { diff --git a/src/Tgstation.Server.Host/Database/DatabaseSeeder.cs b/src/Tgstation.Server.Host/Database/DatabaseSeeder.cs index 9e1c6afd79..26e24c27e4 100644 --- a/src/Tgstation.Server.Host/Database/DatabaseSeeder.cs +++ b/src/Tgstation.Server.Host/Database/DatabaseSeeder.cs @@ -5,6 +5,7 @@ using System; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Tgstation.Server.Api; using Tgstation.Server.Api.Rights; using Tgstation.Server.Host.Configuration; using Tgstation.Server.Host.Models; @@ -112,11 +113,11 @@ namespace Tgstation.Server.Host.Database InstanceManagerRights = RightsHelper.AllRights(), }, CreatedAt = DateTimeOffset.UtcNow, - Name = Api.Models.User.AdminName, - CanonicalName = User.CanonicalizeName(Api.Models.User.AdminName), + Name = DefaultCredentials.AdminUserName, + CanonicalName = User.CanonicalizeName(DefaultCredentials.AdminUserName), Enabled = true, }; - cryptographySuite.SetUserPassword(admin, Api.Models.User.DefaultAdminPassword, true); + cryptographySuite.SetUserPassword(admin, DefaultCredentials.DefaultAdminUserPassword, true); databaseContext.Users.Add(admin); return admin; } @@ -248,7 +249,7 @@ namespace Tgstation.Server.Host.Database } else admin.PermissionSet.AdministrationRights |= AdministrationRights.WriteUsers; - cryptographySuite.SetUserPassword(admin, Api.Models.User.DefaultAdminPassword, false); + cryptographySuite.SetUserPassword(admin, DefaultCredentials.DefaultAdminUserPassword, false); } await databaseContext.Save(cancellationToken).ConfigureAwait(false); @@ -265,7 +266,7 @@ namespace Tgstation.Server.Host.Database var admin = await databaseContext .Users .AsQueryable() - .Where(x => x.CanonicalName == User.CanonicalizeName(Api.Models.User.AdminName)) + .Where(x => x.CanonicalName == User.CanonicalizeName(DefaultCredentials.AdminUserName)) .Include(x => x.CreatedBy) .Include(x => x.PermissionSet) .Include(x => x.Group) diff --git a/src/Tgstation.Server.Host/Database/Migrations/20180918020726_MYAddMinimumSecurity.cs b/src/Tgstation.Server.Host/Database/Migrations/20180918020726_MYAddMinimumSecurity.cs index 398697723b..a12577e3e2 100644 --- a/src/Tgstation.Server.Host/Database/Migrations/20180918020726_MYAddMinimumSecurity.cs +++ b/src/Tgstation.Server.Host/Database/Migrations/20180918020726_MYAddMinimumSecurity.cs @@ -1,11 +1,11 @@ -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; using System; using Tgstation.Server.Api.Models; namespace Tgstation.Server.Host.Database.Migrations { /// - /// Add the and columns for MySQL/MariaDB + /// Add the and columns for MySQL/MariaDB /// public partial class MYAddMinimumSecurity : Migration { diff --git a/src/Tgstation.Server.Host/Database/Migrations/20180918021228_MSAddMinimumSecurity.cs b/src/Tgstation.Server.Host/Database/Migrations/20180918021228_MSAddMinimumSecurity.cs index 02274a85c1..4d6316e70c 100644 --- a/src/Tgstation.Server.Host/Database/Migrations/20180918021228_MSAddMinimumSecurity.cs +++ b/src/Tgstation.Server.Host/Database/Migrations/20180918021228_MSAddMinimumSecurity.cs @@ -1,11 +1,11 @@ -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; using System; using Tgstation.Server.Api.Models; namespace Tgstation.Server.Host.Database.Migrations { /// - /// Add the and columns for MSSQL + /// Add the and columns for MSSQL /// public partial class MSAddMinimumSecurity : Migration { diff --git a/src/Tgstation.Server.Host/Database/Migrations/20200121170252_MSAddChatBotReconnectionInterval.cs b/src/Tgstation.Server.Host/Database/Migrations/20200121170252_MSAddChatBotReconnectionInterval.cs index 67a32a559b..47e4b74c0b 100644 --- a/src/Tgstation.Server.Host/Database/Migrations/20200121170252_MSAddChatBotReconnectionInterval.cs +++ b/src/Tgstation.Server.Host/Database/Migrations/20200121170252_MSAddChatBotReconnectionInterval.cs @@ -4,7 +4,7 @@ using System; namespace Tgstation.Server.Host.Database.Migrations { /// - /// Adds the property for MSSQL. + /// Adds the property for MSSQL. /// public partial class MSAddChatBotReconnectionInterval : Migration { diff --git a/src/Tgstation.Server.Host/Database/Migrations/20200121171159_MYAddChatBotReconnectionInterval.cs b/src/Tgstation.Server.Host/Database/Migrations/20200121171159_MYAddChatBotReconnectionInterval.cs index 2e5f749922..a8dce31746 100644 --- a/src/Tgstation.Server.Host/Database/Migrations/20200121171159_MYAddChatBotReconnectionInterval.cs +++ b/src/Tgstation.Server.Host/Database/Migrations/20200121171159_MYAddChatBotReconnectionInterval.cs @@ -4,7 +4,7 @@ using System; namespace Tgstation.Server.Host.Database.Migrations { /// - /// Adds the property for MySQL/MariaDB. + /// Adds the property for MySQL/MariaDB. /// public partial class MYAddChatBotReconnectionInterval : Migration { diff --git a/src/Tgstation.Server.Host/Database/Migrations/20210211173222_MSTruncateInstanceNames.Designer.cs b/src/Tgstation.Server.Host/Database/Migrations/20210211173222_MSTruncateInstanceNames.Designer.cs new file mode 100644 index 0000000000..0b58612f60 --- /dev/null +++ b/src/Tgstation.Server.Host/Database/Migrations/20210211173222_MSTruncateInstanceNames.Designer.cs @@ -0,0 +1,902 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Tgstation.Server.Host.Database.Migrations +{ + [DbContext(typeof(SqlServerDatabaseContext))] + [Migration("20210211173222_MSTruncateInstanceNames")] + partial class MSTruncateInstanceNames + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.10") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ChannelLimit") + .HasColumnType("int"); + + b.Property("ConnectionString") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasMaxLength(10000); + + b.Property("Enabled") + .HasColumnType("bit"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(100)") + .HasMaxLength(100); + + b.Property("Provider") + .HasColumnType("int"); + + b.Property("ReconnectionInterval") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId", "Name") + .IsUnique(); + + b.ToTable("ChatBots"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ChatSettingsId") + .HasColumnType("bigint"); + + b.Property("DiscordChannelId") + .HasColumnType("decimal(20,0)"); + + b.Property("IrcChannel") + .HasColumnType("nvarchar(100)") + .HasMaxLength(100); + + b.Property("IsAdminChannel") + .IsRequired() + .HasColumnType("bit"); + + b.Property("IsUpdatesChannel") + .IsRequired() + .HasColumnType("bit"); + + b.Property("IsWatchdogChannel") + .IsRequired() + .HasColumnType("bit"); + + b.Property("Tag") + .HasColumnType("nvarchar(max)") + .HasMaxLength(10000); + + b.HasKey("Id"); + + b.HasIndex("ChatSettingsId", "DiscordChannelId") + .IsUnique() + .HasFilter("[DiscordChannelId] IS NOT NULL"); + + b.HasIndex("ChatSettingsId", "IrcChannel") + .IsUnique() + .HasFilter("[IrcChannel] IS NOT NULL"); + + b.ToTable("ChatChannels"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ByondVersion") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("DMApiMajorVersion") + .HasColumnType("int"); + + b.Property("DMApiMinorVersion") + .HasColumnType("int"); + + b.Property("DMApiPatchVersion") + .HasColumnType("int"); + + b.Property("DirectoryName") + .IsRequired() + .HasColumnType("uniqueidentifier"); + + b.Property("DmeName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("GitHubDeploymentId") + .HasColumnType("int"); + + b.Property("GitHubRepoId") + .HasColumnType("bigint"); + + b.Property("JobId") + .HasColumnType("bigint"); + + b.Property("MinimumSecurityLevel") + .HasColumnType("int"); + + b.Property("Output") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("RepositoryOrigin") + .HasColumnType("nvarchar(max)"); + + b.Property("RevisionInformationId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("DirectoryName"); + + b.HasIndex("JobId") + .IsUnique(); + + b.HasIndex("RevisionInformationId"); + + b.ToTable("CompileJobs"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AdditionalParameters") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasMaxLength(10000); + + b.Property("AllowWebClient") + .IsRequired() + .HasColumnType("bit"); + + b.Property("AutoStart") + .IsRequired() + .HasColumnType("bit"); + + b.Property("HeartbeatSeconds") + .HasColumnType("bigint"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("Port") + .HasColumnType("int"); + + b.Property("SecurityLevel") + .HasColumnType("int"); + + b.Property("StartupTimeout") + .HasColumnType("bigint"); + + b.Property("TopicRequestTimeout") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("DreamDaemonSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ApiValidationPort") + .HasColumnType("int"); + + b.Property("ApiValidationSecurityLevel") + .HasColumnType("int"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("ProjectName") + .HasColumnType("nvarchar(max)") + .HasMaxLength(10000); + + b.Property("RequireDMApiValidation") + .IsRequired() + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("DreamMakerSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AutoUpdateInterval") + .HasColumnType("bigint"); + + b.Property("ChatBotLimit") + .HasColumnType("int"); + + b.Property("ConfigurationType") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(100)") + .HasMaxLength(100); + + b.Property("Online") + .IsRequired() + .HasColumnType("bit"); + + b.Property("Path") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("SwarmIdentifer") + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("Path", "SwarmIdentifer") + .IsUnique() + .HasFilter("[SwarmIdentifer] IS NOT NULL"); + + b.ToTable("Instances"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.InstancePermissionSet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ByondRights") + .HasColumnType("decimal(20,0)"); + + b.Property("ChatBotRights") + .HasColumnType("decimal(20,0)"); + + b.Property("ConfigurationRights") + .HasColumnType("decimal(20,0)"); + + b.Property("DreamDaemonRights") + .HasColumnType("decimal(20,0)"); + + b.Property("DreamMakerRights") + .HasColumnType("decimal(20,0)"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("InstancePermissionSetRights") + .HasColumnType("decimal(20,0)"); + + b.Property("PermissionSetId") + .HasColumnType("bigint"); + + b.Property("RepositoryRights") + .HasColumnType("decimal(20,0)"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.HasIndex("PermissionSetId", "InstanceId") + .IsUnique(); + + b.ToTable("InstancePermissionSets"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("CancelRight") + .HasColumnType("decimal(20,0)"); + + b.Property("CancelRightsType") + .HasColumnType("decimal(20,0)"); + + b.Property("Cancelled") + .IsRequired() + .HasColumnType("bit"); + + b.Property("CancelledById") + .HasColumnType("bigint"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ErrorCode") + .HasColumnType("bigint"); + + b.Property("ExceptionDetails") + .HasColumnType("nvarchar(max)"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("StartedAt") + .IsRequired() + .HasColumnType("datetimeoffset"); + + b.Property("StartedById") + .HasColumnType("bigint"); + + b.Property("StoppedAt") + .HasColumnType("datetimeoffset"); + + b.HasKey("Id"); + + b.HasIndex("CancelledById"); + + b.HasIndex("InstanceId"); + + b.HasIndex("StartedById"); + + b.ToTable("Jobs"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ExternalUserId") + .IsRequired() + .HasColumnType("nvarchar(100)") + .HasMaxLength(100); + + b.Property("Provider") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("Provider", "ExternalUserId") + .IsUnique(); + + b.ToTable("OAuthConnections"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AdministrationRights") + .HasColumnType("decimal(20,0)"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("InstanceManagerRights") + .HasColumnType("decimal(20,0)"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("GroupId") + .IsUnique() + .HasFilter("[GroupId] IS NOT NULL"); + + b.HasIndex("UserId") + .IsUnique() + .HasFilter("[UserId] IS NOT NULL"); + + b.ToTable("PermissionSets"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AccessIdentifier") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CompileJobId") + .HasColumnType("bigint"); + + b.Property("LaunchSecurityLevel") + .HasColumnType("int"); + + b.Property("Port") + .HasColumnType("int"); + + b.Property("ProcessId") + .HasColumnType("int"); + + b.Property("RebootState") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CompileJobId"); + + b.ToTable("ReattachInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AccessToken") + .HasColumnType("nvarchar(max)") + .HasMaxLength(10000); + + b.Property("AccessUser") + .HasColumnType("nvarchar(max)") + .HasMaxLength(10000); + + b.Property("AutoUpdatesKeepTestMerges") + .IsRequired() + .HasColumnType("bit"); + + b.Property("AutoUpdatesSynchronize") + .IsRequired() + .HasColumnType("bit"); + + b.Property("CommitterEmail") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasMaxLength(10000); + + b.Property("CommitterName") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasMaxLength(10000); + + b.Property("CreateGitHubDeployments") + .IsRequired() + .HasColumnType("bit"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("PostTestMergeComment") + .IsRequired() + .HasColumnType("bit"); + + b.Property("PushTestMergeCommits") + .IsRequired() + .HasColumnType("bit"); + + b.Property("ShowTestMergeCommitters") + .IsRequired() + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("RepositorySettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("RevisionInformationId") + .HasColumnType("bigint"); + + b.Property("TestMergeId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("RevisionInformationId"); + + b.HasIndex("TestMergeId"); + + b.ToTable("RevInfoTestMerges"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("CommitSha") + .IsRequired() + .HasColumnType("nvarchar(40)") + .HasMaxLength(40); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("OriginCommitSha") + .IsRequired() + .HasColumnType("nvarchar(40)") + .HasMaxLength(40); + + b.Property("Timestamp") + .HasColumnType("datetimeoffset"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId", "CommitSha") + .IsUnique(); + + b.ToTable("RevisionInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Author") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("BodyAtMerge") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Comment") + .HasColumnType("nvarchar(max)") + .HasMaxLength(10000); + + b.Property("MergedAt") + .HasColumnType("datetimeoffset"); + + b.Property("MergedById") + .HasColumnType("bigint"); + + b.Property("Number") + .HasColumnType("int"); + + b.Property("PrimaryRevisionInformationId") + .IsRequired() + .HasColumnType("bigint"); + + b.Property("TargetCommitSha") + .IsRequired() + .HasColumnType("nvarchar(40)") + .HasMaxLength(40); + + b.Property("TitleAtMerge") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Url") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("MergedById"); + + b.HasIndex("PrimaryRevisionInformationId") + .IsUnique(); + + b.ToTable("TestMerges"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("CanonicalName") + .IsRequired() + .HasColumnType("nvarchar(100)") + .HasMaxLength(100); + + b.Property("CreatedAt") + .IsRequired() + .HasColumnType("datetimeoffset"); + + b.Property("CreatedById") + .HasColumnType("bigint"); + + b.Property("Enabled") + .IsRequired() + .HasColumnType("bit"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("LastPasswordUpdate") + .HasColumnType("datetimeoffset"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(100)") + .HasMaxLength(100); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("SystemIdentifier") + .HasColumnType("nvarchar(100)") + .HasMaxLength(100); + + b.HasKey("Id"); + + b.HasIndex("CanonicalName") + .IsUnique(); + + b.HasIndex("CreatedById"); + + b.HasIndex("GroupId"); + + b.HasIndex("SystemIdentifier") + .IsUnique() + .HasFilter("[SystemIdentifier] IS NOT NULL"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.UserGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(100)") + .HasMaxLength(100); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Groups"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("ChatSettings") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b => + { + b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings") + .WithMany("Channels") + .HasForeignKey("ChatSettingsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b => + { + b.HasOne("Tgstation.Server.Host.Models.Job", "Job") + .WithOne() + .HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation") + .WithMany("CompileJobs") + .HasForeignKey("RevisionInformationId") + .OnDelete(DeleteBehavior.ClientNoAction) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("DreamDaemonSettings") + .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("DreamMakerSettings") + .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.InstancePermissionSet", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("InstancePermissionSets") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.PermissionSet", "PermissionSet") + .WithMany("InstancePermissionSets") + .HasForeignKey("PermissionSetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy") + .WithMany() + .HasForeignKey("CancelledById"); + + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("Jobs") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy") + .WithMany() + .HasForeignKey("StartedById") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "User") + .WithMany("OAuthConnections") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b => + { + b.HasOne("Tgstation.Server.Host.Models.UserGroup", "Group") + .WithOne("PermissionSet") + .HasForeignKey("Tgstation.Server.Host.Models.PermissionSet", "GroupId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Tgstation.Server.Host.Models.User", "User") + .WithOne("PermissionSet") + .HasForeignKey("Tgstation.Server.Host.Models.PermissionSet", "UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob") + .WithMany() + .HasForeignKey("CompileJobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("RepositorySettings") + .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b => + { + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation") + .WithMany("ActiveTestMerges") + .HasForeignKey("RevisionInformationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge") + .WithMany("RevisonInformations") + .HasForeignKey("TestMergeId") + .OnDelete(DeleteBehavior.ClientNoAction) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("RevisionInformations") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy") + .WithMany("TestMerges") + .HasForeignKey("MergedById") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation") + .WithOne("PrimaryTestMerge") + .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy") + .WithMany("CreatedUsers") + .HasForeignKey("CreatedById"); + + b.HasOne("Tgstation.Server.Host.Models.UserGroup", "Group") + .WithMany("Users") + .HasForeignKey("GroupId"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Tgstation.Server.Host/Database/Migrations/20210211173222_MSTruncateInstanceNames.cs b/src/Tgstation.Server.Host/Database/Migrations/20210211173222_MSTruncateInstanceNames.cs new file mode 100644 index 0000000000..4b5e0c013a --- /dev/null +++ b/src/Tgstation.Server.Host/Database/Migrations/20210211173222_MSTruncateInstanceNames.cs @@ -0,0 +1,43 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using System; + +namespace Tgstation.Server.Host.Database.Migrations +{ + /// + /// Reduces the index name column size for MSSQL. + /// + public partial class MSTruncateInstanceNames : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + if (migrationBuilder == null) + throw new ArgumentNullException(nameof(migrationBuilder)); + + migrationBuilder.AlterColumn( + name: "Name", + table: "Instances", + maxLength: 100, + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldMaxLength: 10000); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + if (migrationBuilder == null) + throw new ArgumentNullException(nameof(migrationBuilder)); + + migrationBuilder.AlterColumn( + name: "Name", + table: "Instances", + type: "nvarchar(max)", + maxLength: 10000, + nullable: false, + oldClrType: typeof(string), + oldMaxLength: 100); + } + } +} diff --git a/src/Tgstation.Server.Host/Database/Migrations/20210211173316_MYTruncateInstanceNames.Designer.cs b/src/Tgstation.Server.Host/Database/Migrations/20210211173316_MYTruncateInstanceNames.Designer.cs new file mode 100644 index 0000000000..e2ef30208b --- /dev/null +++ b/src/Tgstation.Server.Host/Database/Migrations/20210211173316_MYTruncateInstanceNames.Designer.cs @@ -0,0 +1,886 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Tgstation.Server.Host.Database.Migrations +{ + [DbContext(typeof(MySqlDatabaseContext))] + [Migration("20210211173316_MYTruncateInstanceNames")] + partial class MYTruncateInstanceNames + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.10") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("ChannelLimit") + .IsRequired() + .HasColumnType("smallint unsigned"); + + b.Property("ConnectionString") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4") + .HasMaxLength(10000); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(100) CHARACTER SET utf8mb4") + .HasMaxLength(100); + + b.Property("Provider") + .HasColumnType("int"); + + b.Property("ReconnectionInterval") + .IsRequired() + .HasColumnType("int unsigned"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId", "Name") + .IsUnique(); + + b.ToTable("ChatBots"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("ChatSettingsId") + .HasColumnType("bigint"); + + b.Property("DiscordChannelId") + .HasColumnType("bigint unsigned"); + + b.Property("IrcChannel") + .HasColumnType("varchar(100) CHARACTER SET utf8mb4") + .HasMaxLength(100); + + b.Property("IsAdminChannel") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("IsUpdatesChannel") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("IsWatchdogChannel") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("Tag") + .HasColumnType("longtext CHARACTER SET utf8mb4") + .HasMaxLength(10000); + + b.HasKey("Id"); + + b.HasIndex("ChatSettingsId", "DiscordChannelId") + .IsUnique(); + + b.HasIndex("ChatSettingsId", "IrcChannel") + .IsUnique(); + + b.ToTable("ChatChannels"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("ByondVersion") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("DMApiMajorVersion") + .HasColumnType("int"); + + b.Property("DMApiMinorVersion") + .HasColumnType("int"); + + b.Property("DMApiPatchVersion") + .HasColumnType("int"); + + b.Property("DirectoryName") + .IsRequired() + .HasColumnType("char(36)"); + + b.Property("DmeName") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("GitHubDeploymentId") + .HasColumnType("int"); + + b.Property("GitHubRepoId") + .HasColumnType("bigint"); + + b.Property("JobId") + .HasColumnType("bigint"); + + b.Property("MinimumSecurityLevel") + .HasColumnType("int"); + + b.Property("Output") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("RepositoryOrigin") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("RevisionInformationId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("DirectoryName"); + + b.HasIndex("JobId") + .IsUnique(); + + b.HasIndex("RevisionInformationId"); + + b.ToTable("CompileJobs"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("AdditionalParameters") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4") + .HasMaxLength(10000); + + b.Property("AllowWebClient") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("AutoStart") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("HeartbeatSeconds") + .IsRequired() + .HasColumnType("int unsigned"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("Port") + .IsRequired() + .HasColumnType("smallint unsigned"); + + b.Property("SecurityLevel") + .HasColumnType("int"); + + b.Property("StartupTimeout") + .IsRequired() + .HasColumnType("int unsigned"); + + b.Property("TopicRequestTimeout") + .IsRequired() + .HasColumnType("int unsigned"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("DreamDaemonSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("ApiValidationPort") + .IsRequired() + .HasColumnType("smallint unsigned"); + + b.Property("ApiValidationSecurityLevel") + .HasColumnType("int"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("ProjectName") + .HasColumnType("longtext CHARACTER SET utf8mb4") + .HasMaxLength(10000); + + b.Property("RequireDMApiValidation") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("DreamMakerSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("AutoUpdateInterval") + .IsRequired() + .HasColumnType("int unsigned"); + + b.Property("ChatBotLimit") + .IsRequired() + .HasColumnType("smallint unsigned"); + + b.Property("ConfigurationType") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(100) CHARACTER SET utf8mb4") + .HasMaxLength(100); + + b.Property("Online") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("Path") + .IsRequired() + .HasColumnType("varchar(255) CHARACTER SET utf8mb4"); + + b.Property("SwarmIdentifer") + .HasColumnType("varchar(255) CHARACTER SET utf8mb4"); + + b.HasKey("Id"); + + b.HasIndex("Path", "SwarmIdentifer") + .IsUnique(); + + b.ToTable("Instances"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.InstancePermissionSet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("ByondRights") + .HasColumnType("bigint unsigned"); + + b.Property("ChatBotRights") + .HasColumnType("bigint unsigned"); + + b.Property("ConfigurationRights") + .HasColumnType("bigint unsigned"); + + b.Property("DreamDaemonRights") + .HasColumnType("bigint unsigned"); + + b.Property("DreamMakerRights") + .HasColumnType("bigint unsigned"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("InstancePermissionSetRights") + .HasColumnType("bigint unsigned"); + + b.Property("PermissionSetId") + .HasColumnType("bigint"); + + b.Property("RepositoryRights") + .HasColumnType("bigint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.HasIndex("PermissionSetId", "InstanceId") + .IsUnique(); + + b.ToTable("InstancePermissionSets"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CancelRight") + .HasColumnType("bigint unsigned"); + + b.Property("CancelRightsType") + .HasColumnType("bigint unsigned"); + + b.Property("Cancelled") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("CancelledById") + .HasColumnType("bigint"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("ErrorCode") + .HasColumnType("int unsigned"); + + b.Property("ExceptionDetails") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("StartedAt") + .IsRequired() + .HasColumnType("datetime(6)"); + + b.Property("StartedById") + .HasColumnType("bigint"); + + b.Property("StoppedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("CancelledById"); + + b.HasIndex("InstanceId"); + + b.HasIndex("StartedById"); + + b.ToTable("Jobs"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("ExternalUserId") + .IsRequired() + .HasColumnType("varchar(100) CHARACTER SET utf8mb4") + .HasMaxLength(100); + + b.Property("Provider") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("Provider", "ExternalUserId") + .IsUnique(); + + b.ToTable("OAuthConnections"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("AdministrationRights") + .HasColumnType("bigint unsigned"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("InstanceManagerRights") + .HasColumnType("bigint unsigned"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("GroupId") + .IsUnique(); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("PermissionSets"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("AccessIdentifier") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("CompileJobId") + .HasColumnType("bigint"); + + b.Property("LaunchSecurityLevel") + .HasColumnType("int"); + + b.Property("Port") + .HasColumnType("smallint unsigned"); + + b.Property("ProcessId") + .HasColumnType("int"); + + b.Property("RebootState") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CompileJobId"); + + b.ToTable("ReattachInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("AccessToken") + .HasColumnType("longtext CHARACTER SET utf8mb4") + .HasMaxLength(10000); + + b.Property("AccessUser") + .HasColumnType("longtext CHARACTER SET utf8mb4") + .HasMaxLength(10000); + + b.Property("AutoUpdatesKeepTestMerges") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("AutoUpdatesSynchronize") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("CommitterEmail") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4") + .HasMaxLength(10000); + + b.Property("CommitterName") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4") + .HasMaxLength(10000); + + b.Property("CreateGitHubDeployments") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("PostTestMergeComment") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("PushTestMergeCommits") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("ShowTestMergeCommitters") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("RepositorySettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("RevisionInformationId") + .HasColumnType("bigint"); + + b.Property("TestMergeId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("RevisionInformationId"); + + b.HasIndex("TestMergeId"); + + b.ToTable("RevInfoTestMerges"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CommitSha") + .IsRequired() + .HasColumnType("varchar(40) CHARACTER SET utf8mb4") + .HasMaxLength(40); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("OriginCommitSha") + .IsRequired() + .HasColumnType("varchar(40) CHARACTER SET utf8mb4") + .HasMaxLength(40); + + b.Property("Timestamp") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId", "CommitSha") + .IsUnique(); + + b.ToTable("RevisionInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("Author") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("BodyAtMerge") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("Comment") + .HasColumnType("longtext CHARACTER SET utf8mb4") + .HasMaxLength(10000); + + b.Property("MergedAt") + .HasColumnType("datetime(6)"); + + b.Property("MergedById") + .HasColumnType("bigint"); + + b.Property("Number") + .HasColumnType("int"); + + b.Property("PrimaryRevisionInformationId") + .IsRequired() + .HasColumnType("bigint"); + + b.Property("TargetCommitSha") + .IsRequired() + .HasColumnType("varchar(40) CHARACTER SET utf8mb4") + .HasMaxLength(40); + + b.Property("TitleAtMerge") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("Url") + .IsRequired() + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.HasKey("Id"); + + b.HasIndex("MergedById"); + + b.HasIndex("PrimaryRevisionInformationId") + .IsUnique(); + + b.ToTable("TestMerges"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CanonicalName") + .IsRequired() + .HasColumnType("varchar(100) CHARACTER SET utf8mb4") + .HasMaxLength(100); + + b.Property("CreatedAt") + .IsRequired() + .HasColumnType("datetime(6)"); + + b.Property("CreatedById") + .HasColumnType("bigint"); + + b.Property("Enabled") + .IsRequired() + .HasColumnType("tinyint(1)"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("LastPasswordUpdate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(100) CHARACTER SET utf8mb4") + .HasMaxLength(100); + + b.Property("PasswordHash") + .HasColumnType("longtext CHARACTER SET utf8mb4"); + + b.Property("SystemIdentifier") + .HasColumnType("varchar(100) CHARACTER SET utf8mb4") + .HasMaxLength(100); + + b.HasKey("Id"); + + b.HasIndex("CanonicalName") + .IsUnique(); + + b.HasIndex("CreatedById"); + + b.HasIndex("GroupId"); + + b.HasIndex("SystemIdentifier") + .IsUnique(); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.UserGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(100) CHARACTER SET utf8mb4") + .HasMaxLength(100); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Groups"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("ChatSettings") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b => + { + b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings") + .WithMany("Channels") + .HasForeignKey("ChatSettingsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b => + { + b.HasOne("Tgstation.Server.Host.Models.Job", "Job") + .WithOne() + .HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation") + .WithMany("CompileJobs") + .HasForeignKey("RevisionInformationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("DreamDaemonSettings") + .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("DreamMakerSettings") + .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.InstancePermissionSet", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("InstancePermissionSets") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.PermissionSet", "PermissionSet") + .WithMany("InstancePermissionSets") + .HasForeignKey("PermissionSetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy") + .WithMany() + .HasForeignKey("CancelledById"); + + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("Jobs") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy") + .WithMany() + .HasForeignKey("StartedById") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "User") + .WithMany("OAuthConnections") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b => + { + b.HasOne("Tgstation.Server.Host.Models.UserGroup", "Group") + .WithOne("PermissionSet") + .HasForeignKey("Tgstation.Server.Host.Models.PermissionSet", "GroupId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Tgstation.Server.Host.Models.User", "User") + .WithOne("PermissionSet") + .HasForeignKey("Tgstation.Server.Host.Models.PermissionSet", "UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob") + .WithMany() + .HasForeignKey("CompileJobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("RepositorySettings") + .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b => + { + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation") + .WithMany("ActiveTestMerges") + .HasForeignKey("RevisionInformationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge") + .WithMany("RevisonInformations") + .HasForeignKey("TestMergeId") + .OnDelete(DeleteBehavior.ClientNoAction) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("RevisionInformations") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy") + .WithMany("TestMerges") + .HasForeignKey("MergedById") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation") + .WithOne("PrimaryTestMerge") + .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy") + .WithMany("CreatedUsers") + .HasForeignKey("CreatedById"); + + b.HasOne("Tgstation.Server.Host.Models.UserGroup", "Group") + .WithMany("Users") + .HasForeignKey("GroupId"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Tgstation.Server.Host/Database/Migrations/20210211173316_MYTruncateInstanceNames.cs b/src/Tgstation.Server.Host/Database/Migrations/20210211173316_MYTruncateInstanceNames.cs new file mode 100644 index 0000000000..965260d1cd --- /dev/null +++ b/src/Tgstation.Server.Host/Database/Migrations/20210211173316_MYTruncateInstanceNames.cs @@ -0,0 +1,43 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using System; + +namespace Tgstation.Server.Host.Database.Migrations +{ + /// + /// Reduces the index name column size for MYSQL. + /// + public partial class MYTruncateInstanceNames : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + if (migrationBuilder == null) + throw new ArgumentNullException(nameof(migrationBuilder)); + + migrationBuilder.AlterColumn( + name: "Name", + table: "Instances", + maxLength: 100, + nullable: false, + oldClrType: typeof(string), + oldType: "longtext CHARACTER SET utf8mb4", + oldMaxLength: 10000); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + if (migrationBuilder == null) + throw new ArgumentNullException(nameof(migrationBuilder)); + + migrationBuilder.AlterColumn( + name: "Name", + table: "Instances", + type: "longtext CHARACTER SET utf8mb4", + maxLength: 10000, + nullable: false, + oldClrType: typeof(string), + oldMaxLength: 100); + } + } +} diff --git a/src/Tgstation.Server.Host/Database/Migrations/20210211173407_PGTruncateInstanceNames.Designer.cs b/src/Tgstation.Server.Host/Database/Migrations/20210211173407_PGTruncateInstanceNames.Designer.cs new file mode 100644 index 0000000000..6cceb60e49 --- /dev/null +++ b/src/Tgstation.Server.Host/Database/Migrations/20210211173407_PGTruncateInstanceNames.Designer.cs @@ -0,0 +1,896 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +namespace Tgstation.Server.Host.Database.Migrations +{ + [DbContext(typeof(PostgresSqlDatabaseContext))] + [Migration("20210211173407_PGTruncateInstanceNames")] + partial class PGTruncateInstanceNames + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn) + .HasAnnotation("ProductVersion", "3.1.10") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ChannelLimit") + .HasColumnType("integer"); + + b.Property("ConnectionString") + .IsRequired() + .HasColumnType("character varying(10000)") + .HasMaxLength(10000); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasColumnType("character varying(100)") + .HasMaxLength(100); + + b.Property("Provider") + .HasColumnType("integer"); + + b.Property("ReconnectionInterval") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId", "Name") + .IsUnique(); + + b.ToTable("ChatBots"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ChatSettingsId") + .HasColumnType("bigint"); + + b.Property("DiscordChannelId") + .HasColumnType("numeric(20,0)"); + + b.Property("IrcChannel") + .HasColumnType("character varying(100)") + .HasMaxLength(100); + + b.Property("IsAdminChannel") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("IsUpdatesChannel") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("IsWatchdogChannel") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("Tag") + .HasColumnType("character varying(10000)") + .HasMaxLength(10000); + + b.HasKey("Id"); + + b.HasIndex("ChatSettingsId", "DiscordChannelId") + .IsUnique(); + + b.HasIndex("ChatSettingsId", "IrcChannel") + .IsUnique(); + + b.ToTable("ChatChannels"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ByondVersion") + .IsRequired() + .HasColumnType("text"); + + b.Property("DMApiMajorVersion") + .HasColumnType("integer"); + + b.Property("DMApiMinorVersion") + .HasColumnType("integer"); + + b.Property("DMApiPatchVersion") + .HasColumnType("integer"); + + b.Property("DirectoryName") + .IsRequired() + .HasColumnType("uuid"); + + b.Property("DmeName") + .IsRequired() + .HasColumnType("text"); + + b.Property("GitHubDeploymentId") + .HasColumnType("integer"); + + b.Property("GitHubRepoId") + .HasColumnType("bigint"); + + b.Property("JobId") + .HasColumnType("bigint"); + + b.Property("MinimumSecurityLevel") + .HasColumnType("integer"); + + b.Property("Output") + .IsRequired() + .HasColumnType("text"); + + b.Property("RepositoryOrigin") + .HasColumnType("text"); + + b.Property("RevisionInformationId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("DirectoryName"); + + b.HasIndex("JobId") + .IsUnique(); + + b.HasIndex("RevisionInformationId"); + + b.ToTable("CompileJobs"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("AdditionalParameters") + .IsRequired() + .HasColumnType("character varying(10000)") + .HasMaxLength(10000); + + b.Property("AllowWebClient") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("AutoStart") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("HeartbeatSeconds") + .HasColumnType("bigint"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("Port") + .HasColumnType("integer"); + + b.Property("SecurityLevel") + .HasColumnType("integer"); + + b.Property("StartupTimeout") + .HasColumnType("bigint"); + + b.Property("TopicRequestTimeout") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("DreamDaemonSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ApiValidationPort") + .HasColumnType("integer"); + + b.Property("ApiValidationSecurityLevel") + .HasColumnType("integer"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("ProjectName") + .HasColumnType("character varying(10000)") + .HasMaxLength(10000); + + b.Property("RequireDMApiValidation") + .IsRequired() + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("DreamMakerSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("AutoUpdateInterval") + .HasColumnType("bigint"); + + b.Property("ChatBotLimit") + .HasColumnType("integer"); + + b.Property("ConfigurationType") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("character varying(100)") + .HasMaxLength(100); + + b.Property("Online") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("Path") + .IsRequired() + .HasColumnType("text"); + + b.Property("SwarmIdentifer") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Path", "SwarmIdentifer") + .IsUnique(); + + b.ToTable("Instances"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.InstancePermissionSet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ByondRights") + .HasColumnType("numeric(20,0)"); + + b.Property("ChatBotRights") + .HasColumnType("numeric(20,0)"); + + b.Property("ConfigurationRights") + .HasColumnType("numeric(20,0)"); + + b.Property("DreamDaemonRights") + .HasColumnType("numeric(20,0)"); + + b.Property("DreamMakerRights") + .HasColumnType("numeric(20,0)"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("InstancePermissionSetRights") + .HasColumnType("numeric(20,0)"); + + b.Property("PermissionSetId") + .HasColumnType("bigint"); + + b.Property("RepositoryRights") + .HasColumnType("numeric(20,0)"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.HasIndex("PermissionSetId", "InstanceId") + .IsUnique(); + + b.ToTable("InstancePermissionSets"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("CancelRight") + .HasColumnType("numeric(20,0)"); + + b.Property("CancelRightsType") + .HasColumnType("numeric(20,0)"); + + b.Property("Cancelled") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("CancelledById") + .HasColumnType("bigint"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("ErrorCode") + .HasColumnType("bigint"); + + b.Property("ExceptionDetails") + .HasColumnType("text"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("StartedAt") + .IsRequired() + .HasColumnType("timestamp with time zone"); + + b.Property("StartedById") + .HasColumnType("bigint"); + + b.Property("StoppedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("CancelledById"); + + b.HasIndex("InstanceId"); + + b.HasIndex("StartedById"); + + b.ToTable("Jobs"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ExternalUserId") + .IsRequired() + .HasColumnType("character varying(100)") + .HasMaxLength(100); + + b.Property("Provider") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("Provider", "ExternalUserId") + .IsUnique(); + + b.ToTable("OAuthConnections"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("AdministrationRights") + .HasColumnType("numeric(20,0)"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("InstanceManagerRights") + .HasColumnType("numeric(20,0)"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("GroupId") + .IsUnique(); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("PermissionSets"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("AccessIdentifier") + .IsRequired() + .HasColumnType("text"); + + b.Property("CompileJobId") + .HasColumnType("bigint"); + + b.Property("LaunchSecurityLevel") + .HasColumnType("integer"); + + b.Property("Port") + .HasColumnType("integer"); + + b.Property("ProcessId") + .HasColumnType("integer"); + + b.Property("RebootState") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CompileJobId"); + + b.ToTable("ReattachInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("AccessToken") + .HasColumnType("character varying(10000)") + .HasMaxLength(10000); + + b.Property("AccessUser") + .HasColumnType("character varying(10000)") + .HasMaxLength(10000); + + b.Property("AutoUpdatesKeepTestMerges") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("AutoUpdatesSynchronize") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("CommitterEmail") + .IsRequired() + .HasColumnType("character varying(10000)") + .HasMaxLength(10000); + + b.Property("CommitterName") + .IsRequired() + .HasColumnType("character varying(10000)") + .HasMaxLength(10000); + + b.Property("CreateGitHubDeployments") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("PostTestMergeComment") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("PushTestMergeCommits") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("ShowTestMergeCommitters") + .IsRequired() + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("RepositorySettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("RevisionInformationId") + .HasColumnType("bigint"); + + b.Property("TestMergeId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("RevisionInformationId"); + + b.HasIndex("TestMergeId"); + + b.ToTable("RevInfoTestMerges"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("CommitSha") + .IsRequired() + .HasColumnType("character varying(40)") + .HasMaxLength(40); + + b.Property("InstanceId") + .HasColumnType("bigint"); + + b.Property("OriginCommitSha") + .IsRequired() + .HasColumnType("character varying(40)") + .HasMaxLength(40); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId", "CommitSha") + .IsUnique(); + + b.ToTable("RevisionInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("Author") + .IsRequired() + .HasColumnType("text"); + + b.Property("BodyAtMerge") + .IsRequired() + .HasColumnType("text"); + + b.Property("Comment") + .HasColumnType("character varying(10000)") + .HasMaxLength(10000); + + b.Property("MergedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("MergedById") + .HasColumnType("bigint"); + + b.Property("Number") + .HasColumnType("integer"); + + b.Property("PrimaryRevisionInformationId") + .IsRequired() + .HasColumnType("bigint"); + + b.Property("TargetCommitSha") + .IsRequired() + .HasColumnType("character varying(40)") + .HasMaxLength(40); + + b.Property("TitleAtMerge") + .IsRequired() + .HasColumnType("text"); + + b.Property("Url") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("MergedById"); + + b.HasIndex("PrimaryRevisionInformationId") + .IsUnique(); + + b.ToTable("TestMerges"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("CanonicalName") + .IsRequired() + .HasColumnType("character varying(100)") + .HasMaxLength(100); + + b.Property("CreatedAt") + .IsRequired() + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedById") + .HasColumnType("bigint"); + + b.Property("Enabled") + .IsRequired() + .HasColumnType("boolean"); + + b.Property("GroupId") + .HasColumnType("bigint"); + + b.Property("LastPasswordUpdate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .HasColumnType("character varying(100)") + .HasMaxLength(100); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("SystemIdentifier") + .HasColumnType("character varying(100)") + .HasMaxLength(100); + + b.HasKey("Id"); + + b.HasIndex("CanonicalName") + .IsUnique(); + + b.HasIndex("CreatedById"); + + b.HasIndex("GroupId"); + + b.HasIndex("SystemIdentifier") + .IsUnique(); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.UserGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("Name") + .IsRequired() + .HasColumnType("character varying(100)") + .HasMaxLength(100); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Groups"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("ChatSettings") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b => + { + b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings") + .WithMany("Channels") + .HasForeignKey("ChatSettingsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b => + { + b.HasOne("Tgstation.Server.Host.Models.Job", "Job") + .WithOne() + .HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation") + .WithMany("CompileJobs") + .HasForeignKey("RevisionInformationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("DreamDaemonSettings") + .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("DreamMakerSettings") + .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.InstancePermissionSet", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("InstancePermissionSets") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.PermissionSet", "PermissionSet") + .WithMany("InstancePermissionSets") + .HasForeignKey("PermissionSetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy") + .WithMany() + .HasForeignKey("CancelledById"); + + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("Jobs") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy") + .WithMany() + .HasForeignKey("StartedById") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "User") + .WithMany("OAuthConnections") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.PermissionSet", b => + { + b.HasOne("Tgstation.Server.Host.Models.UserGroup", "Group") + .WithOne("PermissionSet") + .HasForeignKey("Tgstation.Server.Host.Models.PermissionSet", "GroupId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Tgstation.Server.Host.Models.User", "User") + .WithOne("PermissionSet") + .HasForeignKey("Tgstation.Server.Host.Models.PermissionSet", "UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob") + .WithMany() + .HasForeignKey("CompileJobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("RepositorySettings") + .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b => + { + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation") + .WithMany("ActiveTestMerges") + .HasForeignKey("RevisionInformationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge") + .WithMany("RevisonInformations") + .HasForeignKey("TestMergeId") + .OnDelete(DeleteBehavior.ClientNoAction) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("RevisionInformations") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy") + .WithMany("TestMerges") + .HasForeignKey("MergedById") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation") + .WithOne("PrimaryTestMerge") + .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy") + .WithMany("CreatedUsers") + .HasForeignKey("CreatedById"); + + b.HasOne("Tgstation.Server.Host.Models.UserGroup", "Group") + .WithMany("Users") + .HasForeignKey("GroupId"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Tgstation.Server.Host/Database/Migrations/20210211173407_PGTruncateInstanceNames.cs b/src/Tgstation.Server.Host/Database/Migrations/20210211173407_PGTruncateInstanceNames.cs new file mode 100644 index 0000000000..73f768bec0 --- /dev/null +++ b/src/Tgstation.Server.Host/Database/Migrations/20210211173407_PGTruncateInstanceNames.cs @@ -0,0 +1,43 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using System; + +namespace Tgstation.Server.Host.Database.Migrations +{ + /// + /// Reduces the index name column size for PostgresSQL. + /// + public partial class PGTruncateInstanceNames : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + if (migrationBuilder == null) + throw new ArgumentNullException(nameof(migrationBuilder)); + + migrationBuilder.AlterColumn( + name: "Name", + table: "Instances", + maxLength: 100, + nullable: false, + oldClrType: typeof(string), + oldType: "character varying(10000)", + oldMaxLength: 10000); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + if (migrationBuilder == null) + throw new ArgumentNullException(nameof(migrationBuilder)); + + migrationBuilder.AlterColumn( + name: "Name", + table: "Instances", + type: "character varying(10000)", + maxLength: 10000, + nullable: false, + oldClrType: typeof(string), + oldMaxLength: 100); + } + } +} diff --git a/src/Tgstation.Server.Host/Database/Migrations/MySqlDatabaseContextModelSnapshot.cs b/src/Tgstation.Server.Host/Database/Migrations/MySqlDatabaseContextModelSnapshot.cs index f01161f68e..ced5c78474 100644 --- a/src/Tgstation.Server.Host/Database/Migrations/MySqlDatabaseContextModelSnapshot.cs +++ b/src/Tgstation.Server.Host/Database/Migrations/MySqlDatabaseContextModelSnapshot.cs @@ -18,7 +18,7 @@ namespace Tgstation.Server.Host.Database.Migrations modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => { - b.Property("Id") + b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("bigint"); @@ -102,7 +102,7 @@ namespace Tgstation.Server.Host.Database.Migrations modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b => { - b.Property("Id") + b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("bigint"); @@ -244,7 +244,7 @@ namespace Tgstation.Server.Host.Database.Migrations modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b => { - b.Property("Id") + b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("bigint"); @@ -261,8 +261,8 @@ namespace Tgstation.Server.Host.Database.Migrations b.Property("Name") .IsRequired() - .HasColumnType("longtext CHARACTER SET utf8mb4") - .HasMaxLength(10000); + .HasColumnType("varchar(100) CHARACTER SET utf8mb4") + .HasMaxLength(100); b.Property("Online") .IsRequired() @@ -328,7 +328,7 @@ namespace Tgstation.Server.Host.Database.Migrations modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b => { - b.Property("Id") + b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("bigint"); @@ -688,7 +688,7 @@ namespace Tgstation.Server.Host.Database.Migrations modelBuilder.Entity("Tgstation.Server.Host.Models.UserGroup", b => { - b.Property("Id") + b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("bigint"); diff --git a/src/Tgstation.Server.Host/Database/Migrations/PostgresSqlDatabaseContextModelSnapshot.cs b/src/Tgstation.Server.Host/Database/Migrations/PostgresSqlDatabaseContextModelSnapshot.cs index 4668521f4a..ef0f1a270f 100644 --- a/src/Tgstation.Server.Host/Database/Migrations/PostgresSqlDatabaseContextModelSnapshot.cs +++ b/src/Tgstation.Server.Host/Database/Migrations/PostgresSqlDatabaseContextModelSnapshot.cs @@ -20,7 +20,7 @@ namespace Tgstation.Server.Host.Database.Migrations modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => { - b.Property("Id") + b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("bigint") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); @@ -104,7 +104,7 @@ namespace Tgstation.Server.Host.Database.Migrations modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b => { - b.Property("Id") + b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("bigint") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); @@ -244,7 +244,7 @@ namespace Tgstation.Server.Host.Database.Migrations modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b => { - b.Property("Id") + b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("bigint") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); @@ -260,8 +260,8 @@ namespace Tgstation.Server.Host.Database.Migrations b.Property("Name") .IsRequired() - .HasColumnType("character varying(10000)") - .HasMaxLength(10000); + .HasColumnType("character varying(100)") + .HasMaxLength(100); b.Property("Online") .IsRequired() @@ -328,7 +328,7 @@ namespace Tgstation.Server.Host.Database.Migrations modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b => { - b.Property("Id") + b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("bigint") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); @@ -697,7 +697,7 @@ namespace Tgstation.Server.Host.Database.Migrations modelBuilder.Entity("Tgstation.Server.Host.Models.UserGroup", b => { - b.Property("Id") + b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("bigint") .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); diff --git a/src/Tgstation.Server.Host/Database/Migrations/SqlServerDatabaseContextModelSnapshot.cs b/src/Tgstation.Server.Host/Database/Migrations/SqlServerDatabaseContextModelSnapshot.cs index 608266d424..35a6722fd0 100644 --- a/src/Tgstation.Server.Host/Database/Migrations/SqlServerDatabaseContextModelSnapshot.cs +++ b/src/Tgstation.Server.Host/Database/Migrations/SqlServerDatabaseContextModelSnapshot.cs @@ -20,7 +20,7 @@ namespace Tgstation.Server.Host.Database.Migrations modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => { - b.Property("Id") + b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("bigint") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); @@ -106,7 +106,7 @@ namespace Tgstation.Server.Host.Database.Migrations modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b => { - b.Property("Id") + b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("bigint") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); @@ -246,7 +246,7 @@ namespace Tgstation.Server.Host.Database.Migrations modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b => { - b.Property("Id") + b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("bigint") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); @@ -262,8 +262,8 @@ namespace Tgstation.Server.Host.Database.Migrations b.Property("Name") .IsRequired() - .HasColumnType("nvarchar(max)") - .HasMaxLength(10000); + .HasColumnType("nvarchar(100)") + .HasMaxLength(100); b.Property("Online") .IsRequired() @@ -331,7 +331,7 @@ namespace Tgstation.Server.Host.Database.Migrations modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b => { - b.Property("Id") + b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("bigint") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); @@ -703,7 +703,7 @@ namespace Tgstation.Server.Host.Database.Migrations modelBuilder.Entity("Tgstation.Server.Host.Models.UserGroup", b => { - b.Property("Id") + b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("bigint") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); diff --git a/src/Tgstation.Server.Host/Extensions/ApplicationBuilderExtensions.cs b/src/Tgstation.Server.Host/Extensions/ApplicationBuilderExtensions.cs index 28f3d93034..328fb766ce 100644 --- a/src/Tgstation.Server.Host/Extensions/ApplicationBuilderExtensions.cs +++ b/src/Tgstation.Server.Host/Extensions/ApplicationBuilderExtensions.cs @@ -8,6 +8,7 @@ using System; using System.Globalization; using System.Net; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Host.System; namespace Tgstation.Server.Host.Extensions @@ -48,7 +49,7 @@ namespace Tgstation.Server.Host.Extensions } logger.LogDebug(e, "Database conflict!"); - await new ConflictObjectResult(new ErrorMessage(ErrorCode.DatabaseIntegrityConflict) + await new ConflictObjectResult(new ErrorMessageResponse(ErrorCode.DatabaseIntegrityConflict) { AdditionalData = String.Format(CultureInfo.InvariantCulture, (e.InnerException ?? e).Message) }).ExecuteResultAsync(new ActionContext @@ -99,8 +100,8 @@ namespace Tgstation.Server.Host.Extensions catch (Exception e) { logger.LogError(e, "Failed request!"); - await new ObjectResult( - new ErrorMessage(ErrorCode.InternalServerError) + await new JsonResult( + new ErrorMessageResponse(ErrorCode.InternalServerError) { AdditionalData = e.ToString() }) @@ -110,7 +111,8 @@ namespace Tgstation.Server.Host.Extensions .ExecuteResultAsync(new ActionContext { HttpContext = context - }).ConfigureAwait(false); + }) + .ConfigureAwait(false); } }); } diff --git a/src/Tgstation.Server.Host/Jobs/IJobManager.cs b/src/Tgstation.Server.Host/Jobs/IJobManager.cs index ba72d618d0..d0d53255a2 100644 --- a/src/Tgstation.Server.Host/Jobs/IJobManager.cs +++ b/src/Tgstation.Server.Host/Jobs/IJobManager.cs @@ -11,10 +11,10 @@ namespace Tgstation.Server.Host.Jobs public interface IJobManager : IHostedService { /// - /// Get the for a + /// Get the for a /// - /// The to get for - /// The of + /// The to get for + /// The of int? JobProgress(Job job); /// diff --git a/src/Tgstation.Server.Host/Jobs/JobManager.cs b/src/Tgstation.Server.Host/Jobs/JobManager.cs index afd47a3b6b..b71e9bb140 100644 --- a/src/Tgstation.Server.Host/Jobs/JobManager.cs +++ b/src/Tgstation.Server.Host/Jobs/JobManager.cs @@ -78,7 +78,7 @@ namespace Tgstation.Server.Host.Jobs { lock (synchronizationLock) { - if (!jobs.TryGetValue(job.Id, out JobHandler jobHandler)) + if (!jobs.TryGetValue(job.Id.Value, out JobHandler jobHandler)) throw new InvalidOperationException("Job not running!"); return jobHandler; } @@ -105,7 +105,7 @@ namespace Tgstation.Server.Host.Jobs void UpdateProgress(int progress) { lock (synchronizationLock) - if (jobs.TryGetValue(oldJob.Id, out var handler)) + if (jobs.TryGetValue(oldJob.Id.Value, out var handler)) handler.Progress = progress; } @@ -160,8 +160,8 @@ namespace Tgstation.Server.Host.Jobs { lock (synchronizationLock) { - var handler = jobs[job.Id]; - jobs.Remove(job.Id); + var handler = jobs[job.Id.Value]; + jobs.Remove(job.Id.Value); handler.Dispose(); } } @@ -207,7 +207,7 @@ namespace Tgstation.Server.Host.Jobs try { lock (synchronizationLock) - jobs.Add(job.Id, jobHandler); + jobs.Add(job.Id.Value, jobHandler); jobHandler.Start(); } @@ -309,7 +309,7 @@ namespace Tgstation.Server.Host.Jobs throw new ArgumentNullException(nameof(job)); lock (synchronizationLock) { - if (!jobs.TryGetValue(job.Id, out var handler)) + if (!jobs.TryGetValue(job.Id.Value, out var handler)) return null; return handler.Progress; } @@ -323,7 +323,7 @@ namespace Tgstation.Server.Host.Jobs JobHandler handler; lock (synchronizationLock) { - if (!jobs.TryGetValue(job.Id, out handler)) + if (!jobs.TryGetValue(job.Id.Value, out handler)) return; } diff --git a/src/Tgstation.Server.Host/Models/ChatBot.cs b/src/Tgstation.Server.Host/Models/ChatBot.cs index f190f2f043..23f5d380d3 100644 --- a/src/Tgstation.Server.Host/Models/ChatBot.cs +++ b/src/Tgstation.Server.Host/Models/ChatBot.cs @@ -1,14 +1,15 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Host.Models { /// - public sealed class ChatBot : Api.Models.Internal.ChatBot, IApiTransformable + public sealed class ChatBot : Api.Models.Internal.ChatBotSettings, IApiTransformable { /// - /// Default for . + /// Default for . /// public const ushort DefaultChannelLimit = 100; @@ -24,12 +25,12 @@ namespace Tgstation.Server.Host.Models public Instance Instance { get; set; } /// - /// See + /// See /// public ICollection Channels { get; set; } /// - public Api.Models.ChatBot ToApi() => new Api.Models.ChatBot + public ChatBotResponse ToApi() => new ChatBotResponse { Channels = Channels.Select(x => x.ToApi()).ToList(), ConnectionString = ConnectionString, diff --git a/src/Tgstation.Server.Host/Models/CompileJob.cs b/src/Tgstation.Server.Host/Models/CompileJob.cs index 197cbe3217..6e37158c0a 100644 --- a/src/Tgstation.Server.Host/Models/CompileJob.cs +++ b/src/Tgstation.Server.Host/Models/CompileJob.cs @@ -1,14 +1,15 @@ using System; using System.ComponentModel.DataAnnotations; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Host.Models { /// - public sealed class CompileJob : Api.Models.Internal.CompileJob, IApiTransformable + public sealed class CompileJob : Api.Models.Internal.CompileJob, IApiTransformable { /// - /// See + /// See /// [Required] public Job Job { get; set; } @@ -19,7 +20,7 @@ namespace Tgstation.Server.Host.Models public long JobId { get; set; } /// - /// See + /// See /// [Required] public RevisionInformation RevisionInformation { get; set; } @@ -79,7 +80,7 @@ namespace Tgstation.Server.Host.Models } /// - public Api.Models.CompileJob ToApi() => new Api.Models.CompileJob + public CompileJobResponse ToApi() => new CompileJobResponse { DirectoryName = DirectoryName, DmeName = DmeName, diff --git a/src/Tgstation.Server.Host/Models/DreamMakerSettings.cs b/src/Tgstation.Server.Host/Models/DreamMakerSettings.cs index eb3ed1bb19..742070d453 100644 --- a/src/Tgstation.Server.Host/Models/DreamMakerSettings.cs +++ b/src/Tgstation.Server.Host/Models/DreamMakerSettings.cs @@ -1,9 +1,10 @@ using System.ComponentModel.DataAnnotations; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Host.Models { /// - public sealed class DreamMakerSettings : Api.Models.DreamMaker, IApiTransformable + public sealed class DreamMakerSettings : Api.Models.Internal.DreamMakerSettings, IApiTransformable { /// /// The row Id @@ -22,7 +23,7 @@ namespace Tgstation.Server.Host.Models public Instance Instance { get; set; } /// - public Api.Models.DreamMaker ToApi() => new Api.Models.DreamMaker + public DreamMakerResponse ToApi() => new DreamMakerResponse { ProjectName = ProjectName, ApiValidationPort = ApiValidationPort, diff --git a/src/Tgstation.Server.Host/Models/Instance.cs b/src/Tgstation.Server.Host/Models/Instance.cs index 3c721a12f1..0072175b8a 100644 --- a/src/Tgstation.Server.Host/Models/Instance.cs +++ b/src/Tgstation.Server.Host/Models/Instance.cs @@ -1,11 +1,12 @@ using System.Collections.Generic; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Host.Models { /// - /// Represents an in the database + /// Represents an in the database. /// - public sealed class Instance : Api.Models.Instance, IApiTransformable + public sealed class Instance : Api.Models.Instance, IApiTransformable { /// /// Default for . @@ -53,7 +54,7 @@ namespace Tgstation.Server.Host.Models public ICollection Jobs { get; set; } /// - public Api.Models.Instance ToApi() => new Api.Models.Instance + public InstanceResponse ToApi() => new InstanceResponse { AutoUpdateInterval = AutoUpdateInterval, ConfigurationType = ConfigurationType, @@ -61,8 +62,7 @@ namespace Tgstation.Server.Host.Models Name = Name, Path = Path, Online = Online, - ChatBotLimit = ChatBotLimit, - MoveJob = MoveJob, + ChatBotLimit = ChatBotLimit }; } } diff --git a/src/Tgstation.Server.Host/Models/InstancePermissionSet.cs b/src/Tgstation.Server.Host/Models/InstancePermissionSet.cs index c52c553b68..34bfb98f23 100644 --- a/src/Tgstation.Server.Host/Models/InstancePermissionSet.cs +++ b/src/Tgstation.Server.Host/Models/InstancePermissionSet.cs @@ -1,9 +1,10 @@ using System.ComponentModel.DataAnnotations; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Host.Models { /// - public sealed class InstancePermissionSet : Api.Models.InstancePermissionSet, IApiTransformable + public sealed class InstancePermissionSet : Api.Models.Internal.InstancePermissionSet, IApiTransformable { /// /// The row Id @@ -28,7 +29,7 @@ namespace Tgstation.Server.Host.Models public PermissionSet PermissionSet { get; set; } /// - public Api.Models.InstancePermissionSet ToApi() => new Api.Models.InstancePermissionSet + public InstancePermissionSetResponse ToApi() => new InstancePermissionSetResponse { ByondRights = ByondRights, ChatBotRights = ChatBotRights, diff --git a/src/Tgstation.Server.Host/Models/Job.cs b/src/Tgstation.Server.Host/Models/Job.cs index 861981efda..29203669cb 100644 --- a/src/Tgstation.Server.Host/Models/Job.cs +++ b/src/Tgstation.Server.Host/Models/Job.cs @@ -1,20 +1,21 @@ using System.ComponentModel.DataAnnotations; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Host.Models { /// #pragma warning disable CA1724 // naming conflict with gitlab package - public sealed class Job : Api.Models.Internal.Job, IApiTransformable + public sealed class Job : Api.Models.Internal.Job, IApiTransformable #pragma warning restore CA1724 { /// - /// See + /// See /// [Required] public User StartedBy { get; set; } /// - /// See + /// See /// public User CancelledBy { get; set; } @@ -25,19 +26,19 @@ namespace Tgstation.Server.Host.Models public Instance Instance { get; set; } /// - public Api.Models.Job ToApi() => new Api.Models.Job + public JobResponse ToApi() => new JobResponse { Id = Id, StartedAt = StartedAt, StoppedAt = StoppedAt, Cancelled = Cancelled, - CancelledBy = CancelledBy?.ToApi(false), + CancelledBy = CancelledBy?.CreateUserName(), CancelRight = CancelRight, CancelRightsType = CancelRightsType, Description = Description, ExceptionDetails = ExceptionDetails, ErrorCode = ErrorCode, - StartedBy = StartedBy.ToApi(false) + StartedBy = StartedBy.CreateUserName() }; } } diff --git a/src/Tgstation.Server.Host/Models/PermissionSet.cs b/src/Tgstation.Server.Host/Models/PermissionSet.cs index ad5c18ffc6..be56725791 100644 --- a/src/Tgstation.Server.Host/Models/PermissionSet.cs +++ b/src/Tgstation.Server.Host/Models/PermissionSet.cs @@ -6,7 +6,7 @@ namespace Tgstation.Server.Host.Models public sealed class PermissionSet : Api.Models.PermissionSet { /// - /// The of . + /// The of . /// public long? UserId { get; set; } diff --git a/src/Tgstation.Server.Host/Models/RepositorySettings.cs b/src/Tgstation.Server.Host/Models/RepositorySettings.cs index 25fb37edd7..c36eac01db 100644 --- a/src/Tgstation.Server.Host/Models/RepositorySettings.cs +++ b/src/Tgstation.Server.Host/Models/RepositorySettings.cs @@ -1,10 +1,10 @@ using System.ComponentModel.DataAnnotations; -using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Host.Models { /// - public sealed class RepositorySettings : Api.Models.Internal.RepositorySettings, IApiTransformable + public sealed class RepositorySettings : Api.Models.Internal.RepositorySettings, IApiTransformable { /// /// The row Id @@ -12,7 +12,7 @@ namespace Tgstation.Server.Host.Models public long Id { get; set; } /// - /// The instance + /// The instance /// public long InstanceId { get; set; } @@ -23,7 +23,7 @@ namespace Tgstation.Server.Host.Models public Instance Instance { get; set; } /// - public Repository ToApi() => new Repository + public RepositoryResponse ToApi() => new RepositoryResponse { // AccessToken = AccessToken, // never show this AccessUser = AccessUser, diff --git a/src/Tgstation.Server.Host/Models/TestMerge.cs b/src/Tgstation.Server.Host/Models/TestMerge.cs index 3ef25c0e91..0ee060f536 100644 --- a/src/Tgstation.Server.Host/Models/TestMerge.cs +++ b/src/Tgstation.Server.Host/Models/TestMerge.cs @@ -4,7 +4,7 @@ using System.ComponentModel.DataAnnotations; namespace Tgstation.Server.Host.Models { /// - public sealed class TestMerge : Api.Models.Internal.TestMerge, IApiTransformable + public sealed class TestMerge : Api.Models.Internal.TestMergeApiBase, IApiTransformable { /// /// See @@ -37,7 +37,7 @@ namespace Tgstation.Server.Host.Models TitleAtMerge = TitleAtMerge, Comment = Comment, Id = Id, - MergedBy = MergedBy.ToApi(false), + MergedBy = MergedBy.CreateUserName(), Number = Number, TargetCommitSha = TargetCommitSha, Url = Url diff --git a/src/Tgstation.Server.Host/Models/User.cs b/src/Tgstation.Server.Host/Models/User.cs index 20b3dd6d2a..c97703fae2 100644 --- a/src/Tgstation.Server.Host/Models/User.cs +++ b/src/Tgstation.Server.Host/Models/User.cs @@ -3,11 +3,12 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Host.Models { /// - public sealed class User : Api.Models.Internal.User, IApiTransformable + public sealed class User : Api.Models.Internal.UserModelBase, IApiTransformable { /// /// Username used when creating jobs automatically. @@ -20,7 +21,7 @@ namespace Tgstation.Server.Host.Models public string PasswordHash { get; set; } /// - /// See + /// See /// public User CreatedBy { get; set; } @@ -40,7 +41,7 @@ namespace Tgstation.Server.Host.Models public PermissionSet PermissionSet { get; set; } /// - /// The uppercase invariant of + /// The uppercase invariant of /// [Required] [StringLength(Limits.MaximumIndexableStringLength, MinimumLength = 1)] @@ -67,43 +68,35 @@ namespace Tgstation.Server.Host.Models public ICollection OAuthConnections { get; set; } /// - /// Change a into a . + /// Change a into a . /// - /// The . + /// The . /// The . public static string CanonicalizeName(string name) => name?.ToUpperInvariant() ?? throw new ArgumentNullException(nameof(name)); /// - /// See + /// Generate a from . /// - /// If we should recurse on - /// If rights and system identifier should be shown - /// A new - Api.Models.User ToApi(bool recursive, bool showDetails) => new Api.Models.User + /// If we should recurse on . + /// A new . + UserResponse CreateUserResponse(bool recursive) { - CreatedAt = showDetails ? CreatedAt : null, - CreatedBy = showDetails && recursive ? CreatedBy?.ToApi(false, false) : null, - Enabled = showDetails ? Enabled : null, - Id = Id, - Name = Name, - SystemIdentifier = showDetails ? SystemIdentifier : null, - OAuthConnections = showDetails - ? OAuthConnections - ?.Select(x => x.ToApi()) - .ToList() - : null, - Group = showDetails ? Group?.ToApi(false) : null, - PermissionSet = showDetails ? PermissionSet?.ToApi() : null, - }; + var result = CreateUserName(); + if (recursive) + result.CreatedBy = CreatedBy?.CreateUserName(); - /// - /// Convert the to it's API form - /// - /// If system identifier, oauth connections, and group/permission set should be shown. - /// A new - public Api.Models.User ToApi(bool showDetails) => ToApi(true, showDetails); + result.CreatedAt = CreatedAt; + result.Enabled = Enabled; + result.SystemIdentifier = SystemIdentifier; + result.OAuthConnections = OAuthConnections + ?.Select(x => x.ToApi()) + .ToList(); + result.Group = Group?.ToApi(false); + result.PermissionSet = PermissionSet?.ToApi(); + return result; + } /// - public Api.Models.User ToApi() => ToApi(true); + public UserResponse ToApi() => CreateUserResponse(true); } } diff --git a/src/Tgstation.Server.Host/Models/UserGroup.cs b/src/Tgstation.Server.Host/Models/UserGroup.cs index 2253b8de53..46c047df03 100644 --- a/src/Tgstation.Server.Host/Models/UserGroup.cs +++ b/src/Tgstation.Server.Host/Models/UserGroup.cs @@ -1,11 +1,13 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; +using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Host.Models { /// - public sealed class UserGroup : Api.Models.Internal.UserGroupBase, IApiTransformable + public sealed class UserGroup : NamedEntity, IApiTransformable { /// /// The the has. @@ -21,23 +23,22 @@ namespace Tgstation.Server.Host.Models /// /// Convert the to it's API form. /// - /// If should be populated. - /// A new . - public Api.Models.UserGroup ToApi(bool showUsers) => new Api.Models.UserGroup + /// If should be populated. + /// A new . + public UserGroupResponse ToApi(bool showUsers) => new UserGroupResponse { Id = Id, Name = Name, PermissionSet = PermissionSet.ToApi(), Users = showUsers ? Users - ?.Select(x => x.ToApi(false)) - .OfType() + ?.Select(x => x.CreateUserName()) .ToList() - ?? new List() + ?? new List() : null, }; /// - public Api.Models.UserGroup ToApi() => ToApi(true); + public UserGroupResponse ToApi() => ToApi(true); } } diff --git a/src/Tgstation.Server.Host/Security/AuthenticationContext.cs b/src/Tgstation.Server.Host/Security/AuthenticationContext.cs index 23b82e350a..a03772aa4f 100644 --- a/src/Tgstation.Server.Host/Security/AuthenticationContext.cs +++ b/src/Tgstation.Server.Host/Security/AuthenticationContext.cs @@ -5,9 +5,7 @@ using Tgstation.Server.Host.Models; namespace Tgstation.Server.Host.Security { - /// - /// Manages s for a scope - /// + /// sealed class AuthenticationContext : IAuthenticationContext { /// diff --git a/src/Tgstation.Server.Host/Security/IAuthenticationContext.cs b/src/Tgstation.Server.Host/Security/IAuthenticationContext.cs index 4b81a2244a..9c9962ccd8 100644 --- a/src/Tgstation.Server.Host/Security/IAuthenticationContext.cs +++ b/src/Tgstation.Server.Host/Security/IAuthenticationContext.cs @@ -5,7 +5,7 @@ using Tgstation.Server.Host.Models; namespace Tgstation.Server.Host.Security { /// - /// Represents the currently authenticated + /// Represents the currently authenticated . /// public interface IAuthenticationContext : IDisposable { diff --git a/src/Tgstation.Server.Host/Security/IAuthenticationContextFactory.cs b/src/Tgstation.Server.Host/Security/IAuthenticationContextFactory.cs index 196ae9fecf..bc673770ad 100644 --- a/src/Tgstation.Server.Host/Security/IAuthenticationContextFactory.cs +++ b/src/Tgstation.Server.Host/Security/IAuthenticationContextFactory.cs @@ -17,7 +17,7 @@ namespace Tgstation.Server.Host.Security /// /// Create an to populate /// - /// The of the + /// The of the /// The of the operation /// The the resulting 's password must be valid after /// The for the operation diff --git a/src/Tgstation.Server.Host/Security/ITokenFactory.cs b/src/Tgstation.Server.Host/Security/ITokenFactory.cs index f955f6fcd8..58b7e02853 100644 --- a/src/Tgstation.Server.Host/Security/ITokenFactory.cs +++ b/src/Tgstation.Server.Host/Security/ITokenFactory.cs @@ -1,12 +1,12 @@ using Microsoft.IdentityModel.Tokens; using System.Threading; using System.Threading.Tasks; -using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Host.Security { /// - /// For creating s + /// For creating s /// public interface ITokenFactory { @@ -16,12 +16,12 @@ namespace Tgstation.Server.Host.Security TokenValidationParameters ValidationParameters { get; } /// - /// Create a for a given + /// Create a for a given /// - /// The to create the token for. Must have the field available + /// The to create the token for. Must have the field available. /// Whether or not this is an OAuth login. /// The for the operation - /// A resulting in a new - Task CreateToken(Models.User user, bool oAuth, CancellationToken cancellationToken); + /// A resulting in a new + Task CreateToken(Models.User user, bool oAuth, CancellationToken cancellationToken); } } diff --git a/src/Tgstation.Server.Host/Security/IdentityCache.cs b/src/Tgstation.Server.Host/Security/IdentityCache.cs index 6d20f6fb6d..7b39d8294c 100644 --- a/src/Tgstation.Server.Host/Security/IdentityCache.cs +++ b/src/Tgstation.Server.Host/Security/IdentityCache.cs @@ -21,7 +21,7 @@ namespace Tgstation.Server.Host.Security readonly ILogger logger; /// - /// The map of s to s + /// The map of s to s /// readonly Dictionary cachedIdentities; diff --git a/src/Tgstation.Server.Host/Security/TokenFactory.cs b/src/Tgstation.Server.Host/Security/TokenFactory.cs index d4deedbb62..ee37fea604 100644 --- a/src/Tgstation.Server.Host/Security/TokenFactory.cs +++ b/src/Tgstation.Server.Host/Security/TokenFactory.cs @@ -6,7 +6,7 @@ using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Threading; using System.Threading.Tasks; -using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Host.Configuration; using Tgstation.Server.Host.Core; using Tgstation.Server.Host.System; @@ -65,7 +65,7 @@ namespace Tgstation.Server.Host.Security ValidateLifetime = true, ValidateAudience = true, - ValidAudience = typeof(Token).Assembly.GetName().Name, + ValidAudience = typeof(TokenResponse).Assembly.GetName().Name, ClockSkew = TimeSpan.FromMinutes(securityConfiguration.TokenClockSkewMinutes), @@ -76,7 +76,7 @@ namespace Tgstation.Server.Host.Security } /// - public async Task CreateToken(Models.User user, bool oAuth, CancellationToken cancellationToken) + public async Task CreateToken(Models.User user, bool oAuth, CancellationToken cancellationToken) { if (user == null) throw new ArgumentNullException(nameof(user)); @@ -106,7 +106,7 @@ namespace Tgstation.Server.Host.Security }; var token = new JwtSecurityToken(new JwtHeader(new SigningCredentials(ValidationParameters.IssuerSigningKey, SecurityAlgorithms.HmacSha256)), new JwtPayload(claims)); - return new Token + return new TokenResponse { Bearer = new JwtSecurityTokenHandler().WriteToken(token), ExpiresAt = expiry diff --git a/src/Tgstation.Server.Host/Swarm/ISwarmOperations.cs b/src/Tgstation.Server.Host/Swarm/ISwarmOperations.cs index 2b7ccf26d4..3d0d5ae025 100644 --- a/src/Tgstation.Server.Host/Swarm/ISwarmOperations.cs +++ b/src/Tgstation.Server.Host/Swarm/ISwarmOperations.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Host.Swarm { @@ -14,8 +14,8 @@ namespace Tgstation.Server.Host.Swarm /// /// Pass in an updated list of to the node. /// - /// An of the updated s. - void UpdateSwarmServersList(IEnumerable swarmServers); + /// An of the updated s. + void UpdateSwarmServersList(IEnumerable swarmServers); /// /// Notify the node of an update request from the controller. @@ -35,7 +35,7 @@ namespace Tgstation.Server.Host.Swarm /// /// Attempt to register a given with the controller. /// - /// The that is registering. + /// The that is registering. /// The registration . /// if the registration was successful, otherwise. bool RegisterNode(Api.Models.Internal.SwarmServer node, Guid registrationId); diff --git a/src/Tgstation.Server.Host/Swarm/ISwarmService.cs b/src/Tgstation.Server.Host/Swarm/ISwarmService.cs index 1c80b46928..84f2dc4b0c 100644 --- a/src/Tgstation.Server.Host/Swarm/ISwarmService.cs +++ b/src/Tgstation.Server.Host/Swarm/ISwarmService.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Host.Swarm { @@ -41,10 +41,10 @@ namespace Tgstation.Server.Host.Swarm Task CommitUpdate(CancellationToken cancellationToken); /// - /// Gets the list of s in the swarm, including the current one. + /// Gets the list of s in the swarm, including the current one. /// - /// A of s in the swarm. If the server is not part of a swarm, will be returned. - ICollection GetSwarmServers(); + /// A of s in the swarm. If the server is not part of a swarm, will be returned. + ICollection GetSwarmServers(); /// /// Abort an uncommitted update. diff --git a/src/Tgstation.Server.Host/Swarm/SwarmServersUpdateRequest.cs b/src/Tgstation.Server.Host/Swarm/SwarmServersUpdateRequest.cs index ed480707ca..c1e209549c 100644 --- a/src/Tgstation.Server.Host/Swarm/SwarmServersUpdateRequest.cs +++ b/src/Tgstation.Server.Host/Swarm/SwarmServersUpdateRequest.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Host.Swarm { @@ -10,9 +10,9 @@ namespace Tgstation.Server.Host.Swarm public sealed class SwarmServersUpdateRequest { /// - /// The of updated s. + /// The of updated s. /// [Required] - public ICollection SwarmServers { get; set; } + public ICollection SwarmServers { get; set; } } } diff --git a/src/Tgstation.Server.Host/Swarm/SwarmService.cs b/src/Tgstation.Server.Host/Swarm/SwarmService.cs index 1b11dd58b1..f737a09bf3 100644 --- a/src/Tgstation.Server.Host/Swarm/SwarmService.cs +++ b/src/Tgstation.Server.Host/Swarm/SwarmService.cs @@ -12,7 +12,7 @@ using System.Net.Mime; using System.Text; using System.Threading; using System.Threading.Tasks; -using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Host.Configuration; using Tgstation.Server.Host.Core; using Tgstation.Server.Host.Database; @@ -120,9 +120,9 @@ namespace Tgstation.Server.Host.Swarm readonly CancellationTokenSource serverHealthCheckCancellationTokenSource; /// - /// of connected s. + /// of connected s. /// - readonly List swarmServers; + readonly List swarmServers; /// /// of s to registration s. @@ -235,9 +235,9 @@ namespace Tgstation.Server.Host.Swarm if (swarmController) registrationIds = new Dictionary(); - swarmServers = new List + swarmServers = new List { - new SwarmServer + new SwarmServerResponse { Address = swarmConfiguration.Address, Controller = swarmController, @@ -279,7 +279,7 @@ namespace Tgstation.Server.Host.Swarm targetUpdateVersion = null; using var httpClient = httpClientFactory.CreateClient(); - async Task SendRemoteAbort(SwarmServer swarmServer) + async Task SendRemoteAbort(SwarmServerResponse swarmServer) { using var request = PrepareSwarmRequest( swarmServer, @@ -305,7 +305,7 @@ namespace Tgstation.Server.Host.Swarm Task task; if (!swarmController) - task = SendRemoteAbort(new SwarmServer + task = SendRemoteAbort(new SwarmServerResponse { Address = swarmConfiguration.ControllerAddress, }); @@ -394,7 +394,7 @@ namespace Tgstation.Server.Host.Swarm // on the controller, we first need to signal for nodes to go ahead // if anything fails at this point, there's nothing we can do logger.LogDebug("Sending remote commit message to nodes..."); - async Task SendRemoteCommitUpdate(SwarmServer swarmServer) + async Task SendRemoteCommitUpdate(SwarmServerResponse swarmServer) { using var request = PrepareSwarmRequest( swarmServer, @@ -427,7 +427,7 @@ namespace Tgstation.Server.Host.Swarm } /// - public ICollection GetSwarmServers() + public ICollection GetSwarmServers() { if (!SwarmMode) return null; @@ -474,7 +474,7 @@ namespace Tgstation.Server.Host.Swarm } using var httpClient = httpClientFactory.CreateClient(); - async Task RemotePrepareUpdate(SwarmServer swarmServer) + async Task RemotePrepareUpdate(SwarmServerResponse swarmServer) { using var request = PrepareSwarmRequest( swarmServer, @@ -629,7 +629,7 @@ namespace Tgstation.Server.Host.Swarm /// public async Task Shutdown(CancellationToken cancellationToken) { - async Task SendUnregistrationRequest(SwarmServer swarmServer) + async Task SendUnregistrationRequest(SwarmServerResponse swarmServer) { using var httpClient = httpClientFactory.CreateClient(); using var request = PrepareSwarmRequest( @@ -715,11 +715,11 @@ namespace Tgstation.Server.Host.Swarm { using var httpClient = httpClientFactory.CreateClient(); - List currentSwarmServers; + List currentSwarmServers; lock (swarmServers) currentSwarmServers = swarmServers.ToList(); - async Task HealthRequestForServer(SwarmServer swarmServer) + async Task HealthRequestForServer(SwarmServerResponse swarmServer) { using var request = PrepareSwarmRequest( swarmServer, @@ -909,12 +909,12 @@ namespace Tgstation.Server.Host.Swarm async Task SendUpdatedServerListToNodes(CancellationToken cancellationToken) { logger.LogDebug("Sending updated server list to all nodes..."); - List currentSwarmServers; + List currentSwarmServers; lock (swarmServers) currentSwarmServers = swarmServers.ToList(); using var httpClient = httpClientFactory.CreateClient(); - async Task UpdateRequestForServer(SwarmServer swarmServer) + async Task UpdateRequestForServer(SwarmServerResponse swarmServer) { using var request = PrepareSwarmRequest( swarmServer, @@ -953,20 +953,20 @@ namespace Tgstation.Server.Host.Swarm /// /// Prepares a for swarm communication. /// - /// The the message is for, if null will be sent to swarm controller. + /// The the message is for, if null will be sent to swarm controller. /// The . /// The route on to use. /// The body if any. /// An optional override to the . /// A new . HttpRequestMessage PrepareSwarmRequest( - SwarmServer swarmServer, + SwarmServerResponse swarmServer, HttpMethod httpMethod, string subroute, object body, Guid? registrationIdOverride = null) { - swarmServer ??= new SwarmServer + swarmServer ??= new SwarmServerResponse { Address = swarmConfiguration.ControllerAddress, }; @@ -1016,7 +1016,7 @@ namespace Tgstation.Server.Host.Swarm } /// - public void UpdateSwarmServersList(IEnumerable swarmServers) + public void UpdateSwarmServersList(IEnumerable swarmServers) { if (swarmServers == null) throw new ArgumentNullException(nameof(swarmServers)); @@ -1178,7 +1178,7 @@ namespace Tgstation.Server.Host.Swarm registrationIds.Remove(node.Identifier); } - swarmServers.Add(new SwarmServer + swarmServers.Add(new SwarmServerResponse { Address = node.Address, Identifier = node.Identifier, diff --git a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj index 7f947ac1b4..bd024e8cad 100644 --- a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj +++ b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj @@ -66,12 +66,12 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -93,8 +93,8 @@ - - + + diff --git a/src/Tgstation.Server.Host/Transfer/FileTransferService.cs b/src/Tgstation.Server.Host/Transfer/FileTransferService.cs index 08c2649b86..b83a2d2463 100644 --- a/src/Tgstation.Server.Host/Transfer/FileTransferService.cs +++ b/src/Tgstation.Server.Host/Transfer/FileTransferService.cs @@ -5,6 +5,7 @@ using System.IO; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Host.Core; using Tgstation.Server.Host.Extensions; using Tgstation.Server.Host.IO; @@ -43,12 +44,12 @@ namespace Tgstation.Server.Host.Transfer readonly ILogger logger; /// - /// of s to upload s. + /// of s to upload s. /// readonly Dictionary uploadTickets; /// - /// of s to s. + /// of s to s. /// readonly Dictionary downloadTickets; @@ -113,18 +114,17 @@ namespace Tgstation.Server.Host.Transfer } /// - /// Creates a new . + /// Creates a new . /// - /// A new . - FileTicketResult CreateTicket() => new FileTicketResult + /// A new . + FileTicketResponse CreateTicket() => new FileTicketResponse { FileTicket = cryptographySuite.GetSecureString() }; void QueueExpiry(Action expireAction) { - Task oldExpireTask = null; - + Task oldExpireTask; async Task ExpireAsync() { var expireAt = DateTimeOffset.UtcNow + TimeSpan.FromMinutes(TicketValidityMinutes); @@ -150,7 +150,7 @@ namespace Tgstation.Server.Host.Transfer } /// - public FileTicketResult CreateDownload(FileDownloadProvider downloadProvider) + public FileTicketResponse CreateDownload(FileDownloadProvider downloadProvider) { if (downloadProvider == null) throw new ArgumentNullException(nameof(downloadProvider)); @@ -199,7 +199,7 @@ namespace Tgstation.Server.Host.Transfer } /// - public async Task> RetrieveDownloadStream(FileTicketResult ticket, CancellationToken cancellationToken) + public async Task> RetrieveDownloadStream(FileTicketResponse ticket, CancellationToken cancellationToken) { if (ticket == null) throw new ArgumentNullException(nameof(ticket)); @@ -210,7 +210,7 @@ namespace Tgstation.Server.Host.Transfer if (!downloadTickets.TryGetValue(ticket.FileTicket, out downloadProvider)) { logger.LogTrace("Download ticket {0} not found!", ticket.FileTicket); - return Tuple.Create(null, null); + return Tuple.Create(null, null); } downloadTickets.Remove(ticket.FileTicket); @@ -220,7 +220,7 @@ namespace Tgstation.Server.Host.Transfer if (errorCode.HasValue) { logger.LogDebug("Download ticket {0} failed activation!", ticket.FileTicket); - return Tuple.Create(null, new ErrorMessage(errorCode.Value)); + return Tuple.Create(null, new ErrorMessageResponse(errorCode.Value)); } FileStream stream; @@ -233,9 +233,9 @@ namespace Tgstation.Server.Host.Transfer } catch (IOException ex) { - return Tuple.Create( + return Tuple.Create( null, - new ErrorMessage(ErrorCode.IOError) + new ErrorMessageResponse(ErrorCode.IOError) { AdditionalData = ex.ToString() }); @@ -244,7 +244,7 @@ namespace Tgstation.Server.Host.Transfer try { logger.LogTrace("Ticket {0} downloading...", ticket.FileTicket); - return Tuple.Create(stream, null); + return Tuple.Create(stream, null); } catch { @@ -254,7 +254,7 @@ namespace Tgstation.Server.Host.Transfer } /// - public async Task SetUploadStream(FileTicketResult ticket, Stream stream, CancellationToken cancellationToken) + public async Task SetUploadStream(FileTicketResponse ticket, Stream stream, CancellationToken cancellationToken) { if (ticket == null) throw new ArgumentNullException(nameof(ticket)); @@ -265,7 +265,7 @@ namespace Tgstation.Server.Host.Transfer if (!uploadTickets.TryGetValue(ticket.FileTicket, out uploadProvider)) { logger.LogTrace("Upload ticket {0} not found!", ticket.FileTicket); - return new ErrorMessage(ErrorCode.ResourceNotPresent); + return new ErrorMessageResponse(ErrorCode.ResourceNotPresent); } uploadTickets.Remove(ticket.FileTicket); diff --git a/src/Tgstation.Server.Host/Transfer/FileUploadProvider.cs b/src/Tgstation.Server.Host/Transfer/FileUploadProvider.cs index ea5e47a245..286e86d62c 100644 --- a/src/Tgstation.Server.Host/Transfer/FileUploadProvider.cs +++ b/src/Tgstation.Server.Host/Transfer/FileUploadProvider.cs @@ -4,6 +4,7 @@ using System.IO; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Host.Extensions; using Tgstation.Server.Host.IO; @@ -13,7 +14,7 @@ namespace Tgstation.Server.Host.Transfer sealed class FileUploadProvider : IFileUploadTicket { /// - public FileTicketResult Ticket { get; } + public FileTicketResponse Ticket { get; } /// /// The for the ticket duration. @@ -26,7 +27,7 @@ namespace Tgstation.Server.Host.Transfer readonly TaskCompletionSource taskCompletionSource; /// - /// The that completes in or when is called. + /// The that completes in or when is called. /// readonly TaskCompletionSource completionTcs; @@ -36,16 +37,16 @@ namespace Tgstation.Server.Host.Transfer readonly bool requireSynchronousIO; /// - /// The that occurred while processing the upload if any. + /// The that occurred while processing the upload if any. /// - ErrorMessage errorMessage; + ErrorMessageResponse errorMessage; /// /// Initializes a new instance of the . /// /// The value of . /// The value of - public FileUploadProvider(FileTicketResult ticket, bool requireSynchronousIO) + public FileUploadProvider(FileTicketResponse ticket, bool requireSynchronousIO) { Ticket = ticket ?? throw new ArgumentNullException(nameof(ticket)); @@ -84,14 +85,14 @@ namespace Tgstation.Server.Host.Transfer /// /// The containing uploaded data. /// The for the operation. - /// A resulting in , otherwise. - public async Task Completion(Stream stream, CancellationToken cancellationToken) + /// A resulting in , otherwise. + public async Task Completion(Stream stream, CancellationToken cancellationToken) { if (stream == null) throw new ArgumentNullException(nameof(stream)); if (ticketExpiryCts.IsCancellationRequested) - return new ErrorMessage(ErrorCode.ResourceNotPresent); + return new ErrorMessageResponse(ErrorCode.ResourceNotPresent); Stream bufferedStream = null; if (requireSynchronousIO) @@ -111,7 +112,7 @@ namespace Tgstation.Server.Host.Transfer } /// - public void SetErrorMessage(ErrorMessage errorMessage) + public void SetErrorMessage(ErrorMessageResponse errorMessage) { if (errorMessage == null) #pragma warning disable IDE0016 // Use 'throw' expression diff --git a/src/Tgstation.Server.Host/Transfer/IFileTransferStreamHandler.cs b/src/Tgstation.Server.Host/Transfer/IFileTransferStreamHandler.cs index fd8a051332..77c861c865 100644 --- a/src/Tgstation.Server.Host/Transfer/IFileTransferStreamHandler.cs +++ b/src/Tgstation.Server.Host/Transfer/IFileTransferStreamHandler.cs @@ -2,30 +2,30 @@ using System; using System.IO; using System.Threading; using System.Threading.Tasks; -using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Host.Transfer { /// - /// Reads and writes to s associated with s. + /// Reads and writes to s associated with s. /// public interface IFileTransferStreamHandler { /// /// Sets the for a given associated with a pending upload. /// - /// The . + /// The . /// The with uploaded data. /// The for the operation. - /// if the upload completed successfully, otherwise. - Task SetUploadStream(FileTicketResult ticket, Stream stream, CancellationToken cancellationToken); + /// if the upload completed successfully, otherwise. + Task SetUploadStream(FileTicketResponse ticket, Stream stream, CancellationToken cancellationToken); /// /// Gets the the for a given associated with a pending download. /// - /// The . + /// The . /// The for the operation. - /// A containing either a containing the data to download or an to return. - Task> RetrieveDownloadStream(FileTicketResult ticket, CancellationToken cancellationToken); + /// A containing either a containing the data to download or an to return. + Task> RetrieveDownloadStream(FileTicketResponse ticket, CancellationToken cancellationToken); } } diff --git a/src/Tgstation.Server.Host/Transfer/IFileTransferTicketProvider.cs b/src/Tgstation.Server.Host/Transfer/IFileTransferTicketProvider.cs index 5edfb86cd7..dd847774fb 100644 --- a/src/Tgstation.Server.Host/Transfer/IFileTransferTicketProvider.cs +++ b/src/Tgstation.Server.Host/Transfer/IFileTransferTicketProvider.cs @@ -1,4 +1,4 @@ -using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Host.Transfer { @@ -8,11 +8,11 @@ namespace Tgstation.Server.Host.Transfer public interface IFileTransferTicketProvider { /// - /// Create a for a download. + /// Create a for a download. /// /// The . - /// A new for a download. - FileTicketResult CreateDownload(FileDownloadProvider fileDownloadProvider); + /// A new for a download. + FileTicketResponse CreateDownload(FileDownloadProvider fileDownloadProvider); /// /// Create a . diff --git a/src/Tgstation.Server.Host/Transfer/IFileUploadTicket.cs b/src/Tgstation.Server.Host/Transfer/IFileUploadTicket.cs index 4eb1ea622c..622bc90235 100644 --- a/src/Tgstation.Server.Host/Transfer/IFileUploadTicket.cs +++ b/src/Tgstation.Server.Host/Transfer/IFileUploadTicket.cs @@ -2,19 +2,19 @@ using System; using System.IO; using System.Threading; using System.Threading.Tasks; -using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Host.Transfer { /// - /// A that waits for a pending upload. + /// A that waits for a pending upload. /// public interface IFileUploadTicket : IDisposable { /// - /// The . + /// The . /// - FileTicketResult Ticket { get; } + FileTicketResponse Ticket { get; } /// /// Gets the for the uploaded file. @@ -27,7 +27,7 @@ namespace Tgstation.Server.Host.Transfer /// /// Sets an for the upload. Will be returned in upload request as a 409 error. /// - /// The to set. - void SetErrorMessage(ErrorMessage errorMessage); + /// The to set. + void SetErrorMessage(ErrorMessageResponse errorMessage); } } diff --git a/tests/Tgstation.Server.Api.Tests/Models/TestErrorCodeExtensions.cs b/tests/Tgstation.Server.Api.Tests/Models/TestErrorCodeExtensions.cs index 5cdd445145..bdf09285f9 100644 --- a/tests/Tgstation.Server.Api.Tests/Models/TestErrorCodeExtensions.cs +++ b/tests/Tgstation.Server.Api.Tests/Models/TestErrorCodeExtensions.cs @@ -5,7 +5,7 @@ using System.Linq; namespace Tgstation.Server.Api.Models.Tests { /// - /// Tests for the . + /// Tests for the . /// [TestClass] public sealed class TestErrorCodeExtensions diff --git a/tests/Tgstation.Server.Client.Tests/Components/TestDreamDaemonClient.cs b/tests/Tgstation.Server.Client.Tests/Components/TestDreamDaemonClient.cs index 60d40191ad..91c762787b 100644 --- a/tests/Tgstation.Server.Client.Tests/Components/TestDreamDaemonClient.cs +++ b/tests/Tgstation.Server.Client.Tests/Components/TestDreamDaemonClient.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client.Components.Tests { @@ -16,19 +17,19 @@ namespace Tgstation.Server.Client.Components.Tests [TestMethod] public async Task TestStart() { - var example = new Job + var example = new JobResponse { Id = 347, StartedAt = DateTimeOffset.UtcNow }; - var inst = new Instance + var inst = new InstanceResponse { Id = 4958 }; var mockApiClient = new Mock(); - mockApiClient.Setup(x => x.Create(Routes.DreamDaemon, inst.Id, It.IsAny())).Returns(Task.FromResult(example)); + mockApiClient.Setup(x => x.Create(Routes.DreamDaemon, inst.Id.Value, It.IsAny())).Returns(Task.FromResult(example)); var client = new DreamDaemonClient(mockApiClient.Object, inst); diff --git a/tests/Tgstation.Server.Client.Tests/TestApiClient.cs b/tests/Tgstation.Server.Client.Tests/TestApiClient.cs index 29486a7535..5a5ce5aebb 100644 --- a/tests/Tgstation.Server.Client.Tests/TestApiClient.cs +++ b/tests/Tgstation.Server.Client.Tests/TestApiClient.cs @@ -1,4 +1,4 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Newtonsoft.Json; using Newtonsoft.Json.Converters; @@ -11,6 +11,7 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Client.Tests { @@ -20,7 +21,7 @@ namespace Tgstation.Server.Client.Tests [TestMethod] public async Task TestDeserializingByondModelsWork() { - var sample = new Byond + var sample = new ByondResponse { Version = new Version(511, 1385, 0) }; @@ -41,7 +42,7 @@ namespace Tgstation.Server.Client.Tests 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); + var result = await client.Read(Routes.Byond, default).ConfigureAwait(false); Assert.AreEqual(sample.Version, result.Version); Assert.AreEqual(0, result.Version.Build); } @@ -49,7 +50,7 @@ namespace Tgstation.Server.Client.Tests [TestMethod] public async Task TestUnrecognizedResponse() { - var sample = new Byond + var sample = new ByondResponse { Version = new Version(511, 1385) }; @@ -66,7 +67,7 @@ namespace Tgstation.Server.Client.Tests 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); + await Assert.ThrowsExceptionAsync(() => client.Read(Routes.Byond, default)).ConfigureAwait(false); } } } diff --git a/tests/Tgstation.Server.Tests/AdministrationTest.cs b/tests/Tgstation.Server.Tests/AdministrationTest.cs index cae7bc8763..2e54278069 100644 --- a/tests/Tgstation.Server.Tests/AdministrationTest.cs +++ b/tests/Tgstation.Server.Tests/AdministrationTest.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Client; namespace Tgstation.Server.Tests @@ -38,12 +39,12 @@ namespace Tgstation.Server.Tests Assert.IsTrue(logFile.LastModified <= downloadedTuple.Item1.LastModified); Assert.IsNull(logFile.FileTicket); - await ApiAssert.ThrowsException(() => client.GetLog(new LogFile + await ApiAssert.ThrowsException(() => client.GetLog(new LogFileResponse { Name = "very_fake_path.log" }, cancellationToken), ErrorCode.IOError); - await Assert.ThrowsExceptionAsync(() => client.GetLog(new LogFile + await Assert.ThrowsExceptionAsync(() => client.GetLog(new LogFileResponse { Name = "../out_of_bounds.file" }, cancellationToken)); @@ -51,7 +52,7 @@ namespace Tgstation.Server.Tests async Task TestRead(CancellationToken cancellationToken) { - Administration model; + AdministrationResponse model; try { model = await client.Read(cancellationToken).ConfigureAwait(false); diff --git a/tests/Tgstation.Server.Tests/Instance/ByondTest.cs b/tests/Tgstation.Server.Tests/Instance/ByondTest.cs index ca48a4edf5..03b4774087 100644 --- a/tests/Tgstation.Server.Tests/Instance/ByondTest.cs +++ b/tests/Tgstation.Server.Tests/Instance/ByondTest.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; using Tgstation.Server.Client; using Tgstation.Server.Client.Components; using Tgstation.Server.Host.Components.Byond; @@ -41,7 +42,7 @@ namespace Tgstation.Server.Tests.Instance async Task TestInstallFakeVersion(CancellationToken cancellationToken) { - var newModel = new Api.Models.Byond + var newModel = new ByondVersionRequest { Version = new Version(5011, 1385) }; @@ -52,13 +53,12 @@ namespace Tgstation.Server.Tests.Instance async Task TestInstallStable(CancellationToken cancellationToken) { - var newModel = new Api.Models.Byond + var newModel = new ByondVersionRequest { Version = TestVersion }; var test = await byondClient.SetActiveVersion(newModel, null, cancellationToken).ConfigureAwait(false); Assert.IsNotNull(test.InstallJob); - Assert.IsNull(test.Version); await WaitForJob(test.InstallJob, 120, false, null, cancellationToken).ConfigureAwait(false); var currentShit = await byondClient.ActiveVersion(cancellationToken).ConfigureAwait(false); Assert.AreEqual(newModel.Version.Semver(), currentShit.Version); @@ -78,7 +78,6 @@ namespace Tgstation.Server.Tests.Instance var allVersionsTask = byondClient.InstalledVersions(null, cancellationToken); var currentShit = await byondClient.ActiveVersion(cancellationToken).ConfigureAwait(false); Assert.IsNotNull(currentShit); - Assert.IsNull(currentShit.InstallJob); Assert.IsNull(currentShit.Version); var otherShit = await allVersionsTask.ConfigureAwait(false); Assert.IsNotNull(otherShit); @@ -102,7 +101,7 @@ namespace Tgstation.Server.Tests.Instance await byondInstaller.DownloadVersion(TestVersion, cancellationToken)); var test = await byondClient.SetActiveVersion( - new Api.Models.Byond + new ByondVersionRequest { Version = TestVersion, UploadCustomZip = true @@ -118,21 +117,21 @@ namespace Tgstation.Server.Tests.Instance Assert.AreEqual(new Version(TestVersion.Major, TestVersion.Minor, 1), newSettings.Version); // test a few switches - newSettings = await byondClient.SetActiveVersion(new Api.Models.Byond + var installResponse = await byondClient.SetActiveVersion(new ByondVersionRequest { Version = TestVersion }, null, cancellationToken); - Assert.IsNull(newSettings.InstallJob); - await ApiAssert.ThrowsException(() => byondClient.SetActiveVersion(new Api.Models.Byond + Assert.IsNull(installResponse.InstallJob); + await ApiAssert.ThrowsException(() => byondClient.SetActiveVersion(new ByondVersionRequest { Version = new Version(TestVersion.Major, TestVersion.Minor, 2) }, null, cancellationToken), ErrorCode.ByondNonExistentCustomVersion); - newSettings = await byondClient.SetActiveVersion(new Api.Models.Byond + installResponse = await byondClient.SetActiveVersion(new ByondVersionRequest { Version = new Version(TestVersion.Major, TestVersion.Minor, 1) }, null, cancellationToken); - Assert.IsNull(newSettings.InstallJob); + Assert.IsNull(installResponse.InstallJob); } } } diff --git a/tests/Tgstation.Server.Tests/Instance/ChatTest.cs b/tests/Tgstation.Server.Tests/Instance/ChatTest.cs index ef6b8837e7..e6c99c511a 100644 --- a/tests/Tgstation.Server.Tests/Instance/ChatTest.cs +++ b/tests/Tgstation.Server.Tests/Instance/ChatTest.cs @@ -5,6 +5,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; using Tgstation.Server.Client; using Tgstation.Server.Client.Components; @@ -33,7 +34,7 @@ namespace Tgstation.Server.Tests.Instance async Task RunIrc(CancellationToken cancellationToken) { - var firstBot = new ChatBot + var firstBotReq = new ChatBotCreateRequest { ConnectionString = Environment.GetEnvironmentVariable("TGS4_TEST_IRC_CONNECTION_STRING"), Enabled = false, @@ -43,11 +44,11 @@ namespace Tgstation.Server.Tests.Instance ChannelLimit = 1 }; - var csb = new IrcConnectionStringBuilder(firstBot.ConnectionString); + var csb = new IrcConnectionStringBuilder(firstBotReq.ConnectionString); - Assert.IsTrue(csb.Valid, $"Invalid IRC connection string: {firstBot.ConnectionString}"); + Assert.IsTrue(csb.Valid, $"Invalid IRC connection string: {firstBotReq.ConnectionString}"); - firstBot = await chatClient.Create(firstBot, cancellationToken); + var firstBot = await chatClient.Create(firstBotReq, cancellationToken); Assert.AreEqual(csb.ToString(), firstBot.ConnectionString); Assert.AreNotEqual(0, firstBot.Id); @@ -58,25 +59,31 @@ namespace Tgstation.Server.Tests.Instance var retrievedBot = await chatClient.GetId(firstBot, cancellationToken); Assert.AreEqual(firstBot.Id, retrievedBot.Id); - firstBot.Enabled = true; - var updatedBot = await chatClient.Update(firstBot, cancellationToken); + var updatedBot = await chatClient.Update(new ChatBotUpdateRequest + { + Id = firstBot.Id, + Enabled = true + }, cancellationToken); Assert.AreEqual(true, updatedBot.Enabled); - var channelId = Environment.GetEnvironmentVariable("TGS4_TEST_IRC_CHANNEL"); - firstBot.Channels = new List - { - new ChatChannel - { - IsAdminChannel = false, - IsUpdatesChannel = true, - IsWatchdogChannel = true, - Tag = "butt2", - IrcChannel = channelId - } - }; + var channelId = Environment.GetEnvironmentVariable("TGS4_TEST_IRC_CHANNEL");; - updatedBot = await chatClient.Update(firstBot, cancellationToken); + updatedBot = await chatClient.Update(new ChatBotUpdateRequest + { + Id = firstBot.Id, + Channels = new List + { + new ChatChannel + { + IsAdminChannel = false, + IsUpdatesChannel = true, + IsWatchdogChannel = true, + Tag = "butt2", + IrcChannel = channelId + } + } + }, cancellationToken); Assert.AreEqual(true, updatedBot.Enabled); Assert.IsNotNull(updatedBot.Channels); @@ -91,7 +98,7 @@ namespace Tgstation.Server.Tests.Instance async Task RunDiscord(CancellationToken cancellationToken) { - var firstBot = new ChatBot + var firstBotReq = new ChatBotCreateRequest { ConnectionString = new DiscordConnectionStringBuilder @@ -106,11 +113,11 @@ namespace Tgstation.Server.Tests.Instance ChannelLimit = 1 }; - var csb = new DiscordConnectionStringBuilder(firstBot.ConnectionString); + var csb = new DiscordConnectionStringBuilder(firstBotReq.ConnectionString); - Assert.IsTrue(csb.Valid, $"Invalid Discord connection string: {firstBot.ConnectionString}"); + Assert.IsTrue(csb.Valid, $"Invalid Discord connection string: {firstBotReq.ConnectionString}"); - firstBot = await chatClient.Create(firstBot, cancellationToken); + var firstBot = await chatClient.Create(firstBotReq, cancellationToken); Assert.AreEqual(csb.ToString(), firstBot.ConnectionString); Assert.AreNotEqual(0, firstBot.Id); @@ -121,8 +128,11 @@ namespace Tgstation.Server.Tests.Instance var retrievedBot = await chatClient.GetId(firstBot, cancellationToken); Assert.AreEqual(firstBot.Id, retrievedBot.Id); - firstBot.Enabled = true; - var updatedBot = await chatClient.Update(firstBot, cancellationToken); + var updatedBot = await chatClient.Update(new ChatBotUpdateRequest + { + Id = firstBot.Id, + Enabled = true + }, cancellationToken); Assert.AreEqual(true, updatedBot.Enabled); @@ -139,7 +149,21 @@ namespace Tgstation.Server.Tests.Instance } }; - updatedBot = await chatClient.Update(firstBot, cancellationToken); + updatedBot = await chatClient.Update(new ChatBotUpdateRequest + { + Id = firstBot.Id, + Channels = new List + { + new ChatChannel + { + IsAdminChannel = true, + IsUpdatesChannel = true, + IsWatchdogChannel = true, + Tag = "butt", + DiscordChannelId = channelId + } + } + }, cancellationToken); Assert.AreEqual(true, updatedBot.Enabled); Assert.IsNotNull(updatedBot.Channels); @@ -166,7 +190,7 @@ namespace Tgstation.Server.Tests.Instance async Task RunLimitTests(CancellationToken cancellationToken) { - await ApiAssert.ThrowsException(() => chatClient.Create(new ChatBot + await ApiAssert.ThrowsException(() => chatClient.Create(new ChatBotCreateRequest { Name = "asdf", ConnectionString = "asdf", @@ -174,37 +198,45 @@ namespace Tgstation.Server.Tests.Instance }, cancellationToken), ErrorCode.ChatBotMax); var bots = await chatClient.List(null, cancellationToken); - var discordBot = bots.First(bot => bot.Provider.Value == ChatProvider.Discord); + var ogDiscordBot = bots.First(bot => bot.Provider.Value == ChatProvider.Discord); ; + var discordBotReq = new ChatBotUpdateRequest + { + Id = ogDiscordBot.Id, + Channels = ogDiscordBot.Channels.ToList(), + ChannelLimit = 1 + }; // We limited chat bots and channels to 1 and 2 respectively, try violating them - discordBot.Channels.Add( + discordBotReq.Channels.Add( new ChatChannel { IsAdminChannel = true, IsUpdatesChannel = false, IsWatchdogChannel = true, Tag = "butt", - DiscordChannelId = discordBot.Channels.First().DiscordChannelId + DiscordChannelId = discordBotReq.Channels.First().DiscordChannelId }); - await ApiAssert.ThrowsException(() => chatClient.Update(discordBot, cancellationToken), ErrorCode.ChatBotMaxChannels); + await ApiAssert.ThrowsException(() => chatClient.Update(discordBotReq, cancellationToken), ErrorCode.ChatBotMaxChannels); - var oldChannels = discordBot.Channels; - discordBot.Channels = null; - discordBot.ChannelLimit = 0; - await ApiAssert.ThrowsException(() => chatClient.Update(discordBot, cancellationToken), ErrorCode.ChatBotMaxChannels); + var oldChannels = discordBotReq.Channels; + discordBotReq.Channels = null; + discordBotReq.ChannelLimit = 0; + await ApiAssert.ThrowsException(() => chatClient.Update(discordBotReq, cancellationToken), ErrorCode.ChatBotMaxChannels); - discordBot.Channels = oldChannels; - discordBot.ChannelLimit = null; - await ApiAssert.ThrowsException(() => chatClient.Update(discordBot, cancellationToken), ErrorCode.ChatBotMaxChannels); + discordBotReq.Channels = oldChannels; + discordBotReq.ChannelLimit = null; + await ApiAssert.ThrowsException(() => chatClient.Update(discordBotReq, cancellationToken), ErrorCode.ChatBotMaxChannels); - var instance = metadata.CloneMetadata(); - instance.ChatBotLimit = 0; - await ApiAssert.ThrowsException(() => instanceClient.Update(instance, cancellationToken), ErrorCode.ChatBotMax); + await ApiAssert.ThrowsException(() => instanceClient.Update(new InstanceUpdateRequest + { + Id = metadata.Id, + ChatBotLimit = 0 + }, cancellationToken), ErrorCode.ChatBotMax); - discordBot.ChannelLimit = 20; - discordBot.Channels = null; - await chatClient.Update(discordBot, cancellationToken); + discordBotReq.ChannelLimit = 20; + discordBotReq.Channels = null; + await chatClient.Update(discordBotReq, cancellationToken); } } } diff --git a/tests/Tgstation.Server.Tests/Instance/ConfigurationTest.cs b/tests/Tgstation.Server.Tests/Instance/ConfigurationTest.cs index b9e353a632..a368fcd616 100644 --- a/tests/Tgstation.Server.Tests/Instance/ConfigurationTest.cs +++ b/tests/Tgstation.Server.Tests/Instance/ConfigurationTest.cs @@ -8,6 +8,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; using Tgstation.Server.Client; using Tgstation.Server.Client.Components; @@ -24,7 +25,7 @@ namespace Tgstation.Server.Tests.Instance this.instance = instance ?? throw new ArgumentNullException(nameof(instance)); } - bool FileExists(ConfigurationFile file) + bool FileExists(IConfigurationFile file) { var tmp = (file.Path?.StartsWith('/') ?? false) ? '.' + file.Path : file.Path; var path = Path.Combine(instance.Path, "Configuration", tmp); @@ -35,7 +36,7 @@ namespace Tgstation.Server.Tests.Instance async Task TestDeleteDirectory(CancellationToken cancellationToken) { //try to delete non-existent - var TestDir = new ConfigurationFile + var TestDir = new ConfigurationFileRequest { Path = "/TestDeleteDir" }; @@ -45,7 +46,7 @@ namespace Tgstation.Server.Tests.Instance //try to delete non-empty const string TestString = "Hello world!"; using var uploadMs = new MemoryStream(Encoding.UTF8.GetBytes(TestString)); - var file = await configurationClient.Write(new ConfigurationFile + var file = await configurationClient.Write(new ConfigurationFileRequest { Path = TestDir.Path + "/test.txt" }, uploadMs, cancellationToken).ConfigureAwait(false); @@ -72,7 +73,11 @@ namespace Tgstation.Server.Tests.Instance await ApiAssert.ThrowsException(() => configurationClient.DeleteEmptyDirectory(TestDir, cancellationToken), ErrorCode.ConfigurationDirectoryNotEmpty).ConfigureAwait(false); file.FileTicket = null; - await configurationClient.Write(updatedFile, null, cancellationToken).ConfigureAwait(false); + await configurationClient.Write(new ConfigurationFileRequest + { + Path = updatedFile.Path, + LastReadHash = updatedFile.LastReadHash + }, null, cancellationToken).ConfigureAwait(false); Assert.IsFalse(FileExists(file)); await configurationClient.DeleteEmptyDirectory(TestDir, cancellationToken).ConfigureAwait(false); diff --git a/tests/Tgstation.Server.Tests/Instance/DeploymentTest.cs b/tests/Tgstation.Server.Tests/Instance/DeploymentTest.cs index 32c94dc8ae..0c78271c88 100644 --- a/tests/Tgstation.Server.Tests/Instance/DeploymentTest.cs +++ b/tests/Tgstation.Server.Tests/Instance/DeploymentTest.cs @@ -3,6 +3,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; using Tgstation.Server.Client; using Tgstation.Server.Client.Components; using Tgstation.Server.Host.System; @@ -35,7 +36,7 @@ namespace Tgstation.Server.Tests.Instance // by alphabetization rules, it should discover api_free here if (!new PlatformIdentifier().IsWindows) { - var updatedDM = await dreamMakerClient.Update(new DreamMaker + var updatedDM = await dreamMakerClient.Update(new DreamMakerRequest { ProjectName = "tests/DMAPI/ApiFree/api_free", ApiValidationPort = IntegrationTest.DMPort @@ -45,14 +46,14 @@ namespace Tgstation.Server.Tests.Instance } else { - var updatedDM = await dreamMakerClient.Update(new DreamMaker + var updatedDM = await dreamMakerClient.Update(new DreamMakerRequest { ApiValidationPort = IntegrationTest.DMPort }, cancellationToken); Assert.AreEqual(IntegrationTest.DMPort, updatedDM.ApiValidationPort); } - var updatedDD = await dreamDaemonClient.Update(new DreamDaemon + var updatedDD = await dreamDaemonClient.Update(new DreamDaemonRequest { StartupTimeout = 5, Port = IntegrationTest.DDPort @@ -60,12 +61,12 @@ namespace Tgstation.Server.Tests.Instance Assert.AreEqual(5U, updatedDD.StartupTimeout); Assert.AreEqual(IntegrationTest.DDPort, updatedDD.Port); - await ApiAssert.ThrowsException(() => dreamDaemonClient.Update(new DreamDaemon + await ApiAssert.ThrowsException(() => dreamDaemonClient.Update(new DreamDaemonRequest { Port = IntegrationTest.DMPort }, cancellationToken), ErrorCode.PortNotAvailable); - await ApiAssert.ThrowsException(() => dreamMakerClient.Update(new DreamMaker + await ApiAssert.ThrowsException(() => dreamMakerClient.Update(new DreamMakerRequest { ApiValidationPort = IntegrationTest.DDPort }, cancellationToken), ErrorCode.PortNotAvailable); @@ -74,7 +75,7 @@ namespace Tgstation.Server.Tests.Instance await WaitForJob(deployJob, 30, true, ErrorCode.DreamMakerNeverValidated, cancellationToken); const string FailProject = "tests/DMAPI/BuildFail/build_fail"; - var updated = await dreamMakerClient.Update(new DreamMaker + var updated = await dreamMakerClient.Update(new DreamMakerRequest { ProjectName = FailProject }, cancellationToken); @@ -84,7 +85,7 @@ namespace Tgstation.Server.Tests.Instance deployJob = await dreamMakerClient.Compile(cancellationToken); await WaitForJob(deployJob, 30, true, ErrorCode.DreamMakerExitCode, cancellationToken); - await dreamMakerClient.Update(new DreamMaker + await dreamMakerClient.Update(new DreamMakerRequest { ProjectName = "tests/DMAPI/ThisDoesntExist/this_doesnt_exist" }, cancellationToken); diff --git a/tests/Tgstation.Server.Tests/Instance/InstanceTest.cs b/tests/Tgstation.Server.Tests/Instance/InstanceTest.cs index 77bacd7da6..dc210dd6da 100644 --- a/tests/Tgstation.Server.Tests/Instance/InstanceTest.cs +++ b/tests/Tgstation.Server.Tests/Instance/InstanceTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Client; @@ -20,7 +20,7 @@ namespace Tgstation.Server.Tests.Instance public async Task RunTests(CancellationToken cancellationToken) { var byondTest = new ByondTest(instanceClient.Byond, instanceClient.Jobs, instanceClient.Metadata); - var chatTest = new ChatTest(instanceClient.ChatBots, instanceManagerClient, instanceClient.Metadata.CloneMetadata()); + var chatTest = new ChatTest(instanceClient.ChatBots, instanceManagerClient, instanceClient.Metadata); var configTest = new ConfigurationTest(instanceClient.Configuration, instanceClient.Metadata); var repoTest = new RepositoryTest(instanceClient.Repository, instanceClient.Jobs); var dmTest = new DeploymentTest(instanceClient.DreamMaker, instanceClient.DreamDaemon, instanceClient.Jobs); diff --git a/tests/Tgstation.Server.Tests/Instance/JobsRequiredTest.cs b/tests/Tgstation.Server.Tests/Instance/JobsRequiredTest.cs index 6594f345e2..3532e83cfd 100644 --- a/tests/Tgstation.Server.Tests/Instance/JobsRequiredTest.cs +++ b/tests/Tgstation.Server.Tests/Instance/JobsRequiredTest.cs @@ -1,10 +1,11 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Collections.Generic; using System.Text; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Client.Components; namespace Tgstation.Server.Tests.Instance @@ -18,7 +19,7 @@ namespace Tgstation.Server.Tests.Instance this.JobsClient = jobsClient; } - public async Task WaitForJob(Job originalJob, int timeout, bool expectFailure, ErrorCode? expectedCode, CancellationToken cancellationToken) + public async Task WaitForJob(JobResponse originalJob, int timeout, bool expectFailure, ErrorCode? expectedCode, CancellationToken cancellationToken) { var job = originalJob; do @@ -45,7 +46,7 @@ namespace Tgstation.Server.Tests.Instance return job; } - protected async Task WaitForJobProgressThenCancel(Job originalJob, int timeout, CancellationToken cancellationToken) + protected async Task WaitForJobProgressThenCancel(JobResponse originalJob, int timeout, CancellationToken cancellationToken) { var job = originalJob; do diff --git a/tests/Tgstation.Server.Tests/Instance/RepositoryTest.cs b/tests/Tgstation.Server.Tests/Instance/RepositoryTest.cs index f83c916ad6..2accf9c5ba 100644 --- a/tests/Tgstation.Server.Tests/Instance/RepositoryTest.cs +++ b/tests/Tgstation.Server.Tests/Instance/RepositoryTest.cs @@ -5,6 +5,8 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Client; using Tgstation.Server.Client.Components; @@ -45,13 +47,16 @@ namespace Tgstation.Server.Tests.Instance Assert.IsNull(initalRepo.ActiveJob); const string Origin = "https://github.com/tgstation/tgstation-server"; - initalRepo.Origin = new Uri(Origin); - initalRepo.Reference = workingBranch; + var cloneRequest = new RepositoryCreateRequest + { + Origin = new Uri(Origin), + Reference = workingBranch, + }; - var clone = await repositoryClient.Clone(initalRepo, cancellationToken).ConfigureAwait(false); + var clone = await repositoryClient.Clone(cloneRequest, cancellationToken).ConfigureAwait(false); await ApiAssert.ThrowsException(() => repositoryClient.Read(cancellationToken), ErrorCode.RepoCloning); Assert.IsNotNull(clone); - Assert.AreEqual(initalRepo.Origin, clone.Origin); + Assert.AreEqual(cloneRequest.Origin, clone.Origin); Assert.AreEqual(workingBranch, clone.Reference); Assert.IsNull(clone.RevisionInformation); Assert.IsNotNull(clone.ActiveJob); @@ -62,12 +67,12 @@ namespace Tgstation.Server.Tests.Instance Assert.IsNotNull(secondRead); Assert.IsNull(secondRead.ActiveJob); - clone = await repositoryClient.Clone(initalRepo, cancellationToken).ConfigureAwait(false); + clone = await repositoryClient.Clone(cloneRequest, cancellationToken).ConfigureAwait(false); await WaitForJob(clone.ActiveJob, 9000, false, null, cancellationToken).ConfigureAwait(false); var readAfterClone = await repositoryClient.Read(cancellationToken); - Assert.AreEqual(initalRepo.Origin, readAfterClone.Origin); + Assert.AreEqual(cloneRequest.Origin, readAfterClone.Origin); Assert.AreEqual(workingBranch, readAfterClone.Reference); Assert.IsNotNull(readAfterClone.RevisionInformation); Assert.IsNotNull(readAfterClone.RevisionInformation.ActiveTestMerges); @@ -81,32 +86,21 @@ namespace Tgstation.Server.Tests.Instance Assert.AreEqual(readAfterClone.RevisionInformation.CommitSha, readAfterClone.RevisionInformation.OriginCommitSha); Assert.AreNotEqual(default, readAfterClone.RevisionInformation.Timestamp); - readAfterClone.Origin = new Uri("https://github.com/tgstation/tgstation"); - await ApiAssert.ThrowsException(() => repositoryClient.Update(readAfterClone, cancellationToken), ErrorCode.RepoCantChangeOrigin); - readAfterClone.Origin = new Uri(Origin); - // checkout V3 and back - readAfterClone.Reference = "V3"; - var updated = await Checkout(readAfterClone, false, true, cancellationToken); + var updated = await Checkout(new RepositoryUpdateRequest { Reference = "V3" }, false, true, cancellationToken); // Specific SHA - updated.CheckoutSha = "f43f5bd"; - await ApiAssert.ThrowsException(() => Checkout(updated, false, false, cancellationToken), ErrorCode.RepoMismatchShaAndReference); - updated.Reference = null; - updated = await Checkout(updated, false, false, cancellationToken); + await ApiAssert.ThrowsException(() => Checkout(new RepositoryUpdateRequest { Reference = "V3", CheckoutSha = "f43f5bd" }, false, false, cancellationToken), ErrorCode.RepoMismatchShaAndReference); + updated = await Checkout(new RepositoryUpdateRequest { CheckoutSha = "f43f5bd" }, false, false, cancellationToken); // Fake SHA - updated.Reference = null; - updated.CheckoutSha = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; - updated = await Checkout(updated, true, false, cancellationToken); + updated = await Checkout(new RepositoryUpdateRequest { CheckoutSha = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" }, true, false, cancellationToken); // Fake ref - updated.Reference = "Tgs4IntegrationTestFakeBranchNeverNameABranchThis"; - updated = await Checkout(updated, true, true, cancellationToken); + updated = await Checkout(new RepositoryUpdateRequest { Reference = "Tgs4IntegrationTestFakeBranchNeverNameABranchThis" }, true, true, cancellationToken); // Back - updated.Reference = workingBranch; - updated = await Checkout(updated, false, true, cancellationToken); + updated = await Checkout(new RepositoryUpdateRequest { Reference = workingBranch }, false, true, cancellationToken); var testPRString = Environment.GetEnvironmentVariable("TGS4_TEST_PULL_REQUEST_NUMBER"); if (String.IsNullOrWhiteSpace(testPRString)) @@ -125,7 +119,7 @@ namespace Tgstation.Server.Tests.Instance await TestMergeTests(updated, prNumber, cancellationToken); } - async Task Checkout(Repository updated, bool expectFailure, bool isRef, CancellationToken cancellationToken) + async Task Checkout(RepositoryUpdateRequest updated, bool expectFailure, bool isRef, CancellationToken cancellationToken) { var newRef = isRef ? updated.Reference : updated.CheckoutSha; var checkingOut = await repositoryClient.Update(updated, cancellationToken); @@ -144,19 +138,20 @@ namespace Tgstation.Server.Tests.Instance return result; } - async Task TestMergeTests(Repository repository, int prNumber, CancellationToken cancellationToken) + async Task TestMergeTests(RepositoryResponse repository, int prNumber, CancellationToken cancellationToken) { - repository.NewTestMerges = new List - { - new TestMergeParameters - { - Number = prNumber - } - }; - var orignCommit = repository.RevisionInformation.OriginCommitSha; - var numberOnlyMerging = await repositoryClient.Update(repository, cancellationToken); + var numberOnlyMerging = await repositoryClient.Update(new RepositoryUpdateRequest + { + NewTestMerges = new List + { + new TestMergeParameters + { + Number = prNumber + } + } + }, cancellationToken); Assert.IsNotNull(numberOnlyMerging.ActiveJob); Assert.IsTrue(numberOnlyMerging.ActiveJob.Description.Contains(prNumber.ToString())); @@ -181,19 +176,22 @@ namespace Tgstation.Server.Tests.Instance Assert.AreNotEqual(orignCommit, withMerge.RevisionInformation.CommitSha); // Reset, do it again with a comment and specific sha - withMerge.UpdateFromOrigin = true; - withMerge.Reference = repository.Reference; - withMerge.NewTestMerges = new List + var updateRequ = new RepositoryUpdateRequest { - new TestMergeParameters + UpdateFromOrigin = true, + Reference = repository.Reference, + NewTestMerges = new List { - Number = prNumber, - Comment = "asdffdsa", - TargetCommitSha = prRevision + new TestMergeParameters + { + Number = prNumber, + Comment = "asdffdsa", + TargetCommitSha = prRevision + } } }; - var mergingAgain = await repositoryClient.Update(withMerge, cancellationToken); + var mergingAgain = await repositoryClient.Update(updateRequ, cancellationToken); Assert.IsNotNull(mergingAgain.ActiveJob); await WaitForJob(mergingAgain.ActiveJob, 60, false, null, cancellationToken); diff --git a/tests/Tgstation.Server.Tests/Instance/WatchdogTest.cs b/tests/Tgstation.Server.Tests/Instance/WatchdogTest.cs index 80b88a3bde..3df1e9b84b 100644 --- a/tests/Tgstation.Server.Tests/Instance/WatchdogTest.cs +++ b/tests/Tgstation.Server.Tests/Instance/WatchdogTest.cs @@ -11,6 +11,8 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Client; using Tgstation.Server.Client.Components; using Tgstation.Server.Host.Components.Interop; @@ -34,19 +36,19 @@ namespace Tgstation.Server.Tests.Instance { global::System.Console.WriteLine("TEST: START WATCHDOG TESTS"); // Increase startup timeout, disable heartbeats - var initialSettings = await instanceClient.DreamDaemon.Update(new DreamDaemon + var initialSettings = await instanceClient.DreamDaemon.Update(new DreamDaemonRequest { StartupTimeout = 60, HeartbeatSeconds = 0, Port = IntegrationTest.DDPort }, cancellationToken); - await ApiAssert.ThrowsException(() => instanceClient.DreamDaemon.Update(new DreamDaemon + await ApiAssert.ThrowsException(() => instanceClient.DreamDaemon.Update(new DreamDaemonRequest { Port = 0 }, cancellationToken), ErrorCode.ModelValidationFailure); - await ApiAssert.ThrowsException(() => instanceClient.DreamDaemon.Update(new DreamDaemon + await ApiAssert.ThrowsException(() => instanceClient.DreamDaemon.Update(new DreamDaemonRequest { SoftShutdown = true, SoftRestart = true @@ -93,7 +95,7 @@ namespace Tgstation.Server.Tests.Instance KillDD(false); }); - Job job; + JobResponse job; try { await killTaskStarted.Task; @@ -152,7 +154,7 @@ namespace Tgstation.Server.Tests.Instance { global::System.Console.WriteLine("TEST: WATCHDOG BASIC TEST"); - var daemonStatus = await instanceClient.DreamDaemon.Update(new DreamDaemon + var daemonStatus = await instanceClient.DreamDaemon.Update(new DreamDaemonRequest { AdditionalParameters = "test=bababooey" }, cancellationToken); @@ -165,7 +167,7 @@ namespace Tgstation.Server.Tests.Instance Assert.AreEqual(DMApiConstants.InteropVersion, daemonStatus.ActiveCompileJob.DMApiVersion); Assert.AreEqual(DreamDaemonSecurity.Trusted, daemonStatus.ActiveCompileJob.MinimumSecurityLevel); - Job startJob; + JobResponse startJob; if (new PlatformIdentifier().IsWindows) // Can't get address reuse to trigger on linux for some reason using (var blockSocket = new Socket(SocketType.Stream, ProtocolType.Tcp)) { @@ -195,7 +197,7 @@ namespace Tgstation.Server.Tests.Instance await CheckDMApiFail(daemonStatus.ActiveCompileJob, cancellationToken); - daemonStatus = await instanceClient.DreamDaemon.Update(new DreamDaemon + daemonStatus = await instanceClient.DreamDaemon.Update(new DreamDaemonRequest { AdditionalParameters = String.Empty }, cancellationToken); @@ -206,7 +208,7 @@ namespace Tgstation.Server.Tests.Instance { System.Console.WriteLine("TEST: WATCHDOG HEARTBEAT TEST"); // enable heartbeats - await instanceClient.DreamDaemon.Update(new DreamDaemon + await instanceClient.DreamDaemon.Update(new DreamDaemonRequest { HeartbeatSeconds = 1, }, cancellationToken); @@ -235,7 +237,7 @@ namespace Tgstation.Server.Tests.Instance await Task.WhenAny(Task.Delay(20000), ourProcessHandler.Lifetime); Assert.IsFalse(ddProc.HasExited); - await instanceClient.DreamDaemon.Update(new DreamDaemon + await instanceClient.DreamDaemon.Update(new DreamDaemonRequest { SoftShutdown = true }, cancellationToken); @@ -259,13 +261,13 @@ namespace Tgstation.Server.Tests.Instance while (timeout > 0); // disable heartbeats - await instanceClient.DreamDaemon.Update(new DreamDaemon + await instanceClient.DreamDaemon.Update(new DreamDaemonRequest { HeartbeatSeconds = 0, }, cancellationToken); } - async Task StartDD(CancellationToken cancellationToken) + async Task StartDD(CancellationToken cancellationToken) { // integration tests may take a while to release the port using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); @@ -392,7 +394,7 @@ namespace Tgstation.Server.Tests.Instance await WaitForJob(startJob, 70, false, null, cancellationToken); var byondInstallJobTask = instanceClient.Byond.SetActiveVersion( - new Api.Models.Byond + new ByondVersionRequest { Version = versionToInstall }, @@ -486,7 +488,7 @@ namespace Tgstation.Server.Tests.Instance return ddProc != null; } - async Task TellWorldToReboot(CancellationToken cancellationToken) + async Task TellWorldToReboot(CancellationToken cancellationToken) { var daemonStatus = await instanceClient.DreamDaemon.Read(cancellationToken); var initialCompileJob = daemonStatus.ActiveCompileJob; @@ -527,9 +529,9 @@ namespace Tgstation.Server.Tests.Instance return daemonStatus; } - async Task DeployTestDme(string dmeName, DreamDaemonSecurity deploymentSecurity, bool requireApi, CancellationToken cancellationToken) + async Task DeployTestDme(string dmeName, DreamDaemonSecurity deploymentSecurity, bool requireApi, CancellationToken cancellationToken) { - var refreshed = await instanceClient.DreamMaker.Update(new DreamMaker + var refreshed = await instanceClient.DreamMaker.Update(new DreamMakerRequest { ApiValidationSecurityLevel = deploymentSecurity, ProjectName = $"tests/DMAPI/{dmeName}", @@ -551,7 +553,7 @@ namespace Tgstation.Server.Tests.Instance async Task GracefulWatchdogShutdown(uint timeout, CancellationToken cancellationToken) { - await instanceClient.DreamDaemon.Update(new DreamDaemon + await instanceClient.DreamDaemon.Update(new DreamDaemonRequest { SoftShutdown = true }, cancellationToken); @@ -572,7 +574,7 @@ namespace Tgstation.Server.Tests.Instance while (timeout > 0); } - async Task CheckDMApiFail(CompileJob compileJob, CancellationToken cancellationToken) + async Task CheckDMApiFail(CompileJobResponse compileJob, CancellationToken cancellationToken) { var failFile = Path.Combine(instanceClient.Metadata.Path, "Game", compileJob.DirectoryName.Value.ToString(), "A", Path.GetDirectoryName(compileJob.DmeName), "test_fail_reason.txt"); if (!File.Exists(failFile)) diff --git a/tests/Tgstation.Server.Tests/InstanceManagerTest.cs b/tests/Tgstation.Server.Tests/InstanceManagerTest.cs index 495b086304..82d99d3ed9 100644 --- a/tests/Tgstation.Server.Tests/InstanceManagerTest.cs +++ b/tests/Tgstation.Server.Tests/InstanceManagerTest.cs @@ -5,6 +5,8 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Api.Rights; using Tgstation.Server.Client; using Tgstation.Server.Host.Controllers; @@ -26,7 +28,7 @@ namespace Tgstation.Server.Tests this.testRootPath = testRootPath ?? throw new ArgumentNullException(nameof(testRootPath)); } - public Task CreateTestInstance(CancellationToken cancellationToken) => instanceManagerClient.CreateOrAttach(new Api.Models.Instance + public Task CreateTestInstance(CancellationToken cancellationToken) => instanceManagerClient.CreateOrAttach(new InstanceCreateRequest { Name = TestInstanceName, Path = Path.Combine(testRootPath, Guid.NewGuid().ToString()), @@ -34,6 +36,17 @@ namespace Tgstation.Server.Tests ChatBotLimit = 2 }, cancellationToken); + static TRequestType FromResponse(InstanceResponse response) where TRequestType : Api.Models.Instance, new() => new TRequestType + { + Id = response.Id, + Path = response.Path, + AutoUpdateInterval = response.AutoUpdateInterval, + ChatBotLimit = response.ChatBotLimit, + ConfigurationType = response.ConfigurationType, + Name = response.Name, + Online = response.Online + }; + public async Task RunPreInstanceTest(CancellationToken cancellationToken) { var firstTest = await CreateTestInstance(cancellationToken).ConfigureAwait(false); @@ -50,7 +63,7 @@ namespace Tgstation.Server.Tests Directory.CreateDirectory(testNonEmpty); var testFile = Path.Combine(testNonEmpty, "asdf"); await File.WriteAllBytesAsync(testFile, Array.Empty(), cancellationToken).ConfigureAwait(false); - await ApiAssert.ThrowsException(() => instanceManagerClient.CreateOrAttach(new Api.Models.Instance + await ApiAssert.ThrowsException(() => instanceManagerClient.CreateOrAttach(new InstanceCreateRequest { Path = testNonEmpty, Name = "NonEmptyTest" @@ -58,24 +71,24 @@ namespace Tgstation.Server.Tests //check it works for truly empty directories File.Delete(testFile); - var secondTry = await instanceManagerClient.CreateOrAttach(new Api.Models.Instance + var secondTry = await instanceManagerClient.CreateOrAttach(new InstanceCreateRequest { Path = Path.Combine(testRootPath, Guid.NewGuid().ToString()), Name = "NonEmptyTest" }, cancellationToken).ConfigureAwait(false); - await Assert.ThrowsExceptionAsync(() => instanceManagerClient.CreateOrAttach(firstTest, cancellationToken)).ConfigureAwait(false); + await Assert.ThrowsExceptionAsync(() => instanceManagerClient.CreateOrAttach(FromResponse(firstTest), cancellationToken)).ConfigureAwait(false); Assert.IsTrue(Directory.Exists(firstTest.Path)); //can't create instances in installation directory - await ApiAssert.ThrowsException(() => instanceManagerClient.CreateOrAttach(new Api.Models.Instance + await ApiAssert.ThrowsException(() => instanceManagerClient.CreateOrAttach(new InstanceCreateRequest { Path = "./A/Local/Path", Name = "NoInstallDirTest" }, cancellationToken), ErrorCode.InstanceAtConflictingPath).ConfigureAwait(false); //can't create instances as children of other instances - await ApiAssert.ThrowsException(() => instanceManagerClient.CreateOrAttach(new Api.Models.Instance + await ApiAssert.ThrowsException(() => instanceManagerClient.CreateOrAttach(new InstanceCreateRequest { Path = Path.Combine(firstTest.Path, "subdir"), Name = "NoOtherInstanceDirTest" @@ -83,19 +96,19 @@ namespace Tgstation.Server.Tests Assert.IsTrue(Directory.Exists(firstTest.Path)); //can't move to existent directories - await ApiAssert.ThrowsException(() => instanceManagerClient.Update(new Api.Models.Instance + await ApiAssert.ThrowsException(() => instanceManagerClient.Update(new InstanceUpdateRequest { Id = firstTest.Id, Path = testNonEmpty }, cancellationToken), ErrorCode.InstanceAtExistingPath).ConfigureAwait(false); - await ApiAssert.ThrowsException(() => instanceManagerClient.GrantPermissions(new Api.Models.Instance + await ApiAssert.ThrowsException(() => instanceManagerClient.GrantPermissions(new InstanceUpdateRequest { Id = 3482974, }, cancellationToken), ErrorCode.ResourceNotPresent).ConfigureAwait(false); // test can't create instance outside of whitelist - await ApiAssert.ThrowsException(() => instanceManagerClient.CreateOrAttach(new Api.Models.Instance + await ApiAssert.ThrowsException(() => instanceManagerClient.CreateOrAttach(new InstanceCreateRequest { Name = "TestInstanceOutsideOfWhitelist", Path = Path.Combine(testRootPath, "..", Guid.NewGuid().ToString()), @@ -106,7 +119,7 @@ namespace Tgstation.Server.Tests //test basic move Directory.Delete(testNonEmpty); var initialPath = firstTest.Path; - firstTest = await instanceManagerClient.Update(new Api.Models.Instance + firstTest = await instanceManagerClient.Update(new InstanceUpdateRequest { Id = firstTest.Id, Path = testNonEmpty @@ -124,13 +137,13 @@ namespace Tgstation.Server.Tests //online it for real for component tests firstTest.Online = true; firstTest.ConfigurationType = ConfigurationType.HostWrite; - firstTest = await instanceManagerClient.Update(firstTest, cancellationToken).ConfigureAwait(false); + firstTest = await instanceManagerClient.Update(FromResponse(firstTest), cancellationToken).ConfigureAwait(false); Assert.AreEqual(true, firstTest.Online); Assert.AreEqual(ConfigurationType.HostWrite, firstTest.ConfigurationType); Assert.IsTrue(Directory.Exists(firstTest.Path)); //can't move online instance - await ApiAssert.ThrowsException(() => instanceManagerClient.Update(new Api.Models.Instance + await ApiAssert.ThrowsException(() => instanceManagerClient.Update(new InstanceUpdateRequest { Id = firstTest.Id, Path = initialPath @@ -152,7 +165,7 @@ namespace Tgstation.Server.Tests await Assert.ThrowsExceptionAsync(() => instanceClient.PermissionSets.Read(cancellationToken)).ConfigureAwait(false); - await instanceManagerClient.GrantPermissions(new Api.Models.Instance + await instanceManagerClient.GrantPermissions(new InstanceUpdateRequest { Id = firstTest.Id }, cancellationToken).ConfigureAwait(false); @@ -164,7 +177,7 @@ namespace Tgstation.Server.Tests await ApiAssert.ThrowsException(() => instanceManagerClient.Detach(firstTest, cancellationToken), ErrorCode.InstanceDetachOnline).ConfigureAwait(false); firstTest.Online = false; - firstTest = await instanceManagerClient.Update(firstTest, cancellationToken).ConfigureAwait(false); + firstTest = await instanceManagerClient.Update(FromResponse(firstTest), cancellationToken).ConfigureAwait(false); await instanceManagerClient.Detach(firstTest, cancellationToken).ConfigureAwait(false); @@ -172,11 +185,11 @@ namespace Tgstation.Server.Tests Assert.IsTrue(File.Exists(attachPath)); //can recreate detached instance - firstTest = await instanceManagerClient.CreateOrAttach(firstTest, cancellationToken).ConfigureAwait(false); + firstTest = await instanceManagerClient.CreateOrAttach(FromResponse(firstTest), cancellationToken).ConfigureAwait(false); // Test updating only with SetChatBotLimit works var current = await usersClient.Read(cancellationToken); - var update = new UserUpdate + var update = new UserUpdateRequest { Id = current.Id, PermissionSet = new PermissionSet @@ -186,7 +199,7 @@ namespace Tgstation.Server.Tests } }; await usersClient.Update(update, cancellationToken); - var update2 = new Api.Models.Instance + var update2 = new InstanceUpdateRequest { Id = firstTest.Id, ChatBotLimit = 77 @@ -199,7 +212,7 @@ namespace Tgstation.Server.Tests //but only if the attach file exists await instanceManagerClient.Detach(firstTest, cancellationToken).ConfigureAwait(false); File.Delete(attachPath); - await ApiAssert.ThrowsException(() => instanceManagerClient.CreateOrAttach(firstTest, cancellationToken), ErrorCode.InstanceAtExistingPath).ConfigureAwait(false); + await ApiAssert.ThrowsException(() => instanceManagerClient.CreateOrAttach(FromResponse(firstTest), cancellationToken), ErrorCode.InstanceAtExistingPath).ConfigureAwait(false); } } } diff --git a/tests/Tgstation.Server.Tests/IntegrationTest.cs b/tests/Tgstation.Server.Tests/IntegrationTest.cs index 26c192b618..a61532bca8 100644 --- a/tests/Tgstation.Server.Tests/IntegrationTest.cs +++ b/tests/Tgstation.Server.Tests/IntegrationTest.cs @@ -21,6 +21,8 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Api.Rights; using Tgstation.Server.Client; using Tgstation.Server.Host.Components.Events; @@ -65,12 +67,12 @@ namespace Tgstation.Server.Tests using var response = await httpClient.SendAsync(request, cancellationToken); Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); - var message = JsonConvert.DeserializeObject(content); + var message = JsonConvert.DeserializeObject(content); Assert.AreEqual(ErrorCode.OAuthProviderDisabled, message.ErrorCode); } //attempt to update to stable - await adminClient.Administration.Update(new Administration + await adminClient.Administration.Update(new ServerUpdateRequest { NewVersion = testUpdateVersion }, cancellationToken).ConfigureAwait(false); @@ -162,7 +164,7 @@ namespace Tgstation.Server.Tests async Task WaitForSwarmServerUpdate() { - ServerInformation serverInformation; + ServerInformationResponse serverInformation; do { await Task.Delay(TimeSpan.FromSeconds(10)); @@ -171,7 +173,7 @@ namespace Tgstation.Server.Tests while (serverInformation.SwarmServers.Count == 1); } - static void CheckInfo(ServerInformation serverInformation) + static void CheckInfo(ServerInformationResponse serverInformation) { Assert.IsNotNull(serverInformation.SwarmServers); Assert.AreEqual(3, serverInformation.SwarmServers.Count); @@ -205,7 +207,7 @@ namespace Tgstation.Server.Tests CheckInfo(node2Info); // check user info is shared - var newUser = await node2Client.Users.Create(new UserUpdate + var newUser = await node2Client.Users.Create(new UserCreateRequest { Name = "asdf", Password = "asdfasdfasdfasdf", @@ -230,7 +232,7 @@ namespace Tgstation.Server.Tests // check instance info is not shared var controllerInstance = await controllerClient.Instances.CreateOrAttach( - new Api.Models.Instance + new InstanceCreateRequest { Name = "ControllerInstance", Path = Path.Combine(controller.Directory, "ControllerInstance") @@ -238,7 +240,7 @@ namespace Tgstation.Server.Tests cancellationToken); var node2Instance = await node2Client.Instances.CreateOrAttach( - new Api.Models.Instance + new InstanceCreateRequest { Name = "Node2Instance", Path = Path.Combine(node2.Directory, "Node2Instance") @@ -259,7 +261,7 @@ namespace Tgstation.Server.Tests // test update var testUpdateVersion = new Version(4, 8, 1); await node1Client.Administration.Update( - new Administration + new ServerUpdateRequest { NewVersion = testUpdateVersion }, @@ -294,7 +296,7 @@ namespace Tgstation.Server.Tests using var node2Client2 = await CreateAdminClient(node2.Url, cancellationToken); await controllerClient2.Administration.Update( - new Administration + new ServerUpdateRequest { NewVersion = testUpdateVersion }, @@ -375,7 +377,7 @@ namespace Tgstation.Server.Tests async Task WaitForSwarmServerUpdate(IServerClient client, int currentServerCount) { - ServerInformation serverInformation; + ServerInformationResponse serverInformation; do { await Task.Delay(TimeSpan.FromSeconds(10)); @@ -384,7 +386,7 @@ namespace Tgstation.Server.Tests while (serverInformation.SwarmServers.Count == currentServerCount); } - static void CheckInfo(ServerInformation serverInformation) + static void CheckInfo(ServerInformationResponse serverInformation) { Assert.IsNotNull(serverInformation.SwarmServers); Assert.AreEqual(3, serverInformation.SwarmServers.Count); @@ -473,14 +475,14 @@ namespace Tgstation.Server.Tests Assert.IsNotNull(controllerInfo.SwarmServers.SingleOrDefault(x => x.Identifier == "node2")); // update should fail - await controllerClient2.Administration.Update(new Administration + await controllerClient2.Administration.Update(new ServerUpdateRequest { NewVersion = new Version(4, 6, 2) }, cancellationToken); async Task WaitForUpdateFailure() { - ServerInformation serverInformation; + ServerInformationResponse serverInformation; serverInformation = await controllerClient2.ServerInformation(cancellationToken); while (serverInformation.UpdateInProgress) { @@ -538,8 +540,8 @@ namespace Tgstation.Server.Tests Console.WriteLine($"TEST: CreateAdminClient attempt {I}..."); return await clientFactory.CreateFromLogin( url, - User.AdminName, - User.DefaultAdminPassword, + DefaultCredentials.AdminUserName, + DefaultCredentials.DefaultAdminUserPassword, attemptLoginRefresh: false, cancellationToken: cancellationToken) .ConfigureAwait(false); @@ -835,7 +837,7 @@ namespace Tgstation.Server.Tests Assert.AreEqual(WatchdogStatus.Online, dd.Status.Value); await instanceClient.DreamDaemon.Shutdown(cancellationToken); - await instanceClient.DreamDaemon.Update(new DreamDaemon + await instanceClient.DreamDaemon.Update(new DreamDaemonRequest { AutoStart = true }, cancellationToken); diff --git a/tests/Tgstation.Server.Tests/RawRequestTests.cs b/tests/Tgstation.Server.Tests/RawRequestTests.cs index a9e308e73b..125092addf 100644 --- a/tests/Tgstation.Server.Tests/RawRequestTests.cs +++ b/tests/Tgstation.Server.Tests/RawRequestTests.cs @@ -11,6 +11,7 @@ using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Client; using Tgstation.Server.Host; @@ -49,7 +50,7 @@ namespace Tgstation.Server.Tests using var response = await httpClient.SendAsync(request, cancellationToken); Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); - var message = JsonConvert.DeserializeObject(content); + var message = JsonConvert.DeserializeObject(content); Assert.AreEqual(ErrorCode.BadHeaders, message.ErrorCode); } @@ -62,7 +63,7 @@ namespace Tgstation.Server.Tests using var response = await httpClient.SendAsync(request, cancellationToken); Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); - var message = JsonConvert.DeserializeObject(content); + var message = JsonConvert.DeserializeObject(content); Assert.AreEqual(ApiHeaders.Version, message.ApiVersion); } @@ -76,7 +77,7 @@ namespace Tgstation.Server.Tests using var response = await httpClient.SendAsync(request, cancellationToken); Assert.AreEqual(HttpStatusCode.UpgradeRequired, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); - var message = JsonConvert.DeserializeObject(content); + var message = JsonConvert.DeserializeObject(content); Assert.AreEqual(ErrorCode.ApiMismatch, message.ErrorCode); } @@ -90,7 +91,7 @@ namespace Tgstation.Server.Tests using var response = await httpClient.SendAsync(request, cancellationToken); Assert.AreEqual(HttpStatusCode.UpgradeRequired, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); - var message = JsonConvert.DeserializeObject(content); + var message = JsonConvert.DeserializeObject(content); Assert.AreEqual(ErrorCode.ApiMismatch, message.ErrorCode); } @@ -108,7 +109,7 @@ namespace Tgstation.Server.Tests using var response = await httpClient.SendAsync(request, cancellationToken); Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); - var message = JsonConvert.DeserializeObject(content); + var message = JsonConvert.DeserializeObject(content); Assert.AreEqual(ErrorCode.ModelValidationFailure, message.ErrorCode); } @@ -132,7 +133,7 @@ namespace Tgstation.Server.Tests using var response = await httpClient.SendAsync(request, cancellationToken); Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); - var message = JsonConvert.DeserializeObject(content); + var message = JsonConvert.DeserializeObject(content); Assert.AreEqual(ErrorCode.InstanceHeaderRequired, message.ErrorCode); } @@ -146,7 +147,7 @@ namespace Tgstation.Server.Tests using var response = await httpClient.SendAsync(request, cancellationToken); Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); - var message = JsonConvert.DeserializeObject(content); + var message = JsonConvert.DeserializeObject(content); Assert.AreEqual(ErrorCode.BadHeaders, message.ErrorCode); } @@ -161,7 +162,7 @@ namespace Tgstation.Server.Tests using var response = await httpClient.SendAsync(request, cancellationToken); Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); - var message = JsonConvert.DeserializeObject(content); + var message = JsonConvert.DeserializeObject(content); Assert.AreEqual(ErrorCode.BadHeaders, message.ErrorCode); } } @@ -180,7 +181,7 @@ namespace Tgstation.Server.Tests Assert.AreEqual(RuntimeInformation.IsOSPlatform(OSPlatform.Windows), serverInfo.WindowsHost); //check that modifying the token even slightly fucks up the auth - var newToken = new Token + var newToken = new TokenResponse { ExpiresAt = serverClient.Token.ExpiresAt, Bearer = serverClient.Token.Bearer + '0' @@ -228,7 +229,7 @@ namespace Tgstation.Server.Tests using var response = await httpClient.SendAsync(request, cancellationToken); Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); - var message = JsonConvert.DeserializeObject(content); + var message = JsonConvert.DeserializeObject(content); Assert.AreEqual(MediaTypeNames.Application.Json, response.Content.Headers.ContentType.MediaType); Assert.AreEqual(ErrorCode.ModelValidationFailure, message.ErrorCode); } @@ -243,7 +244,7 @@ namespace Tgstation.Server.Tests using var response = await httpClient.SendAsync(request, cancellationToken); Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); - var message = JsonConvert.DeserializeObject(content); + var message = JsonConvert.DeserializeObject(content); Assert.AreEqual(MediaTypeNames.Application.Json, response.Content.Headers.ContentType.MediaType); Assert.AreEqual(ErrorCode.ModelValidationFailure, message.ErrorCode); } diff --git a/tests/Tgstation.Server.Tests/UsersTest.cs b/tests/Tgstation.Server.Tests/UsersTest.cs index 5861ea0f0a..625b0cbc89 100644 --- a/tests/Tgstation.Server.Tests/UsersTest.cs +++ b/tests/Tgstation.Server.Tests/UsersTest.cs @@ -5,6 +5,8 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Request; +using Tgstation.Server.Api.Models.Response; using Tgstation.Server.Api.Rights; using Tgstation.Server.Client; using Tgstation.Server.Host.System; @@ -52,7 +54,7 @@ namespace Tgstation.Server.Tests Assert.IsFalse(users.Any(x => x.Id == systemUser.Id)); await ApiAssert.ThrowsException(() => serverClient.Users.GetId(systemUser, cancellationToken), null); - await ApiAssert.ThrowsException(() => serverClient.Users.Update(new UserUpdate + await ApiAssert.ThrowsException(() => serverClient.Users.Update(new UserUpdateRequest { Id = systemUser.Id }, cancellationToken), null); @@ -65,14 +67,14 @@ namespace Tgstation.Server.Tests Provider = OAuthProvider.Discord } }; - await ApiAssert.ThrowsException(() => serverClient.Users.Update(new UserUpdate + await ApiAssert.ThrowsException(() => serverClient.Users.Update(new UserUpdateRequest { Id = user.Id, OAuthConnections = sampleOAuthConnections }, cancellationToken), ErrorCode.AdminUserCannotOAuth); var testUser = await serverClient.Users.Create( - new UserUpdate + new UserCreateRequest { Name = $"BasicTestUser", Password = "asdfasdjfhauwiehruiy273894234jhndjkwh" @@ -81,7 +83,7 @@ namespace Tgstation.Server.Tests Assert.IsNotNull(testUser.OAuthConnections); testUser = await serverClient.Users.Update( - new UserUpdate + new UserUpdateRequest { Id = testUser.Id, OAuthConnections = sampleOAuthConnections @@ -94,7 +96,7 @@ namespace Tgstation.Server.Tests var group = await serverClient.Groups.Create( - new UserGroup + new UserGroupCreateRequest { Name = "TestGroup" }, @@ -105,7 +107,7 @@ namespace Tgstation.Server.Tests Assert.AreEqual(AdministrationRights.None, group.PermissionSet.AdministrationRights); Assert.AreEqual(InstanceManagerRights.None, group.PermissionSet.InstanceManagerRights); - var group2 = await serverClient.Groups.Create(new UserGroup + var group2 = await serverClient.Groups.Create(new UserGroupCreateRequest { Name = "TestGroup2", PermissionSet = new PermissionSet @@ -130,24 +132,26 @@ namespace Tgstation.Server.Tests groups = await serverClient.Groups.List(null, cancellationToken); Assert.AreEqual(1, groups.Count); - group.PermissionSet.InstanceManagerRights = RightsHelper.AllRights(); - group.PermissionSet.AdministrationRights = RightsHelper.AllRights(); - group.Users = null; - - group = await serverClient.Groups.Update(group, cancellationToken); + group = await serverClient.Groups.Update(new UserGroupUpdateRequest + { + Id = groups.First().Id, + PermissionSet = new PermissionSet + { + InstanceManagerRights = RightsHelper.AllRights(), + AdministrationRights = RightsHelper.AllRights(), + } + }, cancellationToken); Assert.AreEqual(RightsHelper.AllRights(), group.PermissionSet.AdministrationRights); Assert.AreEqual(RightsHelper.AllRights(), group.PermissionSet.InstanceManagerRights); - await ApiAssert.ThrowsException(() => serverClient.Groups.Update(group, cancellationToken), ErrorCode.UserGroupControllerCantEditMembers); - - var testUserUpdate = new UserUpdate + UserUpdateRequest testUserUpdate = new UserCreateRequest { Name = "TestUserWithNoPassword", Password = String.Empty }; - await ApiAssert.ThrowsException(() => serverClient.Users.Create(testUserUpdate, cancellationToken), ErrorCode.UserPasswordLength); + await ApiAssert.ThrowsException(() => serverClient.Users.Create((UserCreateRequest)testUserUpdate, cancellationToken), ErrorCode.UserPasswordLength); testUserUpdate.OAuthConnections = new List { @@ -158,9 +162,9 @@ namespace Tgstation.Server.Tests } }; - var testUser2 = await serverClient.Users.Create(testUserUpdate, cancellationToken); + var testUser2 = await serverClient.Users.Create((UserCreateRequest)testUserUpdate, cancellationToken); - testUserUpdate = new UserUpdate + testUserUpdate = new UserUpdateRequest { Id = testUser2.Id, PermissionSet = testUser2.PermissionSet, @@ -193,7 +197,7 @@ namespace Tgstation.Server.Tests async Task TestCreateSysUser(CancellationToken cancellationToken) { var sysId = Environment.UserName; - var update = new UserUpdate + var update = new UserCreateRequest { SystemIdentifier = sysId }; @@ -205,7 +209,7 @@ namespace Tgstation.Server.Tests async Task TestSpamCreation(CancellationToken cancellationToken) { - ICollection> tasks = new List>(); + ICollection> tasks = new List>(); // Careful with this, very easy to overload the thread pool const int RepeatCount = 100; @@ -219,7 +223,7 @@ namespace Tgstation.Server.Tests { tasks.Add( serverClient.Users.Create( - new UserUpdate + new UserCreateRequest { Name = $"SpamTestUser_{i}", Password = "asdfasdjfhauwiehruiy273894234jhndjkwh" diff --git a/tests/Tgstation.Server.Tests/VersionsTest.cs b/tests/Tgstation.Server.Tests/VersionsTest.cs index a7fde50140..1e769ac96f 100644 --- a/tests/Tgstation.Server.Tests/VersionsTest.cs +++ b/tests/Tgstation.Server.Tests/VersionsTest.cs @@ -56,7 +56,16 @@ namespace Tgstation.Server.Tests [TestMethod] public void TestApiVersion() { - var versionString = versionsPropertyGroup.Element(xmlNamespace + "TgsApiVersion").Value + ".0"; + var versionString = versionsPropertyGroup.Element(xmlNamespace + "TgsApiVersion").Value; + Assert.IsNotNull(versionString); + Assert.IsTrue(Version.TryParse(versionString, out var expected)); + Assert.AreEqual(expected, ApiHeaders.Version); + } + + [TestMethod] + public void TestApiLibraryVersion() + { + var versionString = versionsPropertyGroup.Element(xmlNamespace + "TgsApiLibraryVersion").Value + ".0"; Assert.IsNotNull(versionString); Assert.IsTrue(Version.TryParse(versionString, out var expected)); var actual = typeof(ApiHeaders).Assembly.GetName().Version;