diff --git a/src/Tgstation.Server.Api/Models/CompilerStatus.cs b/src/Tgstation.Server.Api/Models/CompilerStatus.cs
index 8ca0b48143..d29df195e6 100644
--- a/src/Tgstation.Server.Api/Models/CompilerStatus.cs
+++ b/src/Tgstation.Server.Api/Models/CompilerStatus.cs
@@ -12,16 +12,32 @@
///
Idle,
///
- /// The directiory is being targeted
- ///
- Targeting,
- ///
/// The is being copied
///
Copying,
+ ///
+ /// The .dme is having it's server side modifications applied
+ ///
+ Modifying,
///
/// DreamMaker is running
///
- Compiling
+ Compiling,
+ ///
+ /// The DMAPI is being verified
+ ///
+ Verifying,
+ ///
+ /// The compile results are being duplicated
+ ///
+ Duplicating,
+ ///
+ /// The configuration is being linked to the compile results
+ ///
+ Symlinking,
+ ///
+ /// A failed compile job is being erased
+ ///
+ Cleanup
}
}
\ No newline at end of file
diff --git a/src/Tgstation.Server.Api/Models/Instance.cs b/src/Tgstation.Server.Api/Models/Instance.cs
index 22bd31e1be..74910fdf68 100644
--- a/src/Tgstation.Server.Api/Models/Instance.cs
+++ b/src/Tgstation.Server.Api/Models/Instance.cs
@@ -41,6 +41,12 @@ namespace Tgstation.Server.Api.Models
[Permissions(WriteRight = InstanceManagerRights.SetConfiguration)]
public bool ConfigurationAllowed { get; set; }
+ ///
+ /// The time interval in minutes the repository is automatically pulled and compiles
+ ///
+ [Permissions(WriteRight = InstanceManagerRights.SetAutoUpdate)]
+ public int? AutoUpdateInterval { get; set; }
+
///
public Instance CloneMetadata() => new Instance
{
diff --git a/src/Tgstation.Server.Api/Models/Internal/DreamMakerSettings.cs b/src/Tgstation.Server.Api/Models/Internal/DreamMakerSettings.cs
index 2e07ee683a..154167ec82 100644
--- a/src/Tgstation.Server.Api/Models/Internal/DreamMakerSettings.cs
+++ b/src/Tgstation.Server.Api/Models/Internal/DreamMakerSettings.cs
@@ -8,12 +8,6 @@ namespace Tgstation.Server.Api.Models.Internal
[Model(RightsType.DreamMaker, ReadRight = DreamMakerRights.Read, CanCrud = true, RequiresInstance = true)]
public class DreamMakerSettings
{
- ///
- /// How often the automatically compiles in minutes
- ///
- [Permissions(WriteRight = DreamMakerRights.SetAutoCompile)]
- public int? AutoCompileInterval { get; set; }
-
///
/// The .dme file tries to compile with without the extension
///
diff --git a/src/Tgstation.Server.Api/Models/Internal/RepositorySettings.cs b/src/Tgstation.Server.Api/Models/Internal/RepositorySettings.cs
index 35a560f7da..5893293fa7 100644
--- a/src/Tgstation.Server.Api/Models/Internal/RepositorySettings.cs
+++ b/src/Tgstation.Server.Api/Models/Internal/RepositorySettings.cs
@@ -46,11 +46,5 @@ namespace Tgstation.Server.Api.Models.Internal
///
[Permissions(WriteRight = RepositoryRights.ChangeTestMergeCommits)]
public bool ShowTestMergeCommitters { get; set; }
-
- ///
- /// How often the automatically updates in minutes
- ///
- [Permissions(WriteRight = RepositoryRights.ChangeAutoUpdate)]
- public int? AutoUpdateInterval { get; set; }
}
}
diff --git a/src/Tgstation.Server.Api/Models/Job.cs b/src/Tgstation.Server.Api/Models/Job.cs
index b1141f3400..2f413fff43 100644
--- a/src/Tgstation.Server.Api/Models/Job.cs
+++ b/src/Tgstation.Server.Api/Models/Job.cs
@@ -5,12 +5,6 @@
///
public sealed class Job : Internal.Job
{
- ///
- /// If the has incremental progress, this will range from 1 - 100. 0 otherwise
- ///
- [Permissions(DenyWrite = true)]
- public int Progress { get; set; }
-
///
/// The that started the job
///
diff --git a/src/Tgstation.Server.Api/Rights/DreamMakerRights.cs b/src/Tgstation.Server.Api/Rights/DreamMakerRights.cs
index 661530c95a..bce942c165 100644
--- a/src/Tgstation.Server.Api/Rights/DreamMakerRights.cs
+++ b/src/Tgstation.Server.Api/Rights/DreamMakerRights.cs
@@ -25,12 +25,8 @@ namespace Tgstation.Server.Api.Rights
///
CancelCompile = 4,
///
- /// User may modify
- ///
- SetAutoCompile = 8,
- ///
/// User may modify
///
- SetDme = 16
+ SetDme = 8
}
}
diff --git a/src/Tgstation.Server.Api/Rights/InstanceManagerRights.cs b/src/Tgstation.Server.Api/Rights/InstanceManagerRights.cs
index 106eaa1ad1..fbec63f9d5 100644
--- a/src/Tgstation.Server.Api/Rights/InstanceManagerRights.cs
+++ b/src/Tgstation.Server.Api/Rights/InstanceManagerRights.cs
@@ -43,6 +43,10 @@ namespace Tgstation.Server.Api.Rights
///
/// User can change
///
- SetConfiguration = 128
+ SetConfiguration = 128,
+ ///
+ /// User can change
+ ///
+ SetAutoUpdate = 256
}
}
diff --git a/src/Tgstation.Server.Api/Rights/RepositoryRights.cs b/src/Tgstation.Server.Api/Rights/RepositoryRights.cs
index c418507036..23703fd446 100644
--- a/src/Tgstation.Server.Api/Rights/RepositoryRights.cs
+++ b/src/Tgstation.Server.Api/Rights/RepositoryRights.cs
@@ -37,20 +37,16 @@ namespace Tgstation.Server.Api.Rights
///
ChangeTestMergeCommits = 64,
///
- /// User may change
- ///
- ChangeAutoUpdate = 128,
- ///
/// User may read and change and
///
- ChangeCredentials = 256,
+ ChangeCredentials = 128,
///
/// User may set to another git reference (not a SHA)
///
- SetReference = 512,
+ SetReference = 256,
///
/// User may read all fields in the with the exception of
///
- Read = 1024,
+ Read = 512,
}
}
diff --git a/src/Tgstation.Server.Host/Components/DmbFactory.cs b/src/Tgstation.Server.Host/Components/DmbFactory.cs
index cce071c63f..9d21f4747f 100644
--- a/src/Tgstation.Server.Host/Components/DmbFactory.cs
+++ b/src/Tgstation.Server.Host/Components/DmbFactory.cs
@@ -14,9 +14,9 @@ namespace Tgstation.Server.Host.Components
sealed class DmbFactory : IDmbFactory, ICompileJobConsumer
{
///
- /// The for the
+ /// The for the
///
- readonly IDatabaseContext databaseContext;
+ readonly IDatabaseContextFactory databaseContextFactory;
///
/// The for the
///
@@ -42,11 +42,11 @@ namespace Tgstation.Server.Host.Components
///
/// Construct a
///
- /// The value of
+ /// The value of
/// The value of
- public DmbFactory(IDatabaseContext databaseContext, IIOManager ioManager)
+ public DmbFactory(IDatabaseContextFactory databaseContextFactory, IIOManager ioManager)
{
- this.databaseContext = databaseContext ?? throw new ArgumentNullException(nameof(databaseContext));
+ this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
cleanupCts = new CancellationTokenSource();
@@ -117,16 +117,16 @@ namespace Tgstation.Server.Host.Components
}
///
- public async Task StartAsync(CancellationToken cancellationToken)
+ public Task StartAsync(CancellationToken cancellationToken) => databaseContextFactory.UseContext(async (db) =>
{
- var cj = await databaseContext.CompileJobs.OrderByDescending(x => x.Job.StoppedAt).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
+ var cj = await db.CompileJobs.OrderByDescending(x => x.Job.StoppedAt).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
if (cj == default(CompileJob))
return;
LoadCompileJob(cj);
//delete all other compile jobs
var directories = await ioManager.GetDirectories(".", cancellationToken).ConfigureAwait(false);
await Task.WhenAll(directories.Where(x => x != cj.Job.ToString()).Select(x => ioManager.DeleteDirectory(x, cancellationToken))).ConfigureAwait(false);
- }
+ });
///
public async Task StopAsync(CancellationToken cancellationToken)
diff --git a/src/Tgstation.Server.Host/Components/DreamMaker.cs b/src/Tgstation.Server.Host/Components/DreamMaker.cs
index 441b45de40..ffd577f98b 100644
--- a/src/Tgstation.Server.Host/Components/DreamMaker.cs
+++ b/src/Tgstation.Server.Host/Components/DreamMaker.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
+using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -23,10 +24,18 @@ namespace Tgstation.Server.Host.Components
/// Name of the secondary directory used for compilation
///
public const string BDirectoryName = "B";
+ ///
+ /// Extension for .dmbs
+ ///
public const string DmbExtension = ".dmb";
-
+ ///
+ /// Extension for .dmes
+ ///
const string DmeExtension = ".dme";
+ ///
+ public CompilerStatus Status { get; private set; }
+
///
/// The for
///
@@ -61,6 +70,7 @@ namespace Tgstation.Server.Host.Components
/// The value of
/// The value of
/// The value of
+ ///
public DreamMaker(IIOManager ioManager, IConfiguration configuration, IDreamDaemonExecutor dreamDaemonExecutor, IByond byond, IInterop interop, ICompileJobConsumer compileJobConsumer)
{
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
@@ -165,6 +175,12 @@ namespace Tgstation.Server.Host.Components
}
}
+ ///
+ /// Adds server side includes to the .dme being compiled
+ ///
+ /// The for the operation
+ /// The for the operation
+ /// A representing the running operation
async Task ModifyDme(Host.Models.CompileJob job, CancellationToken cancellationToken)
{
var dirA = ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName);
@@ -202,79 +218,95 @@ namespace Tgstation.Server.Host.Components
}
///
- public async Task Compile(string dmeName, IRepository repository, CancellationToken cancellationToken)
+ public async Task Compile(string projectName, IRepository repository, CancellationToken cancellationToken)
{
- var job = new Host.Models.CompileJob
- {
- DirectoryName = Guid.NewGuid(),
- DmeName = dmeName
- };
- await ioManager.CreateDirectory(job.DirectoryName.ToString(), cancellationToken).ConfigureAwait(false);
- var dirA = ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName);
- var dirB = ioManager.ConcatPath(job.DirectoryName.ToString(), BDirectoryName);
-
- async Task CleanupFailedCompile()
- {
- try
- {
- await ioManager.DeleteDirectory(job.DirectoryName.ToString(), CancellationToken.None).ConfigureAwait(false);
- }
- catch { }
- };
-
try
{
- //copy the repository
- var fullDirA = ioManager.ResolvePath(dirA);
- using (repository)
- await repository.CopyTo(fullDirA, cancellationToken).ConfigureAwait(false);
-
- await ModifyDme(job, cancellationToken).ConfigureAwait(false);
-
- //run compiler, verify api
- var ddVerified = await byond.UseExecutables(async (dreamMakerPath, dreamDaemonPath) =>
+ Status = CompilerStatus.Copying;
+ var job = new Host.Models.CompileJob
{
- await RunDreamMaker(dreamMakerPath, job, cancellationToken).ConfigureAwait(false);
+ DirectoryName = Guid.NewGuid(),
+ DmeName = projectName
+ };
+ await ioManager.CreateDirectory(job.DirectoryName.ToString(), cancellationToken).ConfigureAwait(false);
+ var dirA = ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName);
+ var dirB = ioManager.ConcatPath(job.DirectoryName.ToString(), BDirectoryName);
- return job.ExitCode == 0 && await VerifyApi(dreamDaemonPath, job, cancellationToken).ConfigureAwait(false);
- }, true).ConfigureAwait(false);
-
- if (!ddVerified)
- //server never validated or compile failed
- await CleanupFailedCompile().ConfigureAwait(false);
- else
+ async Task CleanupFailedCompile()
{
- job.DMApiValidated = true;
+ Status = CompilerStatus.Cleanup;
+ try
+ {
+ await ioManager.DeleteDirectory(job.DirectoryName.ToString(), CancellationToken.None).ConfigureAwait(false);
+ }
+ catch { }
+ };
- //duplicate the dmb et al
- await ioManager.CopyDirectory(dirA, dirB, null, cancellationToken).ConfigureAwait(false);
+ try
+ {
+ //copy the repository
+ var fullDirA = ioManager.ResolvePath(dirA);
+ using (repository)
+ await repository.CopyTo(fullDirA, cancellationToken).ConfigureAwait(false);
- //symlink in the static data
- var symATask = configuration.SymlinkStaticFilesTo(fullDirA, cancellationToken);
- await configuration.SymlinkStaticFilesTo(ioManager.ResolvePath(dirB), cancellationToken).ConfigureAwait(false);
- await symATask.ConfigureAwait(false);
+ Status = CompilerStatus.Modifying;
+
+ if (job.DmeName == null)
+ {
+ job.DmeName = (await ioManager.GetFilesWithExtension(dirA, DmeExtension, cancellationToken).ConfigureAwait(false)).FirstOrDefault();
+ if (job.DmeName == default)
+ {
+ job.Output = "Unable to find any .dme!";
+ return job;
+ }
+ }
+
+ await ModifyDme(job, cancellationToken).ConfigureAwait(false);
+
+ Status = CompilerStatus.Compiling;
+
+ //run compiler, verify api
+ var ddVerified = await byond.UseExecutables(async (dreamMakerPath, dreamDaemonPath) =>
+ {
+ await RunDreamMaker(dreamMakerPath, job, cancellationToken).ConfigureAwait(false);
+
+ Status = CompilerStatus.Verifying;
+
+ return job.ExitCode == 0 && await VerifyApi(dreamDaemonPath, job, cancellationToken).ConfigureAwait(false);
+ }, true).ConfigureAwait(false);
+
+ if (!ddVerified)
+ //server never validated or compile failed
+ await CleanupFailedCompile().ConfigureAwait(false);
+ else
+ {
+ job.DMApiValidated = true;
+
+ Status = CompilerStatus.Duplicating;
+
+ //duplicate the dmb et al
+ await ioManager.CopyDirectory(dirA, dirB, null, cancellationToken).ConfigureAwait(false);
+
+ Status = CompilerStatus.Symlinking;
+
+ //symlink in the static data
+ var symATask = configuration.SymlinkStaticFilesTo(fullDirA, cancellationToken);
+ await configuration.SymlinkStaticFilesTo(ioManager.ResolvePath(dirB), cancellationToken).ConfigureAwait(false);
+ await symATask.ConfigureAwait(false);
+ }
+ compileJobConsumer.LoadCompileJob(job);
+ return job;
+ }
+ catch
+ {
+ await CleanupFailedCompile().ConfigureAwait(false);
+ throw;
}
- compileJobConsumer.LoadCompileJob(job);
- return job;
}
- catch
+ finally
{
- await CleanupFailedCompile().ConfigureAwait(false);
- throw;
+ Status = CompilerStatus.Idle;
}
}
-
- ///
- public Task StartAsync(CancellationToken cancellationToken)
- {
- //THIS IS FOR AUTO COMPILE INTERVAL STUFF, STOP TRYING TO REMOVE IT
- throw new NotImplementedException();
- }
-
- ///
- public Task StopAsync(CancellationToken cancellationToken)
- {
- throw new NotImplementedException();
- }
}
}
diff --git a/src/Tgstation.Server.Host/Components/IByond.cs b/src/Tgstation.Server.Host/Components/IByond.cs
index acf4f4c7cc..6af7e4627b 100644
--- a/src/Tgstation.Server.Host/Components/IByond.cs
+++ b/src/Tgstation.Server.Host/Components/IByond.cs
@@ -7,7 +7,7 @@ namespace Tgstation.Server.Host.Components
///
/// For managing the BYOND installation
///
- interface IByond
+ public interface IByond
{
///
/// Change the current BYOND version
diff --git a/src/Tgstation.Server.Host/Components/IChat.cs b/src/Tgstation.Server.Host/Components/IChat.cs
index bd43137bca..a1f494a1ff 100644
--- a/src/Tgstation.Server.Host/Components/IChat.cs
+++ b/src/Tgstation.Server.Host/Components/IChat.cs
@@ -10,7 +10,7 @@ namespace Tgstation.Server.Host.Components
///
/// For managing connected chat services
///
- interface IChat : IHostedService
+ public interface IChat : IHostedService
{
///
/// If the IRC client is connected
@@ -47,6 +47,14 @@ namespace Tgstation.Server.Host.Components
/// A representing the running operation
Task SendMessage(string message, IEnumerable channelIds, CancellationToken cancellationToken);
+ ///
+ /// Start tracking json files for commands and channels
+ ///
+ /// The base path of the .jsons
+ /// The name of the chat channels json
+ /// The name of the chat commands json
+ /// The for the operation
+ /// A resulting in a tied to the lifetime of the json trackings
Task TrackJsons(string basePath, string channelsJsonName, string commandsJsonName, CancellationToken cancellationToken);
}
}
\ No newline at end of file
diff --git a/src/Tgstation.Server.Host/Components/IConfiguration.cs b/src/Tgstation.Server.Host/Components/IConfiguration.cs
index 3b31dda93e..da32288f4d 100644
--- a/src/Tgstation.Server.Host/Components/IConfiguration.cs
+++ b/src/Tgstation.Server.Host/Components/IConfiguration.cs
@@ -9,7 +9,7 @@ namespace Tgstation.Server.Host.Components
///
/// For managing the Configuration directory
///
- interface IConfiguration
+ public interface IConfiguration
{
///
/// Copies all files in the CodeModifications directory to
diff --git a/src/Tgstation.Server.Host/Components/IDreamDaemon.cs b/src/Tgstation.Server.Host/Components/IDreamDaemon.cs
index b052ce9ead..dbd32b9d2a 100644
--- a/src/Tgstation.Server.Host/Components/IDreamDaemon.cs
+++ b/src/Tgstation.Server.Host/Components/IDreamDaemon.cs
@@ -9,7 +9,7 @@ namespace Tgstation.Server.Host.Components
///
/// For managing DreamDaemon
///
- interface IDreamDaemon : IHostedService
+ public interface IDreamDaemon : IHostedService
{
///
/// If DreamDaemon is running
diff --git a/src/Tgstation.Server.Host/Components/IDreamMaker.cs b/src/Tgstation.Server.Host/Components/IDreamMaker.cs
index 3e7c52eca5..938aeb46b8 100644
--- a/src/Tgstation.Server.Host/Components/IDreamMaker.cs
+++ b/src/Tgstation.Server.Host/Components/IDreamMaker.cs
@@ -8,15 +8,20 @@ namespace Tgstation.Server.Host.Components
///
/// For managing the compiler
///
- interface IDreamMaker : IHostedService
+ public interface IDreamMaker
{
+ ///
+ /// The of
+ ///
+ Api.Models.CompilerStatus Status { get; }
+
///
/// Starts a compile
///
- /// The .dme file to use without the extension
+ /// The name of the .dme to compile without the extension
/// The to copy from
/// The for the operation
/// A resulting in the partially populated for the operation. In particular, note the field will only have it's field populated
- Task Compile(string dmeName, IRepository repository, CancellationToken cancellationToken);
+ Task Compile(string projectName, IRepository repository, CancellationToken cancellationToken);
}
}
\ No newline at end of file
diff --git a/src/Tgstation.Server.Host/Components/IInstance.cs b/src/Tgstation.Server.Host/Components/IInstance.cs
index 3735a5cc13..a06c30c3a3 100644
--- a/src/Tgstation.Server.Host/Components/IInstance.cs
+++ b/src/Tgstation.Server.Host/Components/IInstance.cs
@@ -1,23 +1,60 @@
using Microsoft.Extensions.Hosting;
+using System.Threading.Tasks;
namespace Tgstation.Server.Host.Components
{
- interface IInstance : IHostedService
+ ///
+ /// For interacting with the instance services
+ ///
+ public interface IInstance : IHostedService
{
+ ///
+ /// The for the
+ ///
IRepositoryManager RepositoryManager { get; }
+ ///
+ /// The for the
+ ///
IByond Byond { get; }
+ ///
+ /// The for the
+ ///
IDreamMaker DreamMaker { get; }
+ ///
+ /// The for the
+ ///
IDreamDaemon DreamDaemon { get; }
+ ///
+ /// The for the
+ ///
IChat Chat { get; }
+ ///
+ /// The for the
+ ///
IConfiguration Configuration { get; }
+ ///
+ /// Get the associated with the
+ ///
+ /// The associated with the
Api.Models.Instance GetMetadata();
+ ///
+ /// Rename the
+ ///
+ /// The new name for the
void Rename(string newName);
+
+ ///
+ /// Change the for the
+ ///
+ /// The new auto update inteval
+ /// A representing the running operation
+ Task SetAutoUpdateInterval(int? newInterval);
}
}
\ No newline at end of file
diff --git a/src/Tgstation.Server.Host/Components/IInstanceFactory.cs b/src/Tgstation.Server.Host/Components/IInstanceFactory.cs
index 7dfd5a667d..d9d4f4cda8 100644
--- a/src/Tgstation.Server.Host/Components/IInstanceFactory.cs
+++ b/src/Tgstation.Server.Host/Components/IInstanceFactory.cs
@@ -1,4 +1,4 @@
-using Tgstation.Server.Host.Models;
+using Tgstation.Server.Host.Core;
namespace Tgstation.Server.Host.Components
{
@@ -11,8 +11,7 @@ namespace Tgstation.Server.Host.Components
/// Create an
///
/// The
- /// The for the operation
/// A new
- IInstance CreateInstance(Host.Models.Instance metadata, IDatabaseContext databaseContext);
+ IInstance CreateInstance(Host.Models.Instance metadata);
}
}
\ No newline at end of file
diff --git a/src/Tgstation.Server.Host/Components/IInstanceManager.cs b/src/Tgstation.Server.Host/Components/IInstanceManager.cs
index c087ad362a..68a60ad247 100644
--- a/src/Tgstation.Server.Host/Components/IInstanceManager.cs
+++ b/src/Tgstation.Server.Host/Components/IInstanceManager.cs
@@ -1,13 +1,12 @@
using System.Threading;
using System.Threading.Tasks;
-using Tgstation.Server.Host.Models;
namespace Tgstation.Server.Host.Components
{
///
/// For managing s
///
- interface IInstanceManager : IInstanceShutdownHandler
+ public interface IInstanceManager : IInstanceShutdownHandler
{
///
/// Get the associated with given
@@ -20,10 +19,9 @@ namespace Tgstation.Server.Host.Components
/// Online an
///
/// The of the desired
- /// The for the operation
/// The for the operation
/// A representing the running operation
- Task OnlineInstance(Host.Models.Instance metadata, IDatabaseContext databaseContext, CancellationToken cancellationToken);
+ Task OnlineInstance(Host.Models.Instance metadata, CancellationToken cancellationToken);
///
/// Offline an
@@ -37,10 +35,9 @@ namespace Tgstation.Server.Host.Components
/// Move an
///
/// The of the desired
- /// The for the operation
/// The new path of the . will have this set on if the operation completes successfully
/// The for the operation
/// A representing the running operation
- Task MoveInstance(Host.Models.Instance metadata, IDatabaseContext databaseContext, string newPath, CancellationToken cancellationToken);
+ Task MoveInstance(Host.Models.Instance metadata, string newPath, CancellationToken cancellationToken);
}
}
diff --git a/src/Tgstation.Server.Host/Components/IInstanceShutdownHandler.cs b/src/Tgstation.Server.Host/Components/IInstanceShutdownHandler.cs
index e07de394b6..87264ca110 100644
--- a/src/Tgstation.Server.Host/Components/IInstanceShutdownHandler.cs
+++ b/src/Tgstation.Server.Host/Components/IInstanceShutdownHandler.cs
@@ -3,8 +3,20 @@ using Tgstation.Server.Api.Models.Internal;
namespace Tgstation.Server.Host.Components
{
- interface IInstanceShutdownHandler
+ ///
+ /// For handling shutdowns
+ ///
+ public interface IInstanceShutdownHandler
{
+ //TODO
+ ///
+ /// OMG
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
Task PreserveActiveExecutablesIfNecessary(DreamDaemonLaunchParameters launchParameters, string accessToken, int pid, bool primary);
}
}
diff --git a/src/Tgstation.Server.Host/Components/IRepository.cs b/src/Tgstation.Server.Host/Components/IRepository.cs
index 7acd5053df..09acbb72c5 100644
--- a/src/Tgstation.Server.Host/Components/IRepository.cs
+++ b/src/Tgstation.Server.Host/Components/IRepository.cs
@@ -8,7 +8,7 @@ namespace Tgstation.Server.Host.Components
///
/// Represents an on-disk git repository
///
- interface IRepository : IDisposable
+ public interface IRepository : IDisposable
{
///
/// If the was cloned from GitHub.com
diff --git a/src/Tgstation.Server.Host/Components/IRepositoryManager.cs b/src/Tgstation.Server.Host/Components/IRepositoryManager.cs
index f778e46798..94fd3a2ac3 100644
--- a/src/Tgstation.Server.Host/Components/IRepositoryManager.cs
+++ b/src/Tgstation.Server.Host/Components/IRepositoryManager.cs
@@ -1,4 +1,4 @@
-using Microsoft.Extensions.Hosting;
+using System;
using System.Threading;
using System.Threading.Tasks;
@@ -7,8 +7,8 @@ namespace Tgstation.Server.Host.Components
///
/// Factory for creating and loading s
///
- interface IRepositoryManager : IHostedService
- {
+ public interface IRepositoryManager
+ {
///
/// Attempt to load the from the default location
///
@@ -19,17 +19,10 @@ namespace Tgstation.Server.Host.Components
///
/// Delete the current and replaces it with a clone of the repository at
///
- /// The location of the remote repository to clone
+ /// The of the remote repository to clone
/// The access string to clone from
/// The for the operation
/// The newly cloned
- Task CloneRepository(string url, string accessString, CancellationToken cancellationToken);
-
- ///
- /// Change the interval in minutes at which the repository auto updates
- ///
- /// The new interval in minutes or to disable the auto update
- /// A representing the running operation
- Task SetAutoUpdateInterval(int? newInterval);
+ Task CloneRepository(Uri url, string accessString, CancellationToken cancellationToken);
}
}
diff --git a/src/Tgstation.Server.Host/Components/Instance.cs b/src/Tgstation.Server.Host/Components/Instance.cs
index 475c12e3d4..aea3891519 100644
--- a/src/Tgstation.Server.Host/Components/Instance.cs
+++ b/src/Tgstation.Server.Host/Components/Instance.cs
@@ -1,28 +1,58 @@
-using System;
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Tgstation.Server.Host.Core;
namespace Tgstation.Server.Host.Components
{
- sealed class Instance : IInstance
+ ///
+ sealed class Instance : IInstance, IDisposable
{
+ ///
public IRepositoryManager RepositoryManager { get; }
+ ///
public IByond Byond { get; }
+ ///
public IDreamMaker DreamMaker { get; }
+ ///
public IDreamDaemon DreamDaemon { get; }
+ ///
public IChat Chat { get; }
+ ///
public IConfiguration Configuration { get; }
+ ///
+ /// The for the
+ ///
readonly ICompileJobConsumer compileJobConsumer;
+
+ ///
+ /// The for the
+ ///
+ readonly IDatabaseContextFactory databaseContextFactory;
+ ///
+ /// The for the
+ ///
readonly Api.Models.Instance metadata;
- public Instance(Api.Models.Instance metadata, IRepositoryManager repositoryManager, IByond byond, IDreamMaker dreamMaker, IDreamDaemon dreamDaemon, IChat chat, IConfiguration configuration, ICompileJobConsumer compileJobConsumer)
+ ///
+ /// The auto update
+ ///
+ Task timerTask;
+ ///
+ /// for
+ ///
+ CancellationTokenSource timerCts;
+
+ public Instance(Api.Models.Instance metadata, IRepositoryManager repositoryManager, IByond byond, IDreamMaker dreamMaker, IDreamDaemon dreamDaemon, IChat chat, IConfiguration configuration, ICompileJobConsumer compileJobConsumer, IDatabaseContextFactory databaseContextFactory)
{
this.metadata = metadata ?? throw new ArgumentNullException(nameof(metadata));
RepositoryManager = repositoryManager ?? throw new ArgumentNullException(nameof(repositoryManager));
@@ -32,10 +62,50 @@ namespace Tgstation.Server.Host.Components
Chat = chat ?? throw new ArgumentNullException(nameof(chat));
Configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
this.compileJobConsumer = compileJobConsumer ?? throw new ArgumentNullException(nameof(compileJobConsumer));
+ this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
}
+ ///
+ public void Dispose() => timerCts?.Dispose();
+
+ ///
+ /// Pull the repository and compile for every set of given
+ ///
+ /// How many minutes the operation should repeat. Does not include running time
+ /// The for the operation
+ /// A representing the running operation
+ async Task TimerLoop(int minutes, CancellationToken cancellationToken)
+ {
+ try
+ {
+ while (true)
+ {
+ await Task.Delay(new TimeSpan(0, minutes, 0), cancellationToken).ConfigureAwait(false);
+
+ string accessToken = null, projectName = null;
+ var dbTask = databaseContextFactory.UseContext(async (db) =>
+ {
+ var instanceQuery = db.Instances.Where(x => x.Id == metadata.Id);
+ var projectNameTask = instanceQuery.Select(x => x.DreamMakerSettings.ProjectName).FirstOrDefaultAsync(cancellationToken);
+ accessToken = await instanceQuery.Select(x => x.RepositorySettings.AccessToken).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
+ projectName = await projectNameTask.ConfigureAwait(false);
+ });
+ using (var repo = await RepositoryManager.LoadRepository(cancellationToken).ConfigureAwait(false))
+ {
+ await dbTask.ConfigureAwait(false);
+ await repo.FetchOrigin(accessToken, cancellationToken).ConfigureAwait(false);
+ await repo.ResetToOrigin(cancellationToken).ConfigureAwait(false);
+ await DreamMaker.Compile(projectName, repo, cancellationToken).ConfigureAwait(false);
+ }
+ }
+ }
+ catch (OperationCanceledException) { }
+ }
+
+ ///
public Api.Models.Instance GetMetadata() => metadata.CloneMetadata();
+ ///
public void Rename(string newName)
{
if (String.IsNullOrWhiteSpace(newName))
@@ -43,8 +113,38 @@ namespace Tgstation.Server.Host.Components
metadata.Name = newName;
}
- public Task StartAsync(CancellationToken cancellationToken) => Task.WhenAll(RepositoryManager.StartAsync(cancellationToken), DreamDaemon.StartAsync(cancellationToken), Chat.StartAsync(cancellationToken), compileJobConsumer.StartAsync(cancellationToken), DreamMaker.StartAsync(cancellationToken));
+ ///
+ public Task StartAsync(CancellationToken cancellationToken) => Task.WhenAll(SetAutoUpdateInterval(metadata.AutoUpdateInterval), DreamDaemon.StartAsync(cancellationToken), Chat.StartAsync(cancellationToken), compileJobConsumer.StartAsync(cancellationToken));
- public Task StopAsync(CancellationToken cancellationToken) => Task.WhenAll(RepositoryManager.StopAsync(cancellationToken), DreamDaemon.StopAsync(cancellationToken), Chat.StopAsync(cancellationToken), compileJobConsumer.StopAsync(cancellationToken), DreamMaker.StopAsync(cancellationToken));
+ ///
+ public Task StopAsync(CancellationToken cancellationToken) => Task.WhenAll(SetAutoUpdateInterval(null), DreamDaemon.StopAsync(cancellationToken), Chat.StopAsync(cancellationToken), compileJobConsumer.StopAsync(cancellationToken));
+
+ ///
+ public async Task SetAutoUpdateInterval(int? newInterval)
+ {
+ Task toWait;
+ lock (this)
+ {
+ if (timerTask != null)
+ {
+ timerCts.Cancel();
+ toWait = timerTask;
+ }
+ else
+ toWait = Task.CompletedTask;
+ }
+ await toWait.ConfigureAwait(false);
+ if (!newInterval.HasValue)
+ return;
+ lock (this)
+ {
+ //race condition, just quit
+ if (timerTask != null)
+ return;
+ timerCts?.Dispose();
+ timerCts = new CancellationTokenSource();
+ timerTask = TimerLoop(newInterval.Value, timerCts.Token);
+ }
+ }
}
}
diff --git a/src/Tgstation.Server.Host/Components/InstanceFactory.cs b/src/Tgstation.Server.Host/Components/InstanceFactory.cs
index ed4955194e..62112fac9b 100644
--- a/src/Tgstation.Server.Host/Components/InstanceFactory.cs
+++ b/src/Tgstation.Server.Host/Components/InstanceFactory.cs
@@ -1,6 +1,5 @@
using System;
using Tgstation.Server.Host.Core;
-using Tgstation.Server.Host.Models;
namespace Tgstation.Server.Host.Components
{
@@ -12,14 +11,24 @@ namespace Tgstation.Server.Host.Components
///
readonly IIOManager ioManager;
+ ///
+ /// The for the
+ ///
+ readonly IDatabaseContextFactory databaseContextFactory;
+
///
/// Construct an
///
/// The value of
- public InstanceFactory(IIOManager ioManager) => this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
+ /// The value of
+ public InstanceFactory(IIOManager ioManager, IDatabaseContextFactory databaseContextFactory)
+ {
+ this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
+ this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
+ }
///
- public IInstance CreateInstance(Host.Models.Instance metadata, IDatabaseContext databaseContext)
+ public IInstance CreateInstance(Host.Models.Instance metadata)
{
//Create the ioManager for the instance
@@ -32,7 +41,7 @@ namespace Tgstation.Server.Host.Components
var configurationIoManager = new ResolvingIOManager(instanceIoManager, "Configuration");
var codeModificationsIoMananger = new ResolvingIOManager(instanceIoManager, "CodeModifications");
- var dmbFactory = new DmbFactory(databaseContext, gameIoManager);
+ var dmbFactory = new DmbFactory(databaseContextFactory, gameIoManager);
diff --git a/src/Tgstation.Server.Host/Components/InstanceManager.cs b/src/Tgstation.Server.Host/Components/InstanceManager.cs
index 1b01e2b8e9..3d1a615f2c 100644
--- a/src/Tgstation.Server.Host/Components/InstanceManager.cs
+++ b/src/Tgstation.Server.Host/Components/InstanceManager.cs
@@ -8,7 +8,6 @@ using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Api.Models.Internal;
using Tgstation.Server.Host.Core;
-using Tgstation.Server.Host.Models;
namespace Tgstation.Server.Host.Components
{
@@ -16,18 +15,18 @@ namespace Tgstation.Server.Host.Components
sealed class InstanceManager : IInstanceManager, IHostedService
{
///
- /// The for the
+ /// The for the
///
readonly IInstanceFactory instanceFactory;
///
- /// The for the
- ///
- readonly IServiceProvider serviceProvider;
- ///
- /// The for the
+ /// The for the
///
readonly IIOManager ioManager;
///
+ /// The for the
+ ///
+ readonly IDatabaseContextFactory databaseContextFactory;
+ ///
/// Map of s to respective s
///
readonly Dictionary instances;
@@ -36,13 +35,13 @@ namespace Tgstation.Server.Host.Components
/// Construct an
///
/// The value of
- /// The value of
/// The value of
- public InstanceManager(IInstanceFactory instanceFactory, IServiceProvider serviceProvider, IIOManager ioManager)
+ /// The value of
+ public InstanceManager(IInstanceFactory instanceFactory, IIOManager ioManager, IDatabaseContextFactory databaseContextFactory)
{
this.instanceFactory = instanceFactory ?? throw new ArgumentNullException(nameof(instanceFactory));
- this.serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
+ this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
instances = new Dictionary();
}
@@ -60,7 +59,7 @@ namespace Tgstation.Server.Host.Components
}
///
- public async Task MoveInstance(Host.Models.Instance instance, IDatabaseContext databaseContext, string newPath, CancellationToken cancellationToken)
+ public async Task MoveInstance(Host.Models.Instance instance, string newPath, CancellationToken cancellationToken)
{
if (newPath == null)
throw new ArgumentNullException(nameof(newPath));
@@ -72,14 +71,14 @@ namespace Tgstation.Server.Host.Components
var oldPath = instance.Path;
await ioManager.CopyDirectory(oldPath, newPath, null, cancellationToken).ConfigureAwait(false);
instance.Path = ioManager.ResolvePath(newPath);
- instanceOnlineTask = OnlineInstance(instance, databaseContext, default);
+ instanceOnlineTask = OnlineInstance(instance, default);
await ioManager.DeleteDirectory(oldPath, cancellationToken).ConfigureAwait(false);
}
finally
{
if (instance.Online)
if (instanceOnlineTask == null)
- await OnlineInstance(instance, databaseContext, default).ConfigureAwait(false);
+ await OnlineInstance(instance, default).ConfigureAwait(false);
else
await instanceOnlineTask.ConfigureAwait(false);
}
@@ -101,11 +100,11 @@ namespace Tgstation.Server.Host.Components
}
///
- public async Task OnlineInstance(Host.Models.Instance metadata, IDatabaseContext databaseContext, CancellationToken cancellationToken)
+ public async Task OnlineInstance(Host.Models.Instance metadata, CancellationToken cancellationToken)
{
if (metadata == null)
throw new ArgumentNullException(nameof(metadata));
- var instance = instanceFactory.CreateInstance(metadata, databaseContext);
+ var instance = instanceFactory.CreateInstance(metadata);
lock (this)
{
if (instances.ContainsKey(metadata.Id))
@@ -116,18 +115,14 @@ namespace Tgstation.Server.Host.Components
}
///
- public async Task StartAsync(CancellationToken cancellationToken)
+ public Task StartAsync(CancellationToken cancellationToken) => databaseContextFactory.UseContext(async databaseContext =>
{
- using(var scope = serviceProvider.CreateScope())
- {
- var databaseContext = scope.ServiceProvider.GetRequiredService();
- await databaseContext.Initialize(cancellationToken).ConfigureAwait(false);
- var dbInstances = databaseContext.Instances.Where(x => x.Online).Include(x => x.RepositorySettings).Include(x => x.ChatSettings).Include(x => x.DreamDaemonSettings).ToAsyncEnumerable();
- var tasks = new List();
- await dbInstances.ForEachAsync(metadata => tasks.Add(OnlineInstance(metadata, databaseContext, cancellationToken)), cancellationToken).ConfigureAwait(false);
- await Task.WhenAll(tasks).ConfigureAwait(false);
- }
- }
+ await databaseContext.Initialize(cancellationToken).ConfigureAwait(false);
+ var dbInstances = databaseContext.Instances.Where(x => x.Online).Include(x => x.RepositorySettings).Include(x => x.ChatSettings).Include(x => x.DreamDaemonSettings).ToAsyncEnumerable();
+ var tasks = new List();
+ await dbInstances.ForEachAsync(metadata => tasks.Add(OnlineInstance(metadata, cancellationToken)), cancellationToken).ConfigureAwait(false);
+ await Task.WhenAll(tasks).ConfigureAwait(false);
+ });
///
public async Task StopAsync(CancellationToken cancellationToken)
diff --git a/src/Tgstation.Server.Host/Components/RepositoryManager.cs b/src/Tgstation.Server.Host/Components/RepositoryManager.cs
index 5982887adb..e87d417cbd 100644
--- a/src/Tgstation.Server.Host/Components/RepositoryManager.cs
+++ b/src/Tgstation.Server.Host/Components/RepositoryManager.cs
@@ -25,16 +25,6 @@ namespace Tgstation.Server.Host.Components
///
readonly SemaphoreSlim semaphore;
- ///
- /// for
- ///
- CancellationTokenSource timerCancellationTokenSource;
-
- ///
- /// Represents the running update timer if any
- ///
- Task currentTimerTask;
-
///
/// Construct a
///
@@ -48,52 +38,10 @@ namespace Tgstation.Server.Host.Components
}
///
- public void Dispose()
- {
- timerCancellationTokenSource?.Dispose();
- semaphore.Dispose();
- }
-
- ///
- /// Stops and joins it
- ///
- /// A representing the running operation
- async Task StopTimer()
- {
- if (currentTimerTask == null)
- return;
- timerCancellationTokenSource.Cancel();
- await currentTimerTask.ConfigureAwait(false);
- currentTimerTask = null;
- }
-
- ///
- /// Asyncronously fetch and reset the current branch for each given amount of
- ///
- /// The delay of the timer
- /// The accessString to use for fetch operations
- /// The for the operation
- /// A representing the running operation
- async Task TimerLoop(int minutes, string accessString, CancellationToken cancellationToken)
- {
- try
- {
- while (true)
- {
- await Task.Delay(TimeSpan.FromMinutes(minutes), cancellationToken).ConfigureAwait(false);
- using (var repo = await LoadRepository(cancellationToken).ConfigureAwait(false))
- {
- //TODO: Find the unauthorized exception, catch it, and log it
- await repo.FetchOrigin(accessString, cancellationToken).ConfigureAwait(false);
- await repo.ResetToOrigin(cancellationToken).ConfigureAwait(false);
- }
- }
- }
- catch (OperationCanceledException) { }
- }
+ public void Dispose() => semaphore.Dispose();
///
- public async Task CloneRepository(string url, string accessString, CancellationToken cancellationToken)
+ public async Task CloneRepository(Uri url, string accessString, CancellationToken cancellationToken)
{
await ioManager.DeleteDirectory(".", cancellationToken).ConfigureAwait(false);
@@ -102,7 +50,7 @@ namespace Tgstation.Server.Host.Components
string path = null;
try
{
- path = LibGit2Sharp.Repository.Clone(Repository.GenerateAuthUrl(url, accessString), ioManager.ResolvePath("."), new CloneOptions
+ path = LibGit2Sharp.Repository.Clone(Repository.GenerateAuthUrl(url.ToString(), accessString), ioManager.ResolvePath("."), new CloneOptions
{
OnProgress = (a) => !cancellationToken.IsCancellationRequested,
OnTransferProgress = (a) => !cancellationToken.IsCancellationRequested,
@@ -134,33 +82,5 @@ namespace Tgstation.Server.Host.Components
localSemaphore = null;
});
}
-
- ///
- public async Task SetAutoUpdateInterval(int? newInterval)
- {
- await StopTimer().ConfigureAwait(false);
- if (!newInterval.HasValue)
- return;
-
- string accessString = null;
-
- if (timerCancellationTokenSource != null)
- timerCancellationTokenSource.Dispose();
- timerCancellationTokenSource = new CancellationTokenSource();
-
- currentTimerTask = TimerLoop(repositorySettings.AutoUpdateInterval.Value, accessString, timerCancellationTokenSource.Token);
- }
-
- ///
- public Task StartAsync(CancellationToken cancellationToken) => SetAutoUpdateInterval(repositorySettings.AutoUpdateInterval);
-
- ///
- public async Task StopAsync(CancellationToken cancellationToken)
- {
- var timerStopTask = StopTimer();
- var tcs = new TaskCompletionSource
public DbSet DreamDaemonSettings { get; set; }
///
- /// The in the
- ///
- public DbSet DreamMakerSettings { get; set; }
- ///
/// The s in the
///
public DbSet Jobs { get; set; }
diff --git a/src/Tgstation.Server.Host/Models/IApiConvertable.cs b/src/Tgstation.Server.Host/Models/IApiConvertable.cs
new file mode 100644
index 0000000000..3a53bb26c7
--- /dev/null
+++ b/src/Tgstation.Server.Host/Models/IApiConvertable.cs
@@ -0,0 +1,15 @@
+namespace Tgstation.Server.Host.Models
+{
+ ///
+ /// For converting models to their API form
+ ///
+ /// Which of the this model backs
+ public interface IApiConvertable where TModel : class
+ {
+ ///
+ /// Convert the model to it's API form
+ ///
+ /// A new
+ TModel ToApi();
+ }
+}
\ No newline at end of file
diff --git a/src/Tgstation.Server.Host/Models/IDatabaseContext.cs b/src/Tgstation.Server.Host/Models/IDatabaseContext.cs
index da1bea8f12..d027884bc4 100644
--- a/src/Tgstation.Server.Host/Models/IDatabaseContext.cs
+++ b/src/Tgstation.Server.Host/Models/IDatabaseContext.cs
@@ -34,6 +34,11 @@ namespace Tgstation.Server.Host.Models
///
DbSet RevisionInformations { get; }
+ ///
+ /// The in the
+ ///
+ DbSet DreamMakerSettings { get; set; }
+
///
/// Get the in the
///
diff --git a/src/Tgstation.Server.Host/Models/InstanceUser.cs b/src/Tgstation.Server.Host/Models/InstanceUser.cs
index 6566b2c891..8e2f3b9ec5 100644
--- a/src/Tgstation.Server.Host/Models/InstanceUser.cs
+++ b/src/Tgstation.Server.Host/Models/InstanceUser.cs
@@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations;
+using Tgstation.Server.Api.Rights;
namespace Tgstation.Server.Host.Models
{
@@ -15,5 +16,14 @@ namespace Tgstation.Server.Host.Models
///
[Required]
public Instance Instance { get; set; }
+
+ ///
+ /// If the has any instance rights
+ ///
+ public bool AnyRights => ByondRights != ByondRights.None ||
+ ChatSettingsRights != ChatSettingsRights.None ||
+ ConfigurationRights != ConfigurationRights.None ||
+ DreamDaemonRights != DreamDaemonRights.None ||
+ DreamMakerRights != DreamMakerRights.None;
}
}
diff --git a/src/Tgstation.Server.Host/Models/Job.cs b/src/Tgstation.Server.Host/Models/Job.cs
index 43d1fefba4..1c0cb88d3c 100644
--- a/src/Tgstation.Server.Host/Models/Job.cs
+++ b/src/Tgstation.Server.Host/Models/Job.cs
@@ -3,12 +3,37 @@
namespace Tgstation.Server.Host.Models
{
///
- public sealed class Job : Api.Models.Internal.Job
+ public sealed class Job : Api.Models.Internal.Job, IApiConvertable
{
///
/// See
///
[Required]
public User StartedBy { get; set; }
+
+ ///
+ /// See
+ ///
+ public User CancelledBy { get; set; }
+
+ ///
+ /// The the job belongs to if any
+ ///
+ public Instance Instance { get; set; }
+
+ ///
+ public Api.Models.Job ToApi() => new Api.Models.Job
+ {
+ Id = Id,
+ StartedAt = StartedAt,
+ StoppedAt = StoppedAt,
+ Cancelled = Cancelled,
+ CancelledBy = CancelledBy.ToApi(),
+ CancelRight = CancelRight,
+ CancelRightsType = CancelRightsType,
+ Description = Description,
+ ExceptionDetails = ExceptionDetails,
+ StartedBy = StartedBy.ToApi()
+ };
}
}
diff --git a/src/Tgstation.Server.Host/Models/RevisionInformation.cs b/src/Tgstation.Server.Host/Models/RevisionInformation.cs
index 057cc7e680..2d05122e66 100644
--- a/src/Tgstation.Server.Host/Models/RevisionInformation.cs
+++ b/src/Tgstation.Server.Host/Models/RevisionInformation.cs
@@ -1,9 +1,10 @@
using System.Collections.Generic;
+using System.Linq;
namespace Tgstation.Server.Host.Models
{
///
- public sealed class RevisionInformation : Api.Models.Internal.RevisionInformation
+ public sealed class RevisionInformation : Api.Models.Internal.RevisionInformation, IApiConvertable
{
///
/// The row Id
@@ -14,5 +15,13 @@ namespace Tgstation.Server.Host.Models
/// See
///
public List TestMerges { get; set; }
+
+ ///
+ public Api.Models.RevisionInformation ToApi() => new Api.Models.RevisionInformation
+ {
+ Commit = Commit,
+ OriginRevision = OriginRevision,
+ TestMerges = TestMerges.Select(x => x.ToApi()).ToList()
+ };
}
}
diff --git a/src/Tgstation.Server.Host/Models/TestMerge.cs b/src/Tgstation.Server.Host/Models/TestMerge.cs
index 05e201c04b..108f584086 100644
--- a/src/Tgstation.Server.Host/Models/TestMerge.cs
+++ b/src/Tgstation.Server.Host/Models/TestMerge.cs
@@ -1,9 +1,10 @@
using System.ComponentModel.DataAnnotations;
+using Tgstation.Server.Api.Models;
namespace Tgstation.Server.Host.Models
{
///
- public sealed class TestMerge : Api.Models.Internal.TestMerge
+ public sealed class TestMerge : Api.Models.Internal.TestMerge, IApiConvertable
{
///
/// See
@@ -15,5 +16,20 @@ namespace Tgstation.Server.Host.Models
/// The for the
///
public RevisionInformation RevisionInformation { get; set; }
+
+ ///
+ public Api.Models.TestMerge ToApi() => new Api.Models.TestMerge
+ {
+ Author = Author,
+ BodyAtMerge = BodyAtMerge,
+ MergedAt = MergedAt,
+ TitleAtMerge = TitleAtMerge,
+ Comment = Comment,
+ Id = Id,
+ MergedBy = MergedBy.ToApi(),
+ Number =Number,
+ PullRequestRevision = PullRequestRevision,
+ Url = Url
+ };
}
}
diff --git a/src/Tgstation.Server.Host/Models/User.cs b/src/Tgstation.Server.Host/Models/User.cs
index dec14852f9..93587647ef 100644
--- a/src/Tgstation.Server.Host/Models/User.cs
+++ b/src/Tgstation.Server.Host/Models/User.cs
@@ -1,9 +1,10 @@
using System.Collections.Generic;
+using Tgstation.Server.Api.Models;
namespace Tgstation.Server.Host.Models
{
///
- public sealed class User : Api.Models.Internal.User
+ public sealed class User : Api.Models.Internal.User, IApiConvertable
{
///
/// The hash of the user's password
@@ -24,5 +25,25 @@ namespace Tgstation.Server.Host.Models
/// The s for the
///
public List InstanceUsers { get; set; }
+
+ ///
+ /// See
+ ///
+ /// If we should recurse on
+ /// A new
+ Api.Models.User ToApi(bool recursive) => new Api.Models.User
+ {
+ AdministrationRights = AdministrationRights,
+ CreatedAt = CreatedAt,
+ CreatedBy = recursive ? CreatedBy?.ToApi(false) : null,
+ Enabled = Enabled,
+ Id = Id,
+ InstanceManagerRights = InstanceManagerRights,
+ Name = Name,
+ SystemIdentifier = SystemIdentifier
+ };
+
+ ///
+ public Api.Models.User ToApi() => ToApi(true);
}
}