diff --git a/src/Tgstation.Server.Host/GraphQL/Types/Instance.cs b/src/Tgstation.Server.Host/GraphQL/Types/Instance.cs new file mode 100644 index 0000000000..ea532baa9f --- /dev/null +++ b/src/Tgstation.Server.Host/GraphQL/Types/Instance.cs @@ -0,0 +1,11 @@ +using System; +using System.Linq; + +namespace Tgstation.Server.Host.GraphQL.Types +{ + public sealed class Instance : Entity + { + public IQueryable QueryableInstancePermissionSets() + => throw new NotImplementedException(); + } +} diff --git a/src/Tgstation.Server.Host/GraphQL/Types/InstancePermissionSet.cs b/src/Tgstation.Server.Host/GraphQL/Types/InstancePermissionSet.cs new file mode 100644 index 0000000000..4730c21aff --- /dev/null +++ b/src/Tgstation.Server.Host/GraphQL/Types/InstancePermissionSet.cs @@ -0,0 +1,42 @@ +using Tgstation.Server.Api.Rights; + +namespace Tgstation.Server.Host.GraphQL.Types +{ + public sealed class InstancePermissionSet + { + /// + /// The of the . + /// + public InstancePermissionSetRights? InstancePermissionSetRights { get; set; } + + /// + /// The of the . + /// + public EngineRights? EngineRights { get; set; } + + /// + /// The of the . + /// + public DreamDaemonRights? DreamDaemonRights { get; set; } + + /// + /// The of the . + /// + public DreamMakerRights? DreamMakerRights { get; set; } + + /// + /// The of the . + /// + public RepositoryRights? RepositoryRights { get; set; } + + /// + /// The of the . + /// + public ChatBotRights? ChatBotRights { get; set; } + + /// + /// The of the . + /// + public ConfigurationRights? ConfigurationRights { get; set; } + } +} diff --git a/src/Tgstation.Server.Host/GraphQL/Types/Interceptors/RightsTypeInterceptor.cs b/src/Tgstation.Server.Host/GraphQL/Types/Interceptors/RightsTypeInterceptor.cs index 405a591510..538c32b3ad 100644 --- a/src/Tgstation.Server.Host/GraphQL/Types/Interceptors/RightsTypeInterceptor.cs +++ b/src/Tgstation.Server.Host/GraphQL/Types/Interceptors/RightsTypeInterceptor.cs @@ -1,7 +1,10 @@ using System; using System.Collections.Generic; +using System.Linq; +using System.Reflection; using HotChocolate.Configuration; +using HotChocolate.Types; using HotChocolate.Types.Descriptors.Definitions; using Tgstation.Server.Api.Rights; @@ -13,6 +16,16 @@ namespace Tgstation.Server.Host.GraphQL.Types.Interceptors /// sealed class RightsTypeInterceptor : TypeInterceptor { + /// + /// Prefix normally used by hot chocolate for flag enums. + /// + const string IsPrefix = "is"; + + /// + /// Name given to default None fields. + /// + const string NoneFieldName = $"{IsPrefix}None"; + /// /// Names of rights GraphQL object types. /// @@ -28,12 +41,14 @@ namespace Tgstation.Server.Host.GraphQL.Types.Interceptors /// public RightsTypeInterceptor() { - objectNames = new HashSet(); - inputNames = new HashSet(); + var rightTypes = Enum.GetValues(); + objectNames = new HashSet(rightTypes.Length); + inputNames = new HashSet(rightTypes.Length); - foreach (var rightType in Enum.GetValues()) + foreach (var rightType in rightTypes) { - var flagName = $"{rightType}RightsFlags"; + var rightName = rightType.ToString(); + var flagName = $"{rightName}RightsFlags"; objectNames.Add(flagName); inputNames.Add($"{flagName}Input"); @@ -50,7 +65,6 @@ namespace Tgstation.Server.Host.GraphQL.Types.Interceptors { TField? noneField = null; - const string NoneFieldName = "isNone"; foreach (var field in fields) { var fieldName = field.Name; @@ -60,7 +74,6 @@ namespace Tgstation.Server.Host.GraphQL.Types.Interceptors continue; } - const string IsPrefix = "is"; if (!fieldName.StartsWith(IsPrefix)) throw new InvalidOperationException("Expected flags enum type field to start with \"is\"!"); @@ -73,8 +86,36 @@ namespace Tgstation.Server.Host.GraphQL.Types.Interceptors fields.Remove(noneField); } + /// + /// Fix the for a tweaked field. + /// + /// The to fix. + static void FixFormatter(IInputValueFormatter inputValueFormatter) + { + // now we're hacking privates, but there's a dictionary with bad keys here that needs adjusting + var dictionary = (Dictionary)(inputValueFormatter + .GetType() + .GetField("_flags", BindingFlags.Instance | BindingFlags.NonPublic) + ?.GetValue(inputValueFormatter) + ?? throw new InvalidOperationException("Could not locate private enum mapping dictionary field!")); + + foreach (var key in dictionary.Keys.ToList()) + { + if (key == NoneFieldName) + { + dictionary.Remove(key); + continue; + } + + var value = dictionary[key]; + var newKey = $"can{key.Substring(IsPrefix.Length)}"; + dictionary.Remove(key); + dictionary.Add(newKey, value); + } + } + /// - public override void OnBeforeRegisterDependencies(ITypeDiscoveryContext discoveryContext, DefinitionBase definition) + public override void OnAfterRegisterDependencies(ITypeDiscoveryContext discoveryContext, DefinitionBase definition) { ArgumentNullException.ThrowIfNull(definition); @@ -84,8 +125,17 @@ namespace Tgstation.Server.Host.GraphQL.Types.Interceptors FixFields(objectTypeDef.Fields); } else if (definition is InputObjectTypeDefinition inputTypeDef) - if (inputNames.Contains(inputTypeDef.Name)) + { + const string PermissionSetInputName = $"{nameof(PermissionSet)}Input"; + const string InstancePermissionSetInputName = $"{nameof(InstancePermissionSet)}Input"; + + var name = inputTypeDef.Name; + if (inputNames.Contains(name)) FixFields(inputTypeDef.Fields); + else if (name == PermissionSetInputName || name == InstancePermissionSetInputName) + foreach (var field in inputTypeDef.Fields) + FixFormatter(field.Formatters.Single()); + } } } } diff --git a/src/Tgstation.Server.Host/GraphQL/Types/LocalGateway.cs b/src/Tgstation.Server.Host/GraphQL/Types/LocalGateway.cs index 09d21f7f2d..1428d2c2f2 100644 --- a/src/Tgstation.Server.Host/GraphQL/Types/LocalGateway.cs +++ b/src/Tgstation.Server.Host/GraphQL/Types/LocalGateway.cs @@ -1,4 +1,7 @@ -using Tgstation.Server.Host.GraphQL.Interfaces; +using System; +using System.Linq; + +using Tgstation.Server.Host.GraphQL.Interfaces; namespace Tgstation.Server.Host.GraphQL.Types { @@ -9,5 +12,8 @@ namespace Tgstation.Server.Host.GraphQL.Types { /// public GatewayInformation Information() => new(); + + public IQueryable Instances() + => throw new NotImplementedException(); } }