From 21c5a2e163911aa76e87ac3545e74317f941dfd4 Mon Sep 17 00:00:00 2001
From: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>
Date: Tue, 2 Dec 2025 14:25:17 +0000
Subject: [PATCH] Fixes
---
src/Speckle.Sdk/Api/GraphQL/Client.cs | 4 +-
.../Enums/FileUploadConversionStatus.cs | 7 +-
.../Api/GraphQL/Enums/ModelIngestionStatus.cs | 14 +
.../ProjectCommentsUpdatedMessageType.cs | 3 +
.../ProjectFileImportUpdatedMessageType.cs | 3 +
...ProjectModelIngestionUpdatedMessageType.cs | 13 +
.../Enums/ProjectModelsUpdatedMessageType.cs | 3 +
.../ProjectPendingModelsUpdatedMessageType.cs | 3 +
.../Enums/ProjectUpdatedMessageType.cs | 3 +
.../ProjectVersionsUpdatedMessageType.cs | 3 +
.../Api/GraphQL/Enums/ProjectVisibility.cs | 4 +
.../Api/GraphQL/Enums/ResourceType.cs | 6 +-
.../Enums/UserProjectsUpdatedMessageType.cs | 3 +
.../Api/GraphQL/Inputs/IngestInputs.cs | 17 +-
.../Api/GraphQL/Models/ModelIngestion.cs | 3 +
.../Models/ModelIngestionStatusData.cs | 9 +
.../GraphQL/Models/SubscriptionMessages.cs | 7 +-
.../Resources/ModelIngestionResource.cs | 264 +++++++++++++-----
.../GraphQL/Resources/SubscriptionResource.cs | 37 ++-
...s.CancelNonExistentIngestion.verified.json | 8 +
...eIngestionNonExistentProject.verified.json | 8 +
...UpdateNonExistentNonExistent.verified.json | 8 +
.../ModelIngestionResourceExceptionalTests.cs | 73 +++++
.../Resources/ModelIngestionResourceTests.cs | 31 +-
.../Resources/SubscriptionResourceTests.cs | 20 ++
.../Speckle.Sdk.Tests.Integration/Fixtures.cs | 2 +-
26 files changed, 460 insertions(+), 96 deletions(-)
create mode 100644 src/Speckle.Sdk/Api/GraphQL/Enums/ModelIngestionStatus.cs
create mode 100644 src/Speckle.Sdk/Api/GraphQL/Enums/ProjectModelIngestionUpdatedMessageType.cs
create mode 100644 src/Speckle.Sdk/Api/GraphQL/Models/ModelIngestionStatusData.cs
create mode 100644 tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ModelIngestionResourceExceptionalTests.CancelNonExistentIngestion.verified.json
create mode 100644 tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ModelIngestionResourceExceptionalTests.CreateIngestionNonExistentProject.verified.json
create mode 100644 tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ModelIngestionResourceExceptionalTests.UpdateNonExistentNonExistent.verified.json
create mode 100644 tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ModelIngestionResourceExceptionalTests.cs
diff --git a/src/Speckle.Sdk/Api/GraphQL/Client.cs b/src/Speckle.Sdk/Api/GraphQL/Client.cs
index b503a162..abe96585 100644
--- a/src/Speckle.Sdk/Api/GraphQL/Client.cs
+++ b/src/Speckle.Sdk/Api/GraphQL/Client.cs
@@ -35,7 +35,7 @@ public sealed class Client : ISpeckleGraphQLClient, IClient
public WorkspaceResource Workspace { get; }
public ServerResource Server { get; }
public FileImportResource FileImport { get; }
- public IngestResource Ingest { get; }
+ public ModelIngestionResource Ingestion { get; }
public Uri ServerUrl => new(Account.serverInfo.url);
@@ -72,7 +72,7 @@ public sealed class Client : ISpeckleGraphQLClient, IClient
Workspace = new(this);
Server = new(this);
FileImport = new(this, blobApiFactory.Create(account));
- Ingest = new(this);
+ Ingestion = new(this);
}
[AutoInterfaceIgnore]
diff --git a/src/Speckle.Sdk/Api/GraphQL/Enums/FileUploadConversionStatus.cs b/src/Speckle.Sdk/Api/GraphQL/Enums/FileUploadConversionStatus.cs
index 327f947e..21f0e49d 100644
--- a/src/Speckle.Sdk/Api/GraphQL/Enums/FileUploadConversionStatus.cs
+++ b/src/Speckle.Sdk/Api/GraphQL/Enums/FileUploadConversionStatus.cs
@@ -1,6 +1,9 @@
-namespace Speckle.Sdk.Api.GraphQL.Enums;
+// ReSharper disable InconsistentNaming
+namespace Speckle.Sdk.Api.GraphQL.Enums;
-//This enum isn't explicitly defined in the schema, instead its usages are int typed (But represent an enum)
+///
+/// This enum isn't explicitly defined in the schema, instead its usages are int typed (But represent an enum)
+///
public enum FileUploadConversionStatus
{
Queued = 0,
diff --git a/src/Speckle.Sdk/Api/GraphQL/Enums/ModelIngestionStatus.cs b/src/Speckle.Sdk/Api/GraphQL/Enums/ModelIngestionStatus.cs
new file mode 100644
index 00000000..85ca92dc
--- /dev/null
+++ b/src/Speckle.Sdk/Api/GraphQL/Enums/ModelIngestionStatus.cs
@@ -0,0 +1,14 @@
+// ReSharper disable InconsistentNaming
+namespace Speckle.Sdk.Api.GraphQL.Enums;
+
+///
+/// string based enum
+///
+public enum ModelIngestionStatus
+{
+ cancelled,
+ failed,
+ processing,
+ queued,
+ success,
+}
diff --git a/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectCommentsUpdatedMessageType.cs b/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectCommentsUpdatedMessageType.cs
index 2500425d..8bd5937c 100644
--- a/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectCommentsUpdatedMessageType.cs
+++ b/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectCommentsUpdatedMessageType.cs
@@ -1,5 +1,8 @@
namespace Speckle.Sdk.Api.GraphQL.Enums;
+///
+/// string based enum
+///
public enum ProjectCommentsUpdatedMessageType
{
ARCHIVED,
diff --git a/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectFileImportUpdatedMessageType.cs b/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectFileImportUpdatedMessageType.cs
index b1947272..9c53eebd 100644
--- a/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectFileImportUpdatedMessageType.cs
+++ b/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectFileImportUpdatedMessageType.cs
@@ -1,5 +1,8 @@
namespace Speckle.Sdk.Api.GraphQL.Enums;
+///
+/// string based enum
+///
public enum ProjectFileImportUpdatedMessageType
{
CREATED,
diff --git a/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectModelIngestionUpdatedMessageType.cs b/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectModelIngestionUpdatedMessageType.cs
new file mode 100644
index 00000000..2ec41934
--- /dev/null
+++ b/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectModelIngestionUpdatedMessageType.cs
@@ -0,0 +1,13 @@
+// ReSharper disable InconsistentNaming
+namespace Speckle.Sdk.Api.GraphQL.Enums;
+
+///
+/// string based enum
+///
+public enum ProjectModelIngestionUpdatedMessageType
+{
+ cancellationRequested,
+ created,
+ deleted,
+ updated,
+}
diff --git a/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectModelsUpdatedMessageType.cs b/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectModelsUpdatedMessageType.cs
index b6316fb0..11dc5440 100644
--- a/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectModelsUpdatedMessageType.cs
+++ b/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectModelsUpdatedMessageType.cs
@@ -1,5 +1,8 @@
namespace Speckle.Sdk.Api.GraphQL.Enums;
+///
+/// string based enum
+///
public enum ProjectModelsUpdatedMessageType
{
CREATED,
diff --git a/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectPendingModelsUpdatedMessageType.cs b/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectPendingModelsUpdatedMessageType.cs
index 8e24a9c8..d2735ab9 100644
--- a/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectPendingModelsUpdatedMessageType.cs
+++ b/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectPendingModelsUpdatedMessageType.cs
@@ -1,5 +1,8 @@
namespace Speckle.Sdk.Api.GraphQL.Enums;
+///
+/// string based enum
+///
public enum ProjectPendingModelsUpdatedMessageType
{
CREATED,
diff --git a/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectUpdatedMessageType.cs b/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectUpdatedMessageType.cs
index a056708c..7e51e635 100644
--- a/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectUpdatedMessageType.cs
+++ b/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectUpdatedMessageType.cs
@@ -1,5 +1,8 @@
namespace Speckle.Sdk.Api.GraphQL.Enums;
+///
+/// string based enum
+///
public enum ProjectUpdatedMessageType
{
DELETED,
diff --git a/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectVersionsUpdatedMessageType.cs b/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectVersionsUpdatedMessageType.cs
index 977c93f8..2f83fbd4 100644
--- a/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectVersionsUpdatedMessageType.cs
+++ b/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectVersionsUpdatedMessageType.cs
@@ -1,5 +1,8 @@
namespace Speckle.Sdk.Api.GraphQL.Enums;
+///
+/// string based enum
+///
public enum ProjectVersionsUpdatedMessageType
{
CREATED,
diff --git a/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectVisibility.cs b/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectVisibility.cs
index b0692127..c019dd38 100644
--- a/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectVisibility.cs
+++ b/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectVisibility.cs
@@ -1,5 +1,9 @@
+// ReSharper disable InconsistentNaming
namespace Speckle.Sdk.Api.GraphQL.Enums;
+///
+/// string based enum
+///
public enum ProjectVisibility
{
Private,
diff --git a/src/Speckle.Sdk/Api/GraphQL/Enums/ResourceType.cs b/src/Speckle.Sdk/Api/GraphQL/Enums/ResourceType.cs
index d592e308..ecb8ba29 100644
--- a/src/Speckle.Sdk/Api/GraphQL/Enums/ResourceType.cs
+++ b/src/Speckle.Sdk/Api/GraphQL/Enums/ResourceType.cs
@@ -1,5 +1,9 @@
-namespace Speckle.Sdk.Api.GraphQL.Enums;
+// ReSharper disable InconsistentNaming
+namespace Speckle.Sdk.Api.GraphQL.Enums;
+///
+/// string based enum
+///
public enum ResourceType
{
commit,
diff --git a/src/Speckle.Sdk/Api/GraphQL/Enums/UserProjectsUpdatedMessageType.cs b/src/Speckle.Sdk/Api/GraphQL/Enums/UserProjectsUpdatedMessageType.cs
index 1ad08ce3..4891c110 100644
--- a/src/Speckle.Sdk/Api/GraphQL/Enums/UserProjectsUpdatedMessageType.cs
+++ b/src/Speckle.Sdk/Api/GraphQL/Enums/UserProjectsUpdatedMessageType.cs
@@ -1,5 +1,8 @@
namespace Speckle.Sdk.Api.GraphQL.Enums;
+///
+/// string based enum
+///
public enum UserProjectsUpdatedMessageType
{
ADDED,
diff --git a/src/Speckle.Sdk/Api/GraphQL/Inputs/IngestInputs.cs b/src/Speckle.Sdk/Api/GraphQL/Inputs/IngestInputs.cs
index 82eaf03a..8fd9db80 100644
--- a/src/Speckle.Sdk/Api/GraphQL/Inputs/IngestInputs.cs
+++ b/src/Speckle.Sdk/Api/GraphQL/Inputs/IngestInputs.cs
@@ -1,4 +1,6 @@
-namespace Speckle.Sdk.Api.GraphQL.Inputs;
+using Speckle.Sdk.Api.GraphQL.Enums;
+
+namespace Speckle.Sdk.Api.GraphQL.Inputs;
public record SourceDataInput(
string sourceApplicationSlug,
@@ -32,3 +34,16 @@ public record ModelIngestionFailedInput(
}
public record ModelIngestionCancelledInput(string ingestionId, string projectId, string cancellationMessage);
+
+public record ProjectModelIngestionSubscriptionInput(
+ string projectId,
+ ModelIngestionReference ingestionReference,
+ ProjectModelIngestionUpdatedMessageType messageType
+);
+
+///
+/// @oneOf i.e. server expects either or , but not both.
+///
+///
+///
+public record ModelIngestionReference(string? ingestionId, string? modelId);
diff --git a/src/Speckle.Sdk/Api/GraphQL/Models/ModelIngestion.cs b/src/Speckle.Sdk/Api/GraphQL/Models/ModelIngestion.cs
index e10d91be..58d0dc67 100644
--- a/src/Speckle.Sdk/Api/GraphQL/Models/ModelIngestion.cs
+++ b/src/Speckle.Sdk/Api/GraphQL/Models/ModelIngestion.cs
@@ -5,5 +5,8 @@ public sealed class ModelIngestion
public required string id { get; init; }
public required DateTime createdAt { get; init; }
public required DateTime updatedAt { get; init; }
+ public required string modelId { get; init; }
+ public required bool cancellationRequested { get; init; }
+ public required ModelIngestionStatusData statusData { get; init; }
// public required LimitedUser user { get; init; }
}
diff --git a/src/Speckle.Sdk/Api/GraphQL/Models/ModelIngestionStatusData.cs b/src/Speckle.Sdk/Api/GraphQL/Models/ModelIngestionStatusData.cs
new file mode 100644
index 00000000..a195bdfc
--- /dev/null
+++ b/src/Speckle.Sdk/Api/GraphQL/Models/ModelIngestionStatusData.cs
@@ -0,0 +1,9 @@
+using Speckle.Sdk.Api.GraphQL.Enums;
+
+namespace Speckle.Sdk.Api.GraphQL.Models;
+
+public sealed class ModelIngestionStatusData
+{
+ public required ModelIngestionStatus status { get; init; }
+ public required string? progressMessage { get; init; }
+}
diff --git a/src/Speckle.Sdk/Api/GraphQL/Models/SubscriptionMessages.cs b/src/Speckle.Sdk/Api/GraphQL/Models/SubscriptionMessages.cs
index 9f085c30..0f4ebb07 100644
--- a/src/Speckle.Sdk/Api/GraphQL/Models/SubscriptionMessages.cs
+++ b/src/Speckle.Sdk/Api/GraphQL/Models/SubscriptionMessages.cs
@@ -83,8 +83,11 @@ public sealed class ProjectVersionsUpdatedMessage : EventArgs
public Version? version { get; init; }
}
-public sealed class ProjectModelIngestionCancellationRequestedMessage : EventArgs
+public sealed class ProjectModelIngestionUpdatedMessage : EventArgs
{
[JsonRequired]
- public required ModelIngestion? modelIngestion { get; init; }
+ public required ModelIngestion modelIngestion { get; init; }
+
+ [JsonRequired]
+ public required ProjectModelIngestionUpdatedMessageType type { get; init; }
}
diff --git a/src/Speckle.Sdk/Api/GraphQL/Resources/ModelIngestionResource.cs b/src/Speckle.Sdk/Api/GraphQL/Resources/ModelIngestionResource.cs
index 2053a7d3..ffcabb91 100644
--- a/src/Speckle.Sdk/Api/GraphQL/Resources/ModelIngestionResource.cs
+++ b/src/Speckle.Sdk/Api/GraphQL/Resources/ModelIngestionResource.cs
@@ -5,11 +5,11 @@ using Speckle.Sdk.Api.GraphQL.Models.Responses;
namespace Speckle.Sdk.Api.GraphQL.Resources;
-public sealed class Ingestionresource
+public sealed class ModelIngestionResource
{
private readonly ISpeckleGraphQLClient _client;
- internal Ingestionresource(ISpeckleGraphQLClient client)
+ internal ModelIngestionResource(ISpeckleGraphQLClient client)
{
_client = client;
}
@@ -32,6 +32,16 @@ public sealed class Ingestionresource
id
createdAt
updatedAt
+ modelId
+ cancellationRequested
+ statusData {
+ ... on HasModelIngestionStatus {
+ status
+ }
+ ... on HasProgressMessage {
+ progressMessage
+ }
+ }
}
}
}
@@ -70,39 +80,14 @@ public sealed class Ingestionresource
id
createdAt
updatedAt
- }
- }
- }
- }
- """;
-
- GraphQLRequest request = new() { Query = QUERY, Variables = new { input } };
-
- var res = await _client
- .ExecuteGraphQLRequest>>>(
- request,
- cancellationToken
- )
- .ConfigureAwait(false);
-
- return res.data.data.data;
- }
-
- ///
- ///
- /// The version id
- ///
- public async Task Complete(ModelIngestionSuccessInput input, CancellationToken cancellationToken = default)
- {
- //language=graphql
- const string QUERY = """
- mutation IngestionComplete($input: ModelIngestionSuccessInput!) {
- data: projectMutations {
- data: modelIngestionMutations {
- data: completeWithVersion(input: $input) {
- data:statusData {
- ... on ModelIngestionSuccessStatus {
- versionId
+ modelId
+ cancellationRequested
+ statusData {
+ ... on HasModelIngestionStatus {
+ status
+ }
+ ... on HasProgressMessage {
+ progressMessage
}
}
}
@@ -113,42 +98,6 @@ public sealed class Ingestionresource
GraphQLRequest request = new() { Query = QUERY, Variables = new { input } };
- var res = await _client
- .ExecuteGraphQLRequest>>>>(
- request,
- cancellationToken
- )
- .ConfigureAwait(false);
-
- return res.data.data.data.data;
- }
-
- ///
- ///
- ///
- ///
- public async Task FailWithError(
- ModelIngestionFailedInput input,
- CancellationToken cancellationToken = default
- )
- {
- //language=graphql
- const string QUERY = """
- mutation IngestionFailWithError($input: ModelIngestionFailedInput!) {
- data: projectMutations {
- data: modelIngestionMutations {
- data: failWithError(input: $input) {
- id
- createdAt
- updatedAt
- }
- }
- }
- }
- """;
-
- GraphQLRequest request = new() { Query = QUERY, Variables = new { input } };
-
var res = await _client
.ExecuteGraphQLRequest>>>(
request,
@@ -160,14 +109,107 @@ public sealed class Ingestionresource
}
///
- /// Request that the ingestion is canceled.
+ /// Request that the server completes the ingestion by creating a version
+ /// If successful, the job will be in a terminal "successful" state.
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// The version id
+ ///
+ public async Task Complete(ModelIngestionSuccessInput input, CancellationToken cancellationToken = default)
+ {
+ //language=graphql
+ const string QUERY = """
+ mutation IngestionComplete($input: ModelIngestionSuccessInput!) {
+ data: projectMutations {
+ data: modelIngestionMutations {
+ data: completeWithVersion(input: $input) {
+ data:statusData {
+ ... on ModelIngestionSuccessStatus {
+ data:versionId
+ }
+ }
+ }
+ }
+ }
+ }
+ """;
+
+ GraphQLRequest request = new() { Query = QUERY, Variables = new { input } };
+
+ var res = await _client
+ .ExecuteGraphQLRequest<
+ RequiredResponse>>>>
+ >(request, cancellationToken)
+ .ConfigureAwait(false);
+
+ return res.data.data.data.data.data;
+ }
+
+ ///
+ /// Fail the job with an error.
///
///
- /// Note it's up to the client to observe this cancellation request
- /// via
- /// and report it as canceled via `ingestion.fail_with_cancelled`.
- ///
- /// See "cooperative cancellation pattern"
+ /// For requested user cancellation, use instead
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task FailWithError(
+ ModelIngestionFailedInput input,
+ CancellationToken cancellationToken = default
+ )
+ {
+ //language=graphql
+ const string QUERY = """
+ mutation IngestionFailWithError($input: ModelIngestionFailedInput!) {
+ data: projectMutations {
+ data: modelIngestionMutations {
+ data: failWithError(input: $input) {
+ id
+ createdAt
+ updatedAt
+ modelId
+ cancellationRequested
+ statusData {
+ ... on HasModelIngestionStatus {
+ status
+ }
+ ... on HasProgressMessage {
+ progressMessage
+ }
+ }
+ }
+ }
+ }
+ }
+ """;
+
+ GraphQLRequest request = new() { Query = QUERY, Variables = new { input } };
+
+ var res = await _client
+ .ExecuteGraphQLRequest>>>(
+ request,
+ cancellationToken
+ )
+ .ConfigureAwait(false);
+
+ return res.data.data.data;
+ }
+
+ ///
+ /// Fail the ingestion with a canceled status.
+ /// This should only be done if the user has explicitly requested cancellation
+ /// Other forms of cancellation use .
+ /// The ingestion should then enter a terminal "canceled" state
+ ///
+ ///
+ ///
///
///
///
@@ -186,6 +228,74 @@ public sealed class Ingestionresource
id
createdAt
updatedAt
+ modelId
+ cancellationRequested
+ statusData {
+ ... on HasModelIngestionStatus {
+ status
+ }
+ ... on HasProgressMessage {
+ progressMessage
+ }
+ }
+ }
+ }
+ }
+ }
+ """;
+
+ GraphQLRequest request = new() { Query = QUERY, Variables = new { input } };
+
+ var res = await _client
+ .ExecuteGraphQLRequest>>>(
+ request,
+ cancellationToken
+ )
+ .ConfigureAwait(false);
+
+ return res.data.data.data;
+ }
+
+ ///
+ /// Request that the is canceled.
+ ///
+ ///
+ /// Note simply calling this mutation does not imediatly cancel, it doesn't even guarantee it will be canceled at all.
+ /// It's up to the client to observe this cancellation request
+ /// via
+ /// and report it as canceled via
+ /// See "cooperative cancellation pattern"
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task RequestCancellation(
+ ModelIngestionCancelledInput input,
+ CancellationToken cancellationToken = default
+ )
+ {
+ //language=graphql
+ const string QUERY = """
+ mutation IngestionRequestCancellation($input: ModelIngestionRequestCancellationInput!) {
+ data: projectMutations {
+ data: modelIngestionMutations {
+ data: requestCancellation (input: $input) {
+ id
+ createdAt
+ updatedAt
+ modelId
+ cancellationRequested
+ statusData {
+ ... on HasModelIngestionStatus {
+ status
+ }
+ ... on HasProgressMessage {
+ progressMessage
+ }
+ }
}
}
}
diff --git a/src/Speckle.Sdk/Api/GraphQL/Resources/SubscriptionResource.cs b/src/Speckle.Sdk/Api/GraphQL/Resources/SubscriptionResource.cs
index ca9ce2ac..4b38a449 100644
--- a/src/Speckle.Sdk/Api/GraphQL/Resources/SubscriptionResource.cs
+++ b/src/Speckle.Sdk/Api/GraphQL/Resources/SubscriptionResource.cs
@@ -1,4 +1,5 @@
using GraphQL;
+using Speckle.Sdk.Api.GraphQL.Enums;
using Speckle.Sdk.Api.GraphQL.Inputs;
using Speckle.Sdk.Api.GraphQL.Models;
using Speckle.Sdk.Api.GraphQL.Models.Responses;
@@ -215,31 +216,49 @@ public sealed class SubscriptionResource : IDisposable
/// Subscribe to a cancellation request being made for a Model Ingestion
///
///
- public Subscription CreateProjectModelIngestionCancellationRequestedSubscription(
- string ingestionId,
- string projectId
+ public Subscription CreateProjectModelIngestionUpdatedSubscription(
+ ProjectModelIngestionSubscriptionInput input
)
{
//language=graphql
const string QUERY = """
- subscription IngestionCancellationRequested($projectId: ID!, $ingestionId: ID!){
- data:projectModelIngestionCancellationRequested(projectId: $projectId, ingestionId: $ingestionId) {
- modelIngestion
- {
+ subscription IngestionUpdated(
+ $input: ProjectModelIngestionSubscriptionInput!
+ ) {
+ data: projectModelIngestionUpdated(input: $input) {
+ modelIngestion {
id
createdAt
updatedAt
}
+ type
}
}
""";
- GraphQLRequest request = new() { Query = QUERY, Variables = new { projectId, ingestionId } };
+ GraphQLRequest request = new() { Query = QUERY, Variables = new { input } };
- Subscription subscription = new(_client, request);
+ Subscription subscription = new(_client, request);
_subscriptions.Add(subscription);
return subscription;
}
+ /// Subscribe to a cancellation request being made for a Model Ingestion
+ ///
+ ///
+ public Subscription CreateProjectModelIngestionCancellationRequestedSubscription(
+ string ingestionId,
+ string projectId
+ )
+ {
+ return CreateProjectModelIngestionUpdatedSubscription(
+ new ProjectModelIngestionSubscriptionInput(
+ projectId,
+ new(ingestionId, null),
+ ProjectModelIngestionUpdatedMessageType.cancellationRequested
+ )
+ );
+ }
+
public void Dispose()
{
foreach (var subscription in _subscriptions)
diff --git a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ModelIngestionResourceExceptionalTests.CancelNonExistentIngestion.verified.json b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ModelIngestionResourceExceptionalTests.CancelNonExistentIngestion.verified.json
new file mode 100644
index 00000000..c6fca10c
--- /dev/null
+++ b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ModelIngestionResourceExceptionalTests.CancelNonExistentIngestion.verified.json
@@ -0,0 +1,8 @@
+{
+ "Type": "AggregateException",
+ "InnerException": {
+ "Data": {},
+ "Message": "NOT_FOUND_ERROR: Model ingestion was not found",
+ "Type": "SpeckleGraphQLException"
+ }
+}
diff --git a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ModelIngestionResourceExceptionalTests.CreateIngestionNonExistentProject.verified.json b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ModelIngestionResourceExceptionalTests.CreateIngestionNonExistentProject.verified.json
new file mode 100644
index 00000000..04a2199a
--- /dev/null
+++ b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ModelIngestionResourceExceptionalTests.CreateIngestionNonExistentProject.verified.json
@@ -0,0 +1,8 @@
+{
+ "Type": "AggregateException",
+ "InnerException": {
+ "Data": {},
+ "Message": "STREAM_NOT_FOUND: Project not found",
+ "Type": "SpeckleGraphQLStreamNotFoundException"
+ }
+}
diff --git a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ModelIngestionResourceExceptionalTests.UpdateNonExistentNonExistent.verified.json b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ModelIngestionResourceExceptionalTests.UpdateNonExistentNonExistent.verified.json
new file mode 100644
index 00000000..c6fca10c
--- /dev/null
+++ b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ModelIngestionResourceExceptionalTests.UpdateNonExistentNonExistent.verified.json
@@ -0,0 +1,8 @@
+{
+ "Type": "AggregateException",
+ "InnerException": {
+ "Data": {},
+ "Message": "NOT_FOUND_ERROR: Model ingestion was not found",
+ "Type": "SpeckleGraphQLException"
+ }
+}
diff --git a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ModelIngestionResourceExceptionalTests.cs b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ModelIngestionResourceExceptionalTests.cs
new file mode 100644
index 00000000..a548d523
--- /dev/null
+++ b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ModelIngestionResourceExceptionalTests.cs
@@ -0,0 +1,73 @@
+using System.Reflection;
+using Speckle.Sdk.Api;
+using Speckle.Sdk.Api.GraphQL.Inputs;
+using Speckle.Sdk.Api.GraphQL.Models;
+using Speckle.Sdk.Api.GraphQL.Resources;
+using Speckle.Sdk.Host;
+using Speckle.Sdk.Models;
+
+namespace Speckle.Sdk.Tests.Integration.API.GraphQL.Resources;
+
+public sealed class ModelIngestionResourceExceptionalTests : IAsyncLifetime
+{
+ private IClient _testUser;
+ private ModelIngestionResource Sut => _testUser.Ingestion;
+ private Project _project;
+ private Model _model;
+
+ public Task DisposeAsync() => Task.CompletedTask;
+
+ public async Task InitializeAsync()
+ {
+ TypeLoader.Reset();
+ TypeLoader.Initialize(typeof(Base).Assembly, Assembly.GetExecutingAssembly());
+
+ _testUser = await Fixtures.SeedUserWithClient();
+ _project = await _testUser.Project.Create(new("Test project", "", null));
+ _model = await _testUser.Model.Create(new("Test Model 1", "", _project.id));
+ }
+
+ [Fact]
+ public async Task CreateIngestionNonExistentProject()
+ {
+ var createInput = new ModelIngestionCreateInput(
+ _model.id,
+ "Doesn't exist...",
+ "Starting processing",
+ new(".NET test runner", "0.0.0", null, null)
+ );
+
+ var ex = await Assert.ThrowsAsync(async () =>
+ {
+ _ = await Sut.Create(createInput);
+ });
+ await Verify(ex);
+ }
+
+ [Fact]
+ public async Task UpdateNonExistentNonExistent()
+ {
+ var updateInput = new ModelIngestionUpdateInput("Doesn't exist", _project.id, "Can't be", 0.5);
+
+ var ex = await Assert.ThrowsAsync(async () =>
+ {
+ _ = await Sut.UpdateProgress(updateInput);
+ });
+ await Verify(ex);
+ }
+
+ [Fact]
+ public async Task CancelNonExistentIngestion()
+ {
+ var input = new ModelIngestionCancelledInput(
+ "Non-existent-ingestion",
+ _project.id,
+ cancellationMessage: "This was cancelled for testing purposes"
+ );
+ var ex = await Assert.ThrowsAsync(async () =>
+ {
+ _ = await Sut.FailWithCancel(input);
+ });
+ await Verify(ex);
+ }
+}
diff --git a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ModelIngestionResourceTests.cs b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ModelIngestionResourceTests.cs
index 6dfb2959..bbd88c6e 100644
--- a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ModelIngestionResourceTests.cs
+++ b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ModelIngestionResourceTests.cs
@@ -1,6 +1,7 @@
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Speckle.Sdk.Api;
+using Speckle.Sdk.Api.GraphQL.Enums;
using Speckle.Sdk.Api.GraphQL.Inputs;
using Speckle.Sdk.Api.GraphQL.Models;
using Speckle.Sdk.Api.GraphQL.Resources;
@@ -49,6 +50,34 @@ public sealed class ModelIngestionResourceTests : IAsyncLifetime
Assert.Equal(ingest.id, res.id);
}
+ [Fact]
+ public async Task CreateAndUpdate()
+ {
+ var createInput = new ModelIngestionCreateInput(
+ _model.id,
+ _project.id,
+ "Starting processing",
+ new(".NET test runner", "0.0.0", null, null)
+ );
+ ModelIngestion ingest = await Sut.Create(createInput);
+
+ await Update(null, "None");
+ await Update(0.1, "0.1");
+ await Update(0.5, "Whoa-oh! We're half way there!");
+ await Update(1, "Finished");
+ await Update(0.2, "Back to processing again");
+
+ async Task Update(double? progress, string message)
+ {
+ var updateInput = new ModelIngestionUpdateInput(ingest.id, _project.id, message, progress);
+ var res = await Sut.UpdateProgress(updateInput);
+
+ Assert.Equal(message, res.statusData.progressMessage);
+ Assert.False(res.cancellationRequested);
+ Assert.Equal(ModelIngestionStatus.processing, res.statusData.status);
+ }
+ }
+
[Fact]
public async Task CreateAndCancel()
{
@@ -103,7 +132,7 @@ public sealed class ModelIngestionResourceTests : IAsyncLifetime
ModelIngestionSuccessInput finish = new(ingest.id, _project.id, sendResult.RootId);
string versionId = await Sut.Complete(finish);
Version version = await _testUser.Version.Get(versionId, _project.id);
- Assert.Equal(versionId, version.id);
+ Assert.Equal(version.id, versionId);
Assert.Equal(sendResult.RootId, version.referencedObject);
}
}
diff --git a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/SubscriptionResourceTests.cs b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/SubscriptionResourceTests.cs
index 7611658e..da015ec4 100644
--- a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/SubscriptionResourceTests.cs
+++ b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/SubscriptionResourceTests.cs
@@ -135,4 +135,24 @@ public class SubscriptionResourceTests : IAsyncLifetime
subscriptionMessage.type.Should().Be(ProjectCommentsUpdatedMessageType.CREATED);
subscriptionMessage.comment.Should().NotBeNull();
}
+
+ [Fact(Timeout = TIMEOUT)]
+ public async Task ProjectModelIngestionCancellationRequested_SubscriptionIsCalled()
+ {
+ ModelIngestion ingestion = await _testUser.Ingestion.Create(
+ new(_testModel.id, _testProject.id, "", new(".NET test", "0.0.0", null, null))
+ );
+ TaskCompletionSource tcs = new();
+ using var sub = Sut.CreateProjectModelIngestionCancellationRequestedSubscription(_testProject.id, ingestion.id);
+ sub.Listeners += (_, message) => tcs.SetResult(message);
+
+ await Task.Delay(WAIT_PERIOD); // Give time to subscription to be setup
+
+ await _testUser.Ingestion.RequestCancellation(new(ingestion.id, _testProject.id, "please cancel"));
+
+ var subscriptionMessage = await tcs.Task;
+
+ subscriptionMessage.Should().NotBeNull();
+ subscriptionMessage.modelIngestion.id.Should().Be(ingestion.id);
+ }
}
diff --git a/tests/Speckle.Sdk.Tests.Integration/Fixtures.cs b/tests/Speckle.Sdk.Tests.Integration/Fixtures.cs
index 3fcfd670..cb678129 100644
--- a/tests/Speckle.Sdk.Tests.Integration/Fixtures.cs
+++ b/tests/Speckle.Sdk.Tests.Integration/Fixtures.cs
@@ -21,7 +21,7 @@ namespace Speckle.Sdk.Tests.Integration;
public static class Fixtures
{
- public static readonly ServerInfo Server = new() { url = "http://localhost:3000", name = "Docker Server" };
+ public static readonly ServerInfo Server = new() { url = "http://localhost", name = "Docker Server" };
public static IServiceProvider ServiceProvider { get; set; }