From 4b584859569e0f66d22f571ba0adb76b05d84813 Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 22 Dec 2025 20:20:54 +0100 Subject: [PATCH 1/5] Fix version number and .net version --- .github/workflows/master.yml | 5 ++--- .github/workflows/nuget.yml | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 87f5cbeab..7e95751c4 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -22,9 +22,8 @@ jobs: run: | $MidnightUtc = [DateTime]::UtcNow.Date $BranchName = "${{ github.ref_name }}".replace('/','-').replace('.','-') - $ApiVersion = (Select-Xml -Path 'src/Artemis.Core/Artemis.Core.csproj' -XPath '//PluginApiVersion').Node.InnerText $NumberOfCommitsToday = (git log --after=$($MidnightUtc.ToString("o")) --oneline | Measure-Object -Line).Lines - $VersionNumber = "$ApiVersion.$($MidnightUtc.ToString("yyyy.MMdd")).$NumberOfCommitsToday" + $VersionNumber = "1.$($MidnightUtc.ToString("yyyy.MMdd")).$NumberOfCommitsToday" # If we're not in master, add the branch name to the version so it counts as prerelease if ($BranchName -ne "master") { $VersionNumber += "-$BranchName" } "version-number=$VersionNumber" >> $Env:GITHUB_OUTPUT @@ -55,7 +54,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: '9.0.x' + dotnet-version: '10.0.x' - name: Publish Artemis run: dotnet publish --configuration Release -p:Version=${{ needs.version.outputs.version-number }} --runtime ${{ matrix.rid }} --output build/${{ matrix.rid }} --self-contained Artemis/src/Artemis.UI.${{ matrix.csproj }}/Artemis.UI.${{ matrix.csproj }}.csproj - name: Upload Artifact diff --git a/.github/workflows/nuget.yml b/.github/workflows/nuget.yml index 75e24ec9e..ddd1f62f8 100644 --- a/.github/workflows/nuget.yml +++ b/.github/workflows/nuget.yml @@ -22,9 +22,8 @@ jobs: run: | $MidnightUtc = [DateTime]::UtcNow.Date $BranchName = "${{ github.ref_name }}".replace('/','-').replace('.','-') - $ApiVersion = (Select-Xml -Path 'src/Artemis.Core/Artemis.Core.csproj' -XPath '//PluginApiVersion').Node.InnerText $NumberOfCommitsToday = (git log --after=$($MidnightUtc.ToString("o")) --oneline | Measure-Object -Line).Lines - $VersionNumber = "$ApiVersion.$($MidnightUtc.ToString("yyyy.MMdd")).$NumberOfCommitsToday" + $VersionNumber = "1.$($MidnightUtc.ToString("yyyy.MMdd")).$NumberOfCommitsToday" # If we're not in master, add the branch name to the version so it counts as prerelease if ($BranchName -ne "master") { $VersionNumber += "-$BranchName" } "version-number=$VersionNumber" >> $Env:GITHUB_OUTPUT @@ -37,7 +36,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: '9.0.x' + dotnet-version: '10.0.x' - name: Checkout uses: actions/checkout@v4 - name: Pack Artemis.Core From 7c7d91eda91373531b14f940b69b00116e64dfb4 Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 22 Dec 2025 20:47:26 +0100 Subject: [PATCH 2/5] Hotfix workshop auto-update --- .../Services/WorkshopService.cs | 76 +++++++++++++------ 1 file changed, 54 insertions(+), 22 deletions(-) diff --git a/src/Artemis.WebClient.Workshop/Services/WorkshopService.cs b/src/Artemis.WebClient.Workshop/Services/WorkshopService.cs index 17ad95888..493ec8131 100644 --- a/src/Artemis.WebClient.Workshop/Services/WorkshopService.cs +++ b/src/Artemis.WebClient.Workshop/Services/WorkshopService.cs @@ -26,9 +26,8 @@ public class WorkshopService : IWorkshopService private readonly IWorkshopClient _workshopClient; private readonly PluginSetting _migratedBuiltInPlugins; - - private bool _initialized; + private bool _mutating; public WorkshopService(ILogger logger, IHttpClientFactory httpClientFactory, @@ -173,27 +172,45 @@ public class WorkshopService : IWorkshopService /// public async Task InstallEntry(IEntrySummary entry, IRelease release, Progress progress, CancellationToken cancellationToken) { - IEntryInstallationHandler handler = _factory.CreateHandler(entry.EntryType); - EntryInstallResult result = await handler.InstallAsync(entry, release, progress, cancellationToken); - if (result.IsSuccess && result.Entry != null) - OnEntryInstalled?.Invoke(this, result.Entry); - else - _logger.Warning("Failed to install entry {Entry}: {Message}", entry, result.Message); + _mutating = true; - return result; + try + { + IEntryInstallationHandler handler = _factory.CreateHandler(entry.EntryType); + EntryInstallResult result = await handler.InstallAsync(entry, release, progress, cancellationToken); + if (result.IsSuccess && result.Entry != null) + OnEntryInstalled?.Invoke(this, result.Entry); + else + _logger.Warning("Failed to install entry {Entry}: {Message}", entry, result.Message); + + return result; + } + finally + { + _mutating = false; + } } /// public async Task UninstallEntry(InstalledEntry installedEntry, CancellationToken cancellationToken) { - IEntryInstallationHandler handler = _factory.CreateHandler(installedEntry.EntryType); - EntryUninstallResult result = await handler.UninstallAsync(installedEntry, cancellationToken); - if (result.IsSuccess) - OnEntryUninstalled?.Invoke(this, installedEntry); - else - _logger.Warning("Failed to uninstall entry {EntryId}: {Message}", installedEntry.Id, result.Message); + _mutating = true; - return result; + try + { + IEntryInstallationHandler handler = _factory.CreateHandler(installedEntry.EntryType); + EntryUninstallResult result = await handler.UninstallAsync(installedEntry, cancellationToken); + if (result.IsSuccess) + OnEntryUninstalled?.Invoke(this, installedEntry); + else + _logger.Warning("Failed to uninstall entry {EntryId}: {Message}", installedEntry.Id, result.Message); + + return result; + } + finally + { + _mutating = false; + } } /// @@ -316,16 +333,28 @@ public class WorkshopService : IWorkshopService // If already migrated, do nothing if (_migratedBuiltInPlugins.Value) return; - - MigratingBuildInPlugins?.Invoke(this, EventArgs.Empty); - bool migrated = await BuiltInPluginsMigrator.Migrate(this, _workshopClient, _logger, _pluginRepository); - _migratedBuiltInPlugins.Value = migrated; - _migratedBuiltInPlugins.Save(); + _mutating = true; + + try + { + MigratingBuildInPlugins?.Invoke(this, EventArgs.Empty); + + bool migrated = await BuiltInPluginsMigrator.Migrate(this, _workshopClient, _logger, _pluginRepository); + _migratedBuiltInPlugins.Value = migrated; + _migratedBuiltInPlugins.Save(); + } + finally + { + _mutating = false; + } } private void ProfileServiceOnProfileRemoved(object? sender, ProfileConfigurationEventArgs e) { + if (_mutating) + return; + InstalledEntry? entry = GetInstalledEntryByProfile(e.ProfileConfiguration); if (entry == null) return; @@ -336,6 +365,9 @@ public class WorkshopService : IWorkshopService private void PluginManagementServiceOnPluginRemoved(object? sender, PluginEventArgs e) { + if (_mutating) + return; + InstalledEntry? entry = GetInstalledEntryByPlugin(e.Plugin); if (entry == null) return; @@ -349,6 +381,6 @@ public class WorkshopService : IWorkshopService public event EventHandler? OnEntryUninstalled; public event EventHandler? OnEntryInstalled; - + public event EventHandler? MigratingBuildInPlugins; } \ No newline at end of file From d063f13ca539fce78c665469675a991fca9d5b26 Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 23 Dec 2025 07:44:52 +0100 Subject: [PATCH 3/5] Hotfix - Auto-enable plugins after updating them --- .../Updating/WorkshopUpdateService.cs | 20 ++++++++++++++++++- .../PluginEntryInstallationHandler.cs | 4 +--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/Artemis.UI/Services/Updating/WorkshopUpdateService.cs b/src/Artemis.UI/Services/Updating/WorkshopUpdateService.cs index 8850de70e..5603aa441 100644 --- a/src/Artemis.UI/Services/Updating/WorkshopUpdateService.cs +++ b/src/Artemis.UI/Services/Updating/WorkshopUpdateService.cs @@ -20,15 +20,21 @@ public class WorkshopUpdateService : IWorkshopUpdateService private readonly ILogger _logger; private readonly IWorkshopClient _client; private readonly IWorkshopService _workshopService; + private readonly IPluginManagementService _pluginManagementService; private readonly Lazy _updateNotificationProvider; private readonly PluginSetting _showNotifications; - public WorkshopUpdateService(ILogger logger, IWorkshopClient client, IWorkshopService workshopService, ISettingsService settingsService, + public WorkshopUpdateService(ILogger logger, + IWorkshopClient client, + IWorkshopService workshopService, + ISettingsService settingsService, + IPluginManagementService pluginManagementService, Lazy updateNotificationProvider) { _logger = logger; _client = client; _workshopService = workshopService; + _pluginManagementService = pluginManagementService; _updateNotificationProvider = updateNotificationProvider; _showNotifications = settingsService.GetSetting("Workshop.ShowNotifications", true); } @@ -88,6 +94,18 @@ public class WorkshopUpdateService : IWorkshopUpdateService else _logger.Warning("Auto-update failed for entry {Entry}: {Message}", entry, updateResult.Message); + if (!updateResult.IsSuccess || updateResult.Installed is not Plugin {IsEnabled: false} updatedPlugin) + return updateResult.IsSuccess; + + try + { + _pluginManagementService.EnablePlugin(updatedPlugin, true, true); + } + catch (Exception e) + { + _logger.Warning(e, "Failed to auto-enable updated plugin {Plugin}", updatedPlugin); + } + return updateResult.IsSuccess; } catch (Exception e) diff --git a/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/PluginEntryInstallationHandler.cs b/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/PluginEntryInstallationHandler.cs index 80a3cb012..7ad0655c1 100644 --- a/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/PluginEntryInstallationHandler.cs +++ b/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/PluginEntryInstallationHandler.cs @@ -101,9 +101,7 @@ public class PluginEntryInstallationHandler : IEntryInstallationHandler { // ignored, will get cleaned up as an orphaned file } - - if (installedEntry.Entity.Id != Guid.Empty) - _workshopService.RemoveInstalledEntry(installedEntry); + return EntryInstallResult.FromException(e); } From 7985cde6a3386a9841689b57bce2d86e52f5b52a Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 23 Dec 2025 08:36:13 +0100 Subject: [PATCH 4/5] Workshop - Update workshop schema --- .../Extensions/IReleaseExtensions.cs | 4 +- .../Extensions/VersionExtensions.cs | 45 - .../EntryReleaseInfoViewModel.cs | 4 +- .../EntryReleaseItemViewModel.cs | 2 +- src/Artemis.WebClient.Workshop/schema.graphql | 1385 ++++++++--------- 5 files changed, 657 insertions(+), 783 deletions(-) delete mode 100644 src/Artemis.UI/Extensions/VersionExtensions.cs diff --git a/src/Artemis.UI/Extensions/IReleaseExtensions.cs b/src/Artemis.UI/Extensions/IReleaseExtensions.cs index fb3adba27..ad7aee561 100644 --- a/src/Artemis.UI/Extensions/IReleaseExtensions.cs +++ b/src/Artemis.UI/Extensions/IReleaseExtensions.cs @@ -16,8 +16,8 @@ public static class ReleaseExtensions { if (release.MinimumVersion == null || Constants.CurrentVersion == "local") return true; - - return release.MinimumVersion <= Version.Parse(Constants.CurrentVersion).ArtemisVersionToLong(); + + return Version.Parse(release.MinimumVersion) <= Version.Parse(Constants.CurrentVersion); } } } \ No newline at end of file diff --git a/src/Artemis.UI/Extensions/VersionExtensions.cs b/src/Artemis.UI/Extensions/VersionExtensions.cs deleted file mode 100644 index 2342c28af..000000000 --- a/src/Artemis.UI/Extensions/VersionExtensions.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; - -namespace Artemis.UI.Extensions; - -public static class VersionExtensions -{ - /// The version to convert - extension(Version version) - { - /// - /// Convert a Version to a long representation for easy comparison in PostgreSQL - /// Assumes format: major.year.dayOfYear.revision (e.g., 1.2024.0225.2) - /// - /// A long value that preserves version comparison order - public long ArtemisVersionToLong() - { - // Format: major.year.dayOfYear.revision - // Convert to: majorYYYYDDDRRRR (16 digits) - // Major: 1 digit (0-9) - // Year: 4 digits (e.g., 2024) - // Day: 3 digits (001-366, padded) - // Revision: 4 digits (0000-9999, padded) - - long major = Math.Max(0, Math.Min(9, version.Major)); - long year = Math.Max(1000, Math.Min(9999, version.Minor)); - long day = Math.Max(1, Math.Min(366, version.Build)); - long revision = Math.Max(0, Math.Min(9999, version.Revision >= 0 ? version.Revision : 0)); - - return major * 100000000000L + - year * 10000000L + - day * 10000L + - revision; - } - - public static Version FromLong(long versionLong) - { - int major = (int)(versionLong / 100000000000L); - int year = (int)((versionLong / 10000000L) % 10000); - int day = (int)((versionLong / 10000L) % 1000); - int revision = (int)(versionLong % 10000L); - - return new Version(major, year, day, revision); - } - } -} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseInfoViewModel.cs b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseInfoViewModel.cs index 7370361fc..2d3575793 100644 --- a/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseInfoViewModel.cs +++ b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseInfoViewModel.cs @@ -69,11 +69,11 @@ public partial class EntryReleaseInfoViewModel : ActivatableViewModelBase }).DisposeWith(d); IsCurrentVersion = Release != null && _workshopService.GetInstalledEntry(Release.Entry.Id)?.ReleaseId == Release.Id; - IncompatibilityReason = Release != null && !Release.IsCompatible() ? $"Requires Artemis v{Version.FromLong(Release.MinimumVersion!.Value)} or later" : null; + IncompatibilityReason = Release != null && !Release.IsCompatible() ? $"Requires Artemis v{Release.MinimumVersion} or later" : null; }); this.WhenAnyValue(vm => vm.Release).Subscribe(r => IsCurrentVersion = r != null && _workshopService.GetInstalledEntry(r.Entry.Id)?.ReleaseId == r.Id); - this.WhenAnyValue(vm => vm.Release).Subscribe(r => IncompatibilityReason = r != null && !r.IsCompatible() ? $"Requires Artemis v{Version.FromLong(r.MinimumVersion!.Value)} or later" : null); + this.WhenAnyValue(vm => vm.Release).Subscribe(r => IncompatibilityReason = r != null && !r.IsCompatible() ? $"Requires Artemis v{r.MinimumVersion} or later" : null); InDetailsScreen = true; } diff --git a/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseItemViewModel.cs b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseItemViewModel.cs index 478da6d7c..c18a32a09 100644 --- a/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseItemViewModel.cs +++ b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseItemViewModel.cs @@ -37,7 +37,7 @@ public partial class EntryReleaseItemViewModel : ActivatableViewModelBase }).DisposeWith(d); IsCurrentVersion = _workshopService.GetInstalledEntry(_entry.Id)?.ReleaseId == Release.Id; - IncompatibilityReason = !Release.IsCompatible() ? $"Requires Artemis v{Version.FromLong(Release.MinimumVersion!.Value)} or later" : null; + IncompatibilityReason = !Release.IsCompatible() ? $"Requires Artemis v{Release.MinimumVersion} or later" : null; }); } diff --git a/src/Artemis.WebClient.Workshop/schema.graphql b/src/Artemis.WebClient.Workshop/schema.graphql index 4962e89b2..d11b90365 100644 --- a/src/Artemis.WebClient.Workshop/schema.graphql +++ b/src/Artemis.WebClient.Workshop/schema.graphql @@ -1,750 +1,669 @@ -schema { - query: Query - mutation: Mutation -} +# This file was generated. Do not edit manually. -type Category { - id: Long! - name: String! - icon: String! - entryType: EntryType +schema { + query: Query + mutation: Mutation } -"Information about the offset pagination." -type CollectionSegmentInfo { - "Indicates whether more items exist following the set defined by the clients arguments." - hasNextPage: Boolean! - "Indicates whether more items exist prior the set defined by the clients arguments." - hasPreviousPage: Boolean! -} - -type DefaultEntryInfo { - entryId: Long! - isEssential: Boolean! - isDeviceProvider: Boolean! -} - -"A segment of a collection." -type EntriesCollectionSegment { - "Information to aid in pagination." - pageInfo: CollectionSegmentInfo! - "A flattened list of the items." - items: [Entry!] - totalCount: Int! @cost(weight: "10") -} - -"A connection to a list of items." -type EntriesV2Connection { - "Information to aid in pagination." - pageInfo: PageInfo! - "A list of edges." - edges: [EntriesV2Edge!] - "A flattened list of the nodes." - nodes: [Entry!] - "Identifies the total count of items in the connection." - totalCount: Int! @cost(weight: "10") -} - -"An edge in a connection." -type EntriesV2Edge { - "A cursor for use in pagination." - cursor: String! - "The item at the end of the edge." - node: Entry! -} - -type Entry { - id: Long! - entryType: EntryType! - createdAt: DateTime! - authorId: UUID! - author: String! - isOfficial: Boolean! - name: String! - summary: String! - description: String! - downloads: Long! - iconId: UUID - icon: Image - latestReleaseId: Long - latestRelease: Release - pluginInfo: PluginInfo - layoutInfo: [LayoutInfo!]! - defaultEntryInfo: DefaultEntryInfo - categories: [Category!]! - tags: [Tag!]! - images: [Image!]! - releases: [Release!]! - dependantReleases: [Release!]! -} - -type Image { - id: UUID! - name: String! - description: String - width: Int! - height: Int! - size: Long! - mimeType: String! - entry: Entry - entryId: Long -} - -type LayoutInfo { - id: Long! - deviceProvider: UUID! - deviceType: RGBDeviceType! - vendor: String! - model: String! - physicalLayout: KeyboardLayoutType - logicalLayout: String - entryId: Long! - entry: Entry! -} - -type Mutation { - addEntry(input: CreateEntryInput!): Entry @authorize @cost(weight: "10") - updateEntry(input: UpdateEntryInput!): Entry @authorize @cost(weight: "10") - removeEntry(id: Long!): Entry @authorize @cost(weight: "10") - updateEntryImage(input: UpdateEntryImageInput!): Image - @authorize - @cost(weight: "10") - setLayoutInfo(input: SetLayoutInfoInput!): [LayoutInfo!]! - @authorize - @cost(weight: "10") - addLayoutInfo(input: CreateLayoutInfoInput!): LayoutInfo - @authorize - @cost(weight: "10") - removeLayoutInfo(id: Long!): LayoutInfo! @authorize @cost(weight: "10") - updateRelease(input: UpdateReleaseInput!): Release - @authorize - @cost(weight: "10") - removeRelease(id: Long!): Release! @authorize @cost(weight: "10") -} - -"Information about pagination in a connection." -type PageInfo { - "Indicates whether more edges exist following the set defined by the clients arguments." - hasNextPage: Boolean! - "Indicates whether more edges exist prior the set defined by the clients arguments." - hasPreviousPage: Boolean! - "When paginating backwards, the cursor to continue." - startCursor: String - "When paginating forwards, the cursor to continue." - endCursor: String -} - -type PluginInfo { - entryId: Long! - entry: Entry! - pluginGuid: UUID! - website: String - helpPage: String - repository: String - requiresAdmin: Boolean! - supportsWindows: Boolean! - supportsLinux: Boolean! - supportsOSX: Boolean! -} - -"A segment of a collection." -type PluginInfosCollectionSegment { - "Information to aid in pagination." - pageInfo: CollectionSegmentInfo! - "A flattened list of the items." - items: [PluginInfo!] - totalCount: Int! @cost(weight: "10") -} - -type Query { - categories( - order: [CategorySortInput!] @cost(weight: "10") - where: CategoryFilterInput @cost(weight: "10") - ): [Category!]! @cost(weight: "10") - entries( - skip: Int - take: Int - search: String - includeDefaults: Boolean - order: [EntrySortInput!] @cost(weight: "10") - where: EntryFilterInput @cost(weight: "10") - ): EntriesCollectionSegment - @listSize( - assumedSize: 100 - slicingArguments: ["take"] - slicingArgumentDefaultValue: 10 - sizedFields: ["items"] - requireOneSlicingArgument: false - ) - @cost(weight: "10") - entriesV2( - search: String - includeDefaults: Boolean - "Returns the first _n_ elements from the list." - first: Int - "Returns the elements in the list that come after the specified cursor." - after: String - "Returns the last _n_ elements from the list." - last: Int - "Returns the elements in the list that come before the specified cursor." - before: String - order: [EntrySortInput!] @cost(weight: "10") - where: EntryFilterInput @cost(weight: "10") - ): EntriesV2Connection - @listSize( - assumedSize: 100 - slicingArguments: ["first", "last"] - slicingArgumentDefaultValue: 10 - sizedFields: ["edges", "nodes"] - requireOneSlicingArgument: false - ) - @cost(weight: "10") - entry(id: Long!): Entry @cost(weight: "10") - submittedEntries( - order: [EntrySortInput!] @cost(weight: "10") - where: EntryFilterInput @cost(weight: "10") - ): [Entry!]! @authorize @cost(weight: "10") - popularEntries(where: EntryFilterInput @cost(weight: "10")): [Entry!]! - @cost(weight: "10") - searchEntries( - input: String! - type: EntryType - order: [EntrySortInput!] @cost(weight: "10") - where: EntryFilterInput @cost(weight: "10") - ): [Entry!]! @cost(weight: "10") - searchLayout( - deviceProvider: UUID! - deviceType: RGBDeviceType! - vendor: String! - model: String! - ): LayoutInfo @cost(weight: "10") - searchKeyboardLayout( - deviceProvider: UUID! - vendor: String! - model: String! - physicalLayout: KeyboardLayoutType! - logicalLayout: String - ): LayoutInfo @cost(weight: "10") - pluginInfos( - skip: Int - take: Int - order: [PluginInfoSortInput!] @cost(weight: "10") - where: PluginInfoFilterInput @cost(weight: "10") - ): PluginInfosCollectionSegment - @listSize( - assumedSize: 100 - slicingArguments: ["take"] - slicingArgumentDefaultValue: 10 - sizedFields: ["items"] - requireOneSlicingArgument: false - ) - @cost(weight: "10") - pluginInfo(pluginGuid: UUID!): PluginInfo @cost(weight: "10") - release(id: Long!): Release @cost(weight: "10") -} - -type Release { - id: Long! - version: String! - changelog: String - createdAt: DateTime! - downloads: Long! - downloadSize: Long! - md5Hash: String - minimumVersion: Long - entry: Entry! - entryId: Long! - dependencies: [Entry!]! -} - -type Tag { - id: Long! - name: String! -} - -input BooleanOperationFilterInput { - eq: Boolean @cost(weight: "10") - neq: Boolean @cost(weight: "10") -} - -input CategoryFilterInput { - and: [CategoryFilterInput!] - or: [CategoryFilterInput!] - id: LongOperationFilterInput - name: StringOperationFilterInput - icon: StringOperationFilterInput - entryType: NullableOfEntryTypeOperationFilterInput -} - -input CategorySortInput { - id: SortEnumType @cost(weight: "10") - name: SortEnumType @cost(weight: "10") - icon: SortEnumType @cost(weight: "10") - entryType: SortEnumType @cost(weight: "10") -} - -input CreateEntryInput { - entryType: EntryType! - name: String! - summary: String! - description: String! - categories: [Long!]! - tags: [String!]! - defaultEntryInfo: DefaultEntryInfoInput -} - -input CreateLayoutInfoInput { - entryId: Long! - deviceProvider: UUID! - deviceType: RGBDeviceType! - vendor: String! - model: String! - physicalLayout: KeyboardLayoutType - logicalLayout: String -} - -input DateTimeOperationFilterInput { - eq: DateTime @cost(weight: "10") - neq: DateTime @cost(weight: "10") - in: [DateTime] @cost(weight: "10") - nin: [DateTime] @cost(weight: "10") - gt: DateTime @cost(weight: "10") - ngt: DateTime @cost(weight: "10") - gte: DateTime @cost(weight: "10") - ngte: DateTime @cost(weight: "10") - lt: DateTime @cost(weight: "10") - nlt: DateTime @cost(weight: "10") - lte: DateTime @cost(weight: "10") - nlte: DateTime @cost(weight: "10") -} - -input DefaultEntryInfoFilterInput { - and: [DefaultEntryInfoFilterInput!] - or: [DefaultEntryInfoFilterInput!] - entryId: LongOperationFilterInput - isEssential: BooleanOperationFilterInput - isDeviceProvider: BooleanOperationFilterInput -} - -input DefaultEntryInfoInput { - isEssential: Boolean! - isDeviceProvider: Boolean! -} - -input DefaultEntryInfoSortInput { - entryId: SortEnumType @cost(weight: "10") - isEssential: SortEnumType @cost(weight: "10") - isDeviceProvider: SortEnumType @cost(weight: "10") -} - -input EntryFilterInput { - and: [EntryFilterInput!] - or: [EntryFilterInput!] - id: LongOperationFilterInput - entryType: EntryTypeOperationFilterInput - createdAt: DateTimeOperationFilterInput - authorId: UuidOperationFilterInput - author: StringOperationFilterInput - isOfficial: BooleanOperationFilterInput - name: StringOperationFilterInput - summary: StringOperationFilterInput - description: StringOperationFilterInput - downloads: LongOperationFilterInput - iconId: UuidOperationFilterInput - icon: ImageFilterInput - latestReleaseId: LongOperationFilterInput - latestRelease: ReleaseFilterInput - pluginInfo: PluginInfoFilterInput - layoutInfo: ListFilterInputTypeOfLayoutInfoFilterInput - defaultEntryInfo: DefaultEntryInfoFilterInput - categories: ListFilterInputTypeOfCategoryFilterInput - tags: ListFilterInputTypeOfTagFilterInput - images: ListFilterInputTypeOfImageFilterInput - releases: ListFilterInputTypeOfReleaseFilterInput - dependantReleases: ListFilterInputTypeOfReleaseFilterInput -} - -input EntrySortInput { - id: SortEnumType @cost(weight: "10") - entryType: SortEnumType @cost(weight: "10") - createdAt: SortEnumType @cost(weight: "10") - authorId: SortEnumType @cost(weight: "10") - author: SortEnumType @cost(weight: "10") - isOfficial: SortEnumType @cost(weight: "10") - name: SortEnumType @cost(weight: "10") - summary: SortEnumType @cost(weight: "10") - description: SortEnumType @cost(weight: "10") - downloads: SortEnumType @cost(weight: "10") - iconId: SortEnumType @cost(weight: "10") - icon: ImageSortInput @cost(weight: "10") - latestReleaseId: SortEnumType @cost(weight: "10") - latestRelease: ReleaseSortInput @cost(weight: "10") - pluginInfo: PluginInfoSortInput @cost(weight: "10") - defaultEntryInfo: DefaultEntryInfoSortInput @cost(weight: "10") -} - -input EntryTypeOperationFilterInput { - eq: EntryType @cost(weight: "10") - neq: EntryType @cost(weight: "10") - in: [EntryType!] @cost(weight: "10") - nin: [EntryType!] @cost(weight: "10") -} - -input ImageFilterInput { - and: [ImageFilterInput!] - or: [ImageFilterInput!] - id: UuidOperationFilterInput - name: StringOperationFilterInput - description: StringOperationFilterInput - width: IntOperationFilterInput - height: IntOperationFilterInput - size: LongOperationFilterInput - mimeType: StringOperationFilterInput - entry: EntryFilterInput - entryId: LongOperationFilterInput -} - -input ImageSortInput { - id: SortEnumType @cost(weight: "10") - name: SortEnumType @cost(weight: "10") - description: SortEnumType @cost(weight: "10") - width: SortEnumType @cost(weight: "10") - height: SortEnumType @cost(weight: "10") - size: SortEnumType @cost(weight: "10") - mimeType: SortEnumType @cost(weight: "10") - entry: EntrySortInput @cost(weight: "10") - entryId: SortEnumType @cost(weight: "10") -} - -input IntOperationFilterInput { - eq: Int @cost(weight: "10") - neq: Int @cost(weight: "10") - in: [Int] @cost(weight: "10") - nin: [Int] @cost(weight: "10") - gt: Int @cost(weight: "10") - ngt: Int @cost(weight: "10") - gte: Int @cost(weight: "10") - ngte: Int @cost(weight: "10") - lt: Int @cost(weight: "10") - nlt: Int @cost(weight: "10") - lte: Int @cost(weight: "10") - nlte: Int @cost(weight: "10") -} - -input LayoutInfoFilterInput { - and: [LayoutInfoFilterInput!] - or: [LayoutInfoFilterInput!] - id: LongOperationFilterInput - deviceProvider: UuidOperationFilterInput - deviceType: RGBDeviceTypeOperationFilterInput - vendor: StringOperationFilterInput - model: StringOperationFilterInput - physicalLayout: NullableOfKeyboardLayoutTypeOperationFilterInput - logicalLayout: StringOperationFilterInput - entryId: LongOperationFilterInput - entry: EntryFilterInput -} - -input LayoutInfoInput { - deviceProvider: UUID! - deviceType: RGBDeviceType! - vendor: String! - model: String! - physicalLayout: KeyboardLayoutType - logicalLayout: String -} - -input ListFilterInputTypeOfCategoryFilterInput { - all: CategoryFilterInput @cost(weight: "10") - none: CategoryFilterInput @cost(weight: "10") - some: CategoryFilterInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input ListFilterInputTypeOfEntryFilterInput { - all: EntryFilterInput @cost(weight: "10") - none: EntryFilterInput @cost(weight: "10") - some: EntryFilterInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input ListFilterInputTypeOfImageFilterInput { - all: ImageFilterInput @cost(weight: "10") - none: ImageFilterInput @cost(weight: "10") - some: ImageFilterInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input ListFilterInputTypeOfLayoutInfoFilterInput { - all: LayoutInfoFilterInput @cost(weight: "10") - none: LayoutInfoFilterInput @cost(weight: "10") - some: LayoutInfoFilterInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input ListFilterInputTypeOfReleaseFilterInput { - all: ReleaseFilterInput @cost(weight: "10") - none: ReleaseFilterInput @cost(weight: "10") - some: ReleaseFilterInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input ListFilterInputTypeOfTagFilterInput { - all: TagFilterInput @cost(weight: "10") - none: TagFilterInput @cost(weight: "10") - some: TagFilterInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input LongOperationFilterInput { - eq: Long @cost(weight: "10") - neq: Long @cost(weight: "10") - in: [Long] @cost(weight: "10") - nin: [Long] @cost(weight: "10") - gt: Long @cost(weight: "10") - ngt: Long @cost(weight: "10") - gte: Long @cost(weight: "10") - ngte: Long @cost(weight: "10") - lt: Long @cost(weight: "10") - nlt: Long @cost(weight: "10") - lte: Long @cost(weight: "10") - nlte: Long @cost(weight: "10") -} - -input NullableOfEntryTypeOperationFilterInput { - eq: EntryType @cost(weight: "10") - neq: EntryType @cost(weight: "10") - in: [EntryType] @cost(weight: "10") - nin: [EntryType] @cost(weight: "10") -} - -input NullableOfKeyboardLayoutTypeOperationFilterInput { - eq: KeyboardLayoutType @cost(weight: "10") - neq: KeyboardLayoutType @cost(weight: "10") - in: [KeyboardLayoutType] @cost(weight: "10") - nin: [KeyboardLayoutType] @cost(weight: "10") -} - -input PluginInfoFilterInput { - and: [PluginInfoFilterInput!] - or: [PluginInfoFilterInput!] - entryId: LongOperationFilterInput - entry: EntryFilterInput - pluginGuid: UuidOperationFilterInput - website: StringOperationFilterInput - helpPage: StringOperationFilterInput - repository: StringOperationFilterInput - requiresAdmin: BooleanOperationFilterInput - supportsWindows: BooleanOperationFilterInput - supportsLinux: BooleanOperationFilterInput - supportsOSX: BooleanOperationFilterInput -} - -input PluginInfoSortInput { - entryId: SortEnumType @cost(weight: "10") - entry: EntrySortInput @cost(weight: "10") - pluginGuid: SortEnumType @cost(weight: "10") - website: SortEnumType @cost(weight: "10") - helpPage: SortEnumType @cost(weight: "10") - repository: SortEnumType @cost(weight: "10") - requiresAdmin: SortEnumType @cost(weight: "10") - supportsWindows: SortEnumType @cost(weight: "10") - supportsLinux: SortEnumType @cost(weight: "10") - supportsOSX: SortEnumType @cost(weight: "10") -} - -input RGBDeviceTypeOperationFilterInput { - eq: RGBDeviceType @cost(weight: "10") - neq: RGBDeviceType @cost(weight: "10") - in: [RGBDeviceType!] @cost(weight: "10") - nin: [RGBDeviceType!] @cost(weight: "10") -} - -input ReleaseFilterInput { - and: [ReleaseFilterInput!] - or: [ReleaseFilterInput!] - id: LongOperationFilterInput - version: StringOperationFilterInput - changelog: StringOperationFilterInput - createdAt: DateTimeOperationFilterInput - downloads: LongOperationFilterInput - downloadSize: LongOperationFilterInput - md5Hash: StringOperationFilterInput - minimumVersion: LongOperationFilterInput - entry: EntryFilterInput - entryId: LongOperationFilterInput - dependencies: ListFilterInputTypeOfEntryFilterInput -} - -input ReleaseSortInput { - id: SortEnumType @cost(weight: "10") - version: SortEnumType @cost(weight: "10") - changelog: SortEnumType @cost(weight: "10") - createdAt: SortEnumType @cost(weight: "10") - downloads: SortEnumType @cost(weight: "10") - downloadSize: SortEnumType @cost(weight: "10") - md5Hash: SortEnumType @cost(weight: "10") - minimumVersion: SortEnumType @cost(weight: "10") - entry: EntrySortInput @cost(weight: "10") - entryId: SortEnumType @cost(weight: "10") -} - -input SetLayoutInfoInput { - entryId: Long! - layoutInfo: [LayoutInfoInput!]! -} - -input StringOperationFilterInput { - and: [StringOperationFilterInput!] - or: [StringOperationFilterInput!] - eq: String @cost(weight: "10") - neq: String @cost(weight: "10") - contains: String @cost(weight: "20") - ncontains: String @cost(weight: "20") - in: [String] @cost(weight: "10") - nin: [String] @cost(weight: "10") - startsWith: String @cost(weight: "20") - nstartsWith: String @cost(weight: "20") - endsWith: String @cost(weight: "20") - nendsWith: String @cost(weight: "20") -} - -input TagFilterInput { - and: [TagFilterInput!] - or: [TagFilterInput!] - id: LongOperationFilterInput - name: StringOperationFilterInput -} - -input UpdateEntryImageInput { - id: UUID! - name: String! - description: String -} - -input UpdateEntryInput { - id: Long! - name: String! - summary: String! - description: String! - categories: [Long!]! - tags: [String!]! - defaultEntryInfo: DefaultEntryInfoInput -} - -input UpdateReleaseInput { - id: Long! - changelog: String -} - -input UuidOperationFilterInput { - eq: UUID @cost(weight: "10") - neq: UUID @cost(weight: "10") - in: [UUID] @cost(weight: "10") - nin: [UUID] @cost(weight: "10") - gt: UUID @cost(weight: "10") - ngt: UUID @cost(weight: "10") - gte: UUID @cost(weight: "10") - ngte: UUID @cost(weight: "10") - lt: UUID @cost(weight: "10") - nlt: UUID @cost(weight: "10") - lte: UUID @cost(weight: "10") - nlte: UUID @cost(weight: "10") -} - -"Defines when a policy shall be executed." -enum ApplyPolicy { - "Before the resolver was executed." - BEFORE_RESOLVER - "After the resolver was executed." - AFTER_RESOLVER - "The policy is applied in the validation step before the execution." - VALIDATION -} - -enum EntryType { - PLUGIN - PROFILE - LAYOUT -} - -enum KeyboardLayoutType { - UNKNOWN - ANSI - ISO - JIS - ABNT - KS -} - -enum RGBDeviceType { - NONE - KEYBOARD - MOUSE - HEADSET - MOUSEPAD - LED_STRIPE - LED_MATRIX - MAINBOARD - GRAPHICS_CARD - DRAM - HEADSET_STAND - KEYPAD - FAN - SPEAKER - COOLER - MONITOR - LED_CONTROLLER - GAME_CONTROLLER - UNKNOWN - ALL -} - -enum SortEnumType { - ASC - DESC -} - -"The authorize directive." -directive @authorize( - "The name of the authorization policy that determines access to the annotated resource." - policy: String - "Roles that are allowed to access the annotated resource." - roles: [String!] - "Defines when when the authorize directive shall be applied.By default the authorize directives are applied during the validation phase." - apply: ApplyPolicy! = BEFORE_RESOLVER -) repeatable on OBJECT | FIELD_DEFINITION - "The purpose of the `cost` directive is to define a `weight` for GraphQL types, fields, and arguments. Static analysis can use these weights when calculating the overall cost of a query or response." directive @cost( - "The `weight` argument defines what value to add to the overall cost for every appearance, or possible appearance, of a type, field, argument, etc." - weight: String! + "The `weight` argument defines what value to add to the overall cost for every appearance, or possible appearance, of a type, field, argument, etc." + weight: String! ) on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM | INPUT_FIELD_DEFINITION "The purpose of the `@listSize` directive is to either inform the static analysis about the size of returned lists (if that information is statically available), or to point the analysis to where to find that information." directive @listSize( - "The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." - assumedSize: Int - "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." - slicingArguments: [String!] - "The `slicingArgumentDefaultValue` argument can be used to define a default value for a slicing argument, which is used if the argument is not present in a query." - slicingArgumentDefaultValue: Int - "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." - sizedFields: [String!] - "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." - requireOneSlicingArgument: Boolean! = true + "The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." + assumedSize: Int, + "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." + requireOneSlicingArgument: Boolean! = true, + "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." + sizedFields: [String!], + "The `slicingArgumentDefaultValue` argument can be used to define a default value for a slicing argument, which is used if the argument is not present in a query." + slicingArgumentDefaultValue: Int, + "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." + slicingArguments: [String!] ) on FIELD_DEFINITION -"The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions." -directive @specifiedBy( - "The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." - url: String! -) on SCALAR +type Category { + entryType: EntryType + icon: String! + id: Long! + name: String! +} + +"Information about the offset pagination." +type CollectionSegmentInfo { + "Indicates whether more items exist following the set defined by the clients arguments." + hasNextPage: Boolean! + "Indicates whether more items exist prior the set defined by the clients arguments." + hasPreviousPage: Boolean! +} + +type DefaultEntryInfo { + entryId: Long! + isDeviceProvider: Boolean! + isEssential: Boolean! +} + +"A segment of a collection." +type EntriesCollectionSegment { + "A flattened list of the items." + items: [Entry!] + "Information to aid in pagination." + pageInfo: CollectionSegmentInfo! + totalCount: Int! +} + +"A connection to a list of items." +type EntriesV2Connection { + "A list of edges." + edges: [EntriesV2Edge!] + "A flattened list of the nodes." + nodes: [Entry!] + "Information to aid in pagination." + pageInfo: PageInfo! + "Identifies the total count of items in the connection." + totalCount: Int! +} + +"An edge in a connection." +type EntriesV2Edge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: Entry! +} + +type Entry { + author: String! + authorId: UUID! + categories: [Category!]! + createdAt: DateTime! + defaultEntryInfo: DefaultEntryInfo + dependantReleases: [Release!]! + description: String! + downloads: Long! + entryType: EntryType! + icon: Image + iconId: UUID + id: Long! + images: [Image!]! + isOfficial: Boolean! + latestRelease: Release + latestReleaseId: Long + layoutInfo: [LayoutInfo!]! + name: String! + pluginInfo: PluginInfo + releases: [Release!]! + summary: String! + tags: [Tag!]! +} + +type Image { + description: String + entry: Entry + entryId: Long + height: Int! + id: UUID! + mimeType: String! + name: String! + size: Long! + width: Int! +} + +type LayoutInfo { + deviceProvider: UUID! + deviceType: RGBDeviceType! + entry: Entry! + entryId: Long! + id: Long! + logicalLayout: String + model: String! + physicalLayout: KeyboardLayoutType + vendor: String! +} + +type Mutation { + addEntry(input: CreateEntryInput!): Entry + addLayoutInfo(input: CreateLayoutInfoInput!): LayoutInfo + removeEntry(id: Long!): Entry + removeLayoutInfo(id: Long!): LayoutInfo! + removeRelease(id: Long!): Release! + setLayoutInfo(input: SetLayoutInfoInput!): [LayoutInfo!]! + updateEntry(input: UpdateEntryInput!): Entry + updateEntryImage(input: UpdateEntryImageInput!): Image + updateRelease(input: UpdateReleaseInput!): Release +} + +"Information about pagination in a connection." +type PageInfo { + "When paginating forwards, the cursor to continue." + endCursor: String + "Indicates whether more edges exist following the set defined by the clients arguments." + hasNextPage: Boolean! + "Indicates whether more edges exist prior the set defined by the clients arguments." + hasPreviousPage: Boolean! + "When paginating backwards, the cursor to continue." + startCursor: String +} + +type PluginInfo { + entry: Entry! + entryId: Long! + helpPage: String + pluginGuid: UUID! + repository: String + requiresAdmin: Boolean! + supportsLinux: Boolean! + supportsOSX: Boolean! + supportsWindows: Boolean! + website: String +} + +"A segment of a collection." +type PluginInfosCollectionSegment { + "A flattened list of the items." + items: [PluginInfo!] + "Information to aid in pagination." + pageInfo: CollectionSegmentInfo! + totalCount: Int! +} + +type Query { + categories(order: [CategorySortInput!], where: CategoryFilterInput): [Category!]! + entries(includeDefaults: Boolean, order: [EntrySortInput!], search: String, skip: Int, take: Int, where: EntryFilterInput): EntriesCollectionSegment + entriesV2( + "Returns the elements in the list that come after the specified cursor." + after: String, + "Returns the elements in the list that come before the specified cursor." + before: String, + "Returns the first _n_ elements from the list." + first: Int, + includeDefaults: Boolean, + "Returns the last _n_ elements from the list." + last: Int, + order: [EntrySortInput!], + search: String, + where: EntryFilterInput + ): EntriesV2Connection + entry(id: Long!): Entry + pluginInfo(pluginGuid: UUID!): PluginInfo + pluginInfos(order: [PluginInfoSortInput!], skip: Int, take: Int, where: PluginInfoFilterInput): PluginInfosCollectionSegment + popularEntries(where: EntryFilterInput): [Entry!]! + release(id: Long!): Release + searchEntries(input: String!, order: [EntrySortInput!], type: EntryType, where: EntryFilterInput): [Entry!]! + searchKeyboardLayout(deviceProvider: UUID!, logicalLayout: String, model: String!, physicalLayout: KeyboardLayoutType!, vendor: String!): LayoutInfo + searchLayout(deviceProvider: UUID!, deviceType: RGBDeviceType!, model: String!, vendor: String!): LayoutInfo + submittedEntries(order: [EntrySortInput!], where: EntryFilterInput): [Entry!]! +} + +type Release { + changelog: String + createdAt: DateTime! + dependencies: [Entry!]! + downloadSize: Long! + downloads: Long! + entry: Entry! + entryId: Long! + id: Long! + md5Hash: String + minimumVersion: String + version: String! +} + +type Tag { + id: Long! + name: String! +} + +"Defines when a policy shall be executed." +enum ApplyPolicy { + "After the resolver was executed." + AFTER_RESOLVER + "Before the resolver was executed." + BEFORE_RESOLVER + "The policy is applied in the validation step before the execution." + VALIDATION +} + +enum EntryType { + LAYOUT + PLUGIN + PROFILE +} + +enum KeyboardLayoutType { + ABNT + ANSI + ISO + JIS + KS + UNKNOWN +} + +enum RGBDeviceType { + ALL + COOLER + DRAM + FAN + GAME_CONTROLLER + GRAPHICS_CARD + HEADSET + HEADSET_STAND + KEYBOARD + KEYPAD + LED_CONTROLLER + LED_MATRIX + LED_STRIPE + MAINBOARD + MONITOR + MOUSE + MOUSEPAD + NONE + SPEAKER + UNKNOWN +} + +enum SortEnumType { + ASC + DESC +} "The `DateTime` scalar represents an ISO-8601 compliant date time type." -scalar DateTime @specifiedBy(url: "https://www.graphql-scalars.com/date-time") +scalar DateTime "The `Long` scalar type represents non-fractional signed whole 64-bit numeric values. Long can represent values between -(2^63) and 2^63 - 1." scalar Long -scalar UUID @specifiedBy(url: "https://tools.ietf.org/html/rfc4122") +scalar UUID + +input BooleanOperationFilterInput { + eq: Boolean + neq: Boolean +} + +input CategoryFilterInput { + and: [CategoryFilterInput!] + entryType: NullableOfEntryTypeOperationFilterInput + icon: StringOperationFilterInput + id: LongOperationFilterInput + name: StringOperationFilterInput + or: [CategoryFilterInput!] +} + +input CategorySortInput { + entryType: SortEnumType + icon: SortEnumType + id: SortEnumType + name: SortEnumType +} + +input CreateEntryInput { + categories: [Long!]! + defaultEntryInfo: DefaultEntryInfoInput + description: String! + entryType: EntryType! + name: String! + summary: String! + tags: [String!]! +} + +input CreateLayoutInfoInput { + deviceProvider: UUID! + deviceType: RGBDeviceType! + entryId: Long! + logicalLayout: String + model: String! + physicalLayout: KeyboardLayoutType + vendor: String! +} + +input DateTimeOperationFilterInput { + eq: DateTime + gt: DateTime + gte: DateTime + in: [DateTime] + lt: DateTime + lte: DateTime + neq: DateTime + ngt: DateTime + ngte: DateTime + nin: [DateTime] + nlt: DateTime + nlte: DateTime +} + +input DefaultEntryInfoFilterInput { + and: [DefaultEntryInfoFilterInput!] + entryId: LongOperationFilterInput + isDeviceProvider: BooleanOperationFilterInput + isEssential: BooleanOperationFilterInput + or: [DefaultEntryInfoFilterInput!] +} + +input DefaultEntryInfoInput { + isDeviceProvider: Boolean! + isEssential: Boolean! +} + +input DefaultEntryInfoSortInput { + entryId: SortEnumType + isDeviceProvider: SortEnumType + isEssential: SortEnumType +} + +input EntryFilterInput { + and: [EntryFilterInput!] + author: StringOperationFilterInput + authorId: UuidOperationFilterInput + categories: ListFilterInputTypeOfCategoryFilterInput + createdAt: DateTimeOperationFilterInput + defaultEntryInfo: DefaultEntryInfoFilterInput + dependantReleases: ListFilterInputTypeOfReleaseFilterInput + description: StringOperationFilterInput + downloads: LongOperationFilterInput + entryType: EntryTypeOperationFilterInput + icon: ImageFilterInput + iconId: UuidOperationFilterInput + id: LongOperationFilterInput + images: ListFilterInputTypeOfImageFilterInput + isOfficial: BooleanOperationFilterInput + latestRelease: ReleaseFilterInput + latestReleaseId: LongOperationFilterInput + layoutInfo: ListFilterInputTypeOfLayoutInfoFilterInput + name: StringOperationFilterInput + or: [EntryFilterInput!] + pluginInfo: PluginInfoFilterInput + releases: ListFilterInputTypeOfReleaseFilterInput + summary: StringOperationFilterInput + tags: ListFilterInputTypeOfTagFilterInput +} + +input EntrySortInput { + author: SortEnumType + authorId: SortEnumType + createdAt: SortEnumType + defaultEntryInfo: DefaultEntryInfoSortInput + description: SortEnumType + downloads: SortEnumType + entryType: SortEnumType + icon: ImageSortInput + iconId: SortEnumType + id: SortEnumType + isOfficial: SortEnumType + latestRelease: ReleaseSortInput + latestReleaseId: SortEnumType + name: SortEnumType + pluginInfo: PluginInfoSortInput + summary: SortEnumType +} + +input EntryTypeOperationFilterInput { + eq: EntryType + in: [EntryType!] + neq: EntryType + nin: [EntryType!] +} + +input ImageFilterInput { + and: [ImageFilterInput!] + description: StringOperationFilterInput + entry: EntryFilterInput + entryId: LongOperationFilterInput + height: IntOperationFilterInput + id: UuidOperationFilterInput + mimeType: StringOperationFilterInput + name: StringOperationFilterInput + or: [ImageFilterInput!] + size: LongOperationFilterInput + width: IntOperationFilterInput +} + +input ImageSortInput { + description: SortEnumType + entry: EntrySortInput + entryId: SortEnumType + height: SortEnumType + id: SortEnumType + mimeType: SortEnumType + name: SortEnumType + size: SortEnumType + width: SortEnumType +} + +input IntOperationFilterInput { + eq: Int + gt: Int + gte: Int + in: [Int] + lt: Int + lte: Int + neq: Int + ngt: Int + ngte: Int + nin: [Int] + nlt: Int + nlte: Int +} + +input LayoutInfoFilterInput { + and: [LayoutInfoFilterInput!] + deviceProvider: UuidOperationFilterInput + deviceType: RGBDeviceTypeOperationFilterInput + entry: EntryFilterInput + entryId: LongOperationFilterInput + id: LongOperationFilterInput + logicalLayout: StringOperationFilterInput + model: StringOperationFilterInput + or: [LayoutInfoFilterInput!] + physicalLayout: NullableOfKeyboardLayoutTypeOperationFilterInput + vendor: StringOperationFilterInput +} + +input LayoutInfoInput { + deviceProvider: UUID! + deviceType: RGBDeviceType! + logicalLayout: String + model: String! + physicalLayout: KeyboardLayoutType + vendor: String! +} + +input ListFilterInputTypeOfCategoryFilterInput { + all: CategoryFilterInput + any: Boolean + none: CategoryFilterInput + some: CategoryFilterInput +} + +input ListFilterInputTypeOfEntryFilterInput { + all: EntryFilterInput + any: Boolean + none: EntryFilterInput + some: EntryFilterInput +} + +input ListFilterInputTypeOfImageFilterInput { + all: ImageFilterInput + any: Boolean + none: ImageFilterInput + some: ImageFilterInput +} + +input ListFilterInputTypeOfLayoutInfoFilterInput { + all: LayoutInfoFilterInput + any: Boolean + none: LayoutInfoFilterInput + some: LayoutInfoFilterInput +} + +input ListFilterInputTypeOfReleaseFilterInput { + all: ReleaseFilterInput + any: Boolean + none: ReleaseFilterInput + some: ReleaseFilterInput +} + +input ListFilterInputTypeOfTagFilterInput { + all: TagFilterInput + any: Boolean + none: TagFilterInput + some: TagFilterInput +} + +input LongOperationFilterInput { + eq: Long + gt: Long + gte: Long + in: [Long] + lt: Long + lte: Long + neq: Long + ngt: Long + ngte: Long + nin: [Long] + nlt: Long + nlte: Long +} + +input NullableOfEntryTypeOperationFilterInput { + eq: EntryType + in: [EntryType] + neq: EntryType + nin: [EntryType] +} + +input NullableOfKeyboardLayoutTypeOperationFilterInput { + eq: KeyboardLayoutType + in: [KeyboardLayoutType] + neq: KeyboardLayoutType + nin: [KeyboardLayoutType] +} + +input PluginInfoFilterInput { + and: [PluginInfoFilterInput!] + entry: EntryFilterInput + entryId: LongOperationFilterInput + helpPage: StringOperationFilterInput + or: [PluginInfoFilterInput!] + pluginGuid: UuidOperationFilterInput + repository: StringOperationFilterInput + requiresAdmin: BooleanOperationFilterInput + supportsLinux: BooleanOperationFilterInput + supportsOSX: BooleanOperationFilterInput + supportsWindows: BooleanOperationFilterInput + website: StringOperationFilterInput +} + +input PluginInfoSortInput { + entry: EntrySortInput + entryId: SortEnumType + helpPage: SortEnumType + pluginGuid: SortEnumType + repository: SortEnumType + requiresAdmin: SortEnumType + supportsLinux: SortEnumType + supportsOSX: SortEnumType + supportsWindows: SortEnumType + website: SortEnumType +} + +input RGBDeviceTypeOperationFilterInput { + eq: RGBDeviceType + in: [RGBDeviceType!] + neq: RGBDeviceType + nin: [RGBDeviceType!] +} + +input ReleaseFilterInput { + and: [ReleaseFilterInput!] + changelog: StringOperationFilterInput + createdAt: DateTimeOperationFilterInput + dependencies: ListFilterInputTypeOfEntryFilterInput + downloadSize: LongOperationFilterInput + downloads: LongOperationFilterInput + entry: EntryFilterInput + entryId: LongOperationFilterInput + id: LongOperationFilterInput + md5Hash: StringOperationFilterInput + minimumVersion: StringOperationFilterInput + or: [ReleaseFilterInput!] + version: StringOperationFilterInput +} + +input ReleaseSortInput { + changelog: SortEnumType + createdAt: SortEnumType + downloadSize: SortEnumType + downloads: SortEnumType + entry: EntrySortInput + entryId: SortEnumType + id: SortEnumType + md5Hash: SortEnumType + minimumVersion: SortEnumType + version: SortEnumType +} + +input SetLayoutInfoInput { + entryId: Long! + layoutInfo: [LayoutInfoInput!]! +} + +input StringOperationFilterInput { + and: [StringOperationFilterInput!] + contains: String + endsWith: String + eq: String + in: [String] + ncontains: String + nendsWith: String + neq: String + nin: [String] + nstartsWith: String + or: [StringOperationFilterInput!] + startsWith: String +} + +input TagFilterInput { + and: [TagFilterInput!] + id: LongOperationFilterInput + name: StringOperationFilterInput + or: [TagFilterInput!] +} + +input UpdateEntryImageInput { + description: String + id: UUID! + name: String! +} + +input UpdateEntryInput { + categories: [Long!]! + defaultEntryInfo: DefaultEntryInfoInput + description: String! + id: Long! + name: String! + summary: String! + tags: [String!]! +} + +input UpdateReleaseInput { + changelog: String + id: Long! +} + +input UuidOperationFilterInput { + eq: UUID + gt: UUID + gte: UUID + in: [UUID] + lt: UUID + lte: UUID + neq: UUID + ngt: UUID + ngte: UUID + nin: [UUID] + nlt: UUID + nlte: UUID +} From efd893c20976bcbc259faf3f2e346e98ea5a859d Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 23 Dec 2025 20:53:39 +0100 Subject: [PATCH 5/5] Workshop - Fix misalignment in EntryTypes causing plugins to not load Core - Downgrade SkiaSharp back to what Avalonia likes, 2.88.9 --- src/Artemis.Core/Constants.cs | 2 +- .../Models/InstalledEntry.cs | 21 +- src/Artemis.WebClient.Workshop/schema.graphql | 835 ++++++++++-------- src/Directory.Packages.props | 4 +- 4 files changed, 479 insertions(+), 383 deletions(-) diff --git a/src/Artemis.Core/Constants.cs b/src/Artemis.Core/Constants.cs index 21e819b2d..06d0bb786 100644 --- a/src/Artemis.Core/Constants.cs +++ b/src/Artemis.Core/Constants.cs @@ -41,7 +41,7 @@ public static class Constants /// The full path to the Artemis data folder /// #if DEBUG - public static readonly string DataFolder = Path.Combine(BaseFolder, "Artemis-dev"); + public static readonly string DataFolder = Path.Combine(BaseFolder, "Artemis"); #else public static readonly string DataFolder = Path.Combine(BaseFolder, "Artemis"); #endif diff --git a/src/Artemis.WebClient.Workshop/Models/InstalledEntry.cs b/src/Artemis.WebClient.Workshop/Models/InstalledEntry.cs index b07033ca6..735f1f1d2 100644 --- a/src/Artemis.WebClient.Workshop/Models/InstalledEntry.cs +++ b/src/Artemis.WebClient.Workshop/Models/InstalledEntry.cs @@ -225,7 +225,6 @@ public class InstalledEntry : CorePropertyChanged IsOfficial = Entity.IsOfficial; Name = Entity.Name; Summary = Entity.Summary; - EntryType = (EntryType) Entity.EntryType; Downloads = Entity.Downloads; CreatedAt = Entity.CreatedAt; LatestReleaseId = Entity.LatestReleaseId; @@ -235,6 +234,15 @@ public class InstalledEntry : CorePropertyChanged ReleaseVersion = Entity.ReleaseVersion; InstalledAt = Entity.InstalledAt; AutoUpdate = Entity.AutoUpdate; + + // Avoiding a cast here, the enum has shifted around before as it is generated from the GraphQL schema + EntryType = Entity.EntryType switch + { + 0 => EntryType.Plugin, + 1 => EntryType.Profile, + 2 => EntryType.Layout, + _ => EntryType + }; _metadata = Entity.Metadata != null ? new Dictionary(Entity.Metadata) : []; } @@ -242,8 +250,6 @@ public class InstalledEntry : CorePropertyChanged internal void Save() { Entity.EntryId = Id; - Entity.EntryType = (int) EntryType; - Entity.Author = Author; Entity.IsOfficial = IsOfficial; Entity.Name = Name; @@ -257,6 +263,15 @@ public class InstalledEntry : CorePropertyChanged Entity.ReleaseVersion = ReleaseVersion; Entity.InstalledAt = InstalledAt; Entity.AutoUpdate = AutoUpdate; + + // Avoiding a cast here, the enum has shifted around before as it is generated from the GraphQL schema + Entity.EntryType = EntryType switch + { + EntryType.Plugin => 0, + EntryType.Profile => 1, + EntryType.Layout => 2, + _ => Entity.EntryType + }; Entity.Metadata = new Dictionary(_metadata); } diff --git a/src/Artemis.WebClient.Workshop/schema.graphql b/src/Artemis.WebClient.Workshop/schema.graphql index d11b90365..9112a4def 100644 --- a/src/Artemis.WebClient.Workshop/schema.graphql +++ b/src/Artemis.WebClient.Workshop/schema.graphql @@ -1,35 +1,13 @@ -# This file was generated. Do not edit manually. - -schema { +schema { query: Query mutation: Mutation } -"The purpose of the `cost` directive is to define a `weight` for GraphQL types, fields, and arguments. Static analysis can use these weights when calculating the overall cost of a query or response." -directive @cost( - "The `weight` argument defines what value to add to the overall cost for every appearance, or possible appearance, of a type, field, argument, etc." - weight: String! -) on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM | INPUT_FIELD_DEFINITION - -"The purpose of the `@listSize` directive is to either inform the static analysis about the size of returned lists (if that information is statically available), or to point the analysis to where to find that information." -directive @listSize( - "The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." - assumedSize: Int, - "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." - requireOneSlicingArgument: Boolean! = true, - "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." - sizedFields: [String!], - "The `slicingArgumentDefaultValue` argument can be used to define a default value for a slicing argument, which is used if the argument is not present in a query." - slicingArgumentDefaultValue: Int, - "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." - slicingArguments: [String!] -) on FIELD_DEFINITION - type Category { - entryType: EntryType - icon: String! id: Long! name: String! + icon: String! + entryType: EntryType } "Information about the offset pagination." @@ -42,29 +20,29 @@ type CollectionSegmentInfo { type DefaultEntryInfo { entryId: Long! - isDeviceProvider: Boolean! isEssential: Boolean! + isDeviceProvider: Boolean! } "A segment of a collection." type EntriesCollectionSegment { - "A flattened list of the items." - items: [Entry!] "Information to aid in pagination." pageInfo: CollectionSegmentInfo! - totalCount: Int! + "A flattened list of the items." + items: [Entry!] + totalCount: Int! @cost(weight: "10") } "A connection to a list of items." type EntriesV2Connection { + "Information to aid in pagination." + pageInfo: PageInfo! "A list of edges." edges: [EntriesV2Edge!] "A flattened list of the nodes." nodes: [Entry!] - "Information to aid in pagination." - pageInfo: PageInfo! "Identifies the total count of items in the connection." - totalCount: Int! + totalCount: Int! @cost(weight: "10") } "An edge in a connection." @@ -76,140 +54,207 @@ type EntriesV2Edge { } type Entry { - author: String! - authorId: UUID! - categories: [Category!]! + id: Long! + entryType: EntryType! createdAt: DateTime! - defaultEntryInfo: DefaultEntryInfo - dependantReleases: [Release!]! + authorId: UUID! + author: String! + isOfficial: Boolean! + name: String! + summary: String! description: String! downloads: Long! - entryType: EntryType! - icon: Image iconId: UUID - id: Long! - images: [Image!]! - isOfficial: Boolean! - latestRelease: Release + icon: Image latestReleaseId: Long - layoutInfo: [LayoutInfo!]! - name: String! + latestRelease: Release pluginInfo: PluginInfo - releases: [Release!]! - summary: String! + layoutInfo: [LayoutInfo!]! + defaultEntryInfo: DefaultEntryInfo + categories: [Category!]! tags: [Tag!]! + images: [Image!]! + releases: [Release!]! + dependantReleases: [Release!]! } type Image { + id: UUID! + name: String! description: String + width: Int! + height: Int! + size: Long! + mimeType: String! entry: Entry entryId: Long - height: Int! - id: UUID! - mimeType: String! - name: String! - size: Long! - width: Int! } type LayoutInfo { + id: Long! deviceProvider: UUID! deviceType: RGBDeviceType! - entry: Entry! - entryId: Long! - id: Long! - logicalLayout: String + vendor: String! model: String! physicalLayout: KeyboardLayoutType - vendor: String! + logicalLayout: String + entryId: Long! + entry: Entry! } type Mutation { - addEntry(input: CreateEntryInput!): Entry - addLayoutInfo(input: CreateLayoutInfoInput!): LayoutInfo - removeEntry(id: Long!): Entry - removeLayoutInfo(id: Long!): LayoutInfo! - removeRelease(id: Long!): Release! - setLayoutInfo(input: SetLayoutInfoInput!): [LayoutInfo!]! - updateEntry(input: UpdateEntryInput!): Entry + addEntry(input: CreateEntryInput!): Entry @authorize @cost(weight: "10") + updateEntry(input: UpdateEntryInput!): Entry @authorize @cost(weight: "10") + removeEntry(id: Long!): Entry @authorize @cost(weight: "10") updateEntryImage(input: UpdateEntryImageInput!): Image + @authorize + @cost(weight: "10") + setLayoutInfo(input: SetLayoutInfoInput!): [LayoutInfo!]! + @authorize + @cost(weight: "10") + addLayoutInfo(input: CreateLayoutInfoInput!): LayoutInfo + @authorize + @cost(weight: "10") + removeLayoutInfo(id: Long!): LayoutInfo! @authorize @cost(weight: "10") updateRelease(input: UpdateReleaseInput!): Release + @authorize + @cost(weight: "10") + removeRelease(id: Long!): Release! @authorize @cost(weight: "10") } "Information about pagination in a connection." type PageInfo { - "When paginating forwards, the cursor to continue." - endCursor: String "Indicates whether more edges exist following the set defined by the clients arguments." hasNextPage: Boolean! "Indicates whether more edges exist prior the set defined by the clients arguments." hasPreviousPage: Boolean! "When paginating backwards, the cursor to continue." startCursor: String + "When paginating forwards, the cursor to continue." + endCursor: String } type PluginInfo { - entry: Entry! entryId: Long! - helpPage: String + entry: Entry! pluginGuid: UUID! + website: String + helpPage: String repository: String requiresAdmin: Boolean! + supportsWindows: Boolean! supportsLinux: Boolean! supportsOSX: Boolean! - supportsWindows: Boolean! - website: String } "A segment of a collection." type PluginInfosCollectionSegment { - "A flattened list of the items." - items: [PluginInfo!] "Information to aid in pagination." pageInfo: CollectionSegmentInfo! - totalCount: Int! + "A flattened list of the items." + items: [PluginInfo!] + totalCount: Int! @cost(weight: "10") } type Query { - categories(order: [CategorySortInput!], where: CategoryFilterInput): [Category!]! - entries(includeDefaults: Boolean, order: [EntrySortInput!], search: String, skip: Int, take: Int, where: EntryFilterInput): EntriesCollectionSegment + categories( + order: [CategorySortInput!] @cost(weight: "10") + where: CategoryFilterInput @cost(weight: "10") + ): [Category!]! @cost(weight: "10") + entries( + skip: Int + take: Int + search: String + includeDefaults: Boolean + order: [EntrySortInput!] @cost(weight: "10") + where: EntryFilterInput @cost(weight: "10") + ): EntriesCollectionSegment + @listSize( + assumedSize: 100 + slicingArguments: ["take"] + slicingArgumentDefaultValue: 10 + sizedFields: ["items"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") entriesV2( - "Returns the elements in the list that come after the specified cursor." - after: String, - "Returns the elements in the list that come before the specified cursor." - before: String, + search: String + includeDefaults: Boolean "Returns the first _n_ elements from the list." - first: Int, - includeDefaults: Boolean, + first: Int + "Returns the elements in the list that come after the specified cursor." + after: String "Returns the last _n_ elements from the list." - last: Int, - order: [EntrySortInput!], - search: String, - where: EntryFilterInput + last: Int + "Returns the elements in the list that come before the specified cursor." + before: String + order: [EntrySortInput!] @cost(weight: "10") + where: EntryFilterInput @cost(weight: "10") ): EntriesV2Connection - entry(id: Long!): Entry - pluginInfo(pluginGuid: UUID!): PluginInfo - pluginInfos(order: [PluginInfoSortInput!], skip: Int, take: Int, where: PluginInfoFilterInput): PluginInfosCollectionSegment - popularEntries(where: EntryFilterInput): [Entry!]! - release(id: Long!): Release - searchEntries(input: String!, order: [EntrySortInput!], type: EntryType, where: EntryFilterInput): [Entry!]! - searchKeyboardLayout(deviceProvider: UUID!, logicalLayout: String, model: String!, physicalLayout: KeyboardLayoutType!, vendor: String!): LayoutInfo - searchLayout(deviceProvider: UUID!, deviceType: RGBDeviceType!, model: String!, vendor: String!): LayoutInfo - submittedEntries(order: [EntrySortInput!], where: EntryFilterInput): [Entry!]! + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 10 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + entry(id: Long!): Entry @cost(weight: "10") + submittedEntries( + order: [EntrySortInput!] @cost(weight: "10") + where: EntryFilterInput @cost(weight: "10") + ): [Entry!]! @authorize @cost(weight: "10") + popularEntries(where: EntryFilterInput @cost(weight: "10")): [Entry!]! + @cost(weight: "10") + searchEntries( + input: String! + type: EntryType + order: [EntrySortInput!] @cost(weight: "10") + where: EntryFilterInput @cost(weight: "10") + ): [Entry!]! @cost(weight: "10") + searchLayout( + deviceProvider: UUID! + deviceType: RGBDeviceType! + vendor: String! + model: String! + ): LayoutInfo @cost(weight: "10") + searchKeyboardLayout( + deviceProvider: UUID! + vendor: String! + model: String! + physicalLayout: KeyboardLayoutType! + logicalLayout: String + ): LayoutInfo @cost(weight: "10") + pluginInfos( + skip: Int + take: Int + order: [PluginInfoSortInput!] @cost(weight: "10") + where: PluginInfoFilterInput @cost(weight: "10") + ): PluginInfosCollectionSegment + @listSize( + assumedSize: 100 + slicingArguments: ["take"] + slicingArgumentDefaultValue: 10 + sizedFields: ["items"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + pluginInfo(pluginGuid: UUID!): PluginInfo @cost(weight: "10") + release(id: Long!): Release @cost(weight: "10") } type Release { + id: Long! + version: String! changelog: String createdAt: DateTime! - dependencies: [Entry!]! - downloadSize: Long! downloads: Long! - entry: Entry! - entryId: Long! - id: Long! + downloadSize: Long! md5Hash: String minimumVersion: String - version: String! + entry: Entry! + entryId: Long! + dependencies: [Entry!]! } type Tag { @@ -217,392 +262,331 @@ type Tag { name: String! } -"Defines when a policy shall be executed." -enum ApplyPolicy { - "After the resolver was executed." - AFTER_RESOLVER - "Before the resolver was executed." - BEFORE_RESOLVER - "The policy is applied in the validation step before the execution." - VALIDATION -} - -enum EntryType { - LAYOUT - PLUGIN - PROFILE -} - -enum KeyboardLayoutType { - ABNT - ANSI - ISO - JIS - KS - UNKNOWN -} - -enum RGBDeviceType { - ALL - COOLER - DRAM - FAN - GAME_CONTROLLER - GRAPHICS_CARD - HEADSET - HEADSET_STAND - KEYBOARD - KEYPAD - LED_CONTROLLER - LED_MATRIX - LED_STRIPE - MAINBOARD - MONITOR - MOUSE - MOUSEPAD - NONE - SPEAKER - UNKNOWN -} - -enum SortEnumType { - ASC - DESC -} - -"The `DateTime` scalar represents an ISO-8601 compliant date time type." -scalar DateTime - -"The `Long` scalar type represents non-fractional signed whole 64-bit numeric values. Long can represent values between -(2^63) and 2^63 - 1." -scalar Long - -scalar UUID - input BooleanOperationFilterInput { - eq: Boolean - neq: Boolean + eq: Boolean @cost(weight: "10") + neq: Boolean @cost(weight: "10") } input CategoryFilterInput { and: [CategoryFilterInput!] - entryType: NullableOfEntryTypeOperationFilterInput - icon: StringOperationFilterInput + or: [CategoryFilterInput!] id: LongOperationFilterInput name: StringOperationFilterInput - or: [CategoryFilterInput!] + icon: StringOperationFilterInput + entryType: NullableOfEntryTypeOperationFilterInput } input CategorySortInput { - entryType: SortEnumType - icon: SortEnumType - id: SortEnumType - name: SortEnumType + id: SortEnumType @cost(weight: "10") + name: SortEnumType @cost(weight: "10") + icon: SortEnumType @cost(weight: "10") + entryType: SortEnumType @cost(weight: "10") } input CreateEntryInput { - categories: [Long!]! - defaultEntryInfo: DefaultEntryInfoInput - description: String! entryType: EntryType! name: String! summary: String! + description: String! + categories: [Long!]! tags: [String!]! + defaultEntryInfo: DefaultEntryInfoInput } input CreateLayoutInfoInput { + entryId: Long! deviceProvider: UUID! deviceType: RGBDeviceType! - entryId: Long! - logicalLayout: String + vendor: String! model: String! physicalLayout: KeyboardLayoutType - vendor: String! + logicalLayout: String } input DateTimeOperationFilterInput { - eq: DateTime - gt: DateTime - gte: DateTime - in: [DateTime] - lt: DateTime - lte: DateTime - neq: DateTime - ngt: DateTime - ngte: DateTime - nin: [DateTime] - nlt: DateTime - nlte: DateTime + eq: DateTime @cost(weight: "10") + neq: DateTime @cost(weight: "10") + in: [DateTime] @cost(weight: "10") + nin: [DateTime] @cost(weight: "10") + gt: DateTime @cost(weight: "10") + ngt: DateTime @cost(weight: "10") + gte: DateTime @cost(weight: "10") + ngte: DateTime @cost(weight: "10") + lt: DateTime @cost(weight: "10") + nlt: DateTime @cost(weight: "10") + lte: DateTime @cost(weight: "10") + nlte: DateTime @cost(weight: "10") } input DefaultEntryInfoFilterInput { and: [DefaultEntryInfoFilterInput!] - entryId: LongOperationFilterInput - isDeviceProvider: BooleanOperationFilterInput - isEssential: BooleanOperationFilterInput or: [DefaultEntryInfoFilterInput!] + entryId: LongOperationFilterInput + isEssential: BooleanOperationFilterInput + isDeviceProvider: BooleanOperationFilterInput } input DefaultEntryInfoInput { - isDeviceProvider: Boolean! isEssential: Boolean! + isDeviceProvider: Boolean! } input DefaultEntryInfoSortInput { - entryId: SortEnumType - isDeviceProvider: SortEnumType - isEssential: SortEnumType + entryId: SortEnumType @cost(weight: "10") + isEssential: SortEnumType @cost(weight: "10") + isDeviceProvider: SortEnumType @cost(weight: "10") } input EntryFilterInput { and: [EntryFilterInput!] - author: StringOperationFilterInput - authorId: UuidOperationFilterInput - categories: ListFilterInputTypeOfCategoryFilterInput + or: [EntryFilterInput!] + id: LongOperationFilterInput + entryType: EntryTypeOperationFilterInput createdAt: DateTimeOperationFilterInput - defaultEntryInfo: DefaultEntryInfoFilterInput - dependantReleases: ListFilterInputTypeOfReleaseFilterInput + authorId: UuidOperationFilterInput + author: StringOperationFilterInput + isOfficial: BooleanOperationFilterInput + name: StringOperationFilterInput + summary: StringOperationFilterInput description: StringOperationFilterInput downloads: LongOperationFilterInput - entryType: EntryTypeOperationFilterInput - icon: ImageFilterInput iconId: UuidOperationFilterInput - id: LongOperationFilterInput - images: ListFilterInputTypeOfImageFilterInput - isOfficial: BooleanOperationFilterInput - latestRelease: ReleaseFilterInput + icon: ImageFilterInput latestReleaseId: LongOperationFilterInput - layoutInfo: ListFilterInputTypeOfLayoutInfoFilterInput - name: StringOperationFilterInput - or: [EntryFilterInput!] + latestRelease: ReleaseFilterInput pluginInfo: PluginInfoFilterInput - releases: ListFilterInputTypeOfReleaseFilterInput - summary: StringOperationFilterInput + layoutInfo: ListFilterInputTypeOfLayoutInfoFilterInput + defaultEntryInfo: DefaultEntryInfoFilterInput + categories: ListFilterInputTypeOfCategoryFilterInput tags: ListFilterInputTypeOfTagFilterInput + images: ListFilterInputTypeOfImageFilterInput + releases: ListFilterInputTypeOfReleaseFilterInput + dependantReleases: ListFilterInputTypeOfReleaseFilterInput } input EntrySortInput { - author: SortEnumType - authorId: SortEnumType - createdAt: SortEnumType - defaultEntryInfo: DefaultEntryInfoSortInput - description: SortEnumType - downloads: SortEnumType - entryType: SortEnumType - icon: ImageSortInput - iconId: SortEnumType - id: SortEnumType - isOfficial: SortEnumType - latestRelease: ReleaseSortInput - latestReleaseId: SortEnumType - name: SortEnumType - pluginInfo: PluginInfoSortInput - summary: SortEnumType + id: SortEnumType @cost(weight: "10") + entryType: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + authorId: SortEnumType @cost(weight: "10") + author: SortEnumType @cost(weight: "10") + isOfficial: SortEnumType @cost(weight: "10") + name: SortEnumType @cost(weight: "10") + summary: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + downloads: SortEnumType @cost(weight: "10") + iconId: SortEnumType @cost(weight: "10") + icon: ImageSortInput @cost(weight: "10") + latestReleaseId: SortEnumType @cost(weight: "10") + latestRelease: ReleaseSortInput @cost(weight: "10") + pluginInfo: PluginInfoSortInput @cost(weight: "10") + defaultEntryInfo: DefaultEntryInfoSortInput @cost(weight: "10") } input EntryTypeOperationFilterInput { - eq: EntryType - in: [EntryType!] - neq: EntryType - nin: [EntryType!] + eq: EntryType @cost(weight: "10") + neq: EntryType @cost(weight: "10") + in: [EntryType!] @cost(weight: "10") + nin: [EntryType!] @cost(weight: "10") } input ImageFilterInput { and: [ImageFilterInput!] + or: [ImageFilterInput!] + id: UuidOperationFilterInput + name: StringOperationFilterInput description: StringOperationFilterInput + width: IntOperationFilterInput + height: IntOperationFilterInput + size: LongOperationFilterInput + mimeType: StringOperationFilterInput entry: EntryFilterInput entryId: LongOperationFilterInput - height: IntOperationFilterInput - id: UuidOperationFilterInput - mimeType: StringOperationFilterInput - name: StringOperationFilterInput - or: [ImageFilterInput!] - size: LongOperationFilterInput - width: IntOperationFilterInput } input ImageSortInput { - description: SortEnumType - entry: EntrySortInput - entryId: SortEnumType - height: SortEnumType - id: SortEnumType - mimeType: SortEnumType - name: SortEnumType - size: SortEnumType - width: SortEnumType + id: SortEnumType @cost(weight: "10") + name: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + width: SortEnumType @cost(weight: "10") + height: SortEnumType @cost(weight: "10") + size: SortEnumType @cost(weight: "10") + mimeType: SortEnumType @cost(weight: "10") + entry: EntrySortInput @cost(weight: "10") + entryId: SortEnumType @cost(weight: "10") } input IntOperationFilterInput { - eq: Int - gt: Int - gte: Int - in: [Int] - lt: Int - lte: Int - neq: Int - ngt: Int - ngte: Int - nin: [Int] - nlt: Int - nlte: Int + eq: Int @cost(weight: "10") + neq: Int @cost(weight: "10") + in: [Int] @cost(weight: "10") + nin: [Int] @cost(weight: "10") + gt: Int @cost(weight: "10") + ngt: Int @cost(weight: "10") + gte: Int @cost(weight: "10") + ngte: Int @cost(weight: "10") + lt: Int @cost(weight: "10") + nlt: Int @cost(weight: "10") + lte: Int @cost(weight: "10") + nlte: Int @cost(weight: "10") } input LayoutInfoFilterInput { and: [LayoutInfoFilterInput!] + or: [LayoutInfoFilterInput!] + id: LongOperationFilterInput deviceProvider: UuidOperationFilterInput deviceType: RGBDeviceTypeOperationFilterInput - entry: EntryFilterInput - entryId: LongOperationFilterInput - id: LongOperationFilterInput - logicalLayout: StringOperationFilterInput - model: StringOperationFilterInput - or: [LayoutInfoFilterInput!] - physicalLayout: NullableOfKeyboardLayoutTypeOperationFilterInput vendor: StringOperationFilterInput + model: StringOperationFilterInput + physicalLayout: NullableOfKeyboardLayoutTypeOperationFilterInput + logicalLayout: StringOperationFilterInput + entryId: LongOperationFilterInput + entry: EntryFilterInput } input LayoutInfoInput { deviceProvider: UUID! deviceType: RGBDeviceType! - logicalLayout: String + vendor: String! model: String! physicalLayout: KeyboardLayoutType - vendor: String! + logicalLayout: String } input ListFilterInputTypeOfCategoryFilterInput { - all: CategoryFilterInput - any: Boolean - none: CategoryFilterInput - some: CategoryFilterInput + all: CategoryFilterInput @cost(weight: "10") + none: CategoryFilterInput @cost(weight: "10") + some: CategoryFilterInput @cost(weight: "10") + any: Boolean @cost(weight: "10") } input ListFilterInputTypeOfEntryFilterInput { - all: EntryFilterInput - any: Boolean - none: EntryFilterInput - some: EntryFilterInput + all: EntryFilterInput @cost(weight: "10") + none: EntryFilterInput @cost(weight: "10") + some: EntryFilterInput @cost(weight: "10") + any: Boolean @cost(weight: "10") } input ListFilterInputTypeOfImageFilterInput { - all: ImageFilterInput - any: Boolean - none: ImageFilterInput - some: ImageFilterInput + all: ImageFilterInput @cost(weight: "10") + none: ImageFilterInput @cost(weight: "10") + some: ImageFilterInput @cost(weight: "10") + any: Boolean @cost(weight: "10") } input ListFilterInputTypeOfLayoutInfoFilterInput { - all: LayoutInfoFilterInput - any: Boolean - none: LayoutInfoFilterInput - some: LayoutInfoFilterInput + all: LayoutInfoFilterInput @cost(weight: "10") + none: LayoutInfoFilterInput @cost(weight: "10") + some: LayoutInfoFilterInput @cost(weight: "10") + any: Boolean @cost(weight: "10") } input ListFilterInputTypeOfReleaseFilterInput { - all: ReleaseFilterInput - any: Boolean - none: ReleaseFilterInput - some: ReleaseFilterInput + all: ReleaseFilterInput @cost(weight: "10") + none: ReleaseFilterInput @cost(weight: "10") + some: ReleaseFilterInput @cost(weight: "10") + any: Boolean @cost(weight: "10") } input ListFilterInputTypeOfTagFilterInput { - all: TagFilterInput - any: Boolean - none: TagFilterInput - some: TagFilterInput + all: TagFilterInput @cost(weight: "10") + none: TagFilterInput @cost(weight: "10") + some: TagFilterInput @cost(weight: "10") + any: Boolean @cost(weight: "10") } input LongOperationFilterInput { - eq: Long - gt: Long - gte: Long - in: [Long] - lt: Long - lte: Long - neq: Long - ngt: Long - ngte: Long - nin: [Long] - nlt: Long - nlte: Long + eq: Long @cost(weight: "10") + neq: Long @cost(weight: "10") + in: [Long] @cost(weight: "10") + nin: [Long] @cost(weight: "10") + gt: Long @cost(weight: "10") + ngt: Long @cost(weight: "10") + gte: Long @cost(weight: "10") + ngte: Long @cost(weight: "10") + lt: Long @cost(weight: "10") + nlt: Long @cost(weight: "10") + lte: Long @cost(weight: "10") + nlte: Long @cost(weight: "10") } input NullableOfEntryTypeOperationFilterInput { - eq: EntryType - in: [EntryType] - neq: EntryType - nin: [EntryType] + eq: EntryType @cost(weight: "10") + neq: EntryType @cost(weight: "10") + in: [EntryType] @cost(weight: "10") + nin: [EntryType] @cost(weight: "10") } input NullableOfKeyboardLayoutTypeOperationFilterInput { - eq: KeyboardLayoutType - in: [KeyboardLayoutType] - neq: KeyboardLayoutType - nin: [KeyboardLayoutType] + eq: KeyboardLayoutType @cost(weight: "10") + neq: KeyboardLayoutType @cost(weight: "10") + in: [KeyboardLayoutType] @cost(weight: "10") + nin: [KeyboardLayoutType] @cost(weight: "10") } input PluginInfoFilterInput { and: [PluginInfoFilterInput!] - entry: EntryFilterInput - entryId: LongOperationFilterInput - helpPage: StringOperationFilterInput or: [PluginInfoFilterInput!] + entryId: LongOperationFilterInput + entry: EntryFilterInput pluginGuid: UuidOperationFilterInput + website: StringOperationFilterInput + helpPage: StringOperationFilterInput repository: StringOperationFilterInput requiresAdmin: BooleanOperationFilterInput + supportsWindows: BooleanOperationFilterInput supportsLinux: BooleanOperationFilterInput supportsOSX: BooleanOperationFilterInput - supportsWindows: BooleanOperationFilterInput - website: StringOperationFilterInput } input PluginInfoSortInput { - entry: EntrySortInput - entryId: SortEnumType - helpPage: SortEnumType - pluginGuid: SortEnumType - repository: SortEnumType - requiresAdmin: SortEnumType - supportsLinux: SortEnumType - supportsOSX: SortEnumType - supportsWindows: SortEnumType - website: SortEnumType + entryId: SortEnumType @cost(weight: "10") + entry: EntrySortInput @cost(weight: "10") + pluginGuid: SortEnumType @cost(weight: "10") + website: SortEnumType @cost(weight: "10") + helpPage: SortEnumType @cost(weight: "10") + repository: SortEnumType @cost(weight: "10") + requiresAdmin: SortEnumType @cost(weight: "10") + supportsWindows: SortEnumType @cost(weight: "10") + supportsLinux: SortEnumType @cost(weight: "10") + supportsOSX: SortEnumType @cost(weight: "10") } input RGBDeviceTypeOperationFilterInput { - eq: RGBDeviceType - in: [RGBDeviceType!] - neq: RGBDeviceType - nin: [RGBDeviceType!] + eq: RGBDeviceType @cost(weight: "10") + neq: RGBDeviceType @cost(weight: "10") + in: [RGBDeviceType!] @cost(weight: "10") + nin: [RGBDeviceType!] @cost(weight: "10") } input ReleaseFilterInput { and: [ReleaseFilterInput!] + or: [ReleaseFilterInput!] + id: LongOperationFilterInput + version: StringOperationFilterInput changelog: StringOperationFilterInput createdAt: DateTimeOperationFilterInput - dependencies: ListFilterInputTypeOfEntryFilterInput - downloadSize: LongOperationFilterInput downloads: LongOperationFilterInput - entry: EntryFilterInput - entryId: LongOperationFilterInput - id: LongOperationFilterInput + downloadSize: LongOperationFilterInput md5Hash: StringOperationFilterInput minimumVersion: StringOperationFilterInput - or: [ReleaseFilterInput!] - version: StringOperationFilterInput + entry: EntryFilterInput + entryId: LongOperationFilterInput + dependencies: ListFilterInputTypeOfEntryFilterInput } input ReleaseSortInput { - changelog: SortEnumType - createdAt: SortEnumType - downloadSize: SortEnumType - downloads: SortEnumType - entry: EntrySortInput - entryId: SortEnumType - id: SortEnumType - md5Hash: SortEnumType - minimumVersion: SortEnumType - version: SortEnumType + id: SortEnumType @cost(weight: "10") + version: SortEnumType @cost(weight: "10") + changelog: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + downloads: SortEnumType @cost(weight: "10") + downloadSize: SortEnumType @cost(weight: "10") + md5Hash: SortEnumType @cost(weight: "10") + minimumVersion: SortEnumType @cost(weight: "10") + entry: EntrySortInput @cost(weight: "10") + entryId: SortEnumType @cost(weight: "10") } input SetLayoutInfoInput { @@ -612,58 +596,155 @@ input SetLayoutInfoInput { input StringOperationFilterInput { and: [StringOperationFilterInput!] - contains: String - endsWith: String - eq: String - in: [String] - ncontains: String - nendsWith: String - neq: String - nin: [String] - nstartsWith: String or: [StringOperationFilterInput!] - startsWith: String + eq: String @cost(weight: "10") + neq: String @cost(weight: "10") + contains: String @cost(weight: "20") + ncontains: String @cost(weight: "20") + in: [String] @cost(weight: "10") + nin: [String] @cost(weight: "10") + startsWith: String @cost(weight: "20") + nstartsWith: String @cost(weight: "20") + endsWith: String @cost(weight: "20") + nendsWith: String @cost(weight: "20") } input TagFilterInput { and: [TagFilterInput!] + or: [TagFilterInput!] id: LongOperationFilterInput name: StringOperationFilterInput - or: [TagFilterInput!] } input UpdateEntryImageInput { - description: String id: UUID! name: String! + description: String } input UpdateEntryInput { - categories: [Long!]! - defaultEntryInfo: DefaultEntryInfoInput - description: String! id: Long! name: String! summary: String! + description: String! + categories: [Long!]! tags: [String!]! + defaultEntryInfo: DefaultEntryInfoInput } input UpdateReleaseInput { - changelog: String id: Long! + changelog: String } input UuidOperationFilterInput { - eq: UUID - gt: UUID - gte: UUID - in: [UUID] - lt: UUID - lte: UUID - neq: UUID - ngt: UUID - ngte: UUID - nin: [UUID] - nlt: UUID - nlte: UUID + eq: UUID @cost(weight: "10") + neq: UUID @cost(weight: "10") + in: [UUID] @cost(weight: "10") + nin: [UUID] @cost(weight: "10") + gt: UUID @cost(weight: "10") + ngt: UUID @cost(weight: "10") + gte: UUID @cost(weight: "10") + ngte: UUID @cost(weight: "10") + lt: UUID @cost(weight: "10") + nlt: UUID @cost(weight: "10") + lte: UUID @cost(weight: "10") + nlte: UUID @cost(weight: "10") } + +"Defines when a policy shall be executed." +enum ApplyPolicy { + "Before the resolver was executed." + BEFORE_RESOLVER + "After the resolver was executed." + AFTER_RESOLVER + "The policy is applied in the validation step before the execution." + VALIDATION +} + +enum EntryType { + PLUGIN + PROFILE + LAYOUT +} + +enum KeyboardLayoutType { + UNKNOWN + ANSI + ISO + JIS + ABNT + KS +} + +enum RGBDeviceType { + NONE + KEYBOARD + MOUSE + HEADSET + MOUSEPAD + LED_STRIPE + LED_MATRIX + MAINBOARD + GRAPHICS_CARD + DRAM + HEADSET_STAND + KEYPAD + FAN + SPEAKER + COOLER + MONITOR + LED_CONTROLLER + GAME_CONTROLLER + UNKNOWN + ALL +} + +enum SortEnumType { + ASC + DESC +} + +"The authorize directive." +directive @authorize( + "The name of the authorization policy that determines access to the annotated resource." + policy: String + "Roles that are allowed to access the annotated resource." + roles: [String!] + "Defines when when the authorize directive shall be applied.By default the authorize directives are applied during the validation phase." + apply: ApplyPolicy! = BEFORE_RESOLVER +) repeatable on OBJECT | FIELD_DEFINITION + +"The purpose of the `cost` directive is to define a `weight` for GraphQL types, fields, and arguments. Static analysis can use these weights when calculating the overall cost of a query or response." +directive @cost( + "The `weight` argument defines what value to add to the overall cost for every appearance, or possible appearance, of a type, field, argument, etc." + weight: String! +) on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM | INPUT_FIELD_DEFINITION + +"The purpose of the `@listSize` directive is to either inform the static analysis about the size of returned lists (if that information is statically available), or to point the analysis to where to find that information." +directive @listSize( + "The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." + assumedSize: Int + "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." + slicingArguments: [String!] + "The `slicingArgumentDefaultValue` argument can be used to define a default value for a slicing argument, which is used if the argument is not present in a query." + slicingArgumentDefaultValue: Int + "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." + sizedFields: [String!] + "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." + requireOneSlicingArgument: Boolean! = true +) on FIELD_DEFINITION + +"The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions." +directive @specifiedBy( + "The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." + url: String! +) on SCALAR + +"The `DateTime` scalar represents an ISO-8601 compliant date time type." +scalar DateTime @specifiedBy(url: "https://www.graphql-scalars.com/date-time") + +"The `Long` scalar type represents non-fractional signed whole 64-bit numeric values. Long can represent values between -(2^63) and 2^63 - 1." +scalar Long + +scalar UUID @specifiedBy(url: "https://tools.ietf.org/html/rfc4122") diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 638947883..bcc2f30e5 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -59,8 +59,8 @@ - - + +