diff --git a/src/Artemis.UI/Artemis.UI.csproj b/src/Artemis.UI/Artemis.UI.csproj
index bf4ce5d0c..987233c0e 100644
--- a/src/Artemis.UI/Artemis.UI.csproj
+++ b/src/Artemis.UI/Artemis.UI.csproj
@@ -11,6 +11,7 @@
+
diff --git a/src/Artemis.UI/ArtemisBootstrapper.cs b/src/Artemis.UI/ArtemisBootstrapper.cs
index 1056070e1..c1dd988a9 100644
--- a/src/Artemis.UI/ArtemisBootstrapper.cs
+++ b/src/Artemis.UI/ArtemisBootstrapper.cs
@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Reactive;
-using System.Threading.Tasks;
using Artemis.Core;
using Artemis.Core.DryIoc;
using Artemis.UI.DryIoc;
@@ -12,7 +11,6 @@ using Artemis.UI.Shared.DataModelPicker;
using Artemis.UI.Shared.DryIoc;
using Artemis.UI.Shared.Services;
using Artemis.VisualScripting.DryIoc;
-using Artemis.WebClient.Updating;
using Artemis.WebClient.Updating.DryIoc;
using Avalonia;
using Avalonia.Controls;
diff --git a/src/Artemis.WebClient.Updating/.config/dotnet-tools.json b/src/Artemis.WebClient.Updating/.config/dotnet-tools.json
new file mode 100644
index 000000000..7d8626c03
--- /dev/null
+++ b/src/Artemis.WebClient.Updating/.config/dotnet-tools.json
@@ -0,0 +1,12 @@
+{
+ "version": 1,
+ "isRoot": true,
+ "tools": {
+ "strawberryshake.tools": {
+ "version": "13.0.0-rc.4",
+ "commands": [
+ "dotnet-graphql"
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.WebClient.Updating/.graphqlconfig b/src/Artemis.WebClient.Updating/.graphqlconfig
new file mode 100644
index 000000000..727ec86a0
--- /dev/null
+++ b/src/Artemis.WebClient.Updating/.graphqlconfig
@@ -0,0 +1,15 @@
+{
+ "name": "Untitled GraphQL Schema",
+ "schemaPath": "schema.graphql",
+ "extensions": {
+ "endpoints": {
+ "Default GraphQL Endpoint": {
+ "url": "https://updating.artemis-rgb.com/graphql",
+ "headers": {
+ "user-agent": "JS GraphQL"
+ },
+ "introspect": true
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.WebClient.Updating/.graphqlrc.json b/src/Artemis.WebClient.Updating/.graphqlrc.json
new file mode 100644
index 000000000..d500e1cee
--- /dev/null
+++ b/src/Artemis.WebClient.Updating/.graphqlrc.json
@@ -0,0 +1,22 @@
+{
+ "schema": "schema.graphql",
+ "documents": "**/*.graphql",
+ "extensions": {
+ "strawberryShake": {
+ "name": "UpdatingClient",
+ "namespace": "Artemis.WebClient.Updating",
+ "url": "https://updating.artemis-rgb.com/graphql/",
+ "emitGeneratedCode": false,
+ "records": {
+ "inputs": false,
+ "entities": false
+ },
+ "transportProfiles": [
+ {
+ "default": "Http",
+ "subscription": "WebSocket"
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.WebClient.Updating/Artemis.WebClient.Updating.csproj b/src/Artemis.WebClient.Updating/Artemis.WebClient.Updating.csproj
new file mode 100644
index 000000000..7f2bb002f
--- /dev/null
+++ b/src/Artemis.WebClient.Updating/Artemis.WebClient.Updating.csproj
@@ -0,0 +1,16 @@
+
+
+
+ net6.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Artemis.WebClient.Updating/DryIoc/ContainerExtensions.cs b/src/Artemis.WebClient.Updating/DryIoc/ContainerExtensions.cs
new file mode 100644
index 000000000..e52e454d3
--- /dev/null
+++ b/src/Artemis.WebClient.Updating/DryIoc/ContainerExtensions.cs
@@ -0,0 +1,26 @@
+using DryIoc;
+using DryIoc.Microsoft.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Artemis.WebClient.Updating.DryIoc;
+
+///
+/// Provides an extension method to register services onto a DryIoc .
+///
+public static class ContainerExtensions
+{
+ ///
+ /// Registers the updating client into the container.
+ ///
+ /// The builder building the current container
+ public static void RegisterUpdatingClient(this IContainer container)
+ {
+ ServiceCollection serviceCollection = new();
+ serviceCollection
+ .AddHttpClient()
+ .AddUpdatingClient()
+ .ConfigureHttpClient(client => client.BaseAddress = new Uri("https://updating.artemis-rgb.com/graphql"));
+
+ container.WithDependencyInjectionAdapter(serviceCollection);
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.WebClient.Updating/Queries/GetReleaseById.graphql b/src/Artemis.WebClient.Updating/Queries/GetReleaseById.graphql
new file mode 100644
index 000000000..fa17ed4f0
--- /dev/null
+++ b/src/Artemis.WebClient.Updating/Queries/GetReleaseById.graphql
@@ -0,0 +1,16 @@
+query GetReleaseById {
+ release(id: "63b71dd69a5bb32a0a81a410") {
+ branch
+ commit
+ version
+ artifacts {
+ platform
+ artifactId
+ fileInfo {
+ md5Hash
+ downloadSize
+ downloads
+ }
+ }
+ }
+}
diff --git a/src/Artemis.WebClient.Updating/schema.extensions.graphql b/src/Artemis.WebClient.Updating/schema.extensions.graphql
new file mode 100644
index 000000000..0b5fbd98b
--- /dev/null
+++ b/src/Artemis.WebClient.Updating/schema.extensions.graphql
@@ -0,0 +1,13 @@
+scalar _KeyFieldSet
+
+directive @key(fields: _KeyFieldSet!) on SCHEMA | OBJECT
+
+directive @serializationType(name: String!) on SCALAR
+
+directive @runtimeType(name: String!) on SCALAR
+
+directive @enumValue(value: String!) on ENUM_VALUE
+
+directive @rename(name: String!) on INPUT_FIELD_DEFINITION | INPUT_OBJECT | ENUM | ENUM_VALUE
+
+extend schema @key(fields: "id")
\ No newline at end of file
diff --git a/src/Artemis.WebClient.Updating/schema.graphql b/src/Artemis.WebClient.Updating/schema.graphql
new file mode 100644
index 000000000..8a4840e8e
--- /dev/null
+++ b/src/Artemis.WebClient.Updating/schema.graphql
@@ -0,0 +1,262 @@
+# This file was generated based on ".graphqlconfig". Do not edit manually.
+
+schema {
+ query: Query
+ mutation: Mutation
+}
+
+"The `@defer` directive may be provided for fragment spreads and inline fragments to inform the executor to delay the execution of the current fragment to indicate deprioritization of the current fragment. A query with `@defer` directive will cause the request to potentially return multiple responses, where non-deferred data is delivered in the initial response and data deferred is delivered in a subsequent response. `@include` and `@skip` take precedence over `@defer`."
+directive @defer(
+ "Deferred when true."
+ if: Boolean,
+ "If this argument label has a value other than null, it will be passed on to the result of this defer directive. This label is intended to give client applications a way to identify to which fragment a deferred result belongs to."
+ label: String
+) on FRAGMENT_SPREAD | INLINE_FRAGMENT
+
+"The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such as asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take precedence over `@stream`."
+directive @stream(
+ "Streamed when true."
+ if: Boolean,
+ "The initial elements that shall be send down to the consumer."
+ initialCount: Int! = 0,
+ "If this argument label has a value other than null, it will be passed on to the result of this stream directive. This label is intended to give client applications a way to identify to which fragment a streamed result belongs to."
+ label: String
+) on FIELD
+
+directive @authorize(
+ "Defines when when the resolver shall be executed.By default the resolver is executed after the policy has determined that the current user is allowed to access the field."
+ apply: ApplyPolicy! = BEFORE_RESOLVER,
+ "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!]
+) on SCHEMA | OBJECT | FIELD_DEFINITION
+
+type ArtemisChannel {
+ branch: String!
+ releases: Int!
+}
+
+type Artifact {
+ artifactId: Long!
+ deltaFileInfo: ArtifactFileInfo!
+ fileInfo: ArtifactFileInfo!
+ fileName(deltaFile: Boolean!): String!
+ platform: Platform!
+}
+
+type ArtifactFileInfo {
+ downloadSize: Long!
+ downloads: Long!
+ md5Hash: String
+}
+
+type Mutation {
+ updateReleaseChangelog(input: UpdateReleaseChangelogInput!): UpdateReleaseChangelogPayload!
+}
+
+type Query {
+ channelByBranch(branch: String!): ArtemisChannel
+ channels: [ArtemisChannel!]!
+ nextRelease(branch: String!, platform: Platform!, version: String!): Release
+ release(id: String!): Release
+ releaseStatistics(order: [ReleaseStatisticSortInput!], where: ReleaseStatisticFilterInput): [ReleaseStatistic!]!
+ releases(order: [ReleaseSortInput!], where: ReleaseFilterInput): [Release!]!
+}
+
+type Release {
+ artifacts: [Artifact!]!
+ branch: String!
+ changelog: String!
+ commit: String!
+ createdAt: DateTime!
+ id: String!
+ isDraft: Boolean!
+ previousRelease: String
+ version: String!
+ workflowRunId: Long!
+}
+
+type ReleaseStatistic {
+ count: Int!
+ lastReportedUsage: DateTime!
+ linuxCount: Int!
+ oSXCount: Int!
+ releaseId: String!
+ windowsCount: Int!
+}
+
+type UpdateReleaseChangelogPayload {
+ release: Release
+}
+
+enum ApplyPolicy {
+ AFTER_RESOLVER
+ BEFORE_RESOLVER
+}
+
+enum Platform {
+ LINUX
+ OSX
+ WINDOWS
+}
+
+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
+
+input ArtifactFileInfoFilterInput {
+ and: [ArtifactFileInfoFilterInput!]
+ downloadSize: ComparableInt64OperationFilterInput
+ downloads: ComparableInt64OperationFilterInput
+ md5Hash: StringOperationFilterInput
+ or: [ArtifactFileInfoFilterInput!]
+}
+
+input ArtifactFilterInput {
+ and: [ArtifactFilterInput!]
+ artifactId: ComparableInt64OperationFilterInput
+ deltaFileInfo: ArtifactFileInfoFilterInput
+ fileInfo: ArtifactFileInfoFilterInput
+ or: [ArtifactFilterInput!]
+ platform: PlatformOperationFilterInput
+}
+
+input BooleanOperationFilterInput {
+ eq: Boolean
+ neq: Boolean
+}
+
+input ComparableDateTimeOffsetOperationFilterInput {
+ 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 ComparableInt32OperationFilterInput {
+ 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 ComparableInt64OperationFilterInput {
+ 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 ListFilterInputTypeOfArtifactFilterInput {
+ all: ArtifactFilterInput
+ any: Boolean
+ none: ArtifactFilterInput
+ some: ArtifactFilterInput
+}
+
+input PlatformOperationFilterInput {
+ eq: Platform
+ in: [Platform!]
+ neq: Platform
+ nin: [Platform!]
+}
+
+input ReleaseFilterInput {
+ and: [ReleaseFilterInput!]
+ artifacts: ListFilterInputTypeOfArtifactFilterInput
+ branch: StringOperationFilterInput
+ changelog: StringOperationFilterInput
+ commit: StringOperationFilterInput
+ createdAt: ComparableDateTimeOffsetOperationFilterInput
+ id: StringOperationFilterInput
+ isDraft: BooleanOperationFilterInput
+ or: [ReleaseFilterInput!]
+ previousRelease: StringOperationFilterInput
+ version: StringOperationFilterInput
+ workflowRunId: ComparableInt64OperationFilterInput
+}
+
+input ReleaseSortInput {
+ branch: SortEnumType
+ changelog: SortEnumType
+ commit: SortEnumType
+ createdAt: SortEnumType
+ id: SortEnumType
+ isDraft: SortEnumType
+ previousRelease: SortEnumType
+ version: SortEnumType
+ workflowRunId: SortEnumType
+}
+
+input ReleaseStatisticFilterInput {
+ and: [ReleaseStatisticFilterInput!]
+ count: ComparableInt32OperationFilterInput
+ lastReportedUsage: ComparableDateTimeOffsetOperationFilterInput
+ linuxCount: ComparableInt32OperationFilterInput
+ oSXCount: ComparableInt32OperationFilterInput
+ or: [ReleaseStatisticFilterInput!]
+ releaseId: StringOperationFilterInput
+ windowsCount: ComparableInt32OperationFilterInput
+}
+
+input ReleaseStatisticSortInput {
+ count: SortEnumType
+ lastReportedUsage: SortEnumType
+ linuxCount: SortEnumType
+ oSXCount: SortEnumType
+ releaseId: SortEnumType
+ windowsCount: SortEnumType
+}
+
+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 UpdateReleaseChangelogInput {
+ changelog: String!
+ id: String!
+ isDraft: Boolean!
+}
diff --git a/src/Artemis.sln b/src/Artemis.sln
index dec26a200..2e53b5d9a 100644
--- a/src/Artemis.sln
+++ b/src/Artemis.sln
@@ -19,6 +19,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Artemis.UI.MacOS", "Artemis
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Artemis.VisualScripting", "Artemis.VisualScripting\Artemis.VisualScripting.csproj", "{412B921A-26F5-4AE6-8B32-0C19BE54F421}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Artemis.WebClient.Updating", "Artemis.WebClient.Updating\Artemis.WebClient.Updating.csproj", "{7C8C6F50-0CC8-45B3-B608-A7218C005E4B}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -57,6 +59,10 @@ Global
{412B921A-26F5-4AE6-8B32-0C19BE54F421}.Debug|x64.Build.0 = Debug|x64
{412B921A-26F5-4AE6-8B32-0C19BE54F421}.Release|x64.ActiveCfg = Release|x64
{412B921A-26F5-4AE6-8B32-0C19BE54F421}.Release|x64.Build.0 = Release|x64
+ {7C8C6F50-0CC8-45B3-B608-A7218C005E4B}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {7C8C6F50-0CC8-45B3-B608-A7218C005E4B}.Debug|x64.Build.0 = Debug|Any CPU
+ {7C8C6F50-0CC8-45B3-B608-A7218C005E4B}.Release|x64.ActiveCfg = Release|Any CPU
+ {7C8C6F50-0CC8-45B3-B608-A7218C005E4B}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE