From 200b84f49a4b5a1cb7321073ff283949453315f5 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Tue, 9 Jul 2024 13:56:03 +0100 Subject: [PATCH] Main to dev (#18) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add Instances base (#6) * Use Uri for checks in GetAccounts function (#8) * Add integration and perf tests to sln (#9) * Remove perf tests (#10) * remove perf tests * do all unit tests * Code coverage (#11) * code coverage * enable codecov for GA * Update README.md * Update coverage and dependencies (#12) * Update coverage and dependencies * fmt * add codecov config * merge DUI3/Alpha into sdk (#13) * merge DUI3/Alpha into sdk * formatting * Merge Objects dui3/alpha -> dev (#14) * merge DUI3/Alpha into sdk * formatting * Objects changes * Objects tests * Unit test project * update codecov to be less intrusive (#15) * update codecov to be less intrusive * fix codecov yaml * add coverage exclusion * Merge sharp `dui3/alpha` -> sdk `main` (#16) * Merge * csharpier format * Fixed polysharp issues * Integration Tests * Fixes * Some nullability fixes (#17) * add coverage exclusion * fix some tests and fix nullability errors --------- Co-authored-by: Oğuzhan Koral <45078678+oguzhankoral@users.noreply.github.com> Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com> --- .github/workflows/ci.yml | 8 +- .github/workflows/main.yml | 6 + .gitignore | 2 +- Directory.Packages.props | 10 +- README.md | 4 +- Speckle.Sdk.sln | 14 + build/Program.cs | 11 +- codecov.yml | 13 + src/Speckle.Core/Api/Exceptions.cs | 97 ++-- .../Client.ObsoleteOperations.cs | 250 --------- .../Client.UserOperations.cs | 99 ---- src/Speckle.Core/Api/GraphQL/Client.cs | 243 +++++---- .../Enums/FileUploadConversionStatus.cs | 10 + .../ProjectCommentsUpdatedMessageType.cs | 8 + .../ProjectFileImportUpdatedMessageType.cs | 7 + .../Enums/ProjectModelsUpdatedMessageType.cs | 8 + .../ProjectPendingModelsUpdatedMessageType.cs | 7 + .../Enums/ProjectUpdatedMessageType.cs | 7 + .../ProjectVersionsUpdatedMessageType.cs | 8 + .../Api/GraphQL/Enums/ProjectVisibility.cs | 8 + .../Api/GraphQL/Enums/ResourceType.cs | 9 + .../Enums/UserProjectsUpdatedMessageType.cs | 7 + .../GraphQL/GraphQLHttpClientExtensions.cs | 4 +- .../Api/GraphQL/ISpeckleGraphQLClient.cs | 20 + .../Api/GraphQL/Inputs/CommentInputs.cs | 17 + .../Api/GraphQL/Inputs/ModelInputs.cs | 11 + .../Api/GraphQL/Inputs/ProjectInputs.cs | 39 ++ .../Api/GraphQL/Inputs/SubscriptionInputs.cs | 7 + .../Api/GraphQL/Inputs/VersionInputs.cs | 9 + .../Client.ActivityOperations.cs | 3 +- .../Client.BranchOperations.cs | 20 + .../Client.CommentOperations.cs | 6 + .../Client.CommitOperations.cs | 14 + .../Client.ObjectOperations.cs | 1 + .../Client.ServerOperations.cs | 1 + .../Client.StreamOperations.cs | 42 +- .../Client.UserOperations.cs | 54 ++ .../Client.Subscriptions.Branch.cs | 0 .../Client.Subscriptions.Commit.cs | 0 .../Client.Subscriptions.Stream.cs | 0 .../LegacyGraphQLModels.cs} | 279 ++-------- .../Api/GraphQL/Legacy/Manager.cs | 98 ++++ .../{ => Legacy}/SubscriptionModels.cs | 12 + .../Api/GraphQL/Models/Collections.cs | 19 + .../Api/GraphQL/Models/Comment.cs | 25 + .../Api/GraphQL/Models/FileUpload.cs | 30 ++ src/Speckle.Core/Api/GraphQL/Models/Model.cs | 22 + .../Api/GraphQL/Models/ModelsTreeItem.cs | 17 + .../Models/PendingStreamCollaborator.cs | 25 + .../Api/GraphQL/Models/Project.cs | 30 ++ .../Api/GraphQL/Models/ProjectCollaborator.cs | 9 + .../Api/GraphQL/Models/ResourceIdentifier.cs | 10 + .../Models/Responses/MutationResponses.cs | 42 ++ .../Api/GraphQL/Models/Responses/Responses.cs | 30 ++ .../Api/GraphQL/Models/ServerInfo.cs | 45 ++ .../GraphQL/Models/SubscriptionMessages.cs | 84 +++ src/Speckle.Core/Api/GraphQL/Models/User.cs | 52 ++ .../Api/GraphQL/Models/Version.cs | 18 + .../Api/GraphQL/Models/ViewerResourceGroup.cs | 11 + .../Api/GraphQL/Models/ViewerResourceItem.cs | 10 + .../GraphQL/Resources/ActiveUserResource.cs | 165 ++++++ .../Api/GraphQL/Resources/CommentResource.cs | 279 ++++++++++ .../Api/GraphQL/Resources/ModelResource.cs | 309 +++++++++++ .../GraphQL/Resources/OtherUserResource.cs | 108 ++++ .../Resources/ProjectInviteResource.cs | 260 +++++++++ .../Api/GraphQL/Resources/ProjectResource.cs | 349 ++++++++++++ .../GraphQL/Resources/SubscriptionResource.cs | 216 ++++++++ .../Api/GraphQL/Resources/VersionResource.cs | 252 +++++++++ .../Api/GraphQL/Resources/graphql.config.yml | 2 + src/Speckle.Core/Api/GraphQL/StreamRoles.cs | 12 + src/Speckle.Core/Api/Helpers.cs | 2 + src/Speckle.Core/Api/ServerLimits.cs | 3 + src/Speckle.Core/Credentials/Account.cs | 2 +- .../Credentials/AccountManager.cs | 41 +- src/Speckle.Core/Credentials/Responses.cs | 51 +- src/Speckle.Core/Credentials/StreamWrapper.cs | 2 +- src/Speckle.Core/Helpers/Http.cs | 5 +- src/Speckle.Core/Logging/SpeckleLog.cs | 3 +- src/Speckle.Core/Models/Base.cs | 9 +- .../Models/GraphTraversal/RuleBuilder.cs | 3 +- .../Models/Instances/IInstanceComponent.cs | 12 + .../Instances/InstanceDefinitionProxy.cs | 14 + .../Models/Instances/InstanceProxy.cs | 26 + .../Serialisation/BaseObjectDeserializerV2.cs | 4 +- .../Serialisation/BaseObjectSerializerV2.cs | 25 +- .../SerializationUtilities/ValueConverter.cs | 27 +- src/Speckle.Core/Speckle.Core.csproj | 4 +- src/Speckle.Core/packages.lock.json | 53 +- .../BuiltElements/AdvanceSteel/AsteelBeam.cs | 4 +- .../BuiltElements/Archicad/ArchicadOpening.cs | 83 +++ .../BuiltElements/Archicad/DirectShape.cs | 3 + .../BuiltElements/Archicad/ElementShape.cs | 16 +- src/Speckle.Objects/BuiltElements/Baseline.cs | 88 +++ src/Speckle.Objects/BuiltElements/Beam.cs | 19 +- src/Speckle.Objects/BuiltElements/Brace.cs | 15 +- .../Civil/CivilAppliedAssembly.cs | 26 + .../Civil/CivilAppliedSubassembly.cs | 37 ++ .../BuiltElements/Civil/CivilBaseline.cs | 54 ++ .../Civil/CivilBaselineRegion.cs | 45 ++ .../Civil/CivilCalculatedLink.cs | 20 + .../Civil/CivilCalculatedPoint.cs | 35 ++ .../Civil/CivilCalculatedShape.cs | 26 + src/Speckle.Objects/BuiltElements/Column.cs | 21 +- src/Speckle.Objects/BuiltElements/Duct.cs | 72 +-- .../BuiltElements/Revit/RevitBeam.cs | 25 +- .../BuiltElements/Revit/RevitBrace.cs | 28 +- .../BuiltElements/Revit/RevitColumn.cs | 115 ++-- .../BuiltElements/Revit/RevitDuct.cs | 193 ++++--- .../BuiltElements/Revit/RevitWall.cs | 146 ++--- src/Speckle.Objects/BuiltElements/Wall.cs | 41 +- src/Speckle.Objects/Geometry/Point.cs | 2 +- src/Speckle.Objects/Interfaces.cs | 21 +- src/Speckle.Objects/ObjectsKit.cs | 9 +- .../Other/Civil/CivilDataField.cs | 16 +- src/Speckle.Objects/Other/DataField.cs | 6 +- src/Speckle.Objects/Other/Transform.cs | 2 +- .../Structural/Geometry/Element1D.cs | 157 +++--- src/Speckle.Objects/packages.lock.json | 59 +-- .../packages.lock.json | 59 +-- .../packages.lock.json | 59 +-- .../Speckle.Core.Serialization.Tests.csproj | 3 +- .../packages.lock.json | 71 ++- .../GraphQL/Legacy/LegacyAPITests.cs} | 13 +- .../GraphQL/Legacy}/Subscriptions/Branches.cs | 2 +- .../GraphQL/Legacy}/Subscriptions/Commits.cs | 2 +- .../GraphQL/Legacy}/Subscriptions/Streams.cs | 2 +- .../Resources/ActiveUserResourceTests.cs | 52 ++ .../GraphQL/Resources/CommentResourceTests.cs | 97 ++++ .../ModelResourceExceptionalTests.cs | 88 +++ .../GraphQL/Resources/ModelResourceTests.cs | 96 ++++ .../Resources/OtherUserResourceTests.cs | 50 ++ .../ProjectInviteResourceExceptionalTests.cs | 32 ++ .../Resources/ProjectInviteResourceTests.cs | 107 ++++ .../ProjectResourceExceptionalTests.cs | 113 ++++ .../GraphQL/Resources/ProjectResourceTests.cs | 72 +++ .../Resources/SubscriptionResourceTests.cs | 120 +++++ .../GraphQL/Resources/VersionResourceTests.cs | 117 ++++ .../Credentials/UserServerInfoTests.cs | 10 +- .../Fixtures.cs | 60 ++- .../GraphQLCLient.cs | 2 +- .../ServerTransportTests.cs | 20 +- .../Speckle.Core.Tests.Integration.csproj | 38 +- .../packages.lock.json | 500 ++++++++++++++++++ .../Api/Operations/ReceiveFromSQLite.cs | 44 -- .../Api/Operations/TraverseCommit.cs | 38 -- .../Speckle.Core.Tests.Performance/Program.cs | 11 - .../RegressionTestConfig.cs | 61 --- .../DeserializationWorkerThreads.cs | 43 -- .../Speckle.Core.Tests.Performance.csproj | 23 - .../TestDataHelper.cs | 54 -- .../Api/GraphQLClient.cs | 15 +- .../AccountServerMigrationTests.cs | 13 +- .../Credentials/Accounts.cs | 2 +- .../SerializerNonBreakingChanges.cs | 48 +- .../Speckle.Core.Tests.Unit.csproj | 5 +- .../Transports/DiskTransportTests.cs | 3 +- .../Transports/MemoryTransportTests.cs | 3 +- .../packages.lock.json | 67 ++- .../Geometry/TransformTests.cs | 2 +- .../ModelPropertySupportedTypes.cs | 81 +++ .../Speckle.Objects.Tests.Unit.csproj | 2 +- .../packages.lock.json | 67 ++- 162 files changed, 6151 insertions(+), 1790 deletions(-) create mode 100644 codecov.yml delete mode 100644 src/Speckle.Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ObsoleteOperations.cs delete mode 100644 src/Speckle.Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.UserOperations.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Enums/FileUploadConversionStatus.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Enums/ProjectCommentsUpdatedMessageType.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Enums/ProjectFileImportUpdatedMessageType.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Enums/ProjectModelsUpdatedMessageType.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Enums/ProjectPendingModelsUpdatedMessageType.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Enums/ProjectUpdatedMessageType.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Enums/ProjectVersionsUpdatedMessageType.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Enums/ProjectVisibility.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Enums/ResourceType.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Enums/UserProjectsUpdatedMessageType.cs create mode 100644 src/Speckle.Core/Api/GraphQL/ISpeckleGraphQLClient.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Inputs/CommentInputs.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Inputs/ModelInputs.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Inputs/ProjectInputs.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Inputs/SubscriptionInputs.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Inputs/VersionInputs.cs rename src/Speckle.Core/Api/GraphQL/{ => Legacy}/Client.GraphqlCleintOperations/Client.ActivityOperations.cs (96%) rename src/Speckle.Core/Api/GraphQL/{ => Legacy}/Client.GraphqlCleintOperations/Client.BranchOperations.cs (86%) rename src/Speckle.Core/Api/GraphQL/{ => Legacy}/Client.GraphqlCleintOperations/Client.CommentOperations.cs (89%) rename src/Speckle.Core/Api/GraphQL/{ => Legacy}/Client.GraphqlCleintOperations/Client.CommitOperations.cs (85%) rename src/Speckle.Core/Api/GraphQL/{ => Legacy}/Client.GraphqlCleintOperations/Client.ObjectOperations.cs (99%) rename src/Speckle.Core/Api/GraphQL/{ => Legacy}/Client.GraphqlCleintOperations/Client.ServerOperations.cs (96%) rename src/Speckle.Core/Api/GraphQL/{ => Legacy}/Client.GraphqlCleintOperations/Client.StreamOperations.cs (87%) create mode 100644 src/Speckle.Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.UserOperations.cs rename src/Speckle.Core/Api/GraphQL/{ => Legacy}/Client.Subscriptions/Client.Subscriptions.Branch.cs (100%) rename src/Speckle.Core/Api/GraphQL/{ => Legacy}/Client.Subscriptions/Client.Subscriptions.Commit.cs (100%) rename src/Speckle.Core/Api/GraphQL/{ => Legacy}/Client.Subscriptions/Client.Subscriptions.Stream.cs (100%) rename src/Speckle.Core/Api/GraphQL/{Models.cs => Legacy/LegacyGraphQLModels.cs} (59%) create mode 100644 src/Speckle.Core/Api/GraphQL/Legacy/Manager.cs rename src/Speckle.Core/Api/GraphQL/{ => Legacy}/SubscriptionModels.cs (74%) create mode 100644 src/Speckle.Core/Api/GraphQL/Models/Collections.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Models/Comment.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Models/FileUpload.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Models/Model.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Models/ModelsTreeItem.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Models/PendingStreamCollaborator.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Models/Project.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Models/ProjectCollaborator.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Models/ResourceIdentifier.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Models/Responses/MutationResponses.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Models/Responses/Responses.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Models/ServerInfo.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Models/SubscriptionMessages.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Models/User.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Models/Version.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Models/ViewerResourceGroup.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Models/ViewerResourceItem.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Resources/ActiveUserResource.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Resources/CommentResource.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Resources/ModelResource.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Resources/OtherUserResource.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Resources/ProjectInviteResource.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Resources/ProjectResource.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Resources/SubscriptionResource.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Resources/VersionResource.cs create mode 100644 src/Speckle.Core/Api/GraphQL/Resources/graphql.config.yml create mode 100644 src/Speckle.Core/Api/GraphQL/StreamRoles.cs create mode 100644 src/Speckle.Core/Models/Instances/IInstanceComponent.cs create mode 100644 src/Speckle.Core/Models/Instances/InstanceDefinitionProxy.cs create mode 100644 src/Speckle.Core/Models/Instances/InstanceProxy.cs create mode 100644 src/Speckle.Objects/BuiltElements/Archicad/ArchicadOpening.cs create mode 100644 src/Speckle.Objects/BuiltElements/Baseline.cs create mode 100644 src/Speckle.Objects/BuiltElements/Civil/CivilAppliedAssembly.cs create mode 100644 src/Speckle.Objects/BuiltElements/Civil/CivilAppliedSubassembly.cs create mode 100644 src/Speckle.Objects/BuiltElements/Civil/CivilBaseline.cs create mode 100644 src/Speckle.Objects/BuiltElements/Civil/CivilBaselineRegion.cs create mode 100644 src/Speckle.Objects/BuiltElements/Civil/CivilCalculatedLink.cs create mode 100644 src/Speckle.Objects/BuiltElements/Civil/CivilCalculatedPoint.cs create mode 100644 src/Speckle.Objects/BuiltElements/Civil/CivilCalculatedShape.cs rename tests/Speckle.Core.Tests.Integration/{Api.cs => Api/GraphQL/Legacy/LegacyAPITests.cs} (96%) rename tests/Speckle.Core.Tests.Integration/{ => Api/GraphQL/Legacy}/Subscriptions/Branches.cs (97%) rename tests/Speckle.Core.Tests.Integration/{ => Api/GraphQL/Legacy}/Subscriptions/Commits.cs (98%) rename tests/Speckle.Core.Tests.Integration/{ => Api/GraphQL/Legacy}/Subscriptions/Streams.cs (97%) create mode 100644 tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ActiveUserResourceTests.cs create mode 100644 tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/CommentResourceTests.cs create mode 100644 tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ModelResourceExceptionalTests.cs create mode 100644 tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ModelResourceTests.cs create mode 100644 tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/OtherUserResourceTests.cs create mode 100644 tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectInviteResourceExceptionalTests.cs create mode 100644 tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectInviteResourceTests.cs create mode 100644 tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectResourceExceptionalTests.cs create mode 100644 tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectResourceTests.cs create mode 100644 tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/SubscriptionResourceTests.cs create mode 100644 tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/VersionResourceTests.cs create mode 100644 tests/Speckle.Core.Tests.Integration/packages.lock.json delete mode 100644 tests/Speckle.Core.Tests.Performance/Api/Operations/ReceiveFromSQLite.cs delete mode 100644 tests/Speckle.Core.Tests.Performance/Api/Operations/TraverseCommit.cs delete mode 100644 tests/Speckle.Core.Tests.Performance/Program.cs delete mode 100644 tests/Speckle.Core.Tests.Performance/RegressionTestConfig.cs delete mode 100644 tests/Speckle.Core.Tests.Performance/Serialisation/DeserializationWorkerThreads.cs delete mode 100644 tests/Speckle.Core.Tests.Performance/Speckle.Core.Tests.Performance.csproj delete mode 100644 tests/Speckle.Core.Tests.Performance/TestDataHelper.cs create mode 100644 tests/Speckle.Objects.Tests.Unit/ModelPropertySupportedTypes.cs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 69da395b..09fe7320 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,8 +19,12 @@ jobs: with: path: ~/.nuget/packages key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} - restore-keys: | - ${{ runner.os }}-nuget- - name: 🔫 Build All run: ./build.sh + + - name: Upload coverage reports to Codecov with GitHub Action + uses: codecov/codecov-action@v4 + with: + files: tests/**/coverage.xml + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 77c4e123..3570361b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -28,6 +28,12 @@ jobs: - name: 🔫 Build and Pack run: ./build.sh pack + + - name: Upload coverage reports to Codecov with GitHub Action + uses: codecov/codecov-action@v4 + with: + files: tests/**/coverage.xml + token: ${{ secrets.CODECOV_TOKEN }} - name: Push to nuget.org run: dotnet nuget push output/*.nupkg --source "https://api.nuget.org/v3/index.json" --api-key ${{secrets.CONNECTORS_NUGET_TOKEN }} --skip-duplicate diff --git a/.gitignore b/.gitignore index d6a5a1d1..6e3d7b2a 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,4 @@ tools .DS_Store *.snupkg -/tests/TestArchives/6d23a38c-f064-4ef1-ad89-b942396f53b9/Scratch +coverage.xml \ No newline at end of file diff --git a/Directory.Packages.props b/Directory.Packages.props index 6be77c03..5a2cafec 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,5 +1,6 @@ + @@ -14,16 +15,15 @@ + - - + + - - @@ -32,4 +32,4 @@ - + \ No newline at end of file diff --git a/README.md b/README.md index e4aa0173..7de6c40f 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,11 @@ [![Twitter Follow](https://img.shields.io/twitter/follow/SpeckleSystems?style=social)](https://twitter.com/SpeckleSystems) [![Community forum users](https://img.shields.io/discourse/users?server=https%3A%2F%2Fdiscourse.speckle.works&style=flat-square&logo=discourse&logoColor=white)](https://discourse.speckle.works) [![website](https://img.shields.io/badge/https://-speckle.systems-royalblue?style=flat-square)](https://speckle.systems) [![docs](https://img.shields.io/badge/docs-speckle.guide-orange?style=flat-square&logo=read-the-docs&logoColor=white)](https://speckle.guide/dev/) +[![codecov](https://codecov.io/gh/specklesystems/speckle-sharp-sdk/graph/badge.svg?token=TTM5OGr38m)](https://codecov.io/gh/specklesystems/speckle-sharp-sdk) + ## **Disclaimer** -This is an early alpha release, not meant for use in production! We're working to stabilise the 2.0 API, and until then there will be breaking changes. You have been warned! +This is an early alpha release, not meant for use in production! We're working to stabilise the 3.0 API, and until then there will be breaking changes. You have been warned! ## Introduction diff --git a/Speckle.Sdk.sln b/Speckle.Sdk.sln index 202d1e1a..fc72c591 100644 --- a/Speckle.Sdk.sln +++ b/Speckle.Sdk.sln @@ -32,6 +32,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Transports.MongoDB" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Core.Serialization.Tests", "tests\Speckle.Core.Serialization.Tests\Speckle.Core.Serialization.Tests.csproj", "{AA1E1E51-49AE-4F71-84B1-938E19695BE0}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Core.Tests.Integration", "tests\Speckle.Core.Tests.Integration\Speckle.Core.Tests.Integration.csproj", "{4FB41A6D-D139-4111-8115-E3F9F6BEAF24}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{B623BD21-5CAA-43F9-A539-1835276C220E}" + ProjectSection(SolutionItems) = preProject + .github\workflows\main.yml = .github\workflows\main.yml + .github\workflows\ci.yml = .github\workflows\ci.yml + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -74,6 +82,10 @@ Global {AA1E1E51-49AE-4F71-84B1-938E19695BE0}.Debug|Any CPU.Build.0 = Debug|Any CPU {AA1E1E51-49AE-4F71-84B1-938E19695BE0}.Release|Any CPU.ActiveCfg = Release|Any CPU {AA1E1E51-49AE-4F71-84B1-938E19695BE0}.Release|Any CPU.Build.0 = Release|Any CPU + {4FB41A6D-D139-4111-8115-E3F9F6BEAF24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4FB41A6D-D139-4111-8115-E3F9F6BEAF24}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4FB41A6D-D139-4111-8115-E3F9F6BEAF24}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4FB41A6D-D139-4111-8115-E3F9F6BEAF24}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {A413E196-3696-4F48-B635-04B5F76BF9C9} = {5CB96C27-FC5B-4A41-86B6-951AF99B8116} @@ -84,5 +96,7 @@ Global {9B8DDEB5-37C7-49B5-984D-C65DE5FCB7B7} = {58D37DA9-F948-48CA-9A73-F5BBBD533DBF} {68078752-7C54-471A-9CB6-E8AEF34A9EFF} = {5CB96C27-FC5B-4A41-86B6-951AF99B8116} {AA1E1E51-49AE-4F71-84B1-938E19695BE0} = {35047EE7-AD1D-4741-80A7-8F0E874718E9} + {4FB41A6D-D139-4111-8115-E3F9F6BEAF24} = {35047EE7-AD1D-4741-80A7-8F0E874718E9} + {B623BD21-5CAA-43F9-A539-1835276C220E} = {DA2AED52-58F9-471E-8AD8-102FD36129E3} EndGlobalSection EndGlobal diff --git a/build/Program.cs b/build/Program.cs index fdbb0e41..e276da07 100644 --- a/build/Program.cs +++ b/build/Program.cs @@ -43,19 +43,22 @@ void RemoveDirectory(string d) Target(RESTORE, () => RunAsync("dotnet", "restore Speckle.Sdk.sln --locked-mode")); -Target(BUILD, DependsOn(RESTORE), () => RunAsync("dotnet", $"build Speckle.Sdk.sln -c Release --no-restore")); +Target(BUILD, DependsOn(RESTORE), () => RunAsync("dotnet", "build Speckle.Sdk.sln -c Release --no-restore")); Target( TEST, DependsOn(BUILD), - Glob.Files(".", "**/*.Tests.Unit.csproj"), + Glob.Files(".", "**/*.Tests.Unit.csproj").Concat(Glob.Files(".", "**/*.Tests.csproj")), async file => { - await RunAsync("dotnet", $"test {file} -c Release --no-build --verbosity=normal"); + await RunAsync( + "dotnet", + $"test {file} -c Release --no-build --no-restore --verbosity=normal /p:AltCover=true /p:AltCoverAttributeFilter=ExcludeFromCodeCoverage" + ); } ); -Target(PACK, DependsOn(BUILD), () => RunAsync("dotnet", "pack Speckle.Sdk.sln -c Release -o output --no-build")); +Target(PACK, DependsOn(TEST), () => RunAsync("dotnet", "pack Speckle.Sdk.sln -c Release -o output --no-build")); Target("default", DependsOn(FORMAT, TEST), () => Console.WriteLine("Done!")); diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 00000000..8221fde1 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,13 @@ +coverage: + status: + project: + default: + informational: true + target: auto + threshold: 1% + base: auto + patch: + default: + informational: true +github_checks: + annotations: false \ No newline at end of file diff --git a/src/Speckle.Core/Api/Exceptions.cs b/src/Speckle.Core/Api/Exceptions.cs index 7d10bd99..f100fd22 100644 --- a/src/Speckle.Core/Api/Exceptions.cs +++ b/src/Speckle.Core/Api/Exceptions.cs @@ -9,50 +9,55 @@ namespace Speckle.Core.Api; /// /// Base class for GraphQL API exceptions /// -public class SpeckleGraphQLException : SpeckleException +public class SpeckleGraphQLException : SpeckleGraphQLException { - private readonly GraphQLRequest _request; - public GraphQLResponse? Response { get; } - - public SpeckleGraphQLException(string message, GraphQLRequest request, GraphQLResponse? response) - : base(message) - { - _request = request; - Response = response; - } + public new GraphQLResponse? Response => (GraphQLResponse?)base.Response; - public SpeckleGraphQLException(string message, Exception inner, GraphQLRequest request, GraphQLResponse? response) - : this(message, inner) - { - _request = request; - Response = response; - } + public SpeckleGraphQLException( + string message, + GraphQLRequest request, + GraphQLResponse? response, + Exception? innerException = null + ) + : base(message, request, response, innerException) { } public SpeckleGraphQLException() { } - public SpeckleGraphQLException(string message) + public SpeckleGraphQLException(string? message) : base(message) { } - public SpeckleGraphQLException(string message, Exception innerException) + public SpeckleGraphQLException(string? message, Exception? innerException) : base(message, innerException) { } +} + +public class SpeckleGraphQLException : SpeckleException +{ + private readonly GraphQLRequest _request; + public IGraphQLResponse? Response { get; } public IEnumerable ErrorMessages => Response?.Errors != null ? Response.Errors.Select(e => e.Message) : Enumerable.Empty(); public IDictionary? Extensions => Response?.Extensions; -} -public class SpeckleGraphQLException : SpeckleGraphQLException -{ - public SpeckleGraphQLException(string message, GraphQLRequest request, GraphQLResponse? response) - : base(message, request, response) { } + public SpeckleGraphQLException( + string? message, + GraphQLRequest request, + IGraphQLResponse? response, + Exception? innerException = null + ) + : base(message, innerException) + { + _request = request; + Response = response; + } public SpeckleGraphQLException() { } - public SpeckleGraphQLException(string message) + public SpeckleGraphQLException(string? message) : base(message) { } - public SpeckleGraphQLException(string message, Exception innerException) + public SpeckleGraphQLException(string? message, Exception? innerException) : base(message, innerException) { } } @@ -61,44 +66,56 @@ public SpeckleGraphQLException(string message, Exception innerException) /// https://www.apollographql.com/docs/apollo-server/v2/data/errors/#unauthenticated /// https://www.apollographql.com/docs/apollo-server/v2/data/errors/#forbidden /// -public class SpeckleGraphQLForbiddenException : SpeckleGraphQLException +public class SpeckleGraphQLForbiddenException : SpeckleGraphQLException { - public SpeckleGraphQLForbiddenException(GraphQLRequest request, GraphQLResponse response) - : base("Your request was forbidden", request, response) { } + public SpeckleGraphQLForbiddenException( + GraphQLRequest request, + IGraphQLResponse response, + Exception? innerException = null + ) + : base("Your request was forbidden", request, response, innerException) { } public SpeckleGraphQLForbiddenException() { } - public SpeckleGraphQLForbiddenException(string message) + public SpeckleGraphQLForbiddenException(string? message) : base(message) { } - public SpeckleGraphQLForbiddenException(string message, Exception innerException) + public SpeckleGraphQLForbiddenException(string? message, Exception? innerException) : base(message, innerException) { } } -public class SpeckleGraphQLInternalErrorException : SpeckleGraphQLException +public class SpeckleGraphQLInternalErrorException : SpeckleGraphQLException { - public SpeckleGraphQLInternalErrorException(GraphQLRequest request, GraphQLResponse response) - : base("Your request failed on the server side", request, response) { } + public SpeckleGraphQLInternalErrorException( + GraphQLRequest request, + IGraphQLResponse response, + Exception? innerException = null + ) + : base("Your request failed on the server side", request, response, innerException) { } public SpeckleGraphQLInternalErrorException() { } - public SpeckleGraphQLInternalErrorException(string message) + public SpeckleGraphQLInternalErrorException(string? message) : base(message) { } - public SpeckleGraphQLInternalErrorException(string message, Exception innerException) + public SpeckleGraphQLInternalErrorException(string? message, Exception? innerException) : base(message, innerException) { } } -public class SpeckleGraphQLStreamNotFoundException : SpeckleGraphQLException +public class SpeckleGraphQLStreamNotFoundException : SpeckleGraphQLException { - public SpeckleGraphQLStreamNotFoundException(GraphQLRequest request, GraphQLResponse response) - : base("Stream not found", request, response) { } + public SpeckleGraphQLStreamNotFoundException( + GraphQLRequest request, + IGraphQLResponse response, + Exception? innerException = null + ) + : base("Stream not found", request, response, innerException) { } public SpeckleGraphQLStreamNotFoundException() { } - public SpeckleGraphQLStreamNotFoundException(string message) + public SpeckleGraphQLStreamNotFoundException(string? message) : base(message) { } - public SpeckleGraphQLStreamNotFoundException(string message, Exception innerException) + public SpeckleGraphQLStreamNotFoundException(string? message, Exception? innerException) : base(message, innerException) { } } diff --git a/src/Speckle.Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ObsoleteOperations.cs b/src/Speckle.Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ObsoleteOperations.cs deleted file mode 100644 index 15d0054f..00000000 --- a/src/Speckle.Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ObsoleteOperations.cs +++ /dev/null @@ -1,250 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Threading; -using System.Threading.Tasks; -using GraphQL; - -namespace Speckle.Core.Api; - -[SuppressMessage("Design", "CA1068:CancellationToken parameters must come last")] -public partial class Client -{ - #region Stream Grant Permission - - /// - /// Grants permissions to a user on a given stream. - /// - /// - /// - [Obsolete("Please use the `StreamUpdatePermission` method", true)] - public Task StreamGrantPermission(StreamPermissionInput permissionInput) - { - return StreamGrantPermission(CancellationToken.None, permissionInput); - } - - /// - /// Grants permissions to a user on a given stream. - /// - /// - /// - /// - [Obsolete("Please use the `StreamUpdatePermission` method", true)] - public async Task StreamGrantPermission( - CancellationToken cancellationToken, - StreamPermissionInput permissionInput - ) - { - var request = new GraphQLRequest - { - Query = - @" - mutation streamGrantPermission($permissionParams: StreamGrantPermissionInput!) { - streamGrantPermission(permissionParams:$permissionParams) - }", - Variables = new { permissionParams = permissionInput } - }; - - var res = await ExecuteGraphQLRequest>(request, cancellationToken).ConfigureAwait(false); - return (bool)res["streamGrantPermission"]; - } - - #endregion - - #region Cancellation token as last param - - [Obsolete("Use overload with cancellation token parameter last")] - public Task> StreamGetActivity( - CancellationToken cancellationToken, - string id, - DateTime? after = null, - DateTime? before = null, - DateTime? cursor = null, - string actionType = "", - int limit = 25 - ) - { - return StreamGetActivity(id, after, before, cursor, actionType, limit, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task> StreamGetBranches( - CancellationToken cancellationToken, - string streamId, - int branchesLimit = 10, - int commitsLimit = 10 - ) - { - return StreamGetBranches(streamId, branchesLimit, commitsLimit, CancellationToken.None); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task BranchCreate(CancellationToken cancellationToken, BranchCreateInput branchInput) - { - return BranchCreate(branchInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task BranchGet( - CancellationToken cancellationToken, - string streamId, - string branchName, - int commitsLimit = 10 - ) - { - return BranchGet(streamId, branchName, commitsLimit, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task BranchUpdate(CancellationToken cancellationToken, BranchUpdateInput branchInput) - { - return BranchUpdate(branchInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task BranchDelete(CancellationToken cancellationToken, BranchDeleteInput branchInput) - { - return BranchDelete(branchInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task StreamGetComments( - CancellationToken cancellationToken, - string streamId, - int limit = 25, - string? cursor = null - ) - { - return StreamGetComments(streamId, limit, cursor, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task StreamGetCommentScreenshot(CancellationToken cancellationToken, string id, string streamId) - { - return StreamGetCommentScreenshot(id, streamId, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task CommitGet(CancellationToken cancellationToken, string streamId, string commitId) - { - return CommitGet(streamId, commitId, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task> StreamGetCommits(CancellationToken cancellationToken, string streamId, int limit = 10) - { - return StreamGetCommits(streamId, limit, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task CommitCreate(CancellationToken cancellationToken, CommitCreateInput commitInput) - { - return CommitCreate(commitInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task CommitUpdate(CancellationToken cancellationToken, CommitUpdateInput commitInput) - { - return CommitUpdate(commitInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task CommitDelete(CancellationToken cancellationToken, CommitDeleteInput commitInput) - { - return CommitDelete(commitInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task CommitReceived(CancellationToken cancellationToken, CommitReceivedInput commitReceivedInput) - { - return CommitReceived(commitReceivedInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task ObjectGet(CancellationToken cancellationToken, string streamId, string objectId) - { - return ObjectGet(streamId, objectId, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task ObjectCountGet(CancellationToken cancellationToken, string streamId, string objectId) - { - return ObjectCountGet(streamId, objectId, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task StreamGet(CancellationToken cancellationToken, string id, int branchesLimit = 10) - { - return StreamGet(id, branchesLimit, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task> StreamsGet(CancellationToken cancellationToken, int limit = 10) - { - return StreamsGet(limit, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task> FavoriteStreamsGet(CancellationToken cancellationToken, int limit = 10) - { - return FavoriteStreamsGet(limit, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task> StreamSearch(CancellationToken cancellationToken, string query, int limit = 10) - { - return StreamSearch(query, limit, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task StreamCreate(CancellationToken cancellationToken, StreamCreateInput streamInput) - { - return StreamCreate(streamInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task StreamUpdate(CancellationToken cancellationToken, StreamUpdateInput streamInput) - { - return StreamUpdate(streamInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task StreamDelete(CancellationToken cancellationToken, string id) - { - return StreamDelete(id, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task StreamRevokePermission( - CancellationToken cancellationToken, - StreamRevokePermissionInput permissionInput - ) - { - return StreamRevokePermission(permissionInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task StreamGetPendingCollaborators(CancellationToken cancellationToken, string id) - { - return StreamGetPendingCollaborators(id, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task StreamInviteCreate(CancellationToken cancellationToken, StreamInviteCreateInput inviteCreateInput) - { - return StreamInviteCreate(inviteCreateInput, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task OtherUserGet(CancellationToken cancellationToken, string id) - { - return OtherUserGet(id, cancellationToken); - } - - [Obsolete("Use overload with cancellation token parameter last")] - public Task> UserSearch(CancellationToken cancellationToken, string query, int limit = 10) - { - return UserSearch(query, limit, cancellationToken); - } - #endregion -} diff --git a/src/Speckle.Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.UserOperations.cs b/src/Speckle.Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.UserOperations.cs deleted file mode 100644 index a79764e5..00000000 --- a/src/Speckle.Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.UserOperations.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using GraphQL; - -namespace Speckle.Core.Api; - -public partial class Client -{ - /// - /// Gets the currently active user profile. - /// - /// - /// - public async Task ActiveUserGet(CancellationToken cancellationToken = default) - { - var request = new GraphQLRequest - { - Query = - @"query User { - activeUser { - id, - email, - name, - bio, - company, - avatar, - verified, - profiles, - role, - } - }" - }; - return (await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false)).activeUser; - } - - /// - /// Get another user's profile by its user id. - /// - /// Id of the user you are looking for - /// - /// - public async Task OtherUserGet(string id, CancellationToken cancellationToken = default) - { - var request = new GraphQLRequest - { - Query = - @"query LimitedUser($id: String!) { - otherUser(id: $id){ - id, - name, - bio, - company, - avatar, - verified, - role, - } - }", - Variables = new { id } - }; - return (await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false)).otherUser; - } - - /// - /// Searches for a user on the server. - /// - /// String to search for. Must be at least 3 characters - /// Max number of users to return - /// - public async Task> UserSearch( - string query, - int limit = 10, - CancellationToken cancellationToken = default - ) - { - var request = new GraphQLRequest - { - Query = - @"query UserSearch($query: String!, $limit: Int!) { - userSearch(query: $query, limit: $limit) { - cursor, - items { - id - name - bio - company - avatar - verified - role - } - } - }", - Variables = new { query, limit } - }; - return (await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false)) - .userSearch - .items; - } -} diff --git a/src/Speckle.Core/Api/GraphQL/Client.cs b/src/Speckle.Core/Api/GraphQL/Client.cs index ae4c43cb..34ebf29a 100644 --- a/src/Speckle.Core/Api/GraphQL/Client.cs +++ b/src/Speckle.Core/Api/GraphQL/Client.cs @@ -15,6 +15,9 @@ using Serilog.Context; using Serilog.Core; using Serilog.Core.Enrichers; +using Serilog.Events; +using Speckle.Core.Api.GraphQL; +using Speckle.Core.Api.GraphQL.Resources; using Speckle.Core.Api.GraphQL.Serializer; using Speckle.Core.Credentials; using Speckle.Core.Helpers; @@ -23,71 +26,55 @@ namespace Speckle.Core.Api; -public sealed partial class Client : IDisposable +public sealed partial class Client : ISpeckleGraphQLClient, IDisposable { - [Obsolete] - internal Client() { } + public ProjectResource Project { get; } + public ModelResource Model { get; } + public VersionResource Version { get; } + public ActiveUserResource ActiveUser { get; } + public OtherUserResource OtherUser { get; } + public ProjectInviteResource ProjectInvite { get; } + public CommentResource Comment { get; } + public SubscriptionResource Subscription { get; } - public Client(Account account) - { - Account = account ?? throw new SpeckleException("Provided account is null."); - - HttpClient = Http.GetHttpProxyClient(null, TimeSpan.FromSeconds(30)); - Http.AddAuthHeader(HttpClient, account.token); + public string ServerUrl => Account.serverInfo.url; - HttpClient.DefaultRequestHeaders.Add("apollographql-client-name", Setup.HostApplication); - HttpClient.DefaultRequestHeaders.Add( - "apollographql-client-version", - Assembly.GetExecutingAssembly().GetName().Version.ToString() - ); + public string ApiToken => Account.token; - GQLClient = new GraphQLHttpClient( - new GraphQLHttpClientOptions - { - EndPoint = new Uri(new Uri(account.serverInfo.url), "/graphql"), - UseWebSocketForQueriesAndMutations = false, - WebSocketProtocol = "graphql-ws", - ConfigureWebSocketConnectionInitPayload = _ => - { - return Http.CanAddAuth(account.token, out string? authValue) ? new { Authorization = authValue } : null; - }, - }, - new NewtonsoftJsonSerializer(), - HttpClient - ); + public System.Version? ServerVersion { get; private set; } - GQLClient.WebSocketReceiveErrors.Subscribe(e => - { - if (e is WebSocketException we) - { - Console.WriteLine( - $"WebSocketException: {we.Message} (WebSocketError {we.WebSocketErrorCode}, ErrorCode {we.ErrorCode}, NativeErrorCode {we.NativeErrorCode}" - ); - } - else - { - Console.WriteLine($"Exception in websocket receive stream: {e}"); - } - }); - } + [JsonIgnore] + public Account Account { get; } - public string ServerUrl => Account.serverInfo.url; + private HttpClient HttpClient { get; } - public string ApiToken => Account.token; + public GraphQLHttpClient GQLClient { get; } - public System.Version? ServerVersion { get; set; } + /// + /// was null + public Client(Account account) + { + Account = account ?? throw new ArgumentException("Provided account is null."); - [JsonIgnore] - public Account Account { get; set; } + Project = new(this); + Model = new(this); + Version = new(this); + ActiveUser = new(this); + OtherUser = new(this); + ProjectInvite = new(this); + Comment = new(this); + Subscription = new(this); - private HttpClient HttpClient { get; set; } + HttpClient = CreateHttpClient(account); - public GraphQLHttpClient GQLClient { get; set; } + GQLClient = CreateGraphQLClient(account, HttpClient); + } public void Dispose() { try { + Subscription.Dispose(); UserStreamAddedSubscription?.Dispose(); UserStreamRemovedSubscription?.Dispose(); StreamUpdatedSubscription?.Dispose(); @@ -116,42 +103,34 @@ internal async Task ExecuteWithResiliencePolicies(Func> func) var delay = Backoff.DecorrelatedJitterBackoffV2(TimeSpan.FromSeconds(1), 5); var graphqlRetry = Policy - .Handle>() + .Handle() .WaitAndRetryAsync( delay, - (ex, timeout, context) => + (ex, timeout, _) => { - var graphqlEx = (SpeckleGraphQLException)ex; - SpeckleLog - .Logger.ForContext("graphqlExtensions", graphqlEx.Extensions) - .ForContext("graphqlErrorMessages", graphqlEx.ErrorMessages) - .Warning( - ex, - "The previous attempt at executing function to get {resultType} failed with {exceptionMessage}. Retrying after {timeout}.", - typeof(T).Name, - ex.Message, - timeout - ); + SpeckleLog.Logger.Debug( + ex, + "The previous attempt at executing function to get {resultType} failed with {exceptionMessage}. Retrying after {timeout}", + typeof(T).Name, + ex.Message, + timeout + ); } ); return await graphqlRetry.ExecuteAsync(func).ConfigureAwait(false); } - /// "FORBIDDEN" on "UNAUTHORIZED" response from server - /// All other request errors - /// The requested a cancel + /// public async Task ExecuteGraphQLRequest(GraphQLRequest request, CancellationToken cancellationToken = default) { using IDisposable context0 = LogContext.Push(CreateEnrichers(request)); + var timer = Stopwatch.StartNew(); - SpeckleLog.Logger.Debug("Starting execution of graphql request to get {resultType}", typeof(T).Name); - var timer = new Stopwatch(); - var success = false; - timer.Start(); + Exception? exception = null; try { - var result = await ExecuteWithResiliencePolicies(async () => + return await ExecuteWithResiliencePolicies(async () => { GraphQLResponse result = await GQLClient .SendMutationAsync(request, cancellationToken) @@ -160,58 +139,28 @@ public async Task ExecuteGraphQLRequest(GraphQLRequest request, Cancellati return result.Data; }) .ConfigureAwait(false); - success = true; - return result; } - // cancellations are bubbling up with no logging - catch (OperationCanceledException) + catch (Exception ex) { + exception = ex; throw; } - // we catch forbidden to rethrow, making sure its not logged. - catch (SpeckleGraphQLForbiddenException) - { - throw; - } - // anything else related to graphql gets logged - catch (SpeckleGraphQLException gqlException) - { - SpeckleLog - .Logger.ForContext("graphqlResponse", gqlException.Response) - .ForContext("graphqlExtensions", gqlException.Extensions) - .ForContext("graphqlErrorMessages", gqlException.ErrorMessages.ToList()) - .Warning( - gqlException, - "Execution of the graphql request to get {resultType} failed with {graphqlExceptionType} {exceptionMessage}.", - typeof(T).Name, - gqlException.GetType().Name, - gqlException.Message - ); - throw; - } - // we log and wrap anything that is not a graphql exception. - // this makes sure, that any graphql operation only throws SpeckleGraphQLExceptions - catch (Exception ex) when (!ex.IsFatal()) - { - SpeckleLog.Logger.Warning( - ex, - "Execution of the graphql request to get {resultType} failed without a graphql response. Cause {exceptionMessage}", - typeof(T).Name, - ex.Message - ); - throw new SpeckleGraphQLException("The graphql request failed without a graphql response", ex, request, null); - } finally { - // this is a performance metric log operation - // this makes sure that both success and failed operations report - // the same performance log - timer.Stop(); - var status = success ? "succeeded" : "failed"; - SpeckleLog.Logger.Information( - "Execution of graphql request to get {resultType} {resultStatus} after {elapsed} seconds", + LogEventLevel logLevel = exception switch + { + null => LogEventLevel.Information, + OperationCanceledException + => cancellationToken.IsCancellationRequested ? LogEventLevel.Debug : LogEventLevel.Error, + SpeckleException => LogEventLevel.Warning, + _ => LogEventLevel.Error, + }; + SpeckleLog.Logger.Write( + logLevel, + exception, + "Execution of the graphql request to get {resultType} completed with success:{status} after {elapsed} seconds", typeof(T).Name, - status, + exception is null, timer.Elapsed.TotalSeconds ); } @@ -235,7 +184,7 @@ internal void MaybeThrowFromGraphQLErrors(GraphQLRequest request, GraphQLResp ) ) { - throw new SpeckleGraphQLForbiddenException(request, response); + throw new SpeckleGraphQLForbiddenException(request, response); } if ( @@ -244,7 +193,7 @@ internal void MaybeThrowFromGraphQLErrors(GraphQLRequest request, GraphQLResp ) ) { - throw new SpeckleGraphQLStreamNotFoundException(request, response); + throw new SpeckleGraphQLStreamNotFoundException(request, response); } if ( @@ -254,7 +203,7 @@ internal void MaybeThrowFromGraphQLErrors(GraphQLRequest request, GraphQLResp ) ) { - throw new SpeckleGraphQLInternalErrorException(request, response); + throw new SpeckleGraphQLInternalErrorException(request, response); } throw new SpeckleGraphQLException("Request failed with errors", request, response); @@ -296,6 +245,10 @@ private ILogEventEnricher[] CreateEnrichers(GraphQLRequest request) }; } + IDisposable ISpeckleGraphQLClient.SubscribeTo(GraphQLRequest request, Action callback) => + SubscribeTo(request, callback); + + /// internal IDisposable SubscribeTo(GraphQLRequest request, Action callback) { using (LogContext.Push(CreateEnrichers(request))) @@ -322,7 +275,7 @@ internal IDisposable SubscribeTo(GraphQLRequest request, Action ca } } // we catch forbidden to rethrow, making sure its not logged. - catch (SpeckleGraphQLForbiddenException) + catch (SpeckleGraphQLForbiddenException) { throw; } @@ -353,7 +306,7 @@ internal IDisposable SubscribeTo(GraphQLRequest request, Action ca // so far we've swallowed these errors SpeckleLog.Logger.Error( ex, - "Subscription request for {resultType} failed with {exceptionMessage}", + "Subscription for {resultType} terminated unexpectedly with {exceptionMessage}", typeof(T).Name, ex.Message ); @@ -372,11 +325,57 @@ internal IDisposable SubscribeTo(GraphQLRequest request, Action ca ); throw new SpeckleGraphQLException( "The graphql request failed without a graphql response", - ex, request, - null + null, + ex ); } } } + + private static GraphQLHttpClient CreateGraphQLClient(Account account, HttpClient httpClient) + { + var gQLClient = new GraphQLHttpClient( + new GraphQLHttpClientOptions + { + EndPoint = new Uri(new Uri(account.serverInfo.url), "/graphql"), + UseWebSocketForQueriesAndMutations = false, + WebSocketProtocol = "graphql-ws", + ConfigureWebSocketConnectionInitPayload = _ => + { + return Http.CanAddAuth(account.token, out string? authValue) ? new { Authorization = authValue } : null; + }, + }, + new NewtonsoftJsonSerializer(), + httpClient + ); + + gQLClient.WebSocketReceiveErrors.Subscribe(e => + { + if (e is WebSocketException we) + { + Console.WriteLine( + $"WebSocketException: {we.Message} (WebSocketError {we.WebSocketErrorCode}, ErrorCode {we.ErrorCode}, NativeErrorCode {we.NativeErrorCode}" + ); + } + else + { + Console.WriteLine($"Exception in websocket receive stream: {e}"); + } + }); + return gQLClient; + } + + private static HttpClient CreateHttpClient(Account account) + { + var httpClient = Http.GetHttpProxyClient(null, TimeSpan.FromSeconds(30)); + Http.AddAuthHeader(httpClient, account.token); + + httpClient.DefaultRequestHeaders.Add("apollographql-client-name", Setup.HostApplication); + httpClient.DefaultRequestHeaders.Add( + "apollographql-client-version", + Assembly.GetExecutingAssembly().GetName().Version.ToString() + ); + return httpClient; + } } diff --git a/src/Speckle.Core/Api/GraphQL/Enums/FileUploadConversionStatus.cs b/src/Speckle.Core/Api/GraphQL/Enums/FileUploadConversionStatus.cs new file mode 100644 index 00000000..4ab9e751 --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Enums/FileUploadConversionStatus.cs @@ -0,0 +1,10 @@ +namespace Speckle.Core.Api.GraphQL.Enums; + +//This enum isn't explicitly defined in the schema, instead its usages are int typed (But represent an enum) +public enum FileUploadConversionStatus +{ + Queued = 0, + Processing = 1, + Success = 2, + Error = 3, +} diff --git a/src/Speckle.Core/Api/GraphQL/Enums/ProjectCommentsUpdatedMessageType.cs b/src/Speckle.Core/Api/GraphQL/Enums/ProjectCommentsUpdatedMessageType.cs new file mode 100644 index 00000000..2f11301d --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Enums/ProjectCommentsUpdatedMessageType.cs @@ -0,0 +1,8 @@ +namespace Speckle.Core.Api.GraphQL.Enums; + +public enum ProjectCommentsUpdatedMessageType +{ + ARCHIVED, + CREATED, + UPDATED, +} diff --git a/src/Speckle.Core/Api/GraphQL/Enums/ProjectFileImportUpdatedMessageType.cs b/src/Speckle.Core/Api/GraphQL/Enums/ProjectFileImportUpdatedMessageType.cs new file mode 100644 index 00000000..7cb7d933 --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Enums/ProjectFileImportUpdatedMessageType.cs @@ -0,0 +1,7 @@ +namespace Speckle.Core.Api.GraphQL.Enums; + +public enum ProjectFileImportUpdatedMessageType +{ + CREATED, + UPDATED, +} diff --git a/src/Speckle.Core/Api/GraphQL/Enums/ProjectModelsUpdatedMessageType.cs b/src/Speckle.Core/Api/GraphQL/Enums/ProjectModelsUpdatedMessageType.cs new file mode 100644 index 00000000..1416691f --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Enums/ProjectModelsUpdatedMessageType.cs @@ -0,0 +1,8 @@ +namespace Speckle.Core.Api.GraphQL.Enums; + +public enum ProjectModelsUpdatedMessageType +{ + CREATED, + DELETED, + UPDATED, +} diff --git a/src/Speckle.Core/Api/GraphQL/Enums/ProjectPendingModelsUpdatedMessageType.cs b/src/Speckle.Core/Api/GraphQL/Enums/ProjectPendingModelsUpdatedMessageType.cs new file mode 100644 index 00000000..42ac2beb --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Enums/ProjectPendingModelsUpdatedMessageType.cs @@ -0,0 +1,7 @@ +namespace Speckle.Core.Api.GraphQL.Enums; + +public enum ProjectPendingModelsUpdatedMessageType +{ + CREATED, + UPDATED, +} diff --git a/src/Speckle.Core/Api/GraphQL/Enums/ProjectUpdatedMessageType.cs b/src/Speckle.Core/Api/GraphQL/Enums/ProjectUpdatedMessageType.cs new file mode 100644 index 00000000..3eccdd5a --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Enums/ProjectUpdatedMessageType.cs @@ -0,0 +1,7 @@ +namespace Speckle.Core.Api.GraphQL.Enums; + +public enum ProjectUpdatedMessageType +{ + DELETED, + UPDATED, +} diff --git a/src/Speckle.Core/Api/GraphQL/Enums/ProjectVersionsUpdatedMessageType.cs b/src/Speckle.Core/Api/GraphQL/Enums/ProjectVersionsUpdatedMessageType.cs new file mode 100644 index 00000000..14e1f700 --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Enums/ProjectVersionsUpdatedMessageType.cs @@ -0,0 +1,8 @@ +namespace Speckle.Core.Api.GraphQL.Enums; + +public enum ProjectVersionsUpdatedMessageType +{ + CREATED, + DELETED, + UPDATED, +} diff --git a/src/Speckle.Core/Api/GraphQL/Enums/ProjectVisibility.cs b/src/Speckle.Core/Api/GraphQL/Enums/ProjectVisibility.cs new file mode 100644 index 00000000..9a62fff9 --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Enums/ProjectVisibility.cs @@ -0,0 +1,8 @@ +namespace Speckle.Core.Api.GraphQL.Enums; + +public enum ProjectVisibility +{ + Private, + Public, + Unlisted +} diff --git a/src/Speckle.Core/Api/GraphQL/Enums/ResourceType.cs b/src/Speckle.Core/Api/GraphQL/Enums/ResourceType.cs new file mode 100644 index 00000000..2fa31c46 --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Enums/ResourceType.cs @@ -0,0 +1,9 @@ +namespace Speckle.Core.Api.GraphQL.Enums; + +public enum ResourceType +{ + commit, + stream, + @object, + comment +} diff --git a/src/Speckle.Core/Api/GraphQL/Enums/UserProjectsUpdatedMessageType.cs b/src/Speckle.Core/Api/GraphQL/Enums/UserProjectsUpdatedMessageType.cs new file mode 100644 index 00000000..5225929a --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Enums/UserProjectsUpdatedMessageType.cs @@ -0,0 +1,7 @@ +namespace Speckle.Core.Api.GraphQL.Enums; + +public enum UserProjectsUpdatedMessageType +{ + ADDED, + REMOVED, +} diff --git a/src/Speckle.Core/Api/GraphQL/GraphQLHttpClientExtensions.cs b/src/Speckle.Core/Api/GraphQL/GraphQLHttpClientExtensions.cs index bbdfddc2..252fa7f9 100644 --- a/src/Speckle.Core/Api/GraphQL/GraphQLHttpClientExtensions.cs +++ b/src/Speckle.Core/Api/GraphQL/GraphQLHttpClientExtensions.cs @@ -3,6 +3,8 @@ using System.Threading.Tasks; using GraphQL; using GraphQL.Client.Http; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Models.Responses; namespace Speckle.Core.Api.GraphQL; @@ -13,7 +15,7 @@ public static class GraphQLHttpClientExtensions /// /// [Optional] defaults to an empty cancellation token /// object excluding any strings (eg "2.7.2-alpha.6995" becomes "2.7.2.6995") - /// + /// public static async Task GetServerVersion( this GraphQLHttpClient client, CancellationToken cancellationToken = default diff --git a/src/Speckle.Core/Api/GraphQL/ISpeckleGraphQLClient.cs b/src/Speckle.Core/Api/GraphQL/ISpeckleGraphQLClient.cs new file mode 100644 index 00000000..5b6a3720 --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/ISpeckleGraphQLClient.cs @@ -0,0 +1,20 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using GraphQL; + +namespace Speckle.Core.Api.GraphQL; + +internal interface ISpeckleGraphQLClient +{ + /// "FORBIDDEN" on "UNAUTHORIZED" response from server + /// All other request errors + /// The requested a cancel + /// This already been disposed + internal Task ExecuteGraphQLRequest(GraphQLRequest request, CancellationToken cancellationToken); + + /// "FORBIDDEN" on "UNAUTHORIZED" response from server + /// All other request errors + /// This already been disposed + internal IDisposable SubscribeTo(GraphQLRequest request, Action callback); +} diff --git a/src/Speckle.Core/Api/GraphQL/Inputs/CommentInputs.cs b/src/Speckle.Core/Api/GraphQL/Inputs/CommentInputs.cs new file mode 100644 index 00000000..df810ade --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Inputs/CommentInputs.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace Speckle.Core.Api.GraphQL.Inputs; + +public sealed record CreateCommentInput( + CommentContentInput content, + string projectId, + string resourceIdString, + string? screenshot, + object? viewerState +); + +public sealed record EditCommentInput(CommentContentInput content, string commentId); + +public sealed record CreateCommentReplyInput(CommentContentInput content, string threadId); + +public sealed record CommentContentInput(IReadOnlyCollection? blobIds, object? doc); diff --git a/src/Speckle.Core/Api/GraphQL/Inputs/ModelInputs.cs b/src/Speckle.Core/Api/GraphQL/Inputs/ModelInputs.cs new file mode 100644 index 00000000..817df64c --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Inputs/ModelInputs.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace Speckle.Core.Api.GraphQL.Inputs; + +public sealed record CreateModelInput(string name, string? description, string projectId); + +public sealed record DeleteModelInput(string id, string projectId); + +public sealed record UpdateModelInput(string id, string? name, string? description, string projectId); + +public sealed record ModelVersionsFilter(IReadOnlyList priorityIds, bool? priorityIdsOnly); diff --git a/src/Speckle.Core/Api/GraphQL/Inputs/ProjectInputs.cs b/src/Speckle.Core/Api/GraphQL/Inputs/ProjectInputs.cs new file mode 100644 index 00000000..568d0093 --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Inputs/ProjectInputs.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using Speckle.Core.Api.GraphQL.Enums; + +namespace Speckle.Core.Api.GraphQL.Inputs; + +public sealed record ProjectCommentsFilter(bool? includeArchived, bool? loadedVersionsOnly, string? resourceIdString); + +public sealed record ProjectCreateInput(string? name, string? description, ProjectVisibility? visibility); + +public sealed record ProjectInviteCreateInput(string? email, string? role, string? serverRole, string? userId); + +public sealed record ProjectInviteUseInput(bool accept, string projectId, string token); + +public sealed record ProjectModelsFilter( + IReadOnlyList? contributors, + IReadOnlyList? excludeIds, + IReadOnlyList? ids, + bool? onlyWithVersions, + string? search, + IReadOnlyList sourceApps +); + +public sealed record ProjectModelsTreeFilter( + IReadOnlyList? contributors, + string? search, + IReadOnlyList? sourceApps +); + +public sealed record ProjectUpdateInput( + string id, + string? name = null, + string? description = null, + bool? allowPublicComments = null, + ProjectVisibility? visibility = null +); + +public sealed record ProjectUpdateRoleInput(string userId, string projectId, string? role); + +public sealed record UserProjectsFilter(string search, IReadOnlyList? onlyWithRoles = null); diff --git a/src/Speckle.Core/Api/GraphQL/Inputs/SubscriptionInputs.cs b/src/Speckle.Core/Api/GraphQL/Inputs/SubscriptionInputs.cs new file mode 100644 index 00000000..86688a28 --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Inputs/SubscriptionInputs.cs @@ -0,0 +1,7 @@ +namespace Speckle.Core.Api.GraphQL.Inputs; + +public sealed record ViewerUpdateTrackingTarget( + string projectId, + string resourceIdString, + bool? loadedVersionsOnly = null +); diff --git a/src/Speckle.Core/Api/GraphQL/Inputs/VersionInputs.cs b/src/Speckle.Core/Api/GraphQL/Inputs/VersionInputs.cs new file mode 100644 index 00000000..5bbee6e7 --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Inputs/VersionInputs.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Speckle.Core.Api.GraphQL.Inputs; + +public sealed record UpdateVersionInput(string versionId, string? message); + +public sealed record MoveVersionsInput(string targetModelName, IReadOnlyList versionIds); + +public sealed record DeleteVersionsInput(IReadOnlyList versionIds); diff --git a/src/Speckle.Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ActivityOperations.cs b/src/Speckle.Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ActivityOperations.cs similarity index 96% rename from src/Speckle.Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ActivityOperations.cs rename to src/Speckle.Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ActivityOperations.cs index 34250ecb..df430294 100644 --- a/src/Speckle.Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ActivityOperations.cs +++ b/src/Speckle.Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ActivityOperations.cs @@ -8,6 +8,7 @@ namespace Speckle.Core.Api; public partial class Client { + //TODO: API gap /// /// Gets the activity of a stream /// @@ -25,7 +26,7 @@ public async Task> StreamGetActivity( DateTime? before = null, DateTime? cursor = null, string actionType = "", - int limit = 25, + int limit = ServerLimits.DEFAULT_PAGINATION_REQUEST, CancellationToken cancellationToken = default ) { diff --git a/src/Speckle.Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.BranchOperations.cs b/src/Speckle.Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.BranchOperations.cs similarity index 86% rename from src/Speckle.Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.BranchOperations.cs rename to src/Speckle.Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.BranchOperations.cs index 1c3a67b1..456098e0 100644 --- a/src/Speckle.Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.BranchOperations.cs +++ b/src/Speckle.Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.BranchOperations.cs @@ -1,7 +1,9 @@ +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using GraphQL; +using Speckle.Core.Api.GraphQL.Resources; namespace Speckle.Core.Api; @@ -14,6 +16,9 @@ public partial class Client /// Id of the stream to get the branches from /// Max number of commits to retrieve /// + /// + /// + [Obsolete($"Use client.{nameof(Model)}.{nameof(ModelResource.GetModels)}")] public async Task> StreamGetBranchesWithLimitRetry(string streamId, int commitsLimit = 10) { List branches; @@ -38,6 +43,9 @@ public async Task> StreamGetBranchesWithLimitRetry(string streamId, /// Max number of commits to retrieve /// /// + /// + /// + [Obsolete($"Use client.{nameof(Model)}.{nameof(ModelResource.GetModels)}")] public async Task> StreamGetBranches( string streamId, int branchesLimit = 10, @@ -86,6 +94,8 @@ public async Task> StreamGetBranches( /// /// /// The branch id. + /// + [Obsolete($"Use client.{nameof(Model)}.{nameof(ModelResource.Create)}")] public async Task BranchCreate(BranchCreateInput branchInput, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -105,6 +115,10 @@ public async Task BranchCreate(BranchCreateInput branchInput, Cancellati /// Name of the branch to get /// /// The requested branch + /// Updated to Model.GetWithVersions + /// + /// + [Obsolete($"Use client.{nameof(Model)}.{nameof(ModelResource.Get)}")] public async Task BranchGet( string streamId, string branchName, @@ -154,6 +168,8 @@ public async Task BranchGet( /// Id of the project to get the model from /// Id of the model /// + /// + [Obsolete($"Use client.{nameof(Model)}.{nameof(ModelResource.Get)}")] public async Task ModelGet(string projectId, string modelId, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -190,6 +206,8 @@ public async Task ModelGet(string projectId, string modelId, Cancellatio /// /// /// The stream's id. + /// + [Obsolete($"Use client.{nameof(Model)}.{nameof(ModelResource.Update)}")] public async Task BranchUpdate(BranchUpdateInput branchInput, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -208,6 +226,8 @@ public async Task BranchUpdate(BranchUpdateInput branchInput, Cancellation /// /// /// + /// + [Obsolete($"Use client.{nameof(Model)}.{nameof(ModelResource.Delete)}")] public async Task BranchDelete(BranchDeleteInput branchInput, CancellationToken cancellationToken = default) { var request = new GraphQLRequest diff --git a/src/Speckle.Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.CommentOperations.cs b/src/Speckle.Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.CommentOperations.cs similarity index 89% rename from src/Speckle.Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.CommentOperations.cs rename to src/Speckle.Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.CommentOperations.cs index 20feaaf3..aebce10a 100644 --- a/src/Speckle.Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.CommentOperations.cs +++ b/src/Speckle.Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.CommentOperations.cs @@ -1,6 +1,8 @@ +using System; using System.Threading; using System.Threading.Tasks; using GraphQL; +using Speckle.Core.Api.GraphQL.Resources; namespace Speckle.Core.Api; @@ -14,6 +16,8 @@ public partial class Client /// Time to filter the comments with /// /// + /// + [Obsolete($"Use client.{nameof(CommentResource)}.{nameof(CommentResource.GetProjectComments)}")] public async Task StreamGetComments( string streamId, int limit = 25, @@ -78,6 +82,8 @@ public async Task StreamGetComments( /// Id of the stream to get the comment from /// /// + /// + [Obsolete($"Use client.{nameof(CommentResource)}.{nameof(CommentResource.GetProjectComments)}")] public async Task StreamGetCommentScreenshot( string id, string streamId, diff --git a/src/Speckle.Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.CommitOperations.cs b/src/Speckle.Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.CommitOperations.cs similarity index 85% rename from src/Speckle.Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.CommitOperations.cs rename to src/Speckle.Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.CommitOperations.cs index d76bc430..d267b762 100644 --- a/src/Speckle.Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.CommitOperations.cs +++ b/src/Speckle.Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.CommitOperations.cs @@ -1,7 +1,9 @@ +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using GraphQL; +using Speckle.Core.Api.GraphQL.Resources; namespace Speckle.Core.Api; @@ -14,6 +16,8 @@ public partial class Client /// Id of the commit to get /// /// + /// + [Obsolete($"Use client.{nameof(Version)}.{nameof(VersionResource.Get)}")] public async Task CommitGet(string streamId, string commitId, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -48,6 +52,8 @@ public async Task CommitGet(string streamId, string commitId, Cancellati /// Max number of commits to get /// /// The requested commits + /// + [Obsolete($"Use client.{nameof(Version)}.{nameof(VersionResource.GetVersions)}")] public async Task> StreamGetCommits( string streamId, int limit = 10, @@ -88,6 +94,8 @@ public async Task> StreamGetCommits( /// /// /// The commit id. + /// + [Obsolete($"Use client.{nameof(VersionResource)}.{nameof(VersionResource.Create)}")] public async Task CommitCreate(CommitCreateInput commitInput, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -106,6 +114,8 @@ public async Task CommitCreate(CommitCreateInput commitInput, Cancellati /// /// /// The stream's id. + /// + [Obsolete($"Use client.{nameof(VersionResource)}.{nameof(VersionResource.Update)}")] public async Task CommitUpdate(CommitUpdateInput commitInput, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -124,6 +134,8 @@ public async Task CommitUpdate(CommitUpdateInput commitInput, Cancellation /// /// /// + /// + [Obsolete($"Use client.{nameof(VersionResource)}.{nameof(VersionResource.Delete)}")] public async Task CommitDelete(CommitDeleteInput commitInput, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -143,6 +155,8 @@ public async Task CommitDelete(CommitDeleteInput commitInput, Cancellation /// /// /// + /// + [Obsolete($"Use client.{nameof(VersionResource)}.{nameof(VersionResource.Received)}")] public async Task CommitReceived( CommitReceivedInput commitReceivedInput, CancellationToken cancellationToken = default diff --git a/src/Speckle.Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ObjectOperations.cs b/src/Speckle.Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ObjectOperations.cs similarity index 99% rename from src/Speckle.Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ObjectOperations.cs rename to src/Speckle.Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ObjectOperations.cs index 79e9f8b9..0f842bc5 100644 --- a/src/Speckle.Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ObjectOperations.cs +++ b/src/Speckle.Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ObjectOperations.cs @@ -6,6 +6,7 @@ namespace Speckle.Core.Api; public partial class Client { + //TODO: API Gap /// /// Gets data about the requested Speckle object from a stream. /// diff --git a/src/Speckle.Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ServerOperations.cs b/src/Speckle.Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ServerOperations.cs similarity index 96% rename from src/Speckle.Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ServerOperations.cs rename to src/Speckle.Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ServerOperations.cs index 2244822d..1ceb07fa 100644 --- a/src/Speckle.Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.ServerOperations.cs +++ b/src/Speckle.Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ServerOperations.cs @@ -3,6 +3,7 @@ using System.Threading; using System.Threading.Tasks; using GraphQL; +using Speckle.Core.Api.GraphQL.Models.Responses; using Speckle.Core.Logging; namespace Speckle.Core.Api; diff --git a/src/Speckle.Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.StreamOperations.cs b/src/Speckle.Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.StreamOperations.cs similarity index 87% rename from src/Speckle.Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.StreamOperations.cs rename to src/Speckle.Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.StreamOperations.cs index f90a6849..dd7be354 100644 --- a/src/Speckle.Core/Api/GraphQL/Client.GraphqlCleintOperations/Client.StreamOperations.cs +++ b/src/Speckle.Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.StreamOperations.cs @@ -4,6 +4,9 @@ using System.Threading; using System.Threading.Tasks; using GraphQL; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Models.Responses; +using Speckle.Core.Api.GraphQL.Resources; using Speckle.Core.Logging; namespace Speckle.Core.Api; @@ -34,11 +37,11 @@ public async Task IsStreamAccessible(string id, CancellationToken cancella return stream.id == id; } - catch (SpeckleGraphQLForbiddenException) + catch (SpeckleGraphQLForbiddenException) { return false; } - catch (SpeckleGraphQLStreamNotFoundException) + catch (SpeckleGraphQLStreamNotFoundException) { return false; } @@ -52,6 +55,9 @@ public async Task IsStreamAccessible(string id, CancellationToken cancella /// Max number of branches to retrieve /// /// + /// + /// + [Obsolete($"Use client.{nameof(Project)}.{nameof(ProjectResource.GetWithModels)}")] public async Task StreamGet(string id, int branchesLimit = 10, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -100,6 +106,8 @@ public async Task StreamGet(string id, int branchesLimit = 10, Cancellat /// Max number of streams to return /// /// + /// + [Obsolete($"Use client.{nameof(ActiveUser)}.{nameof(ActiveUserResource.GetProjects)}")] public async Task> StreamsGet(int limit = 10, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -142,7 +150,7 @@ public async Task> StreamsGet(int limit = 10, CancellationToken can }}" }; - var res = await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false); + var res = await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false); if (res?.activeUser == null) { @@ -154,6 +162,7 @@ public async Task> StreamsGet(int limit = 10, CancellationToken can return res.activeUser.streams.items; } + //TODO: API GAP /// /// Gets all favorite streams for the current user /// @@ -201,7 +210,7 @@ public async Task> FavoriteStreamsGet(int limit = 10, CancellationT }} }}" }; - return (await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false)) + return (await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false)) .activeUser .favoriteStreams .items; @@ -214,6 +223,8 @@ public async Task> FavoriteStreamsGet(int limit = 10, CancellationT /// Max number of streams to return /// /// + /// + [Obsolete($"Use client.{nameof(ActiveUser)}.{nameof(ActiveUserResource.GetProjects)}")] public async Task> StreamSearch( string query, int limit = 10, @@ -258,6 +269,8 @@ public async Task> StreamSearch( /// /// /// The stream's id. + /// + [Obsolete($"Use client.{nameof(Project)}.{nameof(ProjectResource.Create)}")] public async Task StreamCreate(StreamCreateInput streamInput, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -275,6 +288,8 @@ public async Task StreamCreate(StreamCreateInput streamInput, Cancellati /// Note: the id field needs to be a valid stream id. /// /// The stream's id. + /// + [Obsolete($"Use client.{nameof(Project)}.{nameof(ProjectResource.Update)}")] public async Task StreamUpdate(StreamUpdateInput streamInput, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -294,6 +309,8 @@ public async Task StreamUpdate(StreamUpdateInput streamInput, Cancellation /// Id of the stream to be deleted /// /// + /// + [Obsolete($"Use client.{nameof(Project)}.{nameof(ProjectResource.Delete)}")] public async Task StreamDelete(string id, CancellationToken cancellationToken = default) { var request = new GraphQLRequest @@ -336,6 +353,8 @@ public async Task StreamRevokePermission( /// /// /// + /// + [Obsolete($"Use client.{nameof(Project)}.{nameof(ProjectResource.UpdateRole)}")] public async Task StreamUpdatePermission( StreamPermissionInput updatePermissionInput, CancellationToken cancellationToken = default @@ -362,6 +381,8 @@ mutation streamUpdatePermission($permissionParams: StreamUpdatePermissionInput!) /// /// /// + /// + [Obsolete($"Use client.{nameof(Project)}.{nameof(ProjectResource.GetWithTeam)}")] public async Task StreamGetPendingCollaborators( string streamId, CancellationToken cancellationToken = default @@ -396,6 +417,8 @@ public async Task StreamGetPendingCollaborators( /// /// /// + /// + [Obsolete($"Use client.{nameof(ProjectInvite)}.{nameof(ProjectInviteResource.Create)}")] public async Task StreamInviteCreate( StreamInviteCreateInput inviteCreateInput, CancellationToken cancellationToken = default @@ -427,6 +450,8 @@ mutation streamInviteCreate($input: StreamInviteCreateInput!) { /// Id of the invite to cancel /// /// + /// + [Obsolete($"Use client.{nameof(ProjectInvite)}.{nameof(ProjectInviteResource.Cancel)}")] public async Task StreamInviteCancel( string streamId, string inviteId, @@ -456,6 +481,8 @@ mutation streamInviteCancel( $streamId: String!, $inviteId: String! ) { /// /// /// + /// + [Obsolete($"Use client.{nameof(ProjectInvite)}.{nameof(ProjectInviteResource.Use)}")] public async Task StreamInviteUse( string streamId, string token, @@ -482,6 +509,13 @@ mutation streamInviteUse( $accept: Boolean!, $streamId: String!, $token: String! return (bool)res["streamInviteUse"]; } + /// + /// + /// + /// + /// + /// + [Obsolete($"Use client.{nameof(ActiveUser)}.{nameof(ActiveUserResource.ProjectInvites)}")] public async Task> GetAllPendingInvites(CancellationToken cancellationToken = default) { var request = new GraphQLRequest diff --git a/src/Speckle.Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.UserOperations.cs b/src/Speckle.Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.UserOperations.cs new file mode 100644 index 00000000..0ec16712 --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.UserOperations.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; + +namespace Speckle.Core.Api; + +public partial class Client +{ + /// + /// Gets the currently active user profile. + /// + /// + /// + /// + [Obsolete($"Use client.{nameof(ActiveUser)}.{nameof(ActiveUserResource.Get)}")] + public async Task ActiveUserGet(CancellationToken cancellationToken = default) + { + return await ActiveUser.Get(cancellationToken).ConfigureAwait(false); + } + + /// + /// Get another user's profile by its user id. + /// + /// Id of the user you are looking for + /// + /// + /// + [Obsolete($"Use client.{nameof(OtherUser)}.{nameof(OtherUserResource.Get)}")] + public async Task OtherUserGet(string id, CancellationToken cancellationToken = default) + { + return await OtherUser.Get(id, cancellationToken).ConfigureAwait(false); + } + + /// + /// Searches for a user on the server. + /// + /// String to search for. Must be at least 3 characters + /// Max number of users to return + /// + /// + [Obsolete($"Use client.{nameof(OtherUser)}.{nameof(OtherUserResource.UserSearch)}")] + public async Task> UserSearch( + string query, + int limit = 10, + CancellationToken cancellationToken = default + ) + { + var res = await OtherUser.UserSearch(query, limit, cancellationToken: cancellationToken).ConfigureAwait(false); + return res.items; + } +} diff --git a/src/Speckle.Core/Api/GraphQL/Client.Subscriptions/Client.Subscriptions.Branch.cs b/src/Speckle.Core/Api/GraphQL/Legacy/Client.Subscriptions/Client.Subscriptions.Branch.cs similarity index 100% rename from src/Speckle.Core/Api/GraphQL/Client.Subscriptions/Client.Subscriptions.Branch.cs rename to src/Speckle.Core/Api/GraphQL/Legacy/Client.Subscriptions/Client.Subscriptions.Branch.cs diff --git a/src/Speckle.Core/Api/GraphQL/Client.Subscriptions/Client.Subscriptions.Commit.cs b/src/Speckle.Core/Api/GraphQL/Legacy/Client.Subscriptions/Client.Subscriptions.Commit.cs similarity index 100% rename from src/Speckle.Core/Api/GraphQL/Client.Subscriptions/Client.Subscriptions.Commit.cs rename to src/Speckle.Core/Api/GraphQL/Legacy/Client.Subscriptions/Client.Subscriptions.Commit.cs diff --git a/src/Speckle.Core/Api/GraphQL/Client.Subscriptions/Client.Subscriptions.Stream.cs b/src/Speckle.Core/Api/GraphQL/Legacy/Client.Subscriptions/Client.Subscriptions.Stream.cs similarity index 100% rename from src/Speckle.Core/Api/GraphQL/Client.Subscriptions/Client.Subscriptions.Stream.cs rename to src/Speckle.Core/Api/GraphQL/Legacy/Client.Subscriptions/Client.Subscriptions.Stream.cs diff --git a/src/Speckle.Core/Api/GraphQL/Models.cs b/src/Speckle.Core/Api/GraphQL/Legacy/LegacyGraphQLModels.cs similarity index 59% rename from src/Speckle.Core/Api/GraphQL/Models.cs rename to src/Speckle.Core/Api/GraphQL/Legacy/LegacyGraphQLModels.cs index 9b609316..5dcff259 100644 --- a/src/Speckle.Core/Api/GraphQL/Models.cs +++ b/src/Speckle.Core/Api/GraphQL/Legacy/LegacyGraphQLModels.cs @@ -1,13 +1,20 @@ #nullable disable using System; using System.Collections.Generic; -using System.Runtime.InteropServices; -using Speckle.Newtonsoft.Json; +using Speckle.Core.Api.GraphQL.Enums; +using Speckle.Core.Api.GraphQL.Models; namespace Speckle.Core.Api; #region inputs +internal static class DeprecationMessages +{ + public const string FE2_DEPRECATION_MESSAGE = + $"Stream/Branch/Commit API is now deprecated, Use the new Project/Model/Version API functions in {nameof(Client)}"; +} + +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class StreamCreateInput { public string name { get; set; } @@ -15,6 +22,7 @@ public class StreamCreateInput public bool isPublic { get; set; } = true; } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class StreamUpdateInput { public string id { get; set; } @@ -23,6 +31,7 @@ public class StreamUpdateInput public bool isPublic { get; set; } = true; } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class StreamPermissionInput { public string streamId { get; set; } @@ -30,12 +39,14 @@ public class StreamPermissionInput public string role { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class StreamRevokePermissionInput { public string streamId { get; set; } public string userId { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class StreamInviteCreateInput { public string streamId { get; set; } @@ -45,6 +56,7 @@ public class StreamInviteCreateInput public string role { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class BranchCreateInput { public string streamId { get; set; } @@ -52,6 +64,7 @@ public class BranchCreateInput public string description { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class BranchUpdateInput { public string streamId { get; set; } @@ -60,6 +73,7 @@ public class BranchUpdateInput public string description { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class BranchDeleteInput { public string streamId { get; set; } @@ -80,6 +94,7 @@ public class CommitCreateInput public List previousCommitIds { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommitUpdateInput { public string streamId { get; set; } @@ -87,12 +102,14 @@ public class CommitUpdateInput public string message { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommitDeleteInput { public string streamId { get; set; } public string id { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommitReceivedInput { public string streamId { get; set; } @@ -103,6 +120,7 @@ public class CommitReceivedInput #endregion +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Stream { public string id { get; set; } @@ -147,6 +165,7 @@ public override string ToString() } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Collaborator { public string id { get; set; } @@ -160,24 +179,13 @@ public override string ToString() } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class StreamInvitesResponse { public List streamInvites { get; set; } } -public class PendingStreamCollaborator -{ - public string id { get; set; } - public string inviteId { get; set; } - public string streamId { get; set; } - public string streamName { get; set; } - public string title { get; set; } - public string role { get; set; } - public User invitedBy { get; set; } - public User user { get; set; } - public string token { get; set; } -} - +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Branches { public int totalCount { get; set; } @@ -185,6 +193,7 @@ public class Branches public List items { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Commits { public int totalCount { get; set; } @@ -192,6 +201,7 @@ public class Commits public List items { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Commit { public string id { get; set; } @@ -247,6 +257,7 @@ public class InfoCommit public string branchName { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class SpeckleObject { public string id { get; set; } @@ -256,6 +267,7 @@ public class SpeckleObject public DateTime createdAt { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Branch { public string id { get; set; } @@ -269,6 +281,7 @@ public override string ToString() } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Streams { public int totalCount { get; set; } @@ -276,51 +289,14 @@ public class Streams public List items { get; set; } } -public class UserBase -{ - public string id { get; set; } - public string name { get; set; } - public string bio { get; set; } - public string company { get; set; } - public string avatar { get; set; } - public bool verified { get; set; } - public string role { get; set; } - public Streams streams { get; set; } -} - -public class LimitedUser : UserBase -{ - public override string ToString() - { - return $"Other user profile: ({name} | {id})"; - } -} - -public class User : UserBase -{ - public string email { get; set; } - public Streams favoriteStreams { get; set; } - - public override string ToString() - { - return $"User ({email} | {name} | {id})"; - } -} - +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Resource { public string resourceId { get; set; } public ResourceType resourceType { get; set; } } -public enum ResourceType -{ - commit, - stream, - @object, - comment -} - +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Location { public double x { get; set; } @@ -328,105 +304,33 @@ public class Location public double z { get; set; } } -public class UserData -{ - public User user { get; set; } -} - -/// -/// GraphQL DTO model for active user data -/// -public class ActiveUserData -{ - /// - /// User profile of the active user. - /// - public User activeUser { get; set; } -} - -/// -/// GraphQL DTO model for limited user data. Mostly referring to other user's profile. -/// -public class LimitedUserData -{ - /// - /// The limited user profile of another (non active user) - /// - public LimitedUser otherUser { get; set; } -} - +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class UserSearchData { public UserSearch userSearch { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class UserSearch { public string cursor { get; set; } public List items { get; set; } } -public class ServerInfoResponse -{ - // TODO: server and user models are duplicated here and in Speckle.Core.Credentials.Responses - // a bit weird and unnecessary - shouldn't both Credentials and Api share the same models since they're - // all server models that should be consistent? am creating a new obj here as to not reference Credentials in - // this file but it should prob be refactored in the futrue - public ServerInfo serverInfo { get; set; } -} - -// TODO: prob remove and bring one level up and shared w Speckle.Core.Credentials -[ClassInterface(ClassInterfaceType.AutoDual)] -[ComVisible(true)] -public class ServerInfo -{ - public string name { get; set; } - public string company { get; set; } - public string version { get; set; } - public string adminContact { get; set; } - public string description { get; set; } - - /// - /// This field is not returned from the GQL API, - /// it should populated on construction from the response headers. - /// see - /// - public bool frontend2 { get; set; } - - /// - /// This field is not returned from the GQL API, - /// it should populated on construction. - /// see - /// - public string url { get; set; } - - public ServerMigration migration { get; set; } -} - -public class ServerMigration -{ - /// - /// New URI where this server is now deployed - /// - public Uri movedTo { get; set; } - - /// - /// Previous URI where this server used to be deployed - /// - public Uri movedFrom { get; set; } -} - +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class StreamData { public Stream stream { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class StreamsData { public Streams streams { get; set; } } #region comments +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Comments { public int totalCount { get; set; } @@ -434,16 +338,18 @@ public class Comments public List items { get; set; } } -public class CommentData +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] +public sealed class CommentData { - public Comments comments { get; set; } - public List camPos { get; set; } - public object filters { get; set; } - public Location location { get; set; } - public object selection { get; set; } - public object sectionBox { get; set; } + public Comments comments { get; init; } + public List camPos { get; init; } + public object filters { get; init; } + public Location location { get; init; } + public object selection { get; init; } + public object sectionBox { get; init; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommentItem { public string id { get; set; } @@ -460,6 +366,7 @@ public class CommentItem public List resources { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class ContentContent { public string Type { get; set; } @@ -468,116 +375,28 @@ public class ContentContent public string Text { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommentsData { public Comments comments { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommentItemData { public CommentItem comment { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommentActivityMessage { public string type { get; set; } public CommentItem comment { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommentActivityResponse { public CommentActivityMessage commentActivity { get; set; } } #endregion - -#region manager api - -public class Connector -{ - public List Versions { get; set; } = new(); -} - -public class Version -{ - public Version(string number, string url, Os os = Os.Win, Architecture architecture = Architecture.Any) - { - Number = number; - Url = url; - Date = DateTime.Now; - Prerelease = Number.Contains("-"); - Os = os; - Architecture = architecture; - } - - public string Number { get; set; } - public string Url { get; set; } - public Os Os { get; set; } - public Architecture Architecture { get; set; } = Architecture.Any; - public DateTime Date { get; set; } - - [JsonIgnore] - public string DateTimeAgo => Helpers.TimeAgo(Date); - - public bool Prerelease { get; set; } -} - -/// -/// OS -/// NOTE: do not edit order and only append new items as they are serialized to ints -/// -public enum Os -{ - Win, //0 - OSX, //1 - Linux, //2 - Any //3 -} - -/// -/// Architecture -/// NOTE: do not edit order and only append new items as they are serialized to ints -/// -public enum Architecture -{ - Any, //0 - Arm, //1 - Intel //2 -} - -//GHOST API -public class Meta -{ - public Pagination pagination { get; set; } -} - -public class Pagination -{ - public int page { get; set; } - public string limit { get; set; } - public int pages { get; set; } - public int total { get; set; } - public object next { get; set; } - public object prev { get; set; } -} - -public class Tags -{ - public List tags { get; set; } - public Meta meta { get; set; } -} - -public class Tag -{ - public string id { get; set; } - public string name { get; set; } - public string slug { get; set; } - public string description { get; set; } - public string feature_image { get; set; } - public string visibility { get; set; } - public string codeinjection_head { get; set; } - public object codeinjection_foot { get; set; } - public object canonical_url { get; set; } - public string accent_color { get; set; } - public string url { get; set; } -} -#endregion diff --git a/src/Speckle.Core/Api/GraphQL/Legacy/Manager.cs b/src/Speckle.Core/Api/GraphQL/Legacy/Manager.cs new file mode 100644 index 00000000..94c1585e --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Legacy/Manager.cs @@ -0,0 +1,98 @@ +#nullable disable +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Speckle.Core.Api.GraphQL; + +#region manager api + +public class Connector +{ + public List Versions { get; set; } = new(); +} + +public class ConnectorVersion +{ + public ConnectorVersion(string number, string url, Os os = Os.Win, Architecture architecture = Architecture.Any) + { + Number = number; + Url = url; + Date = DateTime.Now; + Prerelease = Number.Contains("-"); + Os = os; + Architecture = architecture; + } + + public string Number { get; set; } + public string Url { get; set; } + public Os Os { get; set; } + public Architecture Architecture { get; set; } = Architecture.Any; + public DateTime Date { get; set; } + + [JsonIgnore] + public string DateTimeAgo => Helpers.TimeAgo(Date); + + public bool Prerelease { get; set; } +} + +/// +/// OS +/// NOTE: do not edit order and only append new items as they are serialized to ints +/// +public enum Os +{ + Win, //0 + OSX, //1 + Linux, //2 + Any //3 +} + +/// +/// Architecture +/// NOTE: do not edit order and only append new items as they are serialized to ints +/// +public enum Architecture +{ + Any, //0 + Arm, //1 + Intel //2 +} + +//GHOST API +public class Meta +{ + public Pagination pagination { get; set; } +} + +public class Pagination +{ + public int page { get; set; } + public string limit { get; set; } + public int pages { get; set; } + public int total { get; set; } + public object next { get; set; } + public object prev { get; set; } +} + +public class Tags +{ + public List tags { get; set; } + public Meta meta { get; set; } +} + +public class Tag +{ + public string id { get; set; } + public string name { get; set; } + public string slug { get; set; } + public string description { get; set; } + public string feature_image { get; set; } + public string visibility { get; set; } + public string codeinjection_head { get; set; } + public object codeinjection_foot { get; set; } + public object canonical_url { get; set; } + public string accent_color { get; set; } + public string url { get; set; } +} +#endregion diff --git a/src/Speckle.Core/Api/GraphQL/SubscriptionModels.cs b/src/Speckle.Core/Api/GraphQL/Legacy/SubscriptionModels.cs similarity index 74% rename from src/Speckle.Core/Api/GraphQL/SubscriptionModels.cs rename to src/Speckle.Core/Api/GraphQL/Legacy/SubscriptionModels.cs index f1b253b6..f330899f 100644 --- a/src/Speckle.Core/Api/GraphQL/SubscriptionModels.cs +++ b/src/Speckle.Core/Api/GraphQL/Legacy/SubscriptionModels.cs @@ -5,6 +5,7 @@ namespace Speckle.Core.Api.SubscriptionModels; #region streams +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class StreamInfo { public string id { get; set; } @@ -13,16 +14,19 @@ public class StreamInfo public string sharedBy { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class UserStreamAddedResult { public StreamInfo userStreamAdded { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class StreamUpdatedResult { public StreamInfo streamUpdated { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class UserStreamRemovedResult { public StreamInfo userStreamRemoved { get; set; } @@ -31,6 +35,7 @@ public class UserStreamRemovedResult #region branches +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class BranchInfo { public string id { get; set; } @@ -40,16 +45,19 @@ public class BranchInfo public string authorId { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class BranchCreatedResult { public BranchInfo branchCreated { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class BranchUpdatedResult { public BranchInfo branchUpdated { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class BranchDeletedResult { public BranchInfo branchDeleted { get; set; } @@ -58,6 +66,7 @@ public class BranchDeletedResult #region commits +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommitInfo { public string id { get; set; } @@ -74,16 +83,19 @@ public class CommitInfo public IList previousCommitIds { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommitCreatedResult { public CommitInfo commitCreated { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommitUpdatedResult { public CommitInfo commitUpdated { get; set; } } +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class CommitDeletedResult { public CommitInfo commitDeleted { get; set; } diff --git a/src/Speckle.Core/Api/GraphQL/Models/Collections.cs b/src/Speckle.Core/Api/GraphQL/Models/Collections.cs new file mode 100644 index 00000000..3e4738aa --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Models/Collections.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; + +namespace Speckle.Core.Api.GraphQL.Models; + +public class ResourceCollection +{ + public int totalCount { get; init; } + + public List items { get; init; } + + public string? cursor { get; init; } +} + +public sealed class CommentReplyAuthorCollection : ResourceCollection { } + +public sealed class ProjectCommentCollection : ResourceCollection +{ + public int totalArchivedCount { get; init; } +} diff --git a/src/Speckle.Core/Api/GraphQL/Models/Comment.cs b/src/Speckle.Core/Api/GraphQL/Models/Comment.cs new file mode 100644 index 00000000..75da443d --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Models/Comment.cs @@ -0,0 +1,25 @@ +#nullable disable + +using System; +using System.Collections.Generic; + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class Comment +{ + public bool archived { get; init; } + public LimitedUser author { get; init; } + public string authorId { get; init; } + public DateTime createdAt { get; init; } + public bool hasParent { get; init; } + public string id { get; init; } + public Comment parent { get; init; } + public string rawText { get; init; } + public ResourceCollection replies { get; init; } + public CommentReplyAuthorCollection replyAuthors { get; init; } + public List resources { get; init; } + public string screenshot { get; init; } + public DateTime updatedAt { get; init; } + public DateTime? viewedAt { get; init; } + public List viewerResources { get; init; } +} diff --git a/src/Speckle.Core/Api/GraphQL/Models/FileUpload.cs b/src/Speckle.Core/Api/GraphQL/Models/FileUpload.cs new file mode 100644 index 00000000..8327e248 --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Models/FileUpload.cs @@ -0,0 +1,30 @@ +#nullable disable + +using System; +using Speckle.Core.Api.GraphQL.Enums; + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class FileUpload +{ + public string convertedCommitId { get; init; } + public DateTime convertedLastUpdate { get; init; } + public FileUploadConversionStatus convertedStatus { get; init; } + public string convertedVersionId { get; init; } + public string fileName { get; init; } + public int fileSize { get; init; } + public string fileType { get; init; } + public string id { get; init; } + public Model model { get; init; } + public string modelName { get; init; } + public string projectId { get; init; } + public bool uploadComplete { get; init; } + public DateTime uploadDate { get; init; } + public string userId { get; init; } + + [Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] + public string branchName { get; init; } + + [Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] + public string streamId { get; init; } +} diff --git a/src/Speckle.Core/Api/GraphQL/Models/Model.cs b/src/Speckle.Core/Api/GraphQL/Models/Model.cs new file mode 100644 index 00000000..1b4f001e --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Models/Model.cs @@ -0,0 +1,22 @@ +#nullable disable +using System; +using System.Collections.Generic; + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class Model +{ + public LimitedUser author { get; init; } + public List childrenTree { get; init; } + public ResourceCollection commentThreads { get; init; } + public DateTime createdAt { get; init; } + public string description { get; init; } + public string displayName { get; init; } + public string id { get; init; } + public string name { get; init; } + public List pendingImportedVersions { get; init; } + public Uri previewUrl { get; init; } + public DateTime updatedAt { get; init; } + public ResourceCollection versions { get; init; } + public Version version { get; init; } +} diff --git a/src/Speckle.Core/Api/GraphQL/Models/ModelsTreeItem.cs b/src/Speckle.Core/Api/GraphQL/Models/ModelsTreeItem.cs new file mode 100644 index 00000000..f0d6e499 --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Models/ModelsTreeItem.cs @@ -0,0 +1,17 @@ +#nullable disable + +using System; +using System.Collections.Generic; + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class ModelsTreeItem +{ + public List children { get; init; } + public string fullName { get; init; } + public bool hasChildren { get; init; } + public string id { get; init; } + public Model model { get; init; } + public string name { get; init; } + public DateTime updatedAt { get; init; } +} diff --git a/src/Speckle.Core/Api/GraphQL/Models/PendingStreamCollaborator.cs b/src/Speckle.Core/Api/GraphQL/Models/PendingStreamCollaborator.cs new file mode 100644 index 00000000..805c0231 --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Models/PendingStreamCollaborator.cs @@ -0,0 +1,25 @@ +#nullable disable +using System; + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class PendingStreamCollaborator +{ + public string id { get; init; } + public string inviteId { get; init; } + + public string projectId { get; init; } + + public string projectName { get; init; } + public string title { get; init; } + public string role { get; init; } + public LimitedUser invitedBy { get; init; } + public LimitedUser user { get; init; } + public string token { get; init; } + + [Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] + public string streamId { get; init; } + + [Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] + public string streamName { get; init; } +} diff --git a/src/Speckle.Core/Api/GraphQL/Models/Project.cs b/src/Speckle.Core/Api/GraphQL/Models/Project.cs new file mode 100644 index 00000000..537ceb4d --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Models/Project.cs @@ -0,0 +1,30 @@ +#nullable disable +using System; +using System.Collections.Generic; +using Speckle.Core.Api.GraphQL.Enums; + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class Project +{ + public bool AllowPublicComments { get; init; } + public ProjectCommentCollection commentThreads { get; init; } + public DateTime createdAt { get; init; } + public string description { get; init; } + public string id { get; init; } + public List invitedTeam { get; init; } + public ResourceCollection models { get; init; } + public string name { get; init; } + public List pendingImportedModels { get; init; } + public string role { get; init; } + public List sourceApps { get; init; } + public List team { get; init; } + public DateTime updatedAt { get; init; } + public ProjectVisibility visibility { get; init; } + + public List viewerResources { get; init; } + public ResourceCollection versions { get; init; } + public Model model { get; init; } + public List modelChildrenTree { get; init; } + public ResourceCollection modelsTree { get; init; } +} diff --git a/src/Speckle.Core/Api/GraphQL/Models/ProjectCollaborator.cs b/src/Speckle.Core/Api/GraphQL/Models/ProjectCollaborator.cs new file mode 100644 index 00000000..ea062831 --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Models/ProjectCollaborator.cs @@ -0,0 +1,9 @@ +#nullable disable + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class ProjectCollaborator +{ + public string role { get; init; } + public LimitedUser user { get; init; } +} diff --git a/src/Speckle.Core/Api/GraphQL/Models/ResourceIdentifier.cs b/src/Speckle.Core/Api/GraphQL/Models/ResourceIdentifier.cs new file mode 100644 index 00000000..670111ca --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Models/ResourceIdentifier.cs @@ -0,0 +1,10 @@ +#nullable disable +using Speckle.Core.Api.GraphQL.Enums; + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class ResourceIdentifier +{ + public string resourceId { get; init; } + public ResourceType resourceType { get; init; } +} diff --git a/src/Speckle.Core/Api/GraphQL/Models/Responses/MutationResponses.cs b/src/Speckle.Core/Api/GraphQL/Models/Responses/MutationResponses.cs new file mode 100644 index 00000000..b456d05e --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Models/Responses/MutationResponses.cs @@ -0,0 +1,42 @@ +namespace Speckle.Core.Api.GraphQL.Models.Responses; + +#nullable disable +internal sealed class ProjectMutation +{ + public Project create { get; init; } + public Project update { get; init; } + public bool delete { get; init; } + public ProjectInviteMutation invites { get; init; } + + public Project updateRole { get; init; } +} + +internal sealed class ProjectInviteMutation +{ + public Project create { get; init; } + public bool use { get; init; } + public Project cancel { get; init; } +} + +internal sealed class ModelMutation +{ + public Model create { get; init; } + public Model update { get; init; } + public bool delete { get; init; } +} + +internal sealed class VersionMutation +{ + public bool delete { get; init; } + public Model moveToModel { get; init; } + public Version update { get; init; } +} + +internal sealed class CommentMutation +{ + public bool archive { get; init; } + public Comment create { get; init; } + public Comment edit { get; init; } + public bool markViewed { get; init; } + public Comment reply { get; init; } +} diff --git a/src/Speckle.Core/Api/GraphQL/Models/Responses/Responses.cs b/src/Speckle.Core/Api/GraphQL/Models/Responses/Responses.cs new file mode 100644 index 00000000..e731f311 --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Models/Responses/Responses.cs @@ -0,0 +1,30 @@ +using Speckle.Newtonsoft.Json; + +namespace Speckle.Core.Api.GraphQL.Models.Responses; + +// This file holds simple records that represent the root GraphQL response data +// For this reason, we're keeping them internal, allowing us to be flexible without the concern for breaking. +// It also exposes fewer similarly named types to dependent assemblies + +internal record ProjectResponse([property: JsonRequired] Project project); + +internal record ActiveUserResponse(User? activeUser); + +internal record LimitedUserResponse(LimitedUser? otherUser); + +internal record ServerInfoResponse([property: JsonRequired] ServerInfo serverInfo); + +internal record ProjectMutationResponse([property: JsonRequired] ProjectMutation projectMutations); + +internal record ModelMutationResponse([property: JsonRequired] ModelMutation modelMutations); + +internal record VersionMutationResponse([property: JsonRequired] VersionMutation versionMutations); + +internal record ProjectInviteResponse(PendingStreamCollaborator? projectInvite); + +internal record UserSearchResponse([property: JsonRequired] ResourceCollection userSearch); + +//All of the above records could be replaced by either RequiredResponse or OptionalResponse, if we use an alias (see https://www.baeldung.com/graphql-field-name) +internal record RequiredResponse([property: JsonRequired] T data); + +internal record OptionalResponse(T? data); diff --git a/src/Speckle.Core/Api/GraphQL/Models/ServerInfo.cs b/src/Speckle.Core/Api/GraphQL/Models/ServerInfo.cs new file mode 100644 index 00000000..c0c36a5a --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Models/ServerInfo.cs @@ -0,0 +1,45 @@ +#nullable disable +using System; +using System.Runtime.InteropServices; + +namespace Speckle.Core.Api.GraphQL.Models; + +[ClassInterface(ClassInterfaceType.AutoDual)] +[ComVisible(true)] +public sealed class ServerInfo +{ + public string name { get; init; } + public string company { get; init; } + public string version { get; init; } + public string adminContact { get; init; } + public string description { get; init; } + + /// + /// This field is not returned from the GQL API, + /// it should be populated after construction from the response headers. + /// see + /// + public bool frontend2 { get; set; } + + /// + /// This field is not returned from the GQL API, + /// it should be populated after construction. + /// see + /// + public string url { get; set; } + + public ServerMigration migration { get; init; } +} + +public sealed class ServerMigration +{ + /// + /// New URI where this server is now deployed + /// + public Uri movedTo { get; set; } + + /// + /// Previous URI where this server used to be deployed + /// + public Uri movedFrom { get; set; } +} diff --git a/src/Speckle.Core/Api/GraphQL/Models/SubscriptionMessages.cs b/src/Speckle.Core/Api/GraphQL/Models/SubscriptionMessages.cs new file mode 100644 index 00000000..0ce6dc56 --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Models/SubscriptionMessages.cs @@ -0,0 +1,84 @@ +using System; +using Speckle.Core.Api.GraphQL.Enums; +using Speckle.Newtonsoft.Json; + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class UserProjectsUpdatedMessage : EventArgs +{ + [JsonRequired] + public string id { get; init; } + + [JsonRequired] + public UserProjectsUpdatedMessageType type { get; init; } + + public Project? project { get; init; } +} + +public sealed class ProjectCommentsUpdatedMessage : EventArgs +{ + [JsonRequired] + public string id { get; init; } + + [JsonRequired] + public ProjectCommentsUpdatedMessageType type { get; init; } + + public Comment? comment { get; init; } +} + +public sealed class ProjectFileImportUpdatedMessage : EventArgs +{ + [JsonRequired] + public string id { get; init; } + + [JsonRequired] + public ProjectFileImportUpdatedMessageType type { get; init; } + + public FileUpload? upload { get; init; } +} + +public sealed class ProjectModelsUpdatedMessage : EventArgs +{ + [JsonRequired] + public string id { get; init; } + + [JsonRequired] + public ProjectModelsUpdatedMessageType type { get; init; } + + public Model? model { get; init; } +} + +public sealed class ProjectPendingModelsUpdatedMessage : EventArgs +{ + [JsonRequired] + public string id { get; init; } + + [JsonRequired] + public ProjectPendingModelsUpdatedMessageType type { get; init; } + + public FileUpload? model { get; init; } +} + +public sealed class ProjectUpdatedMessage : EventArgs +{ + [JsonRequired] + public string id { get; init; } + + [JsonRequired] + public ProjectUpdatedMessageType type { get; init; } + + public Project? project { get; init; } +} + +public sealed class ProjectVersionsUpdatedMessage : EventArgs +{ + [JsonRequired] + public string id { get; init; } + + [JsonRequired] + public ProjectVersionsUpdatedMessageType type { get; init; } + + public string? modelId { get; init; } + + public Version? version { get; init; } +} diff --git a/src/Speckle.Core/Api/GraphQL/Models/User.cs b/src/Speckle.Core/Api/GraphQL/Models/User.cs new file mode 100644 index 00000000..f3d6b268 --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Models/User.cs @@ -0,0 +1,52 @@ +#nullable disable +using System; +using System.Collections.Generic; + +namespace Speckle.Core.Api.GraphQL.Models; + +public abstract class UserBase +{ + public ResourceCollection activity { get; init; } + public string avatar { get; init; } + public string bio { get; init; } + public string company { get; set; } + public string id { get; init; } + public string name { get; init; } + public string role { get; init; } + + public ResourceCollection timeline { get; init; } + public int totalOwnedStreamsFavorites { get; init; } + public bool? verified { get; init; } + + [Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] + public ResourceCollection commits { get; init; } + + [Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] + public ResourceCollection streams { get; init; } +} + +public sealed class LimitedUser : UserBase +{ + public override string ToString() + { + return $"Other user profile: ({name} | {id})"; + } +} + +public sealed class User : UserBase +{ + public DateTime? createdAt { get; init; } + public string email { get; init; } + public bool? hasPendingVerification { get; init; } + public bool? isOnboardingFinished { get; init; } + public List projectInvites { get; init; } + public ResourceCollection projects { get; init; } + + [Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] + public ResourceCollection favoriteStreams { get; init; } + + public override string ToString() + { + return $"User ({email} | {name} | {id})"; + } +} diff --git a/src/Speckle.Core/Api/GraphQL/Models/Version.cs b/src/Speckle.Core/Api/GraphQL/Models/Version.cs new file mode 100644 index 00000000..1aa46b0d --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Models/Version.cs @@ -0,0 +1,18 @@ +#nullable disable + +using System; + +namespace Speckle.Core.Api.GraphQL.Models; + +public sealed class Version +{ + public LimitedUser authorUser { get; init; } + public ResourceCollection commentThreads { get; init; } + public DateTime createdAt { get; init; } + public string id { get; init; } + public string message { get; init; } + public Model model { get; init; } + public Uri previewUrl { get; init; } + public string referencedObject { get; init; } + public string sourceApplication { get; init; } +} diff --git a/src/Speckle.Core/Api/GraphQL/Models/ViewerResourceGroup.cs b/src/Speckle.Core/Api/GraphQL/Models/ViewerResourceGroup.cs new file mode 100644 index 00000000..3ef11ed6 --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Models/ViewerResourceGroup.cs @@ -0,0 +1,11 @@ +#nullable disable + +using System.Collections.Generic; + +namespace Speckle.Core.Api.GraphQL.Models; + +public class ViewerResourceGroup +{ + public string identifier { get; init; } + public List items { get; init; } +} diff --git a/src/Speckle.Core/Api/GraphQL/Models/ViewerResourceItem.cs b/src/Speckle.Core/Api/GraphQL/Models/ViewerResourceItem.cs new file mode 100644 index 00000000..12fc34e2 --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Models/ViewerResourceItem.cs @@ -0,0 +1,10 @@ +#nullable disable + +namespace Speckle.Core.Api.GraphQL.Models; + +public class ViewerResourceItem +{ + public string modelId { get; init; } + public string objectId { get; init; } + public string versionId { get; init; } +} diff --git a/src/Speckle.Core/Api/GraphQL/Resources/ActiveUserResource.cs b/src/Speckle.Core/Api/GraphQL/Resources/ActiveUserResource.cs new file mode 100644 index 00000000..6da8610a --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Resources/ActiveUserResource.cs @@ -0,0 +1,165 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using GraphQL; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Models.Responses; + +namespace Speckle.Core.Api.GraphQL.Resources; + +public sealed class ActiveUserResource +{ + private readonly ISpeckleGraphQLClient _client; + + internal ActiveUserResource(ISpeckleGraphQLClient client) + { + _client = client; + } + + /// + /// Gets the currently active user profile. + /// + /// + /// + /// the requested user, or null if the user does not exist (i.e. was initialised with an unauthenticated account) + /// + public async Task Get(CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + query User { + activeUser { + id, + email, + name, + bio, + company, + avatar, + verified, + profiles, + role, + } + } + """; + var request = new GraphQLRequest { Query = QUERY }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return response.activeUser; + } + + /// Max number of projects to fetch + /// Optional cursor for pagination + /// Optional filter + /// + /// + /// + public async Task> GetProjects( + int limit = ServerLimits.DEFAULT_PAGINATION_REQUEST, + string? cursor = null, + UserProjectsFilter? filter = null, + CancellationToken cancellationToken = default + ) + { + //language=graphql + const string QUERY = """ + query User($limit : Int!, $cursor: String, $filter: UserProjectsFilter) { + activeUser { + projects(limit: $limit, cursor: $cursor, filter: $filter) { + totalCount + items { + id + name + description + visibility + allowPublicComments + role + createdAt + updatedAt + sourceApps + } + } + } + } + """; + var request = new GraphQLRequest + { + Query = QUERY, + Variables = new + { + limit, + cursor, + filter + } + }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + if (response.activeUser is null) + { + throw new SpeckleGraphQLException("GraphQL response indicated that the ActiveUser could not be found"); + } + + return response.activeUser.projects; + } + + /// + /// + /// + public async Task> ProjectInvites(CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + query ProjectInvites { + activeUser { + projectInvites { + id + inviteId + invitedBy { + avatar + bio + company + id + name + role + verified + } + projectId + projectName + role + streamId + streamName + title + token + user { + id, + name, + bio, + company, + verified, + role, + } + } + } + } + """; + + var request = new GraphQLRequest { Query = QUERY }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + if (response.activeUser is null) + { + throw new SpeckleGraphQLException("GraphQL response indicated that the ActiveUser could not be found"); + } + + return response.activeUser.projectInvites; + } +} diff --git a/src/Speckle.Core/Api/GraphQL/Resources/CommentResource.cs b/src/Speckle.Core/Api/GraphQL/Resources/CommentResource.cs new file mode 100644 index 00000000..bcdb4430 --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Resources/CommentResource.cs @@ -0,0 +1,279 @@ +using System.Threading; +using System.Threading.Tasks; +using GraphQL; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Models.Responses; + +namespace Speckle.Core.Api.GraphQL.Resources; + +public sealed class CommentResource +{ + private readonly ISpeckleGraphQLClient _client; + + internal CommentResource(ISpeckleGraphQLClient client) + { + _client = client; + } + + /// + /// Max number of comments to fetch + /// Optional cursor for pagination + /// Optional filter + /// Max number of comment replies to fetch + /// Optional cursor for pagination + /// + /// + /// + public async Task> GetProjectComments( + string projectId, + int limit = ServerLimits.DEFAULT_PAGINATION_REQUEST, + string? cursor = null, + ProjectCommentsFilter? filter = null, + int repliesLimit = ServerLimits.DEFAULT_PAGINATION_REQUEST, + string? repliesCursor = null, + CancellationToken cancellationToken = default + ) + { + //language=graphql + const string QUERY = """ + query CommentThreads($projectId: String!, $cursor: String, $limit: Int!, $filter: ProjectCommentsFilter, $repliesLimit: Int, $repliesCursor: String) { + project(id: $projectId) { + commentThreads(cursor: $cursor, limit: $limit, filter: $filter) { + cursor + totalArchivedCount + totalCount + items { + archived + authorId + createdAt + hasParent + id + rawText + replies(limit: $repliesLimit, cursor: $repliesCursor) { + cursor + items { + archived + authorId + createdAt + hasParent + id + rawText + updatedAt + viewedAt + } + totalCount + } + resources { + resourceId + resourceType + } + screenshot + updatedAt + viewedAt + viewerResources { + modelId + objectId + versionId + } + data + } + } + } + } + """; + + GraphQLRequest request = + new() + { + Query = QUERY, + Variables = new + { + projectId, + cursor, + limit, + filter, + repliesLimit, + repliesCursor, + } + }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return response.project.commentThreads; + } + + /// + /// This function only exists here to be able to integration tests the queries. + /// The process of creating a comment is more complex and javascript specific than we can expose to our SDKs at this time. + /// + /// + /// + /// + /// + internal async Task Create(CreateCommentInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation Mutation($input: CreateCommentInput!) { + data:commentMutations { + create(input: $input) { + archived + authorId + createdAt + hasParent + id + rawText + resources { + resourceId + resourceType + } + screenshot + updatedAt + viewedAt + viewerResources { + modelId + objectId + versionId + } + data + } + } + } + """; + GraphQLRequest request = new(QUERY, variables: new { input }); + var res = await _client + .ExecuteGraphQLRequest>(request, cancellationToken) + .ConfigureAwait(false); + return res.data.create; + } + + /// + /// + /// + /// + /// + internal async Task Edit(EditCommentInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation Mutation($input: EditCommentInput!) { + data:commentMutations { + edit(input: $input) { + archived + authorId + createdAt + hasParent + id + rawText + resources { + resourceId + resourceType + } + screenshot + updatedAt + viewedAt + viewerResources { + modelId + objectId + versionId + } + data + } + } + } + """; + GraphQLRequest request = new(QUERY, variables: new { input }); + var res = await _client + .ExecuteGraphQLRequest>(request, cancellationToken) + .ConfigureAwait(false); + return res.data.edit; + } + + /// + /// + /// + /// + /// + public async Task Archive(string commentId, bool archive = true, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation Mutation($commentId: String!, $archive: Boolean!) { + data:commentMutations { + archive(commentId: $commentId, archived: $archive) + } + } + """; + GraphQLRequest request = new(QUERY, variables: new { commentId, archive }); + var res = await _client + .ExecuteGraphQLRequest>(request, cancellationToken) + .ConfigureAwait(false); + return res.data.archive; + } + + /// + /// + /// + /// + public async Task MarkViewed(string commentId, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation Mutation($commentId: String!) { + data:commentMutations { + markViewed(commentId: $commentId) + } + } + """; + GraphQLRequest request = new(QUERY, variables: new { commentId }); + var res = await _client + .ExecuteGraphQLRequest>(request, cancellationToken) + .ConfigureAwait(false); + return res.data.markViewed; + } + + /// + /// + /// + /// + /// + internal async Task Reply(CreateCommentReplyInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation Mutation($input: CreateCommentReplyInput!) { + data:commentMutations { + reply(input: $input) { + archived + authorId + createdAt + hasParent + id + rawText + resources { + resourceId + resourceType + } + screenshot + updatedAt + viewedAt + viewerResources { + modelId + objectId + versionId + } + data + } + } + } + """; + GraphQLRequest request = new(QUERY, variables: new { input }); + var res = await _client + .ExecuteGraphQLRequest>(request, cancellationToken) + .ConfigureAwait(false); + return res.data.reply; + } +} diff --git a/src/Speckle.Core/Api/GraphQL/Resources/ModelResource.cs b/src/Speckle.Core/Api/GraphQL/Resources/ModelResource.cs new file mode 100644 index 00000000..ee986422 --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Resources/ModelResource.cs @@ -0,0 +1,309 @@ +using System.Threading; +using System.Threading.Tasks; +using GraphQL; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Models.Responses; + +namespace Speckle.Core.Api.GraphQL.Resources; + +public sealed class ModelResource +{ + private readonly ISpeckleGraphQLClient _client; + + internal ModelResource(ISpeckleGraphQLClient client) + { + _client = client; + } + + /// + /// + /// + /// + /// + /// + public async Task Get(string modelId, string projectId, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + query ModelGet($modelId: String!, $projectId: String!) { + project(id: $projectId) { + model(id: $modelId) { + id + name + previewUrl + updatedAt + description + displayName + createdAt + author { + avatar + bio + company + id + name + role + totalOwnedStreamsFavorites + verified + } + } + } + } + """; + var request = new GraphQLRequest { Query = QUERY, Variables = new { modelId, projectId } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return response.project.model; + } + + /// + /// + /// Max number of versions to fetch + /// Optional cursor for pagination + /// Optional versions filter + /// + /// + /// + /// + public async Task GetWithVersions( + string modelId, + string projectId, + int versionsLimit = ServerLimits.DEFAULT_PAGINATION_REQUEST, + string? versionsCursor = null, + ModelVersionsFilter? versionsFilter = null, + CancellationToken cancellationToken = default + ) + { + //language=graphql + const string QUERY = """ + query ModelGetWithVersions($modelId: String!, $projectId: String!, $versionsLimit: Int!, $versionsCursor: String, $versionsFilter: ModelVersionsFilter) { + project(id: $projectId) { + model(id: $modelId) { + id + name + previewUrl + updatedAt + versions(limit: $versionsLimit, cursor: $versionsCursor, filter: $versionsFilter) { + items { + id + referencedObject + message + sourceApplication + createdAt + previewUrl + authorUser { + totalOwnedStreamsFavorites + id + name + bio + company + verified + role + } + } + totalCount + cursor + } + description + displayName + createdAt + author { + avatar + bio + company + id + name + role + totalOwnedStreamsFavorites + verified + } + } + } + } + """; + + var request = new GraphQLRequest + { + Query = QUERY, + Variables = new + { + projectId, + modelId, + versionsLimit, + versionsCursor, + versionsFilter, + } + }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return response.project.model; + } + + /// + /// Max number of models to fetch + /// Optional cursor for pagination + /// Optional models filter + /// + /// + /// + public async Task> GetModels( + string projectId, + int modelsLimit = ServerLimits.DEFAULT_PAGINATION_REQUEST, + string? modelsCursor = null, + ProjectModelsFilter? modelsFilter = null, + CancellationToken cancellationToken = default + ) + { + //language=graphql + const string QUERY = """ + query ProjectGetWithModels($projectId: String!, $modelsLimit: Int!, $modelsCursor: String, $modelsFilter: ProjectModelsFilter) { + project(id: $projectId) { + models(limit: $modelsLimit, cursor: $modelsCursor, filter: $modelsFilter) { + items { + id + name + previewUrl + updatedAt + displayName + description + createdAt + } + totalCount + cursor + } + } + } + """; + GraphQLRequest request = + new() + { + Query = QUERY, + Variables = new + { + projectId, + modelsLimit, + modelsCursor, + modelsFilter + } + }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.project.models; + } + + /// + /// + /// + /// + public async Task Create(CreateModelInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation ModelCreate($input: CreateModelInput!) { + modelMutations { + create(input: $input) { + id + displayName + name + description + createdAt + updatedAt + previewUrl + author { + avatar + bio + company + id + name + role + totalOwnedStreamsFavorites + verified + } + } + } + } + """; + + GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; + + var res = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return res.modelMutations.create; + } + + /// + /// + /// + /// + public async Task Delete(DeleteModelInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation ModelDelete($input: DeleteModelInput!) { + modelMutations { + delete(input: $input) + } + } + """; + + GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; + + var res = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return res.modelMutations.delete; + } + + /// + /// + /// + /// + public async Task Update(UpdateModelInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation ModelUpdate($input: UpdateModelInput!) { + modelMutations { + update(input: $input) { + id + name + displayName + description + createdAt + updatedAt + previewUrl + author { + avatar + bio + company + id + name + role + totalOwnedStreamsFavorites + verified + } + } + } + } + """; + + GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; + + var res = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return res.modelMutations.update; + } +} diff --git a/src/Speckle.Core/Api/GraphQL/Resources/OtherUserResource.cs b/src/Speckle.Core/Api/GraphQL/Resources/OtherUserResource.cs new file mode 100644 index 00000000..c2695615 --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Resources/OtherUserResource.cs @@ -0,0 +1,108 @@ +using System.Threading; +using System.Threading.Tasks; +using GraphQL; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Models.Responses; + +namespace Speckle.Core.Api.GraphQL.Resources; + +public sealed class OtherUserResource +{ + private readonly ISpeckleGraphQLClient _client; + + internal OtherUserResource(ISpeckleGraphQLClient client) + { + _client = client; + } + + /// + /// + /// + /// + /// + /// the requested user, or null if the user does not exist + /// + public async Task Get(string id, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + query LimitedUser($id: String!) { + otherUser(id: $id){ + id, + name, + bio, + company, + avatar, + verified, + role, + } + } + """; + + var request = new GraphQLRequest { Query = QUERY, Variables = new { id } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return response.otherUser; + } + + /// + /// Searches for a user on the server. + /// + /// String to search for. Must be at least 3 characters + /// Max number of users to fetch + /// Optional cursor for pagination + /// + /// + /// + /// + /// + public async Task> UserSearch( + string query, + int limit = ServerLimits.DEFAULT_PAGINATION_REQUEST, + string? cursor = null, + bool archived = false, + bool emailOnly = false, + CancellationToken cancellationToken = default + ) + { + //language=graphql + const string QUERY = """ + query UserSearch($query: String!, $limit: Int!, $cursor: String, $archived: Boolean, $emailOnly: Boolean) { + userSearch(query: $query, limit: $limit, cursor: $cursor, archived: $archived, emailOnly: $emailOnly) { + cursor, + items { + id + name + bio + company + avatar + verified + role + } + } + } + """; + + var request = new GraphQLRequest + { + Query = QUERY, + Variables = new + { + query, + limit, + cursor, + archived, + emailOnly + } + }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return response.userSearch; + } +} diff --git a/src/Speckle.Core/Api/GraphQL/Resources/ProjectInviteResource.cs b/src/Speckle.Core/Api/GraphQL/Resources/ProjectInviteResource.cs new file mode 100644 index 00000000..547c7569 --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Resources/ProjectInviteResource.cs @@ -0,0 +1,260 @@ +using System.Threading; +using System.Threading.Tasks; +using GraphQL; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Models.Responses; + +namespace Speckle.Core.Api.GraphQL.Resources; + +public sealed class ProjectInviteResource +{ + private readonly ISpeckleGraphQLClient _client; + + internal ProjectInviteResource(ISpeckleGraphQLClient client) + { + _client = client; + } + + /// + /// + /// + /// + /// + public async Task Create( + string projectId, + ProjectInviteCreateInput input, + CancellationToken cancellationToken = default + ) + { + //language=graphql + const string QUERY = """ + mutation ProjectInviteCreate($projectId: ID!, $input: ProjectInviteCreateInput!) { + projectMutations { + invites { + create(projectId: $projectId, input: $input) { + id + name + description + visibility + allowPublicComments + role + createdAt + updatedAt + team { + role + user { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + } + invitedTeam { + id + inviteId + projectId + projectName + streamName + title + role + streamId + token + user { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + invitedBy { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + } + } + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { projectId, input } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return response.projectMutations.invites.create; + } + + /// + /// + /// + /// + public async Task Use(ProjectInviteUseInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation ProjectInviteUse($input: ProjectInviteUseInput!) { + projectMutations { + invites { + use(input: $input) + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.projectMutations.invites.use; + } + + /// + /// + /// + /// The invite, or null if no invite exists + /// + public async Task Get( + string projectId, + string? token, + CancellationToken cancellationToken = default + ) + { + //language=graphql + const string QUERY = """ + query ProjectInvite($projectId: String!, $token: String) { + projectInvite(projectId: $projectId, token: $token) { + id + inviteId + invitedBy { + avatar + bio + company + id + name + role + totalOwnedStreamsFavorites + verified + } + projectId + projectName + role + streamId + streamName + title + token + user { + avatar + bio + company + id + name + role + totalOwnedStreamsFavorites + verified + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { projectId, token } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return response.projectInvite; + } + + /// + /// + /// + /// + /// + public async Task Cancel(string projectId, string inviteId, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation ProjectInviteCancel($projectId: ID!, $inviteId: String!) { + projectMutations { + invites { + cancel(projectId: $projectId, inviteId: $inviteId) { + id + name + description + visibility + allowPublicComments + role + createdAt + updatedAt + team { + role + user { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + } + invitedTeam { + id + inviteId + projectId + projectName + streamName + title + role + streamId + token + user { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + invitedBy { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + } + } + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { projectId, inviteId } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return response.projectMutations.invites.cancel; + } +} diff --git a/src/Speckle.Core/Api/GraphQL/Resources/ProjectResource.cs b/src/Speckle.Core/Api/GraphQL/Resources/ProjectResource.cs new file mode 100644 index 00000000..34c505ec --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Resources/ProjectResource.cs @@ -0,0 +1,349 @@ +using System.Threading; +using System.Threading.Tasks; +using GraphQL; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Models.Responses; + +namespace Speckle.Core.Api.GraphQL.Resources; + +public sealed class ProjectResource +{ + private readonly ISpeckleGraphQLClient _client; + + internal ProjectResource(ISpeckleGraphQLClient client) + { + _client = client; + } + + /// + /// + /// + /// + /// + /// + public async Task Get(string projectId, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + query Project($projectId: String!) { + project(id: $projectId) { + id + name + description + visibility + allowPublicComments + role + createdAt + updatedAt + sourceApps + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { projectId } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.project; + } + + /// + /// Max number of models to fetch + /// Optional cursor for pagination + /// Optional models filter + /// + /// + /// + /// + /// + public async Task GetWithModels( + string projectId, + int modelsLimit = ServerLimits.DEFAULT_PAGINATION_REQUEST, + string? modelsCursor = null, + ProjectModelsFilter? modelsFilter = null, + CancellationToken cancellationToken = default + ) + { + //language=graphql + const string QUERY = """ + query ProjectGetWithModels($projectId: String!, $modelsLimit: Int!, $modelsCursor: String, $modelsFilter: ProjectModelsFilter) { + project(id: $projectId) { + id + name + description + visibility + allowPublicComments + role + createdAt + updatedAt + sourceApps + models(limit: $modelsLimit, cursor: $modelsCursor, filter: $modelsFilter) { + items { + id + name + previewUrl + updatedAt + displayName + description + createdAt + } + cursor + totalCount + } + } + } + """; + GraphQLRequest request = + new() + { + Query = QUERY, + Variables = new + { + projectId, + modelsLimit, + modelsCursor, + modelsFilter + } + }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.project; + } + + /// + /// + /// + /// + /// + /// + public async Task GetWithTeam(string projectId, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + query ProjectGetWithTeam($projectId: String!) { + project(id: $projectId) { + id + name + description + visibility + allowPublicComments + role + createdAt + updatedAt + team { + role + user { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + } + invitedTeam { + id + inviteId + projectId + projectName + streamId + streamName + title + role + token + user { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + invitedBy { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { projectId } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.project; + } + + /// + /// + /// + /// + public async Task Create(ProjectCreateInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation ProjectCreate($input: ProjectCreateInput) { + projectMutations { + create(input: $input) { + id + name + description + visibility + allowPublicComments + role + createdAt + updatedAt + sourceApps + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.projectMutations.create; + } + + /// + /// + /// + /// + public async Task Update(ProjectUpdateInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation ProjectUpdate($input: ProjectUpdateInput!) { + projectMutations{ + update(update: $input) { + id + name + description + visibility + allowPublicComments + role + createdAt + updatedAt + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.projectMutations.update; + } + + /// The id of the Project to delete + /// + /// + /// + public async Task Delete(string projectId, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation ProjectDelete($projectId: String!) { + projectMutations { + delete(id: $projectId) + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { projectId } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.projectMutations.delete; + } + + /// + /// + /// + public async Task UpdateRole(ProjectUpdateRoleInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation ProjectUpdateRole($input: ProjectUpdateRoleInput!) { + projectMutations { + updateRole(input: $input) { + id + name + description + visibility + allowPublicComments + role + createdAt + updatedAt + team { + role + user { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + } + invitedTeam { + id + inviteId + projectId + projectName + streamId + streamName + title + role + token + user { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + invitedBy { + totalOwnedStreamsFavorites + id + name + bio + company + avatar + verified + role + } + } + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.projectMutations.updateRole; + } +} diff --git a/src/Speckle.Core/Api/GraphQL/Resources/SubscriptionResource.cs b/src/Speckle.Core/Api/GraphQL/Resources/SubscriptionResource.cs new file mode 100644 index 00000000..37c8154b --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Resources/SubscriptionResource.cs @@ -0,0 +1,216 @@ +using System; +using System.Collections.Generic; +using GraphQL; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Models.Responses; + +namespace Speckle.Core.Api.GraphQL.Resources; + +public sealed class Subscription : IDisposable + where TEventArgs : EventArgs +{ + internal Subscription(ISpeckleGraphQLClient client, GraphQLRequest request) + { + _subscription = client.SubscribeTo>(request, (o, t) => Listeners?.Invoke(o, t.data)); + } + + public event EventHandler? Listeners; + + private readonly IDisposable _subscription; + + public void Dispose() + { + _subscription.Dispose(); + } +} + +public sealed class SubscriptionResource : IDisposable +{ + private readonly ISpeckleGraphQLClient _client; + private readonly List _subscriptions; + + internal SubscriptionResource(ISpeckleGraphQLClient client) + { + _client = client; + _subscriptions = new(); + } + + /// Track newly added or deleted projects owned by the active user + /// + /// You should add event listeners to the returned object.
+ /// You can add multiple listeners to a , and this should be preferred over creating many subscriptions.
+ /// You should ensure proper disposal of the when you're done (see )
+ /// Disposing of the or will also dispose any s it created. + ///
+ /// + public Subscription CreateUserProjectsUpdatedSubscription() + { + //language=graphql + const string QUERY = """ + subscription UserProjectsUpdated { + data:userProjectsUpdated { + id + project { + id + } + type + } + } + """; + GraphQLRequest request = new() { Query = QUERY }; + + Subscription subscription = new(_client, request); + _subscriptions.Add(subscription); + return subscription; + } + + /// Subscribe to updates to resource comments/threads. Optionally specify resource ID string to only receive updates regarding comments for those resources + /// + /// + public Subscription CreateProjectCommentsUpdatedSubscription( + ViewerUpdateTrackingTarget target + ) + { + //language=graphql + const string QUERY = """ + subscription Subscription($target: ViewerUpdateTrackingTarget!) { + data:projectCommentsUpdated(target: $target) { + comment { + id + } + id + type + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { target } }; + + Subscription subscription = new(_client, request); + _subscriptions.Add(subscription); + return subscription; + } + + /// Subscribe to changes to a project's models. Optionally specify to track + /// + /// + public Subscription CreateProjectModelsUpdatedSubscription( + string id, + IReadOnlyList? modelIds = null + ) + { + //language=graphql + const string QUERY = """ + subscription ProjectModelsUpdated($id: String!, $modelIds: [String!]) { + data:projectModelsUpdated(id: $id, modelIds: $modelIds) { + id + model { + id + name + previewUrl + updatedAt + description + displayName + createdAt + author { + avatar + bio + company + id + name + role + totalOwnedStreamsFavorites + verified + } + } + type + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { id, modelIds } }; + + Subscription subscription = new(_client, request); + _subscriptions.Add(subscription); + return subscription; + } + + /// Track updates to a specific project + /// + /// + public Subscription CreateProjectUpdatedSubscription(string id) + { + //language=graphql + const string QUERY = """ + subscription ProjectUpdated($id: String!) { + data:projectUpdated(id: $id) { + id + project { + id + name + description + visibility + allowPublicComments + role + createdAt + updatedAt + sourceApps + } + type + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { id } }; + + Subscription subscription = new(_client, request); + _subscriptions.Add(subscription); + return subscription; + } + + /// Subscribe to changes to a project's versions. + /// + /// + public Subscription CreateProjectVersionsUpdatedSubscription(string id) + { + //language=graphql + const string QUERY = """ + subscription ProjectVersionsUpdated($id: String!) { + data:projectVersionsUpdated(id: $id) { + id + modelId + type + version { + id + referencedObject + message + sourceApplication + createdAt + previewUrl + authorUser { + totalOwnedStreamsFavorites + id + name + bio + company + verified + role + avatar + } + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { id } }; + + Subscription subscription = new(_client, request); + _subscriptions.Add(subscription); + return subscription; + } + + public void Dispose() + { + foreach (var subscription in _subscriptions) + { + subscription.Dispose(); + } + } +} diff --git a/src/Speckle.Core/Api/GraphQL/Resources/VersionResource.cs b/src/Speckle.Core/Api/GraphQL/Resources/VersionResource.cs new file mode 100644 index 00000000..7aeb385d --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Resources/VersionResource.cs @@ -0,0 +1,252 @@ +using System.Threading; +using System.Threading.Tasks; +using GraphQL; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Models.Responses; +using Version = Speckle.Core.Api.GraphQL.Models.Version; + +namespace Speckle.Core.Api.GraphQL.Resources; + +public sealed class VersionResource +{ + private readonly ISpeckleGraphQLClient _client; + + internal VersionResource(ISpeckleGraphQLClient client) + { + _client = client; + } + + /// + /// + /// + /// + /// + /// + public async Task Get( + string versionId, + string modelId, + string projectId, + CancellationToken cancellationToken = default + ) + { + //language=graphql + const string QUERY = """ + query VersionGet($projectId: String!, $modelId: String!, $versionId: String!) { + project(id: $projectId) { + model(id: $modelId) { + version(id: $versionId) { + id + referencedObject + message + sourceApplication + createdAt + previewUrl + authorUser { + totalOwnedStreamsFavorites + id + name + bio + company + verified + role + avatar + } + } + } + } + } + """; + GraphQLRequest request = + new() + { + Query = QUERY, + Variables = new + { + projectId, + modelId, + versionId + } + }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.project.model.version; + } + + /// + /// + /// Max number of versions to fetch + /// Optional cursor for pagination + /// Optional filter + /// + /// + public async Task> GetVersions( + string modelId, + string projectId, + int limit = ServerLimits.DEFAULT_PAGINATION_REQUEST, + string? cursor = null, + ModelVersionsFilter? filter = null, + CancellationToken cancellationToken = default + ) + { + //language=graphql + const string QUERY = """ + query VersionGetVersions($projectId: String!, $modelId: String!, $limit: Int!, $cursor: String, $filter: ModelVersionsFilter) { + project(id: $projectId) { + model(id: $modelId) { + versions(limit: $limit, cursor: $cursor, filter: $filter) { + items { + id + referencedObject + message + sourceApplication + createdAt + previewUrl + authorUser { + totalOwnedStreamsFavorites + id + name + bio + company + verified + role + avatar + } + } + cursor + totalCount + } + } + } + } + """; + + GraphQLRequest request = + new() + { + Query = QUERY, + Variables = new + { + projectId, + modelId, + limit, + cursor, + filter, + } + }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.project.model.versions; + } + + /// + /// + /// + public async Task Create(CommitCreateInput input, CancellationToken cancellationToken = default) + { + //TODO: Implement on server + return await ((Client)_client).CommitCreate(input, cancellationToken).ConfigureAwait(false); + } + + /// + /// + /// + public async Task Update(UpdateVersionInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation VersionUpdate($input: UpdateVersionInput!) { + versionMutations { + update(input: $input) { + id + referencedObject + message + sourceApplication + createdAt + previewUrl + authorUser { + totalOwnedStreamsFavorites + id + name + bio + company + verified + role + avatar + } + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { input, } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.versionMutations.update; + } + + //TODO: Would we rather return the full model here? with or with out versions? + /// + /// + /// + /// + public async Task MoveToModel(MoveVersionsInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation VersionMoveToModel($input: MoveVersionsInput!) { + versionMutations { + moveToModel(input: $input) { + id + } + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { input, } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + return response.versionMutations.moveToModel.id; + } + + /// + /// + /// + public async Task Delete(DeleteVersionsInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation VersionDelete($input: DeleteVersionsInput!) { + versionMutations { + delete(input: $input) + } + } + """; + GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; + + var response = await _client + .ExecuteGraphQLRequest(request, cancellationToken) + .ConfigureAwait(false); + + return response.versionMutations.delete; + } + + /// + /// + /// + /// + public async Task Received( + CommitReceivedInput commitReceivedInput, + CancellationToken cancellationToken = default + ) + { + //TODO: Implement on server + return await ((Client)_client).CommitReceived(commitReceivedInput, cancellationToken).ConfigureAwait(false); + } +} diff --git a/src/Speckle.Core/Api/GraphQL/Resources/graphql.config.yml b/src/Speckle.Core/Api/GraphQL/Resources/graphql.config.yml new file mode 100644 index 00000000..64c50ab2 --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/Resources/graphql.config.yml @@ -0,0 +1,2 @@ +schema: https://app.speckle.systems/graphql +documents: '**/*.graphql' diff --git a/src/Speckle.Core/Api/GraphQL/StreamRoles.cs b/src/Speckle.Core/Api/GraphQL/StreamRoles.cs new file mode 100644 index 00000000..963fd2dd --- /dev/null +++ b/src/Speckle.Core/Api/GraphQL/StreamRoles.cs @@ -0,0 +1,12 @@ +namespace Speckle.Core.Api.GraphQL; + +/// +/// These are the default roles used by the server +/// +public static class StreamRoles +{ + public const string STREAM_OWNER = "stream:owner"; + public const string STREAM_CONTRIBUTOR = "stream:contributor"; + public const string STREAM_REVIEWER = "stream:reviewer"; + public const string? REVOKE = null; +} diff --git a/src/Speckle.Core/Api/Helpers.cs b/src/Speckle.Core/Api/Helpers.cs index 313e1b81..04d6201d 100644 --- a/src/Speckle.Core/Api/Helpers.cs +++ b/src/Speckle.Core/Api/Helpers.cs @@ -10,6 +10,8 @@ using System.Runtime.InteropServices; using System.Text.Json; using System.Threading.Tasks; +using Speckle.Core.Api.GraphQL; +using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Credentials; using Speckle.Core.Helpers; using Speckle.Core.Kits; diff --git a/src/Speckle.Core/Api/ServerLimits.cs b/src/Speckle.Core/Api/ServerLimits.cs index 7f78def9..262d1a63 100644 --- a/src/Speckle.Core/Api/ServerLimits.cs +++ b/src/Speckle.Core/Api/ServerLimits.cs @@ -11,4 +11,7 @@ public static class ServerLimits { public const int BRANCH_GET_LIMIT = 500; public const int OLD_BRANCH_GET_LIMIT = 100; + + /// the default `limit` argument value for paginated requests + public const int DEFAULT_PAGINATION_REQUEST = 25; } diff --git a/src/Speckle.Core/Credentials/Account.cs b/src/Speckle.Core/Credentials/Account.cs index e3ab97ce..2003b44c 100644 --- a/src/Speckle.Core/Credentials/Account.cs +++ b/src/Speckle.Core/Credentials/Account.cs @@ -2,7 +2,7 @@ using System; using System.Runtime.InteropServices; using System.Threading.Tasks; -using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Common; using Speckle.Core.Helpers; diff --git a/src/Speckle.Core/Credentials/AccountManager.cs b/src/Speckle.Core/Credentials/AccountManager.cs index 7457fa62..19ce07e8 100644 --- a/src/Speckle.Core/Credentials/AccountManager.cs +++ b/src/Speckle.Core/Credentials/AccountManager.cs @@ -15,6 +15,8 @@ using GraphQL.Client.Http; using Speckle.Core.Api; using Speckle.Core.Api.GraphQL; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Models.Responses; using Speckle.Core.Api.GraphQL.Serializer; using Speckle.Core.Common; using Speckle.Core.Helpers; @@ -115,22 +117,29 @@ public static async Task GetUserInfo( new NewtonsoftJsonSerializer(), httpClient ); - //language=graphql - var request = new GraphQLRequest { Query = " query { activeUser { name email id company } }" }; + const string QUERY = """ + query { + data:activeUser { + name + email + id + company + } + } + """; + var request = new GraphQLRequest { Query = QUERY }; - var response = await gqlClient.SendQueryAsync(request, cancellationToken).ConfigureAwait(false); + var response = await gqlClient + .SendQueryAsync>(request, cancellationToken) + .ConfigureAwait(false); if (response.Errors != null) { - throw new SpeckleGraphQLException( - $"GraphQL request {nameof(GetUserInfo)} failed", - request, - response - ); + throw new SpeckleGraphQLException($"GraphQL request {nameof(GetUserInfo)} failed", request, response); } - return response.Data.activeUser; + return response.Data.data; } /// @@ -272,21 +281,25 @@ public static async Task UpgradeAccount(string id) await s_accountStorage.WriteComplete().ConfigureAwait(false); } + public static IEnumerable GetAccounts(string serverUrl) + { + return GetAccounts(new Uri(serverUrl)); + } + /// /// Returns all unique accounts matching the serverUrl provided. If an account exists on more than one server, /// typically because it has been migrated, then only the upgraded account (and therefore server) are returned. /// Accounts are deemed to be the same when the Account.Id matches. /// - /// - /// - public static IEnumerable GetAccounts(string serverUrl) + /// Uri for server. + public static IEnumerable GetAccounts(Uri serverUrl) { var accounts = GetAccounts().ToList(); List filtered = new(); foreach (var acc in accounts) { - if (acc.serverInfo?.migration?.movedFrom == new Uri(serverUrl)) + if (acc.serverInfo?.migration?.movedFrom == serverUrl) { filtered.Add(acc); } @@ -296,7 +309,7 @@ public static IEnumerable GetAccounts(string serverUrl) { // we use the userInfo to detect the same account rather than the account.id // which should NOT match for essentially the same accounts but on different servers - i.e. FE1 & FE2 - if (acc.serverInfo.url == serverUrl && !filtered.Any(x => x.userInfo.id == acc.userInfo.id)) + if (new Uri(acc.serverInfo.url) == serverUrl && !filtered.Any(x => x.userInfo.id == acc.userInfo.id)) { filtered.Add(acc); } diff --git a/src/Speckle.Core/Credentials/Responses.cs b/src/Speckle.Core/Credentials/Responses.cs index 04eb87cf..066dfde6 100644 --- a/src/Speckle.Core/Credentials/Responses.cs +++ b/src/Speckle.Core/Credentials/Responses.cs @@ -1,56 +1,42 @@ -#nullable disable using System; using System.Runtime.InteropServices; using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; namespace Speckle.Core.Credentials; -[Obsolete("Use activeUser query and ActiveUserServerInfoResponse instead", true)] -public class UserServerInfoResponse +internal sealed class ActiveUserServerInfoResponse { - public UserInfo user { get; set; } - public ServerInfo serverInfo { get; set; } + public UserInfo activeUser { get; init; } + public ServerInfo serverInfo { get; init; } } -public class ActiveUserServerInfoResponse +internal sealed class TokenExchangeResponse { - public UserInfo activeUser { get; set; } - public ServerInfo serverInfo { get; set; } -} - -[Obsolete("Use activeUser query and ActiveUserResponse instead", true)] -public class UserInfoResponse -{ - public UserInfo user { get; set; } -} - -public class ActiveUserResponse -{ - public UserInfo activeUser { get; set; } + public string token { get; init; } + public string refreshToken { get; init; } } [ClassInterface(ClassInterfaceType.AutoDual)] [ComVisible(true)] -public class UserInfo +public sealed class UserInfo { - public string id { get; set; } - public string name { get; set; } - public string email { get; set; } - public string company { get; set; } - public string avatar { get; set; } + public string id { get; init; } + public string name { get; init; } + public string email { get; init; } + public string? company { get; init; } + public string? avatar { get; init; } - public Streams streams { get; set; } - public Commits commits { get; set; } -} + [Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] + public Streams streams { get; init; } -public class TokenExchangeResponse -{ - public string token { get; set; } - public string refreshToken { get; set; } + [Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] + public Commits commits { get; init; } } [ClassInterface(ClassInterfaceType.AutoDual)] [ComVisible(true)] +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Streams { public int totalCount { get; set; } @@ -58,6 +44,7 @@ public class Streams [ClassInterface(ClassInterfaceType.AutoDual)] [ComVisible(true)] +[Obsolete(DeprecationMessages.FE2_DEPRECATION_MESSAGE)] public class Commits { public int totalCount { get; set; } diff --git a/src/Speckle.Core/Credentials/StreamWrapper.cs b/src/Speckle.Core/Credentials/StreamWrapper.cs index da169517..1619359a 100644 --- a/src/Speckle.Core/Credentials/StreamWrapper.cs +++ b/src/Speckle.Core/Credentials/StreamWrapper.cs @@ -417,7 +417,7 @@ public async Task ValidateWithAccount(Account acc) // Check if the branch exists if (Type == StreamWrapperType.Branch) { - var branch = await client.BranchGet(StreamId, BranchName!, 1).ConfigureAwait(false); + var branch = await client.BranchGet(StreamId, BranchName.NotNull(), 1).ConfigureAwait(false); if (branch == null) { throw new SpeckleException( diff --git a/src/Speckle.Core/Helpers/Http.cs b/src/Speckle.Core/Helpers/Http.cs index 3339bdb9..f1b94b2e 100644 --- a/src/Speckle.Core/Helpers/Http.cs +++ b/src/Speckle.Core/Helpers/Http.cs @@ -12,6 +12,7 @@ using Polly.Extensions.Http; using Polly.Retry; using Serilog.Context; +using Speckle.Core.Common; using Speckle.Core.Credentials; using Speckle.Core.Logging; @@ -165,7 +166,7 @@ public static bool CanAddAuth(string? authToken, out string? bearerHeader) { if (!string.IsNullOrEmpty(authToken)) { - bearerHeader = authToken!.ToLowerInvariant().Contains("bearer") ? authToken : $"Bearer {authToken}"; + bearerHeader = authToken.NotNull().ToLowerInvariant().Contains("bearer") ? authToken : $"Bearer {authToken}"; return true; } @@ -236,7 +237,7 @@ CancellationToken cancellationToken ); if (policyResult.Outcome == OutcomeType.Successful) { - return policyResult.Result!; + return policyResult.Result.NotNull(); } // if the policy failed due to a cancellation, AND it was our cancellation token, then don't wrap the exception, and rethrow an new cancellation diff --git a/src/Speckle.Core/Logging/SpeckleLog.cs b/src/Speckle.Core/Logging/SpeckleLog.cs index 1da1a006..927bf75e 100644 --- a/src/Speckle.Core/Logging/SpeckleLog.cs +++ b/src/Speckle.Core/Logging/SpeckleLog.cs @@ -8,6 +8,7 @@ using Serilog.Core; using Serilog.Events; using Serilog.Exceptions; +using Speckle.Core.Common; using Speckle.Core.Credentials; using Speckle.Core.Helpers; @@ -104,7 +105,7 @@ public static ILogger Logger Initialize("Speckle.Core", "unknown"); } - return s_logger!; + return s_logger.NotNull(); } } diff --git a/src/Speckle.Core/Models/Base.cs b/src/Speckle.Core/Models/Base.cs index 84d84030..b5da1ed6 100644 --- a/src/Speckle.Core/Models/Base.cs +++ b/src/Speckle.Core/Models/Base.cs @@ -7,6 +7,7 @@ using System.Reflection; using System.Text.RegularExpressions; using Speckle.Core.Api; +using Speckle.Core.Common; using Speckle.Core.Helpers; using Speckle.Core.Kits; using Speckle.Core.Logging; @@ -65,7 +66,7 @@ public virtual string speckle_type if (_type == null) { List bases = new(); - Type myType = GetType(); + var myType = GetType().NotNull(); while (myType.Name != nameof(Base)) { @@ -74,7 +75,7 @@ public virtual string speckle_type bases.Add(myType.FullName); } - myType = myType.BaseType!; + myType = myType.BaseType; } if (bases.Count == 0) @@ -104,11 +105,11 @@ public virtual string speckle_type /// the resulting id (hash) public string GetId(bool decompose = false) { - var transports = decompose ? new[] { new MemoryTransport() } : Array.Empty(); + var transports = decompose ? [new MemoryTransport()] : Array.Empty(); var serializer = new BaseObjectSerializerV2(transports); string obj = serializer.Serialize(this); - return JObject.Parse(obj).GetValue(nameof(id))!.ToString(); + return JObject.Parse(obj).GetValue(nameof(id))?.ToString() ?? string.Empty; } /// diff --git a/src/Speckle.Core/Models/GraphTraversal/RuleBuilder.cs b/src/Speckle.Core/Models/GraphTraversal/RuleBuilder.cs index 2482464a..2012f805 100644 --- a/src/Speckle.Core/Models/GraphTraversal/RuleBuilder.cs +++ b/src/Speckle.Core/Models/GraphTraversal/RuleBuilder.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Speckle.Core.Common; namespace Speckle.Core.Models.GraphTraversal; @@ -52,7 +53,7 @@ bool ITraversalRule.DoesRuleHold(Base o) IEnumerable ITraversalRule.MembersToTraverse(Base o) { - return _membersToTraverse!(o).Distinct(); //TODO distinct is expensive, there may be a better way for us to avoid duplicates + return _membersToTraverse.NotNull()(o).Distinct(); //TODO distinct is expensive, there may be a better way for us to avoid duplicates } /// a new Traversal Rule to be initialised using the Builder Pattern interfaces diff --git a/src/Speckle.Core/Models/Instances/IInstanceComponent.cs b/src/Speckle.Core/Models/Instances/IInstanceComponent.cs new file mode 100644 index 00000000..9a647860 --- /dev/null +++ b/src/Speckle.Core/Models/Instances/IInstanceComponent.cs @@ -0,0 +1,12 @@ +namespace Speckle.Core.Models.Instances; + +/// +/// Abstracts over and for sorting and grouping in receive operations. +/// +public interface IInstanceComponent +{ + /// + /// The maximum "depth" at which this or was found. On receive, as instances can be composed of other instances, we need to start from the deepest instance elements first when reconstructing them, starting with definitions first. + /// + public int MaxDepth { get; set; } +} diff --git a/src/Speckle.Core/Models/Instances/InstanceDefinitionProxy.cs b/src/Speckle.Core/Models/Instances/InstanceDefinitionProxy.cs new file mode 100644 index 00000000..e6831d26 --- /dev/null +++ b/src/Speckle.Core/Models/Instances/InstanceDefinitionProxy.cs @@ -0,0 +1,14 @@ +namespace Speckle.Core.Models.Instances; + +/// +/// A proxy class for an instance definition. +/// +public class InstanceDefinitionProxy : Base, IInstanceComponent +{ + /// + /// The original ids of the objects that are part of this definition, as present in the source host app. On receive, they will be mapped to corresponding newly created definition ids. + /// + public List Objects { get; set; } // source app application ids for the objects + + public int MaxDepth { get; set; } +} diff --git a/src/Speckle.Core/Models/Instances/InstanceProxy.cs b/src/Speckle.Core/Models/Instances/InstanceProxy.cs new file mode 100644 index 00000000..246a72e7 --- /dev/null +++ b/src/Speckle.Core/Models/Instances/InstanceProxy.cs @@ -0,0 +1,26 @@ +using Speckle.DoubleNumerics; + +namespace Speckle.Core.Models.Instances; + +/// +/// A proxy class for an instance (e.g, a rhino block, or an autocad block reference). +/// +public class InstanceProxy : Base, IInstanceComponent +{ + /// + /// The definition id as present in the original host app. On receive, it will be mapped to the newly created definition id. + /// + public string DefinitionId { get; set; } + + /// + /// The transform of the instance reference. + /// + public Matrix4x4 Transform { get; set; } + + /// + /// The units of the host application file. + /// + public string Units { get; set; } = Kits.Units.Meters; + + public int MaxDepth { get; set; } +} diff --git a/src/Speckle.Core/Serialisation/BaseObjectDeserializerV2.cs b/src/Speckle.Core/Serialisation/BaseObjectDeserializerV2.cs index 89e84b39..5213c3f8 100644 --- a/src/Speckle.Core/Serialisation/BaseObjectDeserializerV2.cs +++ b/src/Speckle.Core/Serialisation/BaseObjectDeserializerV2.cs @@ -151,7 +151,7 @@ public Base Deserialize(string rootObjectJson) return null; } // Try background work - Task? bgResult = _workerThreads!.TryStartTask(WorkerThreadTaskType.Deserialize, objectJson); //BUG: Because we don't guarantee this task will ever be awaited, this may lead to unobserved exceptions! + Task? bgResult = _workerThreads?.TryStartTask(WorkerThreadTaskType.Deserialize, objectJson); //BUG: Because we don't guarantee this task will ever be awaited, this may lead to unobserved exceptions! if (bgResult != null) { return bgResult; @@ -340,7 +340,7 @@ public Base Deserialize(string rootObjectJson) private Base Dict2Base(Dictionary dictObj) { - string typeName = (string)dictObj[TYPE_DISCRIMINATOR]!; + string typeName = (string)dictObj[TYPE_DISCRIMINATOR].NotNull(); Type type = BaseObjectSerializationUtilities.GetType(typeName); Base baseObj = (Base)Activator.CreateInstance(type); diff --git a/src/Speckle.Core/Serialisation/BaseObjectSerializerV2.cs b/src/Speckle.Core/Serialisation/BaseObjectSerializerV2.cs index 27a1b304..6ae25821 100644 --- a/src/Speckle.Core/Serialisation/BaseObjectSerializerV2.cs +++ b/src/Speckle.Core/Serialisation/BaseObjectSerializerV2.cs @@ -2,17 +2,19 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; -using System.DoubleNumerics; using System.Drawing; using System.Globalization; using System.Linq; using System.Reflection; using System.Threading; -using Speckle.Core.Helpers; +using Sentry; +using Speckle.Core.Common; using Speckle.Core.Logging; using Speckle.Core.Models; using Speckle.Core.Transports; +using Speckle.DoubleNumerics; using Speckle.Newtonsoft.Json; +using Constants = Speckle.Core.Helpers.Constants; using Utilities = Speckle.Core.Models.Utilities; namespace Speckle.Core.Serialisation; @@ -87,14 +89,19 @@ public string Serialize(Base baseObj) IDictionary converted; try { - converted = PreserializeBase(baseObj, true)!; + var x = PreserializeBase(baseObj, true); + if (x is null) + { + throw new SpeckleSerializeException("Already serialized"); + } + converted = x; } catch (Exception ex) when (!ex.IsFatal()) { throw new SpeckleSerializeException($"Failed to extract (pre-serialize) properties from the {baseObj}", ex); } string serialized = Dict2Json(converted); - StoreObject((string)converted["id"]!, serialized); + StoreObject((string)converted["id"].NotNull(), serialized); return serialized; } finally @@ -239,7 +246,7 @@ public string Serialize(Base baseObj) } } - public IDictionary? PreserializeBase( + private IDictionary? PreserializeBase( Base baseObj, bool computeClosures = false, PropertyAttributeInfo inheritedDetachInfo = default @@ -332,8 +339,8 @@ public string Serialize(Base baseObj) if (inheritedDetachInfo.IsDetachable && WriteTransports.Count > 0) { - string json = Dict2Json(convertedBase); - string id = (string)convertedBase["id"]!; + var json = Dict2Json(convertedBase); + var id = (string)convertedBase["id"].NotNull(); StoreObject(id, json); ObjectReference objRef = new() { referencedId = id }; var objRefConverted = (IDictionary?)PreserializeObject(objRef); @@ -413,6 +420,10 @@ private static string ComputeId(IDictionary obj) private static string Dict2Json(IDictionary? obj) { + if (obj is null) + { + return string.Empty; + } string serialized = JsonConvert.SerializeObject(obj); return serialized; } diff --git a/src/Speckle.Core/Serialisation/SerializationUtilities/ValueConverter.cs b/src/Speckle.Core/Serialisation/SerializationUtilities/ValueConverter.cs index 11d04929..58fa5ecb 100644 --- a/src/Speckle.Core/Serialisation/SerializationUtilities/ValueConverter.cs +++ b/src/Speckle.Core/Serialisation/SerializationUtilities/ValueConverter.cs @@ -1,10 +1,11 @@ using System; using System.Collections; using System.Collections.Generic; -using System.DoubleNumerics; +using System.Diagnostics.Contracts; using System.Drawing; using System.Globalization; using Speckle.Core.Logging; +using Speckle.DoubleNumerics; using Numerics = System.Numerics; namespace Speckle.Core.Serialisation.SerializationUtilities; @@ -157,16 +158,17 @@ public static bool ConvertValue(Type type, object? value, out object? convertedV #endregion } - // Handle List - if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>)) + // Handle List<>, IList<>, and IReadOnlyList<> + if (type.IsGenericType && IsGenericList(type)) { if (value is not List valueList) { return false; } + var targetType = typeof(List<>).MakeGenericType(type.GenericTypeArguments); Type listElementType = type.GenericTypeArguments[0]; - IList ret = (IList)Activator.CreateInstance(type, valueList.Count); + IList ret = (IList)Activator.CreateInstance(targetType, valueList.Count); foreach (object inputListElement in valueList) { if (!ConvertValue(listElementType, inputListElement, out object? convertedListElement)) @@ -311,4 +313,21 @@ public static bool ConvertValue(Type type, object? value, out object? convertedV return false; } + + /// + /// Tests that the given is assignable from a generic type def + /// + /// + /// + [Pure] + private static bool IsGenericList(Type type) + { + if (!type.IsGenericType) + { + return false; + } + + Type typeDef = type.GetGenericTypeDefinition(); + return typeDef == typeof(List<>) || typeDef == typeof(IList<>) || typeDef == typeof(IReadOnlyList<>); + } } diff --git a/src/Speckle.Core/Speckle.Core.csproj b/src/Speckle.Core/Speckle.Core.csproj index 0c499dc8..553c3485 100644 --- a/src/Speckle.Core/Speckle.Core.csproj +++ b/src/Speckle.Core/Speckle.Core.csproj @@ -9,7 +9,7 @@ Core is the .NET SDK for Speckle $(PackageTags) core true - System.Runtime.CompilerServices.IsExternalInit;System.Runtime.CompilerServices.RequiresLocationAttribute + System.Runtime.CompilerServices.RequiresLocationAttribute @@ -34,7 +34,7 @@ - + diff --git a/src/Speckle.Core/packages.lock.json b/src/Speckle.Core/packages.lock.json index 2501a46d..048391c8 100644 --- a/src/Speckle.Core/packages.lock.json +++ b/src/Speckle.Core/packages.lock.json @@ -21,12 +21,12 @@ }, "Microsoft.Data.Sqlite": { "type": "Direct", - "requested": "[7.0.5, )", - "resolved": "7.0.5", - "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "requested": "[8.0.6, )", + "resolved": "8.0.6", + "contentHash": "YVzVtU1IoGsTiMAe0BNV9ssKyrUm6lBQJP6w2N4W29YrBYYtyCmTFmrNGjxaJtJXVqttu0sYkPxmStj/OnMFdg==", "dependencies": { - "Microsoft.Data.Sqlite.Core": "7.0.5", - "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + "Microsoft.Data.Sqlite.Core": "8.0.6", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.6" } }, "Microsoft.SourceLink.GitHub": { @@ -157,21 +157,18 @@ "Serilog": "2.10.0" } }, + "Speckle.DoubleNumerics": { + "type": "Direct", + "requested": "[4.0.1-alpha.3, )", + "resolved": "4.0.1-alpha.3", + "contentHash": "7/lg9LDI3TZLiI7FhI2IhRrTzsZyDrQw1rG/S3kRtx0IU3raWzRHL4W5zvHV8LdWkqJEztS/9dUgLNYHV70o5Q==" + }, "Speckle.Newtonsoft.Json": { "type": "Direct", "requested": "[13.0.2, )", "resolved": "13.0.2", "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" }, - "System.DoubleNumerics": { - "type": "Direct", - "requested": "[3.1.3, )", - "resolved": "3.1.3", - "contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==", - "dependencies": { - "NETStandard.Library": "1.6.1" - } - }, "GraphQL.Client.Abstractions": { "type": "Transitive", "resolved": "6.0.0", @@ -246,10 +243,10 @@ }, "Microsoft.Data.Sqlite.Core": { "type": "Transitive", - "resolved": "7.0.5", - "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "resolved": "8.0.6", + "contentHash": "umhZ0ZF2RI81rGFTnYmCxI+Euj4Aqe/6Y4+8CxN9OVJNGDNIqB5laJ3wxQTU8zXCcm2k9F7FL+/6RVoOT4z1Fw==", "dependencies": { - "SQLitePCLRaw.core": "2.1.4" + "SQLitePCLRaw.core": "2.1.6" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { @@ -330,32 +327,32 @@ }, "SQLitePCLRaw.bundle_e_sqlite3": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "resolved": "2.1.6", + "contentHash": "BmAf6XWt4TqtowmiWe4/5rRot6GerAeklmOPfviOvwLoF5WwgxcJHAxZtySuyW9r9w+HLILnm8VfJFLCUJYW8A==", "dependencies": { - "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", - "SQLitePCLRaw.provider.e_sqlite3": "2.1.4" + "SQLitePCLRaw.lib.e_sqlite3": "2.1.6", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.6" } }, "SQLitePCLRaw.core": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "resolved": "2.1.6", + "contentHash": "wO6v9GeMx9CUngAet8hbO7xdm+M42p1XeJq47ogyRoYSvNSp0NGLI+MgC0bhrMk9C17MTVFlLiN6ylyExLCc5w==", "dependencies": { "System.Memory": "4.5.3" } }, "SQLitePCLRaw.lib.e_sqlite3": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + "resolved": "2.1.6", + "contentHash": "2ObJJLkIUIxRpOUlZNGuD4rICpBnrBR5anjyfUFQep4hMOIeqW+XGQYzrNmHSVz5xSWZ3klSbh7sFR6UyDj68Q==" }, "SQLitePCLRaw.provider.e_sqlite3": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==", + "resolved": "2.1.6", + "contentHash": "PQ2Oq3yepLY4P7ll145P3xtx2bX8xF4PzaKPRpw9jZlKvfe4LE/saAV82inND9usn1XRpmxXk7Lal3MTI+6CNg==", "dependencies": { - "SQLitePCLRaw.core": "2.1.4" + "SQLitePCLRaw.core": "2.1.6" } }, "System.Buffers": { diff --git a/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelBeam.cs b/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelBeam.cs index 83db3e1c..af9fde1b 100644 --- a/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelBeam.cs +++ b/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelBeam.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; -using Objects.Geometry; using Objects.Structural.Materials; using Objects.Structural.Properties.Profiles; using Speckle.Core.Kits; @@ -7,7 +5,7 @@ namespace Objects.BuiltElements.AdvanceSteel; -public class AsteelBeam : Beam, IDisplayValue>, IHasVolume, IHasArea, IAsteelObject +public class AsteelBeam : Beam, IHasVolume, IHasArea, IAsteelObject { [DetachProperty] public SectionProfile profile { get; set; } diff --git a/src/Speckle.Objects/BuiltElements/Archicad/ArchicadOpening.cs b/src/Speckle.Objects/BuiltElements/Archicad/ArchicadOpening.cs new file mode 100644 index 00000000..117b072e --- /dev/null +++ b/src/Speckle.Objects/BuiltElements/Archicad/ArchicadOpening.cs @@ -0,0 +1,83 @@ +using System.Collections.Generic; +using Objects.Geometry; +using Speckle.Core.Kits; +using Speckle.Core.Models; + +namespace Objects.BuiltElements.Archicad; + +public class ArchicadOpening : Opening +{ + [SchemaInfo("ArchicadOpening", "Creates an Archicad opening.", "Archicad", "Structure")] + public ArchicadOpening() { } + + public string parentApplicationId { get; set; } + + // Element base + public string? elementType { get; set; } /*APINullabe*/ + + public List? classifications { get; set; } /*APINullabe*/ + public Base? elementProperties { get; set; } + public Base? componentProperties { get; set; } + + // Floor Plan Parameters + public string? floorPlanDisplayMode { get; set; } /*APINullabe*/ + public string? connectionMode { get; set; } /*APINullabe*/ + + // Cut Surfaces Parameters + public bool? cutsurfacesUseLineOfCutElements { get; set; } /*APINullabe*/ + public short? cutsurfacesLinePenIndex { get; set; } /*APINullabe*/ + public string? cutsurfacesLineIndex { get; set; } /*APINullabe*/ + + // Outlines Parameters + public string? outlinesStyle { get; set; } /*APINullabe*/ + public bool? outlinesUseLineOfCutElements { get; set; } /*APINullabe*/ + public string? outlinesUncutLineIndex { get; set; } /*APINullabe*/ + public string? outlinesOverheadLineIndex { get; set; } /*APINullabe*/ + public short? outlinesUncutLinePenIndex { get; set; } /*APINullabe*/ + public short? outlinesOverheadLinePenIndex { get; set; } /*APINullabe*/ + + // Opening Cover Fills Parameters + public bool? useCoverFills { get; set; } /*APINullabe*/ + public bool? useFillsOfCutElements { get; set; } /*APINullabe*/ + public string? coverFillIndex { get; set; } /*APINullabe*/ + public short? coverFillPenIndex { get; set; } /*APINullabe*/ + public short? coverFillBackgroundPenIndex { get; set; } /*APINullabe*/ + public string? coverFillOrientation { get; set; } /*APINullabe*/ // Kérdéses.. + + // Cover Fill Transformation Parameters + public double? coverFillTransformationOrigoX { get; set; } + public double? coverFillTransformationOrigoY { get; set; } + public double? coverFillTransformationOrigoZ { get; set; } + public double? coverFillTransformationXAxisX { get; set; } + public double? coverFillTransformationXAxisY { get; set; } + public double? coverFillTransformationXAxisZ { get; set; } + public double? coverFillTransformationYAxisX { get; set; } + public double? coverFillTransformationYAxisY { get; set; } + public double? coverFillTransformationYAxisZ { get; set; } + + // Reference Axis Parameters + public bool? showReferenceAxis { get; set; } /*APINullabe*/ + public short? referenceAxisPenIndex { get; set; } /*APINullabe*/ + public string? referenceAxisLineTypeIndex { get; set; } /*APINullabe*/ + public double? referenceAxisOverhang { get; set; } /*APINullabe*/ + + // Extrusion Geometry Parameters + // Plane Frame + public Point extrusionGeometryBasePoint { get; set; } + public Vector extrusionGeometryXAxis { get; set; } + public Vector extrusionGeometryYAxis { get; set; } + public Vector extrusionGeometryZAxis { get; set; } + + // Opening Extrustion Parameters + public string? basePolygonType { get; set; } /*APINullabe*/ + public double? width { get; set; } /*APINullabe*/ + public double? height { get; set; } /*APINullabe*/ + public string? constraint { get; set; } /*APINullabe*/ + public string? anchor { get; set; } /*APINullabe */ + public int? anchorIndex { get; set; } /*APINullabe*/ + public double? anchorAltitude { get; set; } /*APINullabe*/ + public string? limitType { get; set; } /*APINullabe*/ + public double? extrusionStartOffSet { get; set; } /*APINullabe*/ + public double? finiteBodyLength { get; set; } /*APINullabe*/ + public string? linkedStatus { get; set; } /*APINullabe*/ +} diff --git a/src/Speckle.Objects/BuiltElements/Archicad/DirectShape.cs b/src/Speckle.Objects/BuiltElements/Archicad/DirectShape.cs index 8b2f11ca..8ae08ece 100644 --- a/src/Speckle.Objects/BuiltElements/Archicad/DirectShape.cs +++ b/src/Speckle.Objects/BuiltElements/Archicad/DirectShape.cs @@ -16,7 +16,10 @@ public DirectShape(string applicationId, List displayValue) // Element base public string elementType { get; set; } + public List classifications { get; set; } + public Base? elementProperties { get; set; } + public Base? componentProperties { get; set; } public ArchicadLevel level { get; set; } diff --git a/src/Speckle.Objects/BuiltElements/Archicad/ElementShape.cs b/src/Speckle.Objects/BuiltElements/Archicad/ElementShape.cs index 4da45cb3..cd78ab47 100644 --- a/src/Speckle.Objects/BuiltElements/Archicad/ElementShape.cs +++ b/src/Speckle.Objects/BuiltElements/Archicad/ElementShape.cs @@ -1,7 +1,9 @@ using System.Collections.Generic; using Objects.Geometry; using Objects.Primitive; +using Speckle.Core.Kits; using Speckle.Core.Models; +using Speckle.Newtonsoft.Json; namespace Objects.BuiltElements.Archicad; @@ -19,6 +21,9 @@ public ElementShape(Polyline contourPolyline, List? holePolylines = nu public List? holePolylines { get; set; } + /// + /// This class is only used for Archicad interop + /// public sealed class PolylineSegment : Base, ICurve { public PolylineSegment() { } @@ -33,21 +38,30 @@ public PolylineSegment(Point startPoint, Point endPoint, double? arcAngle = null public Point startPoint { get; set; } public Point endPoint { get; set; } + + [JsonIgnore] + public string units => Units.Meters; public double arcAngle { get; set; } public bool? bodyFlag { get; set; } public double length { get; set; } public Interval domain { get; set; } = new(0, 1); } + /// + /// This class is only used for Archicad interop + /// public sealed class Polyline : Base, ICurve { public Polyline() { } public Polyline(List segments) { - polylineSegments = segments; + this.polylineSegments = segments; } + [JsonIgnore] + public string units => Units.Meters; + public List polylineSegments { get; set; } = new(); public double length { get; set; } public Interval domain { get; set; } = new(0, 1); diff --git a/src/Speckle.Objects/BuiltElements/Baseline.cs b/src/Speckle.Objects/BuiltElements/Baseline.cs new file mode 100644 index 00000000..a78fc9b0 --- /dev/null +++ b/src/Speckle.Objects/BuiltElements/Baseline.cs @@ -0,0 +1,88 @@ +using Speckle.Core.Models; +using Speckle.Newtonsoft.Json; + +namespace Objects.BuiltElements; + +public abstract class Baseline : Base +{ + protected Baseline() { } + + protected Baseline(string name, bool isFeaturelineBased) + { + this.name = name; + this.isFeaturelineBased = isFeaturelineBased; + } + + /// + /// The name of this baseline + /// + public string name { get; set; } + + /// + /// The horizontal component of this baseline + /// + public abstract Alignment? alignment { get; internal set; } + + /// + /// The vertical component of this baseline + /// + public abstract Profile? profile { get; internal set; } + + [DetachProperty] + public Featureline? featureline { get; internal set; } + + public bool isFeaturelineBased { get; set; } +} + +/// +/// Generic instance class +/// +public abstract class Baseline : Baseline + where TA : Alignment + where TP : Profile +{ + protected Baseline(string name, TA alignment, TP profile, Featureline? featureline, bool isFeaturelineBased) + : base(name, isFeaturelineBased) + { + this.name = name; + typedAlignment = alignment; + typedProfile = profile; + this.featureline = featureline; + this.isFeaturelineBased = isFeaturelineBased; + } + + protected Baseline() + : base(string.Empty, false) { } + + [JsonIgnore] + public TA typedAlignment { get; set; } + + [JsonIgnore] + public TP typedProfile { get; set; } + + [DetachProperty] + public override Alignment? alignment + { + get => typedAlignment; + internal set + { + if (value is TA typeA) + { + typedAlignment = typeA; + } + } + } + + [DetachProperty] + public override Profile? profile + { + get => typedProfile; + internal set + { + if (value is TP typeP) + { + typedProfile = typeP; + } + } + } +} diff --git a/src/Speckle.Objects/BuiltElements/Beam.cs b/src/Speckle.Objects/BuiltElements/Beam.cs index 7d917c6c..d8689216 100644 --- a/src/Speckle.Objects/BuiltElements/Beam.cs +++ b/src/Speckle.Objects/BuiltElements/Beam.cs @@ -5,22 +5,31 @@ namespace Objects.BuiltElements; -public class Beam : Base, IDisplayValue> +public class Beam : Base, IDisplayValue> { public Beam() { } - [SchemaInfo("Beam", "Creates a Speckle beam", "BIM", "Structure")] - public Beam([SchemaMainParam] ICurve baseLine) + public Beam(ICurve baseLine, Level? level, string? units, IReadOnlyList? displayValue = null) { this.baseLine = baseLine; + this.level = level; + this.units = units; + this.displayValue = ((IReadOnlyList?)displayValue) ?? new[] { (Base)baseLine }; } public ICurve baseLine { get; set; } public virtual Level? level { get; internal set; } - public string units { get; set; } + public string? units { get; set; } [DetachProperty] - public List displayValue { get; set; } + public IReadOnlyList displayValue { get; set; } + + #region Schema Info Constructors + [SchemaInfo("Beam", "Creates a Speckle beam", "BIM", "Structure")] + public Beam([SchemaMainParam] ICurve baseLine) + : this(baseLine, null, null) { } + + #endregion } diff --git a/src/Speckle.Objects/BuiltElements/Brace.cs b/src/Speckle.Objects/BuiltElements/Brace.cs index c51cd568..c8a90447 100644 --- a/src/Speckle.Objects/BuiltElements/Brace.cs +++ b/src/Speckle.Objects/BuiltElements/Brace.cs @@ -5,20 +5,25 @@ namespace Objects.BuiltElements; -public class Brace : Base, IDisplayValue> +public class Brace : Base, IDisplayValue> { public Brace() { } - [SchemaInfo("Brace", "Creates a Speckle brace", "BIM", "Structure")] - public Brace([SchemaMainParam] ICurve baseLine) + public Brace(ICurve baseLine, string? units, IReadOnlyList? displayValue = null) { this.baseLine = baseLine; + this.units = units; + this.displayValue = ((IReadOnlyList?)displayValue) ?? new[] { (Base)baseLine }; } public ICurve baseLine { get; set; } - public string units { get; set; } + public string? units { get; set; } [DetachProperty] - public List displayValue { get; set; } + public IReadOnlyList displayValue { get; set; } + + [SchemaInfo("Brace", "Creates a Speckle brace", "BIM", "Structure")] + public Brace([SchemaMainParam] ICurve baseLine) + : this(baseLine, null) { } } diff --git a/src/Speckle.Objects/BuiltElements/Civil/CivilAppliedAssembly.cs b/src/Speckle.Objects/BuiltElements/Civil/CivilAppliedAssembly.cs new file mode 100644 index 00000000..f490c643 --- /dev/null +++ b/src/Speckle.Objects/BuiltElements/Civil/CivilAppliedAssembly.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using Speckle.Core.Models; + +namespace Objects.BuiltElements.Civil; + +public class CivilAppliedAssembly : Base +{ + public CivilAppliedAssembly() { } + + public CivilAppliedAssembly( + List appliedSubassemblies, + double adjustedElevation, + string units + ) + { + this.appliedSubassemblies = appliedSubassemblies; + this.adjustedElevation = adjustedElevation; + this.units = units; + } + + public List appliedSubassemblies { get; set; } + + public double adjustedElevation { get; set; } + + public string units { get; set; } +} diff --git a/src/Speckle.Objects/BuiltElements/Civil/CivilAppliedSubassembly.cs b/src/Speckle.Objects/BuiltElements/Civil/CivilAppliedSubassembly.cs new file mode 100644 index 00000000..f4beb7a3 --- /dev/null +++ b/src/Speckle.Objects/BuiltElements/Civil/CivilAppliedSubassembly.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using Objects.Geometry; +using Objects.Other.Civil; +using Speckle.Core.Models; + +namespace Objects.BuiltElements.Civil; + +public class CivilAppliedSubassembly : Base +{ + public CivilAppliedSubassembly() { } + + public CivilAppliedSubassembly( + string subassemblyId, + string subassemblyName, + List shapes, + Point stationOffsetElevationToBaseline, + List parameters + ) + { + this.subassemblyId = subassemblyId; + this.subassemblyName = subassemblyName; + this.shapes = shapes; + this.stationOffsetElevationToBaseline = stationOffsetElevationToBaseline; + this.parameters = parameters; + } + + public string subassemblyId { get; set; } + + public string subassemblyName { get; set; } + + public List shapes { get; set; } + + public Point stationOffsetElevationToBaseline { get; set; } + + [DetachProperty] + public List parameters { get; set; } +} diff --git a/src/Speckle.Objects/BuiltElements/Civil/CivilBaseline.cs b/src/Speckle.Objects/BuiltElements/Civil/CivilBaseline.cs new file mode 100644 index 00000000..826ef8df --- /dev/null +++ b/src/Speckle.Objects/BuiltElements/Civil/CivilBaseline.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; + +namespace Objects.BuiltElements.Civil; + +public class CivilBaseline : Baseline +{ + public CivilBaseline() { } + + public CivilBaseline( + string name, + List regions, + List stations, + double startStation, + double endStation, + CivilAlignment alignment, + CivilProfile profile + ) + { + this.name = name; + this.regions = regions; + this.stations = stations; + this.startStation = startStation; + this.endStation = endStation; + this.alignment = alignment; + this.profile = profile; + isFeaturelineBased = false; + } + + public CivilBaseline( + string name, + List regions, + List stations, + double startStation, + double endStation, + Featureline featureline + ) + { + this.name = name; + this.regions = regions; + this.stations = stations; + this.startStation = startStation; + this.endStation = endStation; + this.featureline = featureline; + isFeaturelineBased = true; + } + + public List regions { get; set; } + + public List stations { get; set; } + + public double startStation { get; set; } + + public double endStation { get; set; } +} diff --git a/src/Speckle.Objects/BuiltElements/Civil/CivilBaselineRegion.cs b/src/Speckle.Objects/BuiltElements/Civil/CivilBaselineRegion.cs new file mode 100644 index 00000000..5aabffd3 --- /dev/null +++ b/src/Speckle.Objects/BuiltElements/Civil/CivilBaselineRegion.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using Speckle.Core.Models; + +namespace Objects.BuiltElements.Civil; + +public class CivilBaselineRegion : Base +{ + public CivilBaselineRegion() { } + + public CivilBaselineRegion( + string name, + double startStation, + double endStation, + string assemblyId, + string? assemblyName, + List appliedAssemblies + ) + { + this.name = name; + this.startStation = startStation; + this.endStation = endStation; + this.assemblyId = assemblyId; + this.assemblyName = assemblyName; + this.appliedAssemblies = appliedAssemblies; + } + + /// + /// The name of the region + /// + public string name { get; set; } + + /// + /// The id of the assembly of the region + /// + public string assemblyId { get; set; } + + public string? assemblyName { get; set; } + + public double startStation { get; set; } + + public double endStation { get; set; } + + [DetachProperty] + public List appliedAssemblies { get; set; } +} diff --git a/src/Speckle.Objects/BuiltElements/Civil/CivilCalculatedLink.cs b/src/Speckle.Objects/BuiltElements/Civil/CivilCalculatedLink.cs new file mode 100644 index 00000000..aa0bc949 --- /dev/null +++ b/src/Speckle.Objects/BuiltElements/Civil/CivilCalculatedLink.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using Speckle.Core.Models; + +namespace Objects.BuiltElements.Civil; + +public class CivilCalculatedLink : Base, ICivilCalculatedObject +{ + public CivilCalculatedLink() { } + + public CivilCalculatedLink(List codes, List points) + { + this.codes = codes; + this.points = points; + } + + public List codes { get; set; } + + [DetachProperty] + public List points { get; set; } +} diff --git a/src/Speckle.Objects/BuiltElements/Civil/CivilCalculatedPoint.cs b/src/Speckle.Objects/BuiltElements/Civil/CivilCalculatedPoint.cs new file mode 100644 index 00000000..c6ed147b --- /dev/null +++ b/src/Speckle.Objects/BuiltElements/Civil/CivilCalculatedPoint.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using Objects.Geometry; +using Speckle.Core.Models; + +namespace Objects.BuiltElements.Civil; + +public class CivilCalculatedPoint : Base, ICivilCalculatedObject +{ + public CivilCalculatedPoint() { } + + public CivilCalculatedPoint( + Point point, + List codes, + Vector normalToBaseline, + Vector normalToSubassembly, + Point stationOffsetElevationToBaseline + ) + { + this.point = point; + this.codes = codes; + this.normalToBaseline = normalToBaseline; + this.normalToSubassembly = normalToSubassembly; + this.stationOffsetElevationToBaseline = stationOffsetElevationToBaseline; + } + + public Point point { get; set; } + + public List codes { get; set; } + + public Vector normalToBaseline { get; set; } + + public Vector normalToSubassembly { get; set; } + + public Point stationOffsetElevationToBaseline { get; set; } +} diff --git a/src/Speckle.Objects/BuiltElements/Civil/CivilCalculatedShape.cs b/src/Speckle.Objects/BuiltElements/Civil/CivilCalculatedShape.cs new file mode 100644 index 00000000..1e6a3fb7 --- /dev/null +++ b/src/Speckle.Objects/BuiltElements/Civil/CivilCalculatedShape.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using Speckle.Core.Models; + +namespace Objects.BuiltElements.Civil; + +public class CivilCalculatedShape : Base, ICivilCalculatedObject +{ + public CivilCalculatedShape() { } + + public CivilCalculatedShape(List codes, List links, double area, string units) + { + this.codes = codes; + this.links = links; + this.area = area; + this.units = units; + } + + public List codes { get; set; } + + [DetachProperty] + public List links { get; set; } + + public double area { get; set; } + + public string units { get; set; } +} diff --git a/src/Speckle.Objects/BuiltElements/Column.cs b/src/Speckle.Objects/BuiltElements/Column.cs index e13e054e..3b4d4055 100644 --- a/src/Speckle.Objects/BuiltElements/Column.cs +++ b/src/Speckle.Objects/BuiltElements/Column.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using Objects.Geometry; using Speckle.Core.Kits; @@ -5,22 +6,32 @@ namespace Objects.BuiltElements; -public class Column : Base, IDisplayValue> +public class Column : Base, IDisplayValue> { public Column() { } - [SchemaInfo("Column", "Creates a Speckle column", "BIM", "Structure")] - public Column([SchemaMainParam] ICurve baseLine) + public Column(ICurve baseLine, string? units, Level? level = null, IReadOnlyList? displayValue = null) { this.baseLine = baseLine; + this.units = units; + this.level = level; + this.displayValue = ((IReadOnlyList?)displayValue) ?? new[] { (Base)baseLine }; } public ICurve baseLine { get; set; } public virtual Level? level { get; internal set; } - public string units { get; set; } + public string? units { get; set; } [DetachProperty] - public List displayValue { get; set; } + public IReadOnlyList displayValue { get; set; } + + #region Schema Info Constructors + + [SchemaInfo("Column", "Creates a Speckle column", "BIM", "Structure")] + [SchemaDeprecated, Obsolete("Use other constructor")] + public Column([SchemaMainParam] ICurve baseLine) + : this(baseLine, null) { } + #endregion } diff --git a/src/Speckle.Objects/BuiltElements/Duct.cs b/src/Speckle.Objects/BuiltElements/Duct.cs index f530be6f..6ef56bbc 100644 --- a/src/Speckle.Objects/BuiltElements/Duct.cs +++ b/src/Speckle.Objects/BuiltElements/Duct.cs @@ -7,10 +7,47 @@ namespace Objects.BuiltElements; -public class Duct : Base, IDisplayValue> +public class Duct : Base, IDisplayValue> { public Duct() { } + public Duct( + ICurve baseCurve, + double width, + double height, + double diameter, + double length, + string? units, + double velocity = 0, + IReadOnlyList? displayValue = null + ) + { + this.baseCurve = baseCurve; + this.width = width; + this.height = height; + this.diameter = diameter; + this.length = length; + this.units = units; + this.velocity = velocity; + this.displayValue = ((IReadOnlyList?)displayValue) ?? new[] { (Base)baseCurve }; + } + + [JsonIgnore, Obsolete("Replaced with baseCurve property")] + public Line? baseLine { get; set; } + + public ICurve baseCurve { get; set; } + public double width { get; set; } + public double height { get; set; } + public double diameter { get; set; } + public double length { get; set; } + public double velocity { get; set; } + + public string? units { get; set; } + + [DetachProperty] + public IReadOnlyList displayValue { get; set; } + + #region Schema Info Constructors /// /// SchemaBuilder constructor for a Speckle duct /// @@ -22,13 +59,8 @@ public Duct() { } /// Assign units when using this constructor due to , , and params [SchemaInfo("Duct", "Creates a Speckle duct", "BIM", "MEP"), SchemaDeprecated] public Duct([SchemaMainParam] Line baseLine, double width, double height, double diameter, double velocity = 0) - { - baseCurve = baseLine; - this.width = width; - this.height = height; - this.diameter = diameter; - this.velocity = velocity; - } + : this(baseLine, width, height, diameter, default, null, velocity) //TODO: what to do with length??? + { } /// /// SchemaBuilder constructor for a Speckle duct @@ -41,26 +73,6 @@ public Duct([SchemaMainParam] Line baseLine, double width, double height, double /// Assign units when using this constructor due to , , and params [SchemaInfo("Duct", "Creates a Speckle duct", "BIM", "MEP")] public Duct([SchemaMainParam] ICurve baseCurve, double width, double height, double diameter, double velocity = 0) - { - this.baseCurve = baseCurve; - this.width = width; - this.height = height; - this.diameter = diameter; - this.velocity = velocity; - } - - [JsonIgnore, Obsolete("Replaced with baseCurve property")] - public Line baseLine { get; set; } - - public ICurve baseCurve { get; set; } - public double width { get; set; } - public double height { get; set; } - public double diameter { get; set; } - public double length { get; set; } - public double velocity { get; set; } - - public string units { get; set; } - - [DetachProperty] - public List displayValue { get; set; } + : this(baseCurve, width, height, diameter, default, null, velocity) { } //TODO: what to do with length??? + #endregion } diff --git a/src/Speckle.Objects/BuiltElements/Revit/RevitBeam.cs b/src/Speckle.Objects/BuiltElements/Revit/RevitBeam.cs index b483b12a..6d7859e3 100644 --- a/src/Speckle.Objects/BuiltElements/Revit/RevitBeam.cs +++ b/src/Speckle.Objects/BuiltElements/Revit/RevitBeam.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Objects.Geometry; using Objects.Utils; using Speckle.Core.Kits; using Speckle.Core.Models; @@ -9,20 +10,20 @@ public class RevitBeam : Beam { public RevitBeam() { } - [SchemaInfo("RevitBeam", "Creates a Revit beam by curve and base level.", "Revit", "Structure")] public RevitBeam( string family, string type, - [SchemaMainParam] ICurve baseLine, - Level level, + ICurve baseLine, + Level? level, + string? units, + List? displayValue = null, List? parameters = null ) + : base(baseLine, level, units, displayValue) { this.family = family; this.type = type; - this.baseLine = baseLine; this.parameters = parameters?.ToBase(); - this.level = level; } public string family { get; set; } @@ -35,4 +36,18 @@ public RevitBeam( get => base.level; set => base.level = value; } + + #region Schema Info Constructors + + [SchemaInfo("RevitBeam", "Creates a Revit beam by curve and base level.", "Revit", "Structure")] + public RevitBeam( + string family, + string type, + [SchemaMainParam] ICurve baseLine, + Level level, + List? parameters = null + ) + : this(family, type, baseLine, level, null, parameters: parameters) { } + + #endregion } diff --git a/src/Speckle.Objects/BuiltElements/Revit/RevitBrace.cs b/src/Speckle.Objects/BuiltElements/Revit/RevitBrace.cs index 6254f5e9..ce5966af 100644 --- a/src/Speckle.Objects/BuiltElements/Revit/RevitBrace.cs +++ b/src/Speckle.Objects/BuiltElements/Revit/RevitBrace.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Objects.Geometry; using Objects.Utils; using Speckle.Core.Kits; using Speckle.Core.Models; @@ -9,25 +10,42 @@ public class RevitBrace : Brace { public RevitBrace() { } - [SchemaInfo("RevitBrace", "Creates a Revit brace by curve and base level.", "Revit", "Structure")] public RevitBrace( string family, string type, - [SchemaMainParam] ICurve baseLine, + ICurve baseLine, Level? level, + string? units, + string? elementId, + IReadOnlyList? displayValue = null, List? parameters = null ) + : base(baseLine, units, displayValue) { this.family = family; this.type = type; - this.baseLine = baseLine; - this.parameters = parameters?.ToBase(); this.level = level; + this.elementId = elementId; + this.parameters = parameters?.ToBase(); } public string family { get; set; } public string type { get; set; } public Base? parameters { get; set; } - public string elementId { get; set; } + public string? elementId { get; set; } public Level? level { get; set; } + + #region Schema Info Constructor + + [SchemaInfo("RevitBrace", "Creates a Revit brace by curve and base level.", "Revit", "Structure")] + public RevitBrace( + string family, + string type, + [SchemaMainParam] ICurve baseLine, + Level? level, + List? parameters = null + ) + : this(family, type, baseLine, level, null, null, parameters: parameters) { } + + #endregion } diff --git a/src/Speckle.Objects/BuiltElements/Revit/RevitColumn.cs b/src/Speckle.Objects/BuiltElements/Revit/RevitColumn.cs index 5a770db9..64e4db93 100644 --- a/src/Speckle.Objects/BuiltElements/Revit/RevitColumn.cs +++ b/src/Speckle.Objects/BuiltElements/Revit/RevitColumn.cs @@ -1,4 +1,6 @@ +using System; using System.Collections.Generic; +using Objects.Geometry; using Objects.Utils; using Speckle.Core.Kits; using Speckle.Core.Models; @@ -9,49 +11,82 @@ public class RevitColumn : Column { public RevitColumn() { } - /// - /// SchemaBuilder constructor for a Revit column - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Assign units when using this constructor due to and params - [SchemaInfo("RevitColumn Vertical", "Creates a vertical Revit Column by point and levels.", "Revit", "Architecture")] public RevitColumn( string family, string type, - [SchemaParamInfo("Only the lower point of this line will be used as base point."), SchemaMainParam] ICurve baseLine, - Level level, - Level topLevel, + ICurve baseLine, + Level? level, + Level? topLevel, + string? units, + string? elementId, double baseOffset = 0, double topOffset = 0, - bool structural = false, - [SchemaParamInfo("Rotation angle in radians")] double rotation = 0, + bool facingFlipped = false, + bool handFlipped = false, + bool isSlanted = false, + double rotation = 0, + IReadOnlyList? displayValue = null, List? parameters = null ) + : base(baseLine, units, level, displayValue) { this.family = family; this.type = type; - this.baseLine = baseLine; this.topLevel = topLevel; + this.elementId = elementId; this.baseOffset = baseOffset; this.topOffset = topOffset; + this.facingFlipped = facingFlipped; + this.handFlipped = handFlipped; + this.isSlanted = isSlanted; this.rotation = rotation; this.parameters = parameters?.ToBase(); - this.level = level; } - [ - SchemaDeprecated, - SchemaInfo("RevitColumn Slanted (old)", "Creates a slanted Revit Column by curve.", "Revit", "Structure") - ] + public Level? topLevel { get; set; } + public double baseOffset { get; set; } + public double topOffset { get; set; } + public bool facingFlipped { get; set; } + public bool handFlipped { get; set; } + public double rotation { get; set; } + public bool isSlanted { get; set; } + public string family { get; set; } + public string type { get; set; } + public Base? parameters { get; set; } + public string? elementId { get; set; } + + #region Schema Info Constructors + + [SchemaInfo("RevitColumn Vertical", "Creates a vertical Revit Column by point and levels.", "Revit", "Architecture")] + public RevitColumn( + string family, + string type, + [SchemaParamInfo("Only the lower point of this line will be used as base point."), SchemaMainParam] ICurve baseLine, + Level level, + Level topLevel, + double baseOffset = 0, + double topOffset = 0, + bool structural = false, + [SchemaParamInfo("Rotation angle in radians")] double rotation = 0, + List? parameters = null + ) + : this( + family, + type, + baseLine, + level, + topLevel, + null, + null, + baseOffset, + topOffset, + rotation: rotation, + parameters: parameters + ) { } + + [Obsolete("Use other constructors")] + [SchemaDeprecated] + [SchemaInfo("RevitColumn Slanted (old)", "Creates a slanted Revit Column by curve.", "Revit", "Structure")] [System.Diagnostics.CodeAnalysis.SuppressMessage( "Style", "IDE0060:Remove unused parameter", @@ -84,31 +119,7 @@ public RevitColumn( bool structural = false, List? parameters = null ) - { - this.family = family; - this.type = type; - this.baseLine = baseLine; - this.level = level; - this.topLevel = topLevel; - isSlanted = true; - this.parameters = parameters?.ToBase(); - } - - public new Level? level - { - get => base.level; - set => base.level = value; - } + : this(family, type, baseLine, level, topLevel, null, null, displayValue: null, parameters: parameters) { } - public Level? topLevel { get; set; } - public double baseOffset { get; set; } - public double topOffset { get; set; } - public bool facingFlipped { get; set; } - public bool handFlipped { get; set; } - public double rotation { get; set; } - public bool isSlanted { get; set; } - public string family { get; set; } - public string type { get; set; } - public Base? parameters { get; set; } - public string elementId { get; set; } + #endregion } diff --git a/src/Speckle.Objects/BuiltElements/Revit/RevitDuct.cs b/src/Speckle.Objects/BuiltElements/Revit/RevitDuct.cs index 8c75f4ba..15014664 100644 --- a/src/Speckle.Objects/BuiltElements/Revit/RevitDuct.cs +++ b/src/Speckle.Objects/BuiltElements/Revit/RevitDuct.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using Objects.BuiltElements.Revit.Interfaces; using Objects.Geometry; @@ -11,69 +12,52 @@ public class RevitDuct : Duct, IHasMEPConnectors { public RevitDuct() { } - /// - /// SchemaBuilder constructor for a Revit duct (deprecated) - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Assign units when using this constructor due to , , and params - [SchemaInfo("RevitDuct", "Creates a Revit duct", "Revit", "MEP"), SchemaDeprecated] public RevitDuct( string family, string type, - [SchemaMainParam] Line baseLine, + ICurve baseCurve, string systemName, string systemType, Level level, double width, double height, double diameter, + double length, + string? units, + string? elementId, double velocity = 0, + IReadOnlyList? displayValue = null, List? parameters = null ) + : base(baseCurve, width, height, diameter, length, units, velocity, displayValue) { - baseCurve = baseLine; this.family = family; this.type = type; - this.width = width; - this.height = height; - this.diameter = diameter; - this.velocity = velocity; this.systemName = systemName; this.systemType = systemType; - this.parameters = parameters?.ToBase(); this.level = level; + this.parameters = parameters?.ToBase(); + this.elementId = elementId; } - /// - /// SchemaBuilder constructor for a Revit duct - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Assign units when using this constructor due to , , and params - [SchemaInfo("RevitDuct", "Creates a Revit duct", "Revit", "MEP")] + public string family { get; set; } + public string type { get; set; } + public string systemName { get; set; } + public string systemType { get; set; } + public Level level { get; set; } + public Base? parameters { get; set; } + public string? elementId { get; set; } + public List Connectors { get; set; } = new(); + + #region Schema Info Constructors + + [SchemaInfo("RevitDuct (DEPRECATED)", "Creates a Revit duct", "Revit", "MEP")] + [SchemaDeprecated] + [Obsolete("Use other Constructor")] public RevitDuct( string family, string type, - [SchemaMainParam] ICurve baseCurve, + [SchemaMainParam] Line baseLine, string systemName, string systemType, Level level, @@ -84,7 +68,7 @@ public RevitDuct( List? parameters = null ) { - this.baseCurve = baseCurve; + baseCurve = baseLine; this.family = family; this.type = type; this.width = width; @@ -97,36 +81,44 @@ public RevitDuct( this.level = level; } - public string family { get; set; } - public string type { get; set; } - public string systemName { get; set; } - public string systemType { get; set; } - public Level level { get; set; } - public Base? parameters { get; set; } - public string elementId { get; set; } - public List Connectors { get; set; } = new(); + [SchemaInfo("RevitDuct", "Creates a Revit duct", "Revit", "MEP")] + public RevitDuct( + string family, + string type, + [SchemaMainParam] ICurve baseCurve, + string systemName, + string systemType, + Level level, + double width, + double height, + double diameter, + double velocity = 0, + List? parameters = null + ) + : this( + family, + type, + baseCurve, + systemName, + systemType, + level, + width, + height, + diameter, + default, //TODO: what to do with length? + null, + null, + velocity, + parameters: parameters + ) { } + + #endregion } public class RevitFlexDuct : RevitDuct { public RevitFlexDuct() { } - /// - /// SchemaBuilder constructor for a Revit flex duct - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Assign units when using this constructor due to , , and params - [SchemaInfo("RevitFlexDuct", "Creates a Revit flex duct", "Revit", "MEP")] public RevitFlexDuct( string family, string type, @@ -137,27 +129,76 @@ public RevitFlexDuct( double width, double height, double diameter, + double length, Vector startTangent, Vector endTangent, + string? units, + string? elementId, double velocity = 0, + IReadOnlyList? displayValue = null, List? parameters = null ) + : base( + family, + type, + baseCurve, + systemName, + systemType, + level, + width, + height, + diameter, + length, + units, + elementId, + velocity, + displayValue, + parameters + ) { - this.baseCurve = baseCurve; - this.family = family; - this.type = type; - this.width = width; - this.height = height; - this.diameter = diameter; this.startTangent = startTangent; this.endTangent = endTangent; - this.velocity = velocity; - this.systemName = systemName; - this.systemType = systemType; - this.parameters = parameters?.ToBase(); - this.level = level; } public Vector startTangent { get; set; } public Vector endTangent { get; set; } + + #region Schema Info Constructor + + [SchemaInfo("RevitFlexDuct", "Creates a Revit flex duct", "Revit", "MEP")] + public RevitFlexDuct( + string family, + string type, + [SchemaMainParam] ICurve baseCurve, + string systemName, + string systemType, + Level level, + double width, + double height, + double diameter, + Vector startTangent, + Vector endTangent, + double velocity = 0, + List? parameters = null + ) + : this( + family, + type, + baseCurve, + systemName, + systemType, + level, + width, + height, + diameter, + 0, + startTangent, + endTangent, + null, + null, + velocity, + parameters: parameters + ) { } + + #endregion } diff --git a/src/Speckle.Objects/BuiltElements/Revit/RevitWall.cs b/src/Speckle.Objects/BuiltElements/Revit/RevitWall.cs index ac9d4bfe..a9d59dc5 100644 --- a/src/Speckle.Objects/BuiltElements/Revit/RevitWall.cs +++ b/src/Speckle.Objects/BuiltElements/Revit/RevitWall.cs @@ -11,69 +11,84 @@ public class RevitWall : Wall { public RevitWall() { } - /// - /// SchemaBuilder constructor for a Revit wall - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Assign units when using this constructor due to and params - [SchemaInfo( - "RevitWall by curve and levels", - "Creates a Revit wall with a top and base level.", - "Revit", - "Architecture" - )] public RevitWall( string family, string type, - [SchemaMainParam] ICurve baseLine, + ICurve baseLine, Level level, - Level topLevel, + Level? topLevel, + double height, + string? units, + string? elementId, double baseOffset = 0, double topOffset = 0, bool flipped = false, bool structural = false, - [SchemaParamInfo("Set in here any nested elements that this level might have.")] List? elements = null, + IReadOnlyList? displayValue = null, + List? elements = null, List? parameters = null ) + : base(height, units, baseLine, level, displayValue, elements) { this.family = family; this.type = type; - this.baseLine = baseLine; this.baseOffset = baseOffset; this.topOffset = topOffset; this.flipped = flipped; this.structural = structural; - this.level = level; + this.elementId = elementId; this.topLevel = topLevel; - this.elements = elements; this.parameters = parameters?.ToBase(); } - /// - /// SchemaBuilder constructor for a Revit wall - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Assign units when using this constructor due to , , and params + public string family { get; set; } + public string type { get; set; } + public double baseOffset { get; set; } + public double topOffset { get; set; } + public bool flipped { get; set; } + public bool structural { get; set; } + public Level? topLevel { get; set; } + public Base? parameters { get; set; } + public string? elementId { get; set; } + + #region Schema Info Constructors + + [SchemaInfo( + "RevitWall by curve and levels", + "Creates a Revit wall with a top and base level.", + "Revit", + "Architecture" + )] + public RevitWall( + string family, + string type, + [SchemaMainParam] ICurve baseLine, + Level level, + Level topLevel, + double baseOffset = 0, + double topOffset = 0, + bool flipped = false, + bool structural = false, + [SchemaParamInfo("Set in here any nested elements that this level might have.")] List? elements = null, + List? parameters = null + ) + : this( + family, + type, + baseLine, + level, + topLevel, + 0, + null, + null, + baseOffset, + topOffset, + flipped, + structural, + elements: elements, + parameters: parameters + ) { } + [SchemaInfo("RevitWall by curve and height", "Creates an unconnected Revit wall.", "Revit", "Architecture")] public RevitWall( string family, @@ -88,36 +103,23 @@ public RevitWall( [SchemaParamInfo("Set in here any nested elements that this wall might have.")] List? elements = null, List? parameters = null ) - { - this.family = family; - this.type = type; - this.baseLine = baseLine; - this.height = height; - this.baseOffset = baseOffset; - this.topOffset = topOffset; - this.flipped = flipped; - this.structural = structural; - this.level = level; - this.elements = elements; - this.parameters = parameters?.ToBase(); - } - - public string family { get; set; } - public string type { get; set; } - public double baseOffset { get; set; } - public double topOffset { get; set; } - public bool flipped { get; set; } - public bool structural { get; set; } - - public new Level? level - { - get => base.level; - set => base.level = value; - } - - public Level topLevel { get; set; } - public Base? parameters { get; set; } - public string elementId { get; set; } + : this( + family, + type, + baseLine, + level, + null, + height, + null, + null, + baseOffset, + topOffset, + flipped, + structural, + elements: elements, + parameters: parameters + ) { } + #endregion } public class RevitFaceWall : Wall diff --git a/src/Speckle.Objects/BuiltElements/Wall.cs b/src/Speckle.Objects/BuiltElements/Wall.cs index 9f6cb754..46b86843 100644 --- a/src/Speckle.Objects/BuiltElements/Wall.cs +++ b/src/Speckle.Objects/BuiltElements/Wall.cs @@ -5,39 +5,48 @@ namespace Objects.BuiltElements; -public class Wall : Base, IDisplayValue> +public class Wall : Base, IDisplayValue> { public Wall() { } - /// - /// SchemaBuilder constructor for a Speckle wall - /// - /// - /// - /// - /// Assign units when using this constructor due to param - [SchemaInfo("Wall", "Creates a Speckle wall", "BIM", "Architecture")] public Wall( double height, - [SchemaMainParam] ICurve baseLine, - [SchemaParamInfo("Any nested elements that this wall might have")] List? elements = null + string? units, + ICurve baseLine, + Level? level = null, + IReadOnlyList? displayValue = null, + List? elements = null ) { this.height = height; + this.units = units; this.baseLine = baseLine; + this.level = level; + this.displayValue = ((IReadOnlyList?)displayValue) ?? new[] { (Base)baseLine }; this.elements = elements; } public double height { get; set; } - [DetachProperty] - public List? elements { get; set; } - + public string? units { get; set; } public ICurve baseLine { get; set; } public virtual Level? level { get; internal set; } - public string units { get; set; } + [DetachProperty] + public List? elements { get; set; } [DetachProperty] - public List displayValue { get; set; } + public IReadOnlyList displayValue { get; set; } + + #region SchemaInfo Ctors + + [SchemaInfo("Wall", "Creates a Speckle wall", "BIM", "Architecture")] + public Wall( + double height, + [SchemaMainParam] ICurve baseLine, + [SchemaParamInfo("Any nested elements that this wall might have")] List? elements = null + ) + : this(height, null, baseLine, null, null, elements) { } + + #endregion } diff --git a/src/Speckle.Objects/Geometry/Point.cs b/src/Speckle.Objects/Geometry/Point.cs index 000ce698..244ece46 100644 --- a/src/Speckle.Objects/Geometry/Point.cs +++ b/src/Speckle.Objects/Geometry/Point.cs @@ -49,7 +49,7 @@ public Point(Vector vector) [JsonProperty(NullValueHandling = NullValueHandling.Ignore), Obsolete("Use x,y,z properties instead", true)] public List value { - get => null!; + get => new(); set { x = value[0]; diff --git a/src/Speckle.Objects/Interfaces.cs b/src/Speckle.Objects/Interfaces.cs index 9bd5aa4e..373e129d 100644 --- a/src/Speckle.Objects/Interfaces.cs +++ b/src/Speckle.Objects/Interfaces.cs @@ -28,7 +28,7 @@ public interface IHasArea /// /// The area of the object /// - double area { get; set; } + double area { get; } } /// @@ -39,7 +39,7 @@ public interface IHasVolume /// /// The volume of the object /// - double volume { get; set; } + double volume { get; } } /// @@ -50,12 +50,14 @@ public interface ICurve /// /// The length of the curve. /// - double length { get; set; } + double length { get; } /// /// The numerical domain driving the curve's internal parametrization. /// - Interval domain { get; set; } + Interval domain { get; } + + string units { get; } } /// @@ -110,5 +112,16 @@ public interface IDisplayValue T displayValue { get; } } +/// +/// Represents a calculated object for civil disciplines +/// +public interface ICivilCalculatedObject +{ + /// + /// for this calculated object. + /// + List codes { get; set; } +} + #endregion diff --git a/src/Speckle.Objects/ObjectsKit.cs b/src/Speckle.Objects/ObjectsKit.cs index 7a0def99..c09ed192 100644 --- a/src/Speckle.Objects/ObjectsKit.cs +++ b/src/Speckle.Objects/ObjectsKit.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; using System.Reflection; +using Speckle.Core.Common; using Speckle.Core.Helpers; using Speckle.Core.Kits; using Speckle.Core.Logging; @@ -76,9 +77,9 @@ public ISpeckleConverter LoadConverter(string app) private static ISpeckleConverter LoadConverterFromDisk(string app) { - var basePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + var basePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location).NotNull(); - var path = Path.Combine(basePath!, $"Objects.Converter.{app}.dll"); + var path = Path.Combine(basePath, $"Objects.Converter.{app}.dll"); //fallback to the default folder, in case the Objects.dll was loaded in the app domain for other reasons if (!File.Exists(path)) @@ -126,8 +127,8 @@ private static ISpeckleConverter LoadConverterFromDisk(string app) public List GetAvailableConverters() { - var basePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - var allConverters = Directory.EnumerateFiles(basePath!, "Objects.Converter.*.dll").ToArray(); + var basePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location).NotNull(); + var allConverters = Directory.EnumerateFiles(basePath, "Objects.Converter.*.dll").ToArray(); //fallback to the default folder, in case the Objects.dll was loaded in the app domain for other reasons if (allConverters.Length == 0) diff --git a/src/Speckle.Objects/Other/Civil/CivilDataField.cs b/src/Speckle.Objects/Other/Civil/CivilDataField.cs index 48e1a2ad..6919669d 100644 --- a/src/Speckle.Objects/Other/Civil/CivilDataField.cs +++ b/src/Speckle.Objects/Other/Civil/CivilDataField.cs @@ -4,17 +4,27 @@ public class CivilDataField : DataField { public CivilDataField() { } - public CivilDataField(string name, string type, string units, string context, object? value = null) + public CivilDataField( + string name, + string type, + object? value, + string? units = null, + string? context = null, + string? displayName = null + ) { this.name = name; this.type = type; + this.value = value; this.units = units; this.context = context; - this.value = value; + this.displayName = displayName; } /// /// The context type of the Civil3D part /// - public string context { get; set; } + public string? context { get; set; } + + public string? displayName { get; set; } } diff --git a/src/Speckle.Objects/Other/DataField.cs b/src/Speckle.Objects/Other/DataField.cs index 6770b3a8..4d3fd5a1 100644 --- a/src/Speckle.Objects/Other/DataField.cs +++ b/src/Speckle.Objects/Other/DataField.cs @@ -9,12 +9,12 @@ public class DataField : Base { public DataField() { } - public DataField(string name, string type, string units, object? value = null) + public DataField(string name, string type, object? value, string? units = null) { this.name = name; this.type = type; - this.units = units; this.value = value; + this.units = units; } public string name { get; set; } @@ -23,5 +23,5 @@ public DataField(string name, string type, string units, object? value = null) public object? value { get; set; } - public string units { get; set; } + public string? units { get; set; } } diff --git a/src/Speckle.Objects/Other/Transform.cs b/src/Speckle.Objects/Other/Transform.cs index d3a64844..74d38e0b 100644 --- a/src/Speckle.Objects/Other/Transform.cs +++ b/src/Speckle.Objects/Other/Transform.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; -using System.DoubleNumerics; using Objects.Geometry; using Speckle.Core.Kits; using Speckle.Core.Logging; using Speckle.Core.Models; +using Speckle.DoubleNumerics; using Speckle.Newtonsoft.Json; using Vector = Objects.Geometry.Vector; diff --git a/src/Speckle.Objects/Structural/Geometry/Element1D.cs b/src/Speckle.Objects/Structural/Geometry/Element1D.cs index d1d74eab..abce21d7 100644 --- a/src/Speckle.Objects/Structural/Geometry/Element1D.cs +++ b/src/Speckle.Objects/Structural/Geometry/Element1D.cs @@ -6,27 +6,74 @@ namespace Objects.Structural.Geometry; -public class Element1D : Base, IDisplayValue> +public class Element1D : Base, IDisplayValue> { public Element1D() { } - public Element1D(Line baseLine) + public Element1D( + Line baseLine, + Property1D property, + ElementType1D type, + string? name, + string? units, + Restraint? end1Releases = null, + Restraint? end2Releases = null, + Vector? end1Offset = null, + Vector? end2Offset = null, + Plane? localAxis = null, + Node? orientationNode = null, + double orientationAngle = 0, + IReadOnlyList? displayValue = null + ) { this.baseLine = baseLine; + this.property = property; + this.type = type; + this.name = name; + this.units = units; + this.end1Releases = end1Releases ?? new Restraint("FFFFFF"); + this.end2Releases = end2Releases ?? new Restraint("FFFFFF"); + this.end1Offset = end1Offset ?? new Vector(0, 0, 0); + this.end2Offset = end2Offset ?? new Vector(0, 0, 0); + this.localAxis = localAxis; + this.orientationNode = orientationNode; + this.orientationAngle = orientationAngle; + this.displayValue = ((IReadOnlyList?)displayValue) ?? new[] { (Base)baseLine }; } - /// - /// SchemaBuilder constructor for structural 1D element (based on local axis) - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// + public string? name { get; set; } //add unique id as base identifier, name can change too easily + public Line baseLine { get; set; } + + [DetachProperty] + public Property1D property { get; set; } + + public ElementType1D type { get; set; } + public Restraint end1Releases { get; set; } + public Restraint end2Releases { get; set; } + public Vector end1Offset { get; set; } + public Vector end2Offset { get; set; } + public Node? orientationNode { get; set; } + public double orientationAngle { get; set; } + public Plane? localAxis { get; set; } + + [DetachProperty] + public Base? parent { get; set; } //parent element + + [DetachProperty] + public Node? end1Node { get; set; } //startNode + + [DetachProperty] + public Node? end2Node { get; set; } //endNode + + [DetachProperty] + public List? topology { get; set; } + + public string? units { get; set; } + + [DetachProperty] + public IReadOnlyList displayValue { get; set; } + + #region Schema Info Constructors [SchemaInfo( "Element1D (from local axis)", "Creates a Speckle structural 1D element (from local axis)", @@ -46,31 +93,8 @@ public Element1D( [SchemaParamInfo("If null, defaults to no offsets")] Vector? end2Offset = null, Plane? localAxis = null ) - { - this.baseLine = baseLine; - this.property = property; - this.type = type; - this.name = name; - this.end1Releases = end1Releases ?? new Restraint("FFFFFF"); - this.end2Releases = end2Releases ?? new Restraint("FFFFFF"); - this.end1Offset = end1Offset ?? new Vector(0, 0, 0); - this.end2Offset = end2Offset ?? new Vector(0, 0, 0); - this.localAxis = localAxis; - } + : this(baseLine, property, type, name, null, end1Releases, end2Releases, end1Offset, end2Offset, localAxis) { } - /// - /// SchemaBuilder constructor for structural 1D element (based on orientation node and angle) - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// [SchemaInfo( "Element1D (from orientation node and angle)", "Creates a Speckle structural 1D element (from orientation node and angle)", @@ -91,48 +115,19 @@ public Element1D( Node? orientationNode = null, double orientationAngle = 0 ) - { - this.baseLine = baseLine; - this.property = property; - this.type = type; - this.name = name; - this.end1Releases = end1Releases ?? new Restraint("FFFFFF"); - this.end2Releases = end2Releases ?? new Restraint("FFFFFF"); - this.end1Offset = end1Offset ?? new Vector(0, 0, 0); - this.end2Offset = end2Offset ?? new Vector(0, 0, 0); - this.orientationNode = orientationNode; - this.orientationAngle = orientationAngle; - } - - public string? name { get; set; } //add unique id as base identifier, name can change too easily - public Line baseLine { get; set; } + : this( + baseLine, + property, + type, + name, + null, + end1Releases, + end2Releases, + end1Offset, + end2Offset, + orientationNode: orientationNode, + orientationAngle: orientationAngle + ) { } - [DetachProperty] - public Property1D property { get; set; } - - public ElementType1D type { get; set; } - public Restraint end1Releases { get; set; } - public Restraint end2Releases { get; set; } - public Vector end1Offset { get; set; } - public Vector end2Offset { get; set; } - public Node? orientationNode { get; set; } - public double orientationAngle { get; set; } - public Plane? localAxis { get; set; } - - [DetachProperty] - public Base parent { get; set; } //parent element - - [DetachProperty] - public Node end1Node { get; set; } //startNode - - [DetachProperty] - public Node end2Node { get; set; } //endNode - - [DetachProperty] - public List topology { get; set; } - - public string units { get; set; } - - [DetachProperty] - public List displayValue { get; set; } + #endregion } diff --git a/src/Speckle.Objects/packages.lock.json b/src/Speckle.Objects/packages.lock.json index 51b3b55c..77bbee9d 100644 --- a/src/Speckle.Objects/packages.lock.json +++ b/src/Speckle.Objects/packages.lock.json @@ -107,10 +107,10 @@ }, "Microsoft.Data.Sqlite.Core": { "type": "Transitive", - "resolved": "7.0.5", - "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "resolved": "8.0.6", + "contentHash": "umhZ0ZF2RI81rGFTnYmCxI+Euj4Aqe/6Y4+8CxN9OVJNGDNIqB5laJ3wxQTU8zXCcm2k9F7FL+/6RVoOT4z1Fw==", "dependencies": { - "SQLitePCLRaw.core": "2.1.4" + "SQLitePCLRaw.core": "2.1.6" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { @@ -191,32 +191,32 @@ }, "SQLitePCLRaw.bundle_e_sqlite3": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "resolved": "2.1.6", + "contentHash": "BmAf6XWt4TqtowmiWe4/5rRot6GerAeklmOPfviOvwLoF5WwgxcJHAxZtySuyW9r9w+HLILnm8VfJFLCUJYW8A==", "dependencies": { - "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", - "SQLitePCLRaw.provider.e_sqlite3": "2.1.4" + "SQLitePCLRaw.lib.e_sqlite3": "2.1.6", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.6" } }, "SQLitePCLRaw.core": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "resolved": "2.1.6", + "contentHash": "wO6v9GeMx9CUngAet8hbO7xdm+M42p1XeJq47ogyRoYSvNSp0NGLI+MgC0bhrMk9C17MTVFlLiN6ylyExLCc5w==", "dependencies": { "System.Memory": "4.5.3" } }, "SQLitePCLRaw.lib.e_sqlite3": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + "resolved": "2.1.6", + "contentHash": "2ObJJLkIUIxRpOUlZNGuD4rICpBnrBR5anjyfUFQep4hMOIeqW+XGQYzrNmHSVz5xSWZ3klSbh7sFR6UyDj68Q==" }, "SQLitePCLRaw.provider.e_sqlite3": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==", + "resolved": "2.1.6", + "contentHash": "PQ2Oq3yepLY4P7ll145P3xtx2bX8xF4PzaKPRpw9jZlKvfe4LE/saAV82inND9usn1XRpmxXk7Lal3MTI+6CNg==", "dependencies": { - "SQLitePCLRaw.core": "2.1.4" + "SQLitePCLRaw.core": "2.1.6" } }, "System.Buffers": { @@ -327,7 +327,7 @@ "dependencies": { "GraphQL.Client": "[6.0.0, )", "Microsoft.CSharp": "[4.7.0, )", - "Microsoft.Data.Sqlite": "[7.0.5, )", + "Microsoft.Data.Sqlite": "[8.0.6, )", "Polly": "[7.2.3, )", "Polly.Contrib.WaitAndRetry": "[1.1.1, )", "Polly.Extensions.Http": "[3.0.0, )", @@ -339,8 +339,8 @@ "Serilog.Sinks.Console": "[4.1.0, )", "Serilog.Sinks.Seq": "[5.2.2, )", "SerilogTimings": "[3.0.1, )", - "Speckle.Newtonsoft.Json": "[13.0.2, )", - "System.DoubleNumerics": "[3.1.3, )" + "Speckle.DoubleNumerics": "[4.0.1-alpha.3, )", + "Speckle.Newtonsoft.Json": "[13.0.2, )" } }, "GraphQL.Client": { @@ -362,12 +362,12 @@ }, "Microsoft.Data.Sqlite": { "type": "CentralTransitive", - "requested": "[7.0.5, )", - "resolved": "7.0.5", - "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "requested": "[8.0.6, )", + "resolved": "8.0.6", + "contentHash": "YVzVtU1IoGsTiMAe0BNV9ssKyrUm6lBQJP6w2N4W29YrBYYtyCmTFmrNGjxaJtJXVqttu0sYkPxmStj/OnMFdg==", "dependencies": { - "Microsoft.Data.Sqlite.Core": "7.0.5", - "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + "Microsoft.Data.Sqlite.Core": "8.0.6", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.6" } }, "Polly": { @@ -467,20 +467,17 @@ "Serilog": "2.10.0" } }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.0.1-alpha.3, )", + "resolved": "4.0.1-alpha.3", + "contentHash": "7/lg9LDI3TZLiI7FhI2IhRrTzsZyDrQw1rG/S3kRtx0IU3raWzRHL4W5zvHV8LdWkqJEztS/9dUgLNYHV70o5Q==" + }, "Speckle.Newtonsoft.Json": { "type": "CentralTransitive", "requested": "[13.0.2, )", "resolved": "13.0.2", "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" - }, - "System.DoubleNumerics": { - "type": "CentralTransitive", - "requested": "[3.1.3, )", - "resolved": "3.1.3", - "contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==", - "dependencies": { - "NETStandard.Library": "1.6.1" - } } } } diff --git a/src/Speckle.Transports.Disk/packages.lock.json b/src/Speckle.Transports.Disk/packages.lock.json index 51b3b55c..77bbee9d 100644 --- a/src/Speckle.Transports.Disk/packages.lock.json +++ b/src/Speckle.Transports.Disk/packages.lock.json @@ -107,10 +107,10 @@ }, "Microsoft.Data.Sqlite.Core": { "type": "Transitive", - "resolved": "7.0.5", - "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "resolved": "8.0.6", + "contentHash": "umhZ0ZF2RI81rGFTnYmCxI+Euj4Aqe/6Y4+8CxN9OVJNGDNIqB5laJ3wxQTU8zXCcm2k9F7FL+/6RVoOT4z1Fw==", "dependencies": { - "SQLitePCLRaw.core": "2.1.4" + "SQLitePCLRaw.core": "2.1.6" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { @@ -191,32 +191,32 @@ }, "SQLitePCLRaw.bundle_e_sqlite3": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "resolved": "2.1.6", + "contentHash": "BmAf6XWt4TqtowmiWe4/5rRot6GerAeklmOPfviOvwLoF5WwgxcJHAxZtySuyW9r9w+HLILnm8VfJFLCUJYW8A==", "dependencies": { - "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", - "SQLitePCLRaw.provider.e_sqlite3": "2.1.4" + "SQLitePCLRaw.lib.e_sqlite3": "2.1.6", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.6" } }, "SQLitePCLRaw.core": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "resolved": "2.1.6", + "contentHash": "wO6v9GeMx9CUngAet8hbO7xdm+M42p1XeJq47ogyRoYSvNSp0NGLI+MgC0bhrMk9C17MTVFlLiN6ylyExLCc5w==", "dependencies": { "System.Memory": "4.5.3" } }, "SQLitePCLRaw.lib.e_sqlite3": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + "resolved": "2.1.6", + "contentHash": "2ObJJLkIUIxRpOUlZNGuD4rICpBnrBR5anjyfUFQep4hMOIeqW+XGQYzrNmHSVz5xSWZ3klSbh7sFR6UyDj68Q==" }, "SQLitePCLRaw.provider.e_sqlite3": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==", + "resolved": "2.1.6", + "contentHash": "PQ2Oq3yepLY4P7ll145P3xtx2bX8xF4PzaKPRpw9jZlKvfe4LE/saAV82inND9usn1XRpmxXk7Lal3MTI+6CNg==", "dependencies": { - "SQLitePCLRaw.core": "2.1.4" + "SQLitePCLRaw.core": "2.1.6" } }, "System.Buffers": { @@ -327,7 +327,7 @@ "dependencies": { "GraphQL.Client": "[6.0.0, )", "Microsoft.CSharp": "[4.7.0, )", - "Microsoft.Data.Sqlite": "[7.0.5, )", + "Microsoft.Data.Sqlite": "[8.0.6, )", "Polly": "[7.2.3, )", "Polly.Contrib.WaitAndRetry": "[1.1.1, )", "Polly.Extensions.Http": "[3.0.0, )", @@ -339,8 +339,8 @@ "Serilog.Sinks.Console": "[4.1.0, )", "Serilog.Sinks.Seq": "[5.2.2, )", "SerilogTimings": "[3.0.1, )", - "Speckle.Newtonsoft.Json": "[13.0.2, )", - "System.DoubleNumerics": "[3.1.3, )" + "Speckle.DoubleNumerics": "[4.0.1-alpha.3, )", + "Speckle.Newtonsoft.Json": "[13.0.2, )" } }, "GraphQL.Client": { @@ -362,12 +362,12 @@ }, "Microsoft.Data.Sqlite": { "type": "CentralTransitive", - "requested": "[7.0.5, )", - "resolved": "7.0.5", - "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "requested": "[8.0.6, )", + "resolved": "8.0.6", + "contentHash": "YVzVtU1IoGsTiMAe0BNV9ssKyrUm6lBQJP6w2N4W29YrBYYtyCmTFmrNGjxaJtJXVqttu0sYkPxmStj/OnMFdg==", "dependencies": { - "Microsoft.Data.Sqlite.Core": "7.0.5", - "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + "Microsoft.Data.Sqlite.Core": "8.0.6", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.6" } }, "Polly": { @@ -467,20 +467,17 @@ "Serilog": "2.10.0" } }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.0.1-alpha.3, )", + "resolved": "4.0.1-alpha.3", + "contentHash": "7/lg9LDI3TZLiI7FhI2IhRrTzsZyDrQw1rG/S3kRtx0IU3raWzRHL4W5zvHV8LdWkqJEztS/9dUgLNYHV70o5Q==" + }, "Speckle.Newtonsoft.Json": { "type": "CentralTransitive", "requested": "[13.0.2, )", "resolved": "13.0.2", "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" - }, - "System.DoubleNumerics": { - "type": "CentralTransitive", - "requested": "[3.1.3, )", - "resolved": "3.1.3", - "contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==", - "dependencies": { - "NETStandard.Library": "1.6.1" - } } } } diff --git a/src/Speckle.Transports.MongoDB/packages.lock.json b/src/Speckle.Transports.MongoDB/packages.lock.json index 8181b278..15820f07 100644 --- a/src/Speckle.Transports.MongoDB/packages.lock.json +++ b/src/Speckle.Transports.MongoDB/packages.lock.json @@ -144,10 +144,10 @@ }, "Microsoft.Data.Sqlite.Core": { "type": "Transitive", - "resolved": "7.0.5", - "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "resolved": "8.0.6", + "contentHash": "umhZ0ZF2RI81rGFTnYmCxI+Euj4Aqe/6Y4+8CxN9OVJNGDNIqB5laJ3wxQTU8zXCcm2k9F7FL+/6RVoOT4z1Fw==", "dependencies": { - "SQLitePCLRaw.core": "2.1.4" + "SQLitePCLRaw.core": "2.1.6" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { @@ -292,32 +292,32 @@ }, "SQLitePCLRaw.bundle_e_sqlite3": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "resolved": "2.1.6", + "contentHash": "BmAf6XWt4TqtowmiWe4/5rRot6GerAeklmOPfviOvwLoF5WwgxcJHAxZtySuyW9r9w+HLILnm8VfJFLCUJYW8A==", "dependencies": { - "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", - "SQLitePCLRaw.provider.e_sqlite3": "2.1.4" + "SQLitePCLRaw.lib.e_sqlite3": "2.1.6", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.6" } }, "SQLitePCLRaw.core": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "resolved": "2.1.6", + "contentHash": "wO6v9GeMx9CUngAet8hbO7xdm+M42p1XeJq47ogyRoYSvNSp0NGLI+MgC0bhrMk9C17MTVFlLiN6ylyExLCc5w==", "dependencies": { "System.Memory": "4.5.3" } }, "SQLitePCLRaw.lib.e_sqlite3": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + "resolved": "2.1.6", + "contentHash": "2ObJJLkIUIxRpOUlZNGuD4rICpBnrBR5anjyfUFQep4hMOIeqW+XGQYzrNmHSVz5xSWZ3klSbh7sFR6UyDj68Q==" }, "SQLitePCLRaw.provider.e_sqlite3": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==", + "resolved": "2.1.6", + "contentHash": "PQ2Oq3yepLY4P7ll145P3xtx2bX8xF4PzaKPRpw9jZlKvfe4LE/saAV82inND9usn1XRpmxXk7Lal3MTI+6CNg==", "dependencies": { - "SQLitePCLRaw.core": "2.1.4" + "SQLitePCLRaw.core": "2.1.6" } }, "System.Buffers": { @@ -458,7 +458,7 @@ "dependencies": { "GraphQL.Client": "[6.0.0, )", "Microsoft.CSharp": "[4.7.0, )", - "Microsoft.Data.Sqlite": "[7.0.5, )", + "Microsoft.Data.Sqlite": "[8.0.6, )", "Polly": "[7.2.3, )", "Polly.Contrib.WaitAndRetry": "[1.1.1, )", "Polly.Extensions.Http": "[3.0.0, )", @@ -470,8 +470,8 @@ "Serilog.Sinks.Console": "[4.1.0, )", "Serilog.Sinks.Seq": "[5.2.2, )", "SerilogTimings": "[3.0.1, )", - "Speckle.Newtonsoft.Json": "[13.0.2, )", - "System.DoubleNumerics": "[3.1.3, )" + "Speckle.DoubleNumerics": "[4.0.1-alpha.3, )", + "Speckle.Newtonsoft.Json": "[13.0.2, )" } }, "GraphQL.Client": { @@ -493,12 +493,12 @@ }, "Microsoft.Data.Sqlite": { "type": "CentralTransitive", - "requested": "[7.0.5, )", - "resolved": "7.0.5", - "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "requested": "[8.0.6, )", + "resolved": "8.0.6", + "contentHash": "YVzVtU1IoGsTiMAe0BNV9ssKyrUm6lBQJP6w2N4W29YrBYYtyCmTFmrNGjxaJtJXVqttu0sYkPxmStj/OnMFdg==", "dependencies": { - "Microsoft.Data.Sqlite.Core": "7.0.5", - "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + "Microsoft.Data.Sqlite.Core": "8.0.6", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.6" } }, "Polly": { @@ -598,20 +598,17 @@ "Serilog": "2.10.0" } }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.0.1-alpha.3, )", + "resolved": "4.0.1-alpha.3", + "contentHash": "7/lg9LDI3TZLiI7FhI2IhRrTzsZyDrQw1rG/S3kRtx0IU3raWzRHL4W5zvHV8LdWkqJEztS/9dUgLNYHV70o5Q==" + }, "Speckle.Newtonsoft.Json": { "type": "CentralTransitive", "requested": "[13.0.2, )", "resolved": "13.0.2", "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" - }, - "System.DoubleNumerics": { - "type": "CentralTransitive", - "requested": "[3.1.3, )", - "resolved": "3.1.3", - "contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==", - "dependencies": { - "NETStandard.Library": "1.6.1" - } } } } diff --git a/tests/Speckle.Core.Serialization.Tests/Speckle.Core.Serialization.Tests.csproj b/tests/Speckle.Core.Serialization.Tests/Speckle.Core.Serialization.Tests.csproj index edf406d1..baba3242 100644 --- a/tests/Speckle.Core.Serialization.Tests/Speckle.Core.Serialization.Tests.csproj +++ b/tests/Speckle.Core.Serialization.Tests/Speckle.Core.Serialization.Tests.csproj @@ -4,12 +4,13 @@ net8.0 enable true + System.Runtime.CompilerServices.IsExternalInit;System.Runtime.CompilerServices.RequiresLocationAttribute + - diff --git a/tests/Speckle.Core.Serialization.Tests/packages.lock.json b/tests/Speckle.Core.Serialization.Tests/packages.lock.json index ae486086..cb4fd45f 100644 --- a/tests/Speckle.Core.Serialization.Tests/packages.lock.json +++ b/tests/Speckle.Core.Serialization.Tests/packages.lock.json @@ -2,6 +2,12 @@ "version": 2, "dependencies": { "net8.0": { + "altcover": { + "type": "Direct", + "requested": "[8.8.74, )", + "resolved": "8.8.74", + "contentHash": "e8RZNE0vZnuBk/gOAWu9K5wm3S7dOrOlZje3PHI9PJUHqvP1cxVJD1eXAAmddFVlixowB7C7/zvC16GnunC2LQ==" + }, "FluentAssertions": { "type": "Direct", "requested": "[6.12.0, )", @@ -11,12 +17,6 @@ "System.Configuration.ConfigurationManager": "4.4.0" } }, - "JunitXml.TestLogger": { - "type": "Direct", - "requested": "[3.0.124, )", - "resolved": "3.0.124", - "contentHash": "QTZhSNm/xjj24W1yterf6eABv6KO+Y9jBqpau5RzPehdXTXkZcGQaLf/i50nTl+qnSwpbKkrC+bSyvLRE1ZNAg==" - }, "Microsoft.NET.Test.Sdk": { "type": "Direct", "requested": "[17.10.0, )", @@ -132,10 +132,10 @@ }, "Microsoft.Data.Sqlite.Core": { "type": "Transitive", - "resolved": "7.0.5", - "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "resolved": "8.0.6", + "contentHash": "umhZ0ZF2RI81rGFTnYmCxI+Euj4Aqe/6Y4+8CxN9OVJNGDNIqB5laJ3wxQTU8zXCcm2k9F7FL+/6RVoOT4z1Fw==", "dependencies": { - "SQLitePCLRaw.core": "2.1.4" + "SQLitePCLRaw.core": "2.1.6" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { @@ -229,32 +229,32 @@ }, "SQLitePCLRaw.bundle_e_sqlite3": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "resolved": "2.1.6", + "contentHash": "BmAf6XWt4TqtowmiWe4/5rRot6GerAeklmOPfviOvwLoF5WwgxcJHAxZtySuyW9r9w+HLILnm8VfJFLCUJYW8A==", "dependencies": { - "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", - "SQLitePCLRaw.provider.e_sqlite3": "2.1.4" + "SQLitePCLRaw.lib.e_sqlite3": "2.1.6", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.6" } }, "SQLitePCLRaw.core": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "resolved": "2.1.6", + "contentHash": "wO6v9GeMx9CUngAet8hbO7xdm+M42p1XeJq47ogyRoYSvNSp0NGLI+MgC0bhrMk9C17MTVFlLiN6ylyExLCc5w==", "dependencies": { "System.Memory": "4.5.3" } }, "SQLitePCLRaw.lib.e_sqlite3": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + "resolved": "2.1.6", + "contentHash": "2ObJJLkIUIxRpOUlZNGuD4rICpBnrBR5anjyfUFQep4hMOIeqW+XGQYzrNmHSVz5xSWZ3klSbh7sFR6UyDj68Q==" }, "SQLitePCLRaw.provider.e_sqlite3": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==", + "resolved": "2.1.6", + "contentHash": "PQ2Oq3yepLY4P7ll145P3xtx2bX8xF4PzaKPRpw9jZlKvfe4LE/saAV82inND9usn1XRpmxXk7Lal3MTI+6CNg==", "dependencies": { - "SQLitePCLRaw.core": "2.1.4" + "SQLitePCLRaw.core": "2.1.6" } }, "System.Buffers": { @@ -315,7 +315,7 @@ "dependencies": { "GraphQL.Client": "[6.0.0, )", "Microsoft.CSharp": "[4.7.0, )", - "Microsoft.Data.Sqlite": "[7.0.5, )", + "Microsoft.Data.Sqlite": "[8.0.6, )", "Polly": "[7.2.3, )", "Polly.Contrib.WaitAndRetry": "[1.1.1, )", "Polly.Extensions.Http": "[3.0.0, )", @@ -327,8 +327,8 @@ "Serilog.Sinks.Console": "[4.1.0, )", "Serilog.Sinks.Seq": "[5.2.2, )", "SerilogTimings": "[3.0.1, )", - "Speckle.Newtonsoft.Json": "[13.0.2, )", - "System.DoubleNumerics": "[3.1.3, )" + "Speckle.DoubleNumerics": "[4.0.1-alpha.3, )", + "Speckle.Newtonsoft.Json": "[13.0.2, )" } }, "speckle.objects": { @@ -356,12 +356,12 @@ }, "Microsoft.Data.Sqlite": { "type": "CentralTransitive", - "requested": "[7.0.5, )", - "resolved": "7.0.5", - "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "requested": "[8.0.6, )", + "resolved": "8.0.6", + "contentHash": "YVzVtU1IoGsTiMAe0BNV9ssKyrUm6lBQJP6w2N4W29YrBYYtyCmTFmrNGjxaJtJXVqttu0sYkPxmStj/OnMFdg==", "dependencies": { - "Microsoft.Data.Sqlite.Core": "7.0.5", - "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + "Microsoft.Data.Sqlite.Core": "8.0.6", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.6" } }, "Polly": { @@ -457,20 +457,17 @@ "Serilog": "2.10.0" } }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.0.1-alpha.3, )", + "resolved": "4.0.1-alpha.3", + "contentHash": "7/lg9LDI3TZLiI7FhI2IhRrTzsZyDrQw1rG/S3kRtx0IU3raWzRHL4W5zvHV8LdWkqJEztS/9dUgLNYHV70o5Q==" + }, "Speckle.Newtonsoft.Json": { "type": "CentralTransitive", "requested": "[13.0.2, )", "resolved": "13.0.2", "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" - }, - "System.DoubleNumerics": { - "type": "CentralTransitive", - "requested": "[3.1.3, )", - "resolved": "3.1.3", - "contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==", - "dependencies": { - "NETStandard.Library": "1.6.1" - } } } } diff --git a/tests/Speckle.Core.Tests.Integration/Api.cs b/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/LegacyAPITests.cs similarity index 96% rename from tests/Speckle.Core.Tests.Integration/Api.cs rename to tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/LegacyAPITests.cs index bc3b574f..fa2755b7 100644 --- a/tests/Speckle.Core.Tests.Integration/Api.cs +++ b/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/LegacyAPITests.cs @@ -1,12 +1,14 @@ +using Shouldly; using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL; using Speckle.Core.Credentials; using Speckle.Core.Models; using Speckle.Core.Tests.Unit.Kits; using Speckle.Core.Transports; -namespace Speckle.Core.Tests.Integration; +namespace Speckle.Core.Tests.Integration.Api.GraphQL.Legacy; -public class Api : IDisposable +public class LegacyAPITests : IDisposable { private string _branchId = ""; private string _branchName = ""; @@ -47,14 +49,15 @@ private void InitServerTransport() public async Task ActiveUserGet() { var res = await _myClient.ActiveUserGet(); - Assert.That(res.id, Is.EqualTo(_myClient.Account.userInfo.id)); + Assert.That(res!.id, Is.EqualTo(_myClient.Account.userInfo.id)); } [Test] public async Task OtherUserGet() { var res = await _myClient.OtherUserGet(_secondUserAccount.userInfo.id); - Assert.That(res!.name, Is.EqualTo(_secondUserAccount.userInfo.name)); + res.ShouldNotBeNull(); + Assert.That(res.name, Is.EqualTo(_secondUserAccount.userInfo.name)); } [Test] @@ -177,7 +180,7 @@ public async Task StreamUpdatePermission() var res = await _myClient.StreamUpdatePermission( new StreamPermissionInput { - role = "stream:reviewer", + role = StreamRoles.STREAM_REVIEWER, streamId = _streamId, userId = _secondUserAccount.userInfo.id } diff --git a/tests/Speckle.Core.Tests.Integration/Subscriptions/Branches.cs b/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Branches.cs similarity index 97% rename from tests/Speckle.Core.Tests.Integration/Subscriptions/Branches.cs rename to tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Branches.cs index dce80673..2833a279 100644 --- a/tests/Speckle.Core.Tests.Integration/Subscriptions/Branches.cs +++ b/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Branches.cs @@ -2,7 +2,7 @@ using Speckle.Core.Api.SubscriptionModels; using Speckle.Core.Credentials; -namespace Speckle.Core.Tests.Integration.Subscriptions; +namespace Speckle.Core.Tests.Integration.Api.GraphQL.Legacy.Subscriptions; public class Branches : IDisposable { diff --git a/tests/Speckle.Core.Tests.Integration/Subscriptions/Commits.cs b/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Commits.cs similarity index 98% rename from tests/Speckle.Core.Tests.Integration/Subscriptions/Commits.cs rename to tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Commits.cs index ed10de7b..7e25fad6 100644 --- a/tests/Speckle.Core.Tests.Integration/Subscriptions/Commits.cs +++ b/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Commits.cs @@ -5,7 +5,7 @@ using Speckle.Core.Tests.Unit.Kits; using Speckle.Core.Transports; -namespace Speckle.Core.Tests.Integration.Subscriptions; +namespace Speckle.Core.Tests.Integration.Api.GraphQL.Legacy.Subscriptions; public class Commits : IDisposable { diff --git a/tests/Speckle.Core.Tests.Integration/Subscriptions/Streams.cs b/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Streams.cs similarity index 97% rename from tests/Speckle.Core.Tests.Integration/Subscriptions/Streams.cs rename to tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Streams.cs index 5acd4d71..457c2d6b 100644 --- a/tests/Speckle.Core.Tests.Integration/Subscriptions/Streams.cs +++ b/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Streams.cs @@ -2,7 +2,7 @@ using Speckle.Core.Api.SubscriptionModels; using Speckle.Core.Credentials; -namespace Speckle.Core.Tests.Integration.Subscriptions; +namespace Speckle.Core.Tests.Integration.Api.GraphQL.Legacy.Subscriptions; public class Streams : IDisposable { diff --git a/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ActiveUserResourceTests.cs b/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ActiveUserResourceTests.cs new file mode 100644 index 00000000..6615ac8e --- /dev/null +++ b/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ActiveUserResourceTests.cs @@ -0,0 +1,52 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(ActiveUserResource))] +public class ActiveUserResourceTests +{ + private Client _testUser; + private ActiveUserResource Sut => _testUser.ActiveUser; + + [OneTimeSetUp] + public async Task Setup() + { + _testUser = await Fixtures.SeedUserWithClient(); + } + + [Test] + public async Task ActiveUserGet() + { + var res = await Sut.Get(); + Assert.That(res, Is.Not.Null); + Assert.That(res!.id, Is.EqualTo(_testUser.Account.userInfo.id)); + } + + [Test] + public async Task ActiveUserGet_NonAuthed() + { + var result = await Fixtures.Unauthed.ActiveUser.Get(); + Assert.That(result, Is.EqualTo(null)); + } + + [Test] + public async Task ActiveUserGetProjects() + { + var p1 = await _testUser.Project.Create(new("Project 1", null, null)); + var p2 = await _testUser.Project.Create(new("Project 2", null, null)); + + var res = await Sut.GetProjects(); + + Assert.That(res.items, Has.Exactly(1).Items.With.Property(nameof(Project.id)).EqualTo(p1.id)); + Assert.That(res.items, Has.Exactly(1).Items.With.Property(nameof(Project.id)).EqualTo(p2.id)); + Assert.That(res.items, Has.Count.EqualTo(2)); + } + + [Test] + public void ActiveUserGetProjects_NoAuth() + { + Assert.ThrowsAsync(async () => await Fixtures.Unauthed.ActiveUser.GetProjects()); + } +} diff --git a/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/CommentResourceTests.cs b/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/CommentResourceTests.cs new file mode 100644 index 00000000..cdf25948 --- /dev/null +++ b/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/CommentResourceTests.cs @@ -0,0 +1,97 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(CommentResource))] +public class CommentResourceTests +{ + private Client _testUser; + private CommentResource Sut => _testUser.Comment; + private Project _project; + private Model _model; + private string _versionId; + private Comment _comment; + + [SetUp] + public async Task Setup() + { + _testUser = await Fixtures.SeedUserWithClient(); + _project = await _testUser.Project.Create(new("Test project", "", null)); + _model = await _testUser.Model.Create(new("Test Model 1", "", _project.id)); + _versionId = await Fixtures.CreateVersion(_testUser, _project.id, _model.name); + _comment = await CreateComment(); + } + + [Test] + public async Task GetProjectComments() + { + var comments = await Sut.GetProjectComments(_project.id); + Assert.That(comments.items.Count, Is.EqualTo(1)); + Assert.That(comments.totalCount, Is.EqualTo(1)); + + Comment comment = comments.items[0]; + Assert.That(comment, Is.Not.Null); + Assert.That(comment, Has.Property(nameof(Comment.authorId)).EqualTo(_testUser.Account.userInfo.id)); + + Assert.That(comment, Has.Property(nameof(Comment.id)).EqualTo(_comment.id)); + Assert.That(comment, Has.Property(nameof(Comment.authorId)).EqualTo(_comment.authorId)); + Assert.That(comment, Has.Property(nameof(Comment.archived)).EqualTo(_comment.archived)); + Assert.That(comment, Has.Property(nameof(Comment.archived)).EqualTo(false)); + Assert.That(comment, Has.Property(nameof(Comment.createdAt)).EqualTo(_comment.createdAt)); + } + + [Test] + public async Task MarkViewed() + { + var viewed = await Sut.MarkViewed(_comment.id); + Assert.That(viewed, Is.True); + viewed = await Sut.MarkViewed(_comment.id); + Assert.That(viewed, Is.True); + } + + [Test] + public async Task Archive() + { + var archived = await Sut.Archive(_comment.id); + Assert.That(archived, Is.True); + + archived = await Sut.Archive(_comment.id); + Assert.That(archived, Is.True); + } + + [Test] + public async Task Edit() + { + var blobs = await Fixtures.SendBlobData(_testUser.Account, _project.id); + var blobIds = blobs.Select(b => b.id).ToList(); + EditCommentInput input = new(new(blobIds, null), _comment.id); + + var editedComment = await Sut.Edit(input); + + Assert.That(editedComment, Is.Not.Null); + Assert.That(editedComment, Has.Property(nameof(Comment.id)).EqualTo(_comment.id)); + Assert.That(editedComment, Has.Property(nameof(Comment.authorId)).EqualTo(_comment.authorId)); + Assert.That(editedComment, Has.Property(nameof(Comment.createdAt)).EqualTo(_comment.createdAt)); + Assert.That(editedComment, Has.Property(nameof(Comment.updatedAt)).GreaterThanOrEqualTo(_comment.updatedAt)); + } + + [Test] + public async Task Reply() + { + var blobs = await Fixtures.SendBlobData(_testUser.Account, _project.id); + var blobIds = blobs.Select(b => b.id).ToList(); + CreateCommentReplyInput input = new(new(blobIds, null), _comment.id); + + var editedComment = await Sut.Reply(input); + + Assert.That(editedComment, Is.Not.Null); + } + + private async Task CreateComment() + { + return await Fixtures.CreateComment(_testUser, _project.id, _model.id, _versionId); + } +} diff --git a/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ModelResourceExceptionalTests.cs b/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ModelResourceExceptionalTests.cs new file mode 100644 index 00000000..8fe43ba1 --- /dev/null +++ b/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ModelResourceExceptionalTests.cs @@ -0,0 +1,88 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Enums; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(ModelResource))] +public class ModelResourceExceptionalTests +{ + private Client _testUser; + private ModelResource Sut => _testUser.Model; + private Project _project; + private Model _model; + + [OneTimeSetUp] + public async Task Setup() + { + _testUser = await Fixtures.SeedUserWithClient(); + _project = await _testUser.Project.Create(new("Test project", "", ProjectVisibility.Private)); + _model = await _testUser.Model.Create(new("Test Model", "", _project.id)); + } + + [TestCase(null)] + [TestCase("")] + [TestCase(" ")] + public void ModelCreate_Throws_InvalidInput(string name) + { + CreateModelInput input = new(name, null, _project.id); + Assert.CatchAsync(async () => await Sut.Create(input)); + } + + [Test] + public void ModelGet_Throws_NoAuth() + { + Assert.CatchAsync(async () => await Fixtures.Unauthed.Model.Get(_model.id, _project.id)); + } + + [Test] + public void ModelGet_Throws_NonExistentModel() + { + Assert.CatchAsync(async () => await Sut.Get("non existent model", _project.id)); + } + + [Test] + public void ModelGet_Throws_NonExistentProject() + { + Assert.ThrowsAsync( + async () => await Sut.Get(_model.id, "non existent project") + ); + } + + [Test] + public void ModelUpdate_Throws_NonExistentModel() + { + UpdateModelInput input = new("non-existent model", "MY new name", "MY new desc", _project.id); + + Assert.CatchAsync(async () => await Sut.Update(input)); + } + + [Test] + public void ModelUpdate_Throws_NonExistentProject() + { + UpdateModelInput input = new(_model.id, "MY new name", "MY new desc", "non-existent project"); + + Assert.ThrowsAsync(async () => await Sut.Update(input)); + } + + [Test] + public void ModelUpdate_Throws_NonAuthProject() + { + UpdateModelInput input = new(_model.id, "MY new name", "MY new desc", _project.id); + + Assert.CatchAsync(async () => await Fixtures.Unauthed.Model.Update(input)); + } + + [Test] + public async Task ModelDelete_Throws_NoAuth() + { + Model toDelete = await Sut.Create(new("Delete me", null, _project.id)); + DeleteModelInput input = new(toDelete.id, _project.id); + bool response = await Sut.Delete(input); + Assert.That(response, Is.True); + + Assert.CatchAsync(async () => _ = await Sut.Delete(input)); + } +} diff --git a/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ModelResourceTests.cs b/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ModelResourceTests.cs new file mode 100644 index 00000000..e2995acf --- /dev/null +++ b/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ModelResourceTests.cs @@ -0,0 +1,96 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(ModelResource))] +public class ModelResourceTests +{ + private Client _testUser; + private ModelResource Sut => _testUser.Model; + private Project _project; + private Model _model; + + [SetUp] + public async Task Setup() + { + _testUser = await Fixtures.SeedUserWithClient(); + _project = await _testUser.Project.Create(new("Test project", "", null)); + _model = await _testUser.Model.Create(new("Test Model", "", _project.id)); + } + + [TestCase("My Model", "My model description")] + [TestCase("my/nested/model", null)] + public async Task ModelCreate(string name, string description) + { + CreateModelInput input = new(name, description, _project.id); + Model result = await Sut.Create(input); + + Assert.That(result, Is.Not.Null); + Assert.That(result, Has.Property(nameof(result.id)).Not.Null); + Assert.That(result, Has.Property(nameof(result.name)).EqualTo(input.name).IgnoreCase); + Assert.That(result, Has.Property(nameof(result.description)).EqualTo(input.description)); + } + + [Test] + public async Task ModelGet() + { + Model result = await Sut.Get(_model.id, _project.id); + + Assert.That(result.id, Is.EqualTo(_model.id)); + Assert.That(result.name, Is.EqualTo(_model.name)); + Assert.That(result.description, Is.EqualTo(_model.description)); + Assert.That(result.createdAt, Is.EqualTo(_model.createdAt)); + Assert.That(result.updatedAt, Is.EqualTo(_model.updatedAt)); + } + + [Test] + public async Task GetModels() + { + var result = await Sut.GetModels(_project.id); + + Assert.That(result.items, Has.Count.EqualTo(1)); + Assert.That(result.totalCount, Is.EqualTo(1)); + Assert.That(result.items[0], Has.Property(nameof(Model.id)).EqualTo(_model.id)); + } + + [Test] + public async Task Project_GetModels() + { + var result = await _testUser.Project.GetWithModels(_project.id); + + Assert.That(result, Has.Property(nameof(Project.id)).EqualTo(_project.id)); + Assert.That(result.models.items, Has.Count.EqualTo(1)); + Assert.That(result.models.totalCount, Is.EqualTo(1)); + Assert.That(result.models.items[0], Has.Property(nameof(Model.id)).EqualTo(_model.id)); + } + + [Test] + public async Task ModelUpdate() + { + const string NEW_NAME = "MY new name"; + const string NEW_DESCRIPTION = "MY new desc"; + + UpdateModelInput input = new(_model.id, NEW_NAME, NEW_DESCRIPTION, _project.id); + Model updatedModel = await Sut.Update(input); + + Assert.That(updatedModel.id, Is.EqualTo(_model.id)); + Assert.That(updatedModel.name, Is.EqualTo(NEW_NAME).IgnoreCase); + Assert.That(updatedModel.description, Is.EqualTo(NEW_DESCRIPTION)); + Assert.That(updatedModel.updatedAt, Is.GreaterThanOrEqualTo(_model.updatedAt)); + } + + [Test] + public async Task ModelDelete() + { + DeleteModelInput input = new(_model.id, _project.id); + + bool response = await Sut.Delete(input); + Assert.That(response, Is.True); + + Assert.CatchAsync(async () => _ = await Sut.Get(_model.id, _project.id)); + Assert.CatchAsync(async () => _ = await Sut.Delete(input)); + } +} diff --git a/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/OtherUserResourceTests.cs b/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/OtherUserResourceTests.cs new file mode 100644 index 00000000..3ba71a3c --- /dev/null +++ b/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/OtherUserResourceTests.cs @@ -0,0 +1,50 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Resources; +using Speckle.Core.Credentials; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(OtherUserResource))] +public class OtherUserResourceTests +{ + private Client _testUser; + private Account _testData; + private OtherUserResource Sut => _testUser.OtherUser; + + [OneTimeSetUp] + public async Task Setup() + { + _testUser = await Fixtures.SeedUserWithClient(); + _testData = await Fixtures.SeedUser(); + } + + [Test] + public async Task OtherUserGet() + { + var res = await Sut.Get(_testData.userInfo.id); + Assert.That(res, Is.Not.Null); + Assert.That(res!.name, Is.EqualTo(_testData.userInfo.name)); + } + + [Test] + public async Task OtherUserGet_NonExistentUser() + { + var result = await Sut.Get("AnIdThatDoesntExist"); + Assert.That(result, Is.Null); + } + + [Test] + public async Task UserSearch() + { + var res = await Sut.UserSearch(_testData.userInfo.email, 25); + Assert.That(res.items, Has.Count.EqualTo(1)); + Assert.That(res.items[0].id, Is.EqualTo(_testData.userInfo.id)); + } + + [Test] + public async Task UserSearch_NonExistentUser() + { + var res = await Sut.UserSearch("idontexist@example.com", 25); + Assert.That(res.items, Has.Count.EqualTo(0)); + } +} diff --git a/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectInviteResourceExceptionalTests.cs b/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectInviteResourceExceptionalTests.cs new file mode 100644 index 00000000..b2cee38c --- /dev/null +++ b/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectInviteResourceExceptionalTests.cs @@ -0,0 +1,32 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(ProjectInviteResource))] +public class ProjectInviteResourceExceptionalTests +{ + private Client _testUser; + private Project _project; + private ProjectInviteResource Sut => _testUser.ProjectInvite; + + [OneTimeSetUp] + public async Task Setup() + { + _testUser = await Fixtures.SeedUserWithClient(); + _project = await _testUser.Project.Create(new("test", null, null)); + } + + [TestCase(null, null, null, null)] + [TestCase(null, "something", "something", null)] + public void ProjectInviteCreate_InvalidInput(string email, string role, string serverRole, string userId) + { + Assert.CatchAsync(async () => + { + var input = new ProjectInviteCreateInput(email, role, serverRole, userId); + await Sut.Create(_project.id, input); + }); + } +} diff --git a/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectInviteResourceTests.cs b/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectInviteResourceTests.cs new file mode 100644 index 00000000..4d2ff9b1 --- /dev/null +++ b/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectInviteResourceTests.cs @@ -0,0 +1,107 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(ProjectInviteResource))] +public class ProjectInviteResourceTests +{ + private Client _inviter, + _invitee; + private Project _project; + private PendingStreamCollaborator _createdInvite; + + [SetUp] + public async Task Setup() + { + _inviter = await Fixtures.SeedUserWithClient(); + _invitee = await Fixtures.SeedUserWithClient(); + _project = await _inviter.Project.Create(new("test", null, null)); + _createdInvite = await SeedInvite(); + } + + private async Task SeedInvite() + { + ProjectInviteCreateInput input = new(_invitee.Account.userInfo.email, null, null, null); + var res = await _inviter.ProjectInvite.Create(_project.id, input); + var invites = await _invitee.ActiveUser.ProjectInvites(); + return invites.First(i => i.projectId == res.id); + } + + [Test] + public async Task ProjectInviteCreate_ByEmail() + { + ProjectInviteCreateInput input = new(_invitee.Account.userInfo.email, null, null, null); + var res = await _inviter.ProjectInvite.Create(_project.id, input); + + var invites = await _invitee.ActiveUser.ProjectInvites(); + var invite = invites.First(i => i.projectId == res.id); + + Assert.That(res, Has.Property(nameof(_project.id)).EqualTo(_project.id)); + Assert.That(res.invitedTeam, Has.Count.EqualTo(1)); + Assert.That(invite.user.id, Is.EqualTo(_invitee.Account.userInfo.id)); + Assert.That(invite.token, Is.Not.Null); + } + + [Test] + public async Task ProjectInviteCreate_ByUserId() + { + ProjectInviteCreateInput input = new(null, null, null, _invitee.Account.userInfo.id); + var res = await _inviter.ProjectInvite.Create(_project.id, input); + + Assert.That(res, Has.Property(nameof(_project.id)).EqualTo(_project.id)); + Assert.That(res.invitedTeam, Has.Count.EqualTo(1)); + Assert.That(res.invitedTeam[0].user.id, Is.EqualTo(_invitee.Account.userInfo.id)); + } + + [Test] + public async Task ProjectInviteGet() + { + var collaborator = await _invitee.ProjectInvite.Get(_project.id, _createdInvite.token); + + Assert.That( + collaborator, + Has.Property(nameof(PendingStreamCollaborator.inviteId)).EqualTo(_createdInvite.inviteId) + ); + Assert.That(collaborator!.user.id, Is.EqualTo(_createdInvite.user.id)); + } + + [Test] + public async Task ProjectInviteUse_MemberAdded() + { + ProjectInviteUseInput input = new(true, _createdInvite.projectId, _createdInvite.token); + var res = await _invitee.ProjectInvite.Use(input); + Assert.That(res, Is.True); + + var project = await _inviter.Project.GetWithTeam(_project.id); + var teamMembers = project.team.Select(c => c.user.id); + var expectedTeamMembers = new[] { _inviter.Account.userInfo.id, _invitee.Account.userInfo.id }; + Assert.That(teamMembers, Is.EquivalentTo(expectedTeamMembers)); + } + + [Test] + public async Task ProjectInviteCancel_MemberNotAdded() + { + var res = await _inviter.ProjectInvite.Cancel(_createdInvite.projectId, _createdInvite.inviteId); + + Assert.That(res.invitedTeam, Is.Empty); + } + + [Test] + [TestCase(StreamRoles.STREAM_OWNER)] + [TestCase(StreamRoles.STREAM_REVIEWER)] + [TestCase(StreamRoles.STREAM_CONTRIBUTOR)] + [TestCase(StreamRoles.REVOKE)] + public async Task ProjectUpdateRole(string newRole) + { + await ProjectInviteUse_MemberAdded(); + ProjectUpdateRoleInput input = new(_invitee.Account.userInfo.id, _project.id, newRole); + _ = await _inviter.Project.UpdateRole(input); + + Project finalProject = await _invitee.Project.Get(_project.id); + Assert.That(finalProject.role, Is.EqualTo(newRole)); + } +} diff --git a/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectResourceExceptionalTests.cs b/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectResourceExceptionalTests.cs new file mode 100644 index 00000000..47222b76 --- /dev/null +++ b/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectResourceExceptionalTests.cs @@ -0,0 +1,113 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL; +using Speckle.Core.Api.GraphQL.Enums; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(ProjectResource))] +public class ProjectResourceExceptionalTests +{ + private Client _testUser, + _secondUser, + _unauthedUser; + private Project _testProject; + private ProjectResource Sut => _testUser.Project; + + [OneTimeSetUp] + public async Task Setup() + { + _testUser = await Fixtures.SeedUserWithClient(); + _secondUser = await Fixtures.SeedUserWithClient(); + _unauthedUser = Fixtures.Unauthed; + _testProject = await _testUser.Project.Create(new("test project123", "desc", null)); + } + + //We want to check the following cases + // 1. User lacks permissions (without auth) + // 2. Target (Project or user) doesn't exist) + // 3. Cancellation + // 4. Server doesn't exist (is down) + //There's got to be a smarter way to parametrise these... + + [Test] + public void ProjectCreate_WithoutAuth() + { + ProjectCreateInput input = + new("The best project", "The best description for the best project", ProjectVisibility.Private); + + Assert.ThrowsAsync(async () => await _unauthedUser.Project.Create(input)); + } + + [Test] + public async Task ProjectGet_WithoutAuth() + { + ProjectCreateInput input = new("Private Stream", "A very private stream", ProjectVisibility.Private); + + Project privateStream = await Sut.Create(input); + + Assert.ThrowsAsync(async () => await _unauthedUser.Project.Get(privateStream.id)); + } + + [Test] + public void ProjectGet_NonExistentProject() + { + Assert.ThrowsAsync(async () => await Sut.Get("NonExistentProject")); + } + + [Test] + public void ProjectUpdate_NonExistentProject() + { + Assert.ThrowsAsync( + async () => _ = await Sut.Update(new("NonExistentProject", "My new name")) + ); + } + + [Test] + public void ProjectUpdate_NoAuth() + { + Assert.ThrowsAsync( + async () => _ = await _unauthedUser.Project.Update(new(_testProject.id, "My new name")) + ); + } + + [Test] + [TestCase(StreamRoles.STREAM_OWNER)] + [TestCase(StreamRoles.STREAM_CONTRIBUTOR)] + [TestCase(StreamRoles.STREAM_REVIEWER)] + [TestCase(StreamRoles.REVOKE)] + public void ProjectUpdateRole_NonExistentProject(string newRole) + { + ProjectUpdateRoleInput input = new(_secondUser.Account.id, "NonExistentProject", newRole); + + Assert.ThrowsAsync(async () => await Sut.UpdateRole(input)); + } + + [Test] + [TestCase(StreamRoles.STREAM_OWNER)] + [TestCase(StreamRoles.STREAM_CONTRIBUTOR)] + [TestCase(StreamRoles.STREAM_REVIEWER)] + [TestCase(StreamRoles.REVOKE)] + public void ProjectUpdateRole_NonAuth(string newRole) + { + ProjectUpdateRoleInput input = new(_secondUser.Account.id, "NonExistentProject", newRole); + Assert.ThrowsAsync(async () => await _unauthedUser.Project.UpdateRole(input)); + } + + [Test] + public async Task ProjectDelete_NonExistentProject() + { + bool response = await Sut.Delete(_testProject.id); + Assert.That(response, Is.True); + + Assert.ThrowsAsync(async () => _ = await Sut.Get(_testProject.id)); //TODO: Exception types + } + + [Test] + public void ProjectInvites_NoAuth() + { + Assert.ThrowsAsync(async () => await Fixtures.Unauthed.ActiveUser.ProjectInvites()); + } +} diff --git a/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectResourceTests.cs b/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectResourceTests.cs new file mode 100644 index 00000000..66dcba0a --- /dev/null +++ b/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/ProjectResourceTests.cs @@ -0,0 +1,72 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Enums; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(ProjectResource))] +public class ProjectResourceTests +{ + private Client _testUser; + private Project _testProject; + private ProjectResource Sut => _testUser.Project; + + [OneTimeSetUp] + public async Task Setup() + { + _testUser = await Fixtures.SeedUserWithClient(); + _testProject = await _testUser.Project.Create(new("test project123", "desc", null)); + } + + [TestCase("Very private project", "My secret project", ProjectVisibility.Private)] + [TestCase("Very public project", null, ProjectVisibility.Public)] + public async Task ProjectCreate(string name, string desc, ProjectVisibility visibility) + { + ProjectCreateInput input = new(name, desc, visibility); + Project result = await Sut.Create(input); + Assert.That(result, Is.Not.Null); + Assert.That(result, Has.Property(nameof(Project.id)).Not.Null); + Assert.That(result, Has.Property(nameof(Project.name)).EqualTo(input.name)); + Assert.That(result, Has.Property(nameof(Project.description)).EqualTo(input.description ?? string.Empty)); + Assert.That(result, Has.Property(nameof(Project.visibility)).EqualTo(input.visibility)); + } + + [Test] + public async Task ProjectGet() + { + Project result = await Sut.Get(_testProject.id); + + Assert.That(result.id, Is.EqualTo(_testProject.id)); + Assert.That(result.name, Is.EqualTo(_testProject.name)); + Assert.That(result.description, Is.EqualTo(_testProject.description)); + Assert.That(result.visibility, Is.EqualTo(_testProject.visibility)); + Assert.That(result.createdAt, Is.EqualTo(_testProject.createdAt)); + } + + [Test] + public async Task ProjectUpdate() + { + const string NEW_NAME = "MY new name"; + const string NEW_DESCRIPTION = "MY new desc"; + const ProjectVisibility NEW_VISIBILITY = ProjectVisibility.Public; + + Project newProject = await Sut.Update(new(_testProject.id, NEW_NAME, NEW_DESCRIPTION, null, NEW_VISIBILITY)); + + Assert.That(newProject.id, Is.EqualTo(_testProject.id)); + Assert.That(newProject.name, Is.EqualTo(NEW_NAME)); + Assert.That(newProject.description, Is.EqualTo(NEW_DESCRIPTION)); + Assert.That(newProject.visibility, Is.EqualTo(NEW_VISIBILITY)); + } + + [Test] + public async Task ProjectDelete() + { + Project toDelete = await Sut.Create(new("Delete me", null, null)); + bool response = await Sut.Delete(toDelete.id); + Assert.That(response, Is.True); + + Assert.ThrowsAsync(async () => _ = await Sut.Get(toDelete.id)); + } +} diff --git a/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/SubscriptionResourceTests.cs b/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/SubscriptionResourceTests.cs new file mode 100644 index 00000000..7bf52ed1 --- /dev/null +++ b/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/SubscriptionResourceTests.cs @@ -0,0 +1,120 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(SubscriptionResource))] +public class SubscriptionResourceTests +{ + private const int WAIT_PERIOD = 300; + private Client _testUser; + private Project _testProject; + private Model _testModel; + private string _testVersion; + + private SubscriptionResource Sut => _testUser.Subscription; + + [OneTimeSetUp] + public async Task Setup() + { + _testUser = await Fixtures.SeedUserWithClient(); + _testProject = await _testUser.Project.Create(new("test project123", "desc", null)); + _testModel = await _testUser.Model.Create(new("test model", "desc", _testProject.id)); + _testVersion = await Fixtures.CreateVersion(_testUser, _testProject.id, _testModel.name); + } + + [Test] + public async Task UserProjectsUpdated_SubscriptionIsCalled() + { + UserProjectsUpdatedMessage? subscriptionMessage = null; + + using var sub = Sut.CreateUserProjectsUpdatedSubscription(); + sub.Listeners += (_, message) => subscriptionMessage = message; + + await Task.Delay(WAIT_PERIOD); // Give time to subscription to be setup + + var created = await _testUser.Project.Create(new(null, null, null)); + + await Task.Delay(WAIT_PERIOD); // Give time for subscription to be triggered + + Assert.That(subscriptionMessage, Is.Not.Null); + Assert.That(subscriptionMessage!.id, Is.EqualTo(created.id)); + } + + [Test] + public async Task ProjectModelsUpdated_SubscriptionIsCalled() + { + ProjectModelsUpdatedMessage? subscriptionMessage = null; + + using var sub = Sut.CreateProjectModelsUpdatedSubscription(_testProject.id); + sub.Listeners += (_, message) => subscriptionMessage = message; + + await Task.Delay(WAIT_PERIOD); // Give time to subscription to be setup + + CreateModelInput input = new("my model", "myDescription", _testProject.id); + var created = await _testUser.Model.Create(input); + + await Task.Delay(WAIT_PERIOD); // Give time for subscription to be triggered + + Assert.That(subscriptionMessage, Is.Not.Null); + Assert.That(subscriptionMessage!.id, Is.EqualTo(created.id)); + } + + [Test] + public async Task ProjectUpdated_SubscriptionIsCalled() + { + ProjectUpdatedMessage? subscriptionMessage = null; + + using var sub = Sut.CreateProjectUpdatedSubscription(_testProject.id); + sub.Listeners += (_, message) => subscriptionMessage = message; + + await Task.Delay(WAIT_PERIOD); // Give time to subscription to be setup + + var input = new ProjectUpdateInput(_testProject.id, "This is my new name"); + var created = await _testUser.Project.Update(input); + + await Task.Delay(WAIT_PERIOD); // Give time for subscription to be triggered + + Assert.That(subscriptionMessage, Is.Not.Null); + Assert.That(subscriptionMessage!.id, Is.EqualTo(created.id)); + } + + [Test] + public async Task ProjectVersionsUpdated_SubscriptionIsCalled() + { + ProjectVersionsUpdatedMessage? subscriptionMessage = null; + + using var sub = Sut.CreateProjectVersionsUpdatedSubscription(_testProject.id); + sub.Listeners += (_, message) => subscriptionMessage = message; + + await Task.Delay(WAIT_PERIOD); // Give time to subscription to be setup + + var created = await Fixtures.CreateVersion(_testUser, _testProject.id, _testModel.name); + + await Task.Delay(WAIT_PERIOD); // Give time for subscription to be triggered + + Assert.That(subscriptionMessage, Is.Not.Null); + Assert.That(subscriptionMessage!.id, Is.EqualTo(created)); + } + + [Test] + public async Task ProjectCommentsUpdated_SubscriptionIsCalled() + { + string resourceIdString = $"{_testProject.id},{_testModel.id},{_testVersion}"; + ProjectCommentsUpdatedMessage? subscriptionMessage = null; + + using var sub = Sut.CreateProjectCommentsUpdatedSubscription(new(_testProject.id, resourceIdString)); + sub.Listeners += (_, message) => subscriptionMessage = message; + + await Task.Delay(WAIT_PERIOD); // Give time to subscription to be setup + + var created = await Fixtures.CreateComment(_testUser, _testProject.id, _testModel.id, _testVersion); + + await Task.Delay(WAIT_PERIOD); // Give time for subscription to be triggered + + Assert.That(subscriptionMessage, Is.Not.Null); + Assert.That(subscriptionMessage!.id, Is.EqualTo(created.id)); + } +} diff --git a/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/VersionResourceTests.cs b/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/VersionResourceTests.cs new file mode 100644 index 00000000..d54c966e --- /dev/null +++ b/tests/Speckle.Core.Tests.Integration/Api/GraphQL/Resources/VersionResourceTests.cs @@ -0,0 +1,117 @@ +using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Api.GraphQL.Resources; +using Version = Speckle.Core.Api.GraphQL.Models.Version; + +namespace Speckle.Core.Tests.Integration.API.GraphQL.Resources; + +[TestOf(typeof(VersionResource))] +public class VersionResourceTests +{ + private Client _testUser; + private VersionResource Sut => _testUser.Version; + private Project _project; + private Model _model1; + private Model _model2; + private Version _version; + + [SetUp] + public async Task Setup() + { + _testUser = await Fixtures.SeedUserWithClient(); + _project = await _testUser.Project.Create(new("Test project", "", null)); + _model1 = await _testUser.Model.Create(new("Test Model 1", "", _project.id)); + _model2 = await _testUser.Model.Create(new("Test Model 2", "", _project.id)); + + string versionId = await Fixtures.CreateVersion(_testUser, _project.id, "Test Model 1"); + + _version = await Sut.Get(versionId, _model1.id, _project.id); + } + + [Test] + public async Task VersionGet() + { + Version result = await Sut.Get(_version.id, _model1.id, _project.id); + + Assert.That(result, Has.Property(nameof(Version.id)).EqualTo(_version.id)); + Assert.That(result, Has.Property(nameof(Version.message)).EqualTo(_version.message)); + } + + [Test] + public async Task VersionsGet() + { + ResourceCollection result = await Sut.GetVersions(_model1.id, _project.id); + + Assert.That(result.items, Has.Count.EqualTo(1)); + Assert.That(result.totalCount, Is.EqualTo(1)); + Assert.That(result.items[0], Has.Property(nameof(Version.id)).EqualTo(_version.id)); + } + + [Test] + public async Task VersionReceived() + { + CommitReceivedInput input = + new() + { + commitId = _version.id, + message = "we receieved it", + sourceApplication = "Integration test", + streamId = _project.id + }; + var result = await Sut.Received(input); + + Assert.That(result, Is.True); + } + + [Test] + public async Task ModelGetWithVersions() + { + Model result = await _testUser.Model.GetWithVersions(_model1.id, _project.id); + + Assert.That(result, Has.Property(nameof(Model.id)).EqualTo(_model1.id)); + Assert.That(result.versions.items, Has.Count.EqualTo(1)); + Assert.That(result.versions.totalCount, Is.EqualTo(1)); + Assert.That(result.versions.items[0], Has.Property(nameof(Version.id)).EqualTo(_version.id)); + } + + [Test] + public async Task VersionUpdate() + { + const string NEW_MESSAGE = "MY new version message"; + + UpdateVersionInput input = new(_version.id, NEW_MESSAGE); + Version updatedVersion = await Sut.Update(input); + + Assert.That(updatedVersion, Has.Property(nameof(Version.id)).EqualTo(_version.id)); + Assert.That(updatedVersion, Has.Property(nameof(Version.message)).EqualTo(NEW_MESSAGE)); + Assert.That(updatedVersion, Has.Property(nameof(Version.previewUrl)).EqualTo(_version.previewUrl)); + } + + [Test] + public async Task VersionMoveToModel() + { + MoveVersionsInput input = new(_model2.name, new[] { _version.id }); + string id = await Sut.MoveToModel(input); + Assert.That(id, Is.EqualTo(_model2.id)); + Version movedVersion = await Sut.Get(_version.id, _model2.id, _project.id); + + Assert.That(movedVersion, Has.Property(nameof(Version.id)).EqualTo(_version.id)); + Assert.That(movedVersion, Has.Property(nameof(Version.message)).EqualTo(_version.message)); + Assert.That(movedVersion, Has.Property(nameof(Version.previewUrl)).EqualTo(_version.previewUrl)); + + Assert.CatchAsync(async () => await Sut.Get(id, _model1.id, _project.id)); + } + + [Test] + public async Task VersionDelete() + { + DeleteVersionsInput input = new(new[] { _version.id }); + + bool response = await Sut.Delete(input); + Assert.That(response, Is.True); + + Assert.CatchAsync(async () => _ = await Sut.Get(_version.id, _model1.id, _project.id)); + Assert.CatchAsync(async () => _ = await Sut.Delete(input)); + } +} diff --git a/tests/Speckle.Core.Tests.Integration/Credentials/UserServerInfoTests.cs b/tests/Speckle.Core.Tests.Integration/Credentials/UserServerInfoTests.cs index 0929c909..69095973 100644 --- a/tests/Speckle.Core.Tests.Integration/Credentials/UserServerInfoTests.cs +++ b/tests/Speckle.Core.Tests.Integration/Credentials/UserServerInfoTests.cs @@ -1,5 +1,5 @@ using GraphQL.Client.Http; -using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Credentials; namespace Speckle.Core.Tests.Integration.Credentials; @@ -17,19 +17,19 @@ public async Task Setup() [Test] public async Task IsFrontEnd2True() { - ServerInfo result = await AccountManager.GetServerInfo("https://app.speckle.systems/"); + ServerInfo? result = await AccountManager.GetServerInfo("https://app.speckle.systems/"); Assert.That(result, Is.Not.Null); - Assert.That(result.frontend2, Is.True); + Assert.That(result!.frontend2, Is.True); } [Test] public async Task IsFrontEnd2False() { - ServerInfo result = await AccountManager.GetServerInfo("https://speckle.xyz/"); + ServerInfo? result = await AccountManager.GetServerInfo("https://speckle.xyz/"); Assert.That(result, Is.Not.Null); - Assert.That(result.frontend2, Is.False); + Assert.That(result!.frontend2, Is.False); } /// diff --git a/tests/Speckle.Core.Tests.Integration/Fixtures.cs b/tests/Speckle.Core.Tests.Integration/Fixtures.cs index 0e046f9b..0e070713 100644 --- a/tests/Speckle.Core.Tests.Integration/Fixtures.cs +++ b/tests/Speckle.Core.Tests.Integration/Fixtures.cs @@ -4,9 +4,13 @@ using System.Web; using Newtonsoft.Json; using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Inputs; +using Speckle.Core.Api.GraphQL.Models; +using Speckle.Core.Common; using Speckle.Core.Credentials; using Speckle.Core.Logging; using Speckle.Core.Models; +using Speckle.Core.Transports; namespace Speckle.Core.Tests.Integration; @@ -23,7 +27,29 @@ public void BeforeAll() public static class Fixtures { - private static readonly ServerInfo s_server = new() { url = "http://localhost:3000", name = "Docker Server" }; + public static readonly ServerInfo Server = new() { url = "http://localhost:3000", name = "Docker Server" }; + + public static Client Unauthed => new Client(new Account { serverInfo = Server, userInfo = new UserInfo() }); + + public static async Task SeedUserWithClient() + { + return new Client(await SeedUser()); + } + + public static async Task CreateVersion(Client client, string projectId, string branchName) + { + using ServerTransport remote = new(client.Account, projectId); + var objectId = await Operations.Send(new() { applicationId = "ASDF" }, remote, false); + CommitCreateInput input = + new() + { + branchName = branchName, + message = "test version", + objectId = objectId, + streamId = projectId + }; + return await client.Version.Create(input); + } public static async Task SeedUser() { @@ -31,7 +57,7 @@ public static async Task SeedUser() Dictionary user = new() { - ["email"] = $"{seed.Substring(0, 7)}@acme.com", + ["email"] = $"{seed.Substring(0, 7)}@example.com", ["password"] = "12ABC3456789DEF0GHO", ["name"] = $"{seed.Substring(0, 5)} Name" }; @@ -40,7 +66,7 @@ public static async Task SeedUser() new HttpClientHandler { AllowAutoRedirect = false, CheckCertificateRevocationList = true } ); - httpClient.BaseAddress = new Uri(s_server.url); + httpClient.BaseAddress = new Uri(Server.url); string redirectUrl; try @@ -54,7 +80,7 @@ public static async Task SeedUser() } catch (Exception e) { - throw new Exception($"Cannot seed user on the server {s_server.url}", e); + throw new Exception($"Cannot seed user on the server {Server.url}", e); } Uri uri = new(redirectUrl); @@ -80,19 +106,18 @@ await tokenResponse.Content.ReadAsStringAsync() var acc = new Account { - token = deserialised["token"]!, + token = deserialised.NotNull()["token"].NotNull(), userInfo = new UserInfo { id = user["name"], email = user["email"], name = user["name"] }, - serverInfo = s_server + serverInfo = Server }; - using var client = new Client(acc); - var user1 = await client.ActiveUserGet(); - acc.userInfo.id = user1.id; + var user1 = await AccountManager.GetUserInfo(acc.token, acc.serverInfo.url); + acc.userInfo = user1; return acc; } @@ -132,6 +157,23 @@ private static Blob GenerateBlob(string content) File.WriteAllText(filePath, content); return new Blob(filePath); } + + internal static async Task CreateComment(Client client, string projectId, string modelId, string versionId) + { + var blobs = await SendBlobData(client.Account, projectId); + var blobIds = blobs.Select(b => b.id).ToList(); + CreateCommentInput input = new(new(blobIds, null), projectId, $"{projectId},{modelId},{versionId}", null, null); + return await client.Comment.Create(input); + } + + internal static async Task SendBlobData(Account account, string projectId) + { + using ServerTransport remote = new(account, projectId); + var blobs = Fixtures.GenerateThreeBlobs(); + Base myObject = new() { ["blobs"] = blobs }; + await Operations.Send(myObject, remote, false); + return blobs; + } } public class UserIdResponse diff --git a/tests/Speckle.Core.Tests.Integration/GraphQLCLient.cs b/tests/Speckle.Core.Tests.Integration/GraphQLCLient.cs index afdb06ca..985aae17 100644 --- a/tests/Speckle.Core.Tests.Integration/GraphQLCLient.cs +++ b/tests/Speckle.Core.Tests.Integration/GraphQLCLient.cs @@ -19,7 +19,7 @@ public async Task Setup() [Test] public void ThrowsForbiddenException() { - Assert.ThrowsAsync>>( + Assert.ThrowsAsync( async () => await _client.ExecuteGraphQLRequest>( new GraphQLRequest diff --git a/tests/Speckle.Core.Tests.Integration/ServerTransportTests.cs b/tests/Speckle.Core.Tests.Integration/ServerTransportTests.cs index 0baa04ac..7254ab30 100644 --- a/tests/Speckle.Core.Tests.Integration/ServerTransportTests.cs +++ b/tests/Speckle.Core.Tests.Integration/ServerTransportTests.cs @@ -1,4 +1,4 @@ -using System.Collections; +using Shouldly; using Speckle.Core.Api; using Speckle.Core.Credentials; using Speckle.Core.Helpers; @@ -46,6 +46,7 @@ public void TearDown() private void CleanData() { + _transport?.Dispose(); if (Directory.Exists(_basePath)) { Directory.Delete(_basePath, true); @@ -73,7 +74,7 @@ public async Task SendAndReceiveObjectWithBlobs() // NOTE: used to debug diffing // await Operations.Send(myObject, new List { transport }); - var receivedObject = await Operations.Receive(sentObjectId, _transport); + var receivedObject = await Operations.Receive(sentObjectId, _transport, new MemoryTransport()); var allFiles = Directory .GetFiles(_transport.BlobStorageFolder) @@ -85,8 +86,9 @@ public async Task SendAndReceiveObjectWithBlobs() // Check that there are three downloaded blobs! Assert.That(blobPaths, Has.Count.EqualTo(3)); - - var blobs = ((IList)receivedObject["blobs"]!).Cast().ToList(); + var objectBlobs = receivedObject["blobs"] as IList; + objectBlobs.ShouldNotBeNull(); + var blobs = objectBlobs.Cast().ToList(); // Check that we have three blobs Assert.That(blobs, Has.Count.EqualTo(3)); // Check that received blobs point to local path (where they were received) @@ -104,7 +106,7 @@ public async Task SendWithBlobsWithoutSQLiteSendCache() var memTransport = new MemoryTransport(); var sentObjectId = await Operations.Send(myObject, new List { _transport, memTransport }); - var receivedObject = await Operations.Receive(sentObjectId, _transport); + var receivedObject = await Operations.Receive(sentObjectId, _transport, new MemoryTransport()); var allFiles = Directory .GetFiles(_transport.BlobStorageFolder) @@ -117,7 +119,9 @@ public async Task SendWithBlobsWithoutSQLiteSendCache() // Check that there are three downloaded blobs! Assert.That(blobPaths, Has.Count.EqualTo(3)); - var blobs = ((IList)receivedObject["blobs"]!).Cast().ToList(); + var objectBlobs = receivedObject["blobs"] as IList; + objectBlobs.ShouldNotBeNull(); + var blobs = objectBlobs.Cast().ToList(); // Check that we have three blobs Assert.That(blobs, Has.Count.EqualTo(3)); // Check that received blobs point to local path (where they were received) @@ -150,7 +154,9 @@ public async Task SendReceiveWithCleanedMemoryCache() // Check that there are three downloaded blobs! Assert.That(blobPaths.Count, Is.EqualTo(3)); - var blobs = ((IList)receivedObject!["blobs"]!).Cast().ToList(); + var objectBlobs = receivedObject["blobs"] as IList; + objectBlobs.ShouldNotBeNull(); + var blobs = objectBlobs.Cast().ToList(); // Check that we have three blobs Assert.That(blobs, Has.Count.EqualTo(3)); // Check that received blobs point to local path (where they were received) diff --git a/tests/Speckle.Core.Tests.Integration/Speckle.Core.Tests.Integration.csproj b/tests/Speckle.Core.Tests.Integration/Speckle.Core.Tests.Integration.csproj index 2f33bde0..32eddfb1 100644 --- a/tests/Speckle.Core.Tests.Integration/Speckle.Core.Tests.Integration.csproj +++ b/tests/Speckle.Core.Tests.Integration/Speckle.Core.Tests.Integration.csproj @@ -1,43 +1,23 @@  - net7.0 - enable - disable + net8.0 false true + System.Runtime.CompilerServices.IsExternalInit;System.Runtime.CompilerServices.RequiresLocationAttribute - - true - - - - - - - - - - - + + + + - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - + - + + diff --git a/tests/Speckle.Core.Tests.Integration/packages.lock.json b/tests/Speckle.Core.Tests.Integration/packages.lock.json new file mode 100644 index 00000000..270bf26a --- /dev/null +++ b/tests/Speckle.Core.Tests.Integration/packages.lock.json @@ -0,0 +1,500 @@ +{ + "version": 2, + "dependencies": { + "net8.0": { + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.10.0, )", + "resolved": "17.10.0", + "contentHash": "0/2HeACkaHEYU3wc83YlcD2Fi4LMtECJjqrtvw0lPi9DCEa35zSPt1j4fuvM8NagjDqJuh1Ja35WcRtn1Um6/A==", + "dependencies": { + "Microsoft.CodeCoverage": "17.10.0", + "Microsoft.TestPlatform.TestHost": "17.10.0" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "MinVer": { + "type": "Direct", + "requested": "[5.0.0, )", + "resolved": "5.0.0", + "contentHash": "ybkgpQMtt0Fo91l5rYtE3TZtD+Nmy5Ko091xvfXXOosQdMi30XO2EZ2+ShZt89gdu7RMmJqZaJ+e1q6d+6+KNw==" + }, + "NUnit": { + "type": "Direct", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "MT/DpAhjtiytzhTgTqIhBuWx4y26PKfDepYUHUM+5uv4TsryHC2jwFo5e6NhWkApCm/G6kZ80dRjdJFuAxq3rg==" + }, + "NUnit3TestAdapter": { + "type": "Direct", + "requested": "[4.5.0, )", + "resolved": "4.5.0", + "contentHash": "s8JpqTe9bI2f49Pfr3dFRfoVSuFQyraTj68c3XXjIS/MRGvvkLnrg6RLqnTjdShX+AdFUCCU/4Xex58AdUfs6A==" + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.14.1, )", + "resolved": "1.14.1", + "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + }, + "Shouldly": { + "type": "Direct", + "requested": "[4.2.1, )", + "resolved": "4.2.1", + "contentHash": "dKAKiSuhLKqD2TXwLKtqNg1nwzJcIKOOMncZjk9LYe4W+h+SCftpWdxwR79YZUIHMH+3Vu9s0s0UHNrgICLwRQ==", + "dependencies": { + "DiffEngine": "11.3.0", + "EmptyFiles": "4.4.0" + } + }, + "DiffEngine": { + "type": "Transitive", + "resolved": "11.3.0", + "contentHash": "k0ZgZqd09jLZQjR8FyQbSQE86Q7QZnjEzq1LPHtj1R2AoWO8sjV5x+jlSisL7NZAbUOI4y+7Bog8gkr9WIRBGw==", + "dependencies": { + "EmptyFiles": "4.4.0", + "System.Management": "6.0.1" + } + }, + "EmptyFiles": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "gwJEfIGS7FhykvtZoscwXj/XwW+mJY6UbAZk+qtLKFUGWC95kfKXnj8VkxsZQnWBxJemM/q664rGLN5nf+OHZw==" + }, + "GraphQL.Client.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==", + "dependencies": { + "GraphQL.Primitives": "6.0.0" + } + }, + "GraphQL.Client.Abstractions.Websocket": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0" + } + }, + "GraphQL.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==" + }, + "Microsoft.AspNetCore.Http": { + "type": "Transitive", + "resolved": "2.2.2", + "contentHash": "BAibpoItxI5puk7YJbIGj95arZueM8B8M5xT1fXBn3hb3L2G3ucrZcYXv1gXdaroLbntUs8qeV8iuBrpjQsrKw==", + "dependencies": { + "Microsoft.AspNetCore.Http.Abstractions": "2.2.0", + "Microsoft.AspNetCore.WebUtilities": "2.2.0", + "Microsoft.Extensions.ObjectPool": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0", + "Microsoft.Net.Http.Headers": "2.2.0" + } + }, + "Microsoft.AspNetCore.Http.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "Nxs7Z1q3f1STfLYKJSVXCs1iBl+Ya6E8o4Oy1bCxJ/rNI44E/0f6tbsrVqAWfB7jlnJfyaAtIalBVxPKUPQb4Q==", + "dependencies": { + "Microsoft.AspNetCore.Http.Features": "2.2.0", + "System.Text.Encodings.Web": "4.5.0" + } + }, + "Microsoft.AspNetCore.Http.Features": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "ziFz5zH8f33En4dX81LW84I6XrYXKf9jg6aM39cM+LffN9KJahViKZ61dGMSO2gd3e+qe5yBRwsesvyqlZaSMg==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.AspNetCore.WebUtilities": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "9ErxAAKaDzxXASB/b5uLEkLgUWv1QbeVxyJYEHQwMaxXOeFFVkQxiq8RyfVcifLU7NR0QY0p3acqx4ZpYfhHDg==", + "dependencies": { + "Microsoft.Net.Http.Headers": "2.2.0", + "System.Text.Encodings.Web": "4.5.0" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.10.0", + "contentHash": "yC7oSlnR54XO5kOuHlVOKtxomNNN1BWXX8lK1G2jaPXT9sUok7kCOoA4Pgs0qyFaCtMrNsprztYMeoEGqCm4uA==" + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "8.0.6", + "contentHash": "umhZ0ZF2RI81rGFTnYmCxI+Euj4Aqe/6Y4+8CxN9OVJNGDNIqB5laJ3wxQTU8zXCcm2k9F7FL+/6RVoOT4z1Fw==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.6" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.ObjectPool": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "gA8H7uQOnM5gb+L0uTNjViHYr+hRDqCdfugheGo/MxQnuHzmhhzCBTIPm19qL1z1Xe0NEMabfcOBGv9QghlZ8g==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Primitives": "2.2.0", + "System.ComponentModel.Annotations": "4.5.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } + }, + "Microsoft.Net.Http.Headers": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "iZNkjYqlo8sIOI0bQfpsSoMTmB/kyvmV2h225ihyZT33aTp48ZpF6qYnXxzSXmHt8DpBAwBTX+1s1UFLbYfZKg==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0", + "System.Buffers": "4.5.0" + } + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.10.0", + "contentHash": "KkwhjQevuDj0aBRoPLY6OLAhGqbPUEBuKLbaCs0kUVw29qiOYncdORd4mLVJbn9vGZ7/iFGQ/+AoJl0Tu5Umdg==", + "dependencies": { + "System.Reflection.Metadata": "1.6.0" + } + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.10.0", + "contentHash": "LWpMdfqhHvcUkeMCvNYJO8QlPLlYz9XPPb+ZbaXIKhdmjAV0wqTSrTiW5FLaf7RRZT50AQADDOYMOe0HxDxNgA==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.10.0", + "Newtonsoft.Json": "13.0.1" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.1", + "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + }, + "Serilog.Formatting.Compact": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "pNroKVjo+rDqlxNG5PXkRLpfSCuDOBY0ri6jp9PLe505ljqwhwZz8ospy2vWhQlFu5GkIesh3FcDs4n7sWZODA==", + "dependencies": { + "Serilog": "2.8.0" + } + }, + "Serilog.Sinks.File": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.PeriodicBatching": { + "type": "Transitive", + "resolved": "3.1.0", + "contentHash": "NDWR7m3PalVlGEq3rzoktrXikjFMLmpwF0HI4sowo8YDdU+gqPlTHlDQiOGxHfB0sTfjPA9JjA7ctKG9zqjGkw==", + "dependencies": { + "Serilog": "2.0.0" + } + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.6", + "contentHash": "BmAf6XWt4TqtowmiWe4/5rRot6GerAeklmOPfviOvwLoF5WwgxcJHAxZtySuyW9r9w+HLILnm8VfJFLCUJYW8A==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.6", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.6" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.6", + "contentHash": "wO6v9GeMx9CUngAet8hbO7xdm+M42p1XeJq47ogyRoYSvNSp0NGLI+MgC0bhrMk9C17MTVFlLiN6ylyExLCc5w==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.6", + "contentHash": "2ObJJLkIUIxRpOUlZNGuD4rICpBnrBR5anjyfUFQep4hMOIeqW+XGQYzrNmHSVz5xSWZ3klSbh7sFR6UyDj68Q==" + }, + "SQLitePCLRaw.provider.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.6", + "contentHash": "PQ2Oq3yepLY4P7ll145P3xtx2bX8xF4PzaKPRpw9jZlKvfe4LE/saAV82inND9usn1XRpmxXk7Lal3MTI+6CNg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.6" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "pL2ChpaRRWI/p4LXyy4RgeWlYF2sgfj/pnVMvBqwNFr5cXg7CXNnWZWxrOONLg8VGdFB8oB+EG2Qw4MLgTOe+A==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA==" + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.Management": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "10J1D0h/lioojphfJ4Fuh5ZUThT/xOVHdV9roGBittKKNP2PMjrvibEdbVTGZcPra1399Ja3tqIJLyQrc5Wmhg==", + "dependencies": { + "System.CodeDom": "6.0.0" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.3", + "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==" + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "1.6.0", + "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" + }, + "System.Reflection.TypeExtensions": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "VybpaOQQhqE6siHppMktjfGBw1GCwvCqiufqmP8F1nj7fTUNtW35LOEt3UZTEsECfo+ELAl/9o9nJx3U91i7vA==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "Xg4G4Indi4dqP1iuAiMSwpiWS54ZghzR644OtsRCm/m/lBMG8dUBhLVN7hLm8NNrNTR+iGbshCPTwrvxZPlm4g==" + }, + "speckle.core": { + "type": "Project", + "dependencies": { + "GraphQL.Client": "[6.0.0, )", + "Microsoft.CSharp": "[4.7.0, )", + "Microsoft.Data.Sqlite": "[8.0.6, )", + "Polly": "[7.2.3, )", + "Polly.Contrib.WaitAndRetry": "[1.1.1, )", + "Polly.Extensions.Http": "[3.0.0, )", + "Sentry": "[3.33.0, )", + "Sentry.Serilog": "[3.33.0, )", + "Serilog": "[2.12.0, )", + "Serilog.Enrichers.ClientInfo": "[1.3.0, )", + "Serilog.Exceptions": "[8.4.0, )", + "Serilog.Sinks.Console": "[4.1.0, )", + "Serilog.Sinks.Seq": "[5.2.2, )", + "SerilogTimings": "[3.0.1, )", + "Speckle.DoubleNumerics": "[4.0.1-alpha.3, )", + "Speckle.Newtonsoft.Json": "[13.0.2, )" + } + }, + "speckle.core.tests.unit": { + "type": "Project", + "dependencies": { + "Microsoft.NET.Test.Sdk": "[17.10.0, )", + "NUnit": "[4.1.0, )", + "NUnit3TestAdapter": "[4.5.0, )", + "Speckle.Core": "[1.0.0, )", + "Speckle.Transports.Disk": "[1.0.0, )", + "altcover": "[8.8.74, )" + } + }, + "speckle.transports.disk": { + "type": "Project", + "dependencies": { + "Speckle.Core": "[1.0.0, )" + } + }, + "altcover": { + "type": "CentralTransitive", + "requested": "[8.8.74, )", + "resolved": "8.8.74", + "contentHash": "e8RZNE0vZnuBk/gOAWu9K5wm3S7dOrOlZje3PHI9PJUHqvP1cxVJD1eXAAmddFVlixowB7C7/zvC16GnunC2LQ==" + }, + "GraphQL.Client": { + "type": "CentralTransitive", + "requested": "[6.0.0, )", + "resolved": "6.0.0", + "contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==", + "dependencies": { + "GraphQL.Client.Abstractions": "6.0.0", + "GraphQL.Client.Abstractions.Websocket": "6.0.0", + "System.Reactive": "5.0.0" + } + }, + "Microsoft.CSharp": { + "type": "CentralTransitive", + "requested": "[4.7.0, )", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "CentralTransitive", + "requested": "[8.0.6, )", + "resolved": "8.0.6", + "contentHash": "YVzVtU1IoGsTiMAe0BNV9ssKyrUm6lBQJP6w2N4W29YrBYYtyCmTFmrNGjxaJtJXVqttu0sYkPxmStj/OnMFdg==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "8.0.6", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.6" + } + }, + "Polly": { + "type": "CentralTransitive", + "requested": "[7.2.3, )", + "resolved": "7.2.3", + "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + }, + "Polly.Contrib.WaitAndRetry": { + "type": "CentralTransitive", + "requested": "[1.1.1, )", + "resolved": "1.1.1", + "contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA==" + }, + "Polly.Extensions.Http": { + "type": "CentralTransitive", + "requested": "[3.0.0, )", + "resolved": "3.0.0", + "contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==", + "dependencies": { + "Polly": "7.1.0" + } + }, + "Sentry": { + "type": "CentralTransitive", + "requested": "[3.33.0, )", + "resolved": "3.33.0", + "contentHash": "8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A==" + }, + "Sentry.Serilog": { + "type": "CentralTransitive", + "requested": "[3.33.0, )", + "resolved": "3.33.0", + "contentHash": "V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==", + "dependencies": { + "Sentry": "3.33.0", + "Serilog": "2.10.0" + } + }, + "Serilog": { + "type": "CentralTransitive", + "requested": "[2.12.0, )", + "resolved": "2.12.0", + "contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg==" + }, + "Serilog.Enrichers.ClientInfo": { + "type": "CentralTransitive", + "requested": "[1.3.0, )", + "resolved": "1.3.0", + "contentHash": "mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==", + "dependencies": { + "Microsoft.AspNetCore.Http": "2.2.2", + "Serilog": "2.9.0" + } + }, + "Serilog.Exceptions": { + "type": "CentralTransitive", + "requested": "[8.4.0, )", + "resolved": "8.4.0", + "contentHash": "nc/+hUw3lsdo0zCj0KMIybAu7perMx79vu72w0za9Nsi6mWyNkGXxYxakAjWB7nEmYL6zdmhEQRB4oJ2ALUeug==", + "dependencies": { + "Serilog": "2.8.0", + "System.Reflection.TypeExtensions": "4.7.0" + } + }, + "Serilog.Sinks.Console": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "K6N5q+5fetjnJPvCmkWOpJ/V8IEIoMIB1s86OzBrbxwTyHxdx3pmz4H+8+O/Dc/ftUX12DM1aynx/dDowkwzqg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.Seq": { + "type": "CentralTransitive", + "requested": "[5.2.2, )", + "resolved": "5.2.2", + "contentHash": "1Csmo5ua7NKUe0yXUx+zsRefjAniPWcXFhUXxXG8pwo0iMiw2gjn9SOkgYnnxbgWqmlGv236w0N/dHc2v5XwMg==", + "dependencies": { + "Serilog": "2.12.0", + "Serilog.Formatting.Compact": "1.1.0", + "Serilog.Sinks.File": "5.0.0", + "Serilog.Sinks.PeriodicBatching": "3.1.0" + } + }, + "SerilogTimings": { + "type": "CentralTransitive", + "requested": "[3.0.1, )", + "resolved": "3.0.1", + "contentHash": "Zs28eTgszAMwpIrbBnWHBI50yuxL50p/dmAUWmy75+axdZYK/Sjm5/5m1N/CisR8acJUhTVcjPZrsB1P5iv0Uw==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.0.1-alpha.3, )", + "resolved": "4.0.1-alpha.3", + "contentHash": "7/lg9LDI3TZLiI7FhI2IhRrTzsZyDrQw1rG/S3kRtx0IU3raWzRHL4W5zvHV8LdWkqJEztS/9dUgLNYHV70o5Q==" + }, + "Speckle.Newtonsoft.Json": { + "type": "CentralTransitive", + "requested": "[13.0.2, )", + "resolved": "13.0.2", + "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" + } + } + } +} \ No newline at end of file diff --git a/tests/Speckle.Core.Tests.Performance/Api/Operations/ReceiveFromSQLite.cs b/tests/Speckle.Core.Tests.Performance/Api/Operations/ReceiveFromSQLite.cs deleted file mode 100644 index 8a9e3f5c..00000000 --- a/tests/Speckle.Core.Tests.Performance/Api/Operations/ReceiveFromSQLite.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.Diagnostics; -using BenchmarkDotNet.Attributes; -using Speckle.Core.Models; - -namespace Speckle.Core.Tests.Performance.Api.Operations; - -[MemoryDiagnoser] -[RegressionTestConfig(1, 1, 8, nugetVersions: "2.15.2")] -public class ReceiveFromSQLite : IDisposable -{ - [Params(0, 4, 9, 19)] - public int DataComplexity { get; set; } - - private TestDataHelper _dataSource; - - [GlobalSetup] - public async Task Setup() - { - _dataSource = new TestDataHelper(); - await _dataSource.SeedTransport(DataComplexity).ConfigureAwait(false); - } - - [Benchmark] - public async Task Receive_FromSQLite() - { - Base b = await Speckle - .Core.Api.Operations.Receive(_dataSource.ObjectId, null, _dataSource.Transport) - .ConfigureAwait(false); - - Trace.Assert(b is not null); - return b; - } - - [GlobalCleanup] - public virtual void Cleanup() - { - Dispose(); - } - - public void Dispose() - { - _dataSource.Dispose(); - } -} diff --git a/tests/Speckle.Core.Tests.Performance/Api/Operations/TraverseCommit.cs b/tests/Speckle.Core.Tests.Performance/Api/Operations/TraverseCommit.cs deleted file mode 100644 index df3a8434..00000000 --- a/tests/Speckle.Core.Tests.Performance/Api/Operations/TraverseCommit.cs +++ /dev/null @@ -1,38 +0,0 @@ -using BenchmarkDotNet.Attributes; -using Speckle.Core.Models; -using Speckle.Core.Models.GraphTraversal; - -namespace Speckle.Core.Tests.Performance.Api.Operations; - -[MemoryDiagnoser] -[RegressionTestConfig(1, 1, 20, nugetVersions: "2.15.2")] -public class TraverseCommit -{ - [Params(0, 4, 9, 19)] - public int DataComplexity { get; set; } - - private Base _testData; - private GraphTraversal _sut; - - [GlobalSetup] - public async Task Setup() - { - using var dataSource = new TestDataHelper(); - await dataSource.SeedTransport(DataComplexity).ConfigureAwait(false); - _testData = await dataSource.DeserializeBase().ConfigureAwait(false); - - var convertableRule = TraversalRule - .NewTraversalRule() - .When(b => b.speckle_type.Contains("Geometry")) - .When(DefaultTraversal.HasDisplayValue) - .ContinueTraversing(_ => DefaultTraversal.elementsPropAliases); - - _sut = new GraphTraversal(convertableRule, DefaultTraversal.DefaultRule); - } - - [Benchmark] - public List Traverse() - { - return _sut.Traverse(_testData).ToList(); - } -} diff --git a/tests/Speckle.Core.Tests.Performance/Program.cs b/tests/Speckle.Core.Tests.Performance/Program.cs deleted file mode 100644 index 815d61f4..00000000 --- a/tests/Speckle.Core.Tests.Performance/Program.cs +++ /dev/null @@ -1,11 +0,0 @@ -using BenchmarkDotNet.Running; - -namespace Speckle.Core.Tests.Performance; - -public static class Program -{ - public static void Main(string[] args) - { - BenchmarkSwitcher.FromAssemblies(new[] { typeof(Program).Assembly }).Run(args); - } -} diff --git a/tests/Speckle.Core.Tests.Performance/RegressionTestConfig.cs b/tests/Speckle.Core.Tests.Performance/RegressionTestConfig.cs deleted file mode 100644 index ac3a03d5..00000000 --- a/tests/Speckle.Core.Tests.Performance/RegressionTestConfig.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using BenchmarkDotNet.Configs; -using BenchmarkDotNet.Engines; -using BenchmarkDotNet.Environments; -using BenchmarkDotNet.Jobs; - -namespace Speckle.Core.Tests.Performance; - -[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class, AllowMultiple = true)] -[SuppressMessage( - "Design", - "CA1019:Define accessors for attribute arguments", - Justification = "Suggestion does not fit with IConfigSource pattern" -)] -public sealed class RegressionTestConfigAttribute : Attribute, IConfigSource -{ - public IConfig Config { get; private set; } - - public RegressionTestConfigAttribute( - int launchCount = 1, - int warmupCount = 0, - int iterationCount = 10, - RunStrategy strategy = RunStrategy.Monitoring, - bool includeHead = true, - params string[] nugetVersions - ) - { - List jobs = new(); - - if (includeHead) - { - jobs.Add( - new Job("Head") - .WithRuntime(ClrRuntime.Net481) - .WithStrategy(strategy) - .WithLaunchCount(launchCount) - .WithWarmupCount(warmupCount) - .WithIterationCount(iterationCount) - ); - } - - bool isBaseline = true; - foreach (var version in nugetVersions) - { - jobs.Add( - new Job(version) - .WithRuntime(ClrRuntime.Net481) - .WithStrategy(strategy) - .WithLaunchCount(launchCount) - .WithWarmupCount(warmupCount) - .WithIterationCount(iterationCount) - .WithNuGet("Speckle.Objects", version) - .WithBaseline(isBaseline) - ); - - isBaseline = false; - } - - Config = ManualConfig.CreateEmpty().AddJob(jobs.ToArray()); - } -} diff --git a/tests/Speckle.Core.Tests.Performance/Serialisation/DeserializationWorkerThreads.cs b/tests/Speckle.Core.Tests.Performance/Serialisation/DeserializationWorkerThreads.cs deleted file mode 100644 index de50926d..00000000 --- a/tests/Speckle.Core.Tests.Performance/Serialisation/DeserializationWorkerThreads.cs +++ /dev/null @@ -1,43 +0,0 @@ -using BenchmarkDotNet.Attributes; -using Speckle.Core.Models; -using Speckle.Core.Serialisation; - -namespace Speckle.Core.Tests.Performance.Serialisation; - -[MemoryDiagnoser] -[RegressionTestConfig(1, 1, 6)] -public class DeserializationWorkerThreads : IDisposable -{ - public static IEnumerable NumThreadsToTest => Enumerable.Range(0, Environment.ProcessorCount + 1); - - [Params(0, 9)] - public int DataComplexity { get; set; } - - private TestDataHelper _dataSource; - - [GlobalSetup] - public async Task Setup() - { - _dataSource = new TestDataHelper(); - await _dataSource.SeedTransport(DataComplexity).ConfigureAwait(false); - } - - [Benchmark] - [ArgumentsSource(nameof(NumThreadsToTest))] - public Base RunTest(int numThreads) - { - BaseObjectDeserializerV2 sut = new() { WorkerThreadCount = numThreads, ReadTransport = _dataSource.Transport }; - return sut.Deserialize(_dataSource.Transport.GetObject(_dataSource.ObjectId)!); - } - - [GlobalCleanup] - public virtual void Cleanup() - { - Dispose(); - } - - public void Dispose() - { - _dataSource.Dispose(); - } -} diff --git a/tests/Speckle.Core.Tests.Performance/Speckle.Core.Tests.Performance.csproj b/tests/Speckle.Core.Tests.Performance/Speckle.Core.Tests.Performance.csproj deleted file mode 100644 index 625d087f..00000000 --- a/tests/Speckle.Core.Tests.Performance/Speckle.Core.Tests.Performance.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net48 - enable - disable - exe - true - - - - true - - - - - - - - - - - diff --git a/tests/Speckle.Core.Tests.Performance/TestDataHelper.cs b/tests/Speckle.Core.Tests.Performance/TestDataHelper.cs deleted file mode 100644 index f95c872b..00000000 --- a/tests/Speckle.Core.Tests.Performance/TestDataHelper.cs +++ /dev/null @@ -1,54 +0,0 @@ -using Microsoft.Data.Sqlite; -using Speckle.Core.Api; -using Speckle.Core.Credentials; -using Speckle.Core.Models; -using Speckle.Core.Transports; - -namespace Speckle.Core.Tests.Performance; - -public sealed class TestDataHelper : IDisposable -{ - private static readonly string s_basePath = $"./temp {Guid.NewGuid()}"; - private const string APPLICATION_NAME = "Speckle Performance Tests"; - - public SQLiteTransport Transport { get; private set; } - public string ObjectId { get; private set; } - - public async Task SeedTransport(int dataComplexity) - { - Transport = new SQLiteTransport(s_basePath, APPLICATION_NAME); - - //seed SQLite transport with test data - ObjectId = await SeedTransport(dataComplexity, Transport).ConfigureAwait(false); - } - - public static async Task SeedTransport(int dataComplexity, ITransport transport) - { - //seed SQLite transport with test data - StreamWrapper sw = new($"https://latest.speckle.dev/streams/efd2c6a31d/branches/{dataComplexity}"); - var acc = await sw.GetAccount().ConfigureAwait(false); - using var client = new Client(acc); - var branch = await client.BranchGet(sw.StreamId, sw.BranchName!, 1).ConfigureAwait(false); - var objectId = branch.commits.items[0].referencedObject; - - using ServerTransport remoteTransport = new(acc, sw.StreamId); - transport.BeginWrite(); - await remoteTransport.CopyObjectAndChildren(objectId, transport).ConfigureAwait(false); - transport.EndWrite(); - await transport.WriteComplete().ConfigureAwait(false); - - return objectId; - } - - public async Task DeserializeBase() - { - return await Operations.Receive(ObjectId, null, Transport).ConfigureAwait(false); - } - - public void Dispose() - { - Transport.Dispose(); - SqliteConnection.ClearAllPools(); - Directory.Delete(s_basePath, true); - } -} diff --git a/tests/Speckle.Core.Tests.Unit/Api/GraphQLClient.cs b/tests/Speckle.Core.Tests.Unit/Api/GraphQLClient.cs index 0733e39a..9baadcac 100644 --- a/tests/Speckle.Core.Tests.Unit/Api/GraphQLClient.cs +++ b/tests/Speckle.Core.Tests.Unit/Api/GraphQLClient.cs @@ -2,6 +2,7 @@ using GraphQL; using NUnit.Framework; using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Credentials; namespace Speckle.Core.Tests.Unit.Api; @@ -30,16 +31,10 @@ public void Dispose() private static IEnumerable ErrorCases() { + yield return new TestCaseData(typeof(SpeckleGraphQLForbiddenException), new Map { { "code", "FORBIDDEN" } }); + yield return new TestCaseData(typeof(SpeckleGraphQLForbiddenException), new Map { { "code", "UNAUTHENTICATED" } }); yield return new TestCaseData( - typeof(SpeckleGraphQLForbiddenException), - new Map { { "code", "FORBIDDEN" } } - ); - yield return new TestCaseData( - typeof(SpeckleGraphQLForbiddenException), - new Map { { "code", "UNAUTHENTICATED" } } - ); - yield return new TestCaseData( - typeof(SpeckleGraphQLInternalErrorException), + typeof(SpeckleGraphQLInternalErrorException), new Map { { "code", "INTERNAL_SERVER_ERROR" } } ); yield return new TestCaseData(typeof(SpeckleGraphQLException), new Map { { "foo", "bar" } }); @@ -109,7 +104,7 @@ public async Task TestExecuteWithResiliencePoliciesRetry() counter++; if (counter < maxRetryCount) { - throw new SpeckleGraphQLInternalErrorException(new GraphQLRequest(), new GraphQLResponse()); + throw new SpeckleGraphQLInternalErrorException(new GraphQLRequest(), new GraphQLResponse()); } return Task.FromResult(expectedResult); diff --git a/tests/Speckle.Core.Tests.Unit/Credentials/AccountServerMigrationTests.cs b/tests/Speckle.Core.Tests.Unit/Credentials/AccountServerMigrationTests.cs index f66587e2..4ef401ad 100644 --- a/tests/Speckle.Core.Tests.Unit/Credentials/AccountServerMigrationTests.cs +++ b/tests/Speckle.Core.Tests.Unit/Credentials/AccountServerMigrationTests.cs @@ -1,5 +1,5 @@ using NUnit.Framework; -using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Credentials; namespace Speckle.Core.Tests.Unit.Credentials; @@ -14,12 +14,10 @@ public static IEnumerable MigrationTestCase() const string NEW_URL = "https://new.example.com"; const string OTHER_URL = "https://other.example.com"; Account oldAccount = CreateTestAccount(OLD_URL, null, new(NEW_URL)); - Account newAccount = CreateTestAccount(NEW_URL, new(OLD_URL), null); + string accountId = oldAccount.userInfo.id; // new account user must match old account user id + Account newAccount = CreateTestAccount(NEW_URL, new(OLD_URL), null, accountId); Account otherAccount = CreateTestAccount(OTHER_URL, null, null); - // new account user must match old account user id - newAccount.userInfo.id = oldAccount.userInfo.id; - List givenAccounts = new() { oldAccount, newAccount, otherAccount }; yield return new TestCaseData(givenAccounts, NEW_URL, new[] { newAccount }) @@ -59,8 +57,9 @@ public void TearDown() _accountsToCleanUp.Clear(); } - private static Account CreateTestAccount(string url, Uri? movedFrom, Uri? movedTo) + private static Account CreateTestAccount(string url, Uri? movedFrom, Uri? movedTo, string? id = null) { + id ??= Guid.NewGuid().ToString(); return new Account { token = "myToken", @@ -72,7 +71,7 @@ private static Account CreateTestAccount(string url, Uri? movedFrom, Uri? movedT }, userInfo = new UserInfo { - id = Guid.NewGuid().ToString(), + id = id, email = "user@example.com", name = "user" } diff --git a/tests/Speckle.Core.Tests.Unit/Credentials/Accounts.cs b/tests/Speckle.Core.Tests.Unit/Credentials/Accounts.cs index 7c86a66f..4ac91d47 100644 --- a/tests/Speckle.Core.Tests.Unit/Credentials/Accounts.cs +++ b/tests/Speckle.Core.Tests.Unit/Credentials/Accounts.cs @@ -1,5 +1,5 @@ using NUnit.Framework; -using Speckle.Core.Api; +using Speckle.Core.Api.GraphQL.Models; using Speckle.Core.Credentials; namespace Speckle.Core.Tests.Unit.Credentials; diff --git a/tests/Speckle.Core.Tests.Unit/Serialisation/SerializerNonBreakingChanges.cs b/tests/Speckle.Core.Tests.Unit/Serialisation/SerializerNonBreakingChanges.cs index ef2c5e50..c6a18fbc 100644 --- a/tests/Speckle.Core.Tests.Unit/Serialisation/SerializerNonBreakingChanges.cs +++ b/tests/Speckle.Core.Tests.Unit/Serialisation/SerializerNonBreakingChanges.cs @@ -1,9 +1,9 @@ -using System.DoubleNumerics; using System.Drawing; using NUnit.Framework; using Speckle.Core.Api; using Speckle.Core.Helpers; using Speckle.Core.Models; +using Speckle.DoubleNumerics; namespace Speckle.Core.Tests.Unit.Serialisation; @@ -87,6 +87,42 @@ public void ListToArray(double[] testCase) Assert.That(res.value, Is.EquivalentTo(testCase)); } + [Test, TestCaseSource(nameof(s_arrayTestCases))] + public void ListToIList(double[] testCase) + { + var from = new ListDoubleValueMock { value = testCase.ToList() }; + + var res = from.SerializeAsTAndDeserialize(); + Assert.That(res.value, Is.EquivalentTo(testCase)); + } + + [Test, TestCaseSource(nameof(s_arrayTestCases))] + public void ListToIReadOnlyList(double[] testCase) + { + var from = new ListDoubleValueMock { value = testCase.ToList() }; + + var res = from.SerializeAsTAndDeserialize(); + Assert.That(res.value, Is.EquivalentTo(testCase)); + } + + [Test, TestCaseSource(nameof(s_arrayTestCases))] + public void IListToList(double[] testCase) + { + var from = new IListDoubleValueMock { value = testCase.ToList() }; + + var res = from.SerializeAsTAndDeserialize(); + Assert.That(res.value, Is.EquivalentTo(testCase)); + } + + [Test, TestCaseSource(nameof(s_arrayTestCases))] + public void IReadOnlyListToList(double[] testCase) + { + var from = new IReadOnlyListDoubleValueMock { value = testCase.ToList() }; + + var res = from.SerializeAsTAndDeserialize(); + Assert.That(res.value, Is.EquivalentTo(testCase)); + } + [Test, TestCaseSource(nameof(MyEnums))] public void EnumToInt(MyEnum testCase) { @@ -171,6 +207,16 @@ public class ListDoubleValueMock : SerializerMock public List value { get; set; } } +public class IListDoubleValueMock : SerializerMock +{ + public IList value { get; set; } +} + +public class IReadOnlyListDoubleValueMock : SerializerMock +{ + public IReadOnlyList value { get; set; } +} + public class ArrayDoubleValueMock : SerializerMock { public double[] value { get; set; } diff --git a/tests/Speckle.Core.Tests.Unit/Speckle.Core.Tests.Unit.csproj b/tests/Speckle.Core.Tests.Unit/Speckle.Core.Tests.Unit.csproj index 6c07991e..ee67ebc9 100644 --- a/tests/Speckle.Core.Tests.Unit/Speckle.Core.Tests.Unit.csproj +++ b/tests/Speckle.Core.Tests.Unit/Speckle.Core.Tests.Unit.csproj @@ -2,13 +2,14 @@ net8.0 - enable + false true + System.Runtime.CompilerServices.IsExternalInit;System.Runtime.CompilerServices.RequiresLocationAttribute - + diff --git a/tests/Speckle.Core.Tests.Unit/Transports/DiskTransportTests.cs b/tests/Speckle.Core.Tests.Unit/Transports/DiskTransportTests.cs index eb19187d..5659e989 100644 --- a/tests/Speckle.Core.Tests.Unit/Transports/DiskTransportTests.cs +++ b/tests/Speckle.Core.Tests.Unit/Transports/DiskTransportTests.cs @@ -1,4 +1,5 @@ using NUnit.Framework; +using Speckle.Core.Common; using Speckle.Core.Transports; namespace Speckle.Core.Tests.Unit.Transports; @@ -7,7 +8,7 @@ namespace Speckle.Core.Tests.Unit.Transports; [TestOf(nameof(DiskTransport))] public sealed class DiskTransportTests : TransportTests { - protected override ITransport Sut => _diskTransport!; + protected override ITransport Sut => _diskTransport.NotNull(); private DiskTransport _diskTransport; diff --git a/tests/Speckle.Core.Tests.Unit/Transports/MemoryTransportTests.cs b/tests/Speckle.Core.Tests.Unit/Transports/MemoryTransportTests.cs index e518eea5..30a53a5f 100644 --- a/tests/Speckle.Core.Tests.Unit/Transports/MemoryTransportTests.cs +++ b/tests/Speckle.Core.Tests.Unit/Transports/MemoryTransportTests.cs @@ -1,5 +1,6 @@ using System.Collections.Concurrent; using NUnit.Framework; +using Speckle.Core.Common; using Speckle.Core.Transports; namespace Speckle.Core.Tests.Unit.Transports; @@ -8,7 +9,7 @@ namespace Speckle.Core.Tests.Unit.Transports; [TestOf(nameof(MemoryTransport))] public sealed class MemoryTransportTests : TransportTests { - protected override ITransport Sut => _memoryTransport!; + protected override ITransport Sut => _memoryTransport.NotNull(); private MemoryTransport _memoryTransport; diff --git a/tests/Speckle.Core.Tests.Unit/packages.lock.json b/tests/Speckle.Core.Tests.Unit/packages.lock.json index af20dbd0..50be2f6e 100644 --- a/tests/Speckle.Core.Tests.Unit/packages.lock.json +++ b/tests/Speckle.Core.Tests.Unit/packages.lock.json @@ -2,11 +2,11 @@ "version": 2, "dependencies": { "net8.0": { - "JunitXml.TestLogger": { + "altcover": { "type": "Direct", - "requested": "[3.0.124, )", - "resolved": "3.0.124", - "contentHash": "QTZhSNm/xjj24W1yterf6eABv6KO+Y9jBqpau5RzPehdXTXkZcGQaLf/i50nTl+qnSwpbKkrC+bSyvLRE1ZNAg==" + "requested": "[8.8.74, )", + "resolved": "8.8.74", + "contentHash": "e8RZNE0vZnuBk/gOAWu9K5wm3S7dOrOlZje3PHI9PJUHqvP1cxVJD1eXAAmddFVlixowB7C7/zvC16GnunC2LQ==" }, "Microsoft.NET.Test.Sdk": { "type": "Direct", @@ -123,10 +123,10 @@ }, "Microsoft.Data.Sqlite.Core": { "type": "Transitive", - "resolved": "7.0.5", - "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "resolved": "8.0.6", + "contentHash": "umhZ0ZF2RI81rGFTnYmCxI+Euj4Aqe/6Y4+8CxN9OVJNGDNIqB5laJ3wxQTU8zXCcm2k9F7FL+/6RVoOT4z1Fw==", "dependencies": { - "SQLitePCLRaw.core": "2.1.4" + "SQLitePCLRaw.core": "2.1.6" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { @@ -220,32 +220,32 @@ }, "SQLitePCLRaw.bundle_e_sqlite3": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "resolved": "2.1.6", + "contentHash": "BmAf6XWt4TqtowmiWe4/5rRot6GerAeklmOPfviOvwLoF5WwgxcJHAxZtySuyW9r9w+HLILnm8VfJFLCUJYW8A==", "dependencies": { - "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", - "SQLitePCLRaw.provider.e_sqlite3": "2.1.4" + "SQLitePCLRaw.lib.e_sqlite3": "2.1.6", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.6" } }, "SQLitePCLRaw.core": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "resolved": "2.1.6", + "contentHash": "wO6v9GeMx9CUngAet8hbO7xdm+M42p1XeJq47ogyRoYSvNSp0NGLI+MgC0bhrMk9C17MTVFlLiN6ylyExLCc5w==", "dependencies": { "System.Memory": "4.5.3" } }, "SQLitePCLRaw.lib.e_sqlite3": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + "resolved": "2.1.6", + "contentHash": "2ObJJLkIUIxRpOUlZNGuD4rICpBnrBR5anjyfUFQep4hMOIeqW+XGQYzrNmHSVz5xSWZ3klSbh7sFR6UyDj68Q==" }, "SQLitePCLRaw.provider.e_sqlite3": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==", + "resolved": "2.1.6", + "contentHash": "PQ2Oq3yepLY4P7ll145P3xtx2bX8xF4PzaKPRpw9jZlKvfe4LE/saAV82inND9usn1XRpmxXk7Lal3MTI+6CNg==", "dependencies": { - "SQLitePCLRaw.core": "2.1.4" + "SQLitePCLRaw.core": "2.1.6" } }, "System.Buffers": { @@ -293,7 +293,7 @@ "dependencies": { "GraphQL.Client": "[6.0.0, )", "Microsoft.CSharp": "[4.7.0, )", - "Microsoft.Data.Sqlite": "[7.0.5, )", + "Microsoft.Data.Sqlite": "[8.0.6, )", "Polly": "[7.2.3, )", "Polly.Contrib.WaitAndRetry": "[1.1.1, )", "Polly.Extensions.Http": "[3.0.0, )", @@ -305,8 +305,8 @@ "Serilog.Sinks.Console": "[4.1.0, )", "Serilog.Sinks.Seq": "[5.2.2, )", "SerilogTimings": "[3.0.1, )", - "Speckle.Newtonsoft.Json": "[13.0.2, )", - "System.DoubleNumerics": "[3.1.3, )" + "Speckle.DoubleNumerics": "[4.0.1-alpha.3, )", + "Speckle.Newtonsoft.Json": "[13.0.2, )" } }, "speckle.transports.disk": { @@ -334,12 +334,12 @@ }, "Microsoft.Data.Sqlite": { "type": "CentralTransitive", - "requested": "[7.0.5, )", - "resolved": "7.0.5", - "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "requested": "[8.0.6, )", + "resolved": "8.0.6", + "contentHash": "YVzVtU1IoGsTiMAe0BNV9ssKyrUm6lBQJP6w2N4W29YrBYYtyCmTFmrNGjxaJtJXVqttu0sYkPxmStj/OnMFdg==", "dependencies": { - "Microsoft.Data.Sqlite.Core": "7.0.5", - "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + "Microsoft.Data.Sqlite.Core": "8.0.6", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.6" } }, "Polly": { @@ -435,20 +435,17 @@ "Serilog": "2.10.0" } }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.0.1-alpha.3, )", + "resolved": "4.0.1-alpha.3", + "contentHash": "7/lg9LDI3TZLiI7FhI2IhRrTzsZyDrQw1rG/S3kRtx0IU3raWzRHL4W5zvHV8LdWkqJEztS/9dUgLNYHV70o5Q==" + }, "Speckle.Newtonsoft.Json": { "type": "CentralTransitive", "requested": "[13.0.2, )", "resolved": "13.0.2", "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" - }, - "System.DoubleNumerics": { - "type": "CentralTransitive", - "requested": "[3.1.3, )", - "resolved": "3.1.3", - "contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==", - "dependencies": { - "NETStandard.Library": "1.6.1" - } } } } diff --git a/tests/Speckle.Objects.Tests.Unit/Geometry/TransformTests.cs b/tests/Speckle.Objects.Tests.Unit/Geometry/TransformTests.cs index 4a58004c..faaebb49 100644 --- a/tests/Speckle.Objects.Tests.Unit/Geometry/TransformTests.cs +++ b/tests/Speckle.Objects.Tests.Unit/Geometry/TransformTests.cs @@ -1,8 +1,8 @@ using System.Collections; -using System.DoubleNumerics; using NUnit.Framework; using Objects.Other; using Speckle.Core.Kits; +using Speckle.DoubleNumerics; namespace Objects.Tests.Unit.Geometry; diff --git a/tests/Speckle.Objects.Tests.Unit/ModelPropertySupportedTypes.cs b/tests/Speckle.Objects.Tests.Unit/ModelPropertySupportedTypes.cs new file mode 100644 index 00000000..d326ee4b --- /dev/null +++ b/tests/Speckle.Objects.Tests.Unit/ModelPropertySupportedTypes.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using NUnit.Framework; +using Speckle.Core.Models; +using Speckle.Core.Serialisation; +using Speckle.DoubleNumerics; +using Speckle.Newtonsoft.Json; + +namespace Objects.Tests.Unit; + +/// +/// Tests that all Base object models in the kit have properties that are an allowed type +/// This test is not exhaustive, there are plenty of generic arg combinations that will pass this test, +/// but still not work / are not defined behaviour. This test will just catch many types that definitely won't work +/// +public class ModelPropertySupportedTypes +{ + /// + /// Set of types that we support in Base objects + /// If it's not in the list, or is commented out, it's not supported by our serializer! + /// + /// + /// If you're tempted to add to this list, please ensure both our serializer and deserializer support properties of this type + /// Check the + /// Check the + /// (or is an interface where all concrete types are supported) + /// You should also consider adding a test in SerializerNonBreakingChanges + /// + private readonly HashSet _allowedTypes = + new() + { + typeof(Boolean), + typeof(Byte), + typeof(UInt32), + typeof(UInt64), + typeof(Int16), + typeof(Int32), + typeof(Int64), + //typeof(Half), + typeof(Single), + typeof(Double), + typeof(Char), + typeof(string), + typeof(DateTime), + typeof(Guid), + typeof(Color), + typeof(List<>), + typeof(Nullable<>), + typeof(IList<>), + typeof(IReadOnlyList<>), + typeof(Dictionary<,>), + //typeof(IDictionary<,>), + //typeof(IReadOnlyDictionary<,>), + typeof(ICurve), + typeof(Object), + typeof(Matrix4x4), + }; + + [Test] + [TestCaseSource(typeof(GenericTests), nameof(GenericTests.AvailableTypesInKit))] + public void TestObjects(Type t) + { + var members = DynamicBase.GetInstanceMembers(t).Where(p => !p.IsDefined(typeof(JsonIgnoreAttribute), true)); + + foreach (var prop in members) + { + if (prop.PropertyType.IsAssignableTo(typeof(Base))) + continue; + if (prop.PropertyType.IsEnum) + continue; + if (prop.PropertyType.IsSZArray) + continue; + + Type propType = prop.PropertyType; + Type typeDef = propType.IsGenericType ? propType.GetGenericTypeDefinition() : propType; + Assert.That(_allowedTypes, Does.Contain(typeDef), $"{typeDef} was not in allowedTypes"); + } + } +} diff --git a/tests/Speckle.Objects.Tests.Unit/Speckle.Objects.Tests.Unit.csproj b/tests/Speckle.Objects.Tests.Unit/Speckle.Objects.Tests.Unit.csproj index bbe796c3..a29b2820 100644 --- a/tests/Speckle.Objects.Tests.Unit/Speckle.Objects.Tests.Unit.csproj +++ b/tests/Speckle.Objects.Tests.Unit/Speckle.Objects.Tests.Unit.csproj @@ -7,7 +7,7 @@ - + diff --git a/tests/Speckle.Objects.Tests.Unit/packages.lock.json b/tests/Speckle.Objects.Tests.Unit/packages.lock.json index 3f189f8e..57d971d8 100644 --- a/tests/Speckle.Objects.Tests.Unit/packages.lock.json +++ b/tests/Speckle.Objects.Tests.Unit/packages.lock.json @@ -2,11 +2,11 @@ "version": 2, "dependencies": { "net8.0": { - "JunitXml.TestLogger": { + "altcover": { "type": "Direct", - "requested": "[3.0.124, )", - "resolved": "3.0.124", - "contentHash": "QTZhSNm/xjj24W1yterf6eABv6KO+Y9jBqpau5RzPehdXTXkZcGQaLf/i50nTl+qnSwpbKkrC+bSyvLRE1ZNAg==" + "requested": "[8.8.74, )", + "resolved": "8.8.74", + "contentHash": "e8RZNE0vZnuBk/gOAWu9K5wm3S7dOrOlZje3PHI9PJUHqvP1cxVJD1eXAAmddFVlixowB7C7/zvC16GnunC2LQ==" }, "Microsoft.NET.Test.Sdk": { "type": "Direct", @@ -123,10 +123,10 @@ }, "Microsoft.Data.Sqlite.Core": { "type": "Transitive", - "resolved": "7.0.5", - "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "resolved": "8.0.6", + "contentHash": "umhZ0ZF2RI81rGFTnYmCxI+Euj4Aqe/6Y4+8CxN9OVJNGDNIqB5laJ3wxQTU8zXCcm2k9F7FL+/6RVoOT4z1Fw==", "dependencies": { - "SQLitePCLRaw.core": "2.1.4" + "SQLitePCLRaw.core": "2.1.6" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { @@ -220,32 +220,32 @@ }, "SQLitePCLRaw.bundle_e_sqlite3": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "resolved": "2.1.6", + "contentHash": "BmAf6XWt4TqtowmiWe4/5rRot6GerAeklmOPfviOvwLoF5WwgxcJHAxZtySuyW9r9w+HLILnm8VfJFLCUJYW8A==", "dependencies": { - "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", - "SQLitePCLRaw.provider.e_sqlite3": "2.1.4" + "SQLitePCLRaw.lib.e_sqlite3": "2.1.6", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.6" } }, "SQLitePCLRaw.core": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "resolved": "2.1.6", + "contentHash": "wO6v9GeMx9CUngAet8hbO7xdm+M42p1XeJq47ogyRoYSvNSp0NGLI+MgC0bhrMk9C17MTVFlLiN6ylyExLCc5w==", "dependencies": { "System.Memory": "4.5.3" } }, "SQLitePCLRaw.lib.e_sqlite3": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + "resolved": "2.1.6", + "contentHash": "2ObJJLkIUIxRpOUlZNGuD4rICpBnrBR5anjyfUFQep4hMOIeqW+XGQYzrNmHSVz5xSWZ3klSbh7sFR6UyDj68Q==" }, "SQLitePCLRaw.provider.e_sqlite3": { "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==", + "resolved": "2.1.6", + "contentHash": "PQ2Oq3yepLY4P7ll145P3xtx2bX8xF4PzaKPRpw9jZlKvfe4LE/saAV82inND9usn1XRpmxXk7Lal3MTI+6CNg==", "dependencies": { - "SQLitePCLRaw.core": "2.1.4" + "SQLitePCLRaw.core": "2.1.6" } }, "System.Buffers": { @@ -293,7 +293,7 @@ "dependencies": { "GraphQL.Client": "[6.0.0, )", "Microsoft.CSharp": "[4.7.0, )", - "Microsoft.Data.Sqlite": "[7.0.5, )", + "Microsoft.Data.Sqlite": "[8.0.6, )", "Polly": "[7.2.3, )", "Polly.Contrib.WaitAndRetry": "[1.1.1, )", "Polly.Extensions.Http": "[3.0.0, )", @@ -305,8 +305,8 @@ "Serilog.Sinks.Console": "[4.1.0, )", "Serilog.Sinks.Seq": "[5.2.2, )", "SerilogTimings": "[3.0.1, )", - "Speckle.Newtonsoft.Json": "[13.0.2, )", - "System.DoubleNumerics": "[3.1.3, )" + "Speckle.DoubleNumerics": "[4.0.1-alpha.3, )", + "Speckle.Newtonsoft.Json": "[13.0.2, )" } }, "speckle.objects": { @@ -334,12 +334,12 @@ }, "Microsoft.Data.Sqlite": { "type": "CentralTransitive", - "requested": "[7.0.5, )", - "resolved": "7.0.5", - "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "requested": "[8.0.6, )", + "resolved": "8.0.6", + "contentHash": "YVzVtU1IoGsTiMAe0BNV9ssKyrUm6lBQJP6w2N4W29YrBYYtyCmTFmrNGjxaJtJXVqttu0sYkPxmStj/OnMFdg==", "dependencies": { - "Microsoft.Data.Sqlite.Core": "7.0.5", - "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + "Microsoft.Data.Sqlite.Core": "8.0.6", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.6" } }, "Polly": { @@ -435,20 +435,17 @@ "Serilog": "2.10.0" } }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.0.1-alpha.3, )", + "resolved": "4.0.1-alpha.3", + "contentHash": "7/lg9LDI3TZLiI7FhI2IhRrTzsZyDrQw1rG/S3kRtx0IU3raWzRHL4W5zvHV8LdWkqJEztS/9dUgLNYHV70o5Q==" + }, "Speckle.Newtonsoft.Json": { "type": "CentralTransitive", "requested": "[13.0.2, )", "resolved": "13.0.2", "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" - }, - "System.DoubleNumerics": { - "type": "CentralTransitive", - "requested": "[3.1.3, )", - "resolved": "3.1.3", - "contentHash": "KRKEM/L3KBodjA9VOg3EifFVWUY6EOqaMB05UvPEDm7Zeby/kZW+4kdWUEPzW6xtkwf46p661L9NrbeeQhtLzw==", - "dependencies": { - "NETStandard.Library": "1.6.1" - } } } }