From 07713b41e110b004419874497d92685cb577784a Mon Sep 17 00:00:00 2001
From: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>
Date: Tue, 28 Oct 2025 15:48:37 +0000
Subject: [PATCH] Fix(gql)!: Treat UNAUTHORIZED_ACCESS_ERROR as an
`SpeckleGraphQLForbiddenException` (#411)
* Respect UNAUTHORIZED_ACCESS_ERROR
* Correct test setup for automate
* dumb dumb typo
---
src/Speckle.Sdk/Api/Exceptions.cs | 3 +-
.../Api/GraphQL/GraphQLErrorHandler.cs | 4 +-
.../Speckle.Automate.Sdk.Integration.csproj | 7 ++
.../SpeckleAutomate.cs | 6 +-
.../packages.lock.json | 68 +++++++++----------
.../ProjectResourceExceptionalTests.cs | 2 +-
.../Api/GraphQLErrorHandler.cs | 1 +
7 files changed, 52 insertions(+), 39 deletions(-)
diff --git a/src/Speckle.Sdk/Api/Exceptions.cs b/src/Speckle.Sdk/Api/Exceptions.cs
index 94833754..0e0de06c 100644
--- a/src/Speckle.Sdk/Api/Exceptions.cs
+++ b/src/Speckle.Sdk/Api/Exceptions.cs
@@ -23,9 +23,10 @@ public class SpeckleGraphQLException : SpeckleException
}
///
-/// Represents a "FORBIDDEN" or "UNAUTHORIZED" GraphQL error as an exception.
+/// Represents a "FORBIDDEN" or "UNAUTHORIZED" or "UNAUTHORIZED_ACCESS_ERROR" GraphQL error as an exception.
/// https://www.apollographql.com/docs/apollo-server/v2/data/errors/#unauthenticated
/// https://www.apollographql.com/docs/apollo-server/v2/data/errors/#forbidden
+/// https://github.com/specklesystems/speckle-server/blob/v2.23.18/packages/server/modules/shared/errors/index.ts#L34
///
public sealed class SpeckleGraphQLForbiddenException : SpeckleGraphQLException
{
diff --git a/src/Speckle.Sdk/Api/GraphQL/GraphQLErrorHandler.cs b/src/Speckle.Sdk/Api/GraphQL/GraphQLErrorHandler.cs
index 14e902e6..a2e4b54f 100644
--- a/src/Speckle.Sdk/Api/GraphQL/GraphQLErrorHandler.cs
+++ b/src/Speckle.Sdk/Api/GraphQL/GraphQLErrorHandler.cs
@@ -28,7 +28,9 @@ internal static class GraphQLErrorHandler
var ex = code switch
{
"GRAPHQL_PARSE_FAILED" or "GRAPHQL_VALIDATION_FAILED" => new SpeckleGraphQLInvalidQueryException(message),
- "FORBIDDEN" or "UNAUTHENTICATED" => new SpeckleGraphQLForbiddenException(message),
+ "FORBIDDEN" or "UNAUTHENTICATED" or "UNAUTHORIZED_ACCESS_ERROR" => new SpeckleGraphQLForbiddenException(
+ message
+ ),
"STREAM_NOT_FOUND" => new SpeckleGraphQLStreamNotFoundException(message),
"BAD_USER_INPUT" => new SpeckleGraphQLBadInputException(message),
"INTERNAL_SERVER_ERROR" => new SpeckleGraphQLInternalErrorException(message),
diff --git a/tests/Speckle.Automate.Sdk.Integration/Speckle.Automate.Sdk.Integration.csproj b/tests/Speckle.Automate.Sdk.Integration/Speckle.Automate.Sdk.Integration.csproj
index 4ce75cd2..4bdacd22 100644
--- a/tests/Speckle.Automate.Sdk.Integration/Speckle.Automate.Sdk.Integration.csproj
+++ b/tests/Speckle.Automate.Sdk.Integration/Speckle.Automate.Sdk.Integration.csproj
@@ -3,6 +3,13 @@
net8.0
true
+
+
+
+
+
+
+
diff --git a/tests/Speckle.Automate.Sdk.Integration/SpeckleAutomate.cs b/tests/Speckle.Automate.Sdk.Integration/SpeckleAutomate.cs
index c9b5336d..0ac3a1f9 100644
--- a/tests/Speckle.Automate.Sdk.Integration/SpeckleAutomate.cs
+++ b/tests/Speckle.Automate.Sdk.Integration/SpeckleAutomate.cs
@@ -25,7 +25,9 @@ public sealed class AutomationContextTest : IAsyncLifetime
public async Task InitializeAsync()
{
- var serviceProvider = TestServiceSetup.GetServiceProvider();
+ var serviceCollection = new ServiceCollection();
+ serviceCollection.AddAutomateSdk();
+ var serviceProvider = serviceCollection.BuildServiceProvider();
_account = await Fixtures.SeedUser().ConfigureAwait(false);
_client = serviceProvider.GetRequiredService().Create(_account);
_runner = serviceProvider.GetRequiredService();
@@ -42,7 +44,7 @@ public sealed class AutomationContextTest : IAsyncLifetime
private async Task AutomationRunData(Base testObject)
{
Project project = await _client.Project.Create(new("Automate function e2e test", null, ProjectVisibility.Public));
- const string BRANCH_NAME = "main";
+ const string BRANCH_NAME = "Trigger";
var model = await _client.Model.Create(new(BRANCH_NAME, null, project.id));
string modelId = model.id;
diff --git a/tests/Speckle.Automate.Sdk.Integration/packages.lock.json b/tests/Speckle.Automate.Sdk.Integration/packages.lock.json
index c5675743..78c3841b 100644
--- a/tests/Speckle.Automate.Sdk.Integration/packages.lock.json
+++ b/tests/Speckle.Automate.Sdk.Integration/packages.lock.json
@@ -2,6 +2,28 @@
"version": 2,
"dependencies": {
"net8.0": {
+ "altcover": {
+ "type": "Direct",
+ "requested": "[9.0.1, )",
+ "resolved": "9.0.1",
+ "contentHash": "aadciFNDT5bnylaYUkKal+s5hF7yU/lmZxImQWAlk1438iPqK1Uf79H5ylELpyLIU49HL5ql+tnWBihp3WVLCA=="
+ },
+ "AwesomeAssertions": {
+ "type": "Direct",
+ "requested": "[8.1.0, )",
+ "resolved": "8.1.0",
+ "contentHash": "IfNC4cpXPi9tclWvuNO9lfkuIxJsUTLTS1NXto55jDrAUQJYl0zLI9ByISrfkbBE2Xtg+IWaAXQ6jnUx3anDuw=="
+ },
+ "Microsoft.NET.Test.Sdk": {
+ "type": "Direct",
+ "requested": "[17.13.0, )",
+ "resolved": "17.13.0",
+ "contentHash": "W19wCPizaIC9Zh47w8wWI/yxuqR7/dtABwOrc8r2jX/8mUNxM2vw4fXDh+DJTeogxV+KzKwg5jNNGQVwf3LXyA==",
+ "dependencies": {
+ "Microsoft.CodeCoverage": "17.13.0",
+ "Microsoft.TestPlatform.TestHost": "17.13.0"
+ }
+ },
"Microsoft.SourceLink.GitHub": {
"type": "Direct",
"requested": "[8.0.0, )",
@@ -24,6 +46,18 @@
"resolved": "0.9.6",
"contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w=="
},
+ "xunit.assert": {
+ "type": "Direct",
+ "requested": "[2.9.3, )",
+ "resolved": "2.9.3",
+ "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA=="
+ },
+ "xunit.runner.visualstudio": {
+ "type": "Direct",
+ "requested": "[3.0.2, )",
+ "resolved": "3.0.2",
+ "contentHash": "oXbusR6iPq0xlqoikjdLvzh+wQDkMv9If58myz9MEzldS4nIcp442Btgs2sWbYWV+caEluMe2pQCZ0hUZgPiow=="
+ },
"Argon": {
"type": "Transitive",
"resolved": "0.28.0",
@@ -364,18 +398,6 @@
"xunit.runner.visualstudio": "[3.0.2, )"
}
},
- "altcover": {
- "type": "CentralTransitive",
- "requested": "[9.0.1, )",
- "resolved": "9.0.1",
- "contentHash": "aadciFNDT5bnylaYUkKal+s5hF7yU/lmZxImQWAlk1438iPqK1Uf79H5ylELpyLIU49HL5ql+tnWBihp3WVLCA=="
- },
- "AwesomeAssertions": {
- "type": "CentralTransitive",
- "requested": "[8.1.0, )",
- "resolved": "8.1.0",
- "contentHash": "IfNC4cpXPi9tclWvuNO9lfkuIxJsUTLTS1NXto55jDrAUQJYl0zLI9ByISrfkbBE2Xtg+IWaAXQ6jnUx3anDuw=="
- },
"GraphQL.Client": {
"type": "CentralTransitive",
"requested": "[6.0.0, )",
@@ -424,16 +446,6 @@
"Microsoft.Extensions.Options": "2.2.0"
}
},
- "Microsoft.NET.Test.Sdk": {
- "type": "CentralTransitive",
- "requested": "[17.13.0, )",
- "resolved": "17.13.0",
- "contentHash": "W19wCPizaIC9Zh47w8wWI/yxuqR7/dtABwOrc8r2jX/8mUNxM2vw4fXDh+DJTeogxV+KzKwg5jNNGQVwf3LXyA==",
- "dependencies": {
- "Microsoft.CodeCoverage": "17.13.0",
- "Microsoft.TestPlatform.TestHost": "17.13.0"
- }
- },
"Moq": {
"type": "CentralTransitive",
"requested": "[4.20.72, )",
@@ -515,18 +527,6 @@
"xunit.assert": "2.9.3",
"xunit.core": "[2.9.3]"
}
- },
- "xunit.assert": {
- "type": "CentralTransitive",
- "requested": "[2.9.3, )",
- "resolved": "2.9.3",
- "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA=="
- },
- "xunit.runner.visualstudio": {
- "type": "CentralTransitive",
- "requested": "[3.0.2, )",
- "resolved": "3.0.2",
- "contentHash": "oXbusR6iPq0xlqoikjdLvzh+wQDkMv9If58myz9MEzldS4nIcp442Btgs2sWbYWV+caEluMe2pQCZ0hUZgPiow=="
}
}
}
diff --git a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ProjectResourceExceptionalTests.cs b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ProjectResourceExceptionalTests.cs
index 7da9f67e..94034402 100644
--- a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ProjectResourceExceptionalTests.cs
+++ b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ProjectResourceExceptionalTests.cs
@@ -93,7 +93,7 @@ public class ProjectResourceExceptionalTests : IAsyncLifetime
new(_testProject.id, "My new name", ProjectVisibility.Public, "NonExistentWorkspace")
)
);
- ex.InnerExceptions.Single().Should().BeOfType();
+ ex.InnerExceptions.Single().Should().BeOfType();
}
[Theory]
diff --git a/tests/Speckle.Sdk.Tests.Unit/Api/GraphQLErrorHandler.cs b/tests/Speckle.Sdk.Tests.Unit/Api/GraphQLErrorHandler.cs
index 2aa0b3bc..2e83e5c2 100644
--- a/tests/Speckle.Sdk.Tests.Unit/Api/GraphQLErrorHandler.cs
+++ b/tests/Speckle.Sdk.Tests.Unit/Api/GraphQLErrorHandler.cs
@@ -11,6 +11,7 @@ public class GraphQLErrorHandlerTests
{
yield return [typeof(SpeckleGraphQLForbiddenException), new Map { { "code", "FORBIDDEN" } }];
yield return [typeof(SpeckleGraphQLForbiddenException), new Map { { "code", "UNAUTHENTICATED" } }];
+ yield return [typeof(SpeckleGraphQLForbiddenException), new Map { { "code", "UNAUTHORIZED_ACCESS_ERROR" } }];
yield return [typeof(SpeckleGraphQLInternalErrorException), new Map { { "code", "INTERNAL_SERVER_ERROR" } }];
yield return [typeof(SpeckleGraphQLStreamNotFoundException), new Map { { "code", "STREAM_NOT_FOUND" } }];
yield return [typeof(SpeckleGraphQLBadInputException), new Map { { "code", "BAD_USER_INPUT" } }];