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" } }];