diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index a92d6ed8..665e4894 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,16 +3,18 @@ "isRoot": true, "tools": { "csharpier": { - "version": "0.28.2", + "version": "0.30.6", "commands": [ "dotnet-csharpier" - ] + ], + "rollForward": false }, "gitversion.tool": { - "version": "5.12.0", + "version": "6.1.0", "commands": [ "dotnet-gitversion" - ] + ], + "rollForward": false } } } \ No newline at end of file diff --git a/.editorconfig b/.editorconfig index 140ceb7d..27feb2d7 100644 --- a/.editorconfig +++ b/.editorconfig @@ -212,6 +212,7 @@ dotnet_diagnostic.ide0058.severity = none # Remove unnecessary expression value: dotnet_diagnostic.ide0010.severity = none # Add missing cases to switch statement: Too verbose dotnet_diagnostic.ide0200.severity = none # Remove unnecessary lambda expression: may be performance reasons not to dotnet_diagnostic.ide0058.severity = none # Remove unnecessary expression value: Subjective +dotnet_diagnostic.ide0305.severity = none # Use collection expression for fluent: Can obfuscate intent dotnet_diagnostic.ide0001.severity = suggestion # Name can be simplified: Non enforceable in build dotnet_diagnostic.ide0046.severity = suggestion # Use conditional expression for return: Subjective dotnet_diagnostic.ide0045.severity = suggestion # Use conditional expression for assignment: Subjective @@ -233,6 +234,17 @@ dotnet_diagnostic.ide0042.severity = suggestion # Deconstruct variable declarati dotnet_diagnostic.ide0028.severity = suggestion # Use collection initializers: Subjective dotnet_diagnostic.ide0072.severity = suggestion # Populate switch statement: Subjective dotnet_diagnostic.ide0074.severity = suggestion # Use compound assignment: Subjective +dotnet_diagnostic.ide0300.severity = suggestion # Use collection expression for array: Subjective, maybe aspirational +dotnet_diagnostic.ide0290.severity = suggestion # primary constructors: subjective, and readonly properties are not a thing +dotnet_diagnostic.ide0290.severity = suggestion # Use primary constructor: Subjective +dotnet_diagnostic.ide0037.severity = suggestion # Use inferred member names: Sometimes its nice to be explicit +dotnet_diagnostic.ide0301.severity = suggestion # Use collection expression for empty: Subjective, intent +dotnet_diagnostic.ide0021.severity = suggestion # Use expression body for constructors : Subjective +dotnet_diagnostic.ide0090.severity = suggestion # Simplify new expression : Subjective + +dotnet_diagnostic.ide0047.severity = suggestion # Parentheses preferences: IDEs don't properly pick it up +dotnet_diagnostic.ide0130.severity = suggestion # Namespace does not match folder structure : Aspirational +dotnet_diagnostic.ide1006.severity = suggestion # Naming rule violation : Aspirational # Maintainability rules dotnet_diagnostic.ca1501.severity = warning # Avoid excessive inheritance @@ -242,11 +254,7 @@ dotnet_diagnostic.ca1506.severity = warning # Avoid excessive class coupling dotnet_diagnostic.ca1507.severity = warning # Use nameof in place of string dotnet_diagnostic.ca1508.severity = warning # Avoid dead conditional code dotnet_diagnostic.ca1509.severity = warning # Invalid entry in code metrics configuration file -dotnet_diagnostic.ca1861.severity = none # Prefer 'static readonly' fields over constant array arguments if the called method is called repeatedly and is not mutating the passed array (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1861) - -dotnet_diagnostic.cs8618.severity = suggestion # nullable problem -dotnet_diagnostic.CS0809.severity = suggestion # obsolete errors -dotnet_diagnostic.CS0618.severity = suggestion # obsolete errors +dotnet_diagnostic.ca1861.severity = suggestion # Prefer 'static readonly' fields over constant array arguments if the called method is called repeatedly and is not mutating the passed array (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1861) # Performance rules @@ -307,3 +315,13 @@ dotnet_diagnostic.NUnit2039.severity = warning # Consider using Assert.That(actu indent_style = space indent_size = 2 tab_width = 2 + +# Verify +[*.{received,verified}.{json}] +charset = utf-8-bom +end_of_line = lf +indent_size = unset +indent_style = unset +insert_final_newline = false +tab_width = unset +trim_trailing_whitespace = false \ No newline at end of file diff --git a/.gitattributes b/.gitattributes index 0a96b77c..d87a165f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,4 +2,7 @@ * text=auto # need original files to be windows -*.txt text eol=crlf \ No newline at end of file +*.txt text eol=crlf + +# Verify +*.verified.json text eol=lf working-tree-encoding=UTF-8 \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/pr.yml similarity index 94% rename from .github/workflows/ci.yml rename to .github/workflows/pr.yml index 19fce647..f4afe4ea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/pr.yml @@ -26,7 +26,7 @@ jobs: run: ./build.sh - name: Upload coverage reports to Codecov with GitHub Action - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 with: files: tests/**/coverage.xml token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/main.yml b/.github/workflows/release.yml similarity index 94% rename from .github/workflows/main.yml rename to .github/workflows/release.yml index 79de0de5..0ad4a0e9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/release.yml @@ -3,6 +3,7 @@ name: .NET Build and Publish on: push: branches: ["main", "dev"] + tags: ["3.*"] jobs: build: @@ -27,7 +28,7 @@ jobs: run: ./build.sh pack - name: Upload coverage reports to Codecov with GitHub Action - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 with: files: tests/**/coverage.xml token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.gitignore b/.gitignore index 6e3d7b2a..190742b6 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,6 @@ tools .DS_Store *.snupkg -coverage.xml \ No newline at end of file +coverage.xml + +*.received.* \ No newline at end of file diff --git a/CodeMetricsConfig.txt b/CodeMetricsConfig.txt new file mode 100644 index 00000000..e69bf234 --- /dev/null +++ b/CodeMetricsConfig.txt @@ -0,0 +1,4 @@ +CA1502: 25 +CA1501: 5 +CA1506(Method): 50 +CA1506(Type): 95 diff --git a/Directory.Build.props b/Directory.Build.props index aef6ebeb..e37df49c 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,47 +1,72 @@ - - Speckle - Speckle - Copyright (c) AEC Systems Ltd - - - - latest + + + 12 enable enable - Recommended - true - true - true - true - true - False - False true true - false + + + + + + + Speckle + Copyright (c) AEC Systems Ltd + https://speckle.systems/ + logo.png + README.md + https://github.com/specklesystems/speckle-sharp-sdk + git + speckle + Apache-2.0 + + + + false + true + true + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb true + + + true + latest-AllEnabledByDefault + true + true + false + true + + + + + CA5399;CA1812; + + CS1591;CS1573; + + CA1303;CA1304;CA1305;CA1307;CA1308;CA1309;CA1310;CA1311; + + CA1848;CA1727; + + CA1815;CA1725; + + CA1710;CA1711;CA1720;CA1724; + + CA1502;CA1716;NETSDK1206; + $(NoWarn) + + - true - $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - speckle - https://speckle.systems/ - logo.png - https://github.com/specklesystems/speckle-sharp-sdk - git $(MSBuildThisFileDirectory) - true - README.md - Apache-2.0 - true - $(NoWarn);1591;1573 + + + + + + + diff --git a/Directory.Build.targets b/Directory.Build.targets new file mode 100644 index 00000000..10e64e3a --- /dev/null +++ b/Directory.Build.targets @@ -0,0 +1,18 @@ + + + + $(NoWarn); + + CS0618;CA1034;CA2201;CA1051;CA1040;CA1724; + IDE0044;IDE0130;CA1508; + + CA5394;CA2007;CA1852;CA1819;CA1711;CA1063;CA1816;CA2234;CS8618;CA1054;CA1810;CA2208;CA1019;CA1831; + + + + + + + + + diff --git a/Directory.Packages.props b/Directory.Packages.props index 9ae91639..b442fb0c 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,39 +1,37 @@ - + + + - - - - + + + + + + + + - - - - - - + - - - - - - - + - - - + + + + + + + - diff --git a/GitVersion.yml b/GitVersion.yml index 96532bca..cc61f149 100644 --- a/GitVersion.yml +++ b/GitVersion.yml @@ -1,11 +1,6 @@ +workflow: GitFlow/v1 next-version: 3.0.0 -mode: ContinuousDeployment -assembly-informational-format: "{Major}.{Minor}.{Patch}-{PreReleaseTag}" branches: main: - regex: ^main$ - tag: rc - develop: - tag: dev - pull-request: - tag: pr + prevent-increment: + when-current-commit-tagged: true diff --git a/README.md b/README.md index 7de6c40f..5a437272 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,38 @@ -# Core +![Speckle Box](/logo.png) +Speckle | Sharp | SDK +================================================================================================================================= -[![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/) +[![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%2Fspeckle.community&style=flat-square&logo=discourse&logoColor=white)](https://speckle.community) [![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) + > Speckle is the first AEC data hub that connects with your favorite AEC tools. Speckle exists to overcome the challenges of working in a fragmented industry where communication, creative workflows, and the exchange of data are often hindered by siloed software and processes. It is here to make the industry better. -## **Disclaimer** +### .NET SDK, Tests, and Objects -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! +[![Codecov](https://codecov.io/gh/specklesystems/speckle-sharp-sdk/graph/badge.svg?token=TTM5OGr38m)](https://codecov.io/gh/specklesystems/speckle-sharp-sdk) -## Introduction +> [!WARNING] +> This is an early beta 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! -### Core +# Repo structure -Core is the .NET SDK for Speckle 3.0. It uses .NET Standard 2.0 and has been tested on Windows and MacOS. +This repo is the home of our next-generation Speckle .NET SDK. It uses .NET Standard 2.0 and has been tested on Windows and MacOS. + +- **SDK** + - [`Speckle.Sdk`](https://github.com/specklesystems/speckle-sharp-sdk/tree/dev/src/Speckle.Sdk): Transports, serialization, API wrappers, and logging. + - [`Speckle.Sdk.Dependencies`](https://github.com/specklesystems/speckle-sharp-sdk/tree/dev/src/Speckle.Sdk.Dependencies): Dependencies and code that shouldn't cause conflicts in Host Apps. This uses [IL Repack](https://github.com/gluck/il-repack) to merge together and interalized only to be used by Speckle. +- **Speckle Objects** + - [`Speckle.Objects`](https://github.com/specklesystems/speckle-sharp-sdk/tree/dev/src/Speckle.Objects): The Speckle Objects classes used for conversions. +- **Tests** + - [`Tests`](https://github.com/specklesystems/speckle-sharp-sdk/tree/dev/tests): Unit, serialization, integration, and performance tests. + +### Other repos + +Make sure to also check and ⭐️ these other Speckle next generation repositories: + +- [`speckle-sharp-connectors`](https://github.com/specklesystems/speckle-sharp-connectors): our csharp repo of next gen connectors +- [`speckle-sketchup`](https://github.com/specklesystems/speckle-sketchup): Sketchup connector +- [`speckle-powerbi`](https://github.com/specklesystems/speckle-powerbi): PowerBi connector +- and more [connectors & tooling](https://github.com/specklesystems/)! ## Documentation @@ -20,7 +40,7 @@ Comprehensive developer and user documentation can be found in our: ### 📚 [Speckle Docs website](https://speckle.guide/dev/) -## Developing & Debugging +# Developing and Debugging ### Building @@ -41,13 +61,16 @@ There are two test projects, one for unit tests and one for integration tests. T Before embarking on submitting a patch, please make sure you read: -- [Contribution Guidelines](CONTRIBUTING.md), +- [Contribution Guidelines](CONTRIBUTING.md) - [Code of Conduct](CODE_OF_CONDUCT.md) -## Community +# Security and Licensing + +### Security -The Speckle Community hangs out on [the forum](https://discourse.speckle.works), do join and introduce yourself & feel free to ask us questions! +For any security vulnerabilities or concerns, please contact us directly at security[at]speckle.systems. -## License +### License + +Unless otherwise described, the code in this repository is licensed under the Apache-2.0 License. Please note that some modules, extensions or code herein might be otherwise licensed. This is indicated either in the root of the containing folder under a different license file, or in the respective file's header. If you have any questions, don't hesitate to get in touch with us via [email](mailto:hello@speckle.systems). -Unless otherwise described, the code in this repository is licensed under the Apache-2.0 license. diff --git a/Speckle.Sdk.sln b/Speckle.Sdk.sln index d1ed4fb7..cc6a7e94 100644 --- a/Speckle.Sdk.sln +++ b/Speckle.Sdk.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Sdk", "src\Speckle.Sdk\Speckle.Sdk.csproj", "{A413E196-3696-4F48-B635-04B5F76BF9C9}" EndProject @@ -22,6 +22,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{DA2AED README.md = README.md GitVersion.yml = GitVersion.yml docker-compose.yml = docker-compose.yml + CodeMetricsConfig.txt = CodeMetricsConfig.txt + Directory.Build.Targets = Directory.Build.Targets + .config\dotnet-tools.json = .config\dotnet-tools.json EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{58D37DA9-F948-48CA-9A73-F5BBBD533DBF}" @@ -34,13 +37,19 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Sdk.Tests.Integrati 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 + .github\workflows\pr.yml = .github\workflows\pr.yml + .github\workflows\release.yml = .github\workflows\release.yml EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Sdk.Logging", "src\Speckle.Sdk.Logging\Speckle.Sdk.Logging.csproj", "{156313B4-B588-4363-A0ED-5AB3A55AA4E8}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Sdk.Tests.Performance", "tests\Speckle.Sdk.Tests.Performance\Speckle.Sdk.Tests.Performance.csproj", "{870E3396-E6F7-43AE-B120-E651FA4F46BD}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.LongSendProblem", "tests\Speckle.LongSendProblem\Speckle.LongSendProblem.csproj", "{6B8D2DD8-AD20-4021-975C-6FDE71DC425B}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Sdk.Serialization.Testing", "tests\Speckle.Sdk.Serialization.Testing\Speckle.Sdk.Serialization.Testing.csproj", "{FF922B6D-D416-4348-8CB8-0C8B28691070}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Sdk.Dependencies", "src\Speckle.Sdk.Dependencies\Speckle.Sdk.Dependencies.csproj", "{27584AB4-8ACD-4850-8CC2-7E5BC739FB78}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Sdk.Testing", "tests\Speckle.Sdk.Testing\Speckle.Sdk.Testing.csproj", "{7B617C0D-2354-415C-993C-5071D4113E27}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "performance", "performance", "{FFB07238-87E8-463A-AA39-3B38AAAA94C1}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -80,14 +89,22 @@ Global {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 - {156313B4-B588-4363-A0ED-5AB3A55AA4E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {156313B4-B588-4363-A0ED-5AB3A55AA4E8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {156313B4-B588-4363-A0ED-5AB3A55AA4E8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {156313B4-B588-4363-A0ED-5AB3A55AA4E8}.Release|Any CPU.Build.0 = Release|Any CPU - {6B8D2DD8-AD20-4021-975C-6FDE71DC425B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6B8D2DD8-AD20-4021-975C-6FDE71DC425B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6B8D2DD8-AD20-4021-975C-6FDE71DC425B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6B8D2DD8-AD20-4021-975C-6FDE71DC425B}.Release|Any CPU.Build.0 = Release|Any CPU + {870E3396-E6F7-43AE-B120-E651FA4F46BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {870E3396-E6F7-43AE-B120-E651FA4F46BD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {870E3396-E6F7-43AE-B120-E651FA4F46BD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {870E3396-E6F7-43AE-B120-E651FA4F46BD}.Release|Any CPU.Build.0 = Release|Any CPU + {FF922B6D-D416-4348-8CB8-0C8B28691070}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FF922B6D-D416-4348-8CB8-0C8B28691070}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FF922B6D-D416-4348-8CB8-0C8B28691070}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FF922B6D-D416-4348-8CB8-0C8B28691070}.Release|Any CPU.Build.0 = Release|Any CPU + {27584AB4-8ACD-4850-8CC2-7E5BC739FB78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {27584AB4-8ACD-4850-8CC2-7E5BC739FB78}.Debug|Any CPU.Build.0 = Debug|Any CPU + {27584AB4-8ACD-4850-8CC2-7E5BC739FB78}.Release|Any CPU.ActiveCfg = Release|Any CPU + {27584AB4-8ACD-4850-8CC2-7E5BC739FB78}.Release|Any CPU.Build.0 = Release|Any CPU + {7B617C0D-2354-415C-993C-5071D4113E27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7B617C0D-2354-415C-993C-5071D4113E27}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7B617C0D-2354-415C-993C-5071D4113E27}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7B617C0D-2354-415C-993C-5071D4113E27}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {A413E196-3696-4F48-B635-04B5F76BF9C9} = {5CB96C27-FC5B-4A41-86B6-951AF99B8116} @@ -98,7 +115,9 @@ Global {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} - {156313B4-B588-4363-A0ED-5AB3A55AA4E8} = {5CB96C27-FC5B-4A41-86B6-951AF99B8116} - {6B8D2DD8-AD20-4021-975C-6FDE71DC425B} = {35047EE7-AD1D-4741-80A7-8F0E874718E9} + {27584AB4-8ACD-4850-8CC2-7E5BC739FB78} = {5CB96C27-FC5B-4A41-86B6-951AF99B8116} + {7B617C0D-2354-415C-993C-5071D4113E27} = {35047EE7-AD1D-4741-80A7-8F0E874718E9} + {FF922B6D-D416-4348-8CB8-0C8B28691070} = {FFB07238-87E8-463A-AA39-3B38AAAA94C1} + {870E3396-E6F7-43AE-B120-E651FA4F46BD} = {FFB07238-87E8-463A-AA39-3B38AAAA94C1} EndGlobalSection EndGlobal diff --git a/Speckle.Sdk.sln.DotSettings b/Speckle.Sdk.sln.DotSettings new file mode 100644 index 00000000..691a8f91 --- /dev/null +++ b/Speckle.Sdk.sln.DotSettings @@ -0,0 +1,3 @@ + + QL + XYZ \ No newline at end of file diff --git a/build/Program.cs b/build/Program.cs index 0cd07382..0b9f0cda 100644 --- a/build/Program.cs +++ b/build/Program.cs @@ -1,3 +1,4 @@ +using System.Text.Json; using GlobExpressions; using static Bullseye.Targets; using static SimpleExec.Command; @@ -13,6 +14,18 @@ const string INTEGRATION = "integration"; const string PACK = "pack"; const string PACK_LOCAL = "pack-local"; const string CLEAN_LOCKS = "clean-locks"; +const string PERF = "perf"; +const string DEEP_CLEAN = "deep-clean"; + +static async Task<(string, string)> GetVersions() +{ + var (output, _) = await ReadAsync("dotnet", "dotnet-gitversion /output json").ConfigureAwait(false); + output = output.Trim(); + var jDoc = JsonDocument.Parse(output); + var version = jDoc.RootElement.GetProperty("FullSemVer").GetString() ?? "3.0.0-localBuild"; + var fileVersion = jDoc.RootElement.GetProperty("AssemblySemFileVer").GetString() ?? "3.0.0.0"; + return (version, fileVersion); +} Target( CLEAN_LOCKS, @@ -65,7 +78,13 @@ Target( DependsOn(RESTORE), async () => { - await RunAsync("dotnet", $"build Speckle.Sdk.sln -c Release --no-restore"); + var (version, fileVersion) = await GetVersions().ConfigureAwait(false); + Console.WriteLine($"Version: {version} & {fileVersion}"); + await RunAsync( + "dotnet", + $"build Speckle.Sdk.sln -c Release --no-restore -warnaserror -p:Version={version} -p:FileVersion={fileVersion}" + ) + .ConfigureAwait(false); } ); @@ -76,9 +95,10 @@ Target( async file => { await RunAsync( - "dotnet", - $"test {file} -c Release --no-build --no-restore --verbosity=normal /p:AltCover=true /p:AltCoverAttributeFilter=ExcludeFromCodeCoverage /p:AltCoverVerbosity=Warning" - ); + "dotnet", + $"test {file} -c Release --no-build --no-restore --verbosity=normal /p:AltCover=true /p:AltCoverAttributeFilter=ExcludeFromCodeCoverage /p:AltCoverVerbosity=Warning" + ) + .ConfigureAwait(false); } ); @@ -87,22 +107,72 @@ Target( DependsOn(BUILD), async () => { - await RunAsync("docker", "compose -f docker-compose.yml up --wait"); + await RunAsync("docker", "compose -f docker-compose.yml up --wait").ConfigureAwait(false); foreach (var test in Glob.Files(".", "**/*.Tests.Integration.csproj")) { await RunAsync( - "dotnet", - $"test {test} -c Release --no-build --no-restore --verbosity=normal /p:AltCover=true /p:AltCoverAttributeFilter=ExcludeFromCodeCoverage" - ); + "dotnet", + $"test {test} -c Release --no-build --no-restore --verbosity=normal /p:AltCover=true /p:AltCoverAttributeFilter=ExcludeFromCodeCoverage" + ) + .ConfigureAwait(false); } - await RunAsync("docker", "compose down"); + await RunAsync("docker", "compose down").ConfigureAwait(false); } ); -static Task RunRestore() => RunAsync("dotnet", "pack Speckle.Sdk.sln -c Release -o output --no-build"); +Target( + PERF, + Glob.Files(".", "**/*.Tests.Performance.csproj"), + async file => + { + void CheckBuildDirectory(string dir, string build) + { + var binDir = Path.Combine(dir, "bin", build); + Console.WriteLine($"Checking: {binDir}"); + if (Directory.Exists(binDir)) + { + Directory.Delete(binDir, true); + Console.WriteLine($"Deleted: {binDir}"); + } + } + var dir = Path.GetDirectoryName(file) ?? throw new InvalidOperationException(); + CheckBuildDirectory(dir, "Release"); + CheckBuildDirectory(dir, "Debug"); + await RunAsync("dotnet", $"run --project {file} -c Release").ConfigureAwait(false); + } +); -Target(PACK, DependsOn(TEST), RunRestore); -Target(PACK_LOCAL, DependsOn(BUILD), RunRestore); +Target( + DEEP_CLEAN, + () => + { + foreach (var f in Glob.Directories(".", "**/bin")) + { + if (f.StartsWith("build")) + { + continue; + } + Console.WriteLine("Found and will delete: " + f); + Directory.Delete(f, true); + } + foreach (var f in Glob.Directories(".", "**/obj")) + { + if (f.StartsWith("Build")) + { + continue; + } + Console.WriteLine("Found and will delete: " + f); + Directory.Delete(f, true); + } + Console.WriteLine("Running restore now."); + Run("dotnet", "restore .\\Speckle.Sdk.sln --no-cache"); + } +); + +static Task RunPack() => RunAsync("dotnet", "pack Speckle.Sdk.sln -c Release -o output --no-build"); + +Target(PACK, DependsOn(BUILD), RunPack); +Target(PACK_LOCAL, DependsOn(BUILD), RunPack); Target("default", DependsOn(FORMAT, TEST, INTEGRATION), () => Console.WriteLine("Done!")); diff --git a/build/packages.lock.json b/build/packages.lock.json index 0e580e7a..29700b45 100644 --- a/build/packages.lock.json +++ b/build/packages.lock.json @@ -8,12 +8,6 @@ "resolved": "5.0.0", "contentHash": "bqyt+m17ym+5aN45C5oZRAjuLDt8jKiCm/ys1XfymIXSkrTFwvI/QsbY3ucPSHDz7SF7uON7B57kXFv5H2k1ew==" }, - "GitVersion.MsBuild": { - "type": "Direct", - "requested": "[5.12.0, )", - "resolved": "5.12.0", - "contentHash": "dJuigXycpJNOiLT9or7mkHSkGFHgGW3/p6cNNYEKZBa7Hhp1FdX/cvqYWWYhRLpfoZOedeA7aRbYiOB3vW/dvA==" - }, "Glob": { "type": "Direct", "requested": "[1.1.9, )", @@ -32,9 +26,9 @@ }, "PolySharp": { "type": "Direct", - "requested": "[1.14.1, )", - "resolved": "1.14.1", - "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g==" }, "SimpleExec": { "type": "Direct", diff --git a/docker-compose.yml b/docker-compose.yml index 52bf0743..c948270e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,7 +6,7 @@ services: # Speckle Server dependencies ####### postgres: - image: "postgres:14.5-alpine" + image: "postgres:16.4-alpine3.20@sha256:d898b0b78a2627cb4ee63464a14efc9d296884f1b28c841b0ab7d7c42f1fffdf" restart: always environment: POSTGRES_DB: speckle @@ -49,10 +49,6 @@ services: retries: 30 start_period: 10s - #### - # Speckle Server - ####### - speckle-server: image: speckle/speckle-server:latest restart: always @@ -79,6 +75,7 @@ services: # TODO: Change this to the URL of the speckle server, as accessed from the network CANONICAL_URL: "http://127.0.0.1:8080" SPECKLE_AUTOMATE_URL: "http://127.0.0.1:3030" + FRONTEND_ORIGIN: "http://127.0.0.1:8081" # TODO: Change thvolumes: REDIS_URL: "redis://redis" @@ -111,4 +108,4 @@ networks: volumes: postgres-data: redis-data: - minio-data: + minio-data: \ No newline at end of file diff --git a/global.json b/global.json index c19a2e05..4e550c17 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "8.0.100", + "version": "8.0.400", "rollForward": "latestMinor" } } diff --git a/logo.png b/logo.png index 2c86ad54..d56f2522 100644 Binary files a/logo.png and b/logo.png differ diff --git a/src/Directory.Build.props b/src/Directory.Build.props deleted file mode 100644 index a9c5b063..00000000 --- a/src/Directory.Build.props +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - true - - diff --git a/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelBeam.cs b/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelBeam.cs deleted file mode 100644 index c6b9a092..00000000 --- a/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelBeam.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Speckle.Objects.Structural.Materials; -using Speckle.Objects.Structural.Properties.Profiles; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.AdvanceSteel; - -[SpeckleType("Objects.BuiltElements.AdvanceSteel.AsteelBeam")] -public class AsteelBeam : Beam, IHasVolume, IHasArea, IAsteelObject -{ - [DetachProperty] - public SectionProfile profile { get; set; } - - [DetachProperty] - public StructuralMaterial material { get; set; } - - [DetachProperty] - public AsteelSectionProfile asteelProfile { get; set; } - - public double volume { get; set; } - public double area { get; set; } - public Base userAttributes { get; set; } - - public Base asteelProperties { get; set; } - - public AsteelBeam() { } - - [SchemaInfo("AsteelBeam", "Creates a Advance Steel beam by curve.", "Advance Steel", "Structure")] - public AsteelBeam([SchemaMainParam] ICurve baseLine, SectionProfile profile, StructuralMaterial material) - { - this.baseLine = baseLine; - this.profile = profile; - this.material = material; - } -} diff --git a/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelBolt.cs b/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelBolt.cs deleted file mode 100644 index f5dbe850..00000000 --- a/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelBolt.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.AdvanceSteel; - -public abstract class AsteelBolt : Base, IAsteelObject -{ - [DetachProperty] - public List displayValue { get; set; } - - public Base userAttributes { get; set; } - - public Base asteelProperties { get; set; } -} - -[SpeckleType("Objects.BuiltElements.AdvanceSteel.AsteelCircularBolt")] -public class AsteelCircularBolt : AsteelBolt -{ - //[SchemaInfo("AsteelCircularBolt", "Creates a Advance Steel circular bolt.", "Advance Steel", "Structure")] - public AsteelCircularBolt() { } -} - -[SpeckleType("Objects.BuiltElements.AdvanceSteel.AsteelRectangularBolt")] -public class AsteelRectangularBolt : AsteelBolt -{ - //[SchemaInfo("AsteelRectangularBolt", "Creates a Advance Steel rectangular bolt.", "Advance Steel", "Structure")] - public AsteelRectangularBolt() { } -} diff --git a/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelGrating.cs b/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelGrating.cs deleted file mode 100644 index 32071373..00000000 --- a/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelGrating.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.AdvanceSteel; - -[SpeckleType("Objects.BuiltElements.AdvanceSteel.AsteelGrating")] -public class AsteelGrating : Base, IAsteelObject -{ - [DetachProperty] - public List displayValue { get; set; } - - public Base userAttributes { get; set; } - - public Base asteelProperties { get; set; } - - //[SchemaInfo("AsteelGrating", "Creates a Advance Steel grating.", "Advance Steel", "Structure")] - public AsteelGrating() { } -} diff --git a/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelPlate.cs b/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelPlate.cs deleted file mode 100644 index c6c66293..00000000 --- a/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelPlate.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Structural.Materials; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.AdvanceSteel; - -[SpeckleType("Objects.BuiltElements.AdvanceSteel.AsteelPlate")] -public class AsteelPlate : Area, IDisplayValue>, IHasArea, IHasVolume, IAsteelObject -{ - [DetachProperty] - public StructuralMaterial? material { get; set; } - - public Base userAttributes { get; set; } - - public Base asteelProperties { get; set; } - - [SchemaInfo("AsteelPlate", "Creates a Advance Steel plate.", "Advance Steel", "Structure")] - public AsteelPlate(Polyline outline, string units, StructuralMaterial? material = null) - { - this.outline = outline; - this.material = material; - this.units = units; - } - - public AsteelPlate() { } -} diff --git a/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelPolyBeam.cs b/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelPolyBeam.cs deleted file mode 100644 index 16b62f99..00000000 --- a/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelPolyBeam.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.AdvanceSteel; - -[SpeckleType("Objects.BuiltElements.AdvanceSteel.AsteelPolyBeam")] -public class AsteelPolyBeam : AsteelBeam -{ - //[SchemaInfo("AsteelPolyBeam", "Creates a Advance Steel polybeam.", "Advance Steel", "Structure")] - public AsteelPolyBeam() { } -} diff --git a/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelSectionProfile.cs b/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelSectionProfile.cs deleted file mode 100644 index 5ad3ae71..00000000 --- a/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelSectionProfile.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.AdvanceSteel; - -[SpeckleType("Objects.BuiltElements.AdvanceSteel.AsteelSectionProfile")] -public class AsteelSectionProfile : Base -{ - public string ProfSectionType { get; set; } - - public string ProfSectionName { get; set; } - - public AsteelSectionProfileDB SectionProfileDB { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelSectionProfileDB.cs b/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelSectionProfileDB.cs deleted file mode 100644 index 6603ee60..00000000 --- a/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelSectionProfileDB.cs +++ /dev/null @@ -1,6 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.AdvanceSteel; - -[SpeckleType("Objects.BuiltElements.AdvanceSteel.AsteelSectionProfileDB")] -public class AsteelSectionProfileDB : Base { } diff --git a/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelSlab.cs b/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelSlab.cs deleted file mode 100644 index 2022db80..00000000 --- a/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelSlab.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Structural.Materials; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.AdvanceSteel; - -[SpeckleType("Objects.BuiltElements.AdvanceSteel.AsteelSlab")] -public class AsteelSlab : Area, IDisplayValue>, IHasArea, IHasVolume, IAsteelObject -{ - [DetachProperty] - public StructuralMaterial? material { get; set; } - - public Base userAttributes { get; set; } - - public Base asteelProperties { get; set; } - - [SchemaInfo("AsteelSlab", "Creates a Advance Steel slab.", "Advance Steel", "Structure")] - public AsteelSlab(Polyline outline, string units, StructuralMaterial? material = null) - { - this.outline = outline; - this.material = material; - this.units = units; - } - - public AsteelSlab() { } -} diff --git a/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelSpecialPart.cs b/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelSpecialPart.cs deleted file mode 100644 index 0fe582c6..00000000 --- a/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelSpecialPart.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.AdvanceSteel; - -[SpeckleType("Objects.BuiltElements.AdvanceSteel.AsteelSpecialPart")] -public class AsteelSpecialPart : Base, IAsteelObject -{ - [DetachProperty] - public List displayValue { get; set; } - - public Base userAttributes { get; set; } - - public Base asteelProperties { get; set; } - - //[SchemaInfo("AsteelSpecialPart", "Creates a Advance Steel special part.", "Advance Steel", "Structure")] - public AsteelSpecialPart() { } -} diff --git a/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelStraightBeam.cs b/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelStraightBeam.cs deleted file mode 100644 index bf337de2..00000000 --- a/src/Speckle.Objects/BuiltElements/AdvanceSteel/AsteelStraightBeam.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.AdvanceSteel; - -[SpeckleType("Objects.BuiltElements.AdvanceSteel.AsteelStraightBeam")] -public class AsteelStraightBeam : AsteelBeam -{ - //[SchemaInfo("AsteelStraightBeam", "Creates a Advance Steel straightBeam.", "Advance Steel", "Structure")] - public AsteelStraightBeam() { } -} diff --git a/src/Speckle.Objects/BuiltElements/AdvanceSteel/Enums.cs b/src/Speckle.Objects/BuiltElements/AdvanceSteel/Enums.cs deleted file mode 100644 index 297fb2f6..00000000 --- a/src/Speckle.Objects/BuiltElements/AdvanceSteel/Enums.cs +++ /dev/null @@ -1 +0,0 @@ -namespace Speckle.Objects.BuiltElements.AdvanceSteel; diff --git a/src/Speckle.Objects/BuiltElements/AdvanceSteel/IAsteelObject.cs b/src/Speckle.Objects/BuiltElements/AdvanceSteel/IAsteelObject.cs deleted file mode 100644 index 8cb274d5..00000000 --- a/src/Speckle.Objects/BuiltElements/AdvanceSteel/IAsteelObject.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.AdvanceSteel; - -public interface IAsteelObject -{ - Base userAttributes { get; set; } - - Base asteelProperties { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Alignment.cs b/src/Speckle.Objects/BuiltElements/Alignment.cs deleted file mode 100644 index 7bf95c07..00000000 --- a/src/Speckle.Objects/BuiltElements/Alignment.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Speckle.Newtonsoft.Json; -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements; - -[SpeckleType("Objects.BuiltElements.Alignment")] -public class Alignment : Base, IDisplayValue -{ - [JsonIgnore, Obsolete("Use curves property")] - public ICurve baseCurve { get; set; } - - public List curves { get; set; } - - public string name { get; set; } - - public double startStation { get; set; } - - public double endStation { get; set; } - - public List profiles { get; set; } - - /// - /// Station equation list contains doubles indicating raw station back, station back, and station ahead for each station equation - /// - public List stationEquations { get; set; } - - /// - /// Station equation direction for the corresponding station equation should be true for increasing or false for decreasing - /// - public List stationEquationDirections { get; set; } - - public string units { get; set; } - - [DetachProperty] - public Polyline displayValue { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Archicad/ArchicadBeam.cs b/src/Speckle.Objects/BuiltElements/Archicad/ArchicadBeam.cs deleted file mode 100644 index f65ecf30..00000000 --- a/src/Speckle.Objects/BuiltElements/Archicad/ArchicadBeam.cs +++ /dev/null @@ -1,134 +0,0 @@ -using Speckle.Newtonsoft.Json; -using Speckle.Objects.Geometry; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Archicad; - -/* -For further informations about given the variables, visit: -https://archicadapi.graphisoft.com/documentation/api_beamtype -*/ -[SpeckleType("Objects.BuiltElements.Archicad.ArchicadBeam")] -public class ArchicadBeam : Beam -{ - [SchemaInfo("ArchicadBeam", "Creates an Archicad beam by curve.", "Archicad", "Structure")] - public ArchicadBeam() { } - - // Element base - public string? elementType { get; set; } /*APINullabe*/ - - public List? classifications { get; set; } /*APINullabe*/ - public Base? elementProperties { get; set; } - public Base? componentProperties { get; set; } - - public override Level? level - { - get => archicadLevel; - internal set - { - if (value is ArchicadLevel or null) - { - archicadLevel = value as ArchicadLevel; - } - else - { - throw new ArgumentException($"Expected object of type {nameof(ArchicadLevel)}"); - } - } - } - - [JsonIgnore] - public ArchicadLevel? archicadLevel { get; set; } /*APINullabe*/ - - public string? layer { get; set; } /*APINullabe*/ - - // Positioning - public Point begC { get; set; } - public Point endC { get; set; } - public bool? isSlanted { get; set; } /*APINullabe*/ - public double? slantAngle { get; set; } /*APINullabe*/ - public string? beamShape { get; set; } /*APINullabe*/ - public int? sequence { get; set; } /*APINullabe*/ - public double? curveAngle { get; set; } /*APINullabe*/ - public double? verticalCurveHeight { get; set; } /*APINullabe*/ - public bool? isFlipped { get; set; } /*APINullabe*/ - - // End Cuts - public uint? nCuts { get; set; } /*APINullabe*/ - public Dictionary? Cuts { get; set; } - - // Reference Axis - public short? anchorPoint { get; set; } /*APINullabe*/ - public double? offset { get; set; } - public double? profileAngle { get; set; } - - // Segment - public uint? nSegments { get; set; } /*APINullabe*/ - public uint? nProfiles { get; set; } /*APINullabe*/ - public Dictionary? segments { get; set; } /*APINullabe*/ - - // Scheme - public uint? nSchemes { get; set; } - public Dictionary? Schemes { get; set; } - - // Hole - public Dictionary? Holes { get; set; } - - // Floor Plan and Section - Floor Plan Display - public string? showOnStories { get; set; } /*APINullabe*/ - public string? displayOptionName { get; set; } /*APINullabe*/ - public string? uncutProjectionMode { get; set; } /*APINullabe*/ - public string? overheadProjectionMode { get; set; } /*APINullabe*/ - public string? showProjectionName { get; set; } /*APINullabe*/ - - // Floor Plan and Section - Cut Surfaces - public short? cutContourLinePen { get; set; } - public string? cutContourLineType { get; set; } - public short? overrideCutFillPen { get; set; } - public short? overrideCutFillBackgroundPen { get; set; } - - // Floor Plan and Section - Outlines - public string? showOutline { get; set; } /*APINullabe*/ - public short? uncutLinePen { get; set; } /*APINullabe*/ - public string? uncutLinetype { get; set; } /*APINullabe*/ - public short? overheadLinePen { get; set; } /*APINullabe*/ - public string? overheadLinetype { get; set; } /*APINullabe*/ - public short? hiddenLinePen { get; set; } /*APINullabe*/ - public string? hiddenLinetype { get; set; } /*APINullabe*/ - - // Floor Plan and Section - Symbol - public string? showReferenceAxis { get; set; } /*APINullabe*/ - public short? referencePen { get; set; } /*APINullabe*/ - public string? referenceLinetype { get; set; } /*APINullabe*/ - - // Floor Plan and Section - Cover Fills - public bool? useCoverFill { get; set; } /*APINullabe*/ - public bool? useCoverFillFromSurface { get; set; } - public short? coverFillForegroundPen { get; set; } - public short? coverFillBackgroundPen { get; set; } - public string? coverFillType { get; set; } - public string? coverFillTransformationType { get; set; } - public double? coverFillTransformationOrigoX { get; set; } - public double? coverFillTransformationOrigoY { get; set; } - public double? coverFillTransformationXAxisX { get; set; } - public double? coverFillTransformationXAxisY { get; set; } - public double? coverFillTransformationYAxisX { get; set; } - public double? coverFillTransformationYAxisY { get; set; } - - [SpeckleType("Objects.BuiltElements.Archicad.ArchicadBeam+BeamSegment")] - public class BeamSegment : Base - { - // Segment override materials - public string? leftMaterial { get; set; } - public string? topMaterial { get; set; } - public string? rightMaterial { get; set; } - public string? bottomMaterial { get; set; } - - public string? endsMaterial { get; set; } - - // Segment - The overridden materials are chained - public bool? materialChained { get; set; } - public AssemblySegment assemblySegmentData { get; set; } - } -} diff --git a/src/Speckle.Objects/BuiltElements/Archicad/ArchicadColumn.cs b/src/Speckle.Objects/BuiltElements/Archicad/ArchicadColumn.cs deleted file mode 100644 index 2d5e91e9..00000000 --- a/src/Speckle.Objects/BuiltElements/Archicad/ArchicadColumn.cs +++ /dev/null @@ -1,144 +0,0 @@ -using Speckle.Newtonsoft.Json; -using Speckle.Objects.Geometry; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Archicad; - -/* -For further informations about given the variables, visit: -https://archicadapi.graphisoft.com/documentation/api_columntype -*/ -[SpeckleType("Objects.BuiltElements.Archicad.ArchicadColumn")] -public class ArchicadColumn : Column -{ - [SchemaInfo("ArchicadColumn", "Creates an Archicad Column by curve.", "Archicad", "Structure")] - public ArchicadColumn() { } - - // Element base - public string? elementType { get; set; } /*APINullabe*/ - - public List? classifications { get; set; } /*APINullabe*/ - public Base? elementProperties { get; set; } - public Base? componentProperties { get; set; } - - public override Level? level - { - get => archicadLevel; - internal set - { - if (value is ArchicadLevel or null) - { - archicadLevel = value as ArchicadLevel; - } - else - { - throw new ArgumentException($"Expected object of type {nameof(ArchicadLevel)}"); - } - } - } - - [JsonIgnore] - public ArchicadLevel? archicadLevel { get; set; } /*APINullabe*/ - - public string? layer { get; set; } /*APINullabe*/ - - // Wall geometry - public Point origoPos { get; set; } - public double height { get; set; } - - // Positioning - story relation - public double? bottomOffset { get; set; } /*APINullabe*/ - public double? topOffset { get; set; } /*APINullabe*/ - public short? relativeTopStory { get; set; } /*APINullabe*/ - - // Positioning - slanted column - public bool? isSlanted { get; set; } /*APINullabe*/ - public double? slantAngle { get; set; } /*APINullabe*/ - public double? slantDirectionAngle { get; set; } /*APINullabe*/ - public bool? isFlipped { get; set; } /*APINullabe*/ - - // Positioning - wrapping - public bool? wrapping { get; set; } /*APINullabe*/ - - // Positioning - Defines the relation of column to zones (Zone Boundary, Reduce Zone Area Only, No Effect on Zones) - public string? columnRelationToZoneName { get; set; } /*APINullabe*/ - - // End Cuts - public uint? nCuts { get; set; } /*APINullabe*/ - public Dictionary? Cuts { get; set; } /*APINullabe*/ - - // Reference Axis - public short? coreAnchor { get; set; } - public double? axisRotationAngle { get; set; } - - // Segment - public uint? nSegments { get; set; } /*APINullabe*/ - public uint? nProfiles { get; set; } /*APINullabe*/ - public Dictionary? segments { get; set; } /*APINullabe*/ - - // Scheme - public uint? nSchemes { get; set; } - public Dictionary? Schemes { get; set; } - - // Floor Plan and Section - Floor Plan Display - public string? showOnStories { get; set; } /*APINullabe*/ - public string? displayOptionName { get; set; } /*APINullabe*/ - public string? showProjectionName { get; set; } /*APINullabe*/ - - // Floor Plan and Section - Cut Surfaces - public short? corePen { get; set; } - public string? contLtype { get; set; } - public short? venLinePen { get; set; } - public string? venLineType { get; set; } - public short? overrideCutFillPen { get; set; } - public short? overrideCutFillBackgroundPen { get; set; } - - // Floor Plan and Section - Outlines - public short? uncutLinePen { get; set; } /*APINullabe*/ - public string? uncutLinetype { get; set; } /*APINullabe*/ - public short? overheadLinePen { get; set; } /*APINullabe*/ - public string? overheadLinetype { get; set; } /*APINullabe*/ - public short? hiddenLinePen { get; set; } /*APINullabe*/ - public string? hiddenLinetype { get; set; } /*APINullabe*/ - - // Floor Plan and Section - Floor Plan Symbol - public string? coreSymbolTypeName { get; set; } /*APINullabe*/ - public double? coreSymbolPar1 { get; set; } /*APINullabe*/ - public double? coreSymbolPar2 { get; set; } /*APINullabe*/ - public short? coreSymbolPen { get; set; } /*APINullabe*/ - - // Floor Plan and Section - Cover Fills - public bool? useCoverFill { get; set; } /*APINullabe*/ - public bool? useCoverFillFromSurface { get; set; } - public short? coverFillForegroundPen { get; set; } - public short? coverFillBackgroundPen { get; set; } - public string? coverFillType { get; set; } - public string? coverFillTransformationType { get; set; } - public double? coverFillTransformationOrigoX { get; set; } - public double? coverFillTransformationOrigoY { get; set; } - public double? coverFillTransformationXAxisX { get; set; } - public double? coverFillTransformationXAxisY { get; set; } - public double? coverFillTransformationYAxisX { get; set; } - public double? coverFillTransformationYAxisY { get; set; } - - [SpeckleType("Objects.BuiltElements.Archicad.ArchicadColumn+ColumnSegment")] - public class ColumnSegment : Base - { - // Segment - Veneer attributes - public string? veneerType { get; set; } - public string? veneerBuildingMaterial { get; set; } - - public double? veneerThick { get; set; } - - // Segment - The extrusion overridden material name - public string? extrusionSurfaceMaterial { get; set; } - - // Segment - The ends overridden material name - public string? endsSurfaceMaterial { get; set; } - - // Segment - The overridden materials are chained - public bool? materialChained { get; set; } - public AssemblySegment assemblySegmentData { get; set; } - } -} diff --git a/src/Speckle.Objects/BuiltElements/Archicad/ArchicadFloor.cs b/src/Speckle.Objects/BuiltElements/Archicad/ArchicadFloor.cs deleted file mode 100644 index e733b5b5..00000000 --- a/src/Speckle.Objects/BuiltElements/Archicad/ArchicadFloor.cs +++ /dev/null @@ -1,99 +0,0 @@ -using Speckle.Newtonsoft.Json; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Archicad; - -/* -For further informations about given the variables, visit: -https://archicadapi.graphisoft.com/documentation/api_slabtype -*/ -[SpeckleType("Objects.BuiltElements.Archicad.ArchicadFloor")] -public sealed class ArchicadFloor : Floor -{ - // Element base - public string? elementType { get; set; } /*APINullable*/ - - public List? classifications { get; set; } /*APINullable*/ - public Base? elementProperties { get; set; } - public Base? componentProperties { get; set; } - - public override Level? level - { - get => archicadLevel; - internal set - { - if (value is ArchicadLevel or null) - { - archicadLevel = value as ArchicadLevel; - } - else - { - throw new ArgumentException($"Expected object of type {nameof(ArchicadLevel)}"); - } - } - } - - [JsonIgnore] - public ArchicadLevel? archicadLevel { get; set; } /*APINullabe*/ - - public string? layer { get; set; } /*APINullabe*/ - - // Geometry and positioning - public double? thickness { get; set; } - public ElementShape shape { get; set; } - public string? structure { get; set; } /*APINullabe*/ - public string? compositeName { get; set; } - public string? buildingMaterialName { get; set; } - public string? referencePlaneLocation { get; set; } /*APINullabe*/ - - // EdgeTrims - public string? edgeAngleType { get; set; } - public double? edgeAngle { get; set; } - - // Floor Plan and Section - Floor Plan Display - public string? showOnStories { get; set; } /*APINullabe*/ - public Visibility? visibilityCont { get; set; } - public Visibility? visibilityFill { get; set; } - - // Floor Plan and Section - Cut Surfaces - public short? sectContPen { get; set; } - public string? sectContLtype { get; set; } - public short? cutFillPen { get; set; } - public short? cutFillBackgroundPen { get; set; } - - // Floor Plan and Section - Outlines - public short? contourPen { get; set; } - public string? contourLineType { get; set; } - public short? hiddenContourLinePen { get; set; } - public string? hiddenContourLineType { get; set; } - - // Floor Plan and Section - Cover Fills - public bool? useFloorFill { get; set; } - public short? floorFillPen { get; set; } - public short? floorFillBGPen { get; set; } - public string? floorFillName { get; set; } - public bool? use3DHatching { get; set; } - public string? hatchOrientation { get; set; } - public double? hatchOrientationOrigoX { get; set; } - public double? hatchOrientationOrigoY { get; set; } - public double? hatchOrientationXAxisX { get; set; } - public double? hatchOrientationXAxisY { get; set; } - public double? hatchOrientationYAxisX { get; set; } - public double? hatchOrientationYAxisY { get; set; } - - // Model - public string? topMat { get; set; } - public string? sideMat { get; set; } - public string? botMat { get; set; } - public bool? materialsChained { get; set; } - - [SpeckleType("Objects.BuiltElements.Archicad.ArchicadFloor+Visibility")] - public class Visibility : Base - { - public bool? showOnHome { get; set; } - public bool? showAllAbove { get; set; } - public bool? showAllBelow { get; set; } - public short? showRelAbove { get; set; } - public short? showRelBelow { get; set; } - } -} diff --git a/src/Speckle.Objects/BuiltElements/Archicad/ArchicadLevel.cs b/src/Speckle.Objects/BuiltElements/Archicad/ArchicadLevel.cs deleted file mode 100644 index 5b2d4467..00000000 --- a/src/Speckle.Objects/BuiltElements/Archicad/ArchicadLevel.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Archicad; - -/* -For further informations about given the variables, visit: -https://archicadapi.graphisoft.com/documentation/api_storytype -*/ -[SpeckleType("Objects.BuiltElements.Archicad.ArchicadLevel")] -public class ArchicadLevel : Level -{ - public short index { get; set; } - - public ArchicadLevel() { } - - public ArchicadLevel(string name, double elevation, short index) - { - this.name = name; - this.elevation = elevation; - this.index = index; - } - - public ArchicadLevel(string name) - { - this.name = name; - } -} diff --git a/src/Speckle.Objects/BuiltElements/Archicad/ArchicadOpening.cs b/src/Speckle.Objects/BuiltElements/Archicad/ArchicadOpening.cs deleted file mode 100644 index 91394788..00000000 --- a/src/Speckle.Objects/BuiltElements/Archicad/ArchicadOpening.cs +++ /dev/null @@ -1,83 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Archicad; - -[SpeckleType("Objects.BuiltElements.Archicad.ArchicadOpening")] -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/ArchicadRoof.cs b/src/Speckle.Objects/BuiltElements/Archicad/ArchicadRoof.cs deleted file mode 100644 index fa9f2b60..00000000 --- a/src/Speckle.Objects/BuiltElements/Archicad/ArchicadRoof.cs +++ /dev/null @@ -1,293 +0,0 @@ -using Speckle.Newtonsoft.Json; -using Speckle.Objects.Geometry; -using Speckle.Objects.Other; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Archicad; - -/* -For further informations about given the variables, visit: -https://archicadapi.graphisoft.com/documentation/api_shellbasetype -*/ -[SpeckleType("Objects.BuiltElements.Archicad.ArchicadShellBase")] -public class ArchicadShellBase : BuiltElements.Roof -{ - [SpeckleType("Objects.BuiltElements.Archicad.ArchicadShellBase+Visibility")] - public class Visibility : Base - { - public bool? showOnHome { get; set; } - public bool? showAllAbove { get; set; } - public bool? showAllBelow { get; set; } - public short? showRelAbove { get; set; } - public short? showRelBelow { 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; } - - public override Level? level - { - get => archicadLevel; - internal set - { - if (value is ArchicadLevel or null) - { - archicadLevel = value as ArchicadLevel; - } - else - { - throw new ArgumentException($"Expected object of type {nameof(ArchicadLevel)}"); - } - } - } - - [JsonIgnore] - public ArchicadLevel? archicadLevel { get; set; } /*APINullabe*/ - - public string? layer { get; set; } /*APINullabe*/ - - // Geometry and positioning - public double? thickness { get; set; } - public string? structure { get; set; } /*APINullabe*/ - public string? compositeName { get; set; } - public string? buildingMaterialName { get; set; } - - // EdgeTrims - public string? edgeAngleType { get; set; } - public double? edgeAngle { get; set; } - - // Floor Plan and Section - Floor Plan Display - public string? showOnStories { get; set; } /*APINullabe*/ - public Visibility? visibilityCont { get; set; } - public Visibility? visibilityFill { get; set; } - public string? displayOptionName { get; set; } /*APINullabe*/ - public string? showProjectionName { get; set; } /*APINullabe*/ - - // Floor Plan and Section - Cut Surfaces - public short? sectContPen { get; set; } /*APINullabe*/ - public string? sectContLtype { get; set; } /*APINullabe*/ - public short? cutFillPen { get; set; } - public short? cutFillBackgroundPen { get; set; } - - // Floor Plan and Section - Outlines - public short? contourPen { get; set; } /*APINullabe*/ - public string? contourLineType { get; set; } /*APINullabe*/ - public short? overheadLinePen { get; set; } /*APINullabe*/ - public string? overheadLinetype { get; set; } /*APINullabe*/ - - // Floor Plan and Section - Cover Fills - public bool? useFloorFill { get; set; } /*APINullabe*/ - public short? floorFillPen { get; set; } - public short? floorFillBGPen { get; set; } - public string? floorFillName { get; set; } - public bool? use3DHatching { get; set; } - public bool? useFillLocBaseLine { get; set; } - public bool? useSlantedFill { get; set; } - public string? hatchOrientation { get; set; } - public double? hatchOrientationOrigoX { get; set; } - public double? hatchOrientationOrigoY { get; set; } - public double? hatchOrientationXAxisX { get; set; } - public double? hatchOrientationXAxisY { get; set; } - public double? hatchOrientationYAxisX { get; set; } - public double? hatchOrientationYAxisY { get; set; } - - // Model - public string? topMat { get; set; } - public string? sideMat { get; set; } - public string? botMat { get; set; } - public bool? materialsChained { get; set; } - public string? trimmingBodyName { get; set; } /*APINullabe*/ -} - -/* -For further informations about given the variables, visit: -https://archicadapi.graphisoft.com/documentation/api_rooftype -*/ -[SpeckleType("Objects.BuiltElements.Archicad.ArchicadRoof")] -public sealed class ArchicadRoof : ArchicadShellBase -{ - [SpeckleType("Objects.BuiltElements.Archicad.ArchicadRoof+BaseLine")] - public class BaseLine : Base - { - public Point begC { get; set; } - public Point endC { get; set; } - } - - [SpeckleType("Objects.BuiltElements.Archicad.ArchicadRoof+RoofLevel")] - public class RoofLevel : Base - { - public double? levelHeight { get; set; } - public double? levelAngle { get; set; } - } - - [SpeckleType("Objects.BuiltElements.Archicad.ArchicadRoof+LevelEdge")] - public class LevelEdge : Base - { - public double? edgeLevelAngle { get; set; } - public double? eavesOverhang { get; set; } - public string? topMaterial { get; set; } - public string? bottomMaterial { get; set; } - public string? coverFillType { get; set; } - public string? angleType { get; set; } - } - - [SpeckleType("Objects.BuiltElements.Archicad.ArchicadRoof+PivotPolyEdge")] - public class PivotPolyEdge : Base - { - public int? nLevelEdgeData { get; set; } - public Dictionary? roofLevels { get; set; } - } - - // Geometry and positioning - public string roofClassName { get; set; } - public double? planeRoofAngle { get; set; } - public ElementShape shape { get; set; } - public BaseLine? baseLine { get; set; } - public bool? posSign { get; set; } - public ElementShape? pivotPolygon { get; set; } /*APINullabe*/ - public short? levelNum { get; set; } - public Dictionary? levels { get; set; } /*APINullabe*/ - public Dictionary? roofPivotPolyEdges { get; set; } -} - -/* -For further informations about given the variables, visit: -https://archicadapi.graphisoft.com/documentation/api_shelltype -*/ -[SpeckleType("Objects.BuiltElements.Archicad.ArchicadShell")] -public sealed class ArchicadShell : ArchicadShellBase -{ - [SpeckleType("Objects.BuiltElements.Archicad.ArchicadShell+ShellContourEdgeData")] - public class ShellContourEdgeData : Base - { - public string? sideTypeName { get; set; } - public double? sideAngle { get; set; } - public string? edgeTypeName { get; set; } - public string? edgeSideMaterial { get; set; } - } - - [SpeckleType("Objects.BuiltElements.Archicad.ArchicadShell+ShellContourData")] - public class ShellContourData : Base - { - public ElementShape? shellContourPoly { get; set; } - public Transform shellContourPlane { get; set; } - public double? shellContourHeight { get; set; } - public int? shellContourID { get; set; } - public Dictionary? shellContourEdges { get; set; } - } - - // Geometry and positioning - public string? shellClassName { get; set; } /*APINullabe*/ - public Transform? basePlane { get; set; } /*APINullabe*/ - public bool? flipped { get; set; } /*APINullabe*/ - public bool? hasContour { get; set; } /*APINullabe*/ - public int? numHoles { get; set; } /*APINullabe*/ - public Dictionary? shellContours { get; set; } - public string? defaultEdgeType { get; set; } /*APINullabe*/ - - public double? slantAngle { get; set; } - public double? revolutionAngle { get; set; } - public double? distortionAngle { get; set; } - public bool? segmentedSurfaces { get; set; } - public double? shapePlaneTilt { get; set; } - public double? begPlaneTilt { get; set; } - public double? endPlaneTilt { get; set; } - public ElementShape shape { get; set; } - public ElementShape? shape1 { get; set; } /*APINullabe*/ - public ElementShape? shape2 { get; set; } /*APINullabe*/ - public Transform? axisBase { get; set; } /*APINullabe*/ - public Transform? plane1 { get; set; } /*APINullabe*/ - public Transform? plane2 { get; set; } /*APINullabe*/ - public Point? begC { get; set; } - public double? begAngle { get; set; } - public Vector? extrusionVector { get; set; } - public Vector? shapeDirection { get; set; } - public Vector? distortionVector { get; set; } - public string? morphingRuleName { get; set; } - - // Model - [SpeckleType("Objects.BuiltElements.Archicad.ArchicadShell+BegShapeEdge")] - public class BegShapeEdge : Base - { - public string? begShapeEdgeTrimSideType { get; set; } - public double? begShapeEdgeTrimSideAngle { get; set; } - public string? begShapeEdgeSideMaterial { get; set; } - public string? begShapeEdgeType { get; set; } - } - - [SpeckleType("Objects.BuiltElements.Archicad.ArchicadShell+EndShapeEdge")] - public class EndShapeEdge : Base - { - public string? endShapeEdgeTrimSideType { get; set; } - public double? endShapeEdgeTrimSideAngle { get; set; } - public string? endShapeEdgeSideMaterial { get; set; } - public string? endShapeEdgeType { get; set; } - } - - [SpeckleType("Objects.BuiltElements.Archicad.ArchicadShell+ExtrudedEdge1")] - public class ExtrudedEdge1 : Base - { - public string? extrudedEdgeTrimSideType1 { get; set; } - public double? extrudedEdgeTrimSideAngle1 { get; set; } - public string? extrudedEdgeSideMaterial1 { get; set; } - public string? extrudedEdgeType1 { get; set; } - } - - [SpeckleType("Objects.BuiltElements.Archicad.ArchicadShell+ExtrudedEdge2")] - public class ExtrudedEdge2 : Base - { - public string? extrudedEdgeTrimSideType2 { get; set; } - public double? extrudedEdgeTrimSideAngle2 { get; set; } - public string? extrudedEdgeSideMaterial2 { get; set; } - public string? extrudedEdgeType2 { get; set; } - } - - [SpeckleType("Objects.BuiltElements.Archicad.ArchicadShell+RevolvedEdge1")] - public class RevolvedEdge1 : Base - { - public string? revolvedEdgeTrimSideType1 { get; set; } - public double? revolvedEdgeTrimSideAngle1 { get; set; } - public string? revolvedEdgeSideMaterial1 { get; set; } - public string? revolvedEdgeType1 { get; set; } - } - - [SpeckleType("Objects.BuiltElements.Archicad.ArchicadShell+RevolvedEdge2")] - public class RevolvedEdge2 : Base - { - public string? revolvedEdgeTrimSideType2 { get; set; } - public double? revolvedEdgeTrimSideAngle2 { get; set; } - public string? revolvedEdgeSideMaterial2 { get; set; } - public string? revolvedEdgeType2 { get; set; } - } - - [SpeckleType("Objects.BuiltElements.Archicad.ArchicadShell+RuledEdge1")] - public class RuledEdge1 : Base - { - public string? ruledEdgeTrimSideType1 { get; set; } - public double? ruledEdgeTrimSideAngle1 { get; set; } - public string? ruledEdgeSideMaterial1 { get; set; } - public string? ruledEdgeType1 { get; set; } - } - - [SpeckleType("Objects.BuiltElements.Archicad.ArchicadShell+RuledEdge2")] - public class RuledEdge2 : Base - { - public string? ruledEdgeTrimSideType2 { get; set; } - public double? ruledEdgeTrimSideAngle2 { get; set; } - public string? ruledEdgeSideMaterial2 { get; set; } - public string? ruledEdgeType2 { get; set; } - } - - public BegShapeEdge? begShapeEdge { get; set; } - public EndShapeEdge? endShapeEdge { get; set; } - public ExtrudedEdge1? extrudedEdge1 { get; set; } - public ExtrudedEdge2? extrudedEdge2 { get; set; } - public RevolvedEdge1? revolvedEdge1 { get; set; } - public RevolvedEdge2? revolvedEdge2 { get; set; } - public RuledEdge1? ruledEdge1 { get; set; } - public RuledEdge2? ruledEdge2 { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Archicad/ArchicadRoom.cs b/src/Speckle.Objects/BuiltElements/Archicad/ArchicadRoom.cs deleted file mode 100644 index c7b865db..00000000 --- a/src/Speckle.Objects/BuiltElements/Archicad/ArchicadRoom.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Speckle.Newtonsoft.Json; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Archicad; - -/* -For further informations about given the variables, visit: -https://archicadapi.graphisoft.com/documentation/api_zonetype -*/ -[SpeckleType("Objects.BuiltElements.Archicad.ArchicadRoom")] -public class ArchicadRoom : Room -{ - // Element base - public string elementType { get; set; } - - public List classifications { get; set; } - public Base? elementProperties { get; set; } - public Base? componentProperties { get; set; } - - public override Level? level - { - get => archicadLevel; - set => archicadLevel = value as ArchicadLevel ?? null; - } - - [JsonIgnore] - public ArchicadLevel? archicadLevel { get; set; } - - public string? layer { get; set; } /*APINullabe*/ - - public ElementShape shape { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Archicad/ArchicadWall.cs b/src/Speckle.Objects/BuiltElements/Archicad/ArchicadWall.cs deleted file mode 100644 index 83e81cfb..00000000 --- a/src/Speckle.Objects/BuiltElements/Archicad/ArchicadWall.cs +++ /dev/null @@ -1,122 +0,0 @@ -using Speckle.Newtonsoft.Json; -using Speckle.Objects.Geometry; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Archicad; - -/* -For further informations about given the variables, visit: -https://archicadapi.graphisoft.com/documentation/api_walltype -*/ -[SpeckleType("Objects.BuiltElements.Archicad.ArchicadWall")] -public class ArchicadWall : Wall -{ - [SchemaInfo("ArchicadWall", "Creates an Archicad wall.", "Archicad", "Structure")] - public ArchicadWall() { } - - // Element base - public string? elementType { get; set; } /*APINullabe*/ - - public List? classifications { get; set; } /*APINullabe*/ - public Base? elementProperties { get; set; } - public Base? componentProperties { get; set; } - - public override Level? level - { - get => archicadLevel; - internal set - { - if (value is ArchicadLevel or null) - { - archicadLevel = value as ArchicadLevel; - } - else - { - throw new ArgumentException($"Expected object of type {nameof(ArchicadLevel)}"); - } - } - } - - [JsonIgnore] - public ArchicadLevel? archicadLevel { get; set; } /*APINullabe*/ - - public string? layer { get; set; } /*APINullabe*/ - - // Wall geometry - public double? baseOffset { get; set; } /*APINullabe*/ - public Point startPoint { get; set; } - public Point endPoint { get; set; } - - public string? structure { get; set; } /*APINullabe*/ - public string? geometryMethod { get; set; } /*APINullabe*/ - public string? wallComplexity { get; set; } /*APINullabe*/ - - public string? buildingMaterialName { get; set; } - public string? compositeName { get; set; } - public string? profileName { get; set; } - public double? arcAngle { get; set; } - - public ElementShape? shape { get; set; } - - public double? thickness { get; set; } /*APINullabe*/ - - public double? outsideSlantAngle { get; set; } - public double? insideSlantAngle { get; set; } - - public bool? polyWalllCornersCanChange { get; set; } - - // Wall and stories relation - public double? topOffset { get; set; } /*APINullabe*/ - public short? relativeTopStory { get; set; } /*APINullabe*/ - public string? referenceLineLocation { get; set; } /*APINullabe*/ - public double? referenceLineOffset { get; set; } - public double? offsetFromOutside { get; set; } /*APINullabe*/ - public int? referenceLineStartIndex { get; set; } /*APINullabe*/ - public int? referenceLineEndIndex { get; set; } /*APINullabe*/ - public bool flipped { get; set; } - - // Floor Plan and Section - Floor Plan Display - public string? showOnStories { get; set; } /*APINullabe*/ - public string? displayOptionName { get; set; } /*APINullabe*/ - public string? showProjectionName { get; set; } /*APINullabe*/ - - // Floor Plan and Section - Cut Surfaces parameters - public short? cutLinePen { get; set; } - public string? cutLinetype { get; set; } - public short? overrideCutFillPen { get; set; } - public short? overrideCutFillBackgroundPen { get; set; } - - // Floor Plan and Section - Outlines parameters - public short? uncutLinePen { get; set; } /*APINullabe*/ - public string? uncutLinetype { get; set; } /*APINullabe*/ - public short? overheadLinePen { get; set; } /*APINullabe*/ - public string? overheadLinetype { get; set; } /*APINullabe*/ - - // Model - Override Surfaces - public string? referenceMaterialName { get; set; } - public int? referenceMaterialStartIndex { get; set; } - public int? referenceMaterialEndIndex { get; set; } - public string? oppositeMaterialName { get; set; } - public int? oppositeMaterialStartIndex { get; set; } - public int? oppositeMaterialEndIndex { get; set; } - public string? sideMaterialName { get; set; } - public bool? materialsChained { get; set; } /*APINullabe*/ - public bool? inheritEndSurface { get; set; } /*APINullabe*/ - public bool? alignTexture { get; set; } /*APINullabe*/ - public int? sequence { get; set; } /*APINullabe*/ - - // Model - Log Details (log height, start with half log, surface of horizontal edges, log shape) - public double? logHeight { get; set; } - public bool? startWithHalfLog { get; set; } - public string? surfaceOfHorizontalEdges { get; set; } - public string? logShape { get; set; } - - // Model - Defines the relation of wall to zones (Zone Boundary, Reduce Zone Area Only, No Effect on Zones) - public string? wallRelationToZoneName { get; set; } /*APINullabe*/ - - // Does it have any embedded object? - public bool? hasDoor { get; set; } /*APINullabe*/ - - public bool? hasWindow { get; set; } /*APINullabe*/ -} diff --git a/src/Speckle.Objects/BuiltElements/Archicad/AssemblySegment.cs b/src/Speckle.Objects/BuiltElements/Archicad/AssemblySegment.cs deleted file mode 100644 index 172c23c2..00000000 --- a/src/Speckle.Objects/BuiltElements/Archicad/AssemblySegment.cs +++ /dev/null @@ -1,65 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Archicad; - -[SpeckleType("Objects.BuiltElements.Archicad.AssemblySegment")] -public class AssemblySegment : Base -{ - public bool circleBased { get; set; } - - public string modelElemStructureType { get; set; } - - public double nominalHeight { get; set; } - - public double nominalWidth { get; set; } - - public bool isHomogeneous { get; set; } - - public double endWidth { get; set; } - - public double endHeight { get; set; } - - public bool isEndWidthAndHeightLinked { get; set; } - - public bool isWidthAndHeightLinked { get; set; } - - public string profileAttrName { get; set; } - - public string buildingMaterial { get; set; } -} - -[SpeckleType("Objects.BuiltElements.Archicad.AssemblySegmentScheme")] -public class AssemblySegmentScheme : Base -{ - public string lengthType { get; set; } - - public double fixedLength { get; set; } - - public double lengthProportion { get; set; } -} - -[SpeckleType("Objects.BuiltElements.Archicad.AssemblySegmentCut")] -public class AssemblySegmentCut : Base -{ - public string cutType { get; set; } - - public double customAngle { get; set; } -} - -[SpeckleType("Objects.BuiltElements.Archicad.Hole")] -public class Hole : Base -{ - public string holeType { get; set; } - - public bool holeContourOn { get; set; } - - public int holeId { get; set; } - - public double centerx { get; set; } - - public double centerz { get; set; } - - public double width { get; set; } - - public double height { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Archicad/Classification.cs b/src/Speckle.Objects/BuiltElements/Archicad/Classification.cs deleted file mode 100644 index b4fff6cf..00000000 --- a/src/Speckle.Objects/BuiltElements/Archicad/Classification.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Archicad; - -[SpeckleType("Objects.BuiltElements.Archicad.Classification")] -public class Classification : Base -{ - public Classification() { } - - [SchemaInfo("Classification", "A classification to set on an element", "BIM", "All")] - public Classification(string system, string? code = null, string? name = null) - { - this.system = system; - this.code = code; - this.name = name; - } - - public string system { get; set; } - public string? code { get; set; } - public string? name { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Archicad/ComponentProperties.cs b/src/Speckle.Objects/BuiltElements/Archicad/ComponentProperties.cs deleted file mode 100644 index be0f99ae..00000000 --- a/src/Speckle.Objects/BuiltElements/Archicad/ComponentProperties.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Archicad; - -[SpeckleType("Objects.BuiltElements.Archicad.ComponentProperties")] -public class ComponentProperties : Base -{ - public ComponentProperties() { } - - [SchemaInfo("ComponentProperties", "An Archicad element component properties", "Archicad", "Elements")] - public ComponentProperties( - string name, - List propertyGroups, - [SchemaParamInfo("(Optional) Speckle units.")] string units = "" - ) - { - this.name = name; - this.propertyGroups = propertyGroups; - this.units = units; - } - - public string name { get; set; } - public List? propertyGroups { get; set; } - public string units { get; set; } - - /// - /// Turns a List of ComponentProperties into a Base so that it can be used with the Speckle properties prop - /// - /// - /// - public static Base? ToBase(List? componentPropertiesList) - { - if (componentPropertiesList == null || componentPropertiesList.Count == 0) - { - return null; - } - - var @base = new Base(); - - foreach (ComponentProperties componentProperties in componentPropertiesList) - { - @base[RemoveDisallowedPropNameChars(componentProperties.name)] = PropertyGroup.ToBase( - componentProperties.propertyGroups - ); - } - - return @base; - } -} diff --git a/src/Speckle.Objects/BuiltElements/Archicad/DirectShape.cs b/src/Speckle.Objects/BuiltElements/Archicad/DirectShape.cs deleted file mode 100644 index 8806440e..00000000 --- a/src/Speckle.Objects/BuiltElements/Archicad/DirectShape.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Archicad; - -[SpeckleType("Objects.BuiltElements.Archicad.DirectShape")] -public class DirectShape : Base -{ - public DirectShape() { } - - public DirectShape(string applicationId, List displayValue) - { - this.applicationId = applicationId; - this.displayValue = 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; } - - public string units { get; set; } - - [DetachProperty] - public List displayValue { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Archicad/ElementShape.cs b/src/Speckle.Objects/BuiltElements/Archicad/ElementShape.cs deleted file mode 100644 index ca48e028..00000000 --- a/src/Speckle.Objects/BuiltElements/Archicad/ElementShape.cs +++ /dev/null @@ -1,71 +0,0 @@ -using Speckle.Newtonsoft.Json; -using Speckle.Objects.Geometry; -using Speckle.Objects.Primitive; -using Speckle.Sdk.Common; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Archicad; - -[SpeckleType("Objects.BuiltElements.Archicad.ElementShape")] -public sealed class ElementShape : Base -{ - public ElementShape() { } - - public ElementShape(Polyline contourPolyline, List? holePolylines = null) - { - this.contourPolyline = contourPolyline; - this.holePolylines = holePolylines; - } - - public Polyline contourPolyline { get; set; } - - public List? holePolylines { get; set; } - - /// - /// This class is only used for Archicad interop - /// - [SpeckleType("Objects.BuiltElements.Archicad.ElementShape+PolylineSegment")] - public sealed class PolylineSegment : Base, ICurve - { - public PolylineSegment() { } - - public PolylineSegment(Point startPoint, Point endPoint, double? arcAngle = null, bool? bodyFlag = null) - { - this.startPoint = startPoint; - this.endPoint = endPoint; - this.arcAngle = arcAngle ?? 0; - this.bodyFlag = bodyFlag; - } - - 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; } = Interval.UnitInterval; - } - - /// - /// This class is only used for Archicad interop - /// > - [SpeckleType("Objects.BuiltElements.Archicad.ElementShape+Polyline")] - public sealed class Polyline : Base, ICurve - { - public Polyline() { } - - public Polyline(List 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; } = Interval.UnitInterval; - } -} diff --git a/src/Speckle.Objects/BuiltElements/Archicad/Fenestration.cs b/src/Speckle.Objects/BuiltElements/Archicad/Fenestration.cs deleted file mode 100644 index 8f6db424..00000000 --- a/src/Speckle.Objects/BuiltElements/Archicad/Fenestration.cs +++ /dev/null @@ -1,81 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Archicad; - -[SpeckleType("Objects.BuiltElements.Archicad.ArchicadFenestration")] -public class ArchicadFenestration : Base, IDisplayValue> -{ - 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; } - - public double? width { get; set; } /*APINullabe*/ - public double? height { get; set; } /*APINullabe*/ - public double? subFloorThickness { get; set; } /*APINullabe*/ - public bool? reflected { get; set; } /*APINullabe*/ - public bool? oSide { get; set; } /*APINullabe*/ - public bool? refSide { get; set; } /*APINullabe*/ - public string? verticalLinkTypeName { get; set; } - public short? verticalLinkStoryIndex { get; set; } - public bool? wallCutUsing { get; set; } - public short? pen { get; set; } /*APINullabe*/ - public string? lineTypeName { get; set; } /*APINullabe*/ - public string? buildingMaterial { get; set; } /*APINullabe*/ - public string? sectFill { get; set; } /*APINullabe*/ - public short? sectFillPen { get; set; } /*APINullabe*/ - public short? sectBackgroundPen { get; set; } /*APINullabe*/ - public short? sectContPen { get; set; } /*APINullabe*/ - public string? cutLineType { get; set; } /*APINullabe*/ - public string? aboveViewLineType { get; set; } /*APINullabe*/ - public short? aboveViewLinePen { get; set; } /*APINullabe*/ - public short? belowViewLinePen { get; set; } /*APINullabe*/ - public string? belowViewLineType { get; set; } /*APINullabe*/ - public bool? useObjectPens { get; set; } /*APINullabe*/ - public bool? useObjLinetypes { get; set; } /*APINullabe*/ - public bool? useObjMaterials { get; set; } /*APINullabe*/ - public bool? useObjSectAttrs { get; set; } /*APINullabe*/ - public string? libraryPart { get; set; } /*APINullabe*/ - public string? displayOptionName { get; set; } /*APINullabe*/ - - [DetachProperty] - public List displayValue { get; set; } -} - -[SpeckleType("Objects.BuiltElements.Archicad.ArchicadDoorWindowBase")] -public class ArchicadDoorWindowBase : ArchicadFenestration -{ - public double? revealDepthFromSide { get; set; } /*APINullabe*/ - public double? jambDepthHead { get; set; } /*APINullabe*/ - public double? jambDepth { get; set; } /*APINullabe*/ - public double? jambDepth2 { get; set; } /*APINullabe*/ - public double? objLoc { get; set; } /*APINullabe*/ - public double? lower { get; set; } /*APINullabe*/ - public string? directionType { get; set; } /*APINullabe*/ - - public Point? startPoint { get; set; } /*APINullabe*/ - public Point? dirVector { get; set; } /*APINullabe*/ -} - -[SpeckleType("Objects.BuiltElements.Archicad.ArchicadDoor")] -public sealed class ArchicadDoor : ArchicadDoorWindowBase { } - -[SpeckleType("Objects.BuiltElements.Archicad.ArchicadWindow")] -public sealed class ArchicadWindow : ArchicadDoorWindowBase { } - -[SpeckleType("Objects.BuiltElements.Archicad.ArchicadSkylight")] -public sealed class ArchicadSkylight : ArchicadFenestration -{ - public uint? vertexID { get; set; } /*APINullabe*/ - public string? skylightFixMode { get; set; } /*APINullabe*/ - public string? skylightAnchor { get; set; } /*APINullabe*/ - public Point? anchorPosition { get; set; } /*APINullabe*/ - public double? anchorLevel { get; set; } /*APINullabe*/ - public double? azimuthAngle { get; set; } /*APINullabe*/ - public double? elevationAngle { get; set; } /*APINullabe*/ -} diff --git a/src/Speckle.Objects/BuiltElements/Archicad/Property.cs b/src/Speckle.Objects/BuiltElements/Archicad/Property.cs deleted file mode 100644 index 387d35cb..00000000 --- a/src/Speckle.Objects/BuiltElements/Archicad/Property.cs +++ /dev/null @@ -1,54 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Archicad; - -[SpeckleType("Objects.BuiltElements.Archicad.Property")] -public class Property : Base -{ - public Property() { } - - [SchemaInfo("Property", "An Archicad element property", "Archicad", "Elements")] - public Property(string name, object value, [SchemaParamInfo("(Optional) Speckle units.")] string units = "") - { - this.name = name; - this.value = value; - this.units = units; - } - - public string name { get; set; } - public object? value { get; set; } - public List? values { get; set; } - public string units { get; set; } - - /// - /// Turns a List of Property into a Base so that it can be used with the Speckle properties prop - /// - /// - /// - public static Base? ToBase(List? properties) - { - if (properties == null || properties.Count == 0) - { - return null; - } - - var @base = new Base(); - - foreach (Property property in properties) - { - var key = RemoveDisallowedPropNameChars(property.name); - if (string.IsNullOrEmpty(key) || @base[key] != null) - { - continue; - } - - @base[key] = property.value; - - // todo - //property.values; - } - - return @base; - } -} diff --git a/src/Speckle.Objects/BuiltElements/Archicad/PropertyGroup.cs b/src/Speckle.Objects/BuiltElements/Archicad/PropertyGroup.cs deleted file mode 100644 index 66d0023f..00000000 --- a/src/Speckle.Objects/BuiltElements/Archicad/PropertyGroup.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Archicad; - -[SpeckleType("Objects.BuiltElements.Archicad.PropertyGroup")] -public class PropertyGroup : Base -{ - public PropertyGroup() { } - - [SchemaInfo("PropertyGroup", "An Archicad element property group", "Archicad", "Elements")] - public PropertyGroup( - string name, - List propertyList, - [SchemaParamInfo("(Optional) Speckle units.")] string units = "" - ) - { - this.name = name; - this.propertyList = propertyList; - this.units = units; - } - - public string name { get; set; } - public List? propertyList { get; set; } - public string units { get; set; } - - /// - /// Turns a List of PropertyGroup into a Base so that it can be used with the Speckle properties prop - /// - /// - /// - public static Base? ToBase(List? propertyGroups) - { - if (propertyGroups == null || propertyGroups.Count == 0) - { - return null; - } - - var @base = new Base(); - - foreach (PropertyGroup propertyGroup in propertyGroups) - { - @base[RemoveDisallowedPropNameChars(propertyGroup.name)] = Property.ToBase(propertyGroup.propertyList); - } - - return @base; - } -} diff --git a/src/Speckle.Objects/BuiltElements/Area.cs b/src/Speckle.Objects/BuiltElements/Area.cs deleted file mode 100644 index 85689abb..00000000 --- a/src/Speckle.Objects/BuiltElements/Area.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements; - -[SpeckleType("Objects.BuiltElements.Area")] -public class Area : Base, IHasArea, IHasVolume, IDisplayValue> -{ - public Area() { } - - /// - /// SchemaBuilder constructor for an Area - /// - [SchemaInfo("Area", "Creates a Speckle area", "BIM", "Other")] - public Area(string name, string number, Level level, [SchemaMainParam] Point center) - { - this.name = name; - this.number = number; - this.level = level; - this.center = center; - } - - public string name { get; set; } - public string number { get; set; } - public Level level { get; set; } - public Point center { get; set; } - public List voids { get; set; } = new(); - public ICurve outline { get; set; } - - public string units { get; set; } - - [DetachProperty] - public List displayValue { get; set; } - - public double area { get; set; } - public double volume { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Baseline.cs b/src/Speckle.Objects/BuiltElements/Baseline.cs deleted file mode 100644 index 3a97c8f9..00000000 --- a/src/Speckle.Objects/BuiltElements/Baseline.cs +++ /dev/null @@ -1,88 +0,0 @@ -using Speckle.Newtonsoft.Json; -using Speckle.Sdk.Models; - -namespace Speckle.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 deleted file mode 100644 index fe825203..00000000 --- a/src/Speckle.Objects/BuiltElements/Beam.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements; - -[SpeckleType("Objects.BuiltElements.Beam")] -public class Beam : Base, IDisplayValue> -{ - public Beam() { } - - 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; } - - [DetachProperty] - 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 deleted file mode 100644 index c3f48f56..00000000 --- a/src/Speckle.Objects/BuiltElements/Brace.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements; - -[SpeckleType("Objects.BuiltElements.Brace")] -public class Brace : Base, IDisplayValue> -{ - public Brace() { } - - 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; } - - [DetachProperty] - 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/CableTray.cs b/src/Speckle.Objects/BuiltElements/CableTray.cs deleted file mode 100644 index e9da6957..00000000 --- a/src/Speckle.Objects/BuiltElements/CableTray.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements; - -[SpeckleType("Objects.BuiltElements.CableTray")] -public class CableTray : Base, IDisplayValue> -{ - public ICurve baseCurve { get; set; } - public double width { get; set; } - public double height { get; set; } - public double length { get; set; } - - public string units { get; set; } - - [DetachProperty] - public List displayValue { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Ceiling.cs b/src/Speckle.Objects/BuiltElements/Ceiling.cs deleted file mode 100644 index 6d2232a8..00000000 --- a/src/Speckle.Objects/BuiltElements/Ceiling.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements; - -[SpeckleType("Objects.BuiltElements.Ceiling")] -public class Ceiling : Base, IDisplayValue> -{ - public Ceiling() { } - - [SchemaInfo("Ceiling", "Creates a Speckle ceiling", "BIM", "Architecture")] - public Ceiling( - [SchemaMainParam] ICurve outline, - List? voids = null, - [SchemaParamInfo("Any nested elements that this ceiling might have")] List? elements = null - ) - { - this.outline = outline; - this.voids = voids ?? new(); - this.elements = elements; - } - - public ICurve outline { get; set; } - public List voids { get; set; } = new(); - - [DetachProperty] - public List? elements { get; set; } - - public string units { get; set; } - - [DetachProperty] - public List displayValue { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Civil/CivilAlignment.cs b/src/Speckle.Objects/BuiltElements/Civil/CivilAlignment.cs deleted file mode 100644 index d3f5a76b..00000000 --- a/src/Speckle.Objects/BuiltElements/Civil/CivilAlignment.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Civil; - -[SpeckleType("Objects.BuiltElements.Civil.CivilAlignment")] -public class CivilAlignment : Alignment -{ - public string type { get; set; } - - public string site { get; set; } - - public string style { get; set; } - - public double offset { get; set; } - - /// - /// Name of parent alignment if this is an offset alignment - /// - public string parent { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Civil/CivilAppliedAssembly.cs b/src/Speckle.Objects/BuiltElements/Civil/CivilAppliedAssembly.cs deleted file mode 100644 index 3a86da7f..00000000 --- a/src/Speckle.Objects/BuiltElements/Civil/CivilAppliedAssembly.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Civil; - -[SpeckleType("Objects.BuiltElements.Civil.CivilAppliedAssembly")] -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 deleted file mode 100644 index e15771fa..00000000 --- a/src/Speckle.Objects/BuiltElements/Civil/CivilAppliedSubassembly.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Other.Civil; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Civil; - -[SpeckleType("Objects.BuiltElements.Civil.CivilAppliedSubassembly")] -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 deleted file mode 100644 index 5e6f2220..00000000 --- a/src/Speckle.Objects/BuiltElements/Civil/CivilBaseline.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Civil; - -[SpeckleType("Objects.BuiltElements.Civil.CivilBaseline")] -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 deleted file mode 100644 index c163ba7a..00000000 --- a/src/Speckle.Objects/BuiltElements/Civil/CivilBaselineRegion.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Civil; - -[SpeckleType("Objects.BuiltElements.Civil.CivilBaselineRegion")] -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 deleted file mode 100644 index 7b937b78..00000000 --- a/src/Speckle.Objects/BuiltElements/Civil/CivilCalculatedLink.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Civil; - -[SpeckleType("Objects.BuiltElements.Civil.CivilCalculatedLink")] -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 deleted file mode 100644 index 97f875d8..00000000 --- a/src/Speckle.Objects/BuiltElements/Civil/CivilCalculatedPoint.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Civil; - -[SpeckleType("Objects.BuiltElements.Civil.CivilCalculatedPoint")] -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 deleted file mode 100644 index 8ad907fe..00000000 --- a/src/Speckle.Objects/BuiltElements/Civil/CivilCalculatedShape.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Civil; - -[SpeckleType("Objects.BuiltElements.Civil.CivilCalculatedShape")] -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/Civil/CivilProfile.cs b/src/Speckle.Objects/BuiltElements/Civil/CivilProfile.cs deleted file mode 100644 index 44f0cb7f..00000000 --- a/src/Speckle.Objects/BuiltElements/Civil/CivilProfile.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Civil; - -[SpeckleType("Objects.BuiltElements.Civil.CivilProfile")] -public class CivilProfile : Profile -{ - public string type { get; set; } - - public string style { get; set; } - - public double offset { get; set; } - - /// - /// Points of vertical intersection - /// - public List pvis { get; set; } - - /// - /// Name of parent profile if this is an offset profile - /// - public string parent { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Column.cs b/src/Speckle.Objects/BuiltElements/Column.cs deleted file mode 100644 index 0408b020..00000000 --- a/src/Speckle.Objects/BuiltElements/Column.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements; - -[SpeckleType("Objects.BuiltElements.Column")] -public class Column : Base, IDisplayValue> -{ - public Column() { } - - 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; } - - [DetachProperty] - 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/Conduit.cs b/src/Speckle.Objects/BuiltElements/Conduit.cs deleted file mode 100644 index 338d8c85..00000000 --- a/src/Speckle.Objects/BuiltElements/Conduit.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements; - -[SpeckleType("Objects.BuiltElements.Conduit")] -public class Conduit : Base, IDisplayValue> -{ - public ICurve baseCurve { get; set; } - public double diameter { get; set; } - public double length { get; set; } - - public string units { get; set; } - - [DetachProperty] - public List displayValue { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Duct.cs b/src/Speckle.Objects/BuiltElements/Duct.cs deleted file mode 100644 index ec6885ff..00000000 --- a/src/Speckle.Objects/BuiltElements/Duct.cs +++ /dev/null @@ -1,77 +0,0 @@ -using Speckle.Newtonsoft.Json; -using Speckle.Objects.Geometry; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements; - -[SpeckleType("Objects.BuiltElements.Duct")] -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 - /// - /// - /// - /// - /// - /// - /// 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) - : this(baseLine, width, height, diameter, default, null, velocity) //TODO: what to do with length??? - { } - - /// - /// SchemaBuilder constructor for a Speckle duct - /// - /// - /// - /// - /// - /// - /// 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, width, height, diameter, default, null, velocity) { } //TODO: what to do with length??? - #endregion -} diff --git a/src/Speckle.Objects/BuiltElements/Featureline.cs b/src/Speckle.Objects/BuiltElements/Featureline.cs deleted file mode 100644 index 026e97f2..00000000 --- a/src/Speckle.Objects/BuiltElements/Featureline.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements; - -[SpeckleType("Objects.BuiltElements.Featureline")] -public class Featureline : Base, IDisplayValue> -{ - /// - /// The base curve of the featureline - /// - public ICurve curve { get; set; } - - /// - /// The points constructing the Featureline - /// - /// - /// Can include both intersection and elevation points - /// - public List points { get; set; } - - public string name { get; set; } - - public string units { get; set; } - - /// - /// The 3D curves generated from the curve and points of the featureline - /// - [DetachProperty] - public List displayValue { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Floor.cs b/src/Speckle.Objects/BuiltElements/Floor.cs deleted file mode 100644 index f03af878..00000000 --- a/src/Speckle.Objects/BuiltElements/Floor.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements; - -[SpeckleType("Objects.BuiltElements.Floor")] -public class Floor : Base, IDisplayValue> -{ - public Floor() { } - - [SchemaInfo("Floor", "Creates a Speckle floor", "BIM", "Architecture")] - public Floor( - [SchemaMainParam] ICurve outline, - List? voids = null, - [SchemaParamInfo("Any nested elements that this floor might have")] List? elements = null - ) - { - this.outline = outline; - - this.voids = voids ?? new(); - - this.elements = elements; - } - - public ICurve outline { get; set; } - public List voids { get; set; } = new(); - - [DetachProperty] - public List? elements { get; set; } - public virtual Level? level { get; internal set; } - public string units { get; set; } - - [DetachProperty] - public List displayValue { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/GridLine.cs b/src/Speckle.Objects/BuiltElements/GridLine.cs deleted file mode 100644 index 62f50263..00000000 --- a/src/Speckle.Objects/BuiltElements/GridLine.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements; - -[SpeckleType("Objects.BuiltElements.GridLine")] -public class GridLine : Base, IDisplayValue> -{ - public GridLine() { } - - [SchemaInfo("GridLine", "Creates a Speckle grid line", "BIM", "Other"), SchemaDeprecated] - public GridLine( - [SchemaParamInfo("NOTE: only Line and Arc curves are supported in Revit"), SchemaMainParam] ICurve baseLine - ) - { - this.baseLine = baseLine; - } - - [SchemaInfo("GridLine", "Creates a Speckle grid line with a label", "BIM", "Other")] - public GridLine( - [SchemaParamInfo("NOTE: only Line and Arc curves are supported in Revit"), SchemaMainParam] ICurve baseLine, - string label = "" - ) - { - this.baseLine = baseLine; - this.label = label; - } - - public ICurve baseLine { get; set; } - public string label { get; set; } - - public string units { get; set; } - - [DetachProperty] - public List displayValue { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Level.cs b/src/Speckle.Objects/BuiltElements/Level.cs deleted file mode 100644 index 6d066cec..00000000 --- a/src/Speckle.Objects/BuiltElements/Level.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements; - -[SpeckleType("Objects.BuiltElements.Level")] -public class Level : Base -{ - //public List elements { get; set; } - - public Level() { } - - /// - /// SchemaBuilder constructor for a Speckle level - /// - /// - /// - /// Assign units when using this constructor due to param - [SchemaInfo("Level", "Creates a Speckle level", "BIM", "Architecture")] - public Level(string name, double elevation) - { - this.name = name; - this.elevation = elevation; - } - - public string name { get; set; } - public double elevation { get; set; } - - public string units { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Network.cs b/src/Speckle.Objects/BuiltElements/Network.cs deleted file mode 100644 index 6c1a7af1..00000000 --- a/src/Speckle.Objects/BuiltElements/Network.cs +++ /dev/null @@ -1,88 +0,0 @@ -using Speckle.Newtonsoft.Json; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements; - -/// -/// Represents graph connections between built elements objects -/// -/// -/// Network may need to be created first in native applications before they are linked. -/// -[Obsolete("Networks are no longer used in any connector to assemble MEP systems.")] -[SpeckleType("Objects.BuiltElements.Network")] -public class Network : Base -{ - public Network() { } - - public string name { get; set; } - - /// - /// The elements contained in the network - /// - public List elements { get; set; } - - /// - /// The connections between - /// - public List links { get; set; } -} - -[Obsolete("Networks are no longer used in any connector to assemble MEP systems.")] -[SpeckleType("Objects.BuiltElements.NetworkElement")] -public class NetworkElement : Base -{ - public NetworkElement() { } - - public string name { get; set; } - - /// - /// The Base object representing the element in the network (eg Pipe, Duct, etc) - /// - /// - /// Currently named "elements" to assist with receiving in connector flatten method. - /// - [DetachProperty] - public Base elements { get; set; } - - /// - /// The index of the links in that are connected to this element - /// - public List linkIndices { get; set; } - - [JsonIgnore] - public Network network { get; set; } - - /// - /// Retrieves the links for this element - /// - [JsonIgnore] -#pragma warning disable CS8619 // Nullability of reference types in value doesn't match target type. Reason: obsolete. - public List links => linkIndices.Select(i => network?.links[i]).ToList(); -#pragma warning restore CS8619 // Nullability of reference types in value doesn't match target type. Reason: obsolete. -} - -[Obsolete("Networks are no longer used in any connector to assemble MEP systems.")] -[SpeckleType("Objects.BuiltElements.NetworkLink")] -public class NetworkLink : Base -{ - public NetworkLink() { } - - public string name { get; set; } - - /// - /// The index of the elements in that are connected by this link - /// - public List elementIndices { get; set; } - - [JsonIgnore] - public Network network { get; set; } - - /// - /// Retrieves the elements for this link - /// - [JsonIgnore] -#pragma warning disable CS8619 // Nullability of reference types in value doesn't match target type. Reason: obsolete. - public List elements => elementIndices.Select(i => network?.elements[i]).ToList(); -#pragma warning restore CS8619 // Nullability of reference types in value doesn't match target type. Reason: obsolete. -} diff --git a/src/Speckle.Objects/BuiltElements/Opening.cs b/src/Speckle.Objects/BuiltElements/Opening.cs deleted file mode 100644 index ae7cda43..00000000 --- a/src/Speckle.Objects/BuiltElements/Opening.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements; - -[SpeckleType("Objects.BuiltElements.Opening")] -public class Opening : Base -{ - public Opening() { } - - [SchemaInfo("Arch Opening", "Creates a Speckle opening", "BIM", "Architecture")] - public Opening(ICurve outline) - { - this.outline = outline; - } - - public ICurve outline { get; set; } - - public string units { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Pipe.cs b/src/Speckle.Objects/BuiltElements/Pipe.cs deleted file mode 100644 index d4cf7b11..00000000 --- a/src/Speckle.Objects/BuiltElements/Pipe.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements; - -[SpeckleType("Objects.BuiltElements.Pipe")] -public class Pipe : Base, IDisplayValue> -{ - public Pipe() { } - - [SchemaInfo("Pipe", "Creates a Speckle pipe", "BIM", "MEP")] - public Pipe( - [SchemaMainParam] ICurve baseCurve, - double length, - double diameter, - double flowrate = 0, - double relativeRoughness = 0 - ) - { - this.baseCurve = baseCurve; - this.length = length; - this.diameter = diameter; - this["flowRate"] = flowrate; - this["relativeRoughness"] = relativeRoughness; - } - - public ICurve baseCurve { get; set; } - public double length { get; set; } - public double diameter { get; set; } - - public string units { get; set; } - - [DetachProperty] - public List displayValue { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Profile.cs b/src/Speckle.Objects/BuiltElements/Profile.cs deleted file mode 100644 index 18ea60b5..00000000 --- a/src/Speckle.Objects/BuiltElements/Profile.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements; - -[SpeckleType("Objects.BuiltElements.Profile")] -public class Profile : Base, IDisplayValue -{ - public List curves { get; set; } - - public string name { get; set; } - - public double startStation { get; set; } - - public double endStation { get; set; } - - public string units { get; set; } - - [DetachProperty] - public Polyline displayValue { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Rebar.cs b/src/Speckle.Objects/BuiltElements/Rebar.cs deleted file mode 100644 index 94d54603..00000000 --- a/src/Speckle.Objects/BuiltElements/Rebar.cs +++ /dev/null @@ -1,159 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements; - -/// -/// A reinforcement bar group comprised of reinforcing bars of the same type and shape. -/// -/// -/// This class is not suitable for freeform rebar, which can have multiple shapes. -/// -public abstract class RebarGroup : Base, IHasVolume, IDisplayValue> - where T : RebarShape -{ - public RebarGroup() { } - - /// - /// The shape of the rebar group - /// - [DetachProperty] - public RebarShape shape { get; set; } - - /// - /// The number of rebars in the rebar group - /// - /// - /// Excluded end bars are not included in the count - /// - public int number { get; set; } - - /// - /// Indicates if rebar set includes the first bar - /// - /// - /// Only applicable to stirrup (transverse) rebar - /// - public bool hasFirstBar { get; set; } - - /// - /// Indicates if rebar set includes the last bar - /// - /// - /// Only applicable to stirrup (transverse) rebar - /// - public bool hasLastBar { get; set; } - - /// - /// The start hook of bars in the rebar group - /// - /// - /// Null indicates no start hook - /// - [DetachProperty] - public virtual RebarHook? startHook { get; set; } - - /// - /// The end hook of bars in the rebar group - /// - /// - /// Null indicates no end hook - /// - [DetachProperty] - public virtual RebarHook? endHook { get; set; } - - /// - /// The display representation of the rebar group as centerline curves - /// - [DetachProperty] - public List displayValue { get; set; } - - /// - /// The total volume of the rebar group. - /// - public double volume { get; set; } - - public string units { get; set; } -} - -/// -/// The shape describing the geometry and geometry parameters of a reinforcing bar -/// -[SpeckleType("Objects.BuiltElements.RebarShape")] -public class RebarShape : Base -{ - public RebarShape() { } - - /// - /// The name of the rebar shape - /// - public string name { get; set; } - - /// - /// The type of the rebar shape - /// - public RebarType rebarType { get; set; } - - /// - /// The curves of the rebar shape - /// - /// - /// Typically suppresses hooks and bend radius - /// - public List curves { get; set; } = new(); - - /// - /// The diameter of the rebar bar - /// - public double barDiameter { get; set; } - - public string units { get; set; } -} - -[SpeckleType("Objects.BuiltElements.RebarHook")] -public class RebarHook : Base -{ - public RebarHook() { } - - /// - /// The angle of the hook in radians. - /// - public double angle { get; set; } - - /// - /// The length of the hook. - /// - public double length { get; set; } - - /// - /// The radius of the bend of the hook. - /// - public double radius { get; set; } - - public string units { get; set; } -} - -public enum RebarType -{ - Unknown = 0, - Standard = 10, - StirrupPolygonal = 20, - StirrupSpiral = 30, - StirrupTapered = 40 -} - -#region Obsolete -[Obsolete("Deprecated in 2.17: Use the RebarGroup class instead")] -[SpeckleType("Objects.BuiltElements.Rebar")] -public class Rebar : Base, IHasVolume, IDisplayValue> -{ - public List curves { get; set; } = new(); - - public string units { get; set; } - - [DetachProperty] - public List displayValue { get; set; } - - public double volume { get; set; } -} -#endregion diff --git a/src/Speckle.Objects/BuiltElements/Revit/AdaptiveComponent.cs b/src/Speckle.Objects/BuiltElements/Revit/AdaptiveComponent.cs deleted file mode 100644 index bf2ad3ba..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/AdaptiveComponent.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Utils; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.AdaptiveComponent")] -public class AdaptiveComponent : Base, IDisplayValue> -{ - public AdaptiveComponent() { } - - [SchemaInfo("AdaptiveComponent", "Creates a Revit adaptive component by points", "Revit", "Families")] - public AdaptiveComponent( - string type, - string family, - List basePoints, - bool flipped = false, - List? parameters = null - ) - { - this.type = type; - this.family = family; - this.basePoints = basePoints; - this.flipped = flipped; - this.parameters = parameters?.ToBase(); - } - - public string type { get; set; } - public string family { get; set; } - public List basePoints { get; set; } - public bool flipped { get; set; } - public string elementId { get; set; } - public Base? parameters { get; set; } - - public string units { get; set; } - - [DetachProperty] - public List displayValue { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Revit/BuildingPad.cs b/src/Speckle.Objects/BuiltElements/Revit/BuildingPad.cs deleted file mode 100644 index 90854a2d..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/BuildingPad.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.BuildingPad")] -public class BuildingPad : Base, IDisplayValue> -{ - public ICurve outline { get; set; } - - public List voids { get; set; } = new(); - - public string type { get; set; } - - public Level level { get; set; } - - public Base parameters { get; set; } - - public string elementId { get; set; } - - public string units { get; set; } - - [DetachProperty] - public List displayValue { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Revit/Curve/ModelCurves.cs b/src/Speckle.Objects/BuiltElements/Revit/Curve/ModelCurves.cs deleted file mode 100644 index 5e56d80c..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/Curve/ModelCurves.cs +++ /dev/null @@ -1,84 +0,0 @@ -using Speckle.Objects.Utils; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit.Curve; - -[SpeckleType("Objects.BuiltElements.Revit.Curve.ModelCurve")] -public class ModelCurve : Base -{ - public ModelCurve() { } - - [SchemaInfo("ModelCurve", "Creates a Revit model curve", "Revit", "Curves")] - public ModelCurve([SchemaMainParam] ICurve baseCurve, string lineStyle, List? parameters = null) - { - this.baseCurve = baseCurve; - this.lineStyle = lineStyle; - this.parameters = parameters?.ToBase(); - } - - public ICurve baseCurve { get; set; } - public string lineStyle { get; set; } - public Base? parameters { get; set; } - public string elementId { get; set; } - - public string units { get; set; } -} - -[SpeckleType("Objects.BuiltElements.Revit.Curve.DetailCurve")] -public class DetailCurve : Base -{ - public DetailCurve() { } - - [SchemaInfo("DetailCurve", "Creates a Revit detail curve", "Revit", "Curves")] - public DetailCurve([SchemaMainParam] ICurve baseCurve, string lineStyle, List? parameters = null) - { - this.baseCurve = baseCurve; - this.lineStyle = lineStyle; - this.parameters = parameters?.ToBase(); - } - - public ICurve baseCurve { get; set; } - public string lineStyle { get; set; } - public Base? parameters { get; set; } - public string elementId { get; set; } - - public string units { get; set; } -} - -[SpeckleType("Objects.BuiltElements.Revit.Curve.RoomBoundaryLine")] -public class RoomBoundaryLine : Base -{ - public RoomBoundaryLine() { } - - [SchemaInfo("RoomBoundaryLine", "Creates a Revit room boundary line", "Revit", "Curves")] - public RoomBoundaryLine([SchemaMainParam] ICurve baseCurve, List? parameters = null) - { - this.baseCurve = baseCurve; - this.parameters = parameters?.ToBase(); - } - - public ICurve baseCurve { get; set; } - public Base? parameters { get; set; } - public string elementId { get; set; } - public Level level { get; set; } - public string units { get; set; } -} - -[SpeckleType("Objects.BuiltElements.Revit.Curve.SpaceSeparationLine")] -public class SpaceSeparationLine : Base -{ - public SpaceSeparationLine() { } - - [SchemaInfo("SpaceSeparationLine", "Creates a Revit space separation line", "Revit", "Curves")] - public SpaceSeparationLine([SchemaMainParam] ICurve baseCurve, List? parameters = null) - { - this.baseCurve = baseCurve; - this.parameters = parameters?.ToBase(); - } - - public ICurve baseCurve { get; set; } - public Base? parameters { get; set; } - public string elementId { get; set; } - public string units { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Revit/DirectShape.cs b/src/Speckle.Objects/BuiltElements/Revit/DirectShape.cs deleted file mode 100644 index 1f386520..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/DirectShape.cs +++ /dev/null @@ -1,61 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Utils; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.DirectShape")] -public class DirectShape : Base, IDisplayValue> -{ - public DirectShape() { } - - /// - /// Constructs a new instance given a list of objects. - /// - /// The name of the - /// The of this instance. - /// A list of base classes to represent the direct shape (only mesh and brep are allowed, anything else will be ignored.) - /// Optional Parameters for this instance. - [SchemaInfo( - "DirectShape by base geometries", - "Creates a Revit DirectShape using a list of base geometry objects.", - "Revit", - "Families" - )] - public DirectShape(string name, RevitCategory category, List baseGeometries, List? parameters = null) - { - this.name = name; - this.category = category; - this.baseGeometries = baseGeometries.FindAll(IsValidObject); - this.parameters = parameters?.ToBase(); - } - - // moving away from using the RevitCategory Enum - public DirectShape(string name, string builtInCategory, List baseGeometries, List? parameters = null) - { - this.name = name; - this.baseGeometries = baseGeometries.FindAll(IsValidObject); - this.parameters = parameters?.ToBase(); - //TODO: move to typed property alongside all other revit elements - this["builtInCategory"] = builtInCategory; - } - - public string name { get; set; } - public RevitCategory category { get; set; } - public Base? parameters { get; set; } - public string elementId { get; set; } - - [DetachProperty] - public List baseGeometries { get; set; } = new(); - - public string units { get; set; } - - [DetachProperty] - public List displayValue { get; set; } - - public bool IsValidObject(Base @base) - { - return @base is Point || @base is ICurve || @base is Mesh || @base is Brep; - } -} diff --git a/src/Speckle.Objects/BuiltElements/Revit/Enums.cs b/src/Speckle.Objects/BuiltElements/Revit/Enums.cs deleted file mode 100644 index 6be06571..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/Enums.cs +++ /dev/null @@ -1,180 +0,0 @@ -namespace Speckle.Objects.BuiltElements.Revit; - -//This is an enum so that we can easily create a dropdown in GH for schema builder -//NOTE: if edited the list in Objects.Converter.Revit.Categories should be updated too -public enum RevitCategory -{ - AbutmentFoundations = 0, - AbutmentPiles = 1, - AbutmentWalls = 2, - DuctTerminal = 4, - BridgeAbutments = 3, - Alignments = 5, - StructConnectionAnchors = 6, - ApproachSlabs = 7, - BridgeArches = 8, - AudioVisualDevices = 9, - StairsRailingBaluster = 10, - BridgeBearings = 11, - StructConnectionBolts = 12, - BridgeCables = 13, - BridgeDecks = 14, - BridgeFraming = 15, - CableTrayFitting = 16, - CableTrayRun = 17, - CableTray = 18, - Casework = 19, - Ceilings = 20, - Columns = 21, - CommunicationDevices = 22, - ConduitFitting = 23, - Conduit = 24, - Coordination_Model = 25, - BridgeFramingCrossBracing = 26, - CurtainWallPanels = 27, - CurtaSystem = 28, - CurtainWallMullions = 29, - DataDevices = 30, - BridgeFramingDiaphragms = 31, - Doors = 32, - DuctAccessory = 33, - DuctFitting = 34, - PlaceHolderDucts = 35, - DuctSystem = 36, - DuctCurves = 37, - ElectricalEquipment = 38, - ElectricalFixtures = 39, - Entourage = 40, - ExpansionJoints = 41, - FireAlarmDevices = 42, - FireProtection = 43, - Floors = 44, - FoodServiceEquipment = 45, - Furniture = 46, - FurnitureSystems = 47, - GenericAnnotation = 48, - GenericModel = 49, - BridgeGirders = 50, - Hardscape = 51, - LightingDevices = 52, - LightingFixtures = 53, - Lines = 54, - Mass = 55, - MechanicalEquipment = 56, - MedicalEquipment = 57, - NurseCallDevices = 58, - Parking = 59, - Parts = 60, - PierCaps = 61, - PierColumns = 62, - BridgeFoundations = 63, - PierPiles = 64, - BridgeTowers = 65, - PierWalls = 66, - BridgePiers = 67, - PipeAccessory = 68, - PipeFitting = 69, - PlaceHolderPipes = 70, - PipeSegments = 71, - PipeCurves = 72, - PipingSystem = 73, - Planting = 74, - StructConnectionPlates = 75, - PlumbingFixtures = 76, - StructConnectionProfiles = 77, - StairsRailing = 78, - Ramps = 79, - Roads = 80, - Roofs = 81, - SecurityDevices = 82, - StructConnectionShearStuds = 83, - Signage = 84, - Site = 85, - SpecialityEquipment = 86, - Sprinklers = 87, - Stairs = 88, - StructuralFramingSystem = 89, - StructuralColumns = 90, - StructConnections = 91, - FabricAreas = 92, - StructuralFoundation = 93, - StructuralFraming = 94, - Rebar = 95, - Coupler = 96, - StructuralStiffener = 97, - StructuralTendons = 98, - StructuralTruss = 99, - TemporaryStructure = 100, - Topography = 101, - BridgeFramingTrusses = 102, - VerticalCirculation = 103, - VibrationDampers = 104, - VibrationIsolators = 105, - VibrationManagement = 106, - Walls = 107, - StructConnectionWelds = 108, - Windows = 109, - Railings = 110 -} - -/// -/// FamilyDocuments can only be assigned these categories -/// This is a subset of the list above which was manually retrieved from Revit's UI -/// -public enum RevitFamilyCategory -{ - AudioVisualDevices = 9, - CableTrayFitting = 16, - Casework = 19, - Columns = 21, - CommunicationDevices = 22, - ConduitFitting = 23, - DataDevices = 30, - Doors = 32, - DuctAccessory = 33, - DuctFitting = 34, - ElectricalEquipment = 38, - ElectricalFixtures = 39, - Entourage = 40, - FireAlarmDevices = 42, - FireProtection = 43, - FoodServiceEquipment = 45, - Furniture = 46, - FurnitureSystems = 47, - GenericModel = 49, - Hardscape = 51, - LightingDevices = 52, - LightingFixtures = 53, - Mass = 55, - MechanicalEquipment = 56, - MedicalEquipment = 57, - NurseCallDevices = 58, - Parking = 59, - PipeAccessory = 68, - PipeFitting = 69, - Planting = 74, - PlumbingFixtures = 76, - Roads = 80, - SecurityDevices = 82, - Signage = 84, - Site = 85, - SpecialityEquipment = 86, - Sprinklers = 87, - StructuralFramingSystem = 89, - StructuralColumns = 90, - StructConnections = 91, - StructuralFoundation = 93, - StructuralFraming = 94, - StructuralStiffener = 97, - TemporaryStructure = 100, - VerticalCirculation = 103, - Windows = 109, - Railings = 110 -} - -public enum LocationLine -{ - Centerline, - Exterior, - Interior -} diff --git a/src/Speckle.Objects/BuiltElements/Revit/FamilyInstance.cs b/src/Speckle.Objects/BuiltElements/Revit/FamilyInstance.cs deleted file mode 100644 index cb4035bd..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/FamilyInstance.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Utils; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.FamilyInstance")] -public class FamilyInstance : Base, IDisplayValue> -{ - public FamilyInstance() { } - - [SchemaInfo("FamilyInstance", "Creates a Revit family instance", "Revit", "Families")] - public FamilyInstance( - Point basePoint, - string family, - string type, - Level level, - double rotation = 0, - bool facingFlipped = false, - bool handFlipped = false, - List? parameters = null - ) - { - this.basePoint = basePoint; - this.family = family; - this.type = type; - this.level = level; - this.rotation = rotation; - this.facingFlipped = facingFlipped; - this.handFlipped = handFlipped; - mirrored = false; - this.parameters = parameters?.ToBase(); - } - - public Point basePoint { get; set; } - public string family { get; set; } - public string type { get; set; } - public string category { get; set; } - public Level level { get; set; } - public double rotation { get; set; } - public bool facingFlipped { get; set; } - public bool handFlipped { get; set; } - public bool mirrored { get; set; } - public Base? parameters { get; set; } - public string elementId { get; set; } - - [DetachProperty] - public List elements { get; set; } - - public string units { get; set; } - - [DetachProperty] - public List displayValue { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Revit/FreeformElement.cs b/src/Speckle.Objects/BuiltElements/Revit/FreeformElement.cs deleted file mode 100644 index 6ba44237..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/FreeformElement.cs +++ /dev/null @@ -1,140 +0,0 @@ -using Speckle.Newtonsoft.Json; -using Speckle.Objects.Geometry; -using Speckle.Objects.Utils; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.FreeformElement")] -public class FreeformElement : Base, IDisplayValue> -{ - public FreeformElement() { } - - [SchemaInfo( - "Freeform element", - "Creates a Revit Freeform element using a list of Brep or Meshes. Category defaults to Generic Models", - "Revit", - "Families" - )] - public FreeformElement(List baseGeometries, string subcategory = "", List? parameters = null) - { - this.baseGeometries = baseGeometries; - //this.category = category; - this.subcategory = subcategory; - if (!IsValid()) - { - throw new Exception("Freeform elements can only be created from BREPs or Meshes"); - } - - this.parameters = parameters?.ToBase(); - } - - public Base? parameters { get; set; } - - public string subcategory { get; set; } - - public string elementId { get; set; } - - /// - /// DEPRECATED. Sets the geometry contained in the FreeformElement. This field has been deprecated in favor of `baseGeometries` - /// to align with Revit's API. It remains as a setter-only property for backwards compatibility. - /// It will set the first item on the baseGeometries list, and instantiate a list if necessary. - /// - [JsonIgnore, SchemaIgnore, Obsolete("Use 'baseGeometries' instead", true)] - [System.Diagnostics.CodeAnalysis.SuppressMessage( - "Design", - "CA1044:Properties should not be write only", - Justification = "Obsolete" - )] - public Base baseGeometry - { - set - { - if (baseGeometries == null) - { - baseGeometries = new List { value }; - } - else if (baseGeometries.Count == 0) - { - baseGeometries.Add(value); - } - else - { - baseGeometries[0] = value; - } - } - } - - [DetachProperty, Chunkable] - public List baseGeometries { get; set; } - - public string units { get; set; } - - [DetachProperty] - public List displayValue { get; set; } - - public bool IsValid() - { - return baseGeometries.All(IsValidObject); - } - - public bool IsValidObject(Base @base) - { - return @base is Mesh || @base is Brep || @base is Geometry.Curve; - } - - #region Deprecated Constructors - - [ - SchemaDeprecated, - SchemaInfo( - "Freeform element", - "Creates a Revit Freeform element using a list of Brep or Meshes.", - "Revit", - "Families" - ) - ] - [System.Diagnostics.CodeAnalysis.SuppressMessage( - "Usage", - "CA2201:Do not raise reserved exception types", - Justification = "Obsolete" - )] - public FreeformElement(Base baseGeometry, List? parameters = null) - { - if (!IsValidObject(baseGeometry)) - { - throw new Exception("Freeform elements can only be created from BREPs or Meshes"); - } - - baseGeometries = new List { baseGeometry }; - this.parameters = parameters?.ToBase(); - } - - [ - SchemaDeprecated, - SchemaInfo( - "Freeform element", - "Creates a Revit Freeform element using a list of Brep or Meshes.", - "Revit", - "Families" - ) - ] - [System.Diagnostics.CodeAnalysis.SuppressMessage( - "Usage", - "CA2201:Do not raise reserved exception types", - Justification = "Obsolete" - )] - public FreeformElement(List baseGeometries, List? parameters = null) - { - this.baseGeometries = baseGeometries; - if (!IsValid()) - { - throw new Exception("Freeform elements can only be created from BREPs or Meshes"); - } - - this.parameters = parameters?.ToBase(); - } - - #endregion -} diff --git a/src/Speckle.Objects/BuiltElements/Revit/Interfaces/IHasMEPConnectors.cs b/src/Speckle.Objects/BuiltElements/Revit/Interfaces/IHasMEPConnectors.cs deleted file mode 100644 index 5d12fe07..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/Interfaces/IHasMEPConnectors.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Speckle.Objects.BuiltElements.Revit.Interfaces; - -public interface IHasMEPConnectors -{ - List Connectors { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Revit/MEPFamilyInstance.cs b/src/Speckle.Objects/BuiltElements/Revit/MEPFamilyInstance.cs deleted file mode 100644 index 0e522f1b..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/MEPFamilyInstance.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Speckle.Objects.BuiltElements.Revit.Interfaces; -using Speckle.Objects.Other.Revit; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.RevitMEPFamilyInstance")] -public class RevitMEPFamilyInstance : RevitInstance, IHasMEPConnectors -{ - public string RevitPartType { get; set; } - - [DetachProperty] - public List Connectors { get; set; } = new(); - public List Curves { get; set; } = new(); -} diff --git a/src/Speckle.Objects/BuiltElements/Revit/Parameter.cs b/src/Speckle.Objects/BuiltElements/Revit/Parameter.cs deleted file mode 100644 index 0f71131d..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/Parameter.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.Parameter")] -public class Parameter : Base -{ - public Parameter() { } - - [SchemaInfo("Parameter", "A Revit instance parameter to set on an element", "Revit", "Families")] - public Parameter( - [SchemaParamInfo("The Revit display name, BuiltInParameter name or GUID (for shared parameters)")] string name, - object value, - [SchemaParamInfo( - "(Optional) Speckle units. If not set it's retrieved from the current document. For non lenght based parameters (eg. Air Flow) it should be set to 'none' so that the Revit display unit will be used instead." - )] - string units = "" - ) - { - this.name = name; - this.value = value; - this.units = units; - applicationInternalName = name; - } - - public string name { get; set; } - public object? value { get; set; } - public string applicationUnitType { get; set; } //eg UnitType UT_Length - public string applicationUnit { get; set; } //DisplayUnitType eg DUT_MILLIMITERS - public string applicationInternalName { get; set; } //BuiltInParameterName or GUID for shared parameter - - /// - /// If True it's a Shared Parameter, in which case the ApplicationId field will contain this parameter GUID, - /// otherwise it will store its BuiltInParameter name - /// - public bool isShared { get; set; } - - public bool isReadOnly { get; set; } - - /// - /// True = Type Parameter, False = Instance Parameter - /// - public bool isTypeParameter { get; set; } - - public string units { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Revit/ParameterUpdater.cs b/src/Speckle.Objects/BuiltElements/Revit/ParameterUpdater.cs deleted file mode 100644 index 0f7e86b8..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/ParameterUpdater.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Speckle.Objects.Utils; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.ParameterUpdater")] -public class ParameterUpdater : Base -{ - [SchemaInfo("ParameterUpdater", "Updates parameters on a Revit element by id", "Revit", "Families")] - public ParameterUpdater([SchemaParamInfo("A Revit ElementId or UniqueId")] string id, List parameters) - { - elementId = id; - this.parameters = parameters.ToBase(); - } - - public ParameterUpdater() { } - - public string elementId { get; set; } - public Base? parameters { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Revit/ProjectInfo.cs b/src/Speckle.Objects/BuiltElements/Revit/ProjectInfo.cs deleted file mode 100644 index 815239b0..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/ProjectInfo.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Speckle.Objects.Organization; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.ProjectInfo")] -public class ProjectInfo : BIMModelInfo -{ - public string author { get; set; } - public string issueDate { get; set; } - public string organizationDescription { get; set; } - public string organizationName { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Revit/RevitBeam.cs b/src/Speckle.Objects/BuiltElements/Revit/RevitBeam.cs deleted file mode 100644 index aac5875e..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/RevitBeam.cs +++ /dev/null @@ -1,53 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Utils; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.RevitBeam")] -public class RevitBeam : Beam -{ - public RevitBeam() { } - - public RevitBeam( - string family, - string type, - ICurve baseLine, - Level? level, - string? units, - List? displayValue = null, - List? parameters = null - ) - : base(baseLine, level, units, displayValue) - { - this.family = family; - this.type = type; - 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 new Level? level - { - 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 deleted file mode 100644 index d4b0a9e8..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/RevitBrace.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Utils; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.RevitBrace")] -public class RevitBrace : Brace -{ - public RevitBrace() { } - - public RevitBrace( - string family, - string type, - 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.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 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/RevitCableTray.cs b/src/Speckle.Objects/BuiltElements/Revit/RevitCableTray.cs deleted file mode 100644 index b217d24e..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/RevitCableTray.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Speckle.Objects.BuiltElements.Revit.Interfaces; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.RevitCableTray")] -public class RevitCableTray : CableTray, IHasMEPConnectors -{ - public string family { get; set; } - public string type { get; set; } - public Level level { get; set; } - public Base parameters { get; set; } - public string elementId { get; set; } - public List Connectors { get; set; } = new(); -} diff --git a/src/Speckle.Objects/BuiltElements/Revit/RevitCeiling.cs b/src/Speckle.Objects/BuiltElements/Revit/RevitCeiling.cs deleted file mode 100644 index 9e9fce06..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/RevitCeiling.cs +++ /dev/null @@ -1,73 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.RevitCeiling")] -public class RevitCeiling : Ceiling -{ - public RevitCeiling() { } - - [SchemaDeprecated, SchemaInfo("RevitCeiling", "Creates a Revit ceiling", "Revit", "Architecture")] - [System.Diagnostics.CodeAnalysis.SuppressMessage( - "Style", - "IDE0060:Remove unused parameter", - Justification = "Obsolete" - )] - public RevitCeiling( - [SchemaMainParam, SchemaParamInfo("Planar boundary curve")] ICurve outline, - string family, - string type, - Level level, - double slope = 0, - [SchemaParamInfo("Planar line indicating slope direction")] Line? slopeDirection = null, - double offset = 0, - List? voids = null, - [SchemaParamInfo("Any nested elements that this ceiling might have")] List? elements = null - ) - { - this.outline = outline; - this.family = family; - this.type = type; - this.level = level; - this.slope = slope; - this.slopeDirection = slopeDirection; - this.voids = voids ?? new(); - this.elements = elements; - } - - [SchemaInfo("RevitCeiling", "Creates a Revit ceiling", "Revit", "Architecture")] - public RevitCeiling( - [SchemaMainParam, SchemaParamInfo("Planar boundary curve")] ICurve outline, - string family, - string type, - Level level, - double slope = 0, - [SchemaParamInfo("Planar line indicating slope direction")] Line? slopeDirection = null, - List? voids = null, - [SchemaParamInfo("Any nested elements that this ceiling might have")] List? elements = null - ) - { - this.outline = outline; - this.family = family; - this.type = type; - this.level = level; - this.slope = slope; - this.slopeDirection = slopeDirection; - this.voids = voids ?? new(); - this.elements = elements; - } - - public string family { get; set; } - public string type { get; set; } - public Level level { get; set; } - public double slope { get; set; } - public Line? slopeDirection { get; set; } - - [Obsolete("Offset property is now captured in parameters to match the behavior of other Revit objects", true)] - public double offset { get; set; } - - public Base parameters { get; set; } - public string elementId { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Revit/RevitColumn.cs b/src/Speckle.Objects/BuiltElements/Revit/RevitColumn.cs deleted file mode 100644 index 24a9c5a1..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/RevitColumn.cs +++ /dev/null @@ -1,130 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Utils; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.RevitColumn")] -public class RevitColumn : Column -{ - public RevitColumn() { } - - public RevitColumn( - string family, - string type, - ICurve baseLine, - Level? level, - Level? topLevel, - string? units, - string? elementId, - double baseOffset = 0, - double topOffset = 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.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(); - } - - 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; } - - public new Level? level - { - get => base.level; - set => base.level = value; - } - - #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", - Justification = "Obsolete" - )] - public RevitColumn( - string family, - string type, - [SchemaMainParam] ICurve baseLine, - Level level, - bool structural = false, - List? parameters = null - ) - { - this.family = family; - this.type = type; - this.baseLine = baseLine; - this.level = level; - isSlanted = true; - this.parameters = parameters?.ToBase(); - } - - [SchemaInfo("RevitColumn Slanted", "Creates a slanted Revit Column by curve.", "Revit", "Structure")] - public RevitColumn( - string family, - string type, - [SchemaMainParam] ICurve baseLine, - Level level, - Level? topLevel = null, - bool structural = false, - List? parameters = null - ) - : this(family, type, baseLine, level, topLevel, null, null, displayValue: null, parameters: parameters) { } - - #endregion -} diff --git a/src/Speckle.Objects/BuiltElements/Revit/RevitConduit.cs b/src/Speckle.Objects/BuiltElements/Revit/RevitConduit.cs deleted file mode 100644 index 3e488c1e..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/RevitConduit.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Speckle.Objects.BuiltElements.Revit.Interfaces; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.RevitConduit")] -public class RevitConduit : Conduit, IHasMEPConnectors -{ - public string family { get; set; } - - public string type { get; set; } - - public Level level { get; set; } - - public Base parameters { get; set; } - - public string elementId { get; set; } - public List Connectors { get; set; } = new(); -} diff --git a/src/Speckle.Objects/BuiltElements/Revit/RevitCurtainWallPanel.cs b/src/Speckle.Objects/BuiltElements/Revit/RevitCurtainWallPanel.cs deleted file mode 100644 index 56813834..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/RevitCurtainWallPanel.cs +++ /dev/null @@ -1,6 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.RevitCurtainWallPanel")] -public class RevitCurtainWallPanel : RevitElement { } diff --git a/src/Speckle.Objects/BuiltElements/Revit/RevitDuct.cs b/src/Speckle.Objects/BuiltElements/Revit/RevitDuct.cs deleted file mode 100644 index a111d888..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/RevitDuct.cs +++ /dev/null @@ -1,204 +0,0 @@ -using Speckle.Objects.BuiltElements.Revit.Interfaces; -using Speckle.Objects.Geometry; -using Speckle.Objects.Utils; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.RevitDuct")] -public class RevitDuct : Duct, IHasMEPConnectors -{ - public RevitDuct() { } - - public RevitDuct( - string family, - string type, - 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) - { - this.family = family; - this.type = type; - this.systemName = systemName; - this.systemType = systemType; - this.level = level; - this.parameters = parameters?.ToBase(); - this.elementId = elementId; - } - - 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] Line baseLine, - string systemName, - string systemType, - Level level, - double width, - double height, - double diameter, - double velocity = 0, - List? parameters = null - ) - { - 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; - } - - [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 -} - -[SpeckleType("Objects.BuiltElements.Revit.RevitFlexDuct")] -public class RevitFlexDuct : RevitDuct -{ - public RevitFlexDuct() { } - - public RevitFlexDuct( - string family, - string type, - [SchemaMainParam] ICurve baseCurve, - string systemName, - string systemType, - Level level, - 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.startTangent = startTangent; - this.endTangent = endTangent; - } - - 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/RevitElement.cs b/src/Speckle.Objects/BuiltElements/Revit/RevitElement.cs deleted file mode 100644 index fd498ffb..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/RevitElement.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -/// -/// A generic Revit element for which we don't have direct conversions -/// -[SpeckleType("Objects.BuiltElements.Revit.RevitElement")] -public class RevitElement : Base, IDisplayValue> -{ - public string family { get; set; } - public string type { get; set; } - public string category { get; set; } - public Base parameters { get; set; } - public string elementId { get; set; } - - [DetachProperty] - public List displayValue { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Revit/RevitElementType.cs b/src/Speckle.Objects/BuiltElements/Revit/RevitElementType.cs deleted file mode 100644 index 42e97636..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/RevitElementType.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.RevitElementType")] -public class RevitElementType : Base -{ - public string family { get; set; } - - public string type { get; set; } - - public string category { get; set; } -} - -[SpeckleType("Objects.BuiltElements.Revit.RevitMepElementType")] -public class RevitMepElementType : RevitElementType -{ - public string shape { get; set; } -} - -/// -/// Represents the FamilySymbol subclass of ElementType in Revit -/// -[SpeckleType("Objects.BuiltElements.Revit.RevitSymbolElementType")] -public class RevitSymbolElementType : RevitElementType, IDisplayValue> -{ - /// - /// The type of placement for this family symbol - /// - /// See https://www.revitapidocs.com/2023/2abb8627-1da3-4069-05c9-19e4be5e02ad.htm - public string placementType { get; set; } - - /// - /// Subcomponents found in this family symbol - /// - [DetachProperty] - public List elements { get; set; } - - [DetachProperty] - public List displayValue { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Revit/RevitFloor.cs b/src/Speckle.Objects/BuiltElements/Revit/RevitFloor.cs deleted file mode 100644 index 842d9e0d..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/RevitFloor.cs +++ /dev/null @@ -1,53 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Utils; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.RevitFloor")] -public class RevitFloor : Floor -{ - public RevitFloor() { } - - [SchemaInfo("RevitFloor", "Creates a Revit floor by outline and level", "Revit", "Architecture")] - public RevitFloor( - string family, - string type, - [SchemaMainParam] ICurve outline, - Level level, - bool structural = false, - double slope = 0, - Line? slopeDirection = null, - List? voids = null, - [SchemaParamInfo("Any nested elements that this floor might have")] List? elements = null, - List? parameters = null - ) - { - this.family = family; - this.type = type; - this.level = level; - this.structural = structural; - this.slope = slope; - this.slopeDirection = slopeDirection; - this.parameters = parameters?.ToBase(); - this.outline = outline; - this.voids = voids ?? new(); - this.elements = elements; - } - - public string family { get; set; } - public string type { get; set; } - - public new Level? level - { - get => base.level; - set => base.level = value; - } - - public bool structural { get; set; } - public double slope { get; set; } - public Line? slopeDirection { get; set; } - public Base? parameters { get; set; } - public string elementId { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Revit/RevitLevel.cs b/src/Speckle.Objects/BuiltElements/Revit/RevitLevel.cs deleted file mode 100644 index d04e2113..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/RevitLevel.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Speckle.Objects.Utils; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.RevitLevel")] -public class RevitLevel : Level -{ - public RevitLevel() { } - - /// - /// SchemaBuilder constructor for a Revit level - /// - /// - /// - /// - /// - /// Assign units when using this constructor due to param - [SchemaInfo( - "RevitLevel", - "Creates a new Revit level unless one with the same elevation already exists", - "Revit", - "Architecture" - )] - public RevitLevel( - [SchemaParamInfo("Level name. NOTE: updating level name is not supported")] string name, - [SchemaParamInfo( - "Level elevation. NOTE: updating level elevation is not supported, a new one will be created unless another level at the new elevation already exists." - )] - double elevation, - [SchemaParamInfo( - "If true, it creates an associated view in Revit. NOTE: only used when creating a level for the first time" - )] - bool createView, - List? parameters = null - ) - { - this.name = name; - this.elevation = elevation; - this.createView = createView; - this.parameters = parameters?.ToBase(); - referenceOnly = false; - } - - [SchemaInfo("RevitLevel by name", "Gets an existing Revit level by name", "Revit", "Architecture")] - public RevitLevel(string name) - { - this.name = name; - referenceOnly = true; - } - - public bool createView { get; set; } - public Base? parameters { get; set; } - public string elementId { get; set; } - public bool referenceOnly { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Revit/RevitMEPConnector.cs b/src/Speckle.Objects/BuiltElements/Revit/RevitMEPConnector.cs deleted file mode 100644 index cdeab8ce..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/RevitMEPConnector.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.RevitMEPConnector")] -public class RevitMEPConnector : Base -{ - public double angle { get; set; } - public List connectedConnectorIds { get; set; } = new(); - public double height { get; set; } - public Point origin { get; set; } - public double radius { get; set; } - public string shape { get; set; } - public string systemName { get; set; } - public double width { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Revit/RevitNetwork.cs b/src/Speckle.Objects/BuiltElements/Revit/RevitNetwork.cs deleted file mode 100644 index 4394faa4..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/RevitNetwork.cs +++ /dev/null @@ -1,78 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[Obsolete( - "Networks are no longer used to assemble MEP systems in Revit. See the RevitCommitBuilder for MEP systems conversion." -)] -[SpeckleType("Objects.BuiltElements.Revit.RevitNetworkElement")] -public class RevitNetworkElement : NetworkElement -{ - public RevitNetworkElement() { } - - /// - /// Indicates if this element was constructed from an MEP curve - /// - public bool isCurveBased { get; set; } - - /// - /// Indicates if this element needs temporary placeholder objects to be created first when receiving - /// - /// - /// For example, some fittings cannot be created based on connectors, and so will be created similarly to mechanical equipment - /// - public bool isConnectorBased { get; set; } -} - -[Obsolete( - "Networks are no longer used to assemble MEP systems in Revit. See the RevitCommitBuilder for MEP systems conversion." -)] -[SpeckleType("Objects.BuiltElements.Revit.RevitNetworkLink")] -public class RevitNetworkLink : NetworkLink -{ - public double height { get; set; } - public double width { get; set; } - public double diameter { get; set; } - public Point origin { get; set; } - public Vector direction { get; set; } - - /// - /// The system category - /// - public string systemName { get; set; } - - public string systemType { get; set; } - - /// - /// The connector profile shape of the - /// - public string shape { get; set; } - - /// - /// The link domain - /// - public string domain { get; set; } - - /// - /// The index indicating the position of this link on the connected fitting element, if applicable - /// - /// - /// Revit fitting links are 1-indexed. For example, "T" fittings will have ordered links from index 1-3. - /// - public int fittingIndex { get; set; } - - /// - /// Indicates if this link needs temporary placeholder objects to be created first when receiving - /// - /// - /// Placeholder geometry are curves. - /// For example, U-bend links need temporary pipes to be created first, if one or more linked pipes have not yet been created in the network. - /// - public bool needsPlaceholders { get; set; } - - /// - /// Indicates if this link has been connected to its elements - /// - public bool isConnected { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Revit/RevitOpening.cs b/src/Speckle.Objects/BuiltElements/Revit/RevitOpening.cs deleted file mode 100644 index 94f617a5..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/RevitOpening.cs +++ /dev/null @@ -1,109 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Utils; -using Speckle.Sdk; -using Speckle.Sdk.Host; -using Speckle.Sdk.Logging; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.RevitOpening")] -public class RevitOpening : Opening -{ - public Base? parameters { get; set; } - public string elementId { get; set; } -} - -[SpeckleType("Objects.BuiltElements.Revit.RevitVerticalOpening")] -public class RevitVerticalOpening : RevitOpening { } - -[SpeckleType("Objects.BuiltElements.Revit.RevitWallOpening")] -public class RevitWallOpening : RevitOpening -{ - public RevitWallOpening() { } - - [ - Obsolete("Use constructor with Polyline input instead"), - SchemaDeprecated, - SchemaInfo("Revit Wall Opening (Deprecated)", "Creates a Speckle Wall opening for revit", "BIM", "Architecture") - ] - public RevitWallOpening(ICurve outline, RevitWall? host = null) - { - if (outline is not Polyline) - { - throw new SpeckleException("Outline should be a rectangular-shaped polyline"); - } - - this.outline = outline; - this.host = host; - } - - [SchemaInfo("Revit Wall Opening", "Creates a Speckle Wall opening for revit", "Revit", "Architecture")] - public RevitWallOpening(Polyline outline, RevitWall? host = null) - { - if (outline == null) - { - throw new SpeckleException("Outline cannot be null"); - } - - if (outline.GetPoints().Count != 4) - { - throw new SpeckleException("Outline should be a rectangular-shaped polyline"); - } - - this.outline = outline; - this.host = host; - } - - public RevitWall? host { get; set; } -} - -[SpeckleType("Objects.BuiltElements.Revit.RevitShaft")] -public class RevitShaft : RevitOpening -{ - public RevitShaft() { } - - /// - /// SchemaBuilder constructor for a Revit shaft - /// - /// - /// - /// - /// - [SchemaInfo("RevitShaft", "Creates a Revit shaft from a bottom and top level", "Revit", "Architecture")] - public RevitShaft( - [SchemaMainParam] ICurve outline, - Level bottomLevel, - Level topLevel, - List? parameters = null - ) - { - this.outline = outline; - this.bottomLevel = bottomLevel; - this.topLevel = topLevel; - this.parameters = parameters?.ToBase(); - } - - public Level bottomLevel { get; set; } - public Level topLevel { get; set; } - public double height { get; set; } - - /* - /// - /// SchemaBuilder constructor for a Revit shaft - /// - /// - /// - /// - /// - /// Assign units when using this constructor due to param - [SchemaInfo("RevitShaft", "Creates a Revit shaft from a bottom level and height")] - public RevitShaft(ICurve outline, Level bottomLevel, double height, List parameters = null) - { - this.outline = outline; - this.bottomLevel = bottomLevel; - this.height = height; - this.parameters = parameters.ToBase(); - } - */ -} diff --git a/src/Speckle.Objects/BuiltElements/Revit/RevitPipe.cs b/src/Speckle.Objects/BuiltElements/Revit/RevitPipe.cs deleted file mode 100644 index 393a4551..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/RevitPipe.cs +++ /dev/null @@ -1,79 +0,0 @@ -using Speckle.Objects.BuiltElements.Revit.Interfaces; -using Speckle.Objects.Geometry; -using Speckle.Objects.Utils; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.RevitPipe")] -public class RevitPipe : Pipe, IHasMEPConnectors -{ - public RevitPipe() { } - - [SchemaInfo("RevitPipe", "Creates a Revit pipe", "Revit", "MEP")] - public RevitPipe( - string family, - string type, - [SchemaMainParam] ICurve baseCurve, - double diameter, - Level level, - string systemName = "", - string systemType = "", - List? parameters = null - ) - { - this.family = family; - this.type = type; - this.baseCurve = baseCurve; - this.diameter = diameter; - this.systemName = systemName; - this.systemType = systemType; - this.level = level; - this.parameters = parameters?.ToBase(); - } - - public string family { get; set; } - public string type { get; set; } - public string systemName { get; set; } - public string systemType { get; set; } - public Base? parameters { get; set; } - public string elementId { get; set; } - public Level level { get; set; } - public List Connectors { get; set; } = new(); -} - -[SpeckleType("Objects.BuiltElements.Revit.RevitFlexPipe")] -public class RevitFlexPipe : RevitPipe -{ - public RevitFlexPipe() { } - - [SchemaInfo("RevitFlexPipe", "Creates a Revit flex pipe", "Revit", "MEP")] - public RevitFlexPipe( - string family, - string type, - [SchemaMainParam] ICurve baseCurve, - double diameter, - Level level, - Vector startTangent, - Vector endTangent, - string systemName = "", - string systemType = "", - List? parameters = null - ) - { - this.family = family; - this.type = type; - this.baseCurve = baseCurve; - this.diameter = diameter; - this.startTangent = startTangent; - this.endTangent = endTangent; - this.systemName = systemName; - this.systemType = systemType; - this.level = level; - this.parameters = parameters?.ToBase(); - } - - public Vector startTangent { get; set; } - public Vector endTangent { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Revit/RevitRailing.cs b/src/Speckle.Objects/BuiltElements/Revit/RevitRailing.cs deleted file mode 100644 index 997205f5..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/RevitRailing.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.RevitRailing")] -public class RevitRailing : Base, IDisplayValue> -{ - public RevitRailing() { } - - [SchemaInfo("Railing", "Creates a Revit railing by base curve.", "Revit", "Architecture")] - public RevitRailing(string type, [SchemaMainParam] Polycurve baseCurve, Level level, bool flipped = false) - { - this.type = type; - path = baseCurve; - this.level = level; - this.flipped = flipped; - } - - //public string family { get; set; } - public string type { get; set; } - public Level level { get; set; } - public Polycurve path { get; set; } - public bool flipped { get; set; } - public string elementId { get; set; } - public Base parameters { get; set; } - public RevitTopRail topRail { get; set; } - - public string units { get; set; } - - [DetachProperty] - public List displayValue { get; set; } -} - -// Used only to transfer parameters of the top railing -// its display mesh will live in the main railing element -[SpeckleType("Objects.BuiltElements.Revit.RevitTopRail")] -public class RevitTopRail : Base -{ - //public string family { get; set; } - public string type { get; set; } - public string elementId { get; set; } - public Base parameters { get; set; } - - [DetachProperty] - public List displayValue { get; set; } - - public string units { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Revit/RevitRebar.cs b/src/Speckle.Objects/BuiltElements/Revit/RevitRebar.cs deleted file mode 100644 index f542496c..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/RevitRebar.cs +++ /dev/null @@ -1,87 +0,0 @@ -using Speckle.Newtonsoft.Json; -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.RevitRebarGroup")] -public class RevitRebarGroup : RebarGroup -{ - public RevitRebarGroup() { } - - [JsonIgnore] - public RevitRebarShape revitShape { get; set; } - - public override RebarHook? startHook - { - get => revitStartHook; - set => - revitStartHook = value switch - { - RevitRebarHook o => o, - null => null, - _ => throw new ArgumentException($"Expected object of type {nameof(RevitRebarHook)} or null"), - }; - } - - [JsonIgnore] - public RevitRebarHook? revitStartHook { get; set; } - - public override RebarHook? endHook - { - get => revitEndHook; - set => - revitEndHook = value switch - { - RevitRebarHook o => o, - null => null, - _ => throw new ArgumentException($"Expected object of type {nameof(RevitRebarHook)} or null"), - }; - } - - [JsonIgnore] - public RevitRebarHook? revitEndHook { get; set; } - - public string family { get; set; } - public string type { get; set; } - public int barPositions { get; set; } - public Vector normal { get; set; } - public Base parameters { get; set; } - public string elementId { get; set; } -} - -[SpeckleType("Objects.BuiltElements.Revit.RevitRebarShape")] -public class RevitRebarShape : RebarShape -{ - public RevitRebarShape() { } - - public Base parameters { get; set; } - public string elementId { get; set; } -} - -[SpeckleType("Objects.BuiltElements.Revit.RevitRebarHook")] -public class RevitRebarHook : RebarHook -{ - public RevitRebarHook() { } - - public double multiplier { get; set; } - public string orientation { get; set; } - public Base parameters { get; set; } - public string elementId { get; set; } -} - -#region Obsolete -[Obsolete("Deprecated in 2.17: Use RevitRebarGroup class instead")] -[SpeckleType("Objects.BuiltElements.Revit.RevitRebar")] -public class RevitRebar : Rebar -{ - public string family { get; set; } - public string type { get; set; } - public string host { get; set; } - public string barType { get; set; } - public string barStyle { get; set; } - public List shapes { get; set; } - public Base parameters { get; set; } - public string elementId { get; set; } -} -#endregion diff --git a/src/Speckle.Objects/BuiltElements/Revit/RevitRoof/RevitRoof.cs b/src/Speckle.Objects/BuiltElements/Revit/RevitRoof/RevitRoof.cs deleted file mode 100644 index 967bbb64..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/RevitRoof/RevitRoof.cs +++ /dev/null @@ -1,98 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Utils; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit.RevitRoof; - -[SpeckleType("Objects.BuiltElements.Revit.RevitRoof.RevitRoof")] -public class RevitRoof : Roof -{ - public string family { get; set; } - public string type { get; set; } - public Base? parameters { get; set; } - public string elementId { get; set; } - - public new Level? level - { - get => base.level; - set => base.level = value; - } -} - -[SpeckleType("Objects.BuiltElements.Revit.RevitRoof.RevitExtrusionRoof")] -public class RevitExtrusionRoof : RevitRoof -{ - public RevitExtrusionRoof() { } - - /// - /// SchemaBuilder constructor for a Revit extrusion roof - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Assign units when using this constructor due to and params - [SchemaInfo("RevitExtrusionRoof", "Creates a Revit roof by extruding a curve", "Revit", "Architecture")] - public RevitExtrusionRoof( - string family, - string type, - [SchemaParamInfo("Extrusion start")] double start, - [SchemaParamInfo("Extrusion end")] double end, - [SchemaParamInfo("Profile along which to extrude the roof"), SchemaMainParam] Line referenceLine, - Level level, - List? elements = null, - List? parameters = null - ) - { - this.family = family; - this.type = type; - this.parameters = parameters?.ToBase(); - this.level = level; - this.start = start; - this.end = end; - this.referenceLine = referenceLine; - this.elements = elements; - } - - public double start { get; set; } - public double end { get; set; } - public Line referenceLine { get; set; } -} - -[SpeckleType("Objects.BuiltElements.Revit.RevitRoof.RevitFootprintRoof")] -public class RevitFootprintRoof : RevitRoof -{ - public RevitFootprintRoof() { } - - [SchemaInfo("RevitFootprintRoof", "Creates a Revit roof by outline", "Revit", "Architecture")] - public RevitFootprintRoof( - [SchemaMainParam] ICurve outline, - string family, - string type, - Level level, - RevitLevel? cutOffLevel = null, - double slope = 0, - List? voids = null, - List? elements = null, - List? parameters = null - ) - { - this.outline = outline; - this.voids = voids ?? new(); - this.family = family; - this.type = type; - this.slope = slope; - this.parameters = parameters?.ToBase(); - this.level = level; - this.cutOffLevel = cutOffLevel; - this.elements = elements; - } - - public RevitLevel? cutOffLevel { get; set; } - public double? slope { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Revit/RevitStair.cs b/src/Speckle.Objects/BuiltElements/Revit/RevitStair.cs deleted file mode 100644 index 09e5ebfa..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/RevitStair.cs +++ /dev/null @@ -1,82 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.RevitStair")] -public class RevitStair : Base, IDisplayValue> -{ - public string family { get; set; } - public string type { get; set; } - public Level level { get; set; } - public Level topLevel { get; set; } - public double riserHeight { get; set; } - public int risersNumber { get; set; } - public double treadDepth { get; set; } - public int treadsNumber { get; set; } - public double baseElevation { get; set; } - public double topElevation { get; set; } - public bool beginsWithRiser { get; set; } - public double height { get; set; } - public int numberOfStories { get; set; } - public Base parameters { get; set; } - public List runs { get; set; } - public List landings { get; set; } - public List supports { get; set; } - public string elementId { get; set; } - - public string units { get; set; } - - [DetachProperty] - public List displayValue { get; set; } -} - -[SpeckleType("Objects.BuiltElements.Revit.RevitStairRun")] -public class RevitStairRun : Base -{ - public string family { get; set; } - public string type { get; set; } - public Polycurve path { get; set; } - public Polycurve outline { get; set; } - public double runWidth { get; set; } - public int risersNumber { get; set; } - public int treadsNumber { get; set; } - public double baseElevation { get; set; } - public double topElevation { get; set; } - public bool beginsWithRiser { get; set; } - public bool endsWithRiser { get; set; } - public double extensionBelowRiserBase { get; set; } - public double extensionBelowTreadBase { get; set; } - public double height { get; set; } - public string runStyle { get; set; } - public Base parameters { get; set; } - public string elementId { get; set; } - - public string units { get; set; } -} - -[SpeckleType("Objects.BuiltElements.Revit.RevitStairLanding")] -public class RevitStairLanding : Base -{ - public string family { get; set; } - public string type { get; set; } - public bool isAutomaticLanding { get; set; } - public double baseElevation { get; set; } - public double thickness { get; set; } - public Polycurve outline { get; set; } - public Base parameters { get; set; } - public string elementId { get; set; } - - public string units { get; set; } -} - -[SpeckleType("Objects.BuiltElements.Revit.RevitStairSupport")] -public class RevitStairSupport : Base -{ - public string family { get; set; } - public string type { get; set; } - public Base parameters { get; set; } - public string elementId { get; set; } - - public string units { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Revit/RevitTopography.cs b/src/Speckle.Objects/BuiltElements/Revit/RevitTopography.cs deleted file mode 100644 index 81faa748..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/RevitTopography.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Utils; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.RevitTopography")] -public class RevitTopography : Topography -{ - public RevitTopography() { } - - [SchemaInfo("RevitTopography", "Creates a Revit topography", "Revit", "Architecture")] - public RevitTopography([SchemaMainParam] Mesh displayMesh, List? parameters = null) - { - displayValue = new List { displayMesh }; - this.parameters = parameters?.ToBase(); - } - - public string elementId { get; set; } - public Base? parameters { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Revit/RevitToposolid.cs b/src/Speckle.Objects/BuiltElements/Revit/RevitToposolid.cs deleted file mode 100644 index 94061125..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/RevitToposolid.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Utils; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.RevitToposolid")] -public class RevitToposolid : Base, IDisplayValue> -{ - public RevitToposolid() { } - - [SchemaInfo("RevitToposolid", "Creates a Revit Toposolid", "Revit", "Architecture")] - public RevitToposolid( - Level level, - List profiles, - List? topPlanePoints = null, - [SchemaParamInfo("Any nested elements that this floor might have")] List? elements = null, - List? parameters = null - ) - { - this.profiles = profiles; - this.level = level; - this.points = topPlanePoints ?? new(); - this.elements = elements; - this.parameters = parameters?.ToBase(); - } - - public List profiles { get; set; } = new(); - - public List points { get; set; } = new(); - - [DetachProperty] - public List? elements { get; set; } - - [DetachProperty] - public List displayValue { get; set; } - - public string family { get; set; } - public string type { get; set; } - public Level level { get; set; } - public Base? parameters { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Revit/RevitWall.cs b/src/Speckle.Objects/BuiltElements/Revit/RevitWall.cs deleted file mode 100644 index 741fb82e..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/RevitWall.cs +++ /dev/null @@ -1,252 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Utils; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.RevitWall")] -public class RevitWall : Wall -{ - public RevitWall() { } - - public RevitWall( - string family, - string type, - ICurve baseLine, - Level level, - Level? topLevel, - double height, - string? units, - string? elementId, - double baseOffset = 0, - double topOffset = 0, - bool flipped = false, - bool structural = false, - IReadOnlyList? displayValue = null, - List? elements = null, - List? parameters = null - ) - : base(height, units, baseLine, level, displayValue, elements) - { - this.family = family; - this.type = type; - this.baseOffset = baseOffset; - this.topOffset = topOffset; - this.flipped = flipped; - this.structural = structural; - this.elementId = elementId; - this.topLevel = topLevel; - 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 Level? topLevel { get; set; } - public Base? parameters { get; set; } - public string? elementId { get; set; } - - public new Level? level - { - get => base.level; - set => base.level = value; - } - - #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, - string type, - [SchemaMainParam] ICurve baseLine, - Level level, - double height, - double baseOffset = 0, - double topOffset = 0, - bool flipped = false, - bool structural = false, - [SchemaParamInfo("Set in here any nested elements that this wall might have.")] List? elements = null, - List? parameters = null - ) - : this( - family, - type, - baseLine, - level, - null, - height, - null, - null, - baseOffset, - topOffset, - flipped, - structural, - elements: elements, - parameters: parameters - ) { } - #endregion -} - -[SpeckleType("Objects.BuiltElements.Revit.RevitFaceWall")] -public class RevitFaceWall : Wall -{ - public RevitFaceWall() { } - - [SchemaInfo("RevitWall by face", "Creates a Revit wall from a surface.", "Revit", "Architecture")] - public RevitFaceWall( - string family, - string type, - [SchemaParamInfo("Surface or single face Brep to use"), SchemaMainParam] Brep surface, - Level level, - LocationLine locationLine = LocationLine.Interior, - [SchemaParamInfo("Set in here any nested elements that this wall might have.")] List? elements = null, - List? parameters = null - ) - { - if (surface.Surfaces.Count == 0) - { - throw new Exception("Cannot create a RevitWall with an empty BREP"); - } - - if (surface.Surfaces.Count > 1) - { - throw new Exception( - "The provided brep has more than 1 surface. Please deconstruct/explode it to create multiple instances" - ); - } - - this.family = family; - this.type = type; - brep = surface; - this.locationLine = locationLine; - this.level = level; - this.elements = elements; - this.parameters = parameters?.ToBase(); - } - - public string family { get; set; } - public string type { get; set; } - - public Brep brep { get; set; } - - public new Level? level - { - get => base.level; - set => base.level = value; - } - - public LocationLine locationLine { get; set; } - public Base? parameters { get; set; } - public string elementId { get; set; } -} - -[SpeckleType("Objects.BuiltElements.Revit.RevitProfileWall")] -public class RevitProfileWall : Wall -{ - public RevitProfileWall() { } - - [SchemaInfo("RevitWall by profile", "Creates a Revit wall from a profile.", "Revit", "Architecture")] - public RevitProfileWall( - string family, - string type, - [SchemaParamInfo("Profile to use"), SchemaMainParam] Polycurve profile, - Level level, - LocationLine locationLine = LocationLine.Interior, - bool structural = false, - [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.profile = profile; - this.locationLine = locationLine; - this.structural = structural; - this.level = level; - this.elements = elements; - this.parameters = parameters?.ToBase(); - } - - public string family { get; set; } - public string type { get; set; } - public Polycurve profile { get; set; } - - public new Level? level - { - get => base.level; - set => base.level = value; - } - - public LocationLine locationLine { get; set; } - public bool structural { get; set; } - public Base? parameters { get; set; } - public string elementId { get; set; } -} - -// [SchemaDescription("Not supported yet.")] -// [SchemaIgnore] -// public class RevitCurtainWall : Wall -// { -// // TODO -// // What props do/can curtain walls have? - grid, mullions, etc. -// -// [SchemaOptional] -// public bool flipped { get; set; } -// -// [SchemaOptional] -// public Base parameters { get; set; } -// -// [SchemaIgnore] -// public string elementId { get; set; } -// } -// -// [SchemaDescription("Not supported yet.")] -// [SchemaIgnore] -// public class RevitWallByPoint : Base -// { -// [SchemaOptional] -// public Base parameters { get; set; } -// -// [SchemaIgnore] -// public string elementId { get; set; } -// } diff --git a/src/Speckle.Objects/BuiltElements/Revit/RevitWire.cs b/src/Speckle.Objects/BuiltElements/Revit/RevitWire.cs deleted file mode 100644 index 6c09ec74..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/RevitWire.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Speckle.Objects.BuiltElements.Revit.Interfaces; -using Speckle.Objects.Utils; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.RevitWire")] -public class RevitWire : Wire, IHasMEPConnectors -{ - public RevitWire() { } - - [SchemaInfo("RevitWire", "Creates a Revit wire from points and level", "Revit", "MEP")] - public RevitWire( - List constructionPoints, - string family, - string type, - Level level, - string wiringType = "Arc", - List? parameters = null - ) - { - this.constructionPoints = constructionPoints; - this.family = family; - this.type = type; - this.level = level; - this.wiringType = wiringType; - this.parameters = parameters?.ToBase(); - } - - public string family { get; set; } - public string type { get; set; } - public string wiringType { get; set; } - public List constructionPoints { get; set; } // used in constructor for revit native wires - public string system { get; set; } - public Level level { get; set; } - public Base? parameters { get; set; } - public string elementId { get; set; } - public List Connectors { get; set; } = new(); -} diff --git a/src/Speckle.Objects/BuiltElements/Revit/RevitZone.cs b/src/Speckle.Objects/BuiltElements/Revit/RevitZone.cs deleted file mode 100644 index ddadf1a9..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/RevitZone.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.RevitZone")] -public class RevitZone : Zone -{ - public RevitZone() { } - - public Level level { get; set; } - public string phaseName { get; set; } - public Base parameters { get; set; } - public string elementId { get; set; } - public bool isDefault { get; set; } - public string serviceType { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Revit/StructuralConnectionHandler.cs b/src/Speckle.Objects/BuiltElements/Revit/StructuralConnectionHandler.cs deleted file mode 100644 index 28a0e369..00000000 --- a/src/Speckle.Objects/BuiltElements/Revit/StructuralConnectionHandler.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.Revit; - -[SpeckleType("Objects.BuiltElements.Revit.StructuralConnectionHandler")] -public class StructuralConnectionHandler : Base, IDisplayValue> -{ - public string family { get; set; } - public string type { get; set; } - public Point basePoint { get; set; } - - [DetachProperty] - public List connectedElements { get; set; } - - [DetachProperty] - public List displayValue { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Roof.cs b/src/Speckle.Objects/BuiltElements/Roof.cs deleted file mode 100644 index 751cc233..00000000 --- a/src/Speckle.Objects/BuiltElements/Roof.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements; - -[SpeckleType("Objects.BuiltElements.Roof")] -public class Roof : Base, IDisplayValue> -{ - public Roof() { } - - [SchemaDeprecated, SchemaInfo("Roof", "Creates a Speckle roof", "BIM", "Architecture")] - public Roof([SchemaMainParam] ICurve outline, List? voids = null, List? elements = null) - { - this.outline = outline; - this.voids = voids ?? new(); - this.elements = elements; - } - - public ICurve outline { get; set; } - public virtual Level? level { get; internal set; } - public List voids { get; set; } = new(); - - [DetachProperty] - public List? elements { get; set; } - - public string units { get; set; } - - [DetachProperty] - public List displayValue { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Room.cs b/src/Speckle.Objects/BuiltElements/Room.cs deleted file mode 100644 index 6fdca529..00000000 --- a/src/Speckle.Objects/BuiltElements/Room.cs +++ /dev/null @@ -1,62 +0,0 @@ -using Speckle.Objects.BuiltElements.Revit; -using Speckle.Objects.Geometry; -using Speckle.Objects.Utils; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements; - -[SpeckleType("Objects.BuiltElements.Room")] -public class Room : Base, IHasArea, IHasVolume, IDisplayValue> -{ - public Room() { } - - /// - /// SchemaBuilder constructor for a Room - /// - /// Assign units when using this constructor due to prop - [SchemaInfo("Room", "Creates a Speckle room", "BIM", "Architecture")] - public Room(string name, string number, Level level, [SchemaMainParam] Point basePoint) - { - this.name = name; - this.number = number; - this.level = level; - this.basePoint = basePoint; - } - - /// - /// SchemaBuilder constructor for a Room - /// - /// Assign units when using this constructor due to prop - [SchemaInfo("RevitRoom", "Creates a Revit room with parameters", "Revit", "Architecture")] - public Room( - string name, - string number, - Level level, - [SchemaMainParam] Point basePoint, - List? parameters = null - ) - { - this.name = name; - this.number = number; - this.level = level; - this.basePoint = basePoint; - this["parameters"] = parameters?.ToBase(); - } - - public string name { get; set; } - public string number { get; set; } - public virtual Level? level { get; set; } - public Point basePoint { get; set; } - public double height { get; set; } - public List voids { get; set; } = new(); - public ICurve outline { get; set; } - - public string units { get; set; } - - [DetachProperty] - public List displayValue { get; set; } - - public double area { get; set; } - public double volume { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Space.cs b/src/Speckle.Objects/BuiltElements/Space.cs deleted file mode 100644 index 5334b0cc..00000000 --- a/src/Speckle.Objects/BuiltElements/Space.cs +++ /dev/null @@ -1,76 +0,0 @@ -using Speckle.Objects.BuiltElements.Revit; -using Speckle.Objects.Geometry; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements; - -[SpeckleType("Objects.BuiltElements.Space")] -public class Space : Base, IHasArea, IHasVolume, IDisplayValue> -{ - public Space() { } - - [SchemaInfo("Space", "Creates a Speckle space", "BIM", "MEP")] - public Space(string name, string number, [SchemaMainParam] Point basePoint, Level level) - { - this.name = name; - this.number = number; - this.basePoint = basePoint; - this.level = level; - } - - [SchemaInfo( - "Space with top level and offset parameters", - "Creates a Speckle space with the specified top level and offsets", - "BIM", - "MEP" - )] - public Space( - string name, - string number, - [SchemaMainParam] Point basePoint, - Level level, - Level topLevel, - double topOffset, - double baseOffset - ) - { - this.name = name; - this.number = number; - this.basePoint = basePoint; - this.level = level; - this.topLevel = topLevel; - this.topOffset = topOffset; - this.baseOffset = baseOffset; - } - - public string name { get; set; } - public string number { get; set; } - public Point basePoint { get; set; } - public Level level { get; set; } - public double baseOffset { get; set; } - public Level topLevel { get; set; } // corresponds to UpperLimit property in Revit api - public double topOffset { get; set; } // corresponds to LimitOffset property in Revit api - public List voids { get; set; } = new(); - public ICurve outline { get; set; } - public string spaceType { get; set; } - - // add the zone object for better forward compatibility - public RevitZone? zone { get; set; } - - [Obsolete("Use zone property instead")] - public string zoneName { get; internal set; } - public string units { get; set; } - - public string roomId { get; set; } - - public string phaseName { get; set; } - - // additional properties to add: also include space separation lines here? - - [DetachProperty] - public List displayValue { get; set; } - - public double area { get; set; } - public double volume { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Station.cs b/src/Speckle.Objects/BuiltElements/Station.cs deleted file mode 100644 index 2ec00632..00000000 --- a/src/Speckle.Objects/BuiltElements/Station.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements; - -[SpeckleType("Objects.BuiltElements.Station")] -public class Station : Base -{ - public double number { get; set; } - public string type { get; set; } - public Point location { get; set; } - - public string units { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Structure.cs b/src/Speckle.Objects/BuiltElements/Structure.cs deleted file mode 100644 index 54626a0f..00000000 --- a/src/Speckle.Objects/BuiltElements/Structure.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements; - -[SpeckleType("Objects.BuiltElements.Structure")] -public class Structure : Base, IDisplayValue> -{ - public Point location { get; set; } - public List pipeIds { get; set; } - - public string units { get; set; } - - [DetachProperty] - public List displayValue { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/TeklaStructures/BeamPosition.cs b/src/Speckle.Objects/BuiltElements/TeklaStructures/BeamPosition.cs deleted file mode 100644 index cf93afa9..00000000 --- a/src/Speckle.Objects/BuiltElements/TeklaStructures/BeamPosition.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.TeklaStructures; - -[SpeckleType("Objects.BuiltElements.TeklaStructures.TeklaPosition")] -public class TeklaPosition : Base -{ - public TeklaDepthEnum Depth { get; set; } - public TeklaPlaneEnum Plane { get; set; } - public TeklaRotationEnum Rotation { get; set; } - public double depthOffset { get; set; } - public double planeOffset { get; set; } - public double rotationOffset { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/TeklaStructures/Bolts.cs b/src/Speckle.Objects/BuiltElements/TeklaStructures/Bolts.cs deleted file mode 100644 index 264ea0b9..00000000 --- a/src/Speckle.Objects/BuiltElements/TeklaStructures/Bolts.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.TeklaStructures; - -[SpeckleType("Objects.BuiltElements.TeklaStructures.Bolts")] -public class Bolts : Base -{ - [DetachProperty] - public List displayValue { get; set; } - - public Point firstPosition { get; set; } - public Point secondPosition { get; set; } - - public double length { get; set; } - public double boltSize { get; set; } - public double tolerance { get; set; } - public TeklaPosition position { get; set; } - public string boltStandard { get; set; } - public double cutLength { get; set; } - public List coordinates { get; set; } - public List boltedPartsIds { get; set; } = new(); // First guid is PartToBeBolted, second guid is PartToBoltTo, any others are OtherPartsToBolt -} - -[SpeckleType("Objects.BuiltElements.TeklaStructures.BoltsXY")] -public class BoltsXY : Bolts -{ - // Lists of XY positions of bolts for Tekla - public List xPosition { get; set; } - public List yPosition { get; set; } -} - -[SpeckleType("Objects.BuiltElements.TeklaStructures.BoltsArray")] -public class BoltsArray : Bolts -{ - // Lists of XY distances between bolts for Tekla - public List xDistance { get; set; } - public List yDistance { get; set; } -} - -[SpeckleType("Objects.BuiltElements.TeklaStructures.BoltsCircle")] -public class BoltsCircle : Bolts -{ - public int boltCount { get; set; } - public double diameter { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/TeklaStructures/Enums.cs b/src/Speckle.Objects/BuiltElements/TeklaStructures/Enums.cs deleted file mode 100644 index 73c28fea..00000000 --- a/src/Speckle.Objects/BuiltElements/TeklaStructures/Enums.cs +++ /dev/null @@ -1,86 +0,0 @@ -namespace Speckle.Objects.BuiltElements.TeklaStructures; - -public enum TeklaBeamType -{ - Beam, - PolyBeam, - SpiralBeam -} - -public enum TeklaChamferType -{ - none, - line, - rounding, - arc, - arc_point, - square, - square_parallel, - line_and_arc -} - -public enum TeklaWeldType -{ - none, - edge_flange, - square_groove_butt, - bevel_groove_single_v_butt, - bevel_groove_single_bevel_butt, - single_v_butt_with_broad_root_face, - single_bevel_butt_with_broad_root_face, - u_groove_single_u_butt, - j_groove_j_butt, - bevel_backing, - fillet, - plug, - spot, - seam, - slot, - flare_bevel_groove, - flare_v_groove, - corner_flange, - partial_penetration_single_bevel_butt_plus_fillet, - partial_penetration_square_groove_plus_fillet, - melt_through, - steep_flanked_bevel_groove_single_v_butt, - steep_flanked_bevel_groove_single_bevel_butt, - edge, - iso_surfacing, - fold, - inclined -} - -public enum TeklaWeldIntermittentType -{ - continuous, - chain_intermittent, - staggered_intermittent -} - -public enum TeklaDepthEnum -{ - middle, - front, - behind -} - -public enum TeklaPlaneEnum -{ - middle, - left, - right -} - -public enum TeklaRotationEnum -{ - front, - top, - back, - below -} - -public enum TeklaOpeningTypeEnum -{ - beam, - contour -} diff --git a/src/Speckle.Objects/BuiltElements/TeklaStructures/Fitting.cs b/src/Speckle.Objects/BuiltElements/TeklaStructures/Fitting.cs deleted file mode 100644 index 0f634bb1..00000000 --- a/src/Speckle.Objects/BuiltElements/TeklaStructures/Fitting.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.TeklaStructures; - -[SpeckleType("Objects.BuiltElements.TeklaStructures.Fitting")] -public class Fitting : Plane -{ - public string hostID { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/TeklaStructures/TeklaBeam.cs b/src/Speckle.Objects/BuiltElements/TeklaStructures/TeklaBeam.cs deleted file mode 100644 index e43d85fd..00000000 --- a/src/Speckle.Objects/BuiltElements/TeklaStructures/TeklaBeam.cs +++ /dev/null @@ -1,61 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Structural.Materials; -using Speckle.Objects.Structural.Properties.Profiles; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.TeklaStructures; - -[SpeckleType("Objects.BuiltElements.TeklaStructures.TeklaBeam")] -public class TeklaBeam : Beam, IHasVolume, IHasArea -{ - public TeklaBeam() { } - - [SchemaInfo("TeklaBeam", "Creates a Tekla Structures beam by curve.", "Tekla", "Structure")] - public TeklaBeam([SchemaMainParam] ICurve baseLine, SectionProfile profile, StructuralMaterial material) - { - this.baseLine = baseLine; - this.profile = profile; - this.material = material; - } - - public string name { get; set; } - - [DetachProperty] - public SectionProfile profile { get; set; } - - [DetachProperty] - public StructuralMaterial material { get; set; } - - [DetachProperty] - public string finish { get; set; } - - [DetachProperty] - public string classNumber { get; set; } - - public Vector alignmentVector { get; set; } // This can be set to get proper rotation if coming from an application that doesn't have positioning - - [DetachProperty] - public TeklaPosition position { get; set; } - - public Base userProperties { get; set; } - - [DetachProperty] - public Base rebars { get; set; } - - public TeklaBeamType TeklaBeamType { get; set; } - public double area { get; set; } - public double volume { get; set; } -} - -[SpeckleType("Objects.BuiltElements.TeklaStructures.SpiralBeam")] -public class SpiralBeam : TeklaBeam -{ - public Point startPoint { get; set; } - public Point rotationAxisPt1 { get; set; } - public Point rotationAxisPt2 { get; set; } - public double totalRise { get; set; } - public double rotationAngle { get; set; } - public double twistAngleStart { get; set; } - public double twistAngleEnd { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/TeklaStructures/TeklaContourPlate.cs b/src/Speckle.Objects/BuiltElements/TeklaStructures/TeklaContourPlate.cs deleted file mode 100644 index ff2bd6e7..00000000 --- a/src/Speckle.Objects/BuiltElements/TeklaStructures/TeklaContourPlate.cs +++ /dev/null @@ -1,69 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Structural.Materials; -using Speckle.Objects.Structural.Properties.Profiles; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.TeklaStructures; - -[SpeckleType("Objects.BuiltElements.TeklaStructures.TeklaContourPlate")] -public class TeklaContourPlate : Area -{ - [SchemaInfo("ContourPlate", "Creates a TeklaStructures contour plate.", "Tekla", "Structure")] - public TeklaContourPlate( - SectionProfile profile, - Polyline outline, - string finish, - string classNumber, - string units, - StructuralMaterial? material = null, - TeklaPosition? position = null, - Base? rebars = null - ) - { - this.profile = profile; - this.outline = outline; - this.material = material; - this.finish = finish; - this.classNumber = classNumber; - this.position = position ?? new(); - this.rebars = rebars; - this.units = units; - } - - public TeklaContourPlate() { } - - [DetachProperty] - public SectionProfile profile { get; set; } - - [DetachProperty] - public StructuralMaterial? material { get; set; } - - [DetachProperty] - public string finish { get; set; } - - [DetachProperty] - public string classNumber { get; set; } - - [DetachProperty] - public TeklaPosition position { get; set; } = new(); - - [DetachProperty] - public Base? rebars { get; set; } - - public List contour { get; set; } // Use for ToNative to Tekla. Other programs can use Area.outline. -} - -[SpeckleType("Objects.BuiltElements.TeklaStructures.TeklaContourPoint")] -public class TeklaContourPoint : Point -{ - public TeklaContourPoint() { } - - public TeklaContourPoint(Point point) { } - - public TeklaChamferType chamferType { get; set; } - public double xDim { get; set; } - public double yDim { get; set; } - public double dz1 { get; set; } - public double dz2 { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/TeklaStructures/TeklaModel.cs b/src/Speckle.Objects/BuiltElements/TeklaStructures/TeklaModel.cs deleted file mode 100644 index 8368621c..00000000 --- a/src/Speckle.Objects/BuiltElements/TeklaStructures/TeklaModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.TeklaStructures; - -[SpeckleType("Objects.BuiltElements.TeklaStructures.TeklaModel")] -public class TeklaModel : Base -{ - [DetachProperty] - public List Beams { get; set; } - - [DetachProperty] - public List Rebars { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/TeklaStructures/TeklaOpening.cs b/src/Speckle.Objects/BuiltElements/TeklaStructures/TeklaOpening.cs deleted file mode 100644 index 11799199..00000000 --- a/src/Speckle.Objects/BuiltElements/TeklaStructures/TeklaOpening.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.TeklaStructures; - -[SpeckleType("Objects.BuiltElements.TeklaStructures.TeklaOpening")] -public class TeklaOpening : Opening -{ - public string openingHostId { get; set; } - public TeklaOpeningTypeEnum openingType { get; set; } -} - -[SpeckleType("Objects.BuiltElements.TeklaStructures.TeklaContourOpening")] -public class TeklaContourOpening : TeklaOpening -{ - public TeklaContourPlate cuttingPlate { get; set; } - public double thickness { get; set; } -} - -[SpeckleType("Objects.BuiltElements.TeklaStructures.TeklaBeamOpening")] -public class TeklaBeamOpening : TeklaOpening -{ - public TeklaBeam cuttingBeam { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/TeklaStructures/TeklaRebar.cs b/src/Speckle.Objects/BuiltElements/TeklaStructures/TeklaRebar.cs deleted file mode 100644 index 35fa8845..00000000 --- a/src/Speckle.Objects/BuiltElements/TeklaStructures/TeklaRebar.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Speckle.Objects.Structural.Materials; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.TeklaStructures; - -#region Obsolete -[Obsolete("Deprecated in 2.17: Create a TeklaRebarGroup class instead")] -[SpeckleType("Objects.BuiltElements.TeklaStructures.TeklaRebar")] -public class TeklaRebar : Rebar -{ - public string name { get; set; } - - [DetachProperty] - public Hook startHook { get; set; } - - [DetachProperty] - public Hook endHook { get; set; } - - public double classNumber { get; set; } - public string size { get; set; } - - [DetachProperty] - public StructuralMaterial material { get; set; } -} - -[Obsolete("Deprecated in 2.17: Use a RebarHook class instead")] -[SpeckleType("Objects.BuiltElements.TeklaStructures.Hook")] -public class Hook : Base -{ - public double angle { get; set; } - public double length { get; set; } - public double radius { get; set; } - public shape shape { get; set; } -} - -[Obsolete("Deprecated in 2.17: set starthook and endhook to null or refer to hook angle instead")] -[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Obsolete")] -public enum shape -{ - NO_HOOK = 0, - HOOK_90_DEGREES = 1, - HOOK_135_DEGREES = 2, - HOOK_180_DEGREES = 3, - CUSTOM_HOOK = 4 -} -#endregion diff --git a/src/Speckle.Objects/BuiltElements/TeklaStructures/Welds.cs b/src/Speckle.Objects/BuiltElements/TeklaStructures/Welds.cs deleted file mode 100644 index 41555e34..00000000 --- a/src/Speckle.Objects/BuiltElements/TeklaStructures/Welds.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements.TeklaStructures; - -[SpeckleType("Objects.BuiltElements.TeklaStructures.Welds")] -public class Welds : Base -{ - [DetachProperty] - public List displayValue { get; set; } - - public string mainObjectId { get; set; } - public string secondaryObjectId { get; set; } - public double sizeAbove { get; set; } - public double sizeBelow { get; set; } - public double lengthAbove { get; set; } - public double lengthBelow { get; set; } - public double pitchAbove { get; set; } - public double pitchBelow { get; set; } - public double angleAbove { get; set; } // In degrees - public double angleBelow { get; set; } // In degrees - public TeklaWeldType typeAbove { get; set; } - public TeklaWeldType typeBelow { get; set; } - public TeklaWeldIntermittentType intermittentType { get; set; } -} - -[SpeckleType("Objects.BuiltElements.TeklaStructures.PolygonWelds")] -public class PolygonWelds : Welds -{ - public Polyline polyline { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Topography.cs b/src/Speckle.Objects/BuiltElements/Topography.cs deleted file mode 100644 index 3cd9b7d8..00000000 --- a/src/Speckle.Objects/BuiltElements/Topography.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements; - -[SpeckleType("Objects.BuiltElements.Topography")] -public class Topography : Base, IDisplayValue> -{ - public Topography() - { - displayValue = new List(); - } - - [SchemaInfo("Topography", "Creates a Speckle topography", "BIM", "Architecture")] - public Topography([SchemaMainParam] Mesh displayMesh) - { - displayValue = new List { displayMesh }; - } - - public Mesh baseGeometry { get; set; } = new(); - - public string units { get; set; } - - [DetachProperty] - public List displayValue { get; set; } - //TODO Figure out if we should add a new constructor that takes a List or if Topography should just have a single mesh display value -} diff --git a/src/Speckle.Objects/BuiltElements/View.cs b/src/Speckle.Objects/BuiltElements/View.cs deleted file mode 100644 index 9c729ba5..00000000 --- a/src/Speckle.Objects/BuiltElements/View.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements; - -[SpeckleType("Objects.BuiltElements.View")] -public class View : Base -{ - public string name { get; set; } -} - -[SpeckleType("Objects.BuiltElements.View3D")] -public class View3D : View -{ - public Point origin { get; set; } - public Point target { get; set; } - public Vector upDirection { get; set; } - public Vector forwardDirection { get; set; } - public Box boundingBox { get; set; } // x is right, y is top of screen, z is towards viewer - public bool isOrthogonal { get; set; } - - public string units { get; set; } -} - -[SpeckleType("Objects.BuiltElements.View2D")] -public class View2D : View -{ - //public Point topLeft { get; set; } - //public Point bottomRight { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Wall.cs b/src/Speckle.Objects/BuiltElements/Wall.cs deleted file mode 100644 index 28963cf5..00000000 --- a/src/Speckle.Objects/BuiltElements/Wall.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements; - -[SpeckleType("Objects.BuiltElements.Wall")] -public class Wall : Base, IDisplayValue> -{ - public Wall() { } - - public Wall( - double height, - 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; } - - public string? units { get; set; } - public ICurve baseLine { get; set; } - public virtual Level? level { get; internal set; } - - [DetachProperty] - public List? elements { get; set; } - - [DetachProperty] - 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/BuiltElements/Wire.cs b/src/Speckle.Objects/BuiltElements/Wire.cs deleted file mode 100644 index 8800def1..00000000 --- a/src/Speckle.Objects/BuiltElements/Wire.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements; - -[SpeckleType("Objects.BuiltElements.Wire")] -public class Wire : Base -{ - public Wire() { } - - [SchemaInfo("Wire", "Creates a Speckle wire from curve segments and points", "BIM", "MEP")] - public Wire(List segments) - { - this.segments = segments; - } - - public List segments { get; set; } - - public string units { get; set; } -} diff --git a/src/Speckle.Objects/BuiltElements/Zone.cs b/src/Speckle.Objects/BuiltElements/Zone.cs deleted file mode 100644 index 160697bf..00000000 --- a/src/Speckle.Objects/BuiltElements/Zone.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.BuiltElements; - -[SpeckleType("Objects.BuiltElements.Zone")] -public class Zone : Base, IHasArea, IHasVolume -{ - public Zone() { } - - public Zone(string name) - { - this.name = name; - } - - public string name { get; set; } - public string units { get; set; } - - public List spaces { get; set; } - - // implicit measurements - public double area { get; set; } - public double volume { get; set; } - public double perimeter { get; set; } -} diff --git a/src/Speckle.Objects/Data/ArcgisObject.cs b/src/Speckle.Objects/Data/ArcgisObject.cs new file mode 100644 index 00000000..701a7871 --- /dev/null +++ b/src/Speckle.Objects/Data/ArcgisObject.cs @@ -0,0 +1,16 @@ +using Speckle.Sdk.Models; + +namespace Speckle.Objects.Data; + +/// +/// Represents a ArcGIS.Core.CoreObjectsBase object in ArcGIS +/// +[SpeckleType("Objects.Data.ArcgisObject")] +public class ArcgisObject : DataObject, IGisObject +{ + public required string type { get; set; } + + public required string units { get; set; } + + IReadOnlyList IDisplayValue>.displayValue => displayValue; +} diff --git a/src/Speckle.Objects/Data/ArchicadObject.cs b/src/Speckle.Objects/Data/ArchicadObject.cs new file mode 100644 index 00000000..d243ba25 --- /dev/null +++ b/src/Speckle.Objects/Data/ArchicadObject.cs @@ -0,0 +1,19 @@ +using Speckle.Sdk.Models; + +namespace Speckle.Objects.Data; + +/// +/// Represents a base class object in Archicad +/// +[SpeckleType("Objects.Data.ArchicadObject")] +public class ArchicadObject : DataObject, IArchicadObject +{ + public required string type { get; set; } + + public required string level { get; set; } + + [DetachProperty] + public required List elements { get; set; } + + IReadOnlyList IArchicadObject.elements => elements; +} diff --git a/src/Speckle.Objects/Data/Civil3dObject.cs b/src/Speckle.Objects/Data/Civil3dObject.cs new file mode 100644 index 00000000..995a3c1c --- /dev/null +++ b/src/Speckle.Objects/Data/Civil3dObject.cs @@ -0,0 +1,27 @@ +using Speckle.Sdk.Models; + +namespace Speckle.Objects.Data; + +/// +/// Represents an Autodesk.Civil.DatabaseServices.Entity object in Civil3d +/// +[SpeckleType("Objects.Data.Civil3dObject")] +public class Civil3dObject : DataObject, ICivilObject +{ + public required string type { get; set; } + + /// + /// Curves representing the base curve of an entity + /// + public required List? baseCurves { get; set; } + + /// + /// Children objects, eg profiles, this civil entity may contain. + /// + [DetachProperty] + public required List elements { get; set; } + + public required string units { get; set; } + + IReadOnlyList ICivilObject.elements => elements; +} diff --git a/src/Speckle.Objects/Data/DataObject.cs b/src/Speckle.Objects/Data/DataObject.cs new file mode 100644 index 00000000..baefdb03 --- /dev/null +++ b/src/Speckle.Objects/Data/DataObject.cs @@ -0,0 +1,16 @@ +using Speckle.Sdk.Models; + +namespace Speckle.Objects.Data; + +[SpeckleType("Objects.Data.DataObject")] +public class DataObject : Base, IDataObject +{ + public required string name { get; set; } + + [DetachProperty] + public required List displayValue { get; set; } + + public required Dictionary properties { get; set; } + + IReadOnlyList IDisplayValue>.displayValue => displayValue; +} diff --git a/src/Speckle.Objects/Data/EtabsObject.cs b/src/Speckle.Objects/Data/EtabsObject.cs new file mode 100644 index 00000000..3f0aeafe --- /dev/null +++ b/src/Speckle.Objects/Data/EtabsObject.cs @@ -0,0 +1,24 @@ +using Speckle.Sdk.Models; + +namespace Speckle.Objects.Data; + +/// +/// Represents a wrapper object in ETABS +/// +[SpeckleType("Objects.Data.EtabsObject")] +public class EtabsObject : DataObject, ICsiObject +{ + public required string type { get; set; } + + /// + /// Children objects, eg joints, this etabs object may contain. + /// + [DetachProperty] + public required List elements { get; set; } + + public required string units { get; set; } + + IReadOnlyList ICsiObject.elements => elements; + + IReadOnlyList IDisplayValue>.displayValue => displayValue; +} diff --git a/src/Speckle.Objects/Data/NavisworksObject.cs b/src/Speckle.Objects/Data/NavisworksObject.cs new file mode 100644 index 00000000..1b321000 --- /dev/null +++ b/src/Speckle.Objects/Data/NavisworksObject.cs @@ -0,0 +1,14 @@ +using Speckle.Sdk.Models; + +namespace Speckle.Objects.Data; + +/// +/// Represents a "first selectable ancestor" Navisworks.ModelItem object in Navisworks +/// +[SpeckleType("Objects.Data.NavisworksObject")] +public class NavisworksObject : DataObject, INavisworksObject +{ + public required string units { get; set; } + + IReadOnlyList IDisplayValue>.displayValue => displayValue; +} diff --git a/src/Speckle.Objects/Data/RevitObject.cs b/src/Speckle.Objects/Data/RevitObject.cs new file mode 100644 index 00000000..bdbd51cc --- /dev/null +++ b/src/Speckle.Objects/Data/RevitObject.cs @@ -0,0 +1,29 @@ +using Speckle.Sdk.Models; + +namespace Speckle.Objects.Data; + +/// +/// Represents an Autodesk.Revit.DB.Element object in Revit +/// +[SpeckleType("Objects.Data.RevitObject")] +public class RevitObject : DataObject, IRevitObject +{ + public required string type { get; set; } + public required string family { get; set; } + public required string category { get; set; } + + /// + /// A Curve or Point object representing the location of a Revit element. + /// + public required Base? location { get; set; } + + /// + /// Children objects, eg hosted elements, this RevitObject may contain. + /// + [DetachProperty] + public required List elements { get; set; } + + public required string units { get; set; } + + IReadOnlyList IRevitObject.elements => elements; +} diff --git a/src/Speckle.Objects/Data/TeklaObject.cs b/src/Speckle.Objects/Data/TeklaObject.cs new file mode 100644 index 00000000..c8de0a27 --- /dev/null +++ b/src/Speckle.Objects/Data/TeklaObject.cs @@ -0,0 +1,24 @@ +using Speckle.Sdk.Models; + +namespace Speckle.Objects.Data; + +/// +/// Represents an Tekla.Structures.Model.ModelObject object in Tekla Structures +/// +[SpeckleType("Objects.Data.TeklaObject")] +public class TeklaObject : DataObject, ITeklaObject +{ + public required string type { get; set; } + + /// + /// Children objects, eg profiles, this tekla modelobject may contain. + /// + [DetachProperty] + public required List elements { get; set; } + + public required string units { get; set; } + + IReadOnlyList ITeklaObject.elements => elements; + + IReadOnlyList IDisplayValue>.displayValue => displayValue; +} diff --git a/src/Speckle.Objects/Deprecated/LegacyV2.cs b/src/Speckle.Objects/Deprecated/LegacyV2.cs new file mode 100644 index 00000000..9ec73a99 --- /dev/null +++ b/src/Speckle.Objects/Deprecated/LegacyV2.cs @@ -0,0 +1,18 @@ +using Speckle.Sdk.Models; + +namespace Speckle.Objects.Deprecated; + +[SpeckleType("Objects.Deprecated.LegacyV2")] +[DeprecatedSpeckleType("Objects.Other.BlockInstance")] +[DeprecatedSpeckleType("Objects.Other.Revit.RevitInstance")] +[DeprecatedSpeckleType("Objects.BuiltElements.View")] +[DeprecatedSpeckleType("Objects.BuiltElements.GridLine")] +[DeprecatedSpeckleType("Objects.Other.BlockDefinition")] +[DeprecatedSpeckleType("Objects.Other.DisplayStyle")] +[DeprecatedSpeckleType("Objects.Other.Material")] +[DeprecatedSpeckleType("Objects.Other.MaterialQuantity")] +[DeprecatedSpeckleType("Objects.Other.Revit.RevitMaterial")] +[DeprecatedSpeckleType("Objects.BuiltElements.Revit.Parameter")] +[DeprecatedSpeckleType("Objects.BuiltElements.Revit.Curve.ModelCurve")] +[DeprecatedSpeckleType("Objects.BuiltElements.Revit.DirectShape")] +public class LegacyV2 : Base { } diff --git a/src/Speckle.Objects/EncodingOptimisations.cs b/src/Speckle.Objects/EncodingOptimisations.cs index c6203993..d48792f4 100644 --- a/src/Speckle.Objects/EncodingOptimisations.cs +++ b/src/Speckle.Objects/EncodingOptimisations.cs @@ -18,7 +18,7 @@ public static class CurveTypeEncoding /// public static class CurveArrayEncodingExtensions { - public static List ToArray(List curves) + public static List ToArray(IReadOnlyCollection curves) { var list = new List(); foreach (var curve in curves) @@ -47,7 +47,7 @@ public static class CurveArrayEncodingExtensions list.AddRange(p.ToList()); break; default: - throw new Exception($"Unkown curve type: {curve.GetType()}."); + throw new ArgumentOutOfRangeException(nameof(curves), $"Unkown curve type: {curve.GetType()}."); } } diff --git a/src/Speckle.Objects/GIS/CRS.cs b/src/Speckle.Objects/GIS/CRS.cs deleted file mode 100644 index ae4f04dc..00000000 --- a/src/Speckle.Objects/GIS/CRS.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.GIS; - -[SpeckleType("Objects.GIS.CRS")] -public class CRS : Base -{ - public string? name { get; set; } - public string? authority_id { get; set; } - public string? wkt { get; set; } - public string? units_native { get; set; } - public float? offset_x { get; set; } - public float? offset_y { get; set; } - public float? rotation { get; set; } -} diff --git a/src/Speckle.Objects/GIS/GisFeature.cs b/src/Speckle.Objects/GIS/GisFeature.cs deleted file mode 100644 index df4e9490..00000000 --- a/src/Speckle.Objects/GIS/GisFeature.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.GIS; - -[SpeckleType("Objects.GIS.GisFeature")] -public class GisFeature : Base -{ - public GisFeature() - { - attributes = new Base(); - } - - public GisFeature(Base attributes) - { - this.attributes = attributes; - } - - public GisFeature(List geometry, Base attributes) - { - this.geometry = geometry; - this.attributes = attributes; - } - - public GisFeature(Base attributes, List displayValue) - { - this.attributes = attributes; - this.displayValue = displayValue; - } - - public GisFeature(List geometry, Base attributes, List displayValue) - { - this.geometry = geometry; - this.attributes = attributes; - this.displayValue = displayValue; - } - - [DetachProperty] - public List? geometry { get; set; } - - [DetachProperty] - public List? displayValue { get; set; } - public Base attributes { get; set; } -} diff --git a/src/Speckle.Objects/GIS/GisMultipatchFeature.cs b/src/Speckle.Objects/GIS/GisMultipatchFeature.cs deleted file mode 100644 index 7de6a6da..00000000 --- a/src/Speckle.Objects/GIS/GisMultipatchFeature.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.GIS; - -[SpeckleType("Objects.GIS.GisMultipatchFeature")] -public class GisMultipatchFeature : Base, IGisFeature, IDisplayValue> -{ - public required Base attributes { get; set; } - - /// - /// Multipatch geometry should be of type or - /// - [DetachProperty] - public required List geometry { get; set; } - - [DetachProperty] - public required List displayValue { get; set; } -} diff --git a/src/Speckle.Objects/GIS/GisMultipatchGeometry.cs b/src/Speckle.Objects/GIS/GisMultipatchGeometry.cs deleted file mode 100644 index f4bc67db..00000000 --- a/src/Speckle.Objects/GIS/GisMultipatchGeometry.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.GIS; - -[SpeckleType("Objects.GIS.GisMultipatchGeometry")] -public class GisMultipatchGeometry : Base -{ - public string units { get; set; } - public List faces { get; set; } - public List vertices { get; set; } - public List? colors { get; set; } - - public GisMultipatchGeometry() - { - faces = new List(); - vertices = new List(); - } -} diff --git a/src/Speckle.Objects/GIS/GisNonGeometricFeature.cs b/src/Speckle.Objects/GIS/GisNonGeometricFeature.cs deleted file mode 100644 index d53c6b84..00000000 --- a/src/Speckle.Objects/GIS/GisNonGeometricFeature.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.GIS; - -[SpeckleType("Objects.GIS.GisNonGeometricFeature")] -public class GisNonGeometricFeature : Base, IGisFeature -{ - public required Base attributes { get; set; } -} diff --git a/src/Speckle.Objects/GIS/GisPointFeature.cs b/src/Speckle.Objects/GIS/GisPointFeature.cs deleted file mode 100644 index 57fea198..00000000 --- a/src/Speckle.Objects/GIS/GisPointFeature.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Speckle.Newtonsoft.Json; -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.GIS; - -[SpeckleType("Objects.GIS.GisPointFeature")] -public class GisPointFeature : Base, IGisFeature, IDisplayValue> -{ - public required Base attributes { get; set; } - - [JsonIgnore] - public required List geometry - { - get { return displayValue; } - set { displayValue = value; } - } - - [DetachProperty] - public List displayValue { get; set; } -} diff --git a/src/Speckle.Objects/GIS/GisPolygonFeature.cs b/src/Speckle.Objects/GIS/GisPolygonFeature.cs deleted file mode 100644 index 90c9d81f..00000000 --- a/src/Speckle.Objects/GIS/GisPolygonFeature.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.GIS; - -[SpeckleType("Objects.GIS.GisPolygonFeature")] -public class GisPolygonFeature : Base, IGisFeature, IDisplayValue> -{ - public required Base attributes { get; set; } - - [DetachProperty] - public required List geometry { get; set; } - - [DetachProperty] - public required List displayValue { get; set; } -} diff --git a/src/Speckle.Objects/GIS/GisPolylineFeature.cs b/src/Speckle.Objects/GIS/GisPolylineFeature.cs deleted file mode 100644 index dc31bce7..00000000 --- a/src/Speckle.Objects/GIS/GisPolylineFeature.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Speckle.Newtonsoft.Json; -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.GIS; - -[SpeckleType("Objects.GIS.GisPolylineFeature")] -public class GisPolylineFeature : Base, IGisFeature, IDisplayValue> -{ - public required Base attributes { get; set; } - - [JsonIgnore] - public required List geometry - { - get { return displayValue; } - set { displayValue = value; } - } - - [DetachProperty] - public List displayValue { get; set; } -} diff --git a/src/Speckle.Objects/GIS/GisTopography.cs b/src/Speckle.Objects/GIS/GisTopography.cs deleted file mode 100644 index d111264b..00000000 --- a/src/Speckle.Objects/GIS/GisTopography.cs +++ /dev/null @@ -1,6 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.GIS; - -[SpeckleType("Objects.GIS.GisTopography")] -public class GisTopography : RasterElement { } diff --git a/src/Speckle.Objects/GIS/NonGeometryElement.cs b/src/Speckle.Objects/GIS/NonGeometryElement.cs deleted file mode 100644 index 346fc0ae..00000000 --- a/src/Speckle.Objects/GIS/NonGeometryElement.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.GIS; - -[Obsolete("NonGeometryElement was replaced by a more generic class, \"GisFeature\", which contains more information")] -[SpeckleType("Objects.GIS.NonGeometryElement")] -public class NonGeometryElement : Base -{ - public Base? attributes { get; set; } -} diff --git a/src/Speckle.Objects/GIS/PolygonElement.cs b/src/Speckle.Objects/GIS/PolygonElement.cs deleted file mode 100644 index 08ebed2d..00000000 --- a/src/Speckle.Objects/GIS/PolygonElement.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.GIS; - -[Obsolete("PolygonElement was replaced by a more generic class, \"GisFeature\", which contains more information")] -[SpeckleType("Objects.GIS.PolygonElement")] -public class PolygonElement : Base -{ - [DetachProperty] - public List geometry { get; set; } - public Base attributes { get; set; } -} diff --git a/src/Speckle.Objects/GIS/PolygonGeometry.cs b/src/Speckle.Objects/GIS/PolygonGeometry.cs deleted file mode 100644 index 2a3a3cdb..00000000 --- a/src/Speckle.Objects/GIS/PolygonGeometry.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.GIS; - -[SpeckleType("Objects.GIS.PolygonGeometry")] -public class PolygonGeometry : Base -{ - public string units { get; set; } - public Polyline boundary { get; set; } - public List voids { get; set; } - - public PolygonGeometry() - { - voids = new List(); - } -} diff --git a/src/Speckle.Objects/GIS/PolygonGeometry3d.cs b/src/Speckle.Objects/GIS/PolygonGeometry3d.cs deleted file mode 100644 index 6a2ee318..00000000 --- a/src/Speckle.Objects/GIS/PolygonGeometry3d.cs +++ /dev/null @@ -1,6 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.GIS; - -[SpeckleType("Objects.GIS.PolygonGeometry3d")] -public class PolygonGeometry3d : PolygonGeometry { } diff --git a/src/Speckle.Objects/GIS/RasterElement.cs b/src/Speckle.Objects/GIS/RasterElement.cs deleted file mode 100644 index cb1b78fb..00000000 --- a/src/Speckle.Objects/GIS/RasterElement.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.GIS; - -[SpeckleType("Objects.GIS.RasterElement")] -public class RasterElement : Base -{ - public int band_count { get; set; } - public List band_names { get; set; } - public float x_origin { get; set; } - public float y_origin { get; set; } - public int x_size { get; set; } - public int y_size { get; set; } - public float x_resolution { get; set; } - public float y_resolution { get; set; } - public List noDataValue { get; set; } - - [DetachProperty] - public List displayValue { get; set; } - - public RasterElement() - { - displayValue = new List(); - band_names = new List(); - noDataValue = new List(); - } - - public RasterElement( - int bandCount, - List bandNames, - float xOrigin, - float yOrigin, - int xSize, - int ySize, - float xResolution, - float yResolution, - List noDataValue - ) - { - displayValue = new List(); - band_count = bandCount; - band_names = bandNames; - x_origin = xOrigin; - y_origin = yOrigin; - x_size = xSize; - y_size = ySize; - x_resolution = xResolution; - y_resolution = yResolution; - this.noDataValue = noDataValue; - } -} diff --git a/src/Speckle.Objects/GIS/RasterLayer.cs b/src/Speckle.Objects/GIS/RasterLayer.cs deleted file mode 100644 index 71ef4b71..00000000 --- a/src/Speckle.Objects/GIS/RasterLayer.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Speckle.Sdk.Models; -using Speckle.Sdk.Models.Collections; - -namespace Speckle.Objects.GIS; - -[SpeckleType("Objects.GIS.RasterLayer")] -public class RasterLayer : Collection -{ - public CRS? crs { get; set; } - public string? units { get; set; } - public CRS? rasterCrs { get; set; } - public string? geomType { get; set; } - public Dictionary? renderer { get; set; } - - public RasterLayer() - { - collectionType = "RasterLayer"; - } -} diff --git a/src/Speckle.Objects/GIS/VectorLayer.cs b/src/Speckle.Objects/GIS/VectorLayer.cs deleted file mode 100644 index dee18f19..00000000 --- a/src/Speckle.Objects/GIS/VectorLayer.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Speckle.Sdk.Models; -using Speckle.Sdk.Models.Collections; - -namespace Speckle.Objects.GIS; - -[SpeckleType("Objects.GIS.VectorLayer")] -public class VectorLayer : Collection -{ - public CRS? crs { get; set; } - public string? units { get; set; } - public Base attributes { get; set; } - public string? geomType { get; set; } - public string? nativeGeomType { get; set; } - public Dictionary? renderer { get; set; } - - public VectorLayer() - { - collectionType = "VectorLayer"; - attributes = new Base(); - } -} diff --git a/src/Speckle.Objects/Geometry/Arc.cs b/src/Speckle.Objects/Geometry/Arc.cs index 9765670f..6cb3fd20 100644 --- a/src/Speckle.Objects/Geometry/Arc.cs +++ b/src/Speckle.Objects/Geometry/Arc.cs @@ -1,8 +1,7 @@ +using Speckle.Newtonsoft.Json; using Speckle.Objects.Other; using Speckle.Objects.Primitive; -using Speckle.Sdk; using Speckle.Sdk.Common; -using Speckle.Sdk.Logging; using Speckle.Sdk.Models; namespace Speckle.Objects.Geometry; @@ -11,218 +10,78 @@ namespace Speckle.Objects.Geometry; /// Represents a sub-curve of a three-dimensional circle. /// [SpeckleType("Objects.Geometry.Arc")] -public class Arc : Base, IHasBoundingBox, ICurve, IHasArea, ITransformable +public class Arc : Base, IHasBoundingBox, ICurve, ITransformable { - /// - public Arc() { } - /// - /// Constructs a new using angle values. + /// Gets or sets the plane of the . + /// The plane origin is the center. + /// The plane normal indicates the handedness of the such that direction from to is counterclockwise. /// - /// The Plane where the arc will be drawn - /// The radius of the Arc - /// The angle formed between the start point and the X Axis of the plane - /// The angle formed between the end point and the X Axis of the plane - /// The total angle of the Arc in Radians - /// The object's units - /// The object's unique application ID - public Arc( - Plane plane, - double radius, - double startAngle, - double endAngle, - double angleRadians, - string units = Units.Meters, - string? applicationId = null - ) - { - this.plane = plane; - this.radius = radius; - this.startAngle = startAngle; - this.endAngle = endAngle; - this.angleRadians = angleRadians; - domain = - angleRadians > 0 - ? new Interval { start = 0, end = angleRadians } - : new Interval { start = angleRadians, end = 0 }; - this.applicationId = applicationId; - this.units = units; - } - - /// - /// Initialise an `Arc` using the arc angle and the start and end points. - /// The radius, midpoint, start angle, and end angle will be calculated. - /// For now, this assumes 2D arcs on the XY plane - /// - /// The start point of the arc - /// The end point of the arc - /// The arc angle - /// Units (defaults to "m") - /// ID given to the arc in the authoring programme (defaults to null) - public Arc( - Point startPoint, - Point endPoint, - double angleRadians, - string units = Units.Meters, - string? applicationId = null - ) - : this( - new Plane - { - origin = startPoint, - normal = new Vector(0, 0, 1), - xdir = new Vector(1, 0, 0), - ydir = new Vector(0, 1, 0), - units = units - }, - startPoint, - endPoint, - angleRadians, - units, - applicationId - ) { } - - /// - /// Initialise an `Arc` using a plane, the arc angle and the start and end points. - /// The radius, midpoint, start angle, and end angle will be calculated. - /// - /// The Plane where the arc will be drawn - /// The start point of the arc - /// The end point of the arc - /// The arc angle - /// Units (defaults to "m") - /// ID given to the arc in the authoring programme (defaults to null) - public Arc( - Plane plane, - Point startPoint, - Point endPoint, - double angleRadians, - string units = Units.Meters, - string? applicationId = null - ) - { - // don't be annoying - if (angleRadians > Math.PI * 2) - { - throw new SpeckleException("Can't create an arc with an angle greater than 2pi"); - } - - if (startPoint == endPoint) - { - throw new SpeckleException("Can't create an arc where the start and end points are the same"); - } - - this.units = units; - this.startPoint = startPoint; - this.endPoint = endPoint; - this.angleRadians = angleRadians; - domain = - angleRadians > 0 - ? new Interval { start = 0, end = angleRadians } - : new Interval { start = angleRadians, end = 0 }; - this.applicationId = applicationId; - - // find chord and chord angle which may differ from the arc angle - var chordMidpoint = Point.Midpoint(startPoint, endPoint); - var chordLength = Point.Distance(startPoint, endPoint); - var chordAngle = angleRadians; - if (chordAngle > Math.PI) - { - chordAngle -= Math.PI * 2; - } - else if (chordAngle < -Math.PI) - { - chordAngle += Math.PI * 2; - } - // use the law of cosines for an isosceles triangle to get the radius - radius = chordLength / Math.Sqrt(2 - 2 * Math.Cos(chordAngle)); - - // find the chord vector then calculate the perpendicular vector which points to the centre - // which can be used to find the circle centre point - var dir = chordAngle < 0 ? -1 : 1; - var centreToChord = Math.Sqrt(Math.Pow((double)radius, 2) - Math.Pow(chordLength * 0.5, 2)); - var perp = Vector.CrossProduct(new Vector(endPoint - startPoint), plane.normal); - var circleCentre = chordMidpoint + new Point(perp.Unit() * centreToChord * -dir); - plane.origin = circleCentre; - - // use the perpendicular vector in the other direction (from the centre to the arc) to find the arc midpoint - midPoint = - angleRadians > Math.PI - ? chordMidpoint + new Point(perp.Unit() * ((double)radius + centreToChord) * -dir) - : chordMidpoint + new Point(perp.Unit() * ((double)radius - centreToChord) * dir); - - // find the start angle using trig (correcting for quadrant position) and add the arc angle to get the end angle - startAngle = Math.Tan((startPoint.y - circleCentre.y) / (startPoint.x - circleCentre.x)) % (2 * Math.PI); - if (startPoint.x > circleCentre.x && startPoint.y < circleCentre.y) // Q4 - { - startAngle *= -1; - } - else if (startPoint.x < circleCentre.x && startPoint.y < circleCentre.y) // Q3 - { - startAngle += Math.PI; - } - else if (startPoint.x < circleCentre.x && startPoint.y > circleCentre.y) // Q2 - { - startAngle = Math.PI - startAngle; - } - - endAngle = startAngle + angleRadians; - // Set the plane of this arc - this.plane = plane; - } - - /// - /// The radius of the - /// - public double? radius { get; set; } - - /// - /// The start angle of the based on it's - /// - public double? startAngle { get; set; } - - /// - /// The end angle of the based on it's - /// - public double? endAngle { get; set; } - - /// - /// The inner angle of the - /// - public double angleRadians { get; set; } - - /// - /// Gets or sets the plane of the . The plane origin is the center. - /// - public Plane plane { get; set; } + public required Plane plane { get; set; } /// /// The start of the /// - public Point startPoint { get; set; } + public required Point startPoint { get; set; } /// /// Gets or sets the point at 0.5 length. /// - public Point midPoint { get; set; } + public required Point midPoint { get; set; } /// /// The end of the /// - public Point endPoint { get; set; } + public required Point endPoint { get; set; } + + /// + /// The radius of the + /// + public double radius => Point.Distance(plane.origin, startPoint); + + /// + /// OBSOLETE - This is just here for backwards compatibility. + /// + [JsonIgnore, Obsolete("start angle should be calculated from arc startpoint and plane if needed", true)] + public double? startAngle { get; set; } + + /// + /// OBSOLETE - This is just here for backwards compatibility. + /// + [JsonIgnore, Obsolete("end angle should be calculated from arc endpoint and plane if needed", true)] + public double? endAngle { get; set; } + + /// + /// OBSOLETE - This is just here for backwards compatibility. + /// + [JsonIgnore, Obsolete("Refer to measure instead", true)] + public double angleRadians { get; set; } + + /// + /// The measure of the in radians. + /// Calculated using the arc addition postulate using the . + /// + public double measure => + (2 * Math.Asin(Point.Distance(startPoint, midPoint) / (2 * radius))) + + (2 * Math.Asin(Point.Distance(midPoint, endPoint) / (2 * radius))); /// /// The units this object was specified in. /// - public string units { get; set; } + public required string units { get; set; } /// public Interval domain { get; set; } = new() { start = 0, end = 0 }; - /// - public double length { get; set; } + /// + /// The length of the + /// + public double length => radius * measure; - /// + /// + /// OBSOLETE - This is just here for backwards compatibility. + /// + [JsonIgnore, Obsolete("Area property does not belong on an arc", true)] public double area { get; set; } /// @@ -235,10 +94,14 @@ public class Arc : Base, IHasBoundingBox, ICurve, IHasArea, ITransformable midPoint.TransformTo(transform, out Point transformedMidpoint); endPoint.TransformTo(transform, out Point transformedEndPoint); plane.TransformTo(transform, out Plane pln); - var arc = new Arc(pln, transformedStartPoint, transformedEndPoint, angleRadians, units) + Arc arc = new() { + startPoint = transformedStartPoint, + endPoint = transformedEndPoint, midPoint = transformedMidpoint, - domain = domain + plane = pln, + domain = domain, + units = units, }; transformed = arc; return true; @@ -260,13 +123,12 @@ public class Arc : Base, IHasBoundingBox, ICurve, IHasArea, ITransformable public List ToList() { var list = new List(); - list.Add(radius ?? 0); - list.Add(startAngle ?? 0); - list.Add(endAngle ?? 0); - list.Add(angleRadians); + list.Add(radius); + list.Add(0); // Backwards compatibility: start angle + list.Add(0); // Backwards compatibility: end angle + list.Add(measure); list.Add(domain?.start ?? 0); list.Add(domain?.end ?? 0); - list.AddRange(plane.ToList()); list.AddRange(startPoint.ToList()); list.AddRange(midPoint.ToList()); @@ -286,21 +148,18 @@ public class Arc : Base, IHasBoundingBox, ICurve, IHasArea, ITransformable /// A new with the values assigned from the list. public static Arc FromList(List list) { - var arc = new Arc + string units = Units.GetUnitFromEncoding(list[^1]); + Arc arc = new() { - radius = list[2], - startAngle = list[3], - endAngle = list[4], - angleRadians = list[5], domain = new Interval { start = list[6], end = list[7] }, - units = Units.GetUnitFromEncoding(list[list.Count - 1]), - plane = Plane.FromList(list.GetRange(8, 13)) + units = units, + plane = Plane.FromList(list.GetRange(8, 13)), + startPoint = Point.FromList(list.GetRange(21, 3), units), + midPoint = Point.FromList(list.GetRange(24, 3), units), + endPoint = Point.FromList(list.GetRange(27, 3), units), }; - arc.startPoint = Point.FromList(list.GetRange(21, 3), arc.units); - arc.midPoint = Point.FromList(list.GetRange(24, 3), arc.units); - arc.endPoint = Point.FromList(list.GetRange(27, 3), arc.units); - arc.plane.units = arc.units; + arc.plane.units = arc.units; return arc; } } diff --git a/src/Speckle.Objects/Geometry/Autocad/AutocadPolycurve.cs b/src/Speckle.Objects/Geometry/Autocad/AutocadPolycurve.cs index 7258dfd1..e2de3eed 100644 --- a/src/Speckle.Objects/Geometry/Autocad/AutocadPolycurve.cs +++ b/src/Speckle.Objects/Geometry/Autocad/AutocadPolycurve.cs @@ -14,11 +14,6 @@ namespace Speckle.Objects.Geometry.Autocad; [SpeckleType("Objects.Geometry.Autocad.AutocadPolycurve")] public class AutocadPolycurve : Polycurve { - /// - /// Constructs a new empty instance. - /// - public AutocadPolycurve() { } - /// /// Gets or sets the raw coordinates of the vertices. /// @@ -27,7 +22,7 @@ public class AutocadPolycurve : Polycurve /// For Polyline2d and Polyline3d types, these are xyz coordinates in the Global Coordinate System. fml. /// [DetachProperty, Chunkable(31250)] - public List value { get; set; } = new(); + public required List value { get; set; } /// /// The bulge factor at each vertex. Should be null for Polyline3d. @@ -38,24 +33,24 @@ public class AutocadPolycurve : Polycurve /// made negative if the arc goes clockwise from the start point to the endpoint. /// A bulge of 0 indicates a straight segment, and a bulge of 1 is a semicircle. /// - public List? bulges { get; set; } + public required List? bulges { get; set; } /// /// The tangent in radians at each vertex. Should be null for Polyline and Polyline3d. /// - public List? tangents { get; set; } + public required List? tangents { get; set; } /// /// The normal of the plane of the Autocad Polyline or Polyline2d. Should be null for Polyline3d. /// - public Vector? normal { get; set; } + public required Vector? normal { get; set; } /// /// The distance from the plane to the origin of the Autocad Polyline or Polyline2d. Should be null for Polyline3d. /// public double? elevation { get; set; } - public AutocadPolyType polyType { get; set; } + public required AutocadPolyType polyType { get; set; } } /// diff --git a/src/Speckle.Objects/Geometry/Box.cs b/src/Speckle.Objects/Geometry/Box.cs index f2af8df1..ea91cce6 100644 --- a/src/Speckle.Objects/Geometry/Box.cs +++ b/src/Speckle.Objects/Geometry/Box.cs @@ -1,3 +1,4 @@ +using Speckle.Newtonsoft.Json; using Speckle.Objects.Primitive; using Speckle.Sdk.Common; using Speckle.Sdk.Models; @@ -10,10 +11,17 @@ namespace Speckle.Objects.Geometry; [SpeckleType("Objects.Geometry.Box")] public class Box : Base, IHasVolume, IHasArea, IHasBoundingBox { + [JsonIgnore, Obsolete("Use plane property instead", true)] + public Plane basePlane + { + get => plane; + set => plane = value; + } + /// /// Gets or sets the plane that defines the orientation of the /// - public required Plane basePlane { get; set; } + public required Plane plane { get; set; } /// /// Gets or sets the that defines the min and max coordinate in the X direction @@ -39,11 +47,11 @@ public class Box : Base, IHasVolume, IHasArea, IHasBoundingBox public required string units { get; set; } /// - public double area { get; set; } + public double area => 2 * (xSize.Length * ySize.Length + xSize.Length * zSize.Length + ySize.Length * zSize.Length); - /// + [JsonIgnore, Obsolete("Boxs should not have a bounding box", true)] public Box? bbox { get; } /// - public double volume { get; set; } + public double volume => xSize.Length * ySize.Length * zSize.Length; } diff --git a/src/Speckle.Objects/Geometry/Brep.cs b/src/Speckle.Objects/Geometry/Brep.cs index 17d9c1a6..df201c32 100644 --- a/src/Speckle.Objects/Geometry/Brep.cs +++ b/src/Speckle.Objects/Geometry/Brep.cs @@ -220,7 +220,7 @@ public class Brep : Base, IHasArea, IHasVolume, IHasBoundingBox, ITransformable< StartIndex = startIndex, EndIndex = endIndex, ProxyCurveIsReversed = proxyReversed, - Domain = domain + Domain = domain, }; Edges.Add(edge); @@ -277,7 +277,7 @@ public class Brep : Base, IHasArea, IHasVolume, IHasBoundingBox, ITransformable< Brep = this, FaceIndex = faceIndex, TrimIndices = trimIndices, - Type = type + Type = type, }; Loops.Add(loop); i += n + 1; @@ -402,7 +402,7 @@ public class Brep : Base, IHasArea, IHasVolume, IHasBoundingBox, ITransformable< SurfaceIndex = surfIndex, LoopIndices = loopIndices, OuterLoopIndex = outerLoopIndex, - OrientationReversed = orientationIsReversed + OrientationReversed = orientationIsReversed, }; Faces.Add(face); i += n + 1; @@ -490,7 +490,7 @@ public class Brep : Base, IHasArea, IHasVolume, IHasBoundingBox, ITransformable< Faces = new List(Faces.Count), IsClosed = IsClosed, Orientation = Orientation, - applicationId = applicationId ?? id + applicationId = applicationId ?? id, }; foreach (var e in Edges) @@ -504,7 +504,7 @@ public class Brep : Base, IHasArea, IHasVolume, IHasBoundingBox, ITransformable< StartIndex = e.StartIndex, EndIndex = e.EndIndex, ProxyCurveIsReversed = e.ProxyCurveIsReversed, - Domain = e.Domain + Domain = e.Domain, } ); } @@ -517,7 +517,7 @@ public class Brep : Base, IHasArea, IHasVolume, IHasBoundingBox, ITransformable< Brep = transformed, FaceIndex = l.FaceIndex, TrimIndices = l.TrimIndices, - Type = l.Type + Type = l.Type, } ); } @@ -551,7 +551,7 @@ public class Brep : Base, IHasArea, IHasVolume, IHasBoundingBox, ITransformable< SurfaceIndex = f.SurfaceIndex, LoopIndices = f.LoopIndices, OuterLoopIndex = f.OuterLoopIndex, - OrientationReversed = f.OrientationReversed + OrientationReversed = f.OrientationReversed, } ); } @@ -670,7 +670,7 @@ public class Brep : Base, IHasArea, IHasVolume, IHasBoundingBox, ITransformable< SurfaceIndex = f.SurfaceIndex, LoopIndices = f.LoopIndices, OuterLoopIndex = f.OuterLoopIndex, - OrientationReversed = f.OrientationReversed + OrientationReversed = f.OrientationReversed, }; Faces[i] = f; } @@ -698,7 +698,7 @@ public enum BrepOrientation Outward = 1, /// Orientation is not known - Unknown = 2 + Unknown = 2, } /// @@ -722,7 +722,7 @@ public enum BrepLoopType CurveOnSurface, /// Loop is collapsed to a point. - PointOnSurface + PointOnSurface, } /// @@ -737,5 +737,5 @@ public enum BrepTrimType Singular, CurveOnSurface, PointOnSurface, - Slit + Slit, } diff --git a/src/Speckle.Objects/Geometry/BrepX.cs b/src/Speckle.Objects/Geometry/BrepX.cs new file mode 100644 index 00000000..fee979a4 --- /dev/null +++ b/src/Speckle.Objects/Geometry/BrepX.cs @@ -0,0 +1,33 @@ +using Speckle.Objects.Other; +using Speckle.Sdk.Models; + +namespace Speckle.Objects.Geometry; + +public interface IRawEncodedObject +{ + public RawEncoding encodedValue { get; set; } +} + +public abstract class RawEncodedObject : Base, IDisplayValue>, IRawEncodedObject, IHasArea, IHasVolume +{ + [DetachProperty] + public required List displayValue { get; set; } + + [DetachProperty] + public required RawEncoding encodedValue { get; set; } + + public required string units { get; set; } + + public double area { get; set; } + + public double volume { get; set; } +} + +[SpeckleType("Objects.Geometry.BrepX")] +public class BrepX : RawEncodedObject; + +[SpeckleType("Objects.Geometry.ExtrusionX")] +public class ExtrusionX : RawEncodedObject; + +[SpeckleType("Objects.Geometry.SubDX")] +public class SubDX : RawEncodedObject; diff --git a/src/Speckle.Objects/Geometry/Circle.cs b/src/Speckle.Objects/Geometry/Circle.cs index 776dd083..38f0160a 100644 --- a/src/Speckle.Objects/Geometry/Circle.cs +++ b/src/Speckle.Objects/Geometry/Circle.cs @@ -33,12 +33,12 @@ public class Circle : Base, ICurve, IHasArea, IHasBoundingBox public Interval domain { get; set; } = Interval.UnitInterval; /// - public double length { get; set; } + public double length => 2 * Math.PI * radius; //public Point center { get; set; } /// - public double area { get; set; } + public double area => Math.PI * radius * radius; /// public Box? bbox { get; set; } @@ -74,7 +74,7 @@ public class Circle : Base, ICurve, IHasArea, IHasBoundingBox radius = list[2], domain = new Interval { start = list[3], end = list[4] }, plane = Plane.FromList(list.GetRange(5, 13)), - units = Units.GetUnitFromEncoding(list[list.Count - 1]) + units = Units.GetUnitFromEncoding(list[^1]), }; return circle; diff --git a/src/Speckle.Objects/Geometry/ControlPoint.cs b/src/Speckle.Objects/Geometry/ControlPoint.cs index 4ea9fe96..fd942e35 100644 --- a/src/Speckle.Objects/Geometry/ControlPoint.cs +++ b/src/Speckle.Objects/Geometry/ControlPoint.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Speckle.Newtonsoft.Json; using Speckle.Objects.Other; using Speckle.Sdk.Models; @@ -9,18 +10,15 @@ public class ControlPoint : Point, ITransformable { public ControlPoint() { } - public ControlPoint(double x, double y, double z, string units, string? applicationId = null) - : base(x, y, z, units, applicationId) - { - weight = 1; - } - + [SetsRequiredMembers] public ControlPoint(double x, double y, double z, double w, string units, string? applicationId = null) : base(x, y, z, units, applicationId) { weight = w; } + public required double weight { get; set; } + /// /// OBSOLETE - This is just here for backwards compatibility. /// @@ -42,8 +40,6 @@ public class ControlPoint : Point, ITransformable } } - public double weight { get; set; } - public bool TransformTo(Transform transform, out ControlPoint transformed) { TransformTo(transform, out Point transformedPoint); diff --git a/src/Speckle.Objects/Geometry/Curve.cs b/src/Speckle.Objects/Geometry/Curve.cs index 65564a23..bd14e3ac 100644 --- a/src/Speckle.Objects/Geometry/Curve.cs +++ b/src/Speckle.Objects/Geometry/Curve.cs @@ -80,7 +80,7 @@ public class Curve : Base, ICurve, IHasBoundingBox, IHasArea, ITransformable profiles { get; set; } - - public string units { get; set; } - - public double area { get; set; } - - public Box? bbox { get; set; } - public double volume { get; set; } -} diff --git a/src/Speckle.Objects/Geometry/Line.cs b/src/Speckle.Objects/Geometry/Line.cs index b4b6c8f1..2fe90022 100644 --- a/src/Speckle.Objects/Geometry/Line.cs +++ b/src/Speckle.Objects/Geometry/Line.cs @@ -1,9 +1,8 @@ +using System.Diagnostics.CodeAnalysis; using Speckle.Newtonsoft.Json; using Speckle.Objects.Other; using Speckle.Objects.Primitive; -using Speckle.Sdk; using Speckle.Sdk.Common; -using Speckle.Sdk.Logging; using Speckle.Sdk.Models; namespace Speckle.Objects.Geometry; @@ -13,76 +12,38 @@ public class Line : Base, ICurve, IHasBoundingBox, ITransformable { public Line() { } - [Obsolete("Line should not use a constructor that only sets the start point. Deprecated in 2.18.", true)] - public Line(double x, double y, double z = 0, string units = Units.Meters, string? applicationId = null) - { - start = new Point(x, y, z); -#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type. Reason: Obsolete. - end = null; -#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type. Reason: Obsolete. - this.applicationId = applicationId; - this.units = units; - } - - public Line(Point start, Point end, string units = Units.Meters, string? applicationId = null) - { - this.start = start; - this.end = end; - length = Point.Distance(start, end); - this.applicationId = applicationId; - this.units = units; - } - - public Line(IList coordinates, string units = Units.Meters, string? applicationId = null) + /// + /// + /// + /// must have a length of 6 + [SetsRequiredMembers] + public Line(IList coordinates, string units, string? applicationId = null) { if (coordinates.Count < 6) { - throw new SpeckleException("Line from coordinate array requires 6 coordinates."); + throw new ArgumentException("Line from coordinate array requires 6 coordinates.", nameof(coordinates)); } start = new Point(coordinates[0], coordinates[1], coordinates[2], units, applicationId); end = new Point(coordinates[3], coordinates[4], coordinates[5], units, applicationId); - length = Point.Distance(start, end); - this.applicationId = applicationId; this.units = units; + this.applicationId = applicationId; } - [Obsolete("Use IList constructor", true)] - public Line(IEnumerable coordinatesArray, string units = Units.Meters, string? applicationId = null) - : this(coordinatesArray.ToList(), units, applicationId) { } - /// /// OBSOLETE - This is just here for backwards compatibility. - /// You should not use this for anything. Access coordinates using start and end point. + /// You should not use this for anything. /// + [JsonIgnore, Obsolete("Area should not be on the line class", true)] + public double area => 0; - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public List value - { -#pragma warning disable CS8603 // Possible null reference return. Reason: Obsolete. - get => null; -#pragma warning restore CS8603 // Possible null reference return. Reason: Obsolete. - set - { - if (value == null) - { - return; - } + public required string units { get; set; } - start = new Point(value[0], value[1], value[2]); - end = new Point(value[3], value[4], value[5]); - } - } - - public double area { get; set; } - - public string units { get; set; } - - public Point start { get; set; } - public Point end { get; set; } + public required Point start { get; set; } + public required Point end { get; set; } public Interval domain { get; set; } = Interval.UnitInterval; - public double length { get; set; } + public double length => Point.Distance(start, end); public Box? bbox { get; set; } @@ -96,7 +57,7 @@ public class Line : Base, ICurve, IHasBoundingBox, ITransformable end = transformedEnd, applicationId = applicationId, units = units, - domain = domain is null ? Interval.UnitInterval : new() { start = domain.start, end = domain.end } + domain = new() { start = domain.start, end = domain.end }, }; return true; } @@ -121,15 +82,41 @@ public class Line : Base, ICurve, IHasBoundingBox, ITransformable return list; } - public static Line FromList(List list) + public static Line FromList(IReadOnlyList list) { - var units = Units.GetUnitFromEncoding(list[list.Count - 1]); + var units = Units.GetUnitFromEncoding(list[^1]); var startPt = new Point(list[2], list[3], list[4], units); var endPt = new Point(list[5], list[6], list[7], units); - var line = new Line(startPt, endPt, units) + var line = new Line { - domain = new Interval { start = list[8], end = list[9] } + start = startPt, + end = endPt, + units = units, + domain = new Interval { start = list[8], end = list[9] }, }; return line; } + + /// + /// OBSOLETE - This is just here for backwards compatibility. + /// You should not use this for anything. Access coordinates using start and end point. + /// + [ + JsonProperty(NullValueHandling = NullValueHandling.Ignore), + Obsolete("Access coordinates using start and end point", true) + ] + public List? value + { + get => null; + set + { + if (value == null) + { + return; + } + + start = new Point(value[0], value[1], value[2], Units.Meters); + end = new Point(value[3], value[4], value[5], Units.Meters); + } + } } diff --git a/src/Speckle.Objects/Geometry/Mesh.cs b/src/Speckle.Objects/Geometry/Mesh.cs index 0b12498d..e28df42d 100644 --- a/src/Speckle.Objects/Geometry/Mesh.cs +++ b/src/Speckle.Objects/Geometry/Mesh.cs @@ -1,79 +1,61 @@ +using System.Diagnostics.Contracts; using Speckle.Newtonsoft.Json; using Speckle.Objects.Other; +using Speckle.Objects.Utils; using Speckle.Sdk; using Speckle.Sdk.Common; -using Speckle.Sdk.Logging; using Speckle.Sdk.Models; namespace Speckle.Objects.Geometry; +/// More docs on notion [SpeckleType("Objects.Geometry.Mesh")] public class Mesh : Base, IHasBoundingBox, IHasVolume, IHasArea, ITransformable { - public Mesh() { } + /// + /// Flat list of vertex data (flat x,y,z,x,y,z... list) + /// + [DetachProperty, Chunkable(31250)] + public required List vertices { get; set; } /// - /// Constructs a new mesh from it's raw values. + /// Flat list of face data
+ /// Each face starts with the length of the face (e.g. 3 in the case of triangles), followed by that many indices ///
- /// - /// - /// - /// - /// - /// - public Mesh( - List vertices, - List faces, - List? colors = null, - List? texture_coords = null, - string units = Units.Meters, - string? applicationId = null - ) - { - this.vertices = vertices; - this.faces = faces; - this.colors = colors ?? this.colors; - textureCoordinates = texture_coords ?? textureCoordinates; - this.applicationId = applicationId; - this.units = units; - } - - [Obsolete("Use lists constructor", true)] - public Mesh( - double[] vertices, - int[] faces, - int[]? colors = null, - double[]? texture_coords = null, - string units = Units.Meters, - string? applicationId = null - ) - : this( - vertices.ToList(), - faces.ToList(), - colors?.ToList() ?? new(), - texture_coords?.ToList() ?? new(), - units, - applicationId - ) { } - - [DetachProperty, Chunkable(31250)] - public List vertices { get; set; } = new(); - + /// + /// N-gons are supported, but large values of n (> ~50) tend to cause significant performance problems for consumers (e.g. HostApps and . + /// + /// + /// [ + /// 3, 0, 1, 2, //first face, a triangle (3-gon) + /// 4, 1, 2, 3, 4, //second face, a quad (4-gon) + /// 6, 4, 5, 6, 7, 8, 9, //third face, an n-gon (6-gon) + /// ]; [DetachProperty, Chunkable(62500)] - public List faces { get; set; } = new(); + public required List faces { get; set; } - /// Vertex colors as ARGB s + /// Vertex colors as ARGB s + /// Expected that there are either 1 color per vertex, or an empty [DetachProperty, Chunkable(62500)] public List colors { get; set; } = new(); + /// Flat list of texture coordinates (flat u,v,u,v,u,v... list) + /// Expected that there are either 1 texture coordinate per vertex, or an empty [DetachProperty, Chunkable(31250)] public List textureCoordinates { get; set; } = new(); + /// + /// Flat list of vertex normal data (flat x,y,z,x,y,z... list) + /// Expected that there are either 1 texture coordinate per vertex, or an empty + /// + [DetachProperty, Chunkable(31250)] + public List vertexNormals { get; set; } = new(); + /// /// The unit's this is in. /// This should be one of /// - public string units { get; set; } = Units.None; + public required string units { get; set; } /// public double area { get; set; } @@ -117,7 +99,7 @@ public class Mesh : Base, IHasBoundingBox, IHasVolume, IHasArea, ITransformable< applicationId = applicationId ?? id, faces = faces, colors = colors, - units = units + units = units, }; transformed["renderMaterial"] = this["renderMaterial"]; @@ -132,8 +114,6 @@ public class Mesh : Base, IHasBoundingBox, IHasVolume, IHasArea, ITransformable< return res; } - #region Convenience Methods - [JsonIgnore] public int VerticesCount => vertices.Count / 3; @@ -145,6 +125,8 @@ public class Mesh : Base, IHasBoundingBox, IHasVolume, IHasArea, ITransformable< ///
/// The index of the vertex /// Vertex as a + /// It is usually recommended to instead consume the list manually for better performance + [Pure] public Point GetPoint(int index) { index *= 3; @@ -153,6 +135,8 @@ public class Mesh : Base, IHasBoundingBox, IHasVolume, IHasArea, ITransformable< /// as list of s /// when list is malformed + /// It is usually recommended to instead consume the list manually for better performance + [Pure] public List GetPoints() { if (vertices.Count % 3 != 0) @@ -176,78 +160,10 @@ public class Mesh : Base, IHasBoundingBox, IHasVolume, IHasArea, ITransformable< ///
/// The index of the texture coordinate /// Texture coordinate as a + [Pure] public (double, double) GetTextureCoordinate(int index) { index *= 2; return (textureCoordinates[index], textureCoordinates[index + 1]); } - - /// - /// If not already so, this method will align - /// such that a vertex and its corresponding texture coordinates have the same index. - /// This alignment is what is expected by most applications.
- ///
- /// - /// If the calling application expects - /// vertices.count == textureCoordinates.count - /// Then this method should be called by the MeshToNative method before parsing and - /// to ensure compatibility with geometry originating from applications that map to using vertex instance index (rather than vertex index) - ///
- /// , , and lists will be modified to contain no shared vertices (vertices shared between polygons) - ///
- public void AlignVerticesWithTexCoordsByIndex() - { - if (textureCoordinates.Count == 0) - { - return; - } - - if (TextureCoordinatesCount == VerticesCount) - { - return; //Tex-coords already aligned as expected - } - - var facesUnique = new List(faces.Count); - var verticesUnique = new List(TextureCoordinatesCount * 3); - bool hasColors = colors.Count > 0; - var colorsUnique = hasColors ? new List(TextureCoordinatesCount) : null; - - int nIndex = 0; - while (nIndex < faces.Count) - { - int n = faces[nIndex]; - if (n < 3) - { - n += 3; // 0 -> 3, 1 -> 4 - } - - if (nIndex + n >= faces.Count) - { - break; //Malformed face list - } - - facesUnique.Add(n); - for (int i = 1; i <= n; i++) - { - int vertIndex = faces[nIndex + i]; - int newVertIndex = verticesUnique.Count / 3; - - var (x, y, z) = GetPoint(vertIndex); - verticesUnique.Add(x); - verticesUnique.Add(y); - verticesUnique.Add(z); - - colorsUnique?.Add(colors[vertIndex]); - facesUnique.Add(newVertIndex); - } - - nIndex += n + 1; - } - - vertices = verticesUnique; - colors = colorsUnique ?? colors; - faces = facesUnique; - } - - #endregion } diff --git a/src/Speckle.Objects/Geometry/Plane.cs b/src/Speckle.Objects/Geometry/Plane.cs index d270c3ef..2da4b1bf 100644 --- a/src/Speckle.Objects/Geometry/Plane.cs +++ b/src/Speckle.Objects/Geometry/Plane.cs @@ -5,7 +5,7 @@ using Speckle.Sdk.Models; namespace Speckle.Objects.Geometry; /// -/// A 3-dimensional Plane consisting of an origin , and 3 as it's X, Y and Z axis. +/// A 3-dimensional Plane consisting of an origin , and 3 as its X, Y and Z axis. /// [SpeckleType("Objects.Geometry.Plane")] public class Plane : Base, ITransformable @@ -50,7 +50,7 @@ public class Plane : Base, ITransformable xdir = transformedXdir, ydir = transformedYdir, applicationId = applicationId, - units = units + units = units, }; return true; @@ -87,9 +87,9 @@ public class Plane : Base, ITransformable ///
/// The list of values representing this plane /// A new with the provided values. - public static Plane FromList(List list) + public static Plane FromList(IReadOnlyList list) { - var units = Units.GetUnitFromEncoding(list[list.Count - 1]); + var units = Units.GetUnitFromEncoding(list[^1]); var plane = new Plane { origin = new Point(list[0], list[1], list[2], units), diff --git a/src/Speckle.Objects/Geometry/Point.cs b/src/Speckle.Objects/Geometry/Point.cs index 24ea8673..9911ce73 100644 --- a/src/Speckle.Objects/Geometry/Point.cs +++ b/src/Speckle.Objects/Geometry/Point.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Speckle.Newtonsoft.Json; using Speckle.Objects.Other; using Speckle.Sdk.Common; @@ -12,7 +13,7 @@ namespace Speckle.Objects.Geometry; /// TODO: The Point class does not override the Equality operator, which means that there may be cases where `Equals` is used instead of `==`, as the comparison will be done by reference, not value. /// [SpeckleType("Objects.Geometry.Point")] -public class Point : Base, ITransformable +public class Point : Base, ITransformable, IEquatable { /// public Point() { } @@ -25,7 +26,8 @@ public class Point : Base, ITransformable /// The z coordinate /// The units of the point's coordinates. Defaults to Meters. /// The object's unique application ID - public Point(double x, double y, double z = 0d, string units = Units.Meters, string? applicationId = null) + [SetsRequiredMembers] + public Point(double x, double y, double z, string units, string? applicationId = null) { this.x = x; this.y = y; @@ -34,48 +36,26 @@ public class Point : Base, ITransformable this.units = units; } - /// - /// Constructs a new from a - /// - /// The Vector whose coordinates will be used for the Point - public Point(Vector vector) - : this(vector.x, vector.y, vector.z, vector.units, vector.applicationId) { } - - /// - /// Gets or sets the coordinates of the - /// - [JsonProperty(NullValueHandling = NullValueHandling.Ignore), Obsolete("Use x,y,z properties instead", true)] - public List value - { - get => null!; - set - { - x = value[0]; - y = value[1]; - z = value.Count > 2 ? value[2] : 0; - } - } - /// /// The x coordinate of the point. /// - public double x { get; set; } + public required double x { get; set; } /// /// The y coordinate of the point. /// - public double y { get; set; } + public required double y { get; set; } /// /// The z coordinate of the point. /// - public double z { get; set; } + public required double z { get; set; } /// /// The units this is in. /// This should be one of the units specified in /// - public string units { get; set; } + public required string units { get; set; } /// public bool TransformTo(Transform transform, out Point transformed) @@ -88,7 +68,7 @@ public class Point : Base, ITransformable var y = (this.x * matrix.M21 + this.y * matrix.M22 + this.z * matrix.M23 + unitFactor * matrix.M24) / divisor; var z = (this.x * matrix.M31 + this.y * matrix.M32 + this.z * matrix.M33 + unitFactor * matrix.M34) / divisor; - transformed = new Point(x, y, z) { units = units, applicationId = applicationId }; + transformed = new Point(x, y, z, units, applicationId); return true; } @@ -109,6 +89,11 @@ public class Point : Base, ITransformable return new List { x, y, z }; } + public Vector ToVector() + { + return new Vector(x, y, z, units, applicationId); + } + /// /// Creates a new based on a list of coordinates and the unit they're drawn in. /// @@ -216,20 +201,47 @@ public class Point : Base, ITransformable return Math.Sqrt(Math.Pow(x - point.x, 2) + Math.Pow(y - point.y, 2) + Math.Pow(z - point.z, 2)); } - public override bool Equals(object obj) + public bool Equals(Point? other) => this == other; + + public override bool Equals(object? obj) { if (ReferenceEquals(this, obj)) { return true; } - if (ReferenceEquals(obj, null)) + if (obj is Point p) { - return false; + return this == p; } - return this == (Point)obj; + return false; } - public override int GetHashCode() => HashCode.Of(units).And(x).And(y).And(y); + public override int GetHashCode() + { +#if NETSTANDARD2_0 + return HashCode.Of(units).And(x).And(y).And(y); +#else + return HashCode.Combine(units, x, y, z); +#endif + } + + [Obsolete($"Use {nameof(Vector.ToPoint)}", true)] + public Point(Vector _) { } + + /// + /// Gets or sets the coordinates of the + /// + [JsonProperty(NullValueHandling = NullValueHandling.Ignore), Obsolete("Use x,y,z properties instead", true)] + public List value + { + get => null!; + set + { + x = value[0]; + y = value[1]; + z = value.Count > 2 ? value[2] : 0; + } + } } diff --git a/src/Speckle.Objects/Geometry/Pointcloud.cs b/src/Speckle.Objects/Geometry/Pointcloud.cs index 30969db9..ac76f174 100644 --- a/src/Speckle.Objects/Geometry/Pointcloud.cs +++ b/src/Speckle.Objects/Geometry/Pointcloud.cs @@ -1,8 +1,6 @@ using Speckle.Objects.Other; using Speckle.Sdk; using Speckle.Sdk.Common; -using Speckle.Sdk.Host; -using Speckle.Sdk.Logging; using Speckle.Sdk.Models; namespace Speckle.Objects.Geometry; @@ -13,27 +11,11 @@ namespace Speckle.Objects.Geometry; [SpeckleType("Objects.Geometry.Pointcloud")] public class Pointcloud : Base, IHasBoundingBox, ITransformable { - /// - /// Constructs an empty - /// - public Pointcloud() { } - - /// Flat list of x,y,z coordinates - /// Optional list of colors - /// Optional list of sizes - [SchemaInfo(nameof(Pointcloud), "Create a point cloud object", "Objects", "Geometry")] - public Pointcloud(List points, List? colors = null, List? sizes = null) - { - this.points = points; - this.colors = colors ?? new(); - this.sizes = sizes ?? new(); - } - /// /// Gets or sets the list of points of this , stored as a flat list of coordinates [x1,y1,z1,x2,y2,...] /// [DetachProperty, Chunkable(31250)] - public List points { get; set; } = new(); + public required List points { get; set; } /// /// Gets or sets the list of colors of this 's points., stored as ARGB s. @@ -51,7 +33,7 @@ public class Pointcloud : Base, IHasBoundingBox, ITransformable /// The unit's this is in. /// This should be one of /// - public string units { get; set; } + public required string units { get; set; } /// public Box? bbox { get; set; } @@ -73,7 +55,7 @@ public class Pointcloud : Base, IHasBoundingBox, ITransformable points = transformedPoints.SelectMany(o => o.ToList()).ToList(), colors = colors, sizes = sizes, - applicationId = applicationId + applicationId = applicationId, }; return true; diff --git a/src/Speckle.Objects/Geometry/Polycurve.cs b/src/Speckle.Objects/Geometry/Polycurve.cs index 82c63ca6..d081b651 100644 --- a/src/Speckle.Objects/Geometry/Polycurve.cs +++ b/src/Speckle.Objects/Geometry/Polycurve.cs @@ -66,7 +66,7 @@ public class Polycurve : Base, ICurve, IHasArea, IHasBoundingBox, ITransformable segments = transformed, applicationId = applicationId, closed = closed, - units = units + units = units, }; return success; @@ -79,27 +79,37 @@ public class Polycurve : Base, ICurve, IHasArea, IHasBoundingBox, ITransformable /// A with the same shape as the provided polyline. public static implicit operator Polycurve(Polyline polyline) { - Polycurve polycurve = - new() - { - segments = new(), - units = polyline.units, - area = polyline.area, - domain = polyline.domain, - closed = polyline.closed, - bbox = polyline.bbox, - length = polyline.length - }; + Polycurve polycurve = new() + { + segments = new(), + units = polyline.units, + area = polyline.area, + domain = polyline.domain, + closed = polyline.closed, + bbox = polyline.bbox, + length = polyline.length, + }; var points = polyline.GetPoints(); for (var i = 0; i < points.Count - 1; i++) { - var line = new Line(points[i], points[i + 1], polyline.units); + var line = new Line + { + start = points[i], + end = points[i + 1], + units = polyline.units, + }; polycurve.segments.Add(line); } + if (polyline.closed) { - var line = new Line(points[^1], points[0], polyline.units); + var line = new Line + { + start = points[^1], + end = points[0], + units = polyline.units, + }; polycurve.segments.Add(line); } diff --git a/src/Speckle.Objects/Geometry/Polyline.cs b/src/Speckle.Objects/Geometry/Polyline.cs index 3f96c9fa..6c3dac9f 100644 --- a/src/Speckle.Objects/Geometry/Polyline.cs +++ b/src/Speckle.Objects/Geometry/Polyline.cs @@ -59,7 +59,7 @@ public class Polyline : Base, ICurve, IHasArea, IHasBoundingBox, ITransformable value = transformedPoints.SelectMany(o => o.ToList()).ToList(), closed = closed, applicationId = applicationId, - units = units + units = units, }; return true; @@ -118,7 +118,7 @@ public class Polyline : Base, ICurve, IHasArea, IHasBoundingBox, ITransformable closed = (int)list[2] == 1, domain = new Interval { start = list[3], end = list[4] }, value = list.GetRange(6, pointCount), - units = Units.GetUnitFromEncoding(list[^1]) + units = Units.GetUnitFromEncoding(list[^1]), }; } } diff --git a/src/Speckle.Objects/Geometry/PolylineExtensions.cs b/src/Speckle.Objects/Geometry/PolylineExtensions.cs index c4ba0c34..eb64ff92 100644 --- a/src/Speckle.Objects/Geometry/PolylineExtensions.cs +++ b/src/Speckle.Objects/Geometry/PolylineExtensions.cs @@ -13,7 +13,12 @@ public static class PolylineExtensions Point previousPoint = points[0]; for (int i = 1; i < points.Count; i++) { - yield return new Line(previousPoint, points[i], polyline.units); + yield return new Line() + { + start = previousPoint, + end = points[i], + units = polyline.units, + }; previousPoint = points[i]; } } diff --git a/src/Speckle.Objects/Geometry/Spiral.cs b/src/Speckle.Objects/Geometry/Spiral.cs index 35d9d944..9b4ccf8a 100644 --- a/src/Speckle.Objects/Geometry/Spiral.cs +++ b/src/Speckle.Objects/Geometry/Spiral.cs @@ -1,4 +1,5 @@ using Speckle.Objects.Primitive; +using Speckle.Sdk.Common; using Speckle.Sdk.Models; namespace Speckle.Objects.Geometry; @@ -14,28 +15,28 @@ public enum SpiralType CubicParabola, Radioid, Sinusoid, - Unknown + Unknown, } [SpeckleType("Objects.Geometry.Spiral")] public class Spiral : Base, ICurve, IHasBoundingBox, IDisplayValue { - public Point startPoint { get; set; } - public Point endPoint { get; set; } - public Plane plane { get; set; } // plane with origin at spiral center - public double turns { get; set; } // total angle of spiral. positive is counterclockwise, negative is clockwise - public Vector pitchAxis { get; set; } = new(); - public double pitch { get; set; } - public SpiralType spiralType { get; set; } + public required Point startPoint { get; set; } + public required Point endPoint { get; set; } + public required Plane plane { get; set; } // plane with origin at spiral center + public required double turns { get; set; } // total angle of spiral. positive is counterclockwise, negative is clockwise + public required Vector pitchAxis { get; set; } = new(0, 0, 0, Units.None); + public required double pitch { get; set; } + public required SpiralType spiralType { get; set; } - public string units { get; set; } + public required string units { get; set; } - public double length { get; set; } + public required double length { get; set; } - public Interval domain { get; set; } + public required Interval domain { get; set; } [DetachProperty] - public Polyline displayValue { get; set; } + public required Polyline displayValue { get; set; } public Box? bbox { get; set; } } diff --git a/src/Speckle.Objects/Geometry/Surface.cs b/src/Speckle.Objects/Geometry/Surface.cs index 896b7988..d97ed633 100644 --- a/src/Speckle.Objects/Geometry/Surface.cs +++ b/src/Speckle.Objects/Geometry/Surface.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Speckle.Objects.Other; using Speckle.Objects.Primitive; using Speckle.Sdk.Common; @@ -11,45 +12,43 @@ namespace Speckle.Objects.Geometry; [SpeckleType("Objects.Geometry.Surface")] public class Surface : Base, IHasBoundingBox, IHasArea, ITransformable { - /// - /// Constructs a new empty - /// + [Obsolete("Constructor should only be used by serializer, use one of the other constructors instead")] public Surface() { - applicationId = null; - pointData = new List(); + pointData = []; } - /// - /// Constructs a new empty - /// - /// The units this surface is modeled in - /// This surface's unique identifier on a specific application - public Surface(string units = Units.Meters, string? applicationId = null) + public Surface(List> controlPoints) { - this.applicationId = applicationId; - this.units = units; + SetControlPoints(controlPoints); + } + + public Surface(IList pointData, int countU, int countV) + { + this.pointData = pointData; + this.countU = countU; + this.countV = countV; } /// /// The degree of the surface in the U direction /// - public int degreeU { get; set; } + public required int degreeU { get; set; } /// /// The degree of the surface in the V direction /// - public int degreeV { get; set; } + public required int degreeV { get; set; } /// /// Determines if the is rational. /// - public bool rational { get; set; } + public required bool rational { get; set; } /// - /// The raw data of the surface's control points. Use GetControlPoints or SetControlPoints instead of accessing this directly. + /// The raw data of the surface's control points. Use or instead of accessing this directly. /// - public List pointData { get; set; } + public IList pointData { get; set; } /// /// The number of control points in the U direction @@ -64,38 +63,38 @@ public class Surface : Base, IHasBoundingBox, IHasArea, ITransformable /// /// The knot vector in the U direction /// - public List knotsU { get; set; } + public required List knotsU { get; set; } /// /// The knot vector in the V direction /// - public List knotsV { get; set; } + public required List knotsV { get; set; } /// /// The surface's domain in the U direction /// - public Interval domainU { get; set; } + public required Interval domainU { get; set; } /// /// The surface's domain in the V direction /// - public Interval domainV { get; set; } + public required Interval domainV { get; set; } /// /// Determines if a surface is closed around the . /// - public bool closedU { get; set; } + public required bool closedU { get; set; } /// /// Determines if a surface is closed around the /// - public bool closedV { get; set; } + public required bool closedV { get; set; } /// /// The unit's this is in. /// This should be one of /// - public string units { get; set; } + public required string units { get; set; } /// public double area { get; set; } @@ -116,7 +115,7 @@ public class Surface : Base, IHasBoundingBox, IHasArea, ITransformable } } - transformed = new Surface + transformed = new Surface(ptMatrix) { degreeU = degreeU, degreeV = degreeV, @@ -129,9 +128,8 @@ public class Surface : Base, IHasBoundingBox, IHasArea, ITransformable domainV = domainV, knotsU = knotsU, knotsV = knotsV, - units = units + units = units, }; - transformed.SetControlPoints(ptMatrix); return true; } @@ -172,6 +170,9 @@ public class Surface : Base, IHasBoundingBox, IHasArea, ITransformable /// /// A 2-dimensional array of instances. /// The must be ordered following directions "[u][v]" + [MemberNotNull(nameof(pointData))] + [MemberNotNull(nameof(countU))] + [MemberNotNull(nameof(countV))] public void SetControlPoints(List> value) { List data = new(); @@ -229,29 +230,27 @@ public class Surface : Base, IHasBoundingBox, IHasArea, ITransformable /// A new with the provided values. public static Surface FromList(List list) { - var srf = new Surface + var pointCount = (int)list[11]; + var knotsUCount = (int)list[12]; + var knotsVCount = (int)list[13]; + var countU = (int)list[2]; + var countV = (int)list[3]; + + var pointData = list.GetRange(14, pointCount); + var u = list[^1]; + + return new Surface(pointData, countU, countV) { degreeU = (int)list[0], degreeV = (int)list[1], - countU = (int)list[2], - countV = (int)list[3], rational = list[4] == 1, closedU = list[5] == 1, closedV = list[6] == 1, domainU = new Interval { start = list[7], end = list[8] }, - domainV = new Interval { start = list[9], end = list[10] } + domainV = new Interval { start = list[9], end = list[10] }, + knotsU = list.GetRange(14 + pointCount, knotsUCount), + knotsV = list.GetRange(14 + pointCount + knotsUCount, knotsVCount), + units = Units.GetUnitFromEncoding(u), }; - - var pointCount = (int)list[11]; - var knotsUCount = (int)list[12]; - var knotsVCount = (int)list[13]; - - srf.pointData = list.GetRange(14, pointCount); - srf.knotsU = list.GetRange(14 + pointCount, knotsUCount); - srf.knotsV = list.GetRange(14 + pointCount + knotsUCount, knotsVCount); - - var u = list[list.Count - 1]; - srf.units = Units.GetUnitFromEncoding(u); - return srf; } } diff --git a/src/Speckle.Objects/Geometry/Vector.cs b/src/Speckle.Objects/Geometry/Vector.cs index a63e5fb9..a0c06698 100644 --- a/src/Speckle.Objects/Geometry/Vector.cs +++ b/src/Speckle.Objects/Geometry/Vector.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Speckle.Newtonsoft.Json; using Speckle.Objects.Other; using Speckle.Sdk.Common; @@ -21,24 +22,8 @@ public class Vector : Base, IHasBoundingBox, ITransformable /// The y coordinate of the vector /// The units the coordinates are in. /// The unique application ID of the object. - public Vector(double x, double y, string units = Units.Meters, string? applicationId = null) - { - this.x = x; - this.y = y; - z = 0; - this.applicationId = applicationId; - this.units = units; - } - - /// - /// Constructs a new 2D from it's X and Y coordinates. - /// - /// The x coordinate of the vector - /// The y coordinate of the vector - /// The y coordinate of the vector - /// The units the coordinates are in. - /// The unique application ID of the object. - public Vector(double x, double y, double z, string units = Units.Meters, string? applicationId = null) + [SetsRequiredMembers] + public Vector(double x, double y, double z, string units, string? applicationId = null) { this.x = x; this.y = y; @@ -47,54 +32,26 @@ public class Vector : Base, IHasBoundingBox, ITransformable this.units = units; } - /// - /// Constructs a new from a - /// - /// The point whose coordinates will be used - /// The unique application ID of the object. - public Vector(Point point, string? applicationId = null) - : this(point.x, point.y, point.z, point.units ?? Units.None, applicationId) { } - - /// - /// Gets or sets the coordinates of the vector - /// - [ - JsonProperty(NullValueHandling = NullValueHandling.Ignore), - Obsolete("Use X,Y,Z fields to access coordinates instead", true) - ] - public List value - { -#pragma warning disable CS8603 // Possible null reference return. - get => null; -#pragma warning restore CS8603 // Possible null reference return. - set - { - x = value[0]; - y = value[1]; - z = value.Count > 2 ? value[2] : 0; - } - } - /// /// The unit's this is in. /// This should be one of /// - public string units { get; set; } = Units.None; + public required string units { get; set; } /// /// The x coordinate of the vector. /// - public double x { get; set; } + public required double x { get; set; } /// /// The y coordinate of the vector. /// - public double y { get; set; } + public required double y { get; set; } /// /// The z coordinate of the vector. /// - public double z { get; set; } + public required double z { get; set; } /// /// Gets the Euclidean length of this vector. @@ -129,10 +86,9 @@ public class Vector : Base, IHasBoundingBox, ITransformable /// Returns the coordinates of this as a list of numbers /// /// A list of coordinates {x, y, z} - public List ToList() - { - return new List { x, y, z }; - } + public List ToList() => [x, y, z]; + + public Point ToPoint() => new(x, y, z, units, applicationId); /// /// Creates a new vector based on a list of coordinates and the unit they're drawn in. @@ -140,7 +96,7 @@ public class Vector : Base, IHasBoundingBox, ITransformable /// The list of coordinates {x, y, z} /// The units the coordinates are in /// A new with the provided coordinates. - public static Vector FromList(List list, string units) + public static Vector FromList(IReadOnlyList list, string units) { return new Vector(list[0], list[1], list[2], units); } @@ -202,22 +158,16 @@ public class Vector : Base, IHasBoundingBox, ITransformable /// Vector result of the cross product. public static Vector CrossProduct(Vector u, Vector v) { + if (u.units != v.units && u.units != Units.None && v.units != Units.None) + { + throw new ArgumentException("Cannot perform cross product on two vectors with different unit systems"); + } + var x = u.y * v.z - u.z * v.y; var y = u.z * v.x - u.x * v.z; var z = u.x * v.y - u.y * v.x; - return new Vector(x, y, z); - } - - public static double Angle(Vector u, Vector v) - { - return Math.Acos(DotProduct(u, v) / (u.Length * v.Length)); - } - - [Obsolete("Renamed to " + nameof(Normalize), true)] - public void Unitize() - { - Normalize(); + return new Vector(x, y, z, units: u.units); } /// @@ -245,16 +195,22 @@ public class Vector : Base, IHasBoundingBox, ITransformable } /// - /// Returns a normalized copy of this vector. + /// Gets or sets the coordinates of the vector /// - /// A copy of this vector unitized. - public Vector Unit() + [ + JsonProperty(NullValueHandling = NullValueHandling.Ignore), + Obsolete("Use X,Y,Z fields to access coordinates instead", true) + ] + public List value { - return this / Length; - } - - public static Vector Divide(Vector left, Vector right) - { - throw new NotImplementedException(); +#pragma warning disable CS8603 // Possible null reference return. + get => null; +#pragma warning restore CS8603 // Possible null reference return. + set + { + x = value[0]; + y = value[1]; + z = value.Count > 2 ? value[2] : 0; + } } } diff --git a/src/Speckle.Objects/Interfaces.cs b/src/Speckle.Objects/Interfaces.cs index 8610ad4c..b2df1903 100644 --- a/src/Speckle.Objects/Interfaces.cs +++ b/src/Speckle.Objects/Interfaces.cs @@ -1,4 +1,3 @@ -using Speckle.Objects.BuiltElements; using Speckle.Objects.Geometry; using Speckle.Objects.Other; using Speckle.Objects.Primitive; @@ -84,31 +83,19 @@ public interface ITransformable : ISpeckleObject bool TransformTo(Transform transform, out ITransformable transformed); } -#endregion - -#region GIS -public interface IGisFeature : ISpeckleObject -{ - Base attributes { get; set; } -} - -#endregion - -#region Built elements - /// -/// Specifies displayable value(s) to be used as a fallback +/// Specifies displayable simple geometries to be used as a fallback /// if a displayable form cannot be converted. /// /// /// objects that represent conceptual / abstract / mathematically derived geometry /// can use to be used in case the object lacks a natively displayable form. -/// (e.g , , ) +/// (e.g ) /// /// /// Type of display value. /// Expected to be either a type or a of s, -/// most likely or . +/// Should be constrained to types of , , or . /// public interface IDisplayValue : ISpeckleObject { @@ -119,16 +106,77 @@ public interface IDisplayValue : ISpeckleObject T displayValue { get; } } +#endregion + +#region Data objects + /// -/// Represents a calculated object for civil disciplines +/// Specifies properties on objects to be used for data-based workflows /// -public interface ICivilCalculatedObject : ISpeckleObject +public interface IProperties : ISpeckleObject +{ + Dictionary properties { get; } +} + +public interface IDataObject : IProperties, IDisplayValue> { /// - /// for this calculated object. + /// The name of the object, primarily used to decorate the object for consumption in frontend and other apps /// - List codes { get; set; } + string name { get; } } +public interface IRevitObject : IDataObject +{ + string type { get; } + + string family { get; } + + string category { get; } + + Base? location { get; } + + IReadOnlyList elements { get; } +} + +public interface ICivilObject : IDataObject +{ + string type { get; } + + List? baseCurves { get; } + + IReadOnlyList elements { get; } +} + +public interface ITeklaObject : IDataObject +{ + string type { get; } + + IReadOnlyList elements { get; } +} + +public interface ICsiObject : IDataObject +{ + string type { get; } + + IReadOnlyList elements { get; } +} + +public interface IGisObject : IDataObject +{ + string type { get; } +} + +public interface IArchicadObject : IDataObject +{ + string type { get; } + + string level { get; } + + IReadOnlyList elements { get; } +} + +public interface INavisworksObject : IDataObject { } + #endregion diff --git a/src/Speckle.Objects/Organization/DataTable.cs b/src/Speckle.Objects/Organization/DataTable.cs deleted file mode 100644 index e79a6d92..00000000 --- a/src/Speckle.Objects/Organization/DataTable.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Organization; - -[SpeckleType("Objects.Organization.DataTable")] -public class DataTable : Base -{ - public DataTable() { } - - public int columnCount => columnMetadata.Count; - public int rowCount => rowMetadata.Count; - public int headerRowIndex { get; set; } - public string name { get; set; } - public List rowMetadata { get; set; } = new List(); - public List columnMetadata { get; set; } = new List(); - public List> data { get; set; } = new List>(); - - public void AddRow(Base metadata, int index = -1, params object[] objects) - { - if (objects.Length != columnCount) - { - throw new ArgumentException( - $"\"AddRow\" method was passed {objects.Length} objects, but the DataTable has {columnCount} columns. Partial and extended table rows are not accepted by the DataTable object." - ); - } - - if (index < 0 || index >= data.Count) - { - data.Add(objects.ToList()); - rowMetadata.Add(metadata); - } - else - { - data.Insert(index, objects.ToList()); - rowMetadata.Insert(index, metadata); - } - } - - public void DefineColumn(Base metadata) - { - columnMetadata.Add(metadata); - } -} diff --git a/src/Speckle.Objects/Organization/Model.cs b/src/Speckle.Objects/Organization/Model.cs deleted file mode 100644 index 5bdf6bb0..00000000 --- a/src/Speckle.Objects/Organization/Model.cs +++ /dev/null @@ -1,73 +0,0 @@ -using Speckle.Objects.Other; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Organization; - -/// -/// Basic model info class -/// It contains general information about the model and can be extended or subclassed to include more application-specific -/// information. -/// -[SpeckleType("Objects.Organization.ModelInfo")] -public class ModelInfo : Base -{ - /// - /// The name of the model. - /// - public string name { get; set; } - - /// - /// The identifying number of the model. - /// - public string number { get; set; } - - // TODO: not sure about adding a typed `elements` list here? prob should let ppl add whatever named categories here? -} - -// TODO: not quite sure about this name? -/// -/// Extended to contain additional properties applicable to AEC projects. -/// -[SpeckleType("Objects.Organization.BIMModelInfo")] -public class BIMModelInfo : ModelInfo -{ - /// - /// The name of the client - /// - public string clientName { get; set; } - - /// - /// The name of the building - /// - public string buildingName { get; set; } - - /// - /// The status or phase of the model. - /// - public string status { get; set; } - - /// - /// The address of the model. - /// - public string address { get; set; } - - /// - /// The name of the site location as a string. - /// - public string siteName { get; set; } - - /// - /// The latitude of the site location in radians. - /// - public double latitude { get; set; } - - /// - /// The longitude of the site location in radians. - /// - public double longitude { get; set; } - - /// - /// A list of origin locations within this model as a list of s - /// - public List locations { get; set; } -} diff --git a/src/Speckle.Objects/Other/Block.cs b/src/Speckle.Objects/Other/Block.cs deleted file mode 100644 index db1f2061..00000000 --- a/src/Speckle.Objects/Other/Block.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Common; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Other; - -/// -/// Block definition class -/// -[SpeckleType("Objects.Other.BlockDefinition")] -public class BlockDefinition : Base -{ - public BlockDefinition() { } - - [SchemaInfo("Block Definition", "A Speckle Block definition")] - public BlockDefinition(string name, List geometry, Point? basePoint = null) - { - this.name = name; - this.basePoint = basePoint ?? new() { units = Units.None }; - this.geometry = geometry; - } - - public string name { get; set; } - - /// - /// The definition base point of the block - /// - public Point basePoint { get; set; } = new() { units = Units.None }; - - [DetachProperty] - public List geometry { get; set; } - - public string units { get; set; } = Units.None; - - /// - /// Returns the translation transform of the base point to the internal origin [0,0,0] - /// - /// - public Transform GetBasePointTransform() - { - var translation = new Vector(-basePoint.x, -basePoint.y, -basePoint.z) { units = basePoint.units ?? Units.None }; - var transform = new Transform(new Vector(1, 0, 0), new Vector(0, 1, 0), new Vector(1, 0, 0), translation); - return transform; - } -} diff --git a/src/Speckle.Objects/Other/Civil/CivilDataField.cs b/src/Speckle.Objects/Other/Civil/CivilDataField.cs deleted file mode 100644 index 706b79df..00000000 --- a/src/Speckle.Objects/Other/Civil/CivilDataField.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Other.Civil; - -[SpeckleType("Objects.Other.Civil.CivilDataField")] -public class CivilDataField : DataField -{ - public CivilDataField() { } - - 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.displayName = displayName; - } - - /// - /// The context type of the Civil3D part - /// - 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 deleted file mode 100644 index ed55d821..00000000 --- a/src/Speckle.Objects/Other/DataField.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Other; - -/// -/// Generic class for a data field -/// -[SpeckleType("Objects.Other.DataField")] -public class DataField : Base -{ - public DataField() { } - - public DataField(string name, string type, object? value, string? units = null) - { - this.name = name; - this.type = type; - this.value = value; - this.units = units; - } - - public string name { get; set; } - - public string type { get; set; } - - public object? value { get; set; } - - public string? units { get; set; } -} diff --git a/src/Speckle.Objects/Other/Dimension.cs b/src/Speckle.Objects/Other/Dimension.cs deleted file mode 100644 index b5b2f5e3..00000000 --- a/src/Speckle.Objects/Other/Dimension.cs +++ /dev/null @@ -1,100 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Other; - -/// -/// Dimension class -/// -[SpeckleType("Objects.Other.Dimension")] -public class Dimension : Base, IDisplayValue> -{ - /// - /// The measurement of the dimension. - /// - public double measurement { get; set; } - - /// - /// The text of the dimension, without any formatting - /// - public string value { get; set; } - - /// - /// The text of the dimension, with rtf formatting - /// - public string richText { get; set; } - - /// - /// The position of the dimension - /// - public Point position { get; set; } - - /// - /// The position of the text of the dimension - /// - public Point textPosition { get; set; } - - public string units { get; set; } - - /// - /// Curves representing the annotation - /// - public List displayValue { get; set; } = new(); -} - -/// -/// Dimension class measuring a distance -/// -[SpeckleType("Objects.Other.DistanceDimension")] -public class DistanceDimension : Dimension -{ - /// - /// The unitized normal of the dimension. - /// - public Vector direction { get; set; } - - /// - /// Indicates if this dimension is an ordinate dimension - /// - /// Ordinate dimensions (measuring distance between two points exclusively along the x or y axis) - /// are in practice drawn with different conventions than linear dimensions, and are treated as a special subset of them. - public bool isOrdinate { get; set; } - - /// - /// The objects being measured. - /// - /// - /// Distance measurements are between two points - /// - public List measured { get; set; } -} - -/// -/// Dimension class measuring a length -/// -[SpeckleType("Objects.Other.LengthDimension")] -public class LengthDimension : Dimension -{ - /// - /// The objects being measured. - /// - /// - /// For length dimensions, this is a curve - /// - public ICurve measured { get; set; } -} - -/// -/// Dimension class measuring an angle -/// -[SpeckleType("Objects.Other.AngleDimension")] -public class AngleDimension : Dimension -{ - /// - /// The objects being measured. - /// - /// - /// For angle dimensions, this is two lines - /// - public List measured { get; set; } -} diff --git a/src/Speckle.Objects/Other/DisplayStyle.cs b/src/Speckle.Objects/Other/DisplayStyle.cs deleted file mode 100644 index c285b683..00000000 --- a/src/Speckle.Objects/Other/DisplayStyle.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Drawing; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Other; - -/// -/// Minimal display style class. Developed primarily for display styles in Rhino and AutoCAD. -/// Rhino object attributes uses OpenNURBS definition for linetypes and lineweights -/// -[SpeckleType("Objects.Other.DisplayStyle")] -public class DisplayStyle : Base -{ - public string name { get; set; } - public int color { get; set; } = Color.LightGray.ToArgb(); // opacity assumed from a value - public string linetype { get; set; } - - /// - /// The plot weight in the style units - /// - /// A value of 0 indicates a default weight, and -1 indicates an invisible line - public double lineweight { get; set; } - - public string units { get; set; } -} diff --git a/src/Speckle.Objects/Other/Hatch.cs b/src/Speckle.Objects/Other/Hatch.cs deleted file mode 100644 index ca48ff24..00000000 --- a/src/Speckle.Objects/Other/Hatch.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Other; - -[SpeckleType("Objects.Other.Hatch")] -public class Hatch : Base -{ - [Obsolete("Use Loops instead")] - public List curves { get; set; } - - public List loops { get; set; } - public string pattern { get; set; } - public double scale { get; set; } = 1; - public double rotation { get; set; } // relative angle -} - -/// -/// Represents a Hatch Loop from a 's curve. -/// -[SpeckleType("Objects.Other.HatchLoop")] -public class HatchLoop : Base -{ - public HatchLoop() { } - - public HatchLoop(ICurve curve, HatchLoopType type) - { - Curve = curve; - Type = type; - } - - public ICurve Curve { get; set; } - public HatchLoopType Type { get; set; } -} - -/// -/// Represents the type of a loop in a 's curves. -/// -public enum HatchLoopType -{ - Unknown, - Outer, - Inner -} diff --git a/src/Speckle.Objects/Other/Instance.cs b/src/Speckle.Objects/Other/Instance.cs deleted file mode 100644 index 42503914..00000000 --- a/src/Speckle.Objects/Other/Instance.cs +++ /dev/null @@ -1,163 +0,0 @@ -using Speckle.Newtonsoft.Json; -using Speckle.Objects.Geometry; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; -using Speckle.Sdk.Models.GraphTraversal; - -namespace Speckle.Objects.Other; - -public abstract class Instance : Base -{ - protected Instance(Transform transform) - { - this.transform = transform ?? new Transform(); - } - - protected Instance() { } - - /// - /// The column-dominant 4x4 transform of this instance. - /// - /// - /// Indicates transform from internal origin [0,0,0] - /// - public Transform transform { get; set; } - - public abstract Base definition { get; internal set; } - - /// - /// The units of this Instance, should be the same as the instance transform units - /// - public string units { get; set; } - - // helper method that scans an Instance for all transformable geometry and nested instances - protected virtual IEnumerable GetTransformableGeometry() - { - var displayValueRule = TraversalRule - .NewTraversalRule() - .When(DefaultTraversal.HasDisplayValue) - .ContinueTraversing(_ => DefaultTraversal.DisplayValueAndElementsPropAliases); - - var instanceRule = TraversalRule - .NewTraversalRule() - .When(b => b is Instance instance && instance != null) - .ContinueTraversing(DefaultTraversal.None); - - var traversal = new GraphTraversal(instanceRule, displayValueRule, DefaultTraversal.DefaultRule); - - return traversal - .Traverse(definition) - .Select(tc => tc.Current) - .Where(b => b is ITransformable || b is Instance) - .Where(b => b != null); - } - - [SchemaComputed("transformedGeometry")] - public virtual IEnumerable GetTransformedGeometry() - { - return GetTransformableGeometry() - .SelectMany(b => - { - switch (b) - { - case Instance i: - return i.GetTransformedGeometry() - .Select(b => - { - b.TransformTo(transform, out var tranformed); - return tranformed; - }); - case ITransformable bt: - var res = bt.TransformTo(transform, out var transformed); - return res ? new List { transformed } : new(); - default: - return new List(); - } - }) - .Where(b => b != null); - } -} - -/// -/// Generic instance class -/// -public abstract class Instance : Instance - where T : Base -{ - protected Instance(T definition, Transform transform) - : base(transform) - { - typedDefinition = definition; - } - - protected Instance() - : base(new Transform()) { } - - [JsonIgnore] - public T typedDefinition { get; set; } - - [DetachProperty] - public override Base definition - { - get => typedDefinition; - internal set - { - if (value is T type) - { - typedDefinition = type; - } - } - } -} - -/// -/// Block instance class -/// -[SpeckleType("Objects.Other.BlockInstance")] -public class BlockInstance : Instance -{ - public BlockInstance() { } - - [SchemaInfo("Block Instance", "A Speckle Block Instance")] - public BlockInstance(BlockDefinition blockDefinition, Transform transform) - : base(blockDefinition, transform) - { - // OLD: TODO: need to verify - // Add base translation to transform. This assumes the transform is based on the world origin, - // whereas the instance transform assumes it contains the basePoint translation already. - //this.transform = transform * blockDefinition.GetBasePointTransform(); - } - - [DetachProperty, Obsolete("Use definition property", true), JsonIgnore] - public BlockDefinition blockDefinition - { - get => typedDefinition; - set => typedDefinition = value; - } - - protected override IEnumerable GetTransformableGeometry() - { - return typedDefinition.geometry; - } - - /// - /// Returns a plane representing the insertion point and orientation of this Block instance. - /// - /// This method will skip scaling. If you need scaling, we recommend using the transform instead. - /// A Plane on the insertion point of this Block Instance, with the correct 3-axis rotations. - [SchemaComputed("insertionPlane")] - public Plane GetInsertionPlane() - { - // TODO: UPDATE! - var plane = new Plane() - { - origin = typedDefinition.basePoint ?? new Point(0, 0, 0, units), - normal = new Vector(0, 0, 1, units), - xdir = new Vector(1, 0, 0, units), - ydir = new Vector(0, 1, 0, units), - units = units - }; - plane.TransformTo(transform, out Plane tPlane); - return tPlane; - } -} diff --git a/src/Speckle.Objects/Other/MappedBlockWrapper.cs b/src/Speckle.Objects/Other/MappedBlockWrapper.cs deleted file mode 100644 index 26a47d21..00000000 --- a/src/Speckle.Objects/Other/MappedBlockWrapper.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Speckle.Objects.BuiltElements.Revit; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Other; - -[SpeckleType("Objects.Other.MappedBlockWrapper")] -public class MappedBlockWrapper : Base -{ - public string category { get; set; } = RevitCategory.GenericModel.ToString(); - public string? nameOverride { get; set; } - public BlockInstance instance { get; set; } - - public MappedBlockWrapper() { } - - public MappedBlockWrapper(BlockInstance instance, RevitCategory category) - { - this.instance = instance; - this.category = category.ToString(); - } -} diff --git a/src/Speckle.Objects/Other/Material.cs b/src/Speckle.Objects/Other/Material.cs deleted file mode 100644 index de98844a..00000000 --- a/src/Speckle.Objects/Other/Material.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Other; - -/// -/// Generic class for materials containing generic parameters -/// -[SpeckleType("Objects.Other.Material")] -public class Material : Base -{ - public Material() { } - - [SchemaInfo("RevitMaterial", "Creates a Speckle material", "BIM", "Architecture")] - public Material(string name) - { - this.name = name; - } - - public string name { get; set; } -} diff --git a/src/Speckle.Objects/Other/MaterialQuantity.cs b/src/Speckle.Objects/Other/MaterialQuantity.cs deleted file mode 100644 index 900468f7..00000000 --- a/src/Speckle.Objects/Other/MaterialQuantity.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Other; - -[SpeckleType("Objects.Other.MaterialQuantity")] -public class MaterialQuantity : Base -{ - public MaterialQuantity() { } - - [SchemaInfo("MaterialQuantity", "Creates the quantity of a material")] - public MaterialQuantity(Material m, double volume, double area, string units) - { - material = m; - this.volume = volume; - this.area = area; - this.units = units; - } - - [DetachProperty] - public Material material { get; set; } - - public double volume { get; set; } - - /// - /// Area of the material on a element - /// - public double area { get; set; } - - /// - /// UnitMeasure of the quantity,e.g meters implies squaremeters for area and cubicmeters for the volume - /// - public string units { get; set; } -} diff --git a/src/Speckle.Objects/Other/RawEncoding.cs b/src/Speckle.Objects/Other/RawEncoding.cs new file mode 100644 index 00000000..97fc1532 --- /dev/null +++ b/src/Speckle.Objects/Other/RawEncoding.cs @@ -0,0 +1,23 @@ +using Speckle.Sdk.Models; + +namespace Speckle.Objects.Other; + +/// +/// Keeps track of a raw-encoded object in a native supported format. see +/// +[SpeckleType("Objects.Other.RawEncoding")] +public class RawEncoding : Base // note: at this stage, since we're using this for extrusions and subds the name doesn't make sense anymore +{ + public required string format { get; set; } + public required string contents { get; set; } + + public RawEncoding() { } +} + +/// +/// Supported encoding types "strongly" typed strings. This needs to match the extension of the file format. +/// +public static class RawEncodingFormats +{ + public const string RHINO_3DM = "3dm"; +} diff --git a/src/Speckle.Objects/Other/RenderMaterial.cs b/src/Speckle.Objects/Other/RenderMaterial.cs index a9b49af9..b114399a 100644 --- a/src/Speckle.Objects/Other/RenderMaterial.cs +++ b/src/Speckle.Objects/Other/RenderMaterial.cs @@ -1,6 +1,5 @@ using System.Drawing; using Speckle.Newtonsoft.Json; -using Speckle.Sdk.Host; using Speckle.Sdk.Models; using Speckle.Sdk.Models.Proxies; @@ -17,33 +16,13 @@ namespace Speckle.Objects.Other; [SpeckleType("Objects.Other.RenderMaterial")] public class RenderMaterial : Base { - public RenderMaterial() { } - - [SchemaInfo("RenderMaterial", "Creates a render material.", "BIM", "Other")] - public RenderMaterial( - double opacity = 1, - double metalness = 0, - double roughness = 1, - Color? diffuse = null, - Color? emissive = null - ) - { - this.opacity = opacity; - this.metalness = metalness; - this.roughness = roughness; - this.diffuse = diffuse.HasValue ? diffuse.Value.ToArgb() : Color.LightGray.ToArgb(); - this.emissive = emissive.HasValue ? emissive.Value.ToArgb() : Color.Black.ToArgb(); - } - - public string name { get; set; } + public required string name { get; set; } public double opacity { get; set; } = 1; public double metalness { get; set; } public double roughness { get; set; } = 1; - [SchemaIgnore] - public int diffuse { get; set; } = Color.LightGray.ToArgb(); + public required int diffuse { get; set; } = Color.LightGray.ToArgb(); - [SchemaIgnore] public int emissive { get; set; } = Color.Black.ToArgb(); [JsonIgnore] @@ -67,21 +46,13 @@ public class RenderMaterial : Base [SpeckleType("Objects.Other.RenderMaterialProxy")] public class RenderMaterialProxy : Base, IProxyCollection { - public RenderMaterialProxy() { } - - public RenderMaterialProxy(RenderMaterial renderMaterial, List objects) - { - value = renderMaterial; - this.objects = objects; - } - /// /// The list of application ids of objects that use this render material /// - public List objects { get; set; } + public required List objects { get; set; } /// /// The render material used by /// - public RenderMaterial value { get; set; } + public required RenderMaterial value { get; set; } } diff --git a/src/Speckle.Objects/Other/Revit/RevitInstance.cs b/src/Speckle.Objects/Other/Revit/RevitInstance.cs deleted file mode 100644 index d9dd67c7..00000000 --- a/src/Speckle.Objects/Other/Revit/RevitInstance.cs +++ /dev/null @@ -1,70 +0,0 @@ -using Speckle.Objects.BuiltElements; -using Speckle.Objects.BuiltElements.Revit; -using Speckle.Objects.Geometry; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Other.Revit; - -[SpeckleType("Objects.Other.Revit.RevitInstance")] -public class RevitInstance : Instance -{ - public Level level { get; set; } - public bool facingFlipped { get; set; } - public bool handFlipped { get; set; } - public bool mirrored { get; set; } - public Base parameters { get; set; } - public string elementId { get; set; } - - protected override IEnumerable GetTransformableGeometry() - { - var allChildren = typedDefinition.elements ?? new List(); // TODO: this is a bug, should be copying elements list instead of using the reference - if (typedDefinition.displayValue.Count != 0) - { - allChildren.AddRange(typedDefinition.displayValue); - } - - return allChildren; - } - - [SchemaComputed("transformedGeometry")] - public override IEnumerable GetTransformedGeometry() - { - var transformed = base.GetTransformedGeometry().ToList(); - - // add any dynamically attached elements on this instance - if ((this["elements"] ?? this["@elements"]) is List elements) - { - foreach (var element in elements) - { - if (((Base)element)["displayValue"] is List display) - { - transformed.AddRange(display.Cast()); - } - } - } - - return transformed; - } - - /// - /// Returns a plane representing the insertion point and orientation of this revit instance. - /// - /// This method will skip scaling. If you need scaling, we recommend using the transform instead. - /// A Plane on the insertion point of this Block Instance, with the correct 3-axis rotations. - [SchemaComputed("insertionPlane")] - public Plane GetInsertionPlane() - { - // TODO: Check for Revit in GH/DYN - var plane = new Plane() - { - origin = new Point(0, 0, 0, units), - normal = new Vector(0, 0, 1, units), - xdir = new Vector(1, 0, 0, units), - ydir = new Vector(0, 1, 0, units), - units = units, - }; - plane.TransformTo(transform, out Plane tPlane); - return tPlane; - } -} diff --git a/src/Speckle.Objects/Other/Revit/RevitMaterial.cs b/src/Speckle.Objects/Other/Revit/RevitMaterial.cs deleted file mode 100644 index e8d120b8..00000000 --- a/src/Speckle.Objects/Other/Revit/RevitMaterial.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Speckle.Objects.BuiltElements.Revit; -using Speckle.Objects.Utils; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Other.Revit; - -/// -/// Material in Revit defininf all revit properties from Autodesk.Revit.DB.Material -/// -[SpeckleType("Objects.Other.Revit.RevitMaterial")] -public class RevitMaterial : Material -{ - public RevitMaterial() { } - - [SchemaInfo("RevitMaterial", "Creates a Speckle material", "Revit", "Architecture")] - public RevitMaterial( - string name, - string category, - string materialclass, - int shiny, - int smooth, - int transparent, - List? parameters = null - ) - { - this.parameters = parameters?.ToBase(); - this.name = name; - materialCategory = category; - materialClass = materialclass; - shininess = shiny; - smoothness = smooth; - transparency = transparent; - } - - public string materialCategory { get; set; } - public string materialClass { get; set; } - - public int shininess { get; set; } - public int smoothness { get; set; } - public int transparency { get; set; } - - public Base? parameters { get; set; } -} diff --git a/src/Speckle.Objects/Other/Text.cs b/src/Speckle.Objects/Other/Text.cs deleted file mode 100644 index ccf21b19..00000000 --- a/src/Speckle.Objects/Other/Text.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Other; - -/// -/// Text class for Rhino and AutoCAD -/// -[SpeckleType("Objects.Other.Text")] -public class Text : Base, IDisplayValue> -{ - public Plane plane { get; set; } // origin should be center - public double rotation { get; set; } // using radians - public string value { get; set; } // text without RTF - public string richText { get; set; } - public double height { get; set; } - public string units { get; set; } - public List displayValue { get; set; } = new(); -} diff --git a/src/Speckle.Objects/Other/Transform.cs b/src/Speckle.Objects/Other/Transform.cs index 5f6f75fd..327118c0 100644 --- a/src/Speckle.Objects/Other/Transform.cs +++ b/src/Speckle.Objects/Other/Transform.cs @@ -1,11 +1,6 @@ using Speckle.DoubleNumerics; -using Speckle.Newtonsoft.Json; -using Speckle.Objects.Geometry; -using Speckle.Sdk; using Speckle.Sdk.Common; -using Speckle.Sdk.Logging; using Speckle.Sdk.Models; -using Vector = Speckle.Objects.Geometry.Vector; namespace Speckle.Objects.Other; @@ -15,86 +10,6 @@ namespace Speckle.Objects.Other; [SpeckleType("Objects.Other.Transform")] public class Transform : Base { - public Transform() { } - - /// - /// Construct a transform from a row-based double array of size 16 - /// - /// - /// - /// - public Transform(double[] value, string units = Units.None) - { - if (value.Length != 16) - { - throw new ArgumentException( - $"{nameof(Transform)}.{nameof(value)} array is malformed: expected length to be 16", - nameof(value) - ); - } - - matrix = CreateMatrix(value); - this.units = units; - } - - /// - /// Construct a transform from a row-based float array of size 16 - /// - /// - /// - /// - public Transform(float[] value, string units = Units.None) - { - if (value.Length != 16) - { - throw new SpeckleException($"{nameof(Transform)}.{nameof(value)} array is malformed: expected length to be 16"); - } - - matrix = CreateMatrix(value); - this.units = units; - } - - /// - /// Construct a transform from a 4x4 matrix and translation units - /// - /// - /// - public Transform(Matrix4x4 matrix, string units = Units.None) - { - this.matrix = matrix; - this.units = units; - } - - /// - /// Construct a transform given the x, y, and z bases and the translation vector - /// - /// - /// - /// - /// - public Transform(Vector x, Vector y, Vector z, Vector translation) - { - matrix = new Matrix4x4( - x.x, - y.x, - z.x, - translation.x, - x.y, - y.y, - z.y, - translation.y, - x.z, - y.z, - z.z, - translation.z, - 0f, - 0f, - 0f, - 1f - ); - units = translation.units; - } - /// /// The column-based 4x4 transform matrix /// @@ -102,87 +17,12 @@ public class Transform : Base /// Graphics based apps typically use column-based matrices, where the last column defines translation. /// Modelling apps may use row-based matrices, where the last row defines translation. Transpose if so. /// - public Matrix4x4 matrix { get; set; } = Matrix4x4.Identity; + public required Matrix4x4 matrix { get; set; } = Matrix4x4.Identity; /// /// Units for translation /// - public string units { get; set; } = Units.None; - - /// - /// Decomposes matrix into its scaling, rotation, and translation components - /// - /// - /// - /// - /// True if successful, false otherwise - public void Decompose(out Vector3 scale, out Quaternion rotation, out Vector4 translation) - { - // translation - translation = new Vector4(matrix.M14, matrix.M24, matrix.M34, matrix.M44); - - // scale - // this should account for non-uniform scaling - Vector4 basis4dX = new(matrix.M11, matrix.M21, matrix.M31, matrix.M41); - Vector4 basis4dY = new(matrix.M12, matrix.M22, matrix.M32, matrix.M42); - Vector4 basis4dZ = new(matrix.M13, matrix.M23, matrix.M33, matrix.M43); - - // Check for mirroring - Vector3 basisX = new(matrix.M11, matrix.M21, matrix.M31); - Vector3 basisY = new(matrix.M12, matrix.M22, matrix.M32); - Vector3 basisZ = new(matrix.M13, matrix.M23, matrix.M33); - // Negative determinant means flip on Z. - // TODO: Add tests and figure out exactly why this is. Jedd and myself have some theories but it would be nice to document this properly - double determinant = Vector3.Dot(Vector3.Cross(basisX, basisY), basisZ) < 0 ? -1 : 1; - - // Compute the scale, but only multiply the Z scale by the determinant to flag negative scaling on Z axis (see todo above) - scale = new Vector3(basis4dX.Length(), basis4dY.Length(), basis4dZ.Length() * determinant); - - // rotation - // this is using a z-up convention for basis vectors - var up = new Vector3(matrix.M13, matrix.M23, matrix.M33); - var forward = new Vector3(matrix.M12, matrix.M22, matrix.M32); - rotation = LookRotation(forward, up); - } - - private static Quaternion LookRotation(Vector3 forward, Vector3 up) - { - Vector3 vector = new(forward.X / forward.Length(), forward.Y / forward.Length(), forward.Z / forward.Length()); - Vector3 vector2 = Vector3.Cross(up, forward); - Vector3 vector3 = Vector3.Cross(vector, vector2); - var m00 = vector2.X; - var m01 = vector2.Y; - var m02 = vector2.Z; - var m10 = vector3.X; - var m11 = vector3.Y; - var m12 = vector3.Z; - var m20 = vector.X; - var m21 = vector.Y; - var m22 = vector.Z; - - var num8 = m00 + m11 + m22; - if (num8 > 0d) - { - var num = Math.Sqrt(num8 + 1d); - num = 0.5d / num; - return new Quaternion((m12 - m21) * num, (m20 - m02) * num, (m01 - m10) * num, num * 0.5d); - } - if (m00 >= m11 && m00 >= m22) - { - var num7 = Math.Sqrt(1d + m00 - m11 - m22); - var num4 = 0.5d / num7; - return new Quaternion(0.5d * num7, (m01 + m10) * num4, (m02 + m20) * num4, (m12 - m21) * num4); - } - if (m11 > m22) - { - var num6 = Math.Sqrt(1d + m11 - m00 - m22); - var num3 = 0.5d / num6; - return new Quaternion((m10 + m01) * num3, 0.5d * num6, (m21 + m12) * num3, (m20 - m02) * num3); - } - var num5 = Math.Sqrt(1d + m22 - m00 - m11); - var num2 = 0.5d / num5; - return new Quaternion((m20 + m02) * num2, (m21 + m12) * num2, 0.5d * num5, (m01 - m10) * num2); - } + public required string units { get; set; } /// /// Converts this transform to the input units @@ -216,56 +56,7 @@ public class Transform : Base matrix.M41, matrix.M42, matrix.M43, - matrix.M44 - }; - } - - public Transform Inverse() - { - if (Matrix4x4.Invert(matrix, out var transformed)) - { - return new Transform(transformed); - } - throw new SpeckleException("Could not create inverse transform"); - } - - /// - /// Returns the matrix that results from multiplying two matrices together. - /// - /// The first transform - /// The second transform - /// A transform matrix with the units of the first transform - public static Transform operator *(Transform t1, Transform t2) - { - var convertedTransform = CreateMatrix(t2.ConvertToUnits(t1.units)); - var newMatrix = t1.matrix * convertedTransform; - return new Transform(newMatrix, t1.units); - } - - /// - /// Returns the double array of the transform matrix - /// - /// - public double[] ToArray() - { - return new double[] - { - matrix.M11, - matrix.M12, - matrix.M13, - matrix.M14, - matrix.M21, - matrix.M22, - matrix.M23, - matrix.M24, - matrix.M31, - matrix.M32, - matrix.M33, - matrix.M34, - matrix.M41, - matrix.M42, - matrix.M43, - matrix.M44 + matrix.M44, }; } @@ -315,151 +106,30 @@ public class Transform : Base ); } - #region obsolete - - [JsonIgnore, Obsolete("Use the matrix property", true)] - [System.Diagnostics.CodeAnalysis.SuppressMessage( - "Performance", - "CA1819:Properties should not return arrays", - Justification = "Obsolete" - )] - public double[] value - { - get => ToArray(); - set => matrix = CreateMatrix(value); - } - - [JsonIgnore, Obsolete("Use Decompose method", true)] - public double rotationZ - { - get - { - Decompose(out _, out Quaternion rotation, out _); - return Math.Acos(rotation.W) * 2; - } - } - /// - /// Transform a flat list of doubles representing points + /// Returns the double array of the transform matrix /// - [Obsolete("Use transform method in Point class", true)] - public List ApplyToPoints(List points) + /// + public double[] ToArray() { - if (points.Count % 3 != 0) + return new double[] { - throw new SpeckleException( - "Cannot apply transform as the points list is malformed: expected length to be multiple of 3" - ); - } - - var transformed = new List(points.Count); - for (var i = 0; i < points.Count; i += 3) - { - var point = new Point(points[i], points[i + 1], points[i + 2]); - point.TransformTo(this, out Point transformedPoint); - transformed.AddRange(transformedPoint.ToList()); - } - return transformed; + matrix.M11, + matrix.M12, + matrix.M13, + matrix.M14, + matrix.M21, + matrix.M22, + matrix.M23, + matrix.M24, + matrix.M31, + matrix.M32, + matrix.M33, + matrix.M34, + matrix.M41, + matrix.M42, + matrix.M43, + matrix.M44, + }; } - - /// - /// Transform a flat list of speckle Points - /// - [Obsolete("Use transform method in Point class", true)] - public List ApplyToPoints(List points) - { - var transformedPoints = new List(); - foreach (var point in points) - { - point.TransformTo(this, out Point transformedPoint); - transformedPoints.Add(transformedPoint); - } - return transformedPoints; - } - - /// - /// Transform a single speckle Point - /// - [Obsolete("Use transform method in Point class", true)] - public Point? ApplyToPoint(Point point) - { - if (point == null) - { - return null; - } - - point.TransformTo(this, out Point transformedPoint); - return transformedPoint; - } - - /// - /// Transform a list of three doubles representing a point - /// - [Obsolete("Use transform method in Point class", true)] - public List ApplyToPoint(List point) - { - var newPoint = new Point(point[0], point[1], point[2]); - newPoint.TransformTo(this, out Point transformed); - return transformed.ToList(); - } - - /// - /// Transform a single speckle Vector - /// - [Obsolete("Use transform method in Vector class", true)] - public Vector ApplyToVector(Vector vector) - { - var newCoords = ApplyToVector(new List { vector.x, vector.y, vector.z }); - - return new Vector(newCoords[0], newCoords[1], newCoords[2], vector.units, vector.applicationId); - } - - /// - /// Transform a list of three doubles representing a vector - /// - [Obsolete("Use transform method in Vector class", true)] - public List ApplyToVector(List vector) - { - var newPoint = new List(); - - for (var i = 0; i < 12; i += 4) - { - newPoint.Add(vector[0] * value[i] + vector[1] * value[i + 1] + vector[2] * value[i + 2]); - } - - return newPoint; - } - - /// - /// Transform a flat list of ICurves. Note that if any of the ICurves does not implement `ITransformable`, - /// it will not be returned. - /// - [Obsolete("Use transform method in Curve class", true)] - public List ApplyToCurves(List curves, out bool success) - { - // TODO: move to curve class - success = true; - var transformed = new List(); - foreach (var curve in curves) - { - if (curve is ITransformable c) - { - c.TransformTo(this, out ITransformable tc); - transformed.Add((ICurve)tc); - } - else - { - success = false; - } - } - - return transformed; - } - - public static Transform Multiply(Transform left, Transform right) - { - throw new NotImplementedException(); - } - - #endregion } diff --git a/src/Speckle.Objects/Primitive/Interval2d.cs b/src/Speckle.Objects/Primitive/Interval2d.cs deleted file mode 100644 index 8254e08e..00000000 --- a/src/Speckle.Objects/Primitive/Interval2d.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Primitive; - -[SpeckleType("Objects.Primitive.Interval2d")] -public class Interval2d : Base -{ - public Interval2d() { } - - public Interval2d(Interval u, Interval v) - { - this.u = u; - this.v = v; - } - - public Interval2d(double start_u, double end_u, double start_v, double end_v) - { - u = new Interval { start = start_u, end = end_u }; - v = new Interval { start = start_v, end = end_v }; - } - - public Interval u { get; set; } - public Interval v { get; set; } -} diff --git a/src/Speckle.Objects/Speckle.Objects.csproj b/src/Speckle.Objects/Speckle.Objects.csproj index 69da17a1..aeddfb0e 100644 --- a/src/Speckle.Objects/Speckle.Objects.csproj +++ b/src/Speckle.Objects/Speckle.Objects.csproj @@ -1,23 +1,37 @@ - - netstandard2.0 - Speckle.Objects - Speckle.Objects - Speckle.Objects - Objects is the default object model for Speckle - $(PackageTags), objects + + + netstandard2.0;net8.0 System.Runtime.CompilerServices.RequiresLocationAttribute Debug;Release;Local + + + + Speckle.Objects + Objects is the default object model for Speckle + $(PackageTags) objects + + + + true true snupkg - + + + $(NoWarn); + CA1819;CA1008;CA2225; + + + + - + + diff --git a/src/Speckle.Objects/Structural/Analysis/Model.cs b/src/Speckle.Objects/Structural/Analysis/Model.cs deleted file mode 100644 index 89a4d80f..00000000 --- a/src/Speckle.Objects/Structural/Analysis/Model.cs +++ /dev/null @@ -1,63 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Analysis; - -[SpeckleType("Objects.Structural.Analysis.Model")] -public class Model : Base -{ - public Model() { } - - /// - /// SchemaBuilder constructor for a structural model object - /// - /// - /// - /// - /// - /// - /// - [SchemaInfo("Model", "Creates a Speckle structural model object", "Structural", "Analysis")] - public Model( - ModelInfo? specs = null, - List? nodes = null, - List? elements = null, - List? loads = null, - List? restraints = null, - List? properties = null, - List? materials = null - ) - { - this.specs = specs; - this.nodes = nodes; - this.elements = elements; - this.loads = loads; - this.restraints = restraints; - this.properties = properties; - this.materials = materials; - } - - public ModelInfo? specs { get; set; } //container for model and project specifications - - [DetachProperty, Chunkable(5000)] - public List? nodes { get; set; } //nodes list - - [DetachProperty, Chunkable(5000)] - public List? elements { get; set; } //element (or member) list - - [DetachProperty, Chunkable(5000)] - public List? loads { get; set; } //loads list - - [DetachProperty, Chunkable(5000)] - public List? restraints { get; set; } //supports list - - [DetachProperty, Chunkable(5000)] - public List? properties { get; set; } //properties list - - [DetachProperty, Chunkable(5000)] - public List? materials { get; set; } //materials list - - // add "other" - ex. assemblies, grid lines, grid planes, storeys etc? alignment/paths? - - public string? layerDescription { get; set; } //design layer, analysis layer -} diff --git a/src/Speckle.Objects/Structural/Analysis/ModelInfo.cs b/src/Speckle.Objects/Structural/Analysis/ModelInfo.cs deleted file mode 100644 index efe2990a..00000000 --- a/src/Speckle.Objects/Structural/Analysis/ModelInfo.cs +++ /dev/null @@ -1,53 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Analysis; - -[SpeckleType("Objects.Structural.Analysis.ModelInfo")] -public class ModelInfo : Base //titles -{ - public ModelInfo() { } - - /// - /// SchemaBuilder constructor for a model specifications (containing general model and project info) object - /// - /// - /// - /// - /// - /// - /// Initials that identify the creator of the model - /// - [SchemaInfo( - "ModelInfo", - "Creates a Speckle object which describes basic model and project information for a structural model", - "Structural", - "Analysis" - )] - public ModelInfo( - string? name = null, - string? description = null, - string? projectNumber = null, - string? projectName = null, - ModelSettings? settings = null, - string? engInitials = null, - string? application = null - ) - { - this.name = name; - this.description = description; - this.projectNumber = projectNumber; - this.projectName = projectName; - this.settings = settings; - initials = engInitials; - this.application = application; - } - - public string? name { get; set; } //title - public string? description { get; set; } //subtitle - public string? projectNumber { get; set; } //could a project info object be a potential upstream change, as addition to default Speckle Kit? - public string? projectName { get; set; } - public ModelSettings? settings { get; set; } - public string? initials { get; set; } //engineer initials - public string? application { get; set; } //ex. GSA, Tekla (reference Applications class?) -} diff --git a/src/Speckle.Objects/Structural/Analysis/ModelSettings.cs b/src/Speckle.Objects/Structural/Analysis/ModelSettings.cs deleted file mode 100644 index af18344a..00000000 --- a/src/Speckle.Objects/Structural/Analysis/ModelSettings.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Analysis; - -/// -/// Codes and standards references, model units, design settings, analysis settings, precision and tolerances -/// -[SpeckleType("Objects.Structural.Analysis.ModelSettings")] -public class ModelSettings : Base -{ - public ModelSettings() { } - - /// - /// SchemaBuilder constructor for a model settings object - /// - /// - /// - /// - /// - [SchemaInfo( - "ModelSettings", - "Creates a Speckle object which describes design and analysis settings for the structural model", - "Structural", - "Analysis" - )] - public ModelSettings( - ModelUnits? modelUnits = null, - string? steelCode = null, - string? concreteCode = null, - double coincidenceTolerance = 10 - ) - { - this.modelUnits = modelUnits ?? new ModelUnits(UnitsType.Metric); - this.steelCode = steelCode; - this.concreteCode = concreteCode; - this.coincidenceTolerance = coincidenceTolerance; - } - - /// - /// Units object containing units information for key structural model quantities - /// - [DetachProperty] - public ModelUnits modelUnits { get; set; } - - public string? steelCode { get; set; } //could be enum - public string? concreteCode { get; set; } //could be enum - public double coincidenceTolerance { get; set; } -} diff --git a/src/Speckle.Objects/Structural/Analysis/ModelUnits.cs b/src/Speckle.Objects/Structural/Analysis/ModelUnits.cs deleted file mode 100644 index 05c24b8d..00000000 --- a/src/Speckle.Objects/Structural/Analysis/ModelUnits.cs +++ /dev/null @@ -1,108 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Analysis; - -[SpeckleType("Objects.Structural.Analysis.ModelUnits")] -public class ModelUnits : Base -{ - public ModelUnits() { } - - [SchemaInfo( - "ModelUnits", - "Creates a Speckle object which specifies the units associated with the model", - "Structural", - "Analysis" - )] - public ModelUnits([SchemaParamInfo("Select a set of default units based on the unit system")] UnitsType unitsType) - { - if (unitsType == UnitsType.Metric) - { - length = "m"; - sections = "m"; - displacements = "m"; - stress = "Pa"; - force = "N"; - mass = "kg"; - time = "s"; - temperature = "K"; - velocity = "m/s"; - acceleration = "m/s2"; - energy = "J"; - angle = "deg"; - } - if (unitsType == UnitsType.Imperial) - { - length = "ft"; - sections = "in"; - displacements = "in"; - stress = "kip/in2"; - force = "kip"; - mass = "lb"; - time = "s"; - temperature = "F"; - velocity = "ft/s"; - acceleration = "ft/s2"; - energy = "ft.lbf"; - angle = "deg"; - } - } - - [SchemaInfo( - "ModelUnits (custom)", - "Creates a Speckle object which specifies the units associated with the model", - "Structural", - "Analysis" - )] - public ModelUnits( - [SchemaParamInfo("Used for length and length derived units such as area")] string length = "m", - [SchemaParamInfo("Used for cross-sectional properties")] string sections = "m", - [SchemaParamInfo("Used for displacements and cross-sectional dimensions")] string displacements = "m", - [SchemaParamInfo( - "Used for stress (distinct from force and length) and stress related quantities like the elastic modulus" - )] - string stress = "Pa", - [SchemaParamInfo("Used for force and force derived units such as moment, etc., but not for stress")] - string force = "N", - [SchemaParamInfo("Used for mass and mass derived units such as inertia")] string mass = "kg", - [SchemaParamInfo("Used for time and time derived units, such as frequency")] string time = "s", - [SchemaParamInfo("Used for temperature and temperature derived units such as coefficients of expansion")] - string temperature = "K", - [SchemaParamInfo("Used for velocity and velocity derived units")] string velocity = "m/s", - [SchemaParamInfo( - "Used for acceleration and acceleration derived units (considered as distinct from length and time units)" - )] - string acceleration = "m/s2", - [SchemaParamInfo("Used for energy and energy derived units (considered as distinct from force and length units)")] - string energy = "J", - [SchemaParamInfo("To allow selection between degrees and radians for angle measures")] string angle = "deg" - ) - { - this.length = length; - this.sections = sections; - this.displacements = displacements; - this.stress = stress; - this.force = force; - this.mass = mass; - this.time = time; - this.temperature = temperature; - this.velocity = velocity; - this.acceleration = acceleration; - this.energy = energy; - } - - // use enums instead of strings - public string length { get; set; } // m, cm, mm, ft, in - public string sections { get; set; } //m, cm, mm, ft, in - public string displacements { get; set; } // m, cm, mm, ft, in - public string stress { get; set; } //Pa, kPa, MPa, GPa, N/m², N/mm², kip/in², psi, psf, ksi - public string force { get; set; } //N, kN, MN, lbf, kip, tf - public string mass { get; set; } //kg, t, kt, g, lb, Ton, slug, kip.s²/in, kip.s²/ft, lbf.s²/in, lbf.s²/ft, kip - public string time { get; set; } // s, ms, min, h, d - public string temperature { get; set; } // °C, K, °F - public string velocity { get; set; } //m/s, cm/s, mm/s, ft/s, in/s, km/h, mph - public string acceleration { get; set; } //m/s², cm/s², mm/s², ft/s², in/s², g, %g, milli-g, Gal - public string energy { get; set; } //J, KJ, MJ, GJ, kWh, in.lbf, ft.lbf, cal, Btu - public string angle { get; set; } //deg, rad - public string strain { get; set; } //ε, %ε, mε, με -} diff --git a/src/Speckle.Objects/Structural/Analysis/UnitTypes.cs b/src/Speckle.Objects/Structural/Analysis/UnitTypes.cs deleted file mode 100644 index c7ad5596..00000000 --- a/src/Speckle.Objects/Structural/Analysis/UnitTypes.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Speckle.Objects.Structural.Analysis; - -public enum UnitsType -{ - Metric, - Imperial -} diff --git a/src/Speckle.Objects/Structural/Axis.cs b/src/Speckle.Objects/Structural/Axis.cs deleted file mode 100644 index 25d32c87..00000000 --- a/src/Speckle.Objects/Structural/Axis.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Speckle.Objects.Structural; - -public enum AxisType -{ - Cartesian, - Cylindrical, - Spherical -} - -public enum LoadAxisType -{ - Global, - Local, // local element axes - DeformedLocal // element local axis that is embedded in the element as it deforms -} diff --git a/src/Speckle.Objects/Structural/CSI/Analysis/CSIStories.cs b/src/Speckle.Objects/Structural/CSI/Analysis/CSIStories.cs deleted file mode 100644 index 6ef3a7d0..00000000 --- a/src/Speckle.Objects/Structural/CSI/Analysis/CSIStories.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Speckle.Objects.Structural.Geometry; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.CSI.Analysis; - -[SpeckleType("Objects.Structural.CSI.Analysis.CSIStories")] -public class CSIStories : Base -{ - public double BaseElevation { get; set; } - public int NumberStories { get; set; } - - [DetachProperty] - public List CSIStory { get; set; } -} - -[SpeckleType("Objects.Structural.CSI.Analysis.CSIStorey")] -public class CSIStorey : Storey -{ - public CSIStorey( - string name, - double elevation, - double storeyHeight, - bool isMasterStory, - string similarToStory, - bool spliceAbove, - double spliceHeight - ) - { - this.name = name; - this.elevation = elevation; - this.storeyHeight = storeyHeight; - IsMasterStory = isMasterStory; - SimilarToStory = similarToStory; - SpliceAbove = spliceAbove; - SpliceHeight = spliceHeight; - Color = 0; - } - - public CSIStorey() { } - - public double storeyHeight { get; set; } - public bool IsMasterStory { get; set; } - public string SimilarToStory { get; set; } - public bool SpliceAbove { get; set; } - - public double SpliceHeight { get; set; } - public int Color { get; set; } -} diff --git a/src/Speckle.Objects/Structural/CSI/Analysis/ETABSAnalysis.cs b/src/Speckle.Objects/Structural/CSI/Analysis/ETABSAnalysis.cs deleted file mode 100644 index 0d54d69e..00000000 --- a/src/Speckle.Objects/Structural/CSI/Analysis/ETABSAnalysis.cs +++ /dev/null @@ -1,99 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.CSI.Analysis; - -[SpeckleType("Objects.Structural.CSI.Analysis.CSIAnalysis")] -public class CSIAnalysis : Base -{ - public ActiveDOFs activeDOFs { get; set; } - public FloorMeshSettings floorMeshSettings { get; set; } -} - -[SpeckleType("Objects.Structural.CSI.Analysis.ActiveDOFs")] -public class ActiveDOFs : Base -{ - public ActiveDOFs() { } - - public ActiveDOFs(bool UX, bool UY, bool UZ, bool RX, bool RY, bool RZ) - { - this.UX = UX; - this.UY = UY; - this.UZ = UZ; - this.RX = RX; - this.RY = RY; - this.RZ = RZ; - } - - public bool UX { get; set; } - public bool UY { get; set; } - public bool UZ { get; set; } - public bool RX { get; set; } - public bool RY { get; set; } - public bool RZ { get; set; } -} - -[SpeckleType("Objects.Structural.CSI.Analysis.FloorMeshSettings")] -public class FloorMeshSettings : Base -{ - public FloorMeshSettings() { } - - public FloorMeshSettings(MeshOption meshOption, double maximumMeshSize) - { - this.meshOption = meshOption; - this.maximumMeshSize = maximumMeshSize; - } - - public MeshOption meshOption { get; set; } - public double maximumMeshSize { get; set; } -} - -[SpeckleType("Objects.Structural.CSI.Analysis.WallMeshSettings")] -public class WallMeshSettings : Base -{ - public WallMeshSettings() { } - - public WallMeshSettings(double maximumMeshSize) - { - this.maximumMeshSize = maximumMeshSize; - } - - public double maximumMeshSize { get; set; } -} - -[SpeckleType("Objects.Structural.CSI.Analysis.CrackingAnalysisOptions")] -public class CrackingAnalysisOptions : Base -{ - public CrackingAnalysisOptions() { } - - public CrackingAnalysisOptions(string reinforcementSource, double minTensionRatio, double minCompressionRatio) - { - this.reinforcementSource = reinforcementSource; - this.minTensionRatio = minTensionRatio; - this.minCompressionRatio = minCompressionRatio; - } - - public string reinforcementSource { get; set; } - public double minTensionRatio { get; set; } - public double minCompressionRatio { get; set; } -} - -[SpeckleType("Objects.Structural.CSI.Analysis.SAPFireOptions")] -public class SAPFireOptions : Base -{ - public SAPFireOptions() { } - - public SAPFireOptions(SolverOption solverOption, AnalysisProcess analysisProcess) - { - this.solverOption = solverOption; - this.analysisProcess = analysisProcess; - } - - public SolverOption solverOption { get; set; } - public AnalysisProcess analysisProcess { get; set; } -} - -public enum MeshOption { } - -public enum SolverOption { } - -public enum AnalysisProcess { } diff --git a/src/Speckle.Objects/Structural/CSI/Analysis/ETABSAreaType.cs b/src/Speckle.Objects/Structural/CSI/Analysis/ETABSAreaType.cs deleted file mode 100644 index 4baed6f3..00000000 --- a/src/Speckle.Objects/Structural/CSI/Analysis/ETABSAreaType.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace Speckle.Objects.Structural.CSI.Analysis; - -public enum CSIPropertyType2D -{ - Deck, - Slab, - Shell, - Wall -} - -public enum SlabType -{ - Slab, - Drop, - Ribbed, - Waffle, - Mat, - Footing, - Null -} - -public enum ShellType -{ - ShellThin, - ShellThick, - Membrane, - Layered, - Null -} - -public enum DeckType -{ - Filled, - Unfilled, - SolidSlab, - Null -} diff --git a/src/Speckle.Objects/Structural/CSI/Analysis/ETABSLoadingType.cs b/src/Speckle.Objects/Structural/CSI/Analysis/ETABSLoadingType.cs deleted file mode 100644 index 7da15361..00000000 --- a/src/Speckle.Objects/Structural/CSI/Analysis/ETABSLoadingType.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Speckle.Objects.Structural.CSI.Analysis; - -public enum WindPressureType -{ - Windward, - other -} diff --git a/src/Speckle.Objects/Structural/CSI/Geometry/CSIElement1D.cs b/src/Speckle.Objects/Structural/CSI/Geometry/CSIElement1D.cs deleted file mode 100644 index fad1cb9a..00000000 --- a/src/Speckle.Objects/Structural/CSI/Geometry/CSIElement1D.cs +++ /dev/null @@ -1,124 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Structural.CSI.Properties; -using Speckle.Objects.Structural.Geometry; -using Speckle.Objects.Structural.Properties; -using Speckle.Objects.Structural.Results; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.CSI.Geometry; - -[SpeckleType("Objects.Structural.CSI.Geometry.CSIElement1D")] -public class CSIElement1D : Element1D -{ - /// - /// SchemaBuilder constructor for structural 1D element (based on local axis) - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - [SchemaInfo("Element1D (from local axis)", "Creates a Speckle CSI 1D element (from local axis)", "CSI", "Geometry")] - public CSIElement1D( - Line baseLine, - Property1D property, - ElementType1D type, - string? name = null, - [SchemaParamInfo("If null, restraint condition defaults to unreleased (fully fixed translations and rotations)")] - Restraint? end1Releases = null, - [SchemaParamInfo("If null, restraint condition defaults to unreleased (fully fixed translations and rotations)")] - Restraint? end2Releases = null, - [SchemaParamInfo("If null, defaults to no offsets")] Vector? end1Offset = null, - [SchemaParamInfo("If null, defaults to no offsets")] Vector? end2Offset = null, - Plane? localAxis = null, - CSILinearSpring? CSILinearSpring = null, - [SchemaParamInfo("an Array of 8 values referring to the modifiers as seen in CSI in order")] - double[]? Modifier = null, - DesignProcedure DesignProcedure = DesignProcedure.NoDesign - ) - { - 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.CSILinearSpring = CSILinearSpring; - this.DesignProcedure = DesignProcedure; - Modifiers = Modifier; - } - - /// - /// SchemaBuilder constructor for structural 1D element (based on orientation node and angle) - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - [SchemaInfo( - "Element1D (from orientation node and angle)", - "Creates a Speckle CSI 1D element (from orientation node and angle)", - "CSI", - "Geometry" - )] - public CSIElement1D( - Line baseLine, - Property1D property, - ElementType1D type, - string? name = null, - [SchemaParamInfo("If null, restraint condition defaults to unreleased (fully fixed translations and rotations)")] - Restraint? end1Releases = null, - [SchemaParamInfo("If null, restraint condition defaults to unreleased (fully fixed translations and rotations)")] - Restraint? end2Releases = null, - [SchemaParamInfo("If null, defaults to no offsets")] Vector? end1Offset = null, - [SchemaParamInfo("If null, defaults to no offsets")] Vector? end2Offset = null, - Node? orientationNode = null, - double orientationAngle = 0, - CSILinearSpring? CSILinearSpring = null, - [SchemaParamInfo("an Array of 8 values referring to the modifiers as seen in CSI in order")] - double[]? Modifier = null, - DesignProcedure DesignProcedure = DesignProcedure.NoDesign - ) - { - 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; - this.CSILinearSpring = CSILinearSpring; - this.DesignProcedure = DesignProcedure; - Modifiers = Modifier; - } - - public CSIElement1D() { } - - [DetachProperty] - public CSILinearSpring? CSILinearSpring { get; set; } - - public string PierAssignment { get; set; } - public string SpandrelAssignment { get; set; } - public double[]? Modifiers { get; set; } - public DesignProcedure DesignProcedure { get; set; } - - [DetachProperty] - public AnalyticalResults? AnalysisResults { get; set; } -} diff --git a/src/Speckle.Objects/Structural/CSI/Geometry/CSIElement2D.cs b/src/Speckle.Objects/Structural/CSI/Geometry/CSIElement2D.cs deleted file mode 100644 index 8cf5dcc0..00000000 --- a/src/Speckle.Objects/Structural/CSI/Geometry/CSIElement2D.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Speckle.Objects.Structural.CSI.Properties; -using Speckle.Objects.Structural.Geometry; -using Speckle.Objects.Structural.Properties; -using Speckle.Objects.Structural.Results; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.CSI.Geometry; - -[SpeckleType("Objects.Structural.CSI.Geometry.CSIElement2D")] -public class CSIElement2D : Element2D -{ - [SchemaInfo( - "Element2D", - "Creates a Speckle CSI 2D element (based on a list of edge ie. external, geometry defining nodes)", - "CSI", - "Geometry" - )] - public CSIElement2D( - List nodes, - Property2D property, - double offset = 0, - double orientationAngle = 0, - double[]? modifiers = null, - CSIAreaSpring? CSIAreaSpring = null, - CSIDiaphragm? CSIDiaphragm = null - ) - { - topology = nodes; - this.property = property; - this.offset = offset; - this.orientationAngle = orientationAngle; - DiaphragmAssignment = CSIDiaphragm?.name; - this.CSIAreaSpring = CSIAreaSpring; - this.modifiers = modifiers; - } - - public CSIElement2D() { } - - [DetachProperty] - public CSIAreaSpring? CSIAreaSpring { get; set; } - - public string? DiaphragmAssignment { get; set; } - public string? PierAssignment { get; set; } - public string? SpandrelAssignment { get; set; } - public double[]? modifiers { get; set; } - public bool Opening { get; set; } - - [DetachProperty] - public AnalyticalResults? AnalysisResults { get; set; } -} diff --git a/src/Speckle.Objects/Structural/CSI/Geometry/CSIGridLines.cs b/src/Speckle.Objects/Structural/CSI/Geometry/CSIGridLines.cs deleted file mode 100644 index fb480d44..00000000 --- a/src/Speckle.Objects/Structural/CSI/Geometry/CSIGridLines.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Speckle.Objects.BuiltElements; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.CSI.Geometry; - -[SpeckleType("Objects.Structural.CSI.Geometry.CSIGridLines")] -public class CSIGridLines : Base -{ - public double Xo { get; set; } - public double Yo { get; set; } - public double Rz { get; set; } - public string GridSystemType { get; set; } - - [DetachProperty] - public List gridLines { get; set; } -} diff --git a/src/Speckle.Objects/Structural/CSI/Geometry/CSINode.cs b/src/Speckle.Objects/Structural/CSI/Geometry/CSINode.cs deleted file mode 100644 index 0476938f..00000000 --- a/src/Speckle.Objects/Structural/CSI/Geometry/CSINode.cs +++ /dev/null @@ -1,71 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Structural.CSI.Properties; -using Speckle.Objects.Structural.Geometry; -using Speckle.Objects.Structural.Properties; -using Speckle.Objects.Structural.Results; -using Speckle.Sdk.Common; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.CSI.Geometry; - -[SpeckleType("Objects.Structural.CSI.Geometry.CSINode")] -public class CSINode : Node -{ - [SchemaInfo( - "Node with properties", - "Creates a Speckle CSI node with spring, mass and/or damper properties", - "CSI", - "Geometry" - )] - public CSINode( - Point basePoint, - string? name = null, - [SchemaParamInfo("If null, restraint condition defaults to free/fully released")] Restraint? restraint = null, - [SchemaParamInfo( - "If null, axis defaults to world xy (z axis defines the vertical direction, positive direction is up)" - )] - Axis? constraintAxis = null, - CSISpringProperty? springProperty = null, - PropertyMass? massProperty = null, - PropertyDamper? damperProperty = null, - CSIDiaphragm? CSIDiaphragm = null, - DiaphragmOption DiaphragmOption = DiaphragmOption.FromShellObject - ) - { - this.basePoint = basePoint; - this.name = name; - this.restraint = restraint ?? new Restraint("RRRRRR"); - this.constraintAxis = - constraintAxis - ?? new Axis( - "Global", - AxisType.Cartesian, - new Plane - { - origin = new Point(0, 0), - normal = new Vector(0, 0, 1), - xdir = new Vector(1, 0, 0), - ydir = new Vector(0, 1, 0), - units = Units.Meters, //Not sure if defaulting to meters is correct, but it was what we were doing previously inside Plane's ctor - } - ); - CSISpringProperty = springProperty; - this.massProperty = massProperty; - this.damperProperty = damperProperty; - DiaphragmAssignment = CSIDiaphragm?.name; - this.DiaphragmOption = DiaphragmOption; - } - - public CSINode() { } - - [DetachProperty] - public CSISpringProperty? CSISpringProperty { get; set; } - - public string? DiaphragmAssignment { get; set; } - - public DiaphragmOption DiaphragmOption { get; set; } - - [DetachProperty] - public AnalyticalResults? AnalysisResults { get; set; } -} diff --git a/src/Speckle.Objects/Structural/CSI/Geometry/CSIPier.cs b/src/Speckle.Objects/Structural/CSI/Geometry/CSIPier.cs deleted file mode 100644 index 4a5f9def..00000000 --- a/src/Speckle.Objects/Structural/CSI/Geometry/CSIPier.cs +++ /dev/null @@ -1,66 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.CSI.Geometry; - -[SpeckleType("Objects.Structural.CSI.Geometry.CSIPier")] -public class CSIPier : Base -{ - public CSIPier( - string name, - int numberStories, - string[] storyName, - double[] axisAngle, - int[] numAreaObjs, - int[] numLineObjs, - double[] widthBot, - double[] thicknessBot, - double[] widthTop, - double[] thicknessTop, - string[] matProp, - double[] centerofGravityBotX, - double[] centerofGravityBotY, - double[] centerofGravityBotZ, - double[] centerofGravityTopX, - double[] centerofGravityTopY, - double[] centerofGravityTopZ - ) - { - this.name = name; - this.numberStories = numberStories; - this.storyName = storyName; - this.axisAngle = axisAngle; - this.numAreaObjs = numAreaObjs; - this.numLineObjs = numLineObjs; - this.widthBot = widthBot; - this.thicknessBot = thicknessBot; - this.widthTop = widthTop; - this.thicknessTop = thicknessTop; - this.matProp = matProp; - this.centerofGravityBotX = centerofGravityBotX; - this.centerofGravityBotY = centerofGravityBotY; - this.centerofGravityBotZ = centerofGravityBotZ; - this.centerofGravityTopX = centerofGravityTopX; - this.centerofGravityTopY = centerofGravityTopY; - this.centerofGravityTopZ = centerofGravityTopZ; - } - - public CSIPier() { } - - public string name { get; set; } - public int numberStories { get; set; } - public string[] storyName { get; set; } - public double[] axisAngle { get; set; } - public int[] numAreaObjs { get; set; } - public int[] numLineObjs { get; set; } - public double[] widthBot { get; set; } - public double[] thicknessBot { get; set; } - public double[] widthTop { get; set; } - public double[] thicknessTop { get; set; } - public string[] matProp { get; set; } - public double[] centerofGravityBotX { get; set; } - public double[] centerofGravityBotY { get; set; } - public double[] centerofGravityBotZ { get; set; } - public double[] centerofGravityTopX { get; set; } - public double[] centerofGravityTopY { get; set; } - public double[] centerofGravityTopZ { get; set; } -} diff --git a/src/Speckle.Objects/Structural/CSI/Geometry/CSISpandrel.cs b/src/Speckle.Objects/Structural/CSI/Geometry/CSISpandrel.cs deleted file mode 100644 index 55b00b50..00000000 --- a/src/Speckle.Objects/Structural/CSI/Geometry/CSISpandrel.cs +++ /dev/null @@ -1,69 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.CSI.Geometry; - -[SpeckleType("Objects.Structural.CSI.Geometry.CSISpandrel")] -public class CSISpandrel : Base -{ - public CSISpandrel( - string name, - bool multistory, - int numberStories, - string[] storyName, - int[] numAreaObjs, - int[] numLineObjs, - double[] length, - double[] depthLeft, - double[] thickLeft, - double[] depthRight, - double[] thickRight, - string[] matProp, - double[] centerofGravityLeftX, - double[] centerofGravityLeftY, - double[] centerofGravityLeftZ, - double[] centerofGravityRightX, - double[] centerofGravityRightY, - double[] centerofGravityRightZ - ) - { - this.name = name; - this.multistory = multistory; - this.numberStories = numberStories; - this.storyName = storyName; - this.numAreaObjs = numAreaObjs; - this.numLineObjs = numLineObjs; - this.length = length; - this.depthLeft = depthLeft; - this.thickLeft = thickLeft; - this.depthRight = depthRight; - this.thickRight = thickRight; - this.matProp = matProp; - this.centerofGravityLeftX = centerofGravityLeftX; - this.centerofGravityLeftY = centerofGravityLeftY; - this.centerofGravityLeftZ = centerofGravityLeftZ; - this.centerofGravityRightX = centerofGravityRightX; - this.centerofGravityRightY = centerofGravityRightY; - this.centerofGravityRightZ = centerofGravityRightZ; - } - - public CSISpandrel() { } - - public string name { get; set; } - public bool multistory { get; set; } - public int numberStories { get; set; } - public string[] storyName { get; set; } - public int[] numAreaObjs { get; set; } - public int[] numLineObjs { get; set; } - public double[] length { get; set; } - public double[] depthLeft { get; set; } - public double[] thickLeft { get; set; } - public double[] depthRight { get; set; } - public double[] thickRight { get; set; } - public string[] matProp { get; set; } - public double[] centerofGravityLeftX { get; set; } - public double[] centerofGravityLeftY { get; set; } - public double[] centerofGravityLeftZ { get; set; } - public double[] centerofGravityRightX { get; set; } - public double[] centerofGravityRightY { get; set; } - public double[] centerofGravityRightZ { get; set; } -} diff --git a/src/Speckle.Objects/Structural/CSI/Geometry/CSITendon.cs b/src/Speckle.Objects/Structural/CSI/Geometry/CSITendon.cs deleted file mode 100644 index 82eb3a04..00000000 --- a/src/Speckle.Objects/Structural/CSI/Geometry/CSITendon.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Structural.CSI.Properties; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.CSI.Geometry; - -[SpeckleType("Objects.Structural.CSI.Geometry.CSITendon")] -public class CSITendon : CSIElement1D -{ - public CSITendon(string name, Polycurve polycurve, CSITendonProperty CSITendonProperty) - { - this.name = name; - this.polycurve = polycurve; - this.CSITendonProperty = CSITendonProperty; - } - - public CSITendon() { } - - public Polycurve polycurve { get; set; } - - [DetachProperty] - public CSITendonProperty CSITendonProperty { get; set; } -} diff --git a/src/Speckle.Objects/Structural/CSI/Loading/CSIWindLoading.cs b/src/Speckle.Objects/Structural/CSI/Loading/CSIWindLoading.cs deleted file mode 100644 index 4091ea0e..00000000 --- a/src/Speckle.Objects/Structural/CSI/Loading/CSIWindLoading.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Speckle.Objects.Structural.CSI.Analysis; -using Speckle.Objects.Structural.Loading; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.CSI.Loading; - -[SpeckleType("Objects.Structural.CSI.Loading.CSIWindLoadingFace")] -public class CSIWindLoadingFace : LoadFace -{ - public double Cp { get; set; } - - public WindPressureType WindPressureType { get; set; } -} diff --git a/src/Speckle.Objects/Structural/CSI/Materials/CSIConcrete.cs b/src/Speckle.Objects/Structural/CSI/Materials/CSIConcrete.cs deleted file mode 100644 index 3501e9d1..00000000 --- a/src/Speckle.Objects/Structural/CSI/Materials/CSIConcrete.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Speckle.Objects.Structural.Materials; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.CSI.Materials; - -[SpeckleType("Objects.Structural.CSI.Materials.CSIConcrete")] -public class CSIConcrete : Concrete -{ - public int SSHysType { get; set; } - public int SSType { get; set; } - - public double finalSlope { get; set; } - - public double frictionAngle { get; set; } - public double dialationalAngle { get; set; } -} diff --git a/src/Speckle.Objects/Structural/CSI/Materials/CSIRebar.cs b/src/Speckle.Objects/Structural/CSI/Materials/CSIRebar.cs deleted file mode 100644 index 622b53bd..00000000 --- a/src/Speckle.Objects/Structural/CSI/Materials/CSIRebar.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Speckle.Objects.Structural.Materials; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.CSI.Materials; - -[SpeckleType("Objects.Structural.CSI.Materials.CSIRebar")] -public class CSIRebar : StructuralMaterial { } diff --git a/src/Speckle.Objects/Structural/CSI/Materials/CSISteel.cs b/src/Speckle.Objects/Structural/CSI/Materials/CSISteel.cs deleted file mode 100644 index a915c36b..00000000 --- a/src/Speckle.Objects/Structural/CSI/Materials/CSISteel.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Speckle.Objects.Structural.Materials; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.CSI.Materials; - -[SpeckleType("Objects.Structural.CSI.Materials.CSISteel")] -public class CSISteel : Steel -{ - public int SSHysType { get; set; } - public int SSType { get; set; } - public double EFy { get; set; } - public double EFu { get; set; } - public double strainAtMaxStress { get; set; } - public double strainAtHardening { get; set; } -} diff --git a/src/Speckle.Objects/Structural/CSI/Properties/CSIDiaphragm.cs b/src/Speckle.Objects/Structural/CSI/Properties/CSIDiaphragm.cs deleted file mode 100644 index 13abbff5..00000000 --- a/src/Speckle.Objects/Structural/CSI/Properties/CSIDiaphragm.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.CSI.Properties; - -[SpeckleType("Objects.Structural.CSI.Properties.CSIDiaphragm")] -public class CSIDiaphragm : Base -{ - [SchemaInfo("CSI Diaphragm", "Create an CSI Diaphragm", "CSI", "Properties")] - public CSIDiaphragm(string name, bool semiRigid) - { - this.name = name; - SemiRigid = semiRigid; - } - - public CSIDiaphragm() { } - - public string name { get; set; } - public bool SemiRigid { get; set; } -} diff --git a/src/Speckle.Objects/Structural/CSI/Properties/CSILinkProperty.cs b/src/Speckle.Objects/Structural/CSI/Properties/CSILinkProperty.cs deleted file mode 100644 index e53848ee..00000000 --- a/src/Speckle.Objects/Structural/CSI/Properties/CSILinkProperty.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Speckle.Objects.Structural.Properties; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.CSI.Properties; - -[SpeckleType("Objects.Structural.CSI.Properties.CSILinkProperty")] -public class CSILinkProperty : Property1D -{ - [SchemaInfo("CSILink", "Create an CSI Link Property", "CSI", "Properties")] - public CSILinkProperty( - string name, - double mass, - double weight, - double rotationalInertia1, - double rotationalInertia2, - double rotationalInertia3, - double m2PdeltaEnd1, - double mP2deltaEnd2, - double mP3deltaEnd1, - double mP3deltaEnd2 - ) - { - this.name = name; - this.mass = mass; - this.weight = weight; - this.rotationalInertia1 = rotationalInertia1; - this.rotationalInertia2 = rotationalInertia2; - this.rotationalInertia3 = rotationalInertia3; - M2PdeltaEnd1 = m2PdeltaEnd1; - MP2deltaEnd2 = mP2deltaEnd2; - MP3deltaEnd1 = mP3deltaEnd1; - MP3deltaEnd2 = mP3deltaEnd2; - } - - public CSILinkProperty() { } - - public double mass { get; set; } - public double weight { get; set; } - public double rotationalInertia1 { get; set; } - public double rotationalInertia2 { get; set; } - public double rotationalInertia3 { get; set; } - public double M2PdeltaEnd1 { get; set; } - public double MP2deltaEnd2 { get; set; } - public double MP3deltaEnd1 { get; set; } - public double MP3deltaEnd2 { get; set; } -} diff --git a/src/Speckle.Objects/Structural/CSI/Properties/CSIProperty2D.cs b/src/Speckle.Objects/Structural/CSI/Properties/CSIProperty2D.cs deleted file mode 100644 index 4f2dbfce..00000000 --- a/src/Speckle.Objects/Structural/CSI/Properties/CSIProperty2D.cs +++ /dev/null @@ -1,274 +0,0 @@ -using Speckle.Objects.Structural.CSI.Analysis; -using Speckle.Objects.Structural.Materials; -using Speckle.Objects.Structural.Properties; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.CSI.Properties; - -[SpeckleType("Objects.Structural.CSI.Properties.CSIOpening")] -public class CSIOpening : Property2D -{ - [SchemaInfo("Opening", "Create an CSI Opening", "CSI", "Properties")] - public CSIOpening(bool isOpening) - { - this.isOpening = isOpening; - } - - public CSIOpening() { } - - public bool isOpening { get; set; } -} - -[SpeckleType("Objects.Structural.CSI.Properties.CSIProperty2D")] -public class CSIProperty2D : Property2D -{ - public CSIPropertyType2D type2D { get; set; } - public SlabType slabType { get; set; } - public DeckType deckType { get; set; } - public ShellType shellType { get; set; } - - [SpeckleType("Objects.Structural.CSI.Properties.CSIProperty2D+WaffleSlab")] - public class WaffleSlab : CSIProperty2D - { - public WaffleSlab() { } - - [SchemaInfo("WaffleSlab", "Create an CSI Waffle Slab", "CSI", "Properties")] - public WaffleSlab( - string PropertyName, - ShellType shell, - StructuralMaterial ConcreteMaterial, - double Thickness, - double overAllDepth, - double stemWidthBot, - double stemWidthTop, - double ribSpacingDir1, - double ribSpacingDir2 - ) - { - type2D = CSIPropertyType2D.Slab; - slabType = SlabType.Waffle; - deckType = DeckType.Null; - - name = PropertyName; - shellType = shell; - material = ConcreteMaterial; - thickness = Thickness; - - OverAllDepth = overAllDepth; - StemWidthBot = stemWidthBot; - StemWidthTop = stemWidthTop; - RibSpacingDir1 = ribSpacingDir1; - RibSpacingDir2 = ribSpacingDir2; - } - - public double OverAllDepth { get; set; } - public double StemWidthBot { get; set; } - public double StemWidthTop { get; set; } - public double RibSpacingDir1 { get; set; } - public double RibSpacingDir2 { get; set; } - - //[SchemaInfo("WaffleSlab","Define a WaffleSlab Area Property")] - } - - [SpeckleType("Objects.Structural.CSI.Properties.CSIProperty2D+RibbedSlab")] - public class RibbedSlab : CSIProperty2D - { - public RibbedSlab() { } - - [SchemaInfo("RibbedSlab", "Create an CSI Ribbed Slab", "CSI", "Properties")] - public RibbedSlab( - string PropertyName, - ShellType shell, - StructuralMaterial ConcreteMaterial, - double Thickness, - double overAllDepth, - double stemWidthBot, - double stemWidthTop, - double ribSpacing, - int ribsParallelTo - ) - { - type2D = CSIPropertyType2D.Slab; - slabType = SlabType.Ribbed; - deckType = DeckType.Null; - - name = PropertyName; - shellType = shell; - material = ConcreteMaterial; - thickness = Thickness; - - OverAllDepth = overAllDepth; - StemWidthBot = stemWidthBot; - StemWidthTop = stemWidthTop; - RibSpacing = ribSpacing; - RibsParallelTo = ribsParallelTo; - } - - public double OverAllDepth { get; set; } - public double StemWidthBot { get; set; } - public double StemWidthTop { get; set; } - public double RibSpacing { get; set; } - public int RibsParallelTo { get; set; } - } - - [SpeckleType("Objects.Structural.CSI.Properties.CSIProperty2D+Slab")] - public class Slab : CSIProperty2D - { - public Slab() { } - - [SchemaInfo("Slab", "Create an CSI Slab", "CSI", "Properties")] - public Slab(string PropertyName, ShellType shell, StructuralMaterial ConcreteMaterial, double Thickness) - { - type2D = CSIPropertyType2D.Slab; - slabType = SlabType.Slab; - deckType = DeckType.Null; - - name = PropertyName; - shellType = shell; - material = ConcreteMaterial; - thickness = Thickness; - } - } - - [SpeckleType("Objects.Structural.CSI.Properties.CSIProperty2D+DeckFilled")] - public class DeckFilled : CSIProperty2D - { - public DeckFilled() { } - - [SchemaInfo("DeckFilled", "Create an CSI Filled Deck", "CSI", "Properties")] - public DeckFilled( - string PropertyName, - ShellType shell, - StructuralMaterial ConcreteMaterial, - double DeckThickness, - double slabDepth, - double shearStudDia, - double shearStudFu, - double shearStudHt, - double ribDepth, - double ribWidthTop, - double ribWidthBot, - double ribSpacing, - double shearThickness, - double unitWeight - ) - { - type2D = CSIPropertyType2D.Deck; - slabType = SlabType.Null; - deckType = DeckType.Filled; - - name = PropertyName; - shellType = shell; - material = ConcreteMaterial; - thickness = DeckThickness; - - SlabDepth = slabDepth; - ShearStudDia = shearStudDia; - ShearStudFu = shearStudFu; - ShearStudHt = shearStudHt; - RibDepth = ribDepth; - RibWidthTop = ribWidthTop; - RibWidthBot = ribWidthBot; - RibSpacing = ribSpacing; - ShearThickness = shearThickness; - UnitWeight = unitWeight; - } - - public double SlabDepth { get; set; } - public double ShearStudDia { get; set; } - public double ShearStudFu { get; set; } - public double ShearStudHt { get; set; } - public double RibDepth { get; set; } - public double RibWidthTop { get; set; } - public double RibWidthBot { get; set; } - public double RibSpacing { get; set; } - public double ShearThickness { get; set; } - public double UnitWeight { get; set; } - } - - [SpeckleType("Objects.Structural.CSI.Properties.CSIProperty2D+DeckUnFilled")] - public class DeckUnFilled : CSIProperty2D - { - [SchemaInfo("DeckUnFilled", "Create an CSI UnFilled Deck", "CSI", "Properties")] - public DeckUnFilled( - string PropertyName, - ShellType shell, - StructuralMaterial Material, - double DeckThickness, - double slabDepth, - double ribDepth, - double ribWidthTop, - double ribWidthBot, - double ribSpacing, - double shearThickness, - double unitWeight - ) - { - type2D = CSIPropertyType2D.Deck; - slabType = SlabType.Null; - deckType = DeckType.Unfilled; - - name = PropertyName; - shellType = shell; - material = Material; - thickness = DeckThickness; - - SlabDepth = slabDepth; - RibDepth = ribDepth; - RibWidthTop = ribWidthTop; - RibWidthBot = ribWidthBot; - RibSpacing = ribSpacing; - ShearThickness = shearThickness; - UnitWeight = unitWeight; - } - - public DeckUnFilled() { } - - public double SlabDepth { get; set; } - public double RibDepth { get; set; } - public double RibWidthTop { get; set; } - public double RibWidthBot { get; set; } - public double RibSpacing { get; set; } - public double ShearThickness { get; set; } - public double UnitWeight { get; set; } - } - - [SpeckleType("Objects.Structural.CSI.Properties.CSIProperty2D+DeckSlab")] - public class DeckSlab : CSIProperty2D - { - [SchemaInfo("DeckSlab", "Create an CSI Slab Deck", "CSI", "Properties")] - public DeckSlab( - string PropertyName, - ShellType shell, - StructuralMaterial ConcreteMaterial, - double DeckThickness, - double slabDepth, - double shearStudDia, - double shearStudFu, - double shearStudHt - ) - { - type2D = CSIPropertyType2D.Deck; - slabType = SlabType.Null; - deckType = DeckType.SolidSlab; - - name = PropertyName; - shellType = shell; - material = ConcreteMaterial; - thickness = DeckThickness; - - SlabDepth = slabDepth; - ShearStudDia = shearStudDia; - ShearStudFu = shearStudFu; - ShearStudHt = shearStudHt; - } - - public DeckSlab() { } - - public double SlabDepth { get; set; } - public double ShearStudDia { get; set; } - public double ShearStudFu { get; set; } - public double ShearStudHt { get; set; } - } -} diff --git a/src/Speckle.Objects/Structural/CSI/Properties/CSISpringProperty.cs b/src/Speckle.Objects/Structural/CSI/Properties/CSISpringProperty.cs deleted file mode 100644 index 184896c1..00000000 --- a/src/Speckle.Objects/Structural/CSI/Properties/CSISpringProperty.cs +++ /dev/null @@ -1,107 +0,0 @@ -using Speckle.Objects.Structural.Properties; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.CSI.Properties; - -[SpeckleType("Objects.Structural.CSI.Properties.CSISpringProperty")] -public class CSISpringProperty : PropertySpring -{ - public CSISpringProperty() { } - - [SchemaInfo("PointSpring from Link", "Create an CSI PointSpring from Link", "CSI", "Properties")] - public CSISpringProperty( - string name, - string cYs, - double StiffnessX, - double StiffnessY, - double StiffnessZ, - double StiffnessXX, - double StiffnezzYY, - double StiffnessZZ - ) - { - this.name = name; - springOption = SpringOption.Link; - stiffnessX = StiffnessX; - stiffnessY = StiffnessY; - stiffnessZ = StiffnessZ; - stiffnessXX = StiffnessXX; - stiffnessYY = StiffnezzYY; - stiffnessZZ = StiffnessZZ; - CYs = cYs; - } - - [SchemaInfo("PointSpring from Soil Profile", "Create an CSI PointSpring from Soil Profile", "CSI", "Properties")] - public CSISpringProperty(string name, string soilProfile, string footing, double period) - { - this.name = name; - springOption = SpringOption.SoilProfileFooting; - SoilProfile = soilProfile; - this.footing = footing; - this.period = period; - } - - public SpringOption springOption { get; set; } - public string CYs { get; set; } - public string SoilProfile { get; set; } - public string footing { get; set; } - public double period { get; set; } -} - -[SpeckleType("Objects.Structural.CSI.Properties.CSILinearSpring")] -public class CSILinearSpring : PropertySpring -{ - public CSILinearSpring() { } - - [SchemaInfo("LinearSpring", "Create an CSI LinearSpring", "CSI", "Properties")] - public CSILinearSpring( - string name, - double StiffnessX, - double StiffnessY, - double StiffnessZ, - double StiffnessXX, - NonLinearOptions linearOption1, - NonLinearOptions linearOption2, - string? applicationID = null - ) - { - this.name = name; - stiffnessX = StiffnessX; - stiffnessY = StiffnessY; - stiffnessZ = StiffnessZ; - stiffnessXX = StiffnessXX; - LinearOption1 = linearOption1; - LinearOption2 = linearOption2; - applicationId = applicationID; - } - - public NonLinearOptions LinearOption1 { get; set; } - public NonLinearOptions LinearOption2 { get; set; } -} - -[SpeckleType("Objects.Structural.CSI.Properties.CSIAreaSpring")] -public class CSIAreaSpring : PropertySpring -{ - public CSIAreaSpring() { } - - [SchemaInfo("LinearSpring", "Create an CSI AreaSpring", "CSI", "Properties")] - public CSIAreaSpring( - string name, - double StiffnessX, - double StiffnessY, - double StiffnessZ, - NonLinearOptions linearOption3, - string? applicationID = null - ) - { - this.name = name; - stiffnessX = StiffnessX; - stiffnessY = StiffnessY; - stiffnessZ = StiffnessZ; - LinearOption3 = linearOption3; - applicationId = applicationID; - } - - public NonLinearOptions LinearOption3 { get; set; } -} diff --git a/src/Speckle.Objects/Structural/CSI/Properties/CSITendonProperty.cs b/src/Speckle.Objects/Structural/CSI/Properties/CSITendonProperty.cs deleted file mode 100644 index 0fd0fee7..00000000 --- a/src/Speckle.Objects/Structural/CSI/Properties/CSITendonProperty.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Speckle.Objects.Structural.Properties; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.CSI.Properties; - -[SpeckleType("Objects.Structural.CSI.Properties.CSITendonProperty")] -public class CSITendonProperty : Property1D -{ - public ModelingOption modelingOption { get; set; } - public double Area { get; set; } -} diff --git a/src/Speckle.Objects/Structural/CSI/Properties/ETABSProperty.cs b/src/Speckle.Objects/Structural/CSI/Properties/ETABSProperty.cs deleted file mode 100644 index 76290979..00000000 --- a/src/Speckle.Objects/Structural/CSI/Properties/ETABSProperty.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace Speckle.Objects.Structural.CSI.Properties; - -public enum DiaphragmOption -{ - Disconnect, - FromShellObject, - DefinedDiaphragm -} - -public enum NonLinearOptions -{ - Linear, - CompressionOnly, - TensionOnly -} - -public enum SpringOption -{ - Link, - SoilProfileFooting -} - -public enum ModelingOption -{ - Loads, - Elements -} - -public enum DesignProcedure -{ - ProgramDetermined, - SteelFrameDesign, - ConcreteFrameDesign, - CompositeBeamDesign, - SteelJoistDesign, - NoDesign, - CompositeColumnDesign -} diff --git a/src/Speckle.Objects/Structural/GSA/Analysis/GSAAnalysisCase.cs b/src/Speckle.Objects/Structural/GSA/Analysis/GSAAnalysisCase.cs deleted file mode 100644 index 46fc3041..00000000 --- a/src/Speckle.Objects/Structural/GSA/Analysis/GSAAnalysisCase.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Speckle.Objects.Structural.Loading; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Analysis; - -[SpeckleType("Objects.Structural.GSA.Analysis.GSAAnalysisCase")] -public class GSAAnalysisCase : Base -{ - public GSAAnalysisCase() { } - - [SchemaInfo("GSAAnalysisCase", "Creates a Speckle structural analysis case for GSA", "GSA", "Analysis")] - public GSAAnalysisCase( - int nativeId, - string name, - GSATask task, - [SchemaParamInfo("A list of load cases")] List loadCases, - [SchemaParamInfo("A list of load factors (to be mapped to provided load cases)")] List loadFactors - ) - { - if (loadCases.Count != loadFactors.Count) - { - throw new ArgumentException("Number of load cases provided does not match number of load factors provided"); - } - - this.nativeId = nativeId; - this.name = name; - this.task = task; - this.loadCases = loadCases; - this.loadFactors = loadFactors; - } - - public int nativeId { get; set; } - public string name { get; set; } - - [DetachProperty] - public GSATask task { get; set; } //task reference - - [DetachProperty] - public List loadCases { get; set; } - - public List loadFactors { get; set; } -} diff --git a/src/Speckle.Objects/Structural/GSA/Analysis/GSAStage.cs b/src/Speckle.Objects/Structural/GSA/Analysis/GSAStage.cs deleted file mode 100644 index 38cc2b29..00000000 --- a/src/Speckle.Objects/Structural/GSA/Analysis/GSAStage.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Analysis; - -[SpeckleType("Objects.Structural.GSA.Analysis.GSAStage")] -public class GSAStage : Base -{ - public GSAStage() { } - - [SchemaInfo("GSAStage", "Creates a Speckle structural analysis stage for GSA", "GSA", "Analysis")] - public GSAStage( - int nativeId, - string name, - string colour, - List elements, - double creepFactor, - int stageTime, - List lockedElements - ) - { - this.nativeId = nativeId; - this.name = name; - this.colour = colour; - this.elements = elements; - this.creepFactor = creepFactor; - this.stageTime = stageTime; - this.lockedElements = lockedElements; - } - - public int nativeId { get; set; } - public string name { get; set; } - public string colour { get; set; } - - [DetachProperty, Chunkable(5000)] - public List elements { get; set; } - - public double creepFactor { get; set; } //Phi - public int stageTime { get; set; } //number of days - - [DetachProperty, Chunkable(5000)] - public List lockedElements { get; set; } //elements not part of the current analysis stage -} diff --git a/src/Speckle.Objects/Structural/GSA/Analysis/GSATask.cs b/src/Speckle.Objects/Structural/GSA/Analysis/GSATask.cs deleted file mode 100644 index 002ba506..00000000 --- a/src/Speckle.Objects/Structural/GSA/Analysis/GSATask.cs +++ /dev/null @@ -1,54 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Analysis; - -[SpeckleType("Objects.Structural.GSA.Analysis.GSATask")] -public class GSATask : Base -{ - public GSATask() { } - - [SchemaInfo("GSAAnalysisTask", "Creates a Speckle structural analysis task for GSA", "GSA", "Analysis")] - public GSATask(int nativeId, string name) - { - this.nativeId = nativeId; - this.name = name; - } - - public int nativeId { get; set; } //equiv to num - public string name { get; set; } - public string stage { get; set; } - public string solver { get; set; } - public SolutionType solutionType { get; set; } - public int modeParameter1 { get; set; } //start mode - public int modeParameter2 { get; set; } //number of modes - public int numIterations { get; set; } - public string PDeltaOption { get; set; } - public string PDeltaCase { get; set; } - public string PrestressCase { get; set; } - public string resultSyntax { get; set; } - public PruningOption prune { get; set; } -} - -public enum SolutionType -{ - Undefined, //no solution specified - Static, - Modal, - Ritz, - Buckling, - StaticPDelta, - ModalPDelta, - RitzPDelta, - Mass, - Stability, - StabilityPDelta, - BucklingNonLinear, - Influence -} - -public enum PruningOption -{ - None, - Influence -} diff --git a/src/Speckle.Objects/Structural/GSA/Bridge/GSAAlignment.cs b/src/Speckle.Objects/Structural/GSA/Bridge/GSAAlignment.cs deleted file mode 100644 index 96d6e3be..00000000 --- a/src/Speckle.Objects/Structural/GSA/Bridge/GSAAlignment.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Speckle.Objects.Structural.GSA.Geometry; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Bridge; - -[SpeckleType("Objects.Structural.GSA.Bridge.GSAAlignment")] -public class GSAAlignment : Base -{ - public GSAAlignment() { } - - [SchemaInfo( - "GSAAlignment", - "Creates a Speckle structural alignment for GSA (as a setting out feature for bridge models)", - "GSA", - "Bridge" - )] - public GSAAlignment( - int nativeId, - string name, - GSAGridSurface gridSurface, - List chainage, - List curvature - ) - { - this.nativeId = nativeId; - this.name = name; - this.gridSurface = gridSurface; - this.chainage = chainage; - this.curvature = curvature; - } - - public int nativeId { get; set; } - public string name { get; set; } - - [DetachProperty] - public GSAGridSurface gridSurface { get; set; } - - public List chainage { get; set; } - public List curvature { get; set; } - - public int GetNumAlignmentPoints() - { - return chainage.Count + curvature.Count; - } -} diff --git a/src/Speckle.Objects/Structural/GSA/Bridge/GSAInfluence.cs b/src/Speckle.Objects/Structural/GSA/Bridge/GSAInfluence.cs deleted file mode 100644 index fe4faf93..00000000 --- a/src/Speckle.Objects/Structural/GSA/Bridge/GSAInfluence.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Speckle.Objects.Structural.Loading; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Bridge; - -[SpeckleType("Objects.Structural.GSA.Bridge.GSAInfluence")] -public class GSAInfluence : Base -{ - public GSAInfluence() { } - - public GSAInfluence(int nativeId, string name, double factor, InfluenceType type, LoadDirection direction) - { - this.nativeId = nativeId; - this.name = name; - this.factor = factor; - this.type = type; - this.direction = direction; - } - - public int nativeId { get; set; } - public string name { get; set; } - public double factor { get; set; } - public InfluenceType type { get; set; } - public LoadDirection direction { get; set; } -} - -public enum InfluenceType -{ - NotSet = 0, - FORCE, - DISPLACEMENT -} diff --git a/src/Speckle.Objects/Structural/GSA/Bridge/GSAInfluenceBeam.cs b/src/Speckle.Objects/Structural/GSA/Bridge/GSAInfluenceBeam.cs deleted file mode 100644 index f0bbf2a3..00000000 --- a/src/Speckle.Objects/Structural/GSA/Bridge/GSAInfluenceBeam.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Speckle.Objects.Structural.GSA.Geometry; -using Speckle.Objects.Structural.Loading; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Bridge; - -[SpeckleType("Objects.Structural.GSA.Bridge.GSAInfluenceBeam")] -public class GSAInfluenceBeam : GSAInfluence -{ - public GSAInfluenceBeam() { } - - [SchemaInfo( - "GSAInfluenceBeam", - "Creates a Speckle structural beam influence effect for GSA (for an influence analysis)", - "GSA", - "Bridge" - )] - public GSAInfluenceBeam( - int nativeId, - string name, - double factor, - InfluenceType type, - LoadDirection direction, - GSAElement1D element, - double position - ) - { - this.nativeId = nativeId; - this.name = name; - this.factor = factor; - this.type = type; - this.direction = direction; - this.element = element; - this.position = position; - } - - [DetachProperty] - public GSAElement1D element { get; set; } - - public double position { get; set; } -} diff --git a/src/Speckle.Objects/Structural/GSA/Bridge/GSAInfluenceNode.cs b/src/Speckle.Objects/Structural/GSA/Bridge/GSAInfluenceNode.cs deleted file mode 100644 index 2b79964b..00000000 --- a/src/Speckle.Objects/Structural/GSA/Bridge/GSAInfluenceNode.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Speckle.Objects.Structural.Geometry; -using Speckle.Objects.Structural.Loading; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Bridge; - -[SpeckleType("Objects.Structural.GSA.Bridge.GSAInfluenceNode")] -public class GSAInfluenceNode : GSAInfluence -{ - public GSAInfluenceNode() { } - - [SchemaInfo( - "GSAInfluenceBeam", - "Creates a Speckle structural node influence effect for GSA (for an influence analysis)", - "GSA", - "Bridge" - )] - public GSAInfluenceNode( - int nativeId, - string name, - double factor, - InfluenceType type, - LoadDirection direction, - Node node, - Axis axis - ) - { - this.nativeId = nativeId; - this.name = name; - this.factor = factor; - this.type = type; - this.direction = direction; - this.node = node; - this.axis = axis; - } - - [DetachProperty] - public Node node { get; set; } - - [DetachProperty] - public Axis axis { get; set; } -} diff --git a/src/Speckle.Objects/Structural/GSA/Bridge/GSAPath.cs b/src/Speckle.Objects/Structural/GSA/Bridge/GSAPath.cs deleted file mode 100644 index d24f8d8e..00000000 --- a/src/Speckle.Objects/Structural/GSA/Bridge/GSAPath.cs +++ /dev/null @@ -1,63 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Bridge; - -[SpeckleType("Objects.Structural.GSA.Bridge.GSAPath")] -public class GSAPath : Base -{ - public GSAPath() { } - - [SchemaInfo( - "GSAPath", - "Creates a Speckle structural path for GSA (a path defines traffic lines along a bridge relative to an alignments, for influence analysis)", - "GSA", - "Bridge" - )] - public GSAPath( - int nativeId, - string name, - PathType type, - int group, - GSAAlignment alignment, - double left, - double right, - double factor, - int numMarkedLanes - ) - { - this.nativeId = nativeId; - this.name = name; - this.type = type; - this.group = group; - this.alignment = alignment; - this.left = left; - this.right = right; - this.factor = factor; - this.numMarkedLanes = numMarkedLanes; - } - - public int nativeId { get; set; } - public string name { get; set; } - public PathType type { get; set; } - public int group { get; set; } - - [DetachProperty] - public GSAAlignment alignment { get; set; } - - public double left { get; set; } //left / centre offset - public double right { get; set; } //right offset / gauge - public double factor { get; set; } //left factor - public int numMarkedLanes { get; set; } -} - -public enum PathType -{ - NotSet = 0, - LANE, - FOOTWAY, - TRACK, - VEHICLE, - CWAY_1WAY, - CWAY_2WAY -} diff --git a/src/Speckle.Objects/Structural/GSA/Bridge/GSAUserVehicle.cs b/src/Speckle.Objects/Structural/GSA/Bridge/GSAUserVehicle.cs deleted file mode 100644 index 59fa1ef2..00000000 --- a/src/Speckle.Objects/Structural/GSA/Bridge/GSAUserVehicle.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Bridge; - -[SpeckleType("Objects.Structural.GSA.Bridge.GSAUserVehicle")] -public class GSAUserVehicle : Base -{ - public GSAUserVehicle() { } - - [SchemaInfo( - "GSAUserVehicle", - "Creates a Speckle structural user-defined vehicle (as a pattern of loading based on axle and wheel positions, for influence analysis) for GSA", - "GSA", - "Bridge" - )] - public GSAUserVehicle( - int nativeId, - string name, - double width, - List axlePositions, - List axleOffsets, - List axleLeft, - List axleRight - ) - { - this.nativeId = nativeId; - this.name = name; - this.width = width; - this.axlePositions = axlePositions; - this.axleOffsets = axleOffsets; - this.axleLeft = axleLeft; - this.axleRight = axleRight; - } - - public int nativeId { get; set; } - public string name { get; set; } - public double width { get; set; } //vehicle width - public List axlePositions { get; set; } - public List axleOffsets { get; set; } // offset from centreline - public List axleLeft { get; set; } //load on left side - public List axleRight { get; set; } //load on right side -} diff --git a/src/Speckle.Objects/Structural/GSA/Geometry/GSAAssembly.cs b/src/Speckle.Objects/Structural/GSA/Geometry/GSAAssembly.cs deleted file mode 100644 index a0f57570..00000000 --- a/src/Speckle.Objects/Structural/GSA/Geometry/GSAAssembly.cs +++ /dev/null @@ -1,67 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Geometry; - -[SpeckleType("Objects.Structural.GSA.Geometry.GSAAssembly")] -public class GSAAssembly : Base -{ - public GSAAssembly() { } - - [SchemaInfo( - "GSAAssembly", - "Creates a Speckle structural assembly (ie. a way to define an entity that is formed from a collection of elements or members) for GSA", - "GSA", - "Bridge" - )] - public GSAAssembly( - int nativeId, - string name, - List entities, - GSANode end1Node, - GSANode end2Node, - GSANode orientationNode, - double sizeY, - double sizeZ, - string curveType, - int curveOrder, - string pointDefinition, - List points - ) - { - this.nativeId = nativeId; - this.name = name; - this.entities = entities; - this.end1Node = end1Node; - this.end2Node = end2Node; - this.orientationNode = orientationNode; - this.sizeY = sizeY; - this.sizeZ = sizeZ; - this.curveType = curveType; - this.curveOrder = curveOrder; - this.pointDefinition = pointDefinition; - this.points = points; - } - - public int nativeId { get; set; } //equiv to num record of gwa keyword - public string name { get; set; } - - [DetachProperty, Chunkable(5000)] - public List entities { get; set; } //nodes, elements, members - - [DetachProperty] - public GSANode end1Node { get; set; } - - [DetachProperty] - public GSANode end2Node { get; set; } - - [DetachProperty] - public GSANode orientationNode { get; set; } - - public double sizeY { get; set; } - public double sizeZ { get; set; } - public string curveType { get; set; } // enum? circular or lagrange sufficient? - public int curveOrder { get; set; } - public string pointDefinition { get; set; } // enum as well? points and spacing to start? || points and storeys to be supported - public List points { get; set; } // or make this Base type to accomdate storey list and explicit range? or add sep property for those cases? -} diff --git a/src/Speckle.Objects/Structural/GSA/Geometry/GSAElement1D.cs b/src/Speckle.Objects/Structural/GSA/Geometry/GSAElement1D.cs deleted file mode 100644 index 48e8b43f..00000000 --- a/src/Speckle.Objects/Structural/GSA/Geometry/GSAElement1D.cs +++ /dev/null @@ -1,83 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Structural.Geometry; -using Speckle.Objects.Structural.Properties; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Geometry; - -[SpeckleType("Objects.Structural.GSA.Geometry.GSAElement1D")] -public class GSAElement1D : Element1D -{ - public GSAElement1D() { } - - [SchemaInfo( - "GSAElement1D (from local axis)", - "Creates a Speckle structural 1D element for GSA (from local axis)", - "GSA", - "Geometry" - )] - public GSAElement1D( - int nativeId, - Line baseLine, - Property1D property, - ElementType1D type, - [SchemaParamInfo("If null, restraint condition defaults to unreleased (fully fixed translations and rotations)")] - Restraint? end1Releases = null, - [SchemaParamInfo("If null, restraint condition defaults to unreleased (fully fixed translations and rotations)")] - Restraint? end2Releases = null, - [SchemaParamInfo("If null, defaults to no offsets")] Vector? end1Offset = null, - [SchemaParamInfo("If null, defaults to no offsets")] Vector? end2Offset = null, - Plane? localAxis = null - ) - { - this.nativeId = nativeId; - this.baseLine = baseLine; - this.property = property; - this.type = type; - 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; - } - - [SchemaInfo( - "GSAElement1D (from orientation node and angle)", - "Creates a Speckle structural 1D element for GSA (from orientation node and angle)", - "GSA", - "Geometry" - )] - public GSAElement1D( - int nativeId, - Line baseLine, - Property1D property, - ElementType1D type, - [SchemaParamInfo("If null, restraint condition defaults to unreleased (fully fixed translations and rotations)")] - Restraint? end1Releases = null, - [SchemaParamInfo("If null, restraint condition defaults to unreleased (fully fixed translations and rotations)")] - Restraint? end2Releases = null, - [SchemaParamInfo("If null, defaults to no offsets")] Vector? end1Offset = null, - [SchemaParamInfo("If null, defaults to no offsets")] Vector? end2Offset = null, - Node? orientationNode = null, - double orientationAngle = 0 - ) - { - this.nativeId = nativeId; - this.baseLine = baseLine; - this.property = property; - this.type = type; - 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 int nativeId { get; set; } //equiv to num record of gwa keyword - public int group { get; set; } - public string colour { get; set; } - public string action { get; set; } - public bool isDummy { get; set; } -} diff --git a/src/Speckle.Objects/Structural/GSA/Geometry/GSAElement2D.cs b/src/Speckle.Objects/Structural/GSA/Geometry/GSAElement2D.cs deleted file mode 100644 index 5797e2f1..00000000 --- a/src/Speckle.Objects/Structural/GSA/Geometry/GSAElement2D.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Speckle.Objects.Structural.Geometry; -using Speckle.Objects.Structural.Properties; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Geometry; - -[SpeckleType("Objects.Structural.GSA.Geometry.GSAElement2D")] -public class GSAElement2D : Element2D -{ - public GSAElement2D() { } - - [SchemaInfo("GSAElement2D", "Creates a Speckle structural 2D element for GSA", "GSA", "Geometry")] - public GSAElement2D( - int nativeId, - List nodes, - Property2D property, - ElementType2D type, - string? name = null, - double offset = 0, - double orientationAngle = 0, - int group = 0, - string colour = "NO_RGB", - bool isDummy = false - ) - { - this.nativeId = nativeId; - topology = nodes; - this.property = property; - this.type = type; - this.name = name ?? ""; - this.nativeId = nativeId; - this.offset = offset; - this.orientationAngle = orientationAngle; - this.group = group; - this.colour = colour; - this.isDummy = isDummy; - } - - public int nativeId { get; set; } - public int group { get; set; } - public string colour { get; set; } - public bool isDummy { get; set; } -} diff --git a/src/Speckle.Objects/Structural/GSA/Geometry/GSAElement3D.cs b/src/Speckle.Objects/Structural/GSA/Geometry/GSAElement3D.cs deleted file mode 100644 index b01e225e..00000000 --- a/src/Speckle.Objects/Structural/GSA/Geometry/GSAElement3D.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Structural.Geometry; -using Speckle.Objects.Structural.Properties; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Geometry; - -[SpeckleType("Objects.Structural.GSA.Geometry.GSAElement3D")] -public class GSAElement3D : Element3D -{ - public GSAElement3D() { } - - [SchemaInfo("GSAElement3D", "Creates a Speckle structural 3D element for GSA", "GSA", "Geometry")] - public GSAElement3D( - int nativeId, - Mesh baseMesh, - Property3D property, - ElementType3D type, - string? name = null, - double orientationAngle = 0, - int group = 0, - string colour = "NO_RGB", - bool isDummy = false - ) - { - this.nativeId = nativeId; - this.baseMesh = baseMesh; - this.property = property; - this.type = type; - this.name = name ?? ""; - this.orientationAngle = orientationAngle; - this.group = group; - this.colour = colour; - this.isDummy = isDummy; - } - - public int nativeId { get; set; } - public int group { get; set; } - public string colour { get; set; } - public bool isDummy { get; set; } -} diff --git a/src/Speckle.Objects/Structural/GSA/Geometry/GSAGeneralisedRestraint.cs b/src/Speckle.Objects/Structural/GSA/Geometry/GSAGeneralisedRestraint.cs deleted file mode 100644 index e862d3e7..00000000 --- a/src/Speckle.Objects/Structural/GSA/Geometry/GSAGeneralisedRestraint.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Speckle.Objects.Structural.Geometry; -using Speckle.Objects.Structural.GSA.Analysis; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Geometry; - -[SpeckleType("Objects.Structural.GSA.Geometry.GSAGeneralisedRestraint")] -public class GSAGeneralisedRestraint : Base -{ - public GSAGeneralisedRestraint() { } - - [SchemaInfo( - "GSAGeneralisedRestraint", - "Creates a Speckle structural generalised restraint (a set of restraint conditions to be applied to a list of nodes) for GSA", - "GSA", - "Geometry" - )] - public GSAGeneralisedRestraint( - int nativeId, - string name, - Restraint restraint, - List nodes, - List stages - ) - { - this.nativeId = nativeId; - this.name = name; - this.restraint = restraint; - this.nodes = nodes; - this.stages = stages; - } - - public int nativeId { get; set; } - public string name { get; set; } - - [DetachProperty] - public Restraint restraint { get; set; } - - [DetachProperty, Chunkable(5000)] - public List nodes { get; set; } - - [DetachProperty] - public List stages { get; set; } -} diff --git a/src/Speckle.Objects/Structural/GSA/Geometry/GSAGridLine.cs b/src/Speckle.Objects/Structural/GSA/Geometry/GSAGridLine.cs deleted file mode 100644 index 38b08c41..00000000 --- a/src/Speckle.Objects/Structural/GSA/Geometry/GSAGridLine.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Speckle.Objects.BuiltElements; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Geometry; - -[SpeckleType("Objects.Structural.GSA.Geometry.GSAGridLine")] -public class GSAGridLine : GridLine -{ - public GSAGridLine() { } - - [SchemaInfo("GSAGridLine", "Creates a Speckle structural grid line for GSA", "GSA", "Geometry")] - public GSAGridLine(int nativeId, string name, ICurve line) - { - this.nativeId = nativeId; - label = name; - baseLine = line; - } - - public int nativeId { get; set; } -} diff --git a/src/Speckle.Objects/Structural/GSA/Geometry/GSAGridPlane.cs b/src/Speckle.Objects/Structural/GSA/Geometry/GSAGridPlane.cs deleted file mode 100644 index 96b485c3..00000000 --- a/src/Speckle.Objects/Structural/GSA/Geometry/GSAGridPlane.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Speckle.Objects.Structural.Geometry; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Geometry; - -[SpeckleType("Objects.Structural.GSA.Geometry.GSAGridPlane")] -public class GSAGridPlane : Storey -{ - public GSAGridPlane() { } - - [SchemaInfo("GSAGridPlane", "Creates a Speckle structural grid plane for GSA", "GSA", "Geometry")] - public GSAGridPlane(int nativeId, string name, Axis axis, double elevation) - { - this.nativeId = nativeId; - this.name = name; - this.axis = axis; - this.elevation = elevation; // the height of the grid plane above the origin (of the specified axis) - } - - public int nativeId { get; set; } - - [DetachProperty] - public Axis axis { get; set; } - - public double? toleranceBelow { get; set; } - public double? toleranceAbove { get; set; } -} diff --git a/src/Speckle.Objects/Structural/GSA/Geometry/GSAGridSurface.cs b/src/Speckle.Objects/Structural/GSA/Geometry/GSAGridSurface.cs deleted file mode 100644 index ec45949c..00000000 --- a/src/Speckle.Objects/Structural/GSA/Geometry/GSAGridSurface.cs +++ /dev/null @@ -1,62 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Geometry; - -[SpeckleType("Objects.Structural.GSA.Geometry.GSAGridSurface")] -public class GSAGridSurface : Base -{ - public GSAGridSurface() { } - - [SchemaInfo("GSAGridSurface", "Creates a Speckle structural grid surface for GSA", "GSA", "Geometry")] - public GSAGridSurface( - string name, - int nativeId, - GSAGridPlane gridPlane, - double tolerance, - double spanDirection, - LoadExpansion loadExpansion, - GridSurfaceSpanType span, - List elements - ) - { - this.name = name; - this.nativeId = nativeId; - this.gridPlane = gridPlane; - this.tolerance = tolerance; - this.spanDirection = spanDirection; - this.loadExpansion = loadExpansion; - this.span = span; - this.elements = elements; - } - - public string name { get; set; } - public int nativeId { get; set; } - - [DetachProperty] - public GSAGridPlane gridPlane { get; set; } - - public double tolerance { get; set; } - public double spanDirection { get; set; } - public LoadExpansion loadExpansion { get; set; } - public GridSurfaceSpanType span { get; set; } - - [DetachProperty, Chunkable(5000)] - public List elements { get; set; } -} - -public enum GridSurfaceSpanType -{ - NotSet = 0, - OneWay, - TwoWay -} - -public enum LoadExpansion -{ - NotSet = 0, - Legacy = 1, - PlaneAspect = 2, - PlaneSmooth = 3, - PlaneCorner = 4 -} diff --git a/src/Speckle.Objects/Structural/GSA/Geometry/GSAMember1D.cs b/src/Speckle.Objects/Structural/GSA/Geometry/GSAMember1D.cs deleted file mode 100644 index 2dbd15e6..00000000 --- a/src/Speckle.Objects/Structural/GSA/Geometry/GSAMember1D.cs +++ /dev/null @@ -1,80 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Structural.Geometry; -using Speckle.Objects.Structural.Properties; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Geometry; - -[SpeckleType("Objects.Structural.GSA.Geometry.GSAMember1D")] -public class GSAMember1D : Element1D -{ - public GSAMember1D() { } - - [SchemaInfo( - "GSAMember1D (from local axis)", - "Creates a Speckle structural 1D member for GSA (from local axis)", - "GSA", - "Geometry" - )] - public GSAMember1D( - int nativeId, - Line baseLine, - Property1D property, - ElementType1D type, - Restraint end1Releases, - Restraint end2Releases, - Vector end1Offset, - Vector end2Offset, - Plane localAxis - ) - { - this.nativeId = nativeId; - this.baseLine = baseLine; - this.property = property; - this.type = type; - this.end1Releases = end1Releases; - this.end2Releases = end2Releases; - this.end1Offset = end1Offset; - this.end2Offset = end2Offset; - this.localAxis = localAxis; - } - - [SchemaInfo( - "GSAMember1D (from orientation node and angle)", - "Creates a Speckle structural 1D member for GSA (from orientation node and angle)", - "GSA", - "Geometry" - )] - public GSAMember1D( - int nativeId, - Line baseLine, - Property1D property, - ElementType1D type, - Restraint end1Releases, - Restraint end2Releases, - Vector end1Offset, - Vector end2Offset, - GSANode orientationNode, - double orientationAngle - ) - { - this.nativeId = nativeId; - this.baseLine = baseLine; - this.property = property; - this.type = type; - this.end1Releases = end1Releases; - this.end2Releases = end2Releases; - this.end1Offset = end1Offset; - this.end2Offset = end2Offset; - this.orientationNode = orientationNode; - this.orientationAngle = orientationAngle; - } - - public int nativeId { get; set; } - public int group { get; set; } - public string colour { get; set; } - public bool isDummy { get; set; } - public bool intersectsWithOthers { get; set; } - public double targetMeshSize { get; set; } -} diff --git a/src/Speckle.Objects/Structural/GSA/Geometry/GSAMember2D.cs b/src/Speckle.Objects/Structural/GSA/Geometry/GSAMember2D.cs deleted file mode 100644 index 8027d8c1..00000000 --- a/src/Speckle.Objects/Structural/GSA/Geometry/GSAMember2D.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Speckle.Objects.Structural.Geometry; -using Speckle.Objects.Structural.Properties; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Geometry; - -[SpeckleType("Objects.Structural.GSA.Geometry.GSAMember2D")] -public class GSAMember2D : Element2D -{ - public GSAMember2D() { } - - [SchemaInfo("GSAMember2D", "Creates a Speckle structural 2D member for GSA", "GSA", "Geometry")] - public GSAMember2D( - [SchemaParamInfo( - "An ordered list of nodes which represents the perimeter of a member (ie. order of points should based on valid polyline)" - )] - List perimeter, - Property2D property, - ElementType2D type, - [SchemaParamInfo( - "A list of ordered lists of nodes representing the voids within a member (ie. order of points should be based on valid polyline)" - )] - List>? voids = null, - double offset = 0, - double orientationAngle = 0 - ) - { - topology = perimeter; //needs to be ordered properly (ie. matching the point order of a valid polyline) - this.property = property; - this.type = type; - this.voids = voids; //needs to be ordered properly (ie. matching the point order of a valid polyline) - this.offset = offset; - this.orientationAngle = orientationAngle; - } - - public int nativeId { get; set; } - public int group { get; set; } - public string colour { get; set; } - public bool isDummy { get; set; } - public bool intersectsWithOthers { get; set; } - public double targetMeshSize { get; set; } - public List>? voids { get; set; } -} diff --git a/src/Speckle.Objects/Structural/GSA/Geometry/GSANode.cs b/src/Speckle.Objects/Structural/GSA/Geometry/GSANode.cs deleted file mode 100644 index 37a9b7bc..00000000 --- a/src/Speckle.Objects/Structural/GSA/Geometry/GSANode.cs +++ /dev/null @@ -1,66 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Structural.Geometry; -using Speckle.Objects.Structural.Properties; -using Speckle.Sdk.Common; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Geometry; - -[SpeckleType("Objects.Structural.GSA.Geometry.GSANode")] -public class GSANode : Node -{ - public GSANode() { } - - /// - /// SchemaBuilder constructor for a GSA node - /// - /// - /// - /// - /// - /// - /// - /// - [SchemaInfo("GSANode", "Creates a Speckle structural node for GSA", "GSA", "Geometry")] - public GSANode( - int nativeId, - Point basePoint, - Restraint restraint, - Axis? constraintAxis = null, - PropertySpring? springProperty = null, - PropertyMass? massProperty = null, - PropertyDamper? damperProperty = null, - double localElementSize = 0, - string colour = "NO_RGB" - ) - { - this.nativeId = nativeId; - this.basePoint = basePoint; - this.restraint = restraint; - this.constraintAxis = - constraintAxis == null - ? new Axis( - "Global", - AxisType.Cartesian, - new Plane() - { - origin = new Point(0, 0), - normal = new Vector(0, 0, 1), - xdir = new Vector(1, 0, 0), - ydir = new Vector(0, 1, 0), - units = Units.Meters //Not sure if defaulting to meters is correct, but it was what we were doing previously inside Plane's ctor - } - ) - : constraintAxis; - this.springProperty = springProperty; - this.massProperty = massProperty; - this.damperProperty = damperProperty; - this.localElementSize = localElementSize; - this.colour = colour; - } - - public int nativeId { get; set; } - public double localElementSize { get; set; } - public string colour { get; set; } -} diff --git a/src/Speckle.Objects/Structural/GSA/Geometry/GSARigidConstraint.cs b/src/Speckle.Objects/Structural/GSA/Geometry/GSARigidConstraint.cs deleted file mode 100644 index 2eb3cbfd..00000000 --- a/src/Speckle.Objects/Structural/GSA/Geometry/GSARigidConstraint.cs +++ /dev/null @@ -1,88 +0,0 @@ -using Speckle.Objects.Structural.Geometry; -using Speckle.Objects.Structural.GSA.Analysis; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Geometry; - -[SpeckleType("Objects.Structural.GSA.Geometry.GSARigidConstraint")] -public class GSARigidConstraint : Base -{ - public GSARigidConstraint() { } - - [SchemaInfo( - "GSARigidConstraint", - "Creates a Speckle structural rigid restraint (a set of nodes constrained to move as a rigid body) for GSA", - "GSA", - "Geometry" - )] - public GSARigidConstraint( - string name, - int nativeId, - Node primaryNode, - List constrainedNodes, - Base parentMember, - List stages, - LinkageType type, - Dictionary> constraintCondition - ) - { - this.name = name; - this.nativeId = nativeId; - this.primaryNode = primaryNode; - this.constrainedNodes = constrainedNodes; - this.parentMember = parentMember; - this.stages = stages; - this.type = type; - this.constraintCondition = constraintCondition; - } - - public string name { get; set; } - public int nativeId { get; set; } - - [DetachProperty] - public Node primaryNode { get; set; } - - [DetachProperty, Chunkable(5000)] - public List constrainedNodes { get; set; } - - [DetachProperty] - public Base parentMember { get; set; } - - [DetachProperty] - public List stages { get; set; } - - public LinkageType type { get; set; } - public Dictionary> constraintCondition { get; set; } -} - -public enum AxisDirection6 -{ - NotSet = 0, - X, - Y, - Z, - XX, - YY, - ZZ -} - -public enum LinkageType -{ - NotSet = 0, - ALL, - XY_PLANE, - YZ_PLANE, - ZX_PLANE, - XY_PLATE, - YZ_PLATE, - ZX_PLATE, - PIN, - XY_PLANE_PIN, - YZ_PLANE_PIN, - ZX_PLANE_PIN, - XY_PLATE_PIN, - YZ_PLATE_PIN, - ZX_PLATE_PIN, - Custom -} diff --git a/src/Speckle.Objects/Structural/GSA/Geometry/GSAStorey.cs b/src/Speckle.Objects/Structural/GSA/Geometry/GSAStorey.cs deleted file mode 100644 index 0369673c..00000000 --- a/src/Speckle.Objects/Structural/GSA/Geometry/GSAStorey.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Speckle.Objects.Structural.Geometry; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Geometry; - -[SpeckleType("Objects.Structural.GSA.Geometry.GSAStorey")] -public class GSAStorey : Storey -{ - public GSAStorey() { } - - [SchemaInfo( - "GSAStorey", - "Creates a Speckle structural storey (to describe floor levels/storeys in the structural model) for GSA", - "GSA", - "Geometry" - )] - public GSAStorey(int nativeId, string name, Axis axis, double elevation, double toleranceBelow, double toleranceAbove) - { - this.nativeId = nativeId; - this.name = name; - this.axis = axis; - this.elevation = elevation; - this.toleranceBelow = toleranceBelow; - this.toleranceAbove = toleranceAbove; - } - - public int nativeId { get; set; } - - [DetachProperty] - public Axis axis { get; set; } - - public double toleranceBelow { get; set; } - public double toleranceAbove { get; set; } -} diff --git a/src/Speckle.Objects/Structural/GSA/Loading/GSALoadBeam.cs b/src/Speckle.Objects/Structural/GSA/Loading/GSALoadBeam.cs deleted file mode 100644 index 8b1f229c..00000000 --- a/src/Speckle.Objects/Structural/GSA/Loading/GSALoadBeam.cs +++ /dev/null @@ -1,79 +0,0 @@ -using Speckle.Objects.Structural.Geometry; -using Speckle.Objects.Structural.Loading; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Loading; - -[SpeckleType("Objects.Structural.GSA.Loading.GSALoadBeam")] -public class GSALoadBeam : LoadBeam -{ - public GSALoadBeam() { } - - [SchemaInfo("GSALoadBeam", "Creates a Speckle structural beam (1D elem/member) load for GSA", "GSA", "Loading")] - public GSALoadBeam( - int nativeId, - LoadCase loadCase, - List elements, - BeamLoadType loadType, - LoadDirection direction, - LoadAxisType loadAxisType = LoadAxisType.Global, - [SchemaParamInfo( - "A list that represents load magnitude (number of values varies based on load type - Point: 1, Uniform: 1, Linear: 2, Patch: 2, Tri-linear:2)" - )] - List? values = null, - [SchemaParamInfo( - "A list that represents load locations (number of values varies based on load type - Point: 1, Uniform: null, Linear: null, Patch: 2, Tri-linear: 2)" - )] - List? positions = null, - bool isProjected = false - ) - { - this.nativeId = nativeId; - this.loadCase = loadCase; - this.elements = elements; - this.loadType = loadType; - this.direction = direction; - this.loadAxisType = loadAxisType; - this.values = values; - this.positions = positions; - this.isProjected = isProjected; - } - - [SchemaInfo( - "GSALoadBeam (user-defined axis)", - "Creates a Speckle structural beam (1D elem/member) load (specified for a user-defined axis) for GSA", - "GSA", - "Loading" - )] - public GSALoadBeam( - int nativeId, - LoadCase loadCase, - List elements, - BeamLoadType loadType, - LoadDirection direction, - Axis loadAxis, - [SchemaParamInfo( - "A list that represents load magnitude (number of values varies based on load type - Point: 1, Uniform: 1, Linear: 2, Patch: 2, Tri-linear:2)" - )] - List? values = null, - [SchemaParamInfo( - "A list that represents load locations (number of values varies based on load type - Point: 1, Uniform: null, Linear: null, Patch: 2, Tri-linear: 2)" - )] - List? positions = null, - bool isProjected = false - ) - { - this.nativeId = nativeId; - this.loadCase = loadCase; - this.elements = elements; - this.loadType = loadType; - this.direction = direction; - this.values = values; - this.positions = positions; - this.isProjected = isProjected; - this.nativeId = nativeId; - } - - public int nativeId { get; set; } -} diff --git a/src/Speckle.Objects/Structural/GSA/Loading/GSALoadCase.cs b/src/Speckle.Objects/Structural/GSA/Loading/GSALoadCase.cs deleted file mode 100644 index e41f03bb..00000000 --- a/src/Speckle.Objects/Structural/GSA/Loading/GSALoadCase.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Speckle.Objects.Structural.Loading; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Loading; - -[SpeckleType("Objects.Structural.GSA.Loading.GSALoadCase")] -public class GSALoadCase : LoadCase -{ - public GSALoadCase() { } - - [SchemaInfo("GSALoadCase", "Creates a Speckle structural load case for GSA", "GSA", "Loading")] - public GSALoadCase( - int nativeId, - string name, - LoadType loadType, - LoadDirection2D loadDirection, - string? source = null, - ActionType actionType = ActionType.None, - string? description = null, - string? include = null, - bool bridge = false - ) - { - this.nativeId = nativeId; - this.name = name; - this.loadType = loadType; - group = source ?? ""; - this.actionType = actionType; - this.description = description ?? ""; - direction = loadDirection; - this.include = include ?? ""; - this.bridge = bridge; - } - - public int nativeId { get; set; } - public LoadDirection2D direction { get; set; } - public string include { get; set; } - public bool bridge { get; set; } -} diff --git a/src/Speckle.Objects/Structural/GSA/Loading/GSALoadCombination.cs b/src/Speckle.Objects/Structural/GSA/Loading/GSALoadCombination.cs deleted file mode 100644 index a9897e91..00000000 --- a/src/Speckle.Objects/Structural/GSA/Loading/GSALoadCombination.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Speckle.Objects.Structural.Loading; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Loading; - -[SpeckleType("Objects.Structural.GSA.Loading.GSALoadCombination")] -public class GSALoadCombination : LoadCombination -{ - public GSALoadCombination() { } - - [SchemaInfo("GSALoadCombination", "Creates a Speckle load combination for GSA", "GSA", "Loading")] - public GSALoadCombination( - int nativeId, - string name, - [SchemaParamInfo("A list of load cases")] List loadCases, - [SchemaParamInfo("A list of load factors (to be mapped to provided load cases)")] List loadFactors - ) - { - this.nativeId = nativeId; - this.name = name; - - if (loadCases.Count != loadFactors.Count) - { - throw new ArgumentException("Number of load cases provided does not match number of load factors provided"); - } - - this.loadFactors = loadFactors; - this.loadCases = loadCases; - this.nativeId = nativeId; - } - - public int nativeId { get; set; } -} diff --git a/src/Speckle.Objects/Structural/GSA/Loading/GSALoadFace.cs b/src/Speckle.Objects/Structural/GSA/Loading/GSALoadFace.cs deleted file mode 100644 index c5be4878..00000000 --- a/src/Speckle.Objects/Structural/GSA/Loading/GSALoadFace.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Speckle.Objects.Structural.Loading; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Loading; - -[SpeckleType("Objects.Structural.GSA.Loading.GSALoadFace")] -public class GSALoadFace : LoadFace -{ - public GSALoadFace() { } - - [SchemaInfo("GSALoadFace", "Creates a Speckle structural face (2D elem/member) load for GSA", "GSA", "Loading")] - public GSALoadFace( - int nativeId, - LoadCase loadCase, - List elements, - FaceLoadType loadType, - LoadDirection2D direction, - LoadAxisType loadAxisType = LoadAxisType.Global, - [SchemaParamInfo( - "A list that represents load magnitude (number of values varies based on load type - Uniform: 1, Variable: 4 (corner nodes), Point: 1)" - )] - List? values = null, - [SchemaParamInfo( - "A list that represents load locations (number of values varies based on load type - Uniform: null, Variable: null, Point: 2)" - )] - List? positions = null, - bool isProjected = false - ) - { - this.nativeId = nativeId; - this.loadCase = loadCase; - this.elements = elements; - this.loadType = loadType; - this.direction = direction; - this.loadAxisType = loadAxisType; - this.values = values; - this.positions = positions; - this.isProjected = isProjected; - } - - public int nativeId { get; set; } -} diff --git a/src/Speckle.Objects/Structural/GSA/Loading/GSALoadGravity.cs b/src/Speckle.Objects/Structural/GSA/Loading/GSALoadGravity.cs deleted file mode 100644 index c2ebbac6..00000000 --- a/src/Speckle.Objects/Structural/GSA/Loading/GSALoadGravity.cs +++ /dev/null @@ -1,73 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Structural.Loading; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Loading; - -[SpeckleType("Objects.Structural.GSA.Loading.GSALoadGravity")] -public class GSALoadGravity : LoadGravity -{ - public GSALoadGravity() { } - - [SchemaInfo( - "GSALoadGravity", - "Creates a Speckle structural gravity load (applied to all nodes and elements) for GSA", - "GSA", - "Loading" - )] - public GSALoadGravity(int nativeId, string name, LoadCase loadCase, Vector? gravityFactors = null) - { - this.nativeId = nativeId; - this.name = name; - this.loadCase = loadCase; - this.gravityFactors = gravityFactors ?? new Vector(0, 0, -1); - } - - [SchemaInfo( - "GSALoadGravity (specified elements)", - "Creates a Speckle structural gravity load (applied to specified elements) for GSA", - "GSA", - "Loading" - )] - public GSALoadGravity( - int nativeId, - string name, - LoadCase loadCase, - List elements, - Vector? gravityFactors = null - ) - { - this.nativeId = nativeId; - this.name = name; - this.elements = elements; - this.loadCase = loadCase; - this.gravityFactors = gravityFactors ?? new Vector(0, 0, -1); - } - - [SchemaInfo( - "GSALoadGravity (specified elements and nodes)", - "Creates a Speckle structural gravity load (applied to specified nodes and elements) for GSA", - "GSA", - "Loading" - )] - public GSALoadGravity( - int nativeId, - string name, - LoadCase loadCase, - List elements, - List nodes, - Vector? gravityFactors = null, - string? nativedId = null - ) - { - this.nativeId = nativeId; - this.name = name; - this.elements = elements; - this.nodes = nodes; - this.loadCase = loadCase; - this.gravityFactors = gravityFactors ?? new Vector(0, 0, -1); - } - - public int nativeId { get; set; } -} diff --git a/src/Speckle.Objects/Structural/GSA/Loading/GSALoadGrid.cs b/src/Speckle.Objects/Structural/GSA/Loading/GSALoadGrid.cs deleted file mode 100644 index c886d45a..00000000 --- a/src/Speckle.Objects/Structural/GSA/Loading/GSALoadGrid.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Speckle.Objects.Structural.Geometry; -using Speckle.Objects.Structural.GSA.Geometry; -using Speckle.Objects.Structural.Loading; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Loading; - -[SpeckleType("Objects.Structural.GSA.Loading.GSALoadGrid")] -public abstract class GSALoadGrid : Load -{ - protected GSALoadGrid() { } - - protected GSALoadGrid(int nativeId, GSAGridSurface gridSurface, Axis loadAxis, LoadDirection2D direction) - { - this.nativeId = nativeId; - this.gridSurface = gridSurface; - this.loadAxis = loadAxis; - this.direction = direction; - } - - public int nativeId { get; set; } - public GSAGridSurface gridSurface { get; set; } - public Axis loadAxis { get; set; } - public LoadDirection2D direction { get; set; } -} diff --git a/src/Speckle.Objects/Structural/GSA/Loading/GSALoadGridArea.cs b/src/Speckle.Objects/Structural/GSA/Loading/GSALoadGridArea.cs deleted file mode 100644 index 0a7f0ff6..00000000 --- a/src/Speckle.Objects/Structural/GSA/Loading/GSALoadGridArea.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Structural.Geometry; -using Speckle.Objects.Structural.GSA.Geometry; -using Speckle.Objects.Structural.Loading; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Loading; - -[SpeckleType("Objects.Structural.GSA.Loading.GSALoadGridArea")] -public class GSALoadGridArea : GSALoadGrid -{ - public GSALoadGridArea() { } - - public GSALoadGridArea( - int nativeId, - GSAGridSurface gridSurface, - Axis loadAxis, - LoadDirection2D direction, - Polyline polyline, - bool isProjected, - double value - ) - { - this.nativeId = nativeId; - this.gridSurface = gridSurface; - this.loadAxis = loadAxis; - this.direction = direction; - this.polyline = polyline; - this.isProjected = isProjected; - this.value = value; - } - - public Polyline polyline { get; set; } - public bool isProjected { get; set; } - public double value { get; set; } -} diff --git a/src/Speckle.Objects/Structural/GSA/Loading/GSALoadGridLine.cs b/src/Speckle.Objects/Structural/GSA/Loading/GSALoadGridLine.cs deleted file mode 100644 index 9157b4e1..00000000 --- a/src/Speckle.Objects/Structural/GSA/Loading/GSALoadGridLine.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Structural.Geometry; -using Speckle.Objects.Structural.GSA.Geometry; -using Speckle.Objects.Structural.Loading; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Loading; - -[SpeckleType("Objects.Structural.GSA.Loading.GSALoadGridLine")] -public class GSALoadGridLine : GSALoadGrid -{ - public GSALoadGridLine() { } - - public GSALoadGridLine( - int nativeId, - GSAGridSurface gridSurface, - Axis loadAxis, - LoadDirection2D direction, - Polyline polyline, - bool isProjected, - List values - ) - { - this.nativeId = nativeId; - this.gridSurface = gridSurface; - this.loadAxis = loadAxis; - this.direction = direction; - this.polyline = polyline; - this.isProjected = isProjected; - this.values = values; - } - - public Polyline polyline { get; set; } - public bool isProjected { get; set; } - public List values { get; set; } -} diff --git a/src/Speckle.Objects/Structural/GSA/Loading/GSALoadGridPoint.cs b/src/Speckle.Objects/Structural/GSA/Loading/GSALoadGridPoint.cs deleted file mode 100644 index 623fe839..00000000 --- a/src/Speckle.Objects/Structural/GSA/Loading/GSALoadGridPoint.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Structural.Geometry; -using Speckle.Objects.Structural.GSA.Geometry; -using Speckle.Objects.Structural.Loading; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Loading; - -[SpeckleType("Objects.Structural.GSA.Loading.GSALoadGridPoint")] -public class GSALoadGridPoint : GSALoadGrid -{ - public GSALoadGridPoint() { } - - public GSALoadGridPoint( - int nativeId, - GSAGridSurface gridSurface, - Axis loadAxis, - LoadDirection2D direction, - Point position, - double value - ) - { - this.nativeId = nativeId; - this.gridSurface = gridSurface; - this.loadAxis = loadAxis; - this.direction = direction; - this.position = position; - this.value = value; - } - - public Point position { get; set; } - public double value { get; set; } -} diff --git a/src/Speckle.Objects/Structural/GSA/Loading/GSALoadNode.cs b/src/Speckle.Objects/Structural/GSA/Loading/GSALoadNode.cs deleted file mode 100644 index a2efb316..00000000 --- a/src/Speckle.Objects/Structural/GSA/Loading/GSALoadNode.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Speckle.Objects.Structural.Geometry; -using Speckle.Objects.Structural.GSA.Geometry; -using Speckle.Objects.Structural.Loading; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Loading; - -[SpeckleType("Objects.Structural.GSA.Loading.GSALoadNode")] -public class GSALoadNode : LoadNode -{ - public GSALoadNode() { } - - [SchemaInfo("GSALoadNode", "Creates a Speckle node load for GSA", "GSA", "Loading")] - public GSALoadNode( - int nativeId, - string name, - LoadCase loadCase, - List nodes, - LoadDirection direction, - double value - ) - { - this.nativeId = nativeId; - this.name = name; - this.loadCase = loadCase; - List baseNodes = nodes.ConvertAll(x => (Node)x); - this.nodes = baseNodes; - this.direction = direction; - this.value = value; - } - - [SchemaInfo( - "GSALoadNode (user-defined axis)", - "Creates a Speckle node load (user-defined axis) for GSA", - "GSA", - "Loading" - )] - public GSALoadNode( - int nativeId, - string name, - LoadCase loadCase, - List nodes, - Axis loadAxis, - LoadDirection direction, - double value - ) - { - this.nativeId = nativeId; - this.name = name; - this.loadCase = loadCase; - this.nodes = nodes; - this.loadAxis = loadAxis; - this.direction = direction; - this.value = value; - } - - public int nativeId { get; set; } -} diff --git a/src/Speckle.Objects/Structural/GSA/Loading/GSALoadThermal2d.cs b/src/Speckle.Objects/Structural/GSA/Loading/GSALoadThermal2d.cs deleted file mode 100644 index 7097485c..00000000 --- a/src/Speckle.Objects/Structural/GSA/Loading/GSALoadThermal2d.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Speckle.Objects.Structural.Geometry; -using Speckle.Objects.Structural.Loading; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Loading; - -[SpeckleType("Objects.Structural.GSA.Loading.GSALoadThermal2d")] -public class GSALoadThermal2d : Load -{ - public GSALoadThermal2d() { } - - public GSALoadThermal2d(int nativeId, List elements, Thermal2dLoadType type, List values) - { - this.nativeId = nativeId; - this.elements = elements; - this.type = type; - this.values = values; - } - - public int nativeId { get; set; } - - [DetachProperty, Chunkable(5000)] - public List elements { get; set; } - - public Thermal2dLoadType type { get; set; } - public List values { get; set; } -} - -public enum Thermal2dLoadType -{ - NotSet = 0, - Uniform, - Gradient, - General -} diff --git a/src/Speckle.Objects/Structural/GSA/Loading/GSAPolyline.cs b/src/Speckle.Objects/Structural/GSA/Loading/GSAPolyline.cs deleted file mode 100644 index 3a10c1b6..00000000 --- a/src/Speckle.Objects/Structural/GSA/Loading/GSAPolyline.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Structural.GSA.Geometry; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Loading; - -[SpeckleType("Objects.Structural.GSA.Loading.GSAPolyline")] -public class GSAPolyline : Polyline -{ - public GSAPolyline() { } - - [SchemaInfo("GSAPolyline", "Creates a Speckle structural polyline for GSA", "GSA", "Geometry")] - public GSAPolyline( - string name, - int nativeId, - IEnumerable coordinatesArray, - string colour, - GSAGridPlane gridPlane - ) - { - this.name = name; - this.nativeId = nativeId; - value = coordinatesArray.ToList(); - this.colour = colour; - this.gridPlane = gridPlane; - } - - public string name { get; set; } - public int nativeId { get; set; } - public string colour { get; set; } - - [DetachProperty] - public GSAGridPlane gridPlane { get; set; } -} diff --git a/src/Speckle.Objects/Structural/GSA/Materials/GSAConcrete.cs b/src/Speckle.Objects/Structural/GSA/Materials/GSAConcrete.cs deleted file mode 100644 index edfc08a5..00000000 --- a/src/Speckle.Objects/Structural/GSA/Materials/GSAConcrete.cs +++ /dev/null @@ -1,90 +0,0 @@ -using Speckle.Objects.Structural.Materials; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Materials; - -[SpeckleType("Objects.Structural.GSA.Materials.GSAConcrete")] -public class GSAConcrete : Concrete -{ - public GSAConcrete() { } - - [SchemaInfo("GSAConcrete", "Creates a Speckle structural concrete material for GSA", "GSA", "Materials")] - public GSAConcrete( - int nativeId, - string name, - string? grade = null, - string? designCode = null, - string? codeYear = null, - double elasticModulus = 0, - double compressiveStrength = 0, - double tensileStrength = 0, - double flexuralStrength = 0, - double maxCompressiveStrain = 0, - double maxTensileStrain = 0, - double maxAggregateSize = 0, - bool lightweight = false, - double poissonsRatio = 0, - double shearModulus = 0, - double density = 0, - double alpha = 0, - double dampingRatio = 0, - double cost = 0, - string colour = "NO_RGB" - ) - { - this.nativeId = nativeId; - this.name = name; - this.grade = grade; - materialType = MaterialType.Concrete; - this.designCode = designCode; - this.codeYear = codeYear; - this.elasticModulus = elasticModulus; - this.compressiveStrength = compressiveStrength; - this.tensileStrength = tensileStrength; - this.flexuralStrength = flexuralStrength; - this.maxCompressiveStrain = maxCompressiveStrain; - this.maxTensileStrain = maxTensileStrain; - this.maxAggregateSize = maxAggregateSize; - this.lightweight = lightweight; - this.poissonsRatio = poissonsRatio; - this.shearModulus = shearModulus; - this.density = density; - this.dampingRatio = dampingRatio; - this.cost = cost; - this.colour = colour; - } - - public int nativeId { get; set; } - public string colour { get; set; } - - // FROM GWA - //public string Name { get => name; set { name = value; } } - //public GsaMat Mat; - //public MatConcreteType Type; - //public MatConcreteCement Cement; - //public double? Fc; // - //public double? Fcd; // - //public double? Fcdc; // - //public double? Fcdt; // - //public double? Fcfib; - //public double? EmEs; - //public double? N; - //public double? Emod; - //public double? EpsPeak; - //public double? EpsMax; - //public double? EpsU; // have tens and comp represented separately - //public double? EpsAx; // have tens and comp represented separately - //public double? EpsTran; - //public double? EpsAxs; - //public bool Light; // add this - //public double? Agg; - //public double? XdMin; - //public double? XdMax; - //public double? Beta; - //public double? Shrink; - //public double? Confine; - //public double? Fcc; - //public double? EpsPlasC; - //public double? EpsUC; -} diff --git a/src/Speckle.Objects/Structural/GSA/Materials/GSAMaterial.cs b/src/Speckle.Objects/Structural/GSA/Materials/GSAMaterial.cs deleted file mode 100644 index e5ff5399..00000000 --- a/src/Speckle.Objects/Structural/GSA/Materials/GSAMaterial.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Speckle.Objects.Structural.Materials; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Materials; - -[SpeckleType("Objects.Structural.GSA.Materials.GSAMaterial")] -public class GSAMaterial : StructuralMaterial -{ - public GSAMaterial() { } - - [SchemaInfo("GSAMaterial", "Creates a Speckle structural material for GSA", "GSA", "Materials")] - public GSAMaterial( - int nativeId, - string name, - MaterialType type, - string? grade = null, - string? designCode = null, - string? codeYear = null, - double strength = 0, - double elasticModulus = 0, - double poissonsRatio = 0, - double shearModulus = 0, - double rho = 0, - double alpha = 0, - double dampingRatio = 0, - double cost = 0, - string colour = "NO_RGB" - ) - { - this.nativeId = nativeId; - this.name = name; - this.grade = grade; - materialType = type; - this.designCode = designCode; - this.codeYear = codeYear; - this.strength = strength; - this.elasticModulus = elasticModulus; - this.poissonsRatio = poissonsRatio; - this.shearModulus = shearModulus; - density = rho; - thermalExpansivity = alpha; - this.dampingRatio = dampingRatio; - this.cost = cost; - this.colour = colour; - } - - public int nativeId { get; set; } - public string colour { get; set; } -} diff --git a/src/Speckle.Objects/Structural/GSA/Materials/GSASteel.cs b/src/Speckle.Objects/Structural/GSA/Materials/GSASteel.cs deleted file mode 100644 index 5e487338..00000000 --- a/src/Speckle.Objects/Structural/GSA/Materials/GSASteel.cs +++ /dev/null @@ -1,65 +0,0 @@ -using Speckle.Objects.Structural.Materials; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Materials; - -[SpeckleType("Objects.Structural.GSA.Materials.GSASteel")] -public class GSASteel : Steel -{ - public GSASteel() { } - - [SchemaInfo( - "Steel", - "Creates a Speckle structural material for steel (to be used in structural analysis models)", - "Structural", - "Materials" - )] - public GSASteel( - int nativeId, - string name, - string? grade = null, - string? designCode = null, - string? codeYear = null, - double elasticModulus = 0, - double yieldStrength = 0, - double ultimateStrength = 0, - double maxStrain = 0, - double poissonsRatio = 0, - double shearModulus = 0, - double density = 0, - double alpha = 0, - double dampingRatio = 0, - double cost = 0, - string colour = "NO_RGB" - ) - { - this.nativeId = nativeId; - this.name = name; - this.grade = grade; - materialType = MaterialType.Concrete; - this.designCode = designCode; - this.codeYear = codeYear; - this.elasticModulus = elasticModulus; - this.yieldStrength = yieldStrength; - this.ultimateStrength = ultimateStrength; - this.maxStrain = maxStrain; - this.poissonsRatio = poissonsRatio; - this.shearModulus = shearModulus; - this.density = density; - this.dampingRatio = dampingRatio; - this.cost = cost; - this.colour = colour; - } - - public int nativeId { get; set; } - public string colour { get; set; } - - // FROM GWA - //public string Name { get => name; set { name = value; } } - //public GsaMat Mat; - //public double? Fy; - //public double? Fu; - //public double? EpsP; - //public double? Eh; // -} diff --git a/src/Speckle.Objects/Structural/GSA/Properties/GSAProperty1D.cs b/src/Speckle.Objects/Structural/GSA/Properties/GSAProperty1D.cs deleted file mode 100644 index 6271376b..00000000 --- a/src/Speckle.Objects/Structural/GSA/Properties/GSAProperty1D.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Speckle.Objects.Structural.Materials; -using Speckle.Objects.Structural.Properties; -using Speckle.Objects.Structural.Properties.Profiles; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Properties; - -[SpeckleType("Objects.Structural.GSA.Properties.GSAProperty1D")] -public class GSAProperty1D : Property1D -{ - public GSAProperty1D() { } - - [SchemaInfo("GSAProperty1D", "Creates a Speckle structural 1D element property for GSA", "GSA", "Properties")] - public GSAProperty1D( - int nativeId, - string name, - StructuralMaterial material, - SectionProfile profile, - double cost = 0, - double additionalMass = 0 - ) - { - this.nativeId = nativeId; - this.name = name; - this.material = material; - this.profile = profile; - this.cost = cost; - this.additionalMass = additionalMass; - } - - public int nativeId { get; set; } - - [DetachProperty] - public StructuralMaterial designMaterial { get; set; } - - public double additionalMass { get; set; } - public double? cost { get; set; } - public int? poolRef { get; set; } - public string colour { get; set; } -} diff --git a/src/Speckle.Objects/Structural/GSA/Properties/GSAProperty2D.cs b/src/Speckle.Objects/Structural/GSA/Properties/GSAProperty2D.cs deleted file mode 100644 index 6d8612ee..00000000 --- a/src/Speckle.Objects/Structural/GSA/Properties/GSAProperty2D.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Speckle.Objects.Structural.Materials; -using Speckle.Objects.Structural.Properties; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.GSA.Properties; - -[SpeckleType("Objects.Structural.GSA.Properties.Property2D")] -public class GSAProperty2D : Property2D -{ - public GSAProperty2D() { } - - [SchemaInfo("GSAProperty2D", "Creates a Speckle structural 2D element property for GSA", "GSA", "Properties")] - public GSAProperty2D(int nativeId, string name, StructuralMaterial material, double thickness) - { - this.nativeId = nativeId; - this.name = name; - this.material = material; - this.thickness = thickness; - } - - public int nativeId { get; set; } - - [DetachProperty] - public StructuralMaterial designMaterial { get; set; } - - public double cost { get; set; } - public double additionalMass { get; set; } - public string concreteSlabProp { get; set; } - public string colour { get; set; } -} diff --git a/src/Speckle.Objects/Structural/Geometry/Axis.cs b/src/Speckle.Objects/Structural/Geometry/Axis.cs deleted file mode 100644 index bfffd86d..00000000 --- a/src/Speckle.Objects/Structural/Geometry/Axis.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Geometry; - -[SpeckleType("Objects.Structural.Geometry.Axis")] -public class Axis : Base -{ - public Axis() { } - - [SchemaInfo("Axis", "Creates a Speckle structural axis (a user-defined axis)", "Structural", "Geometry")] - public Axis(string name, AxisType axisType = AxisType.Cartesian, Plane? definition = null) - { - this.name = name; - this.axisType = axisType; - this.definition = definition; - } - - public string name { get; set; } - public AxisType axisType { get; set; } - public Plane? definition { get; set; } -} diff --git a/src/Speckle.Objects/Structural/Geometry/Element1D.cs b/src/Speckle.Objects/Structural/Geometry/Element1D.cs deleted file mode 100644 index cc7abe9a..00000000 --- a/src/Speckle.Objects/Structural/Geometry/Element1D.cs +++ /dev/null @@ -1,133 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Structural.Properties; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Geometry; - -[SpeckleType("Objects.Structural.Geometry.Element1D")] -public class Element1D : Base, IDisplayValue> -{ - public Element1D() { } - - 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 }; - } - - 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)", - "Structural", - "Geometry" - )] - public Element1D( - Line baseLine, - Property1D property, - ElementType1D type, - string? name = null, - [SchemaParamInfo("If null, restraint condition defaults to unreleased (fully fixed translations and rotations)")] - Restraint? end1Releases = null, - [SchemaParamInfo("If null, restraint condition defaults to unreleased (fully fixed translations and rotations)")] - Restraint? end2Releases = null, - [SchemaParamInfo("If null, defaults to no offsets")] Vector? end1Offset = null, - [SchemaParamInfo("If null, defaults to no offsets")] Vector? end2Offset = null, - Plane? localAxis = null - ) - : this(baseLine, property, type, name, null, end1Releases, end2Releases, end1Offset, end2Offset, localAxis) { } - - [SchemaInfo( - "Element1D (from orientation node and angle)", - "Creates a Speckle structural 1D element (from orientation node and angle)", - "Structural", - "Geometry" - )] - public Element1D( - Line baseLine, - Property1D property, - ElementType1D type, - string? name = null, - [SchemaParamInfo("If null, restraint condition defaults to unreleased (fully fixed translations and rotations)")] - Restraint? end1Releases = null, - [SchemaParamInfo("If null, restraint condition defaults to unreleased (fully fixed translations and rotations)")] - Restraint? end2Releases = null, - [SchemaParamInfo("If null, defaults to no offsets")] Vector? end1Offset = null, - [SchemaParamInfo("If null, defaults to no offsets")] Vector? end2Offset = null, - Node? orientationNode = null, - double orientationAngle = 0 - ) - : this( - baseLine, - property, - type, - name, - null, - end1Releases, - end2Releases, - end1Offset, - end2Offset, - orientationNode: orientationNode, - orientationAngle: orientationAngle - ) { } - - #endregion -} diff --git a/src/Speckle.Objects/Structural/Geometry/Element2D.cs b/src/Speckle.Objects/Structural/Geometry/Element2D.cs deleted file mode 100644 index 467097a1..00000000 --- a/src/Speckle.Objects/Structural/Geometry/Element2D.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Structural.Properties; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Geometry; - -[SpeckleType("Objects.Structural.Geometry.Element2D")] -public class Element2D : Base, IDisplayValue> -{ - public Element2D() { } - - public Element2D(List nodes) - { - topology = nodes; - } - - [SchemaInfo( - "Element2D", - "Creates a Speckle structural 2D element (based on a list of edge ie. external, geometry defining nodes)", - "Structural", - "Geometry" - )] - public Element2D(List nodes, Property2D property, double offset = 0, double orientationAngle = 0) - { - topology = nodes; - this.property = property; - this.offset = offset; - this.orientationAngle = orientationAngle; - } - - public string name { get; set; } - - [DetachProperty] - public Property2D property { get; set; } - - public ElementType2D type { get; set; } - public double offset { get; set; } //z direction (normal) - public double orientationAngle { get; set; } - - [DetachProperty] - public Base parent { get; set; } //parent element - - [DetachProperty] - public List topology { get; set; } - public List openings { get; set; } - - public string units { get; set; } - - [DetachProperty] - public List displayValue { get; set; } -} diff --git a/src/Speckle.Objects/Structural/Geometry/Element3D.cs b/src/Speckle.Objects/Structural/Geometry/Element3D.cs deleted file mode 100644 index 4e82dcd7..00000000 --- a/src/Speckle.Objects/Structural/Geometry/Element3D.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Structural.Properties; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Geometry; - -[SpeckleType("Objects.Structural.Geometry.Element3D")] -public class Element3D : Base -{ - public Element3D() { } - - public Element3D(Mesh baseMesh) - { - this.baseMesh = baseMesh; - } - - [SchemaInfo("Element3D", "Creates a Speckle structural 3D element", "Structural", "Geometry")] - public Element3D( - Mesh baseMesh, - Property3D property, - ElementType3D type, - string? name = null, - double orientationAngle = 0 - ) - { - this.baseMesh = baseMesh; - this.property = property; - this.type = type; - this.name = name; - this.orientationAngle = orientationAngle; - } - - public string? name { get; set; } - public Mesh baseMesh { get; set; } //rhino - parent mesh? elements (including props/materias) explicitly defined in a list - - [DetachProperty] - public Property3D property { get; set; } - - public ElementType3D type { get; set; } - public double orientationAngle { get; set; } - - [DetachProperty] - public Base parent { get; set; } //parent element - - [DetachProperty] - public List topology { get; set; } - - public string units { get; set; } -} diff --git a/src/Speckle.Objects/Structural/Geometry/ElementType.cs b/src/Speckle.Objects/Structural/Geometry/ElementType.cs deleted file mode 100644 index 398ca288..00000000 --- a/src/Speckle.Objects/Structural/Geometry/ElementType.cs +++ /dev/null @@ -1,35 +0,0 @@ -namespace Speckle.Objects.Structural.Geometry; - -public enum ElementType1D -{ - Beam, - Brace, - Bar, - Column, - Rod, - Spring, - Tie, - Strut, - Link, - Damper, - Cable, - Spacer, - Other, - Null -} - -public enum ElementType2D -{ - Quad4, - Quad8, - Triangle3, - Triangle6 -} - -public enum ElementType3D -{ - Brick8, - Wedge6, - Pyramid5, - Tetra4 -} diff --git a/src/Speckle.Objects/Structural/Geometry/MemberType.cs b/src/Speckle.Objects/Structural/Geometry/MemberType.cs deleted file mode 100644 index 67898a06..00000000 --- a/src/Speckle.Objects/Structural/Geometry/MemberType.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Speckle.Objects.Structural.Geometry; - -public enum MemberType -{ - Beam, - Column, - Generic1D, - Slab, - Wall, - Generic2D, - VoidCutter1D, - VoidCutter2D -} diff --git a/src/Speckle.Objects/Structural/Geometry/MemberType1D.cs b/src/Speckle.Objects/Structural/Geometry/MemberType1D.cs deleted file mode 100644 index 2b824375..00000000 --- a/src/Speckle.Objects/Structural/Geometry/MemberType1D.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Speckle.Objects.Structural.Geometry; - -public enum MemberType1D -{ - Beam, - Column, - Generic1D -} - -public enum MemberType2D -{ - Slab, - Wall, - Generic2D, - VoidCutter1D, - VoidCutter2D -} diff --git a/src/Speckle.Objects/Structural/Geometry/Node.cs b/src/Speckle.Objects/Structural/Geometry/Node.cs deleted file mode 100644 index 359b4dc6..00000000 --- a/src/Speckle.Objects/Structural/Geometry/Node.cs +++ /dev/null @@ -1,80 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Objects.Structural.Properties; -using Speckle.Sdk.Common; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Geometry; - -[SpeckleType("Objects.Structural.Geometry.Node")] -public class Node : Base -{ - public Node() { } - - public Node(Point basePoint) - { - this.basePoint = basePoint; - } - - [SchemaInfo( - "Node with properties", - "Creates a Speckle structural node with spring, mass and/or damper properties", - "Structural", - "Geometry" - )] - public Node( - Point basePoint, - string? name = null, - [SchemaParamInfo("If null, restraint condition defaults to free/fully released")] Restraint? restraint = null, - [SchemaParamInfo( - "If null, axis defaults to world xy (z axis defines the vertical direction, positive direction is up)" - )] - Axis? constraintAxis = null, - PropertySpring? springProperty = null, - PropertyMass? massProperty = null, - PropertyDamper? damperProperty = null - ) - { - this.basePoint = basePoint; - this.name = name; - this.restraint = restraint ?? new Restraint("RRRRRR"); - this.constraintAxis = - constraintAxis - ?? new Axis( - "Global", - AxisType.Cartesian, - new Plane - { - origin = new Point(0, 0), - normal = new Vector(0, 0, 1), - xdir = new Vector(1, 0, 0), - ydir = new Vector(0, 1, 0), - units = Units.Meters, //Not sure if defaulting to meters is correct, but it was what we were doing previously inside Plane's ctor - } - ); - this.springProperty = springProperty; - this.massProperty = massProperty; - this.damperProperty = damperProperty; - } - - //public int nativeId { get; set; } //equivalent to num record in GWA keyword, can be used as a unique identifier for other software - public string? name { get; set; } - public Point basePoint { get; set; } - - [DetachProperty] - public Axis? constraintAxis { get; set; } // can be detachable? ex. a user-specified axis - - [DetachProperty] - public Restraint? restraint { get; set; } // can be detachable? ex. reuse pinned support condition - - [DetachProperty] - public PropertySpring? springProperty { get; set; } - - [DetachProperty] - public PropertyMass? massProperty { get; set; } - - [DetachProperty] - public PropertyDamper? damperProperty { get; set; } - - public string units { get; set; } = Units.None; -} diff --git a/src/Speckle.Objects/Structural/Geometry/Restraint.cs b/src/Speckle.Objects/Structural/Geometry/Restraint.cs deleted file mode 100644 index 580b2734..00000000 --- a/src/Speckle.Objects/Structural/Geometry/Restraint.cs +++ /dev/null @@ -1,89 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Geometry; - -[SpeckleType("Objects.Structural.Geometry.Restraint")] -public class Restraint : Base -{ - public Restraint() { } - - [SchemaInfo("Restraint (by code)", "Creates a Speckle restraint object", "Structural", "Geometry")] - public Restraint( - [SchemaParamInfo( - "A 6-character string to describe the restraint condition (F = Fixed, R = Released) for each degree of freedom - the first 3 characters represent translational degrees of freedom in the X, Y, and Z axes and the last 3 characters represent rotational degrees of freedom about the X, Y, and Z axes (ex. FFFRRR denotes a pinned condition, FFFFFF denotes a fixed condition)" - )] - string code - ) - { - this.code = code.ToUpper(); - } - - [SchemaInfo( - "Restraint (by code and stiffness)", - "Creates a Speckle restraint object (to describe support conditions with an explicit stiffness)", - "Structural", - "Geometry" - )] - public Restraint( - [SchemaParamInfo( - "A 6-character string to describe the restraint condition (F = Fixed, R = Released, K = Stiffness) for each degree of freedom - the first 3 characters represent translational degrees of freedom in the X, Y, and Z axes and the last 3 characters represent rotational degrees of freedom about the X, Y, and Z axes (ex. FFSRRR denotes fixed translation about the x and y axis, a spring stiffness for translation in the z axis and releases for all rotational degrees of freedom)" - )] - string code, - [SchemaParamInfo("Applies only if the restraint code character for translation in x is 'K'")] double stiffnessX = 0, - [SchemaParamInfo("Applies only if the restraint code character for translation in y is 'K'")] double stiffnessY = 0, - [SchemaParamInfo("Applies only if the restraint code character for translation in z is 'K'")] double stiffnessZ = 0, - [SchemaParamInfo("Applies only if the restraint code character for rotation about x is 'K'")] - double stiffnessXX = 0, - [SchemaParamInfo("Applies only if the restraint code character for rotation about y is 'K'")] - double stiffnessYY = 0, - [SchemaParamInfo("Applies only if the restraint code character for rotation about z is 'K'")] double stiffnessZZ = 0 - ) - { - this.code = code.ToUpper(); - this.stiffnessX = code[0] == 'K' || code[0] == 'k' ? stiffnessX : 0; - this.stiffnessY = code[1] == 'K' || code[1] == 'k' ? stiffnessY : 0; - this.stiffnessZ = code[2] == 'K' || code[2] == 'k' ? stiffnessZ : 0; - this.stiffnessXX = code[3] == 'K' || code[3] == 'k' ? stiffnessXX : 0; - this.stiffnessYY = code[4] == 'K' || code[4] == 'k' ? stiffnessYY : 0; - this.stiffnessZZ = code[5] == 'K' || code[5] == 'k' ? stiffnessZZ : 0; - } - - [SchemaInfo( - "Restraint (by enum)", - "Creates a Speckle restraint object (for pinned condition or fixed condition)", - "Structural", - "Geometry" - )] - public Restraint(RestraintType restraintType) - { - if (restraintType == RestraintType.Free) - { - code = "RRRRRR"; - } - - if (restraintType == RestraintType.Pinned) - { - code = "FFFRRR"; - } - - if (restraintType == RestraintType.Fixed) - { - code = "FFFFFF"; - } - - if (restraintType == RestraintType.Roller) - { - code = "RRFRRR"; - } - } - - public string code { get; set; } //a string to describe the restraint type for each degree of freedom - ex. FFFRRR (pin) / FFFFFF (fix) - public double stiffnessX { get; set; } - public double stiffnessY { get; set; } - public double stiffnessZ { get; set; } - public double stiffnessXX { get; set; } - public double stiffnessYY { get; set; } - public double stiffnessZZ { get; set; } - public string units { get; set; } -} diff --git a/src/Speckle.Objects/Structural/Geometry/RestraintType.cs b/src/Speckle.Objects/Structural/Geometry/RestraintType.cs deleted file mode 100644 index 9c7896d4..00000000 --- a/src/Speckle.Objects/Structural/Geometry/RestraintType.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Speckle.Objects.Structural.Geometry; - -public enum RestraintType -{ - Free, //Release - Pinned, - Fixed, - Roller - //Spring //flexible - //rigid, free, flexible, comp only, tens only, flex comp only, flex tens only, non lin <-- SAF - //free, fixed, fixed negative, fixed positive, spring, spring negative, spring positive, spring relative, spring relative neg, spring relative pos, non lin, friction, damped, gap <-- BHoM -} - -public enum RestraintDescription -{ - none, - all, - x, - y, - z, - xy, - xz, - yz -} diff --git a/src/Speckle.Objects/Structural/Geometry/Storey.cs b/src/Speckle.Objects/Structural/Geometry/Storey.cs deleted file mode 100644 index 67c1796b..00000000 --- a/src/Speckle.Objects/Structural/Geometry/Storey.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Speckle.Objects.BuiltElements; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Geometry; - -[SpeckleType("Objects.Structural.Geometry.Storey")] -public class Storey : Level // or inherit from Base? -{ - public Storey() { } - - /// - /// A storey in the structural model - /// - /// The name of the storey - /// The elevation of the storey (along the global z-axis, ie. storey exists in the global XY plane) - [SchemaInfo( - "Storey", - "Creates a Speckle structural storey (to describe floor levels/storeys in the structural model)", - "Structural", - "Geometry" - )] - public Storey(string name, double elevation) - { - this.name = name; - this.elevation = elevation; - } -} diff --git a/src/Speckle.Objects/Structural/Loading/Load.cs b/src/Speckle.Objects/Structural/Loading/Load.cs deleted file mode 100644 index 93f2e78d..00000000 --- a/src/Speckle.Objects/Structural/Loading/Load.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Loading; - -[SpeckleType("Objects.Structural.Loading.Load")] -public class Load : Base -{ - public Load() { } - - /// - /// A generalised structural load, described by a name and load case - /// - /// Name of the load - /// Load case specification for the load - public Load(string? name, LoadCase loadCase) - { - this.name = name; - this.loadCase = loadCase; - } - - public string? name { get; set; } - - [DetachProperty] - public LoadCase loadCase { get; set; } - - public string units { get; set; } -} diff --git a/src/Speckle.Objects/Structural/Loading/LoadBeam.cs b/src/Speckle.Objects/Structural/Loading/LoadBeam.cs deleted file mode 100644 index 30ce7f87..00000000 --- a/src/Speckle.Objects/Structural/Loading/LoadBeam.cs +++ /dev/null @@ -1,114 +0,0 @@ -using Speckle.Objects.Structural.Geometry; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Loading; - -[SpeckleType("Objects.Structural.Loading.LoadBeam")] -public class LoadBeam : Load -{ - public LoadBeam() { } - - /// - /// A beam load (for 1D elements) - /// - /// The load case in which the load applies - /// A list of 1D elements to apply the load to - /// The type of loading applied - /// The direction of the load, with respect to the specified axis - /// The axis in which the direction of the load is defined - /// The magnitude of the load, either a force or moment - /// The locations of the load - /// Whether the load is projected (ie. whether the distributed load is specified as the intensity applied to the projection of the element on the surface normal to the direction of the load, like snow in an inclined roof) - /// A name or description to identify the load - [SchemaInfo("Beam Load", "Creates a Speckle structural beam (1D elem/member) load", "Structural", "Loading")] - public LoadBeam( - LoadCase loadCase, - List elements, - BeamLoadType loadType, - LoadDirection direction, - LoadAxisType loadAxis = LoadAxisType.Global, - [SchemaParamInfo( - "A list that represents load magnitude (number of values varies based on load type - Point: 1, Uniform: 1, Linear: 2, Patch: 2, Tri-linear:2)" - )] - List? values = null, - [SchemaParamInfo( - "A list that represents load locations (number of values varies based on load type - Point: 1, Uniform: null, Linear: null, Patch: 2, Tri-linear: 2)" - )] - List? positions = null, - bool isProjected = false, - string? name = null - ) - { - this.loadCase = loadCase; - this.elements = elements; - this.loadType = loadType; - this.direction = direction; - loadAxisType = loadAxis; - this.values = values; - this.positions = positions; - this.isProjected = isProjected; - this.name = name; - } - - /// - /// A beam load (for 1D elements) with a user-defined axis - /// - /// The load case in which the load applies - /// A list of 1D elements to apply the load to - /// The type of loading applied - /// The direction of the load, with respect to the specified axis - /// The axis in which the direction of the load is defined (can be a user-defined axis) - /// The magnitude of the load, either a force or moment - /// The locations of the load - /// Whether the load is projected (ie. whether the distributed load is specified as the intensity applied to the projection of the element on the surface normal to the direction of the load, like snow in an inclined roof) - /// A name or description to identify the load - [SchemaInfo( - "Beam Load (user-defined axis)", - "Creates a Speckle structural beam (1D elem/member) load (specified using a user-defined axis)", - "Structural", - "Loading" - )] - public LoadBeam( - LoadCase loadCase, - List elements, - BeamLoadType loadType, - LoadDirection direction, - Axis loadAxis, - [SchemaParamInfo( - "A list that represents load magnitude (number of values varies based on load type - Point: 1, Uniform: 1, Linear: 2, Patch: 2, Tri-linear:2)" - )] - List? values = null, - [SchemaParamInfo( - "A list that represents load locations (number of values varies based on load type - Point: 1, Uniform: null, Linear: null, Patch: 2, Tri-linear: 2)" - )] - List? positions = null, - bool isProjected = false, - string? name = null - ) - { - this.loadCase = loadCase; - this.elements = elements; - this.loadType = loadType; - this.direction = direction; - this.loadAxis = loadAxis; - this.values = values; - this.positions = positions; - this.isProjected = isProjected; - this.name = name; - } - - [DetachProperty, Chunkable(5000)] - public List elements { get; set; } - - public BeamLoadType loadType { get; set; } - public LoadDirection direction { get; set; } - - [DetachProperty] - public Axis loadAxis { get; set; } - - public LoadAxisType loadAxisType { get; set; } - public bool isProjected { get; set; } - public List? values { get; set; } - public List? positions { get; set; } -} diff --git a/src/Speckle.Objects/Structural/Loading/LoadCase.cs b/src/Speckle.Objects/Structural/Loading/LoadCase.cs deleted file mode 100644 index 26bea424..00000000 --- a/src/Speckle.Objects/Structural/Loading/LoadCase.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Loading; - -[SpeckleType("Objects.Structural.Loading.LoadCase")] -public class LoadCase : Base // or LoadPattern? (per CSI) -{ - public LoadCase() { } - - /// - /// A structural load case, a load case gives a way of grouping load effects together - /// - /// The name of the load case (the names of individual loads that are associated with the load case are defined elsewhere, in the loads themselves) - /// The type of the load case - /// A way of grouping load cases with the similar characteristics (ex. the source/mass source/origin of the loads) - /// The type of action of the load - /// A description of the load case - [SchemaInfo("Load Case", "Creates a Speckle structural load case", "Structural", "Loading")] - public LoadCase( - string name, - LoadType loadType, - string? group = null, - ActionType actionType = ActionType.None, - string? description = null - ) - { - this.name = name; - this.loadType = loadType; - this.group = group; - this.actionType = actionType; - this.description = description ?? ""; - } - - public string name { get; set; } //load case title, ex. "Dead load" - public LoadType loadType { get; set; } //ex. Dead load - public string? group { get; set; } //or load group, "A" - public ActionType actionType { get; set; } //ex. Permanent - public string description { get; set; } = ""; //category as alternative, ex. Offices – Cat.B, assembly area -} diff --git a/src/Speckle.Objects/Structural/Loading/LoadCombination.cs b/src/Speckle.Objects/Structural/Loading/LoadCombination.cs deleted file mode 100644 index fcd927b6..00000000 --- a/src/Speckle.Objects/Structural/Loading/LoadCombination.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Loading; - -[SpeckleType("Objects.Structural.Loading.LoadCombination")] -public class LoadCombination : Base //combination case -{ - public LoadCombination() { } - - /// - /// - /// - /// - /// - /// - /// - [SchemaInfo("Load Combination", "Creates a Speckle load combination", "Structural", "Loading")] - public LoadCombination( - string name, - [SchemaParamInfo("A list of load cases")] List loadCases, - [SchemaParamInfo("A list of load factors (to be mapped to provided load cases)")] List loadFactors, - CombinationType combinationType - ) - { - if (loadCases.Count != loadFactors.Count) - { - throw new ArgumentException("Number of load cases provided does not match number of load factors provided"); - } - - this.name = name; - this.loadCases = loadCases; - this.loadFactors = loadFactors; - this.combinationType = combinationType; - } - - public string name { get; set; } - - [DetachProperty] - public List loadCases { get; set; } - - public List loadFactors { get; set; } - public CombinationType combinationType { get; set; } -} diff --git a/src/Speckle.Objects/Structural/Loading/LoadFace.cs b/src/Speckle.Objects/Structural/Loading/LoadFace.cs deleted file mode 100644 index 469648f9..00000000 --- a/src/Speckle.Objects/Structural/Loading/LoadFace.cs +++ /dev/null @@ -1,114 +0,0 @@ -using Speckle.Objects.Structural.Geometry; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Loading; - -[SpeckleType("Objects.Structural.Loading.LoadFace")] -public class LoadFace : Load -{ - public LoadFace() { } - - /// - /// A face load (for 2D elements) - /// - /// The load case in which the load applies - /// A list of 2D elements to apply the load to - /// The type of loading applied - /// The direction of the load, with respect to the specified axis - /// The axis in which the direction of the load is defined - /// The magnitude of the load, either a pressure or a force at the specified point - /// The locations of the load - /// Whether the load is projected (ie. whether the distributed load is specified as the intensity applied to the projection of the element on the surface normal to the direction of the load, like snow in an inclined roof) - /// A name or description to identify the load - [SchemaInfo("Face Load", "Creates a Speckle structural face (2D elem/member) load", "Structural", "Loading")] - public LoadFace( - LoadCase loadCase, - List elements, - FaceLoadType loadType, - LoadDirection2D direction, - LoadAxisType loadAxis = LoadAxisType.Global, - [SchemaParamInfo( - "A list that represents load magnitude (number of values varies based on load type - Uniform: 1, Variable: 4 (corner nodes), Point: 1)" - )] - List? values = null, - [SchemaParamInfo( - "A list that represents load locations (number of values varies based on load type - Uniform: null, Variable: null, Point: 2)" - )] - List? positions = null, - bool isProjected = false, - string? name = null - ) - { - this.loadCase = loadCase; - this.elements = elements; - this.loadType = loadType; - this.direction = direction; - loadAxisType = loadAxis; - this.values = values; - this.positions = positions; - this.isProjected = isProjected; - this.name = name; - } - - /// - /// A face load (for 2D elements) with a user-defined axis - /// - /// The load case in which the load applies - /// A list of 2D elements to apply the load to - /// The type of loading applied - /// The direction of the load, with respect to the specified axis - /// The axis in which the direction of the load is defined (can be a user-defined axis) - /// The magnitude of the load, either a pressure or a force at the specified point - /// The locations of the load - /// Whether the load is projected (ie. whether the distributed load is specified as the intensity applied to the projection of the element on the surface normal to the direction of the load, like snow in an inclined roof) - /// A name or description to identify the load - [SchemaInfo( - "Face Load (user-defined axis)", - "Creates a Speckle structural face (2D elem/member) load (specified using a user-defined axis)", - "Structural", - "Loading" - )] - public LoadFace( - LoadCase loadCase, - List elements, - FaceLoadType loadType, - LoadDirection2D direction, - Axis loadAxis, - [SchemaParamInfo( - "A list that represents load magnitude (number of values varies based on load type - Uniform: 1, Variable: 4 (corner nodes), Point: 1)" - )] - List? values = null, - [SchemaParamInfo( - "A list that represents load locations (number of values varies based on load type - Uniform: null, Variable: null, Point: 2)" - )] - List? positions = null, - bool isProjected = false, - string? name = null - ) - { - this.loadCase = loadCase; - this.elements = elements; - this.loadType = loadType; - this.direction = direction; - this.loadAxis = loadAxis; - this.values = values; - this.positions = positions; - this.isProjected = isProjected; - this.name = name; - } - - [DetachProperty, Chunkable(5000)] - public List elements { get; set; } - - public FaceLoadType loadType { get; set; } - public LoadDirection2D direction { get; set; } - - [DetachProperty] - public Axis loadAxis { get; set; } - - public LoadAxisType loadAxisType { get; set; } - public bool isProjected { get; set; } - public List? values { get; set; } - public List? positions { get; set; } -} diff --git a/src/Speckle.Objects/Structural/Loading/LoadGravity.cs b/src/Speckle.Objects/Structural/Loading/LoadGravity.cs deleted file mode 100644 index 17b7ed0a..00000000 --- a/src/Speckle.Objects/Structural/Loading/LoadGravity.cs +++ /dev/null @@ -1,88 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Loading; - -[SpeckleType("Objects.Structural.Loading.LoadGravity")] -public class LoadGravity : Load -{ - public LoadGravity() { } - - /// - /// A gravity load (applied to all elements) - /// - /// A name or description to identify the load - /// The load case in which the load applies - /// A list of factors that apply on the “magnitude" of gravity (in terms of g, accleration of gravity) in each of the global axis (x, y and z) directions. Ex. For a model with global z-axis vertically upwards, the gravity factors of (0, 0, −1) represent a normal vertical gravity load on the structure - [SchemaInfo( - "Gravity Load (all elements)", - "Creates a Speckle structural gravity load (applied to all nodes and elements)", - "Structural", - "Loading" - )] - public LoadGravity(LoadCase loadCase, Vector? gravityFactors = null, string? name = null) - { - this.loadCase = loadCase; - this.gravityFactors = gravityFactors ?? new Vector(0, 0, -1); - this.name = name; - } - - /// - /// A gravity load (applied to the specified elements) - /// - /// A name or description to identify the load - /// The load case in which the load applies - /// A list of elements to apply the load to - /// A list of factors that apply on the “magnitude" of gravity (in terms of g, accleration of gravity) in each of the global axis (x, y and z) directions. Ex. For a model with global z-axis vertically upwards, the gravity factors of (0, 0, −1) represent a normal vertical gravity load on the structure - [SchemaInfo( - "Gravity Load (specified elements)", - "Creates a Speckle structural gravity load (applied to specified elements)", - "Structural", - "Loading" - )] - public LoadGravity(LoadCase loadCase, List elements, Vector? gravityFactors = null, string? name = null) - { - this.elements = elements; - this.loadCase = loadCase; - this.gravityFactors = gravityFactors ?? new Vector(0, 0, -1); - this.name = name; - } - - /// - /// A gravity load (applied to the specified elements and nodes) - /// - /// A name or description to identify the load - /// The load case in which the load applies - /// A list of elements to apply the load to - /// A list of nodes to apply the load to - /// A list of factors that apply on the “magnitude" of gravity (in terms of g, accleration of gravity) in each of the global axis (x, y and z) directions. Ex. For a model with global z-axis vertically upwards, the gravity factors of (0, 0, −1) represent a normal vertical gravity load on the structure - [SchemaInfo( - "Gravity Load (specified elements and nodes)", - "Creates a Speckle structural gravity load (applied to specified nodes and elements)", - "Structural", - "Loading" - )] - public LoadGravity( - LoadCase loadCase, - List elements, - List nodes, - Vector? gravityFactors = null, - string? name = null - ) - { - this.elements = elements; - this.nodes = nodes; - this.loadCase = loadCase; - this.gravityFactors = gravityFactors ?? new Vector(0, 0, -1); - this.name = name; - } - - [DetachProperty, Chunkable(5000)] - public List elements { get; set; } - - [DetachProperty, Chunkable(5000)] - public List nodes { get; set; } - - public Vector gravityFactors { get; set; } // a normal vertical gravity load is Z = -1 -} diff --git a/src/Speckle.Objects/Structural/Loading/LoadNode.cs b/src/Speckle.Objects/Structural/Loading/LoadNode.cs deleted file mode 100644 index 8169ff8c..00000000 --- a/src/Speckle.Objects/Structural/Loading/LoadNode.cs +++ /dev/null @@ -1,70 +0,0 @@ -using Speckle.Objects.Structural.Geometry; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Loading; - -[SpeckleType("Objects.Structural.Loading.LoadNode")] -public class LoadNode : Load -{ - public LoadNode() { } - - /// - /// A node load (applied in the global axis) - /// - /// The load case in which the load applies - /// >A list of nodes to apply the load to - /// The direction of the loading, relative to the specified axis - /// The magnitude of the load, either a force or moment - /// A name or description to identify the load/// - [SchemaInfo("Node Load", "Creates a Speckle node load", "Structural", "Loading")] - public LoadNode(LoadCase loadCase, List nodes, LoadDirection direction, double value, string? name = null) - { - this.name = name; - this.loadCase = loadCase; - this.nodes = nodes; - this.direction = direction; - this.value = value; - } - - /// - /// A node load (based on a user-defined axis) - /// - /// The load case in which the load applies - /// >A list of nodes to apply the load to - /// The axis in which the load is applied - /// The direction of the loading, relative to the specified axis - /// The magnitude of the load, either a force or moment - /// A name or description to identify the load/// - [SchemaInfo( - "Node Load (user-defined axis)", - "Creates a Speckle node load (specifed using a user-defined axis)", - "Structural", - "Loading" - )] - public LoadNode( - LoadCase loadCase, - List nodes, - Axis loadAxis, - LoadDirection direction, - double value, - string? name = null - ) - { - this.name = name; - this.loadCase = loadCase; - this.nodes = nodes; - this.loadAxis = loadAxis; - this.direction = direction; - this.value = value; - } - - [DetachProperty, Chunkable(5000)] - public List nodes { get; set; } - - [DetachProperty] - public Axis loadAxis { get; set; } - - public LoadDirection direction { get; set; } - public double value { get; set; } //a force or a moment, displacement (translation or rotation) and settlement to be covered in other classes -} diff --git a/src/Speckle.Objects/Structural/Loading/Loads.cs b/src/Speckle.Objects/Structural/Loading/Loads.cs deleted file mode 100644 index 718e1966..00000000 --- a/src/Speckle.Objects/Structural/Loading/Loads.cs +++ /dev/null @@ -1,74 +0,0 @@ -namespace Speckle.Objects.Structural.Loading; - -public enum LoadType -{ - None, - Dead, - SuperDead, - Soil, - Live, - LiveRoof, - ReducibleLive, - Wind, - Snow, - Rain, - Thermal, - Notional, - Prestress, - Equivalent, - Accidental, - SeismicRSA, - SeismicAccTorsion, - SeismicStatic, - Other -} - -public enum ActionType -{ - None, - Permanent, - Variable, - Accidental -} - -public enum BeamLoadType -{ - Point, - Uniform, - Linear, - Patch, - TriLinear -} - -public enum FaceLoadType -{ - Constant, - Variable, - Point -} - -public enum LoadDirection2D -{ - X, - Y, - Z -} - -public enum LoadDirection -{ - X, - Y, - Z, - XX, - YY, - ZZ -} - -public enum CombinationType -{ - LinearAdd, - Envelope, - AbsoluteAdd, - SRSS, - RangeAdd // what's this? -} diff --git a/src/Speckle.Objects/Structural/MaterialType.cs b/src/Speckle.Objects/Structural/MaterialType.cs deleted file mode 100644 index 82dcf457..00000000 --- a/src/Speckle.Objects/Structural/MaterialType.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Speckle.Objects.Structural; - -public enum MaterialType -{ - Concrete, - Steel, - Timber, - Aluminium, - Masonry, - FRP, - Glass, - Fabric, - Rebar, - Tendon, - ColdFormed, - Other -} diff --git a/src/Speckle.Objects/Structural/Materials/Concrete.cs b/src/Speckle.Objects/Structural/Materials/Concrete.cs deleted file mode 100644 index 99dcc323..00000000 --- a/src/Speckle.Objects/Structural/Materials/Concrete.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Materials; - -[SpeckleType("Objects.Structural.Materials.Concrete")] -public class Concrete : StructuralMaterial -{ - public Concrete() { } - - [SchemaInfo( - "Concrete", - "Creates a Speckle structural material for concrete (to be used in structural analysis models)", - "Structural", - "Materials" - )] - public Concrete( - string name, - string? grade = null, - string? designCode = null, - string? codeYear = null, - double elasticModulus = 0, - double compressiveStrength = 0, - double tensileStrength = 0, - double flexuralStrength = 0, - double maxCompressiveStrain = 0, - double maxTensileStrain = 0, - double maxAggregateSize = 0, - bool lightweight = false, - double poissonsRatio = 0, - double shearModulus = 0, - double density = 0, - double thermalExpansivity = 0, - double dampingRatio = 0 - ) - { - this.name = name; - this.grade = grade; - materialType = MaterialType.Concrete; - this.designCode = designCode; - this.codeYear = codeYear; - this.elasticModulus = elasticModulus; - this.compressiveStrength = compressiveStrength; - this.tensileStrength = tensileStrength; - this.flexuralStrength = flexuralStrength; - this.maxCompressiveStrain = maxCompressiveStrain; - this.maxTensileStrain = maxTensileStrain; - this.maxAggregateSize = maxAggregateSize; - this.lightweight = lightweight; - this.poissonsRatio = poissonsRatio; - this.shearModulus = shearModulus; - this.density = density; - this.thermalExpansivity = thermalExpansivity; - this.dampingRatio = dampingRatio; - } - - public double compressiveStrength { get; set; } //forgo using "strength" property in Material class - public double tensileStrength { get; set; } //design calc impacts - public double flexuralStrength { get; set; } //design calc impacts - public double maxCompressiveStrain { get; set; } //failure strain - public double maxTensileStrain { get; set; } - public double maxAggregateSize { get; set; } - public bool lightweight { get; set; } //whether or not it's a lightweight concrete -} diff --git a/src/Speckle.Objects/Structural/Materials/Steel.cs b/src/Speckle.Objects/Structural/Materials/Steel.cs deleted file mode 100644 index 687a7398..00000000 --- a/src/Speckle.Objects/Structural/Materials/Steel.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Materials; - -[SpeckleType("Objects.Structural.Materials.Steel")] -public class Steel : StructuralMaterial -{ - public Steel() { } - - [SchemaInfo( - "Steel", - "Creates a Speckle structural material for steel (to be used in structural analysis models)", - "Structural", - "Materials" - )] - public Steel( - string name, - string? grade = null, - string? designCode = null, - string? codeYear = null, - double elasticModulus = 0, - double yieldStrength = 0, - double ultimateStrength = 0, - double maxStrain = 0, - double poissonsRatio = 0, - double shearModulus = 0, - double density = 0, - double alpha = 0, - double dampingRatio = 0 - ) - { - this.name = name; - this.grade = grade; - materialType = MaterialType.Steel; - this.designCode = designCode; - this.codeYear = codeYear; - this.elasticModulus = elasticModulus; - this.yieldStrength = yieldStrength; - this.ultimateStrength = ultimateStrength; - this.maxStrain = maxStrain; - this.poissonsRatio = poissonsRatio; - this.shearModulus = shearModulus; - this.density = density; - this.dampingRatio = dampingRatio; - } - - public double yieldStrength { get; set; } //or yieldStress - public double ultimateStrength { get; set; } //ultimateStress - public double maxStrain { get; set; } //failureStrain - public double strainHardeningModulus { get; set; } -} diff --git a/src/Speckle.Objects/Structural/Materials/StructuralMaterial.cs b/src/Speckle.Objects/Structural/Materials/StructuralMaterial.cs deleted file mode 100644 index 4d5eed39..00000000 --- a/src/Speckle.Objects/Structural/Materials/StructuralMaterial.cs +++ /dev/null @@ -1,82 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Materials; - -[SpeckleType("Objects.Structural.Materials.StructuralMaterial")] -public class StructuralMaterial : Base -{ - // add carbon/environmental parameters? - - public StructuralMaterial() { } - - [SchemaInfo("Structural Material", "Creates a Speckle structural material", "Structural", "Materials")] - public StructuralMaterial( - string name, - MaterialType type, - string? grade = null, - string? designCode = null, - string? codeYear = null - ) - { - this.name = name; - materialType = type; - this.grade = grade; - this.designCode = designCode; - this.codeYear = codeYear; - } - - [SchemaInfo( - "Structural Material (with properties)", - "Creates a Speckle structural material with (isotropic) properties", - "Structural", - "Materials" - )] - public StructuralMaterial( - string name, - MaterialType type, - string? grade = null, - string? designCode = null, - string? codeYear = null, - double strength = 0, - double elasticModulus = 0, - double poissonsRatio = 0, - double shearModulus = 0, - double rho = 0, - double alpha = 0, - double dampingRatio = 0, - double materialSafetyFactor = 0, - double cost = 0 - ) - { - this.name = name; - this.grade = grade; - materialType = type; - this.designCode = designCode; - this.codeYear = codeYear; - this.strength = strength; - this.elasticModulus = elasticModulus; - this.poissonsRatio = poissonsRatio; - this.shearModulus = shearModulus; - density = rho; - thermalExpansivity = alpha; - this.dampingRatio = dampingRatio; - this.materialSafetyFactor = materialSafetyFactor; - this.cost = cost; - } - - public string name { get; set; } - public string? grade { get; set; } //ex. 350W(G40.21 Plate), could be set in name too - public MaterialType materialType { get; set; } - public string? designCode { get; set; } - public string? codeYear { get; set; } - public double strength { get; set; } - public double elasticModulus { get; set; } // E - public double poissonsRatio { get; set; } // nu - public double shearModulus { get; set; } // G - public double density { get; set; } // rho - public double thermalExpansivity { get; set; } // alpha, thermal coefficient of expansion - public double dampingRatio { get; set; } // zeta, material damping fraction - public double cost { get; set; } // material rate (ie. $/weight) - public double materialSafetyFactor { get; set; } //resistance factor -} diff --git a/src/Speckle.Objects/Structural/Materials/Timber.cs b/src/Speckle.Objects/Structural/Materials/Timber.cs deleted file mode 100644 index b2c8160e..00000000 --- a/src/Speckle.Objects/Structural/Materials/Timber.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Materials; - -[SpeckleType("Objects.Structural.Materials.Timber")] -public class Timber : StructuralMaterial -{ - public Timber() { } - - [SchemaInfo( - "Timber", - "Creates a Speckle structural material for timber (to be used in structural analysis models)", - "Structural", - "Materials" - )] - public Timber( - string name, - string? species = null, - string? grade = null, - string? designCode = null, - string? codeYear = null, - double strength = 0, - double elasticModulus = 0, - double poissonsRatio = 0, - double shearModulus = 0, - double density = 0, - double thermalExpansivity = 0, - double dampingRatio = 0 - ) - { - this.name = name; - this.grade = grade; - this.species = species; - materialType = MaterialType.Timber; - this.designCode = designCode; - this.codeYear = codeYear; - this.strength = strength; - this.elasticModulus = elasticModulus; - this.poissonsRatio = poissonsRatio; - this.shearModulus = shearModulus; - this.density = density; - this.thermalExpansivity = thermalExpansivity; - this.dampingRatio = dampingRatio; - } - - //missing timber-specific properties? parallel to grain, perpendicular to grain - public string? species { get; set; } -} diff --git a/src/Speckle.Objects/Structural/Properties/Profiles/SectionProfile.cs b/src/Speckle.Objects/Structural/Properties/Profiles/SectionProfile.cs deleted file mode 100644 index f6725606..00000000 --- a/src/Speckle.Objects/Structural/Properties/Profiles/SectionProfile.cs +++ /dev/null @@ -1,260 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Properties.Profiles; - -[SpeckleType("Objects.Structural.Properties.Profiles.SectionProfile")] -public class SectionProfile : Base //section profile description -{ - public SectionProfile() { } - - public SectionProfile( - string name, - ShapeType shapeType, - double area, - double Iyy, - double Izz, - double J, - double Ky, - double Kz, - double weight - ) - { - this.name = name; - this.shapeType = shapeType; - this.area = area; - this.Iyy = Izz; - this.Izz = Izz; - this.J = J; - this.Ky = Ky; - this.Kz = Kz; - this.weight = weight; - } - - public string name { get; set; } - public virtual ShapeType shapeType { get; set; } = ShapeType.Undefined; - - public string shapeName => shapeType.ToString(); - - public double area { get; set; } - - //Moment of inertia about the Major axis - public double Iyy { get; set; } - public double Izz { get; set; } - public double J { get; set; } - public double Ky { get; set; } - public double Kz { get; set; } - public double weight { get; set; } //section weight, ex. kg/m weight is defined in materials though more ? rather than section profile. - public string units { get; set; } -} - -[SpeckleType("Objects.Structural.Properties.Profiles.Rectangular")] -public class Rectangular : SectionProfile -{ - public Rectangular() { } - - [SchemaInfo( - "Rectangular", - "Creates a Speckle structural rectangular section profile", - "Structural", - "Section Profile" - )] - public Rectangular(string name, double depth, double width, double webThickness = 0, double flangeThickness = 0) - { - this.name = name; - this.depth = depth; - this.width = width; - this.webThickness = webThickness; - this.flangeThickness = flangeThickness; - } - - public double depth { get; set; } - public double width { get; set; } - public double webThickness { get; set; } // tw - public double flangeThickness { get; set; } // tf - public override ShapeType shapeType { get; set; } = ShapeType.Rectangular; -} - -[SpeckleType("Objects.Structural.Properties.Profiles.Circular")] -public class Circular : SectionProfile -{ - public Circular() { } - - [SchemaInfo("Circular", "Creates a Speckle structural circular section profile", "Structural", "Section Profile")] - public Circular(string name, double radius, double wallThickness = 0) - { - this.name = name; - this.radius = radius; - this.wallThickness = wallThickness; - } - - public double radius { get; set; } - public double wallThickness { get; set; } - public override ShapeType shapeType { get; set; } = ShapeType.Circular; -} - -[SpeckleType("Objects.Structural.Properties.Profiles.ISection")] -public class ISection : SectionProfile -{ - public ISection() { } - - [SchemaInfo("ISection", "Creates a Speckle structural I section profile", "Structural", "Section Profile")] - public ISection(string name, double depth, double width, double webThickness, double flangeThickness) - { - this.name = name; - this.depth = depth; - this.width = width; - this.webThickness = webThickness; - this.flangeThickness = flangeThickness; - } - - public double depth { get; set; } - public double width { get; set; } - public double webThickness { get; set; } - public double flangeThickness { get; set; } - public override ShapeType shapeType { get; set; } = ShapeType.I; -} - -[SpeckleType("Objects.Structural.Properties.Profiles.Tee")] -public class Tee : SectionProfile -{ - public Tee() { } - - [SchemaInfo("Tee", "Creates a Speckle structural Tee section profile", "Structural", "Section Profile")] - public Tee(string name, double depth, double width, double webThickness, double flangeThickness) - { - this.name = name; - this.depth = depth; - this.width = width; - this.webThickness = webThickness; - this.flangeThickness = flangeThickness; - } - - public double depth { get; set; } - public double width { get; set; } - public double webThickness { get; set; } - public double flangeThickness { get; set; } - public override ShapeType shapeType { get; set; } = ShapeType.Tee; -} - -[SpeckleType("Objects.Structural.Properties.Profiles.Angle")] -public class Angle : SectionProfile -{ - public Angle() { } - - [SchemaInfo("Angle", "Creates a Speckle structural angle section profile", "Structural", "Section Profile")] - public Angle(string name, double depth, double width, double webThickness, double flangeThickness) - { - this.name = name; - this.depth = depth; - this.width = width; - this.webThickness = webThickness; - this.flangeThickness = flangeThickness; - } - - public double depth { get; set; } - public double width { get; set; } - public double webThickness { get; set; } - public double flangeThickness { get; set; } - public override ShapeType shapeType { get; set; } = ShapeType.Angle; -} - -[SpeckleType("Objects.Structural.Properties.Profiles.Channel")] -public class Channel : SectionProfile -{ - public Channel() { } - - [SchemaInfo("Channel", "Creates a Speckle structural channel section profile", "Structural", "Section Profile")] - public Channel(string name, double depth, double width, double webThickness, double flangeThickness) - { - this.name = name; - this.depth = depth; - this.width = width; - this.webThickness = webThickness; - this.flangeThickness = flangeThickness; - } - - public double depth { get; set; } - public double width { get; set; } - public double webThickness { get; set; } - public double flangeThickness { get; set; } - public override ShapeType shapeType { get; set; } = ShapeType.Channel; -} - -[SpeckleType("Objects.Structural.Properties.Profiles.Perimeter")] -public class Perimeter : SectionProfile -{ - public Perimeter() { } - - [SchemaInfo( - "Perimeter", - "Creates a Speckle structural section profile defined by a perimeter curve and, if applicable, a list of void curves", - "Structural", - "Section Profile" - )] - public Perimeter(string name, ICurve outline, List? voids = null) - { - this.name = name; - this.outline = outline; - this.voids = voids ?? new(); - } - - public ICurve outline { get; set; } - public List voids { get; set; } = new(); -} - -[SpeckleType("Objects.Structural.Properties.Profiles.Catalogue")] -public class Catalogue : SectionProfile -{ - public Catalogue() { } - - [SchemaInfo( - "Catalogue (by description)", - "Creates a Speckle structural section profile based on a catalogue section description", - "Structural", - "Section Profile" - )] - public Catalogue(string description) - { - this.description = description; - } - - [SchemaInfo("Catalogue", "Creates a Speckle structural section profile", "Structural", "Section Profile")] - public Catalogue(string name, string catalogueName, string sectionType, string sectionName) - { - this.name = name; - this.catalogueName = catalogueName; - this.sectionType = sectionType; - this.sectionName = sectionName; - } - - public string description { get; set; } // a description string for a catalogue section, per a to be defined convention for industry-typical, commonly manufactured sections - SAF Formcodes, Oasys profiles? - public string catalogueName { get; set; } // ex. AISC, could be enum value - public string sectionType { get; set; } // ex. W shapes, could be enum value - public string sectionName { get; set; } // ex. W44x335, could be enum value -} - -[SpeckleType("Objects.Structural.Properties.Profiles.Explicit")] -public class Explicit : SectionProfile -{ - public Explicit() { } - - [SchemaInfo( - "Explicit", - "Creates a Speckle structural section profile based on explicitly defining geometric properties", - "Structural", - "Section Profile" - )] - public Explicit(string name, double area, double Iyy, double Izz, double J, double Ky, double Kz) - { - this.name = name; - this.area = area; - this.Iyy = Iyy; - this.Izz = Izz; - this.J = J; - this.Ky = Ky; - this.Kz = Kz; - } - - public override ShapeType shapeType { get; set; } = ShapeType.Explicit; -} diff --git a/src/Speckle.Objects/Structural/Properties/Property.cs b/src/Speckle.Objects/Structural/Properties/Property.cs deleted file mode 100644 index 4e56f0e9..00000000 --- a/src/Speckle.Objects/Structural/Properties/Property.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Properties; - -[SpeckleType("Objects.Structural.Properties.Property")] -public class Property : Base -{ - public Property() { } - - [SchemaInfo("Property", "Creates a Speckle structural property", "Structural", "Properties")] - public Property(string name) - { - this.name = name; - } - - public string name { get; set; } -} diff --git a/src/Speckle.Objects/Structural/Properties/Property1D.cs b/src/Speckle.Objects/Structural/Properties/Property1D.cs deleted file mode 100644 index 29bf1455..00000000 --- a/src/Speckle.Objects/Structural/Properties/Property1D.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Speckle.Objects.Structural.Geometry; -using Speckle.Objects.Structural.Materials; -using Speckle.Objects.Structural.Properties.Profiles; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Properties; - -[SpeckleType("Objects.Structural.Properties.Property1D")] -public class Property1D : Property //SectionProperty as alt class name -{ - public Property1D() { } - - [SchemaInfo("Property1D (by name)", "Creates a Speckle structural 1D element property", "Structural", "Properties")] - public Property1D(string name) - { - this.name = name; - } - - [SchemaInfo("Property1D", "Creates a Speckle structural 1D element property", "Structural", "Properties")] - public Property1D(string name, StructuralMaterial? material, SectionProfile profile) - { - this.name = name; - this.material = material; - this.profile = profile; - } - - public MemberType memberType { get; set; } - - [DetachProperty] - public StructuralMaterial? material { get; set; } - - [DetachProperty] - public SectionProfile profile { get; set; } //section description - - public BaseReferencePoint referencePoint { get; set; } - public double offsetY { get; set; } //offset from reference point - public double offsetZ { get; set; } //offset from reference point -} diff --git a/src/Speckle.Objects/Structural/Properties/Property2D.cs b/src/Speckle.Objects/Structural/Properties/Property2D.cs deleted file mode 100644 index 5c6e96e7..00000000 --- a/src/Speckle.Objects/Structural/Properties/Property2D.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Speckle.Objects.Structural.Geometry; -using Speckle.Objects.Structural.Materials; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Properties; - -[SpeckleType("Objects.Structural.Properties.Property2D")] -public class Property2D : Property -{ - public Property2D() { } - - [SchemaInfo("Property2D (by name)", "Creates a Speckle structural 2D element property", "Structural", "Properties")] - public Property2D(string name) - { - this.name = name; - } - - [SchemaInfo("Property2D", "Creates a Speckle structural 2D element property", "Structural", "Properties")] - public Property2D(string name, StructuralMaterial? material, PropertyType2D type, double thickness) - { - this.name = name; - this.material = material; - this.type = type; - this.thickness = thickness; - } - - public PropertyType2D type { get; set; } - public double thickness { get; set; } //also thickness type? ex. waffle vs constant - - [DetachProperty] - public StructuralMaterial? material { get; set; } - - [DetachProperty] - public Axis orientationAxis { get; set; } - - public ReferenceSurface refSurface { get; set; } //system plane - public double zOffset { get; set; } //relative to reference surface - public double modifierInPlane { get; set; } - public double modifierBending { get; set; } - public double modifierShear { get; set; } - public double modifierVolume { get; set; } -} diff --git a/src/Speckle.Objects/Structural/Properties/Property3D.cs b/src/Speckle.Objects/Structural/Properties/Property3D.cs deleted file mode 100644 index 53534989..00000000 --- a/src/Speckle.Objects/Structural/Properties/Property3D.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Speckle.Objects.Structural.Geometry; -using Speckle.Objects.Structural.Materials; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Properties; - -[SpeckleType("Objects.Structural.Properties.Property3D")] -public class Property3D : Property -{ - public Property3D() { } - - [SchemaInfo("Property3D (by name)", "Creates a Speckle structural 3D element property", "Structural", "Properties")] - public Property3D(string name) - { - this.name = name; - } - - [SchemaInfo("Property3D", "Creates a Speckle structural 3D element property", "Structural", "Properties")] - public Property3D(string name, PropertyType3D type, StructuralMaterial material) - { - this.name = name; - this.type = type; - this.material = material; - } - - public PropertyType3D type { get; set; } - - [DetachProperty] - public StructuralMaterial material { get; set; } - - [DetachProperty] - public Axis orientationAxis { get; set; } -} diff --git a/src/Speckle.Objects/Structural/Properties/PropertyDamper.cs b/src/Speckle.Objects/Structural/Properties/PropertyDamper.cs deleted file mode 100644 index 41bee3b5..00000000 --- a/src/Speckle.Objects/Structural/Properties/PropertyDamper.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Properties; - -[SpeckleType("Objects.Structural.Properties.PropertyDamper")] -public class PropertyDamper : Property -{ - public PropertyDamper() { } - - [SchemaInfo("PropertyDamper", "Creates a Speckle structural damper property", "Structural", "Properties")] - public PropertyDamper(string name) - { - this.name = name; - } - - [SchemaInfo( - "PropertyDamper (general)", - "Creates a Speckle structural damper property (for 6 degrees of freedom)", - "Structural", - "Properties" - )] - public PropertyDamper( - string name, - PropertyTypeDamper damperType, - double dampingX = 0, - double dampingY = 0, - double dampingZ = 0, - double dampingXX = 0, - double dampingYY = 0, - double dampingZZ = 0 - ) - { - this.name = name; - this.damperType = damperType; - this.dampingX = dampingX; - this.dampingY = dampingY; - this.dampingZ = dampingZ; - this.dampingXX = dampingXX; - this.dampingYY = dampingYY; - this.dampingZZ = dampingZZ; - } - - public PropertyTypeDamper damperType { get; set; } - public double dampingX { get; set; } - public double dampingY { get; set; } - public double dampingZ { get; set; } - public double dampingXX { get; set; } - public double dampingYY { get; set; } - public double dampingZZ { get; set; } -} diff --git a/src/Speckle.Objects/Structural/Properties/PropertyMass.cs b/src/Speckle.Objects/Structural/Properties/PropertyMass.cs deleted file mode 100644 index fa617837..00000000 --- a/src/Speckle.Objects/Structural/Properties/PropertyMass.cs +++ /dev/null @@ -1,58 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Properties; - -[SpeckleType("Objects.Structural.Properties.PropertyMass")] -public class PropertyMass : Property // nodal constraint axis of the node assumed to be mass property axis -{ - public PropertyMass() { } - - [SchemaInfo("PropertyMass", "Creates a Speckle structural mass property", "Structural", "Properties")] - public PropertyMass(string name) - { - this.name = name; - } - - [SchemaInfo("PropertyMass (general)", "Creates a Speckle structural mass property", "Structural", "Properties")] - public PropertyMass( - string name, - double mass, - double inertiaXX = 0, - double inertiaYY = 0, - double inertiaZZ = 0, - double inertiaXY = 0, - double inertiaYZ = 0, - double inertiaZX = 0, - bool massModified = false, - double massModifierX = 0, - double massModifierY = 0, - double massModifierZ = 0 - ) - { - this.name = name; - this.mass = mass; - this.inertiaXX = inertiaXX; - this.inertiaYY = inertiaYY; - this.inertiaZZ = inertiaZZ; - this.inertiaXY = inertiaXY; - this.inertiaYZ = inertiaYZ; - this.inertiaZX = inertiaZX; - this.massModified = massModified; - this.massModifierX = massModifierX; - this.massModifierY = massModifierY; - this.massModifierZ = massModifierZ; - } - - public double mass { get; set; } - public double inertiaXX { get; set; } - public double inertiaYY { get; set; } - public double inertiaZZ { get; set; } - public double inertiaXY { get; set; } - public double inertiaYZ { get; set; } - public double inertiaZX { get; set; } - public bool massModified { get; set; } - public double massModifierX { get; set; } - public double massModifierY { get; set; } - public double massModifierZ { get; set; } -} diff --git a/src/Speckle.Objects/Structural/Properties/PropertySpring.cs b/src/Speckle.Objects/Structural/Properties/PropertySpring.cs deleted file mode 100644 index dae13533..00000000 --- a/src/Speckle.Objects/Structural/Properties/PropertySpring.cs +++ /dev/null @@ -1,109 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Properties; - -[SpeckleType("Objects.Structural.Properties.PropertySpring")] -public class PropertySpring : Property -{ - public PropertySpring() { } - - [SchemaInfo("PropertySpring", "Creates a Speckle structural spring property", "Structural", "Properties")] - public PropertySpring(string name) - { - this.name = name; - } - - [SchemaInfo( - "PropertySpring (linear/elastic)", - "Creates a Speckle structural spring property (linear/elastic spring)", - "Structural", - "Properties" - )] - public PropertySpring( - string name, - double stiffnessX = 0, - double stiffnessY = 0, - double stiffnessZ = 0, - double stiffnessXX = 0, - double stiffnessYY = 0, - double stiffnessZZ = 0, - double dampingRatio = 0 - ) - { - this.name = name; - springType = PropertyTypeSpring.General; - this.stiffnessX = stiffnessX; - this.stiffnessY = stiffnessY; - this.stiffnessZ = stiffnessZ; - this.stiffnessXX = stiffnessXX; - this.stiffnessYY = stiffnessYY; - this.stiffnessZZ = stiffnessZZ; - this.dampingRatio = dampingRatio; - } - - [SchemaInfo( - "PropertySpring (non-linear)", - "Creates a Speckle structural spring property (non-linear spring)", - "Structural", - "Properties" - )] - public PropertySpring( - string name, - double springCurveX = 0, - double stiffnessX = 0, - double springCurveY = 0, - double stiffnessY = 0, - double springCurveZ = 0, - double stiffnessZ = 0, - double springCurveXX = 0, - double stiffnessXX = 0, - double springCurveYY = 0, - double stiffnessYY = 0, - double springCurveZZ = 0, - double stiffnessZZ = 0, - double dampingRatio = 0 - ) - { - this.name = name; - springType = PropertyTypeSpring.General; - this.springCurveX = springCurveX; - this.springCurveY = springCurveY; - this.springCurveZ = springCurveZ; - this.springCurveXX = springCurveXX; - this.springCurveYY = springCurveYY; - this.springCurveZZ = springCurveZZ; - this.stiffnessX = springCurveX == 0 ? stiffnessX : 0; - this.stiffnessY = springCurveY == 0 ? stiffnessY : 0; - this.stiffnessZ = springCurveZ == 0 ? stiffnessZ : 0; - this.stiffnessXX = springCurveXX == 0 ? stiffnessXX : 0; - this.stiffnessYY = springCurveYY == 0 ? stiffnessYY : 0; - this.stiffnessZZ = springCurveZZ == 0 ? stiffnessZZ : 0; - this.dampingRatio = dampingRatio; - } - - public PropertyTypeSpring springType { get; set; } - public double springCurveX { get; set; } //if 0 spring is elastic, otherwise refers to a material curve by number - public double stiffnessX { get; set; } - public double springCurveY { get; set; } //if 0 spring is elastic, otherwise refers to a material curve by number - public double stiffnessY { get; set; } - public double springCurveZ { get; set; } //if 0 spring is elastic, otherwise refers to a material curve by number - public double stiffnessZ { get; set; } - public double springCurveXX { get; set; } //if 0 spring is elastic, otherwise refers to a material curve by number - public double stiffnessXX { get; set; } - public double springCurveYY { get; set; } //if 0 spring is elastic, otherwise refers to a material curve by number - public double stiffnessYY { get; set; } - public double springCurveZZ { get; set; } //if 0 spring is elastic, otherwise refers to a material curve by number - public double stiffnessZZ { get; set; } - public double dampingRatio { get; set; } - public double dampingX { get; set; } //is this needed? springType can't be set to DAMPER - public double dampingY { get; set; } //is this needed? springType can't be set to DAMPER - public double dampingZ { get; set; } //is this needed? springType can't be set to DAMPER - public double dampingXX { get; set; } //is this needed? springType can't be set to DAMPER - public double dampingYY { get; set; } //is this needed? springType can't be set to DAMPER - public double dampingZZ { get; set; } //is this needed? springType can't be set to DAMPER - public double matrix { get; set; } //refers to spring matrix record. - public double positiveLockup { get; set; } - public double negativeLockup { get; set; } - public double frictionCoefficient { get; set; } -} diff --git a/src/Speckle.Objects/Structural/PropertyType.cs b/src/Speckle.Objects/Structural/PropertyType.cs deleted file mode 100644 index ea3717d7..00000000 --- a/src/Speckle.Objects/Structural/PropertyType.cs +++ /dev/null @@ -1,79 +0,0 @@ -namespace Speckle.Objects.Structural; - -public enum PropertyType2D -{ - Stress, - Fabric, - Plate, - Shell, - Curved, - Wall, - Strain, - Axi, - Load -} - -public enum ReferenceSurface -{ - Top, - Middle, - Bottom - //TOP_CENTRE, CENTROID,BOT_CENTRE -} - -public enum PropertyType3D -{ - Solid, - Infinite -} - -public enum PropertyTypeSpring -{ - Axial, - Torsional, - General, - Matrix, - TensionOnly, - CompressionOnly, - Connector, - LockUp, - Gap, - Friction - //Translational, //old - //Rotational //old -} - -public enum PropertyTypeDamper -{ - Axial, //translational - Torsional, //rotational - General -} - -public enum BaseReferencePoint -{ - Centroid, - TopLeft, - TopCentre, - TopRight, - MidLeft, - MidRight, - BotLeft, - BotCentre, - BotRight -} - -public enum ShapeType -{ - Rectangular, - Circular, - I, - Tee, - Angle, - Channel, - Perimeter, - Box, - Catalogue, - Explicit, - Undefined -} diff --git a/src/Speckle.Objects/Structural/Results/AnalyticalResults.cs b/src/Speckle.Objects/Structural/Results/AnalyticalResults.cs deleted file mode 100644 index de667a46..00000000 --- a/src/Speckle.Objects/Structural/Results/AnalyticalResults.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Results; - -[SpeckleType("Objects.Structural.Results.AnalyticalResults")] -public class AnalyticalResults : Base -{ - public string? lengthUnits { get; set; } - public string? forceUnits { get; set; } - - [DetachProperty] - public List resultsByLoadCombination { get; set; } -} diff --git a/src/Speckle.Objects/Structural/Results/Result.cs b/src/Speckle.Objects/Structural/Results/Result.cs deleted file mode 100644 index 258f26d7..00000000 --- a/src/Speckle.Objects/Structural/Results/Result.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Speckle.Objects.Structural.Loading; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Results; - -[SpeckleType("Objects.Structural.Results.Result")] -public class Result : Base -{ - public Result() { } - - public Result(LoadCase resultCase, string? description = null) - { - this.resultCase = resultCase; - this.description = description ?? ""; - } - - public Result(LoadCombination resultCase, string? description = null) - { - this.resultCase = resultCase; - this.description = description ?? ""; - } - - [DetachProperty] - public Base resultCase { get; set; } //loadCase or loadCombination - - public string permutation { get; set; } //for enveloped cases? - public string description { get; set; } = ""; -} diff --git a/src/Speckle.Objects/Structural/Results/Result1D.cs b/src/Speckle.Objects/Structural/Results/Result1D.cs deleted file mode 100644 index 3534010e..00000000 --- a/src/Speckle.Objects/Structural/Results/Result1D.cs +++ /dev/null @@ -1,171 +0,0 @@ -using Speckle.Objects.Structural.Geometry; -using Speckle.Objects.Structural.Loading; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Results; - -[SpeckleType("Objects.Structural.Results.ResultSet1D")] -public class ResultSet1D : Result -{ - public ResultSet1D() { } - - [SchemaInfo("ResultSet1D", "Creates a Speckle 1D element result set object", "Structural", "Results")] - public ResultSet1D(List results1D) - { - this.results1D = results1D; - } - - [DetachProperty] - public List results1D { get; set; } -} - -[SpeckleType("Objects.Structural.Results.Result1D")] -public class Result1D : Result //result at a single position along a 1D element, ie. 1D element contains multiple Result1D objects to describe result at end 1, mid-span, end 2 -{ - public Result1D() { } - - [SchemaInfo( - "Result1D (load case)", - "Creates a Speckle 1D element result object (for load case)", - "Structural", - "Results" - )] - public Result1D( - Element1D element, - LoadCase resultCase, - float position, - float dispX, - float dispY, - float dispZ, - float rotXX, - float rotYY, - float rotZZ, - float forceX, - float forceY, - float forceZ, - float momentXX, - float momentYY, - float momentZZ, - float axialStress, - float shearStressY, - float shearStressZ, - float bendingStressYPos, - float bendingStressYNeg, - float bendingStressZPos, - float bendingStressZNeg, - float combinedStressMax, - float combinedStressMin - ) - { - this.element = element; - this.resultCase = resultCase; - this.position = position; - this.dispX = dispX; - this.dispY = dispY; - this.dispZ = dispZ; - this.rotXX = rotXX; - this.rotYY = rotYY; - this.rotZZ = rotZZ; - this.forceX = forceX; - this.forceY = forceY; - this.forceZ = forceZ; - this.momentXX = momentXX; - this.momentYY = momentYY; - this.momentZZ = momentZZ; - this.axialStress = axialStress; - this.shearStressY = shearStressY; - this.shearStressZ = shearStressZ; - this.bendingStressYPos = bendingStressYPos; - this.bendingStressYNeg = bendingStressYNeg; - this.bendingStressZPos = bendingStressZPos; - this.bendingStressZNeg = bendingStressZNeg; - this.combinedStressMax = combinedStressMax; - this.combinedStressMin = combinedStressMin; - } - - [SchemaInfo( - "Result1D (load combination)", - "Creates a Speckle 1D element result object (for load combination)", - "Structural", - "Results" - )] - public Result1D( - Element1D element, - LoadCombination resultCase, - float position, - float dispX, - float dispY, - float dispZ, - float rotXX, - float rotYY, - float rotZZ, - float forceX, - float forceY, - float forceZ, - float momentXX, - float momentYY, - float momentZZ, - float axialStress, - float shearStressY, - float shearStressZ, - float bendingStressYPos, - float bendingStressYNeg, - float bendingStressZPos, - float bendingStressZNeg, - float combinedStressMax, - float combinedStressMin - ) - { - this.element = element; - this.resultCase = resultCase; - this.position = position; - this.dispX = dispX; - this.dispY = dispY; - this.dispZ = dispZ; - this.rotXX = rotXX; - this.rotYY = rotYY; - this.rotZZ = rotZZ; - this.forceX = forceX; - this.forceY = forceY; - this.forceZ = forceZ; - this.momentXX = momentXX; - this.momentYY = momentYY; - this.momentZZ = momentZZ; - this.axialStress = axialStress; - this.shearStressY = shearStressY; - this.shearStressZ = shearStressZ; - this.bendingStressYPos = bendingStressYPos; - this.bendingStressYNeg = bendingStressYNeg; - this.bendingStressZPos = bendingStressZPos; - this.bendingStressZNeg = bendingStressZNeg; - this.combinedStressMax = combinedStressMax; - this.combinedStressMin = combinedStressMin; - } - - [DetachProperty] - public Element1D element { get; set; } - - public float? position { get; set; } //location along 1D element, normalised position (from 0 for end 1 to 1 for end 2) - public float? dispX { get; set; } - public float? dispY { get; set; } - public float? dispZ { get; set; } - public float? rotXX { get; set; } - public float? rotYY { get; set; } - public float? rotZZ { get; set; } - public float? forceX { get; set; } - public float? forceY { get; set; } - public float? forceZ { get; set; } - public float? momentXX { get; set; } - public float? momentYY { get; set; } - public float? momentZZ { get; set; } - public float? axialStress { get; set; } //axial stress, ie. Fx/Area - public float? shearStressY { get; set; } //shear stress, in minor axis dir, ie. Fy/Area - public float? shearStressZ { get; set; } //shear stress, in major axis dir, ie. Fz/Area - public float? bendingStressYPos { get; set; } //bending stress, about minor axis, ie. Myy/Iyy x Dz (Dz as distance from the centroid to the edge of the section in the +ve z direction) - public float? bendingStressYNeg { get; set; } //bending stress, about minor axis, ie. Myy/Iyy x Dz (Dz as distance from the centroid to the edge of the section in the -ve z direction) - public float? bendingStressZPos { get; set; } //bending stress, about major axis, ie. -Mzz/Izz x Dy (Dy as distance from the centroid to the edge of the section in the +ve y direction) - public float? bendingStressZNeg { get; set; } //bending stress, about major axis, ie. -Mzz/Izz x Dy (Dy as distance from the centroid to the edge of the section in the -ve y direction) - public float? combinedStressMax { get; set; } //maximum extreme fibre longitudinal stress due to axial forces and transverse bending - public float? combinedStressMin { get; set; } //minimum extreme fibre longitudinal stress due to axial forces and transverse bending -} diff --git a/src/Speckle.Objects/Structural/Results/Result2D.cs b/src/Speckle.Objects/Structural/Results/Result2D.cs deleted file mode 100644 index da2e4657..00000000 --- a/src/Speckle.Objects/Structural/Results/Result2D.cs +++ /dev/null @@ -1,211 +0,0 @@ -using Speckle.Objects.Structural.Geometry; -using Speckle.Objects.Structural.Loading; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Results; - -[SpeckleType("Objects.Structural.Results.ResultSet2D")] -public class ResultSet2D : Result -{ - public ResultSet2D() { } - - [SchemaInfo("ResultSet2D", "Creates a Speckle 2D element result set object", "Structural", "Results")] - public ResultSet2D(List results2D) - { - this.results2D = results2D; - } - - [DetachProperty] - public List results2D { get; set; } -} - -[SpeckleType("Objects.Structural.Results.Result2D")] -public class Result2D : Result //result at a single position within a 2D element, ie. 2D element contains multiple Result2D objects to describe result at node 1, node 2, node 3, node4 and centre of 4-node quad element -{ - public Result2D() { } - - [SchemaInfo( - "Result2D (load case)", - "Creates a Speckle 2D element result object (for load case)", - "Structural", - "Results" - )] - public Result2D( - Element2D element, - LoadCase resultCase, - List position, - float dispX, - float dispY, - float dispZ, - float forceXX, - float forceYY, - float forceXY, - float momentXX, - float momentYY, - float momentXY, - float shearX, - float shearY, - float stressTopXX, - float stressTopYY, - float stressTopZZ, - float stressTopXY, - float stressTopYZ, - float stressTopZX, - float stressMidXX, - float stressMidYY, - float stressMidZZ, - float stressMidXY, - float stressMidYZ, - float stressMidZX, - float stressBotXX, - float stressBotYY, - float stressBotZZ, - float stressBotXY, - float stressBotYZ, - float stressBotZX - ) - { - this.element = element; - this.resultCase = resultCase; - this.position = position; - this.dispX = dispX; - this.dispY = dispY; - this.dispZ = dispZ; - this.forceXX = forceXX; - this.forceYY = forceYY; - this.forceXY = forceXY; - this.momentXX = momentXX; - this.momentYY = momentYY; - this.momentXY = momentXY; - this.shearX = shearX; - this.shearY = shearY; - this.stressTopXX = stressTopXX; - this.stressTopYY = stressTopYY; - this.stressTopZZ = stressTopZZ; - this.stressTopXY = stressTopXY; - this.stressTopYZ = stressTopYZ; - this.stressTopZX = stressTopZX; - this.stressMidXX = stressMidXX; - this.stressMidYY = stressMidYY; - this.stressMidZZ = stressMidZZ; - this.stressMidXY = stressMidXY; - this.stressMidYZ = stressMidYZ; - this.stressMidZX = stressMidZX; - this.stressBotXX = stressBotXX; - this.stressBotYY = stressBotYY; - this.stressBotZZ = stressBotZZ; - this.stressBotXY = stressBotXY; - this.stressBotYZ = stressBotYZ; - this.stressBotZX = stressBotZX; - } - - [SchemaInfo( - "Result2D (load combination)", - "Creates a Speckle 2D element result object (for load combination)", - "Structural", - "Results" - )] - public Result2D( - Element2D element, - LoadCombination resultCase, - List position, - float dispX, - float dispY, - float dispZ, - float forceXX, - float forceYY, - float forceXY, - float momentXX, - float momentYY, - float momentXY, - float shearX, - float shearY, - float stressTopXX, - float stressTopYY, - float stressTopZZ, - float stressTopXY, - float stressTopYZ, - float stressTopZX, - float stressMidXX, - float stressMidYY, - float stressMidZZ, - float stressMidXY, - float stressMidYZ, - float stressMidZX, - float stressBotXX, - float stressBotYY, - float stressBotZZ, - float stressBotXY, - float stressBotYZ, - float stressBotZX - ) - { - this.element = element; - this.resultCase = resultCase; - this.position = position; - this.dispX = dispX; - this.dispY = dispY; - this.dispZ = dispZ; - this.forceXX = forceXX; - this.forceYY = forceYY; - this.forceXY = forceXY; - this.momentXX = momentXX; - this.momentYY = momentYY; - this.momentXY = momentXY; - this.shearX = shearX; - this.shearY = shearY; - this.stressTopXX = stressTopXX; - this.stressTopYY = stressTopYY; - this.stressTopZZ = stressTopZZ; - this.stressTopXY = stressTopXY; - this.stressTopYZ = stressTopYZ; - this.stressTopZX = stressTopZX; - this.stressMidXX = stressMidXX; - this.stressMidYY = stressMidYY; - this.stressMidZZ = stressMidZZ; - this.stressMidXY = stressMidXY; - this.stressMidYZ = stressMidYZ; - this.stressMidZX = stressMidZX; - this.stressBotXX = stressBotXX; - this.stressBotYY = stressBotYY; - this.stressBotZZ = stressBotZZ; - this.stressBotXY = stressBotXY; - this.stressBotYZ = stressBotYZ; - this.stressBotZX = stressBotZX; - } - - [DetachProperty] - public Element2D element { get; set; } - - public List position { get; set; } //relative position within element (x,y in range [0:1], { 0.5, 0.5 } corresponds to centre of element, { 0, 0 } correponds to corner/at a node of a element - public float? dispX { get; set; } - public float? dispY { get; set; } - public float? dispZ { get; set; } - public float? forceXX { get; set; } //in-plane force per unit length in x direction - public float? forceYY { get; set; } //in-plane force per unit length in y direction - public float? forceXY { get; set; } //in-plane force per unit length in xy direction (at interface) - public float? momentXX { get; set; } //moment per unit length in x direction - public float? momentYY { get; set; } //moment per unit length in y direction - public float? momentXY { get; set; } //moment per unit length in xy direction - public float? shearX { get; set; } //through thickness shear force per unit length in x direction - public float? shearY { get; set; } //through thickness shear force per unit length in y direction - public float? stressTopXX { get; set; } //in-plane stress in x direction at top layer of element - public float? stressTopYY { get; set; } //in-plane stress in y direction at top layer of element - public float? stressTopZZ { get; set; } //in-plane stress in z direction (through thickness) at top layer of element - public float? stressTopXY { get; set; } //shear stress in xy direction at top layer of element - public float? stressTopYZ { get; set; } //shear stress in yz direction at top layer of element - public float? stressTopZX { get; set; } //shear stress in zx direction at top layer of element - public float? stressMidXX { get; set; } //in-plane stress in x direction at mid layer of element - public float? stressMidYY { get; set; } //in-plane stress in y direction at mid layer of element - public float? stressMidZZ { get; set; } //in-plane stress in z direction (through thickness) at mid layer of element - public float? stressMidXY { get; set; } //shear stress in xy direction at mid layer of element - public float? stressMidYZ { get; set; } //shear stress in yz direction at mid layer of element - public float? stressMidZX { get; set; } //shear stress in zx direction at mid layer of element - public float? stressBotXX { get; set; } //in-plane stress in x direction at bot layer of element - public float? stressBotYY { get; set; } //in-plane stress in y direction at bot layer of element - public float? stressBotZZ { get; set; } //in-plane stress in z direction (through thickness) at bot layer of element - public float? stressBotXY { get; set; } //shear stress in xy direction at bot layer of element - public float? stressBotYZ { get; set; } //shear stress in yz direction at bot layer of element - public float? stressBotZX { get; set; } //shear stress in zx direction at bot layer of element -} diff --git a/src/Speckle.Objects/Structural/Results/Result3D.cs b/src/Speckle.Objects/Structural/Results/Result3D.cs deleted file mode 100644 index 8e91193c..00000000 --- a/src/Speckle.Objects/Structural/Results/Result3D.cs +++ /dev/null @@ -1,111 +0,0 @@ -using Speckle.Objects.Structural.Geometry; -using Speckle.Objects.Structural.Loading; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Results; - -[SpeckleType("Objects.Structural.Results.ResultSet3D")] -public class ResultSet3D : Result -{ - public ResultSet3D() { } - - [SchemaInfo("ResultSet3D", "Creates a Speckle 3D element result set object", "Structural", "Results")] - public ResultSet3D(List results3D) - { - this.results3D = results3D; - } - - [DetachProperty] - public List results3D { get; set; } -} - -[SpeckleType("Objects.Structural.Results.Result3D")] -public class Result3D : Result -{ - public Result3D() { } - - [SchemaInfo( - "Result3D (load case)", - "Creates a Speckle 3D element result object (for load case)", - "Structural", - "Results" - )] - public Result3D( - Element3D element, - LoadCase resultCase, - List position, - float dispX, - float dispY, - float dispZ, - float stressXX, - float stressYY, - float stressZZ, - float stressXY, - float stressYZ, - float stressZX - ) - { - this.element = element; - this.resultCase = resultCase; - this.position = position; - this.dispX = dispX; - this.dispY = dispY; - this.dispZ = dispZ; - this.stressXX = stressXX; - this.stressYY = stressYY; - this.stressZZ = stressZZ; - this.stressXY = stressXY; - this.stressYZ = stressYZ; - this.stressZX = stressZX; - } - - [SchemaInfo( - "Result3D (load combination)", - "Creates a Speckle 3D element result object (for load combination)", - "Structural", - "Results" - )] - public Result3D( - Element3D element, - LoadCombination resultCase, - List position, - float dispX, - float dispY, - float dispZ, - float stressXX, - float stressYY, - float stressZZ, - float stressXY, - float stressYZ, - float stressZX - ) - { - this.element = element; - this.resultCase = resultCase; - this.position = position; - this.dispX = dispX; - this.dispY = dispY; - this.dispZ = dispZ; - this.stressXX = stressXX; - this.stressYY = stressYY; - this.stressZZ = stressZZ; - this.stressXY = stressXY; - this.stressYZ = stressYZ; - this.stressZX = stressZX; - } - - [DetachProperty] - public Element3D element { get; set; } - - public List position { get; set; } //relative position within element (x,y,z in range [0:1] to describe position) - public float? dispX { get; set; } - public float? dispY { get; set; } - public float? dispZ { get; set; } - public float? stressXX { get; set; } - public float? stressYY { get; set; } - public float? stressZZ { get; set; } - public float? stressXY { get; set; } - public float? stressYZ { get; set; } - public float? stressZX { get; set; } -} diff --git a/src/Speckle.Objects/Structural/Results/ResultAll.cs b/src/Speckle.Objects/Structural/Results/ResultAll.cs deleted file mode 100644 index 3dce49bc..00000000 --- a/src/Speckle.Objects/Structural/Results/ResultAll.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Results; - -[SpeckleType("Objects.Structural.Results.ResultSetAll")] -public class ResultSetAll : Base -{ - public ResultSetAll() { } - - [SchemaInfo( - "ResultSetAll", - "Creates a Speckle result set object for 1d element, 2d element, 3d element global and nodal results", - "Structural", - "Results" - )] - public ResultSetAll( - ResultSet1D? results1D, - ResultSet2D? results2D, - ResultSet3D? results3D, - ResultGlobal? resultsGlobal, - ResultSetNode? resultsNode - ) - { - this.results1D = results1D; - this.results2D = results2D; - this.results3D = results3D; - this.resultsGlobal = resultsGlobal; - this.resultsNode = resultsNode; - } - - [DetachProperty] - public ResultSet1D? results1D { get; set; } //1d element results - - [DetachProperty] - public ResultSet2D? results2D { get; set; } //2d elements results - - [DetachProperty] - public ResultSet3D? results3D { get; set; } //3d elements results - - [DetachProperty] - public ResultGlobal? resultsGlobal { get; set; } //global results - - [DetachProperty] - public ResultSetNode? resultsNode { get; set; } //nodal results -} diff --git a/src/Speckle.Objects/Structural/Results/ResultGlobal.cs b/src/Speckle.Objects/Structural/Results/ResultGlobal.cs deleted file mode 100644 index 7008c83d..00000000 --- a/src/Speckle.Objects/Structural/Results/ResultGlobal.cs +++ /dev/null @@ -1,157 +0,0 @@ -using Speckle.Objects.Structural.Analysis; -using Speckle.Objects.Structural.Loading; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Results; - -[SpeckleType("Objects.Structural.Results.ResultGlobal")] -public class ResultGlobal : Result -{ - public ResultGlobal() { } - - [SchemaInfo( - "ResultGlobal (load case)", - "Creates a Speckle global result object (for load case)", - "Structural", - "Results" - )] - public ResultGlobal( - LoadCase resultCase, - float loadX, - float loadY, - float loadZ, - float loadXX, - float loadYY, - float loadZZ, - float reactionX, - float reactionY, - float reactionZ, - float reactionXX, - float reactionYY, - float reactionZZ, - float mode, - float frequency, - float loadFactor, - float modalStiffness, - float modalGeoStiffness, - float effMassX, - float effMassY, - float effMassZ, - float effMassXX, - float effMassYY, - float effMassZZ - ) - { - this.resultCase = resultCase; - this.loadX = loadX; - this.loadY = loadY; - this.loadZ = loadZ; - this.loadXX = loadXX; - this.loadYY = loadYY; - this.loadZZ = loadZZ; - this.reactionX = reactionX; - this.reactionY = reactionY; - this.reactionZ = reactionZ; - this.reactionXX = reactionXX; - this.reactionYY = reactionYY; - this.reactionZZ = reactionZZ; - this.mode = mode; - this.frequency = frequency; - this.loadFactor = loadFactor; - this.modalStiffness = modalStiffness; - this.modalGeoStiffness = modalGeoStiffness; - this.effMassX = effMassX; - this.effMassY = effMassY; - this.effMassZ = effMassZ; - this.effMassXX = effMassXX; - this.effMassYY = effMassYY; - this.effMassZZ = effMassZZ; - } - - [SchemaInfo( - "ResultGlobal (load combination)", - "Creates a Speckle global result object (for load combination)", - "Structural", - "Results" - )] - public ResultGlobal( - LoadCombination resultCase, - float loadX, - float loadY, - float loadZ, - float loadXX, - float loadYY, - float loadZZ, - float reactionX, - float reactionY, - float reactionZ, - float reactionXX, - float reactionYY, - float reactionZZ, - float mode, - float frequency, - float loadFactor, - float modalStiffness, - float modalGeoStiffness, - float effMassX, - float effMassY, - float effMassZ, - float effMassXX, - float effMassYY, - float effMassZZ - ) - { - this.resultCase = resultCase; - this.loadX = loadX; - this.loadY = loadY; - this.loadZ = loadZ; - this.loadXX = loadXX; - this.loadYY = loadYY; - this.loadZZ = loadZZ; - this.reactionX = reactionX; - this.reactionY = reactionY; - this.reactionZ = reactionZ; - this.reactionXX = reactionXX; - this.reactionYY = reactionYY; - this.reactionZZ = reactionZZ; - this.mode = mode; - this.frequency = frequency; - this.loadFactor = loadFactor; - this.modalStiffness = modalStiffness; - this.modalGeoStiffness = modalGeoStiffness; - this.effMassX = effMassX; - this.effMassY = effMassY; - this.effMassZ = effMassZ; - this.effMassXX = effMassXX; - this.effMassYY = effMassYY; - this.effMassZZ = effMassZZ; - } - - [DetachProperty] - public Model model { get; set; } // this should be a model identifier instead - - public float? loadX { get; set; } - public float? loadY { get; set; } - public float? loadZ { get; set; } - public float? loadXX { get; set; } - public float? loadYY { get; set; } - public float? loadZZ { get; set; } - public float? reactionX { get; set; } - public float? reactionY { get; set; } - public float? reactionZ { get; set; } - public float? reactionXX { get; set; } - public float? reactionYY { get; set; } - public float? reactionZZ { get; set; } - public float? mode { get; set; } - public float? frequency { get; set; } - public float? loadFactor { get; set; } - public float? modalStiffness { get; set; } - public float? modalGeoStiffness { get; set; } - public float? effMassX { get; set; } - public float? effMassY { get; set; } - public float? effMassZ { get; set; } - public float? effMassXX { get; set; } - public float? effMassYY { get; set; } - public float? effMassZZ { get; set; } -} diff --git a/src/Speckle.Objects/Structural/Results/ResultNode.cs b/src/Speckle.Objects/Structural/Results/ResultNode.cs deleted file mode 100644 index 555666f3..00000000 --- a/src/Speckle.Objects/Structural/Results/ResultNode.cs +++ /dev/null @@ -1,212 +0,0 @@ -using Speckle.Objects.Structural.Geometry; -using Speckle.Objects.Structural.Loading; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Structural.Results; - -public enum CaseType -{ - Analysis, - Combination -} - -[SpeckleType("Objects.Structural.Results.ResultSetNode")] -public class ResultSetNode : Result -{ - public ResultSetNode() { } - - [SchemaInfo("ResultSetNode", "Creates a Speckle node result set object", "Structural", "Results")] - public ResultSetNode(List resultsNode) - { - this.resultsNode = resultsNode; - } - - [DetachProperty] - public List resultsNode { get; set; } -} - -[SpeckleType("Objects.Structural.Results.ResultNode")] -public class ResultNode : Result -{ - public ResultNode() { } - - [SchemaInfo("ResultNode (load case)", "Creates a Speckle structural nodal result object", "Structural", "Results")] - public ResultNode( - LoadCase resultCase, - Node node, - float dispX, - float dispY, - float dispZ, - float rotXX, - float rotYY, - float rotZZ, - float reactionX, - float reactionY, - float reactionZ, - float reactionXX, - float reactionYY, - float reactionZZ, - float constraintX, - float constraintY, - float constraintZ, - float constraintXX, - float constraintYY, - float constraintZZ, - float velX, - float velY, - float velZ, - float velXX, - float velYY, - float velZZ, - float accX, - float accY, - float accZ, - float accXX, - float accYY, - float accZZ - ) - { - this.resultCase = resultCase; - this.node = node; - this.dispX = dispX; - this.dispY = dispY; - this.dispZ = dispZ; - this.rotXX = rotXX; - this.rotYY = rotYY; - this.rotZZ = rotZZ; - this.reactionX = reactionX; - this.reactionY = reactionY; - this.reactionZ = reactionZ; - this.reactionXX = reactionXX; - this.reactionYY = reactionYY; - this.reactionZZ = reactionZZ; - this.constraintX = constraintX; - this.constraintY = constraintY; - this.constraintZ = constraintZ; - this.constraintXX = constraintXX; - this.constraintYY = constraintYY; - this.constraintZZ = constraintZZ; - this.velX = velX; - this.velY = velY; - this.velZ = velZ; - this.velXX = velXX; - this.velYY = velYY; - this.velZZ = velZZ; - this.accX = accX; - this.accY = accY; - this.accZ = accZ; - this.accXX = accXX; - this.accYY = accYY; - this.accZZ = accZZ; - } - - [SchemaInfo( - "ResultNode (load combination)", - "Creates a Speckle structural nodal result object", - "Structural", - "Results" - )] - public ResultNode( - LoadCombination resultCase, - Node node, - float dispX, - float dispY, - float dispZ, - float rotXX, - float rotYY, - float rotZZ, - float reactionX, - float reactionY, - float reactionZ, - float reactionXX, - float reactionYY, - float reactionZZ, - float constraintX, - float constraintY, - float constraintZ, - float constraintXX, - float constraintYY, - float constraintZZ, - float velX, - float velY, - float velZ, - float velXX, - float velYY, - float velZZ, - float accX, - float accY, - float accZ, - float accXX, - float accYY, - float accZZ - ) - { - this.resultCase = resultCase; - this.node = node; - this.dispX = dispX; - this.dispY = dispY; - this.dispZ = dispZ; - this.rotXX = rotXX; - this.rotYY = rotYY; - this.rotZZ = rotZZ; - this.reactionX = reactionX; - this.reactionY = reactionY; - this.reactionZ = reactionZ; - this.reactionXX = reactionXX; - this.reactionYY = reactionYY; - this.reactionZZ = reactionZZ; - this.constraintX = constraintX; - this.constraintY = constraintY; - this.constraintZ = constraintZ; - this.constraintXX = constraintXX; - this.constraintYY = constraintYY; - this.constraintZZ = constraintZZ; - this.velX = velX; - this.velY = velY; - this.velZ = velZ; - this.velXX = velXX; - this.velYY = velYY; - this.velZZ = velZZ; - this.accX = accX; - this.accY = accY; - this.accZ = accZ; - this.accXX = accXX; - this.accYY = accYY; - this.accZZ = accZZ; - } - - [DetachProperty] - public Node node { get; set; } - - public float? dispX { get; set; } - public float? dispY { get; set; } - public float? dispZ { get; set; } - public float? rotXX { get; set; } - public float? rotYY { get; set; } - public float? rotZZ { get; set; } - public float? reactionX { get; set; } - public float? reactionY { get; set; } - public float? reactionZ { get; set; } - public float? reactionXX { get; set; } - public float? reactionYY { get; set; } - public float? reactionZZ { get; set; } - public float? constraintX { get; set; } - public float? constraintY { get; set; } - public float? constraintZ { get; set; } - public float? constraintXX { get; set; } - public float? constraintYY { get; set; } - public float? constraintZZ { get; set; } - public float? velX { get; set; } - public float? velY { get; set; } - public float? velZ { get; set; } - public float? velXX { get; set; } - public float? velYY { get; set; } - public float? velZZ { get; set; } - public float? accX { get; set; } - public float? accY { get; set; } - public float? accZ { get; set; } - public float? accXX { get; set; } - public float? accYY { get; set; } - public float? accZZ { get; set; } -} diff --git a/src/Speckle.Objects/Utils/Parameters.cs b/src/Speckle.Objects/Utils/Parameters.cs deleted file mode 100644 index 47ade2df..00000000 --- a/src/Speckle.Objects/Utils/Parameters.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Speckle.Objects.BuiltElements.Revit; -using Speckle.Sdk.Models; - -namespace Speckle.Objects.Utils; - -public static class Parameters -{ - /// - /// Turns a List of Parameters into a Base so that it can be used with the Speckle parameters prop - /// - /// - /// - public static Base? ToBase(this List parameters) - { - if (parameters == null) - { - return null; - } - - var @base = new Base(); - - foreach (Parameter p in parameters) - { - //if an applicationId is defined (BuiltInName) use that as key, otherwise use the display name - var key = string.IsNullOrEmpty(p.applicationInternalName) ? p.name : p.applicationInternalName; - if (string.IsNullOrEmpty(key) || @base[key] != null) - { - continue; - } - - @base[key] = p; - } - - return @base; - } -} diff --git a/src/Speckle.Objects/packages.lock.json b/src/Speckle.Objects/packages.lock.json index fecf7c61..8a87b684 100644 --- a/src/Speckle.Objects/packages.lock.json +++ b/src/Speckle.Objects/packages.lock.json @@ -2,12 +2,6 @@ "version": 2, "dependencies": { ".NETStandard,Version=v2.0": { - "GitVersion.MsBuild": { - "type": "Direct", - "requested": "[5.12.0, )", - "resolved": "5.12.0", - "contentHash": "dJuigXycpJNOiLT9or7mkHSkGFHgGW3/p6cNNYEKZBa7Hhp1FdX/cvqYWWYhRLpfoZOedeA7aRbYiOB3vW/dvA==" - }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -29,9 +23,9 @@ }, "PolySharp": { "type": "Direct", - "requested": "[1.14.1, )", - "resolved": "1.14.1", - "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g==" }, "Speckle.InterfaceGenerator": { "type": "Direct", @@ -60,14 +54,6 @@ "resolved": "6.0.0", "contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==" }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==", - "dependencies": { - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, "Microsoft.Build.Tasks.Git": { "type": "Transitive", "resolved": "8.0.0", @@ -75,12 +61,60 @@ }, "Microsoft.Data.Sqlite.Core": { "type": "Transitive", - "resolved": "7.0.7", - "contentHash": "21FRzcJhaTrlv7kTrqr/ltFcSQM2TyuTTPhUcjO8H73od7Bb3QraNW90c7lUucNI/245XPkKZG4fp7/7OsKCSg==", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", "dependencies": { "SQLitePCLRaw.core": "2.1.4" } }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "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.NETCore.Platforms": { "type": "Transitive", "resolved": "1.1.0", @@ -128,23 +162,28 @@ }, "System.Buffers": { "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + "resolved": "4.4.0", + "contentHash": "AwarXzzoDwX6BgrhjoJsk6tUezZEozOT5Y9QKF94Gl4JK91I4PIIBkBco9068Y9/Dra8Dkbie99kXB8+1BaYKw==" + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==", + "resolved": "4.5.3", + "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==", "dependencies": { - "System.Buffers": "4.5.1", + "System.Buffers": "4.4.0", "System.Numerics.Vectors": "4.4.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" + "System.Runtime.CompilerServices.Unsafe": "4.5.2" } }, "System.Numerics.Vectors": { "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + "resolved": "4.4.0", + "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" }, "System.Reactive": { "type": "Transitive", @@ -166,8 +205,8 @@ }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" + "resolved": "4.5.3", + "contentHash": "3TIsJhD1EiiT0w2CcDMN/iSSwnNnsrnbzeVHSKkaEgV85txMprmuO+Yq2AdSbeVGcg28pdNDTPK87tJhX7VFHw==" }, "System.Runtime.InteropServices.WindowsRuntime": { "type": "Transitive", @@ -177,15 +216,6 @@ "System.Runtime": "4.3.0" } }, - "System.Text.Encodings.Web": { - "type": "Transitive", - "resolved": "5.0.1", - "contentHash": "KmJ+CJXizDofbq6mpqDoRRLcxgOd2z9X3XoFNULSbvbqVRZkFX3istvr+MUjL6Zw1RT+RNdoI4GYidIINtgvqQ==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Memory": "4.5.4" - } - }, "System.Threading.Tasks.Extensions": { "type": "Transitive", "resolved": "4.5.4", @@ -198,18 +228,260 @@ "type": "Project", "dependencies": { "GraphQL.Client": "[6.0.0, )", + "Microsoft.Bcl.AsyncInterfaces": "[5.0.0, )", "Microsoft.CSharp": "[4.7.0, )", - "Microsoft.Data.Sqlite": "[7.0.7, )", - "Polly": "[7.2.3, )", - "Polly.Contrib.WaitAndRetry": "[1.1.1, )", - "Polly.Extensions.Http": "[3.0.0, )", - "Speckle.DoubleNumerics": "[4.0.1, )", + "Microsoft.Data.Sqlite": "[7.0.5, )", + "Microsoft.Extensions.DependencyInjection.Abstractions": "[2.2.0, )", + "Microsoft.Extensions.Logging": "[2.2.0, )", + "Speckle.DoubleNumerics": "[4.1.0, )", "Speckle.Newtonsoft.Json": "[13.0.2, )", - "Speckle.Sdk.Logging": "[1.0.0, )", - "System.Text.Json": "[5.0.2, )" + "Speckle.Sdk.Dependencies": "[1.0.0, )" } }, - "speckle.sdk.logging": { + "speckle.sdk.dependencies": { + "type": "Project" + }, + "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.Bcl.AsyncInterfaces": { + "type": "CentralTransitive", + "requested": "[5.0.0, )", + "resolved": "5.0.0", + "contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.CSharp": { + "type": "CentralTransitive", + "requested": "[4.7.0, )", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "CentralTransitive", + "requested": "[7.0.5, )", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.Newtonsoft.Json": { + "type": "CentralTransitive", + "requested": "[13.0.2, )", + "resolved": "13.0.2", + "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" + } + }, + "net8.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" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "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.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "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.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "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.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw==" + }, + "speckle.sdk": { + "type": "Project", + "dependencies": { + "GraphQL.Client": "[6.0.0, )", + "Microsoft.CSharp": "[4.7.0, )", + "Microsoft.Data.Sqlite": "[7.0.5, )", + "Microsoft.Extensions.DependencyInjection.Abstractions": "[2.2.0, )", + "Microsoft.Extensions.Logging": "[2.2.0, )", + "Speckle.DoubleNumerics": "[4.1.0, )", + "Speckle.Newtonsoft.Json": "[13.0.2, )", + "Speckle.Sdk.Dependencies": "[1.0.0, )" + } + }, + "speckle.sdk.dependencies": { "type": "Project" }, "GraphQL.Client": { @@ -231,61 +503,43 @@ }, "Microsoft.Data.Sqlite": { "type": "CentralTransitive", - "requested": "[7.0.7, )", - "resolved": "7.0.7", - "contentHash": "tiNmV1oPy+Z2R7Wd0bPB/FxCr8B+/5q11OpDMG751GA/YuOL7MZrBFfzv5oFRlFe08K6sjrnbrauzzGIeNrzLQ==", + "requested": "[7.0.5, )", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", "dependencies": { - "Microsoft.Data.Sqlite.Core": "7.0.7", + "Microsoft.Data.Sqlite.Core": "7.0.5", "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" } }, - "Polly": { + "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "CentralTransitive", - "requested": "[7.2.3, )", - "resolved": "7.2.3", - "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" }, - "Polly.Contrib.WaitAndRetry": { + "Microsoft.Extensions.Logging": { "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==", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", "dependencies": { - "Polly": "7.1.0" + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" } }, "Speckle.DoubleNumerics": { "type": "CentralTransitive", - "requested": "[4.0.1, )", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" }, "Speckle.Newtonsoft.Json": { "type": "CentralTransitive", "requested": "[13.0.2, )", "resolved": "13.0.2", "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" - }, - "System.Text.Json": { - "type": "CentralTransitive", - "requested": "[5.0.2, )", - "resolved": "5.0.2", - "contentHash": "I47dVIGiV6SfAyppphxqupertT/5oZkYLDCX6vC3HpOI4ZLjyoKAreUoem2ie6G0RbRuFrlqz/PcTQjfb2DOfQ==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "5.0.0", - "System.Buffers": "4.5.1", - "System.Memory": "4.5.4", - "System.Numerics.Vectors": "4.5.0", - "System.Runtime.CompilerServices.Unsafe": "5.0.0", - "System.Text.Encodings.Web": "5.0.1", - "System.Threading.Tasks.Extensions": "4.5.4" - } } } } diff --git a/src/Speckle.Sdk.Dependencies/Collections.cs b/src/Speckle.Sdk.Dependencies/Collections.cs new file mode 100644 index 00000000..aa622c83 --- /dev/null +++ b/src/Speckle.Sdk.Dependencies/Collections.cs @@ -0,0 +1,25 @@ +using System.Collections.Frozen; + +namespace Speckle.Sdk.Dependencies; + +public static class Collections +{ +#if NET5_0_OR_GREATER + public static IReadOnlySet Freeze(this IEnumerable source) +#else + public static IReadOnlyCollection Freeze(this IEnumerable source) +#endif + { + return source.ToFrozenSet(); + } + + public static IReadOnlyDictionary Freeze( + this IEnumerable> source + ) + where TKey : notnull => source.ToFrozenDictionary(); +} + +public static class EnumerableExtensions +{ + public static IEnumerable RangeFrom(int from, int to) => Enumerable.Range(from, to - from + 1); +} diff --git a/src/Speckle.Sdk.Dependencies/GraphQLRetry.cs b/src/Speckle.Sdk.Dependencies/GraphQLRetry.cs new file mode 100644 index 00000000..13d60031 --- /dev/null +++ b/src/Speckle.Sdk.Dependencies/GraphQLRetry.cs @@ -0,0 +1,27 @@ +using Polly; +using Polly.Contrib.WaitAndRetry; + +namespace Speckle.Sdk.Dependencies; + +public static class GraphQLRetry +{ + public static async Task ExecuteAsync( + Func> func, + Action? onRetry = null + ) + where TInnerException : Exception + { + var delay = Backoff.DecorrelatedJitterBackoffV2(TimeSpan.FromSeconds(1), 5); + var graphqlRetry = Policy + .HandleInner() + .WaitAndRetryAsync( + delay, + (ex, timeout, _) => + { + onRetry?.Invoke(ex, timeout); + } + ); + + return await graphqlRetry.ExecuteAsync(func).ConfigureAwait(false); + } +} diff --git a/src/Speckle.Sdk.Logging/ISpeckleActivity.cs b/src/Speckle.Sdk.Dependencies/ISdkActivity.cs similarity index 73% rename from src/Speckle.Sdk.Logging/ISpeckleActivity.cs rename to src/Speckle.Sdk.Dependencies/ISdkActivity.cs index 3ce6004a..d832c769 100644 --- a/src/Speckle.Sdk.Logging/ISpeckleActivity.cs +++ b/src/Speckle.Sdk.Dependencies/ISdkActivity.cs @@ -1,14 +1,16 @@ namespace Speckle.Sdk.Logging; -public interface ISpeckleActivity : IDisposable +public interface ISdkActivity : IDisposable { void SetTag(string key, object? value); void RecordException(Exception e); string TraceId { get; } - void SetStatus(SpeckleActivityStatusCode code); + void SetStatus(SdkActivityStatusCode code); + + void InjectHeaders(Action header); } -public enum SpeckleActivityStatusCode +public enum SdkActivityStatusCode { /// Unset status code is the default value indicating the status code is not initialized. Unset, diff --git a/src/Speckle.Sdk.Dependencies/ISdkActivityFactory.cs b/src/Speckle.Sdk.Dependencies/ISdkActivityFactory.cs new file mode 100644 index 00000000..6db37c9a --- /dev/null +++ b/src/Speckle.Sdk.Dependencies/ISdkActivityFactory.cs @@ -0,0 +1,8 @@ +using System.Runtime.CompilerServices; + +namespace Speckle.Sdk.Logging; + +public interface ISdkActivityFactory : IDisposable +{ + ISdkActivity? Start(string? name = default, [CallerMemberName] string source = ""); +} diff --git a/src/Speckle.Sdk.Dependencies/Pool.cs b/src/Speckle.Sdk.Dependencies/Pool.cs new file mode 100644 index 00000000..99209aa2 --- /dev/null +++ b/src/Speckle.Sdk.Dependencies/Pool.cs @@ -0,0 +1,18 @@ +using Microsoft.Extensions.ObjectPool; + +namespace Speckle.Sdk.Dependencies; + +public class Pool + where T : class, new() +{ + private readonly ObjectPool _objectPool; + + internal Pool(IPooledObjectPolicy objectPolicy) + { + _objectPool = ObjectPool.Create(objectPolicy); + } + + public T Get() => _objectPool.Get(); + + public void Return(T obj) => _objectPool.Return(obj); +} diff --git a/src/Speckle.Sdk.Dependencies/Pools.cs b/src/Speckle.Sdk.Dependencies/Pools.cs new file mode 100644 index 00000000..734b7432 --- /dev/null +++ b/src/Speckle.Sdk.Dependencies/Pools.cs @@ -0,0 +1,70 @@ +using System.Collections.Concurrent; +using System.Text; +using Microsoft.Extensions.ObjectPool; + +namespace Speckle.Sdk.Dependencies; + +public static class Pools +{ + public const int DefaultCapacity = 50; + + public static Pool> ObjectDictionaries { get; } = new(new ObjectDictionaryPolicy()); + + private sealed class ObjectDictionaryPolicy : IPooledObjectPolicy> + { + public Dictionary Create() => new(50, StringComparer.OrdinalIgnoreCase); + + public bool Return(Dictionary obj) + { + obj.Clear(); + return true; + } + } + + public static Pool StringBuilders { get; } = + new(new StringBuilderPooledObjectPolicy() { MaximumRetainedCapacity = 100 * 1024 * 1024 }); + + private sealed class ObjectDictionaryPolicy : IPooledObjectPolicy> + where TKey : notnull + { + public Dictionary Create() => new(DefaultCapacity); + + public bool Return(Dictionary obj) + { + obj.Clear(); + return true; + } + } + + private sealed class ObjectConcurrentDictionaryPolicy + : IPooledObjectPolicy> + where TKey : notnull + { + public ConcurrentDictionary Create() => new(Environment.ProcessorCount, DefaultCapacity); + + public bool Return(ConcurrentDictionary obj) + { + obj.Clear(); + return true; + } + } + + private sealed class ObjectListPolicy : IPooledObjectPolicy> + { + public List Create() => new(DefaultCapacity); + + public bool Return(List obj) + { + obj.Clear(); + return true; + } + } + + public static Pool> CreateListPool() => new(new ObjectListPolicy()); + + public static Pool> CreateDictionaryPool() + where TKey : notnull => new(new ObjectDictionaryPolicy()); + + public static Pool> CreateConcurrentDictionaryPool() + where TKey : notnull => new(new ObjectConcurrentDictionaryPolicy()); +} diff --git a/src/Speckle.Sdk.Dependencies/Serialization/Batch.cs b/src/Speckle.Sdk.Dependencies/Serialization/Batch.cs new file mode 100644 index 00000000..b65a406b --- /dev/null +++ b/src/Speckle.Sdk.Dependencies/Serialization/Batch.cs @@ -0,0 +1,33 @@ +using System.Buffers; +using Speckle.Sdk.Dependencies; + +namespace Speckle.Sdk.Serialisation.V2.Send; + +public sealed class Batch : IMemoryOwner + where T : IHasByteSize +{ + private static readonly Pool> _pool = Pools.CreateListPool(); +#pragma warning disable IDE0032 + private readonly List _items = _pool.Get(); + private int _batchByteSize; +#pragma warning restore IDE0032 + + public void Add(T item) + { + _items.Add(item); + _batchByteSize += item.ByteSize; + } + + public void TrimExcess() + { + _items.TrimExcess(); + _batchByteSize = _items.Sum(x => x.ByteSize); + } + + public int BatchByteSize => _batchByteSize; + public List Items => _items; + + public void Dispose() => _pool.Return(_items); + + public Memory Memory => new(_items.ToArray()); +} diff --git a/src/Speckle.Sdk.Dependencies/Serialization/BatchExtensions.cs b/src/Speckle.Sdk.Dependencies/Serialization/BatchExtensions.cs new file mode 100644 index 00000000..6867204c --- /dev/null +++ b/src/Speckle.Sdk.Dependencies/Serialization/BatchExtensions.cs @@ -0,0 +1,34 @@ +using System.Buffers; + +namespace Speckle.Sdk.Serialisation.V2.Send; + +public static class BatchExtensions +{ + public static Batch CreateBatch() + where T : IHasByteSize => new(); + + public static void TrimBatch(ref IMemoryOwner batch, bool isVerifiedFull) + where T : IHasByteSize + { + if (!isVerifiedFull) + { + ((Batch)batch).TrimExcess(); + } + } + + public static void AddBatchItem(this IMemoryOwner batch, T item) + where T : IHasByteSize => ((Batch)batch).Add(item); + + public static int GetBatchSize(this IMemoryOwner batch, Action logAsWarning, int maxBatchSize) + where T : IHasByteSize + { + var currentSize = ((Batch)batch).BatchByteSize; + if (currentSize > maxBatchSize) + { + logAsWarning($"Batch size exceeded. Current size: {currentSize} bytes. Max size: {maxBatchSize} bytes."); + return maxBatchSize; + } + + return currentSize; + } +} diff --git a/src/Speckle.Sdk.Dependencies/Serialization/ChannelExtensions.cs b/src/Speckle.Sdk.Dependencies/Serialization/ChannelExtensions.cs new file mode 100644 index 00000000..f8f3524c --- /dev/null +++ b/src/Speckle.Sdk.Dependencies/Serialization/ChannelExtensions.cs @@ -0,0 +1,24 @@ +using System.Buffers; +using System.Threading.Channels; +using Open.ChannelExtensions; + +namespace Speckle.Sdk.Serialisation.V2.Send; + +public static class ChannelExtensions +{ + public static BatchingChannelReader> BatchByByteSize( + this ChannelReader source, + Action logAsWarning, + int batchSize, + bool singleReader = false, + bool allowSynchronousContinuations = false + ) + where T : IHasByteSize => + new SizeBatchingChannelReader( + source ?? throw new ArgumentNullException(nameof(source)), + logAsWarning, + batchSize, + singleReader, + allowSynchronousContinuations + ); +} diff --git a/src/Speckle.Sdk.Dependencies/Serialization/ChannelLoader.cs b/src/Speckle.Sdk.Dependencies/Serialization/ChannelLoader.cs new file mode 100644 index 00000000..a05b7d7f --- /dev/null +++ b/src/Speckle.Sdk.Dependencies/Serialization/ChannelLoader.cs @@ -0,0 +1,114 @@ +using System.Threading.Channels; +using Open.ChannelExtensions; + +namespace Speckle.Sdk.Dependencies.Serialization; + +public abstract class ChannelLoader(CancellationToken cancellationToken) +{ + private const int RECEIVE_CAPACITY = 5000; + + private const int HTTP_GET_CHUNK_SIZE = 500; + private const int MAX_PARALLELISM_HTTP = 4; + private static readonly TimeSpan HTTP_BATCH_TIMEOUT = TimeSpan.FromSeconds(2); + private const int MAX_SAVE_CACHE_BATCH = 500; + private const int MAX_SAVE_CACHE_PARALLELISM = 4; + + private readonly CancellationTokenSource _cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + + private readonly Channel _channel = Channel.CreateBounded( + new BoundedChannelOptions(RECEIVE_CAPACITY) + { + AllowSynchronousContinuations = true, + Capacity = RECEIVE_CAPACITY, + SingleWriter = false, + SingleReader = false, + FullMode = BoundedChannelFullMode.Wait, + }, + _ => throw new NotImplementedException("Dropping items not supported.") + ); + + protected async Task GetAndCache(IEnumerable allChildrenIds, int? maxParallelism = null) => + await _channel + .Source(allChildrenIds, _cts.Token) + .Pipe(maxParallelism ?? Environment.ProcessorCount, CheckCache, cancellationToken: _cts.Token) + .Filter(x => x is not null) + .Batch(HTTP_GET_CHUNK_SIZE) + .WithTimeout(HTTP_BATCH_TIMEOUT) + .PipeAsync( + maxParallelism ?? MAX_PARALLELISM_HTTP, + async x => await Download(x).ConfigureAwait(false), + -1, + false, + _cts.Token + ) + .Join() + .Batch(MAX_SAVE_CACHE_BATCH) + .WithTimeout(HTTP_BATCH_TIMEOUT) + .ReadAllConcurrently(maxParallelism ?? MAX_SAVE_CACHE_PARALLELISM, SaveToCache, _cts.Token) + .ContinueWith( + t => + { + Exception? ex = t.Exception; + if (ex is null && t.Status is TaskStatus.Canceled && !_cts.Token.IsCancellationRequested) + { + ex = new OperationCanceledException(); + } + + if (ex is not null) + { + RecordException(ex); + } + + _channel.Writer.TryComplete(ex); + }, + _cts.Token, + TaskContinuationOptions.ExecuteSynchronously, + TaskScheduler.Current + ) + .ConfigureAwait(false); + + public abstract string? CheckCache(string id); + + public async Task> Download(List ids) + { + try + { + return await DownloadInternal(ids).ConfigureAwait(false); + } +#pragma warning disable CA1031 + catch (Exception ex) +#pragma warning restore CA1031 + { + RecordException(ex); + return []; + } + } + + protected abstract Task> DownloadInternal(List batch); + + public void SaveToCache(List batch) + { + try + { + SaveToCacheInternal(batch); + } +#pragma warning disable CA1031 + catch (Exception ex) +#pragma warning restore CA1031 + { + RecordException(ex); + } + } + + protected abstract void SaveToCacheInternal(List batch); + + protected Exception? Exception { get; private set; } + + private void RecordException(Exception ex) + { + Exception = ex; + _channel.Writer.TryComplete(ex); + //cancel everything! + _cts.Cancel(); + } +} diff --git a/src/Speckle.Sdk.Dependencies/Serialization/ChannelSaver.cs b/src/Speckle.Sdk.Dependencies/Serialization/ChannelSaver.cs new file mode 100644 index 00000000..5bb65dba --- /dev/null +++ b/src/Speckle.Sdk.Dependencies/Serialization/ChannelSaver.cs @@ -0,0 +1,130 @@ +using System.Buffers; +using System.Threading.Channels; +using Open.ChannelExtensions; +using Speckle.Sdk.Serialisation.V2.Send; + +namespace Speckle.Sdk.Dependencies.Serialization; + +public abstract class ChannelSaver(Action logAsWarning, CancellationToken cancellationToken) + where T : IHasByteSize +{ + private const int SEND_CAPACITY = 500; + private const int HTTP_SEND_CHUNK_SIZE = 25_000_000; //bytes + private static readonly TimeSpan HTTP_BATCH_TIMEOUT = TimeSpan.FromSeconds(2); + private const int MAX_PARALLELISM_HTTP = 4; + private const int HTTP_CAPACITY = 500; + private const int MAX_CACHE_WRITE_PARALLELISM = 4; + private const int MAX_CACHE_BATCH = 500; + + private readonly CancellationTokenSource _cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + + private readonly Channel _checkCacheChannel = Channel.CreateBounded( + new BoundedChannelOptions(SEND_CAPACITY) + { + AllowSynchronousContinuations = true, + Capacity = SEND_CAPACITY, + SingleWriter = false, + SingleReader = false, + FullMode = BoundedChannelFullMode.Wait, + }, + _ => throw new NotImplementedException("Dropping items not supported.") + ); + + public Task Start() => + _checkCacheChannel + .Reader.BatchByByteSize(logAsWarning, HTTP_SEND_CHUNK_SIZE) + .WithTimeout(HTTP_BATCH_TIMEOUT) + .PipeAsync( + MAX_PARALLELISM_HTTP, + async x => await SendToServer(x).ConfigureAwait(false), + HTTP_CAPACITY, + false, + _cts.Token + ) + .Join() + .Batch(MAX_CACHE_BATCH) + .WithTimeout(HTTP_BATCH_TIMEOUT) + .ReadAllConcurrently(MAX_CACHE_WRITE_PARALLELISM, SaveToCache, _cts.Token) + .ContinueWith( + t => + { + Exception? ex = t.Exception; + if (ex is null && t.Status is TaskStatus.Canceled && !_cts.Token.IsCancellationRequested) + { + ex = new OperationCanceledException(); + } + + if (ex is not null) + { + RecordException(ex); + } + _checkCacheChannel.Writer.TryComplete(ex); + }, + _cts.Token, + TaskContinuationOptions.ExecuteSynchronously, + TaskScheduler.Current + ); + + public async ValueTask Save(T item) + { + if (Exception is not null || _cts.IsCancellationRequested) + { + return; //don't save if we're already done through an error + } + await _checkCacheChannel.Writer.WriteAsync(item).ConfigureAwait(false); + } + + private async Task> SendToServer(IMemoryOwner batch) + { + try + { + await SendToServer((Batch)batch).ConfigureAwait(false); + return batch; + } +#pragma warning disable CA1031 + catch (Exception ex) +#pragma warning restore CA1031 + { + RecordException(ex); + return batch; + } + } + + public async Task SendToServer(Batch batch) + { + try + { + await SendToServerInternal(batch).ConfigureAwait(false); + } +#pragma warning disable CA1031 + catch (Exception ex) +#pragma warning restore CA1031 + { + RecordException(ex); + } + } + + protected abstract Task SendToServerInternal(Batch batch); + + public abstract void SaveToCache(List item); + + public void DoneTraversing() => _checkCacheChannel.Writer.TryComplete(); + + public async Task DoneSaving() + { + if (!_checkCacheChannel.Reader.Completion.IsCompleted) + { + await _checkCacheChannel.Reader.Completion.ConfigureAwait(false); + } + } + + protected Exception? Exception { get; set; } + + private void RecordException(Exception ex) + { + Exception = ex; + _checkCacheChannel.Writer.TryComplete(ex); + //cancel everything! + _cts.Cancel(); + } +} diff --git a/src/Speckle.Sdk.Dependencies/Serialization/SizeBatchingChannelReader.cs b/src/Speckle.Sdk.Dependencies/Serialization/SizeBatchingChannelReader.cs new file mode 100644 index 00000000..5596edd4 --- /dev/null +++ b/src/Speckle.Sdk.Dependencies/Serialization/SizeBatchingChannelReader.cs @@ -0,0 +1,38 @@ +using System.Buffers; +using System.Threading.Channels; +using Open.ChannelExtensions; + +namespace Speckle.Sdk.Serialisation.V2.Send; + +public interface IHasByteSize +{ + int ByteSize { get; } +} + +public sealed class SizeBatchingChannelReader( + ChannelReader source, + Action logAsWarning, + int batchSize, + bool singleReader, + bool syncCont = false +) + : BatchingChannelReader>( + _ => BatchExtensions.CreateBatch(), + source, + batchSize, + singleReader, + syncCont + ) + where T : IHasByteSize +{ + private readonly int _batchSize = batchSize; + + protected override IMemoryOwner CreateBatch(int capacity) => BatchExtensions.CreateBatch(); + + protected override void TrimBatch(ref IMemoryOwner batch, bool isVerifiedFull) => + BatchExtensions.TrimBatch(ref batch, isVerifiedFull); + + protected override void AddBatchItem(IMemoryOwner batch, T item) => batch.AddBatchItem(item); + + protected override int GetBatchSize(IMemoryOwner batch) => batch.GetBatchSize(logAsWarning, _batchSize); +} diff --git a/src/Speckle.Sdk.Dependencies/Speckle.Sdk.Dependencies.csproj b/src/Speckle.Sdk.Dependencies/Speckle.Sdk.Dependencies.csproj new file mode 100644 index 00000000..b33a09ef --- /dev/null +++ b/src/Speckle.Sdk.Dependencies/Speckle.Sdk.Dependencies.csproj @@ -0,0 +1,36 @@ + + + + netstandard2.0;net8.0 + Debug;Release;Local + Debug;Release;Local + true + true + + + + Speckle.Sdk.Dependencies + The .NET SDK for Speckle + $(PackageTags) core sdk + + + + true + true + snupkg + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + diff --git a/src/Speckle.Sdk/Helpers/SpeckleHttpClientHandler.cs b/src/Speckle.Sdk.Dependencies/SpeckleHttpClientHandler.cs similarity index 69% rename from src/Speckle.Sdk/Helpers/SpeckleHttpClientHandler.cs rename to src/Speckle.Sdk.Dependencies/SpeckleHttpClientHandler.cs index 49744d19..2070de4e 100644 --- a/src/Speckle.Sdk/Helpers/SpeckleHttpClientHandler.cs +++ b/src/Speckle.Sdk.Dependencies/SpeckleHttpClientHandler.cs @@ -1,6 +1,4 @@ -using System.Diagnostics; using Polly; -using Speckle.Sdk.Common; using Speckle.Sdk.Logging; namespace Speckle.Sdk.Helpers; @@ -8,10 +6,16 @@ namespace Speckle.Sdk.Helpers; public sealed class SpeckleHttpClientHandler : DelegatingHandler { private readonly IAsyncPolicy _resiliencePolicy; + private readonly ISdkActivityFactory _activityFactory; - public SpeckleHttpClientHandler(HttpMessageHandler innerHandler, IAsyncPolicy resiliencePolicy) + internal SpeckleHttpClientHandler( + HttpMessageHandler innerHandler, + ISdkActivityFactory activityFactory, + IAsyncPolicy resiliencePolicy + ) : base(innerHandler) { + _activityFactory = activityFactory; _resiliencePolicy = resiliencePolicy; } @@ -25,7 +29,7 @@ public sealed class SpeckleHttpClientHandler : DelegatingHandler // this is a preliminary client server correlation implementation // refactor this, when we have a better observability stack var context = new Context(); - using var activity = SpeckleActivityFactory.Start("Http Request"); + using var activity = _activityFactory.Start("Http Request"); { activity?.SetTag("http.method", request.Method); activity?.SetTag("http.url", request.RequestUri); @@ -34,6 +38,7 @@ public sealed class SpeckleHttpClientHandler : DelegatingHandler context.Add("retryCount", 0); request.Headers.Add("x-request-id", context.CorrelationId.ToString()); + activity?.InjectHeaders((k, v) => request.Headers.TryAddWithoutValidation(k, v)); var policyResult = await _resiliencePolicy .ExecuteAndCaptureAsync( @@ -46,19 +51,23 @@ public sealed class SpeckleHttpClientHandler : DelegatingHandler .ConfigureAwait(false); context.TryGetValue("retryCount", out var retryCount); activity?.SetTag("retryCount", retryCount); - activity?.SetStatus( - policyResult.Result.IsSuccessStatusCode ? SpeckleActivityStatusCode.Ok : SpeckleActivityStatusCode.Error - ); - if (policyResult.FinalException != null) - { - activity?.RecordException(policyResult.FinalException); - } if (policyResult.Outcome == OutcomeType.Successful) { - return policyResult.Result.NotNull(); + activity?.SetStatus(SdkActivityStatusCode.Ok); + return policyResult.Result; } + activity?.SetStatus(SdkActivityStatusCode.Error); + + if (policyResult.FinalException is null) + { + // Outcome was not successful, but did not terminate with an exception (e.g. repeated 500 responses) + return policyResult.FinalHandledResult; + } + + activity?.RecordException(policyResult.FinalException); + // 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 if (policyResult.FinalException is OperationCanceledException) { diff --git a/src/Speckle.Sdk.Dependencies/SpeckleHttpClientHandlerFactory.cs b/src/Speckle.Sdk.Dependencies/SpeckleHttpClientHandlerFactory.cs new file mode 100644 index 00000000..908e9a94 --- /dev/null +++ b/src/Speckle.Sdk.Dependencies/SpeckleHttpClientHandlerFactory.cs @@ -0,0 +1,47 @@ +using Polly; +using Polly.Contrib.WaitAndRetry; +using Polly.Extensions.Http; +using Polly.Timeout; +using Speckle.InterfaceGenerator; +using Speckle.Sdk.Logging; + +namespace Speckle.Sdk.Helpers; + +[GenerateAutoInterface] +public sealed class SpeckleHttpClientHandlerFactory(ISdkActivityFactory activityFactory) + : ISpeckleHttpClientHandlerFactory +{ + public const int DEFAULT_TIMEOUT_SECONDS = 60; + + public IEnumerable DefaultDelay() + { + return Backoff.DecorrelatedJitterBackoffV2(TimeSpan.FromMilliseconds(200), 5); + } + + internal IAsyncPolicy HttpAsyncPolicy( + IEnumerable? delay = null, + int timeoutSeconds = DEFAULT_TIMEOUT_SECONDS + ) + { + var retryPolicy = HttpPolicyExtensions + .HandleTransientHttpError() + .Or() + .WaitAndRetryAsync( + delay ?? DefaultDelay(), + (ex, timeSpan, retryAttempt, context) => + { + context.Remove("retryCount"); + context.Add("retryCount", retryAttempt); + } + ); + + var timeoutPolicy = Policy.TimeoutAsync(timeoutSeconds); + + return Policy.WrapAsync(retryPolicy, timeoutPolicy); + } + + public SpeckleHttpClientHandler Create( + HttpMessageHandler? innerHandler = null, + int timeoutSeconds = DEFAULT_TIMEOUT_SECONDS + ) => new(innerHandler ?? new HttpClientHandler(), activityFactory, HttpAsyncPolicy(timeoutSeconds: timeoutSeconds)); +} diff --git a/src/Speckle.Sdk.Dependencies/packages.lock.json b/src/Speckle.Sdk.Dependencies/packages.lock.json new file mode 100644 index 00000000..c0197ff7 --- /dev/null +++ b/src/Speckle.Sdk.Dependencies/packages.lock.json @@ -0,0 +1,253 @@ +{ + "version": 2, + "dependencies": { + ".NETStandard,Version=v2.0": { + "ILRepack.FullAuto": { + "type": "Direct", + "requested": "[1.6.0, )", + "resolved": "1.6.0", + "contentHash": "34qp/HQ0XRIWCjtNGUOslJ6p9eNWqHXZQ+xx1iBCvXy3mj8tEiqIwRG+LubFyKCJITqMh5cpFvFl20/6+Dmy+g==", + "dependencies": { + "ILRepack": "2.0.33" + } + }, + "Microsoft.Extensions.ObjectPool": { + "type": "Direct", + "requested": "[9.0.1, )", + "resolved": "9.0.1", + "contentHash": "r64veU9uYILp6pYqfo3qzRab8zLMALvXZgT4VRY79tXMLu8X79uTlJ6nqPLtPIVhfCPXycRh8ILyFz/gGBDQdQ==" + }, + "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" + } + }, + "NETStandard.Library": { + "type": "Direct", + "requested": "[2.0.3, )", + "resolved": "2.0.3", + "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0" + } + }, + "Open.ChannelExtensions": { + "type": "Direct", + "requested": "[9.0.0, )", + "resolved": "9.0.0", + "contentHash": "DP+l5S6G46wcuY4I4kNXE+RDOmJr0DKuMienOdt0mMBN9z7vmLSC8YQbqCyb9i9LNjXj1tgCx5LyitJiRr/v7g==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "9.0.0", + "System.Collections.Immutable": "9.0.0", + "System.Threading.Channels": "9.0.0" + } + }, + "Polly": { + "type": "Direct", + "requested": "[7.2.3, )", + "resolved": "7.2.3", + "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + }, + "Polly.Contrib.WaitAndRetry": { + "type": "Direct", + "requested": "[1.1.1, )", + "resolved": "1.1.1", + "contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA==" + }, + "Polly.Extensions.Http": { + "type": "Direct", + "requested": "[3.0.0, )", + "resolved": "3.0.0", + "contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==", + "dependencies": { + "Polly": "7.1.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "System.Threading.Channels": { + "type": "Direct", + "requested": "[9.0.2, )", + "resolved": "9.0.2", + "contentHash": "pUmqkuBS9OxWHOlfNad09Oxc8gRbxgN9UQtsqHPst4jfcgZRxQetNcsT2oe+VnUpEFAtBy1FZcJZiOscrBmA7g==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "9.0.2", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "ILRepack": { + "type": "Transitive", + "resolved": "2.0.33", + "contentHash": "xb2h1CsOepoYwdXEPui9VcQglwABQwNf9cccZbf+acarEzF5PUp8Xx71nFXIhOgEdm6wrxAoF6xAxK4m/XFRUQ==" + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "QhkXUl2gNrQtvPmtBTQHb0YsUrDiDQ2QS09YbtTTiSjGcf7NBqtYbrG/BE06zcBPCKEwQGzIv13IVdXNOSub2w==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "CentralTransitive", + "requested": "[5.0.0, )", + "resolved": "9.0.2", + "contentHash": "1CED0BGD7dCKsbe7tDhzpPB2Qdi9x35QChu6zkBEI4s0T5bDkkttGReqQnOeOfRNSxtP2WvpX6Ik/0O93XDuMw==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + } + }, + "net8.0": { + "ILRepack.FullAuto": { + "type": "Direct", + "requested": "[1.6.0, )", + "resolved": "1.6.0", + "contentHash": "34qp/HQ0XRIWCjtNGUOslJ6p9eNWqHXZQ+xx1iBCvXy3mj8tEiqIwRG+LubFyKCJITqMh5cpFvFl20/6+Dmy+g==", + "dependencies": { + "ILRepack": "2.0.33" + } + }, + "Microsoft.Extensions.ObjectPool": { + "type": "Direct", + "requested": "[9.0.1, )", + "resolved": "9.0.1", + "contentHash": "r64veU9uYILp6pYqfo3qzRab8zLMALvXZgT4VRY79tXMLu8X79uTlJ6nqPLtPIVhfCPXycRh8ILyFz/gGBDQdQ==" + }, + "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" + } + }, + "Open.ChannelExtensions": { + "type": "Direct", + "requested": "[9.0.0, )", + "resolved": "9.0.0", + "contentHash": "DP+l5S6G46wcuY4I4kNXE+RDOmJr0DKuMienOdt0mMBN9z7vmLSC8YQbqCyb9i9LNjXj1tgCx5LyitJiRr/v7g==" + }, + "Polly": { + "type": "Direct", + "requested": "[7.2.3, )", + "resolved": "7.2.3", + "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + }, + "Polly.Contrib.WaitAndRetry": { + "type": "Direct", + "requested": "[1.1.1, )", + "resolved": "1.1.1", + "contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA==" + }, + "Polly.Extensions.Http": { + "type": "Direct", + "requested": "[3.0.0, )", + "resolved": "3.0.0", + "contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==", + "dependencies": { + "Polly": "7.1.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "System.Threading.Channels": { + "type": "Direct", + "requested": "[9.0.2, )", + "resolved": "9.0.2", + "contentHash": "pUmqkuBS9OxWHOlfNad09Oxc8gRbxgN9UQtsqHPst4jfcgZRxQetNcsT2oe+VnUpEFAtBy1FZcJZiOscrBmA7g==" + }, + "ILRepack": { + "type": "Transitive", + "resolved": "2.0.33", + "contentHash": "xb2h1CsOepoYwdXEPui9VcQglwABQwNf9cccZbf+acarEzF5PUp8Xx71nFXIhOgEdm6wrxAoF6xAxK4m/XFRUQ==" + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + } + } + } +} \ No newline at end of file diff --git a/src/Speckle.Sdk.Logging/Consts.cs b/src/Speckle.Sdk.Logging/Consts.cs deleted file mode 100644 index 833d694e..00000000 --- a/src/Speckle.Sdk.Logging/Consts.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Reflection; - -namespace Speckle.Sdk.Logging; - -public static class Consts -{ - public const string SERVICE_NAME = "connector.name"; - public const string SERVICE_SLUG = "connector.slug"; - public const string OS_NAME = "os.name"; - public const string OS_TYPE = "os.type"; - public const string OS_SLUG = "os.slug"; - public const string RUNTIME_NAME = "runtime.name"; - - public static readonly string Application = "speckle-connectors"; - public static string Version => Assembly.GetExecutingAssembly().GetPackageVersion(); - - public static string GetPackageVersion(this Assembly assembly) - { - // MinVer https://github.com/adamralph/minver?tab=readme-ov-file#version-numbers - // together with Microsoft.SourceLink.GitHub https://github.com/dotnet/sourcelink - // fills AssemblyInformationalVersionAttribute by - // {majorVersion}.{minorVersion}.{patchVersion}.{pre-release label}.{pre-release version}.{gitHeight}+{Git SHA of current commit} - // Ex: 1.5.0-alpha.1.40+807f703e1b4d9874a92bd86d9f2d4ebe5b5d52e4 - // The following parts are optional: pre-release label, pre-release version, git height, Git SHA of current commit - // For package version, value of AssemblyInformationalVersionAttribute without commit hash is returned. - - var informationalVersion = assembly - .GetCustomAttribute() - ?.InformationalVersion; - if (informationalVersion is null) - { - return String.Empty; - } - - var indexOfPlusSign = informationalVersion.IndexOf('+'); - return indexOfPlusSign > 0 ? informationalVersion.Substring(0, indexOfPlusSign) : informationalVersion; - } -} diff --git a/src/Speckle.Sdk.Logging/ISpeckleLogger.cs b/src/Speckle.Sdk.Logging/ISpeckleLogger.cs deleted file mode 100644 index 6df70b7a..00000000 --- a/src/Speckle.Sdk.Logging/ISpeckleLogger.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Speckle.Sdk.Logging; - -public interface ISpeckleLogger -{ - void Write(SpeckleLogLevel speckleLogLevel, string message, params object?[] arguments); - void Write(SpeckleLogLevel speckleLogLevel, Exception? exception, string message, params object?[] arguments); - - void Debug(string message, params object?[] arguments); - void Debug(Exception exception, string message, params object?[] arguments); - void Warning(string message, params object?[] arguments); - void Warning(Exception exception, string message, params object?[] arguments); - void Information(string message, params object?[] arguments); - - void Information(Exception exception, string message, params object?[] arguments); - - void Error(string message, params object?[] arguments); - void Error(Exception exception, string message, params object?[] arguments); - void Fatal(Exception exception, string message, params object?[] arguments); -} diff --git a/src/Speckle.Sdk.Logging/LogBuilder.cs b/src/Speckle.Sdk.Logging/LogBuilder.cs deleted file mode 100644 index 54ed1845..00000000 --- a/src/Speckle.Sdk.Logging/LogBuilder.cs +++ /dev/null @@ -1,197 +0,0 @@ -using System.Diagnostics; -using System.Reflection; -using System.Runtime.InteropServices; -using OpenTelemetry.Exporter; -using OpenTelemetry.Resources; -using OpenTelemetry.Trace; -using Serilog; -using Serilog.Exceptions; -using Serilog.Sinks.OpenTelemetry; - -namespace Speckle.Sdk.Logging; - -public static class LogBuilder -{ - public static IDisposable? Initialize( - string userId, - string applicationAndVersion, - string slug, - SpeckleLogging? speckleLogging, - SpeckleTracing? speckleTracing - ) - { - var resourceBuilder = ResourceBuilder - .CreateEmpty() - .AddService(serviceName: Consts.Application, serviceVersion: Consts.Version) - .AddAttributes( - new List> - { - new(Consts.SERVICE_NAME, applicationAndVersion), - new(Consts.SERVICE_SLUG, slug), - new(Consts.OS_NAME, Environment.OSVersion.ToString()), - new(Consts.OS_TYPE, RuntimeInformation.ProcessArchitecture.ToString()), - new(Consts.OS_SLUG, DetermineHostOsSlug()), - new(Consts.RUNTIME_NAME, RuntimeInformation.FrameworkDescription) - } - ); - var fileVersionInfo = GetFileVersionInfo(); - var serilogLogConfiguration = new LoggerConfiguration() - .MinimumLevel.Is(SpeckleLogger.GetLevel(speckleLogging?.MinimumLevel ?? SpeckleLogLevel.Warning)) - .Enrich.FromLogContext() - .Enrich.WithProperty("id", userId) - .Enrich.WithProperty("version", fileVersionInfo.FileVersion) - .Enrich.WithProperty("productVersion", fileVersionInfo.ProductVersion) - .Enrich.WithProperty("hostOs", DetermineHostOsSlug()) - .Enrich.WithProperty("hostOsVersion", Environment.OSVersion) - .Enrich.WithProperty("hostOsArchitecture", RuntimeInformation.ProcessArchitecture.ToString()) - .Enrich.WithProperty("runtime", RuntimeInformation.FrameworkDescription) - .Enrich.WithExceptionDetails(); - - if (speckleLogging?.File is not null) - { - // TODO: check if we have write permissions to the file. - var logFilePath = SpecklePathProvider.LogFolderPath(applicationAndVersion); - logFilePath = Path.Combine(logFilePath, speckleLogging.File.Path ?? "SpeckleCoreLog.txt"); - serilogLogConfiguration = serilogLogConfiguration.WriteTo.File( - logFilePath, - rollingInterval: RollingInterval.Day, - retainedFileCountLimit: 10 - ); - } - - if (speckleLogging?.Console ?? false) - { - serilogLogConfiguration = serilogLogConfiguration.WriteTo.Console(); - } - - if (speckleLogging?.Otel is not null) - { - serilogLogConfiguration = InitializeOtelLogging(serilogLogConfiguration, speckleLogging.Otel, resourceBuilder); - } - var logger = serilogLogConfiguration.CreateLogger(); - SpeckleLog.SpeckleLogger = logger; - - logger - .ForContext("hostApplication", applicationAndVersion) - .ForContext("userApplicationDataPath", SpecklePathProvider.UserApplicationDataPath()) - .ForContext("installApplicationDataPath", SpecklePathProvider.InstallApplicationDataPath) - .Information( - "Initialized logger inside {hostApplication}/{productVersion}/{version} for user {id}. Path info {userApplicationDataPath} {installApplicationDataPath}." - ); - return InitializeOtelTracing(speckleTracing, resourceBuilder); - } - - private static FileVersionInfo GetFileVersionInfo() - { - var assembly = Assembly.GetExecutingAssembly().Location; - return FileVersionInfo.GetVersionInfo(assembly); - } - - private static string DetermineHostOsSlug() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - return "Windows"; - } - - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - return "MacOS"; - } - - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - return "Linux"; - } - - return RuntimeInformation.OSDescription; - } - - private static LoggerConfiguration InitializeOtelLogging( - LoggerConfiguration serilogLogConfiguration, - SpeckleOtelLogging speckleOtelLogging, - ResourceBuilder resourceBuilder - ) => - serilogLogConfiguration.WriteTo.OpenTelemetry(o => - { - o.Protocol = OtlpProtocol.HttpProtobuf; - o.LogsEndpoint = speckleOtelLogging.Endpoint; - o.Headers = speckleOtelLogging.Headers ?? o.Headers; - o.ResourceAttributes = resourceBuilder.Build().Attributes.ToDictionary(x => x.Key, x => x.Value); - }); - - private static IDisposable? InitializeOtelTracing(SpeckleTracing? logConfiguration, ResourceBuilder resourceBuilder) - { - var consoleEnabled = logConfiguration?.Console ?? false; - var otelEnabled = logConfiguration?.Otel?.Enabled ?? false; - if (!consoleEnabled && !otelEnabled) - { - return null; - } - - var tracerProviderBuilder = OpenTelemetry.Sdk.CreateTracerProviderBuilder().AddSource(Consts.Application); - tracerProviderBuilder = tracerProviderBuilder.AddHttpClientInstrumentation( - (options) => - { - options.FilterHttpWebRequest = (httpWebRequest) => - { - // Example: Only collect telemetry about HTTP GET requests. - return httpWebRequest.Method.Equals(HttpMethod.Get.Method); - }; - options.EnrichWithHttpWebRequest = (activity, httpWebRequest) => - { - activity.SetTag("requestVersion", httpWebRequest.ProtocolVersion); - }; - // Note: Only called on .NET Framework. - options.EnrichWithHttpWebResponse = (activity, httpWebResponse) => - { - activity.SetTag("responseVersion", httpWebResponse.ProtocolVersion); - }; - // Note: Only called on .NET & .NET Core runtimes. - options.EnrichWithHttpRequestMessage = (activity, httpRequestMessage) => - { - activity.SetTag("requestVersion", httpRequestMessage.Version); - }; - // Note: Only called on .NET & .NET Core runtimes. - options.EnrichWithHttpResponseMessage = (activity, httpResponseMessage) => - { - activity.SetTag("responseVersion", httpResponseMessage.Version); - }; - // Note: Called for all runtimes. - options.EnrichWithException = (activity, exception) => - { - activity.SetTag("stackTrace", exception.StackTrace); - }; - options.RecordException = true; - } - ); - if (otelEnabled) - { - tracerProviderBuilder = tracerProviderBuilder.AddOtlpExporter(x => ProcessOptions(logConfiguration!, x)); - } - - if (consoleEnabled) - { - tracerProviderBuilder = tracerProviderBuilder.AddConsoleExporter(); - } - - tracerProviderBuilder = tracerProviderBuilder.SetResourceBuilder(resourceBuilder).SetSampler(); - - return tracerProviderBuilder.Build(); - } - - private static void ProcessOptions(SpeckleTracing logConfiguration, OtlpExporterOptions options) - { - options.Protocol = OtlpExportProtocol.HttpProtobuf; - var headers = string.Join(",", logConfiguration.Otel?.Headers?.Select(x => x.Key + "=" + x.Value) ?? []); - if (headers.Length != 0) - { - options.Headers = headers; - } - - if (logConfiguration.Otel?.Endpoint is not null) - { - options.Endpoint = new Uri(logConfiguration.Otel.Endpoint); - } - } -} diff --git a/src/Speckle.Sdk.Logging/LogExtensions.cs b/src/Speckle.Sdk.Logging/LogExtensions.cs deleted file mode 100644 index 71ce99e2..00000000 --- a/src/Speckle.Sdk.Logging/LogExtensions.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Microsoft.Extensions.Logging; - -namespace Speckle.Sdk.Logging; - -public static class LogExtensions -{ - public static void Debug(this ILogger logger, string message, params object?[] arguments) => - logger.LogDebug(message, arguments); - - public static void Debug(this ILogger logger, Exception exception, string message, params object?[] arguments) => - logger.LogDebug(exception, message, arguments); - - public static void Warning(this ILogger logger, string message, params object?[] arguments) => - logger.LogWarning(message, arguments); - - public static void Warning(this ILogger logger, Exception exception, string message, params object?[] arguments) => - logger.LogWarning(exception, message, arguments); - - public static void Information(this ILogger logger, string message, params object?[] arguments) => - logger.LogInformation(message, arguments); - - public static void Information( - this ILogger logger, - Exception exception, - string message, - params object?[] arguments - ) => logger.LogInformation(exception, message, arguments); - - public static void Error(this ILogger logger, string message, params object?[] arguments) => - logger.LogError(message, arguments); - - public static void Error(this ILogger logger, Exception exception, string message, params object?[] arguments) => - logger.LogError(exception, message, arguments); - - public static void Fatal(this ILogger logger, Exception exception, string message, params object?[] arguments) => - logger.LogCritical(exception, message, arguments); -} diff --git a/src/Speckle.Sdk.Logging/Path.cs b/src/Speckle.Sdk.Logging/Path.cs deleted file mode 100644 index 66c1b28d..00000000 --- a/src/Speckle.Sdk.Logging/Path.cs +++ /dev/null @@ -1,162 +0,0 @@ -using System.Reflection; - -namespace Speckle.Sdk.Logging; - -/// -/// Helper class dedicated for Speckle specific Path operations. -/// -public static class SpecklePathProvider -{ - private static string s_applicationName = "Speckle"; - - private static string s_blobFolderName = "Blobs"; - - private static string s_kitsFolderName = "Kits"; - - private static string s_accountsFolderName = "Accounts"; - - private static string s_objectsFolderName = "Objects"; - - private const string LOG_FOLDER_NAME = "Logs"; - - private static string UserDataPathEnvVar => "SPECKLE_USERDATA_PATH"; - private static string? Path => Environment.GetEnvironmentVariable(UserDataPathEnvVar); - - /// - /// Get the installation path. - /// - public static string InstallApplicationDataPath => - Assembly.GetAssembly(typeof(SpecklePathProvider)).Location.Contains("ProgramData") - ? Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) - : UserApplicationDataPath(); - - /// - /// Get the path where the Speckle applications should be installed - /// - public static string InstallSpeckleFolderPath => EnsureFolderExists(InstallApplicationDataPath, s_applicationName); - - /// - /// Get the folder where the user's Speckle data should be stored. - /// - public static string UserSpeckleFolderPath => EnsureFolderExists(UserApplicationDataPath(), s_applicationName); - - /// - /// Get the folder where the Speckle kits should be stored. - /// - public static string KitsFolderPath => EnsureFolderExists(InstallSpeckleFolderPath, s_kitsFolderName); - - /// - /// - /// - public static string ObjectsFolderPath => EnsureFolderExists(KitsFolderPath, s_objectsFolderName); - - /// - /// Get the folder where the Speckle accounts data should be stored. - /// - public static string AccountsFolderPath => EnsureFolderExists(UserSpeckleFolderPath, s_accountsFolderName); - - /// - /// Override the global Speckle application name. - /// - /// - public static void OverrideApplicationName(string applicationName) - { - s_applicationName = applicationName; - } - - /// - /// Override the global Speckle application data path. - /// - public static void OverrideApplicationDataPath(string? path) - { - Environment.SetEnvironmentVariable(UserDataPathEnvVar, path); - } - - /// - /// Override the global Blob storage folder name. - /// - public static void OverrideBlobStorageFolder(string blobFolderName) - { - s_blobFolderName = blobFolderName; - } - - /// - /// Override the global Kits folder name. - /// - public static void OverrideKitsFolderName(string kitsFolderName) - { - s_kitsFolderName = kitsFolderName; - } - - /// - /// Override the global Accounts folder name. - /// - public static void OverrideAccountsFolderName(string accountsFolderName) - { - s_accountsFolderName = accountsFolderName; - } - - /// - /// - /// - public static void OverrideObjectsFolderName(string objectsFolderName) - { - s_objectsFolderName = objectsFolderName; - } - - /// - /// Get the platform specific user configuration folder path. - /// - public static string UserApplicationDataPath() - { - // if we have an override, just return that - var pathOverride = Path; - if (pathOverride != null && !string.IsNullOrEmpty(pathOverride)) - { - return pathOverride; - } - - // on desktop linux and macos we use the appdata. - // but we might not have write access to the disk - // so the catch falls back to the user profile - try - { - return Environment.GetFolderPath( - Environment.SpecialFolder.ApplicationData, - // if the folder doesn't exist, we get back an empty string on OSX, - // which in turn, breaks other stuff down the line. - // passing in the Create option ensures that this directory exists, - // which is not a given on all OS-es. - Environment.SpecialFolderOption.Create - ); - } - catch (SystemException ex) when (ex is PlatformNotSupportedException or ArgumentException) - { - //Adding this log just so we confidently know which Exception type to catch here. - SpeckleLog.Logger.Warning(ex, "Falling back to user profile path"); - - // on server linux, there might not be a user setup, things can run under root - // in that case, the appdata variable is most probably not set up - // we fall back to the value of the home folder - return Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); - } - } - - /// - /// Get the folder where the user's Speckle blobs should be stored. - /// - public static string BlobStoragePath(string? path = null) - { - return EnsureFolderExists(path ?? UserSpeckleFolderPath, s_blobFolderName); - } - - private static string EnsureFolderExists(params string[] folderName) - { - var path = System.IO.Path.Combine(folderName); - Directory.CreateDirectory(path); - return path; - } - - internal static string LogFolderPath(string applicationAndVersion) => - EnsureFolderExists(UserSpeckleFolderPath, LOG_FOLDER_NAME, applicationAndVersion); -} diff --git a/src/Speckle.Sdk.Logging/Speckle.Sdk.Logging.csproj b/src/Speckle.Sdk.Logging/Speckle.Sdk.Logging.csproj deleted file mode 100644 index c654bdc6..00000000 --- a/src/Speckle.Sdk.Logging/Speckle.Sdk.Logging.csproj +++ /dev/null @@ -1,25 +0,0 @@ - - - - netstandard2.0 - - - Debug;Release;Local - true - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - diff --git a/src/Speckle.Sdk.Logging/SpeckleActivity.cs b/src/Speckle.Sdk.Logging/SpeckleActivity.cs deleted file mode 100644 index 7312f382..00000000 --- a/src/Speckle.Sdk.Logging/SpeckleActivity.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Diagnostics; -using OpenTelemetry.Trace; - -namespace Speckle.Sdk.Logging; - -public class SpeckleActivity(Activity activity) : ISpeckleActivity -{ - public void Dispose() => activity.Dispose(); - - public void SetTag(string key, object? value) => activity.SetTag(key, value); - - public void RecordException(Exception e) => activity.RecordException(e); - - public string TraceId => activity.TraceId.ToString(); - - public void SetStatus(SpeckleActivityStatusCode code) => - activity.SetStatus( - code switch - { - SpeckleActivityStatusCode.Error => ActivityStatusCode.Error, - SpeckleActivityStatusCode.Unset => ActivityStatusCode.Unset, - SpeckleActivityStatusCode.Ok => ActivityStatusCode.Ok, - _ => throw new ArgumentOutOfRangeException(nameof(code), code, null) - } - ); -} diff --git a/src/Speckle.Sdk.Logging/SpeckleActivityFactory.cs b/src/Speckle.Sdk.Logging/SpeckleActivityFactory.cs deleted file mode 100644 index 855fa300..00000000 --- a/src/Speckle.Sdk.Logging/SpeckleActivityFactory.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Diagnostics; -using System.Runtime.CompilerServices; - -namespace Speckle.Sdk.Logging; - -public static class SpeckleActivityFactory -{ - private static readonly ActivitySource? s_activitySource = new(Consts.Application, Consts.Version); - - public static ISpeckleActivity? Start(string? name = null, [CallerMemberName] string source = "") - { - var activity = s_activitySource?.StartActivity(name ?? source, ActivityKind.Client); - if (activity is null) - { - return null; - } - return new SpeckleActivity(activity); - } -} diff --git a/src/Speckle.Sdk.Logging/SpeckleLog.cs b/src/Speckle.Sdk.Logging/SpeckleLog.cs deleted file mode 100644 index c5f02ea1..00000000 --- a/src/Speckle.Sdk.Logging/SpeckleLog.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Serilog.Core; - -namespace Speckle.Sdk.Logging; - -public static class SpeckleLog -{ - internal static Serilog.ILogger SpeckleLogger { get; set; } = Serilog.Core.Logger.None; - public static ISpeckleLogger Logger => new SpeckleLogger(SpeckleLogger); - - public static ISpeckleLogger Create(string name) => - new SpeckleLogger(SpeckleLogger.ForContext(Constants.SourceContextPropertyName, name)); -} diff --git a/src/Speckle.Sdk.Logging/SpeckleLogConfiguration.cs b/src/Speckle.Sdk.Logging/SpeckleLogConfiguration.cs deleted file mode 100644 index 9f2d6561..00000000 --- a/src/Speckle.Sdk.Logging/SpeckleLogConfiguration.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Speckle.Sdk.Logging; - -/// -/// Configuration object for the Speckle logging system. -/// - -public record SpeckleLogging( - SpeckleLogLevel MinimumLevel = SpeckleLogLevel.Warning, - bool Console = true, - SpeckleFileLogging? File = null, - SpeckleOtelLogging? Otel = null -); - -public record SpeckleFileLogging(string? Path = null, bool Enabled = true); - -public record SpeckleOtelLogging(string Endpoint, bool Enabled = true, Dictionary? Headers = null); - -public record SpeckleTracing(bool Console = false, SpeckleOtelTracing? Otel = null); - -public record SpeckleOtelTracing( - string? Endpoint = null, - bool Enabled = true, - Dictionary? Headers = null -); diff --git a/src/Speckle.Sdk.Logging/SpeckleLogLevel.cs b/src/Speckle.Sdk.Logging/SpeckleLogLevel.cs deleted file mode 100644 index e84ca64e..00000000 --- a/src/Speckle.Sdk.Logging/SpeckleLogLevel.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace Speckle.Sdk.Logging; - -public enum SpeckleLogLevel -{ - /// - /// Anything and everything you might want to know about - /// a running block of code. - /// - Verbose, - - /// - /// Internal system events that aren't necessarily - /// observable from the outside. - /// - Debug, - - /// - /// The lifeblood of operational intelligence - things - /// happen. - /// - Information, - - /// - /// Service is degraded or endangered. - /// - Warning, - - /// - /// Functionality is unavailable, invariants are broken - /// or data is lost. - /// - Error, - - /// - /// If you have a pager, it goes off when one of these - /// occurs. - /// - Fatal -} diff --git a/src/Speckle.Sdk.Logging/SpeckleLogger.cs b/src/Speckle.Sdk.Logging/SpeckleLogger.cs deleted file mode 100644 index 5b83eff2..00000000 --- a/src/Speckle.Sdk.Logging/SpeckleLogger.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Serilog; -using Serilog.Events; - -namespace Speckle.Sdk.Logging; - -internal class SpeckleLogger : ISpeckleLogger -{ - private readonly Serilog.ILogger _logger; - - public SpeckleLogger(ILogger logger) - { - _logger = logger; - } - - internal static LogEventLevel GetLevel(SpeckleLogLevel speckleLogLevel) => - speckleLogLevel switch - { - SpeckleLogLevel.Debug => LogEventLevel.Debug, - SpeckleLogLevel.Verbose => LogEventLevel.Verbose, - SpeckleLogLevel.Information => LogEventLevel.Information, - SpeckleLogLevel.Warning => LogEventLevel.Warning, - SpeckleLogLevel.Error => LogEventLevel.Error, - SpeckleLogLevel.Fatal => LogEventLevel.Fatal, - _ => throw new ArgumentOutOfRangeException(nameof(speckleLogLevel), speckleLogLevel, null) - }; - - public void Write(SpeckleLogLevel speckleLogLevel, string message, params object?[] arguments) => - _logger.Write(GetLevel(speckleLogLevel), message, arguments); - - public void Write( - SpeckleLogLevel speckleLogLevel, - Exception? exception, - string message, - params object?[] arguments - ) => _logger.Write(GetLevel(speckleLogLevel), exception, message, arguments); - - public void Debug(string message, params object?[] arguments) => _logger.Debug(message, arguments); - - public void Debug(Exception exception, string message, params object?[] arguments) => - _logger.Debug(exception, message, arguments); - - public void Warning(string message, params object?[] arguments) => _logger.Warning(message, arguments); - - public void Warning(Exception exception, string message, params object?[] arguments) => - _logger.Warning(exception, message, arguments); - - public void Information(string message, params object?[] arguments) => _logger.Information(message, arguments); - - public void Information(Exception exception, string message, params object?[] arguments) => - _logger.Information(exception, message, arguments); - - public void Error(string message, params object?[] arguments) => _logger.Error(message, arguments); - - public void Error(Exception exception, string message, params object?[] arguments) => - _logger.Error(exception, message, arguments); - - public void Fatal(Exception exception, string message, params object?[] arguments) => - _logger.Fatal(exception, message, arguments); -} diff --git a/src/Speckle.Sdk.Logging/packages.lock.json b/src/Speckle.Sdk.Logging/packages.lock.json deleted file mode 100644 index 0cc017ba..00000000 --- a/src/Speckle.Sdk.Logging/packages.lock.json +++ /dev/null @@ -1,451 +0,0 @@ -{ - "version": 2, - "dependencies": { - ".NETStandard,Version=v2.0": { - "GitVersion.MsBuild": { - "type": "Direct", - "requested": "[5.12.0, )", - "resolved": "5.12.0", - "contentHash": "dJuigXycpJNOiLT9or7mkHSkGFHgGW3/p6cNNYEKZBa7Hhp1FdX/cvqYWWYhRLpfoZOedeA7aRbYiOB3vW/dvA==" - }, - "ILRepack.FullAuto": { - "type": "Direct", - "requested": "[1.6.0, )", - "resolved": "1.6.0", - "contentHash": "34qp/HQ0XRIWCjtNGUOslJ6p9eNWqHXZQ+xx1iBCvXy3mj8tEiqIwRG+LubFyKCJITqMh5cpFvFl20/6+Dmy+g==", - "dependencies": { - "ILRepack": "2.0.33" - } - }, - "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" - } - }, - "NETStandard.Library": { - "type": "Direct", - "requested": "[2.0.3, )", - "resolved": "2.0.3", - "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0" - } - }, - "OpenTelemetry.Exporter.Console": { - "type": "Direct", - "requested": "[1.9.0, )", - "resolved": "1.9.0", - "contentHash": "TbScDLSc6kcji+/wZYIf8/HBV2SnttzN7PNxr3TYczlmGlU4K2ugujp6seSktEO4OaAvKRd7Y3CG3SKNj0C+1Q==", - "dependencies": { - "OpenTelemetry": "1.9.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2" - } - }, - "OpenTelemetry.Exporter.OpenTelemetryProtocol": { - "type": "Direct", - "requested": "[1.9.0, )", - "resolved": "1.9.0", - "contentHash": "qzFOP3V2eYIVbug3U4BJzzidHe9JhAJ42WZ/H8pUp/45Ry3MQQg/+e/ZieClJcxKnpbkXi7dUq1rpvuNp+yBYA==", - "dependencies": { - "Google.Protobuf": "[3.22.5, 4.0.0)", - "Grpc": "[2.44.0, 3.0.0)", - "Microsoft.Extensions.Configuration.Binder": "8.0.1", - "OpenTelemetry": "1.9.0" - } - }, - "OpenTelemetry.Instrumentation.Http": { - "type": "Direct", - "requested": "[1.9.0, )", - "resolved": "1.9.0", - "contentHash": "+ZXppf8Qxz3OdC803T8fB6i8iSscc8PsxMnM/JizSOYmkz+8vGiScEiaBBBFNZtMh2KpA0q+qxwnSwQUkbvzog==", - "dependencies": { - "Microsoft.Extensions.Configuration": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0", - "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.9.0, 2.0.0)" - } - }, - "PolySharp": { - "type": "Direct", - "requested": "[1.14.1, )", - "resolved": "1.14.1", - "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" - }, - "Serilog": { - "type": "Direct", - "requested": "[4.0.1, )", - "resolved": "4.0.1", - "contentHash": "pzeDRXdpSLSsgBHpZcmpIDxqMy845Ab4s+dfnBg0sN9h8q/4Wo3vAoe0QCGPze1Q06EVtEPupS+UvLm8iXQmTQ==", - "dependencies": { - "System.Diagnostics.DiagnosticSource": "8.0.1", - "System.Threading.Channels": "8.0.0" - } - }, - "Serilog.Exceptions": { - "type": "Direct", - "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": "Direct", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "fQGWqVMClCP2yEyTXPIinSr5c+CBGUvBybPxjAGcf7ctDhadFhrQw03Mv8rJ07/wR5PDfFjewf2LimvXCDzpbA==", - "dependencies": { - "Serilog": "4.0.0" - } - }, - "Serilog.Sinks.File": { - "type": "Direct", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "lxjg89Y8gJMmFxVkbZ+qDgjl+T4yC5F7WSLTvA+5q0R04tfKVLRL/EHpYoJ/MEQd2EeCKDuylBIVnAYMotmh2A==", - "dependencies": { - "Serilog": "4.0.0" - } - }, - "Serilog.Sinks.OpenTelemetry": { - "type": "Direct", - "requested": "[4.0.0, )", - "resolved": "4.0.0", - "contentHash": "M4WKDojg8a+msY5qhNGg5PYZ9ZVelcqipkP/CrfDAOvGCZ3MYj+cGXgnTanffYyIoC2onabaOtydqW6lqT/P/w==", - "dependencies": { - "Google.Protobuf": "3.26.1", - "Grpc.Net.Client": "2.62.0", - "Serilog": "4.0.0" - } - }, - "Speckle.InterfaceGenerator": { - "type": "Direct", - "requested": "[0.9.6, )", - "resolved": "0.9.6", - "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" - }, - "Google.Protobuf": { - "type": "Transitive", - "resolved": "3.26.1", - "contentHash": "CHZX8zXqhF/fdUtd+AYzew8T2HFkAoe5c7lbGxZY/qryAlQXckDvM5BfOJjXlMS7kyICqQTMszj4w1bX5uBJ/w==", - "dependencies": { - "System.Memory": "4.5.3", - "System.Runtime.CompilerServices.Unsafe": "4.5.2" - } - }, - "Grpc": { - "type": "Transitive", - "resolved": "2.44.0", - "contentHash": "9a6FybBPcVMAxSXMTbwYxBmAe7dG+hMMSTrar4lccIXZxUNQM9zMEMzMqih1QTypdLv6udf1+oVOTf11T4c/sQ==", - "dependencies": { - "Grpc.Core": "2.44.0" - } - }, - "Grpc.Core": { - "type": "Transitive", - "resolved": "2.44.0", - "contentHash": "H2rTNePSYeEqUgBBqiE5KZ/yOX7jEtETjWjv4gGtnYxrlpZ79VAHe+4KtZAnt2KJMiGvqTm7eo1SPk+QpiPfaw==", - "dependencies": { - "Grpc.Core.Api": "2.44.0", - "System.Memory": "4.5.3" - } - }, - "Grpc.Core.Api": { - "type": "Transitive", - "resolved": "2.62.0", - "contentHash": "q4Jj6bRZHNnE4CMLqgjiBUCKLit+tRr0simZsS2W6U++akd7CzXByeKy2tddqT68hFzP2XzceXA2YtBTfWtixA==", - "dependencies": { - "System.Memory": "4.5.3" - } - }, - "Grpc.Net.Client": { - "type": "Transitive", - "resolved": "2.62.0", - "contentHash": "C7HxLt+wWPTpPFORRHkxxtDLL+K/jXSmZBaPLhFM8AEkN0bYjklIfCwnzajn1gcbRcEETBb0WnRgHJdVzpwbCg==", - "dependencies": { - "Grpc.Net.Common": "2.62.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.1" - } - }, - "Grpc.Net.Common": { - "type": "Transitive", - "resolved": "2.62.0", - "contentHash": "eBv5I4RPWfdezGXqooU5hs3+XcfVMLk5XDlA4G/Nd9TMX78ZGrFl/lM1Ad187zgBLmH7WPAgfjKRWLBwaa1Wbw==", - "dependencies": { - "Grpc.Core.Api": "2.62.0" - } - }, - "ILRepack": { - "type": "Transitive", - "resolved": "2.0.33", - "contentHash": "xb2h1CsOepoYwdXEPui9VcQglwABQwNf9cccZbf+acarEzF5PUp8Xx71nFXIhOgEdm6wrxAoF6xAxK4m/XFRUQ==" - }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==", - "dependencies": { - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Microsoft.Build.Tasks.Git": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" - }, - "Microsoft.Extensions.Configuration": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", - "Microsoft.Extensions.Primitives": "8.0.0" - } - }, - "Microsoft.Extensions.Configuration.Abstractions": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", - "dependencies": { - "Microsoft.Extensions.Primitives": "8.0.0" - } - }, - "Microsoft.Extensions.Configuration.Binder": { - "type": "Transitive", - "resolved": "8.0.1", - "contentHash": "2UKFJnLiBt7Od6nCnTqP9rTIUNhzmn9Hv1l2FchyKbz8xieB9ULwZTbQZMw+M24Qw3F5dzzH1U9PPleN0LNLOQ==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" - } - }, - "Microsoft.Extensions.DependencyInjection": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Microsoft.Extensions.DependencyInjection.Abstractions": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Microsoft.Extensions.Diagnostics.Abstractions": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "JHYCQG7HmugNYUhOl368g+NMxYE/N/AiclCYRNlgCY9eVyiBkOHMwK4x60RYMxv9EL3+rmj1mqHvdCiPpC+D4Q==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0", - "System.Buffers": "4.5.1", - "System.Diagnostics.DiagnosticSource": "8.0.0", - "System.Memory": "4.5.5" - } - }, - "Microsoft.Extensions.Logging": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "tvRkov9tAJ3xP51LCv3FJ2zINmv1P8Hi8lhhtcKGqM+ImiTCC84uOPEI4z8Cdq2C3o9e+Aa0Gw0rmrsJD77W+w==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "Microsoft.Extensions.DependencyInjection": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0", - "System.Diagnostics.DiagnosticSource": "8.0.0" - } - }, - "Microsoft.Extensions.Logging.Abstractions": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "System.Buffers": "4.5.1", - "System.Memory": "4.5.5" - } - }, - "Microsoft.Extensions.Logging.Configuration": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "ixXXV0G/12g6MXK65TLngYN9V5hQQRuV+fZi882WIoVJT7h5JvoYoxTEwCgdqwLjSneqh1O+66gM8sMr9z/rsQ==", - "dependencies": { - "Microsoft.Extensions.Configuration": "8.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", - "Microsoft.Extensions.Configuration.Binder": "8.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "Microsoft.Extensions.Logging": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "8.0.0" - } - }, - "Microsoft.Extensions.Options": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "JOVOfqpnqlVLUzINQ2fox8evY2SKLYJ3BV8QDe/Jyp21u1T7r45x/R/5QdteURMR5r01GxeJSBBUOCOyaNXA3g==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "Microsoft.Extensions.Primitives": "8.0.0", - "System.ComponentModel.Annotations": "5.0.0" - } - }, - "Microsoft.Extensions.Options.ConfigurationExtensions": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "0f4DMRqEd50zQh+UyJc+/HiBsZ3vhAQALgdkcQEalSH1L2isdC7Yj54M3cyo5e+BeO5fcBQ7Dxly8XiBBcvRgw==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", - "Microsoft.Extensions.Configuration.Binder": "8.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0", - "Microsoft.Extensions.Primitives": "8.0.0" - } - }, - "Microsoft.Extensions.Primitives": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==", - "dependencies": { - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" - }, - "Microsoft.SourceLink.Common": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" - }, - "OpenTelemetry": { - "type": "Transitive", - "resolved": "1.9.0", - "contentHash": "7scS6BUhwYeSXEDGhCxMSezmvyCoDU5kFQbmfyW9iVvVTcWhec+1KIN33/LOCdBXRkzt2y7+g03mkdAB0XZ9Fw==", - "dependencies": { - "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.0", - "Microsoft.Extensions.Logging.Configuration": "8.0.0", - "OpenTelemetry.Api.ProviderBuilderExtensions": "1.9.0" - } - }, - "OpenTelemetry.Api": { - "type": "Transitive", - "resolved": "1.9.0", - "contentHash": "Xz8ZvM1Lm0m7BbtGBnw2JlPo++YKyMp08zMK5p0mf+cIi5jeMt2+QsYu9X6YEAbjCxBQYwEak5Z8sY6Ig2WcwQ==", - "dependencies": { - "System.Diagnostics.DiagnosticSource": "8.0.0" - } - }, - "OpenTelemetry.Api.ProviderBuilderExtensions": { - "type": "Transitive", - "resolved": "1.9.0", - "contentHash": "L0D4LBR5JFmwLun5MCWVGapsJLV0ANZ+XXu9NEI3JE/HRKkRuUO+J2MuHD5DBwiU//QMYYM4B22oev1hVLoHDQ==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "OpenTelemetry.Api": "1.9.0" - } - }, - "System.Buffers": { - "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" - }, - "System.ComponentModel.Annotations": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg==" - }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "8.0.1", - "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==", - "dependencies": { - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "System.Memory": { - "type": "Transitive", - "resolved": "4.5.5", - "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.4.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" - } - }, - "System.Numerics.Vectors": { - "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" - }, - "System.Reflection.TypeExtensions": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "VybpaOQQhqE6siHppMktjfGBw1GCwvCqiufqmP8F1nj7fTUNtW35LOEt3UZTEsECfo+ELAl/9o9nJx3U91i7vA==" - }, - "System.Runtime.CompilerServices.Unsafe": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" - }, - "System.Text.Encodings.Web": { - "type": "Transitive", - "resolved": "4.7.2", - "contentHash": "iTUgB/WtrZ1sWZs84F2hwyQhiRH6QNjQv2DkwrH+WP6RoFga2Q1m3f9/Q7FG8cck8AdHitQkmkXSY8qylcDmuA==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Memory": "4.5.4" - } - }, - "System.Threading.Channels": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "CMaFr7v+57RW7uZfZkPExsPB6ljwzhjACWW1gfU35Y56rk72B/Wu+sTqxVmGSk4SFUlPc3cjeKND0zktziyjBA==", - "dependencies": { - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "System.Threading.Tasks.Extensions": { - "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "4.5.3" - } - }, - "System.Text.Json": { - "type": "CentralTransitive", - "requested": "[5.0.2, )", - "resolved": "4.7.2", - "contentHash": "TcMd95wcrubm9nHvJEQs70rC0H/8omiSGGpU4FQ/ZA1URIqD4pjmFJh2Mfv1yH1eHgJDWTi2hMDXwTET+zOOyg==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "1.1.0", - "System.Buffers": "4.5.1", - "System.Memory": "4.5.4", - "System.Numerics.Vectors": "4.5.0", - "System.Runtime.CompilerServices.Unsafe": "4.7.1", - "System.Text.Encodings.Web": "4.7.1", - "System.Threading.Tasks.Extensions": "4.5.4" - } - } - } - } -} \ No newline at end of file diff --git a/src/Speckle.Sdk/Api/Exceptions.cs b/src/Speckle.Sdk/Api/Exceptions.cs index 165a086b..838b3df1 100644 --- a/src/Speckle.Sdk/Api/Exceptions.cs +++ b/src/Speckle.Sdk/Api/Exceptions.cs @@ -1,54 +1,17 @@ -using GraphQL; -using Speckle.Sdk.Logging; +using Speckle.Sdk.Api.GraphQL; namespace Speckle.Sdk.Api; /// -/// Base class for GraphQL API exceptions +/// The base class for all GraphQL errors (these are errors in the graphql response) +/// Some specific codes are maped to subtypes +/// +/// +/// +/// /// -public class SpeckleGraphQLException : SpeckleGraphQLException -{ - public new GraphQLResponse? Response => (GraphQLResponse?)base.Response; - - public SpeckleGraphQLException( - string message, - GraphQLRequest request, - GraphQLResponse? response, - Exception? innerException = null - ) - : base(message, request, response, innerException) { } - - public SpeckleGraphQLException() { } - - public SpeckleGraphQLException(string? message) - : base(message) { } - - 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 SpeckleGraphQLException( - string? message, - GraphQLRequest request, - IGraphQLResponse? response, - Exception? innerException = null - ) - : base(message, innerException) - { - _request = request; - Response = response; - } - public SpeckleGraphQLException() { } public SpeckleGraphQLException(string? message) @@ -59,19 +22,12 @@ public class SpeckleGraphQLException : SpeckleException } /// -/// Represents a "FORBIDDEN" on "UNAUTHORIZED" GraphQL error as an exception. +/// Represents a "FORBIDDEN" or "UNAUTHORIZED" GraphQL error as an exception. /// https://www.apollographql.com/docs/apollo-server/v2/data/errors/#unauthenticated /// https://www.apollographql.com/docs/apollo-server/v2/data/errors/#forbidden /// -public class SpeckleGraphQLForbiddenException : SpeckleGraphQLException +public sealed class SpeckleGraphQLForbiddenException : SpeckleGraphQLException { - public SpeckleGraphQLForbiddenException( - GraphQLRequest request, - IGraphQLResponse response, - Exception? innerException = null - ) - : base("Your request was forbidden", request, response, innerException) { } - public SpeckleGraphQLForbiddenException() { } public SpeckleGraphQLForbiddenException(string? message) @@ -81,15 +37,12 @@ public class SpeckleGraphQLForbiddenException : SpeckleGraphQLException : base(message, innerException) { } } -public class SpeckleGraphQLInternalErrorException : SpeckleGraphQLException +/// +/// Represents a "INTERNAL_SERVER_ERROR" GraphQL error as an exception. +/// https://www.apollographql.com/docs/apollo-server/v2/data/errors#internal_server_error +/// +public sealed class SpeckleGraphQLInternalErrorException : SpeckleGraphQLException { - 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) @@ -99,15 +52,11 @@ public class SpeckleGraphQLInternalErrorException : SpeckleGraphQLException : base(message, innerException) { } } -public class SpeckleGraphQLStreamNotFoundException : SpeckleGraphQLException +/// +/// Represents the custom "STREAM_NOT_FOUND" GraphQL error as an exception. +/// +public sealed class SpeckleGraphQLStreamNotFoundException : SpeckleGraphQLException { - public SpeckleGraphQLStreamNotFoundException( - GraphQLRequest request, - IGraphQLResponse response, - Exception? innerException = null - ) - : base("Stream not found", request, response, innerException) { } - public SpeckleGraphQLStreamNotFoundException() { } public SpeckleGraphQLStreamNotFoundException(string? message) @@ -116,3 +65,34 @@ public class SpeckleGraphQLStreamNotFoundException : SpeckleGraphQLException public SpeckleGraphQLStreamNotFoundException(string? message, Exception? innerException) : base(message, innerException) { } } + +/// +/// Represents a "BAD_USER_INPUT" GraphQL error as an exception. +/// https://www.apollographql.com/docs/apollo-server/v2/data/errors#bad_user_input +/// +public sealed class SpeckleGraphQLBadInputException : SpeckleGraphQLException +{ + public SpeckleGraphQLBadInputException() { } + + public SpeckleGraphQLBadInputException(string? message) + : base(message) { } + + public SpeckleGraphQLBadInputException(string? message, Exception? innerException) + : base(message, innerException) { } +} + +/// +/// Represents a "GRAPHQL_PARSE_FAILED" or "GRAPHQL_VALIDATION_FAILED" GraphQL error as an exception. +/// https://www.apollographql.com/docs/apollo-server/v2/data/errors#graphql_parse_failed +/// https://www.apollographql.com/docs/apollo-server/v2/data/errors#graphql_validation_failed +/// +public sealed class SpeckleGraphQLInvalidQueryException : SpeckleGraphQLException +{ + public SpeckleGraphQLInvalidQueryException() { } + + public SpeckleGraphQLInvalidQueryException(string? message) + : base(message) { } + + public SpeckleGraphQLInvalidQueryException(string? message, Exception? innerException) + : base(message, innerException) { } +} diff --git a/src/Speckle.Sdk/Api/GraphQL/Client.cs b/src/Speckle.Sdk/Api/GraphQL/Client.cs index d187eae6..d575e494 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Client.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Client.cs @@ -1,23 +1,26 @@ -using System.Diagnostics; -using System.Dynamic; +using System.Diagnostics.CodeAnalysis; using System.Net.WebSockets; using System.Reflection; using GraphQL; using GraphQL.Client.Http; -using Polly; -using Polly.Contrib.WaitAndRetry; +using Microsoft.Extensions.Logging; using Speckle.Newtonsoft.Json; +using Speckle.Newtonsoft.Json.Serialization; using Speckle.Sdk.Api.GraphQL; using Speckle.Sdk.Api.GraphQL.Resources; using Speckle.Sdk.Api.GraphQL.Serializer; using Speckle.Sdk.Credentials; +using Speckle.Sdk.Dependencies; using Speckle.Sdk.Helpers; using Speckle.Sdk.Logging; namespace Speckle.Sdk.Api; -public sealed partial class Client : ISpeckleGraphQLClient, IDisposable +[SuppressMessage("Maintainability", "CA1506:Avoid excessive class coupling", Justification = "Class needs refactor")] +public sealed class Client : ISpeckleGraphQLClient, IDisposable { + private readonly ILogger _logger; + private readonly ISdkActivityFactory _activityFactory; public ProjectResource Project { get; } public ModelResource Model { get; } public VersionResource Version { get; } @@ -27,11 +30,7 @@ public sealed partial class Client : ISpeckleGraphQLClient, IDisposable public CommentResource Comment { get; } public SubscriptionResource Subscription { get; } - public string ServerUrl => Account.serverInfo.url; - - public string ApiToken => Account.token; - - public System.Version? ServerVersion { get; private set; } + public Uri ServerUrl => new(Account.serverInfo.url); [JsonIgnore] public Account Account { get; } @@ -42,8 +41,16 @@ public sealed partial class Client : ISpeckleGraphQLClient, IDisposable /// /// was null - public Client(Account account) + public Client( + ILogger logger, + ISdkActivityFactory activityFactory, + ISpeckleApplication application, + ISpeckleHttp speckleHttp, + Account account + ) { + _logger = logger; + _activityFactory = activityFactory; Account = account ?? throw new ArgumentException("Provided account is null."); Project = new(this); @@ -55,7 +62,7 @@ public sealed partial class Client : ISpeckleGraphQLClient, IDisposable Comment = new(this); Subscription = new(this); - HttpClient = CreateHttpClient(account); + HttpClient = CreateHttpClient(application, speckleHttp, account); GQLClient = CreateGraphQLClient(account, HttpClient); } @@ -65,47 +72,44 @@ public sealed partial class Client : ISpeckleGraphQLClient, IDisposable try { Subscription.Dispose(); - UserStreamAddedSubscription?.Dispose(); - UserStreamRemovedSubscription?.Dispose(); - StreamUpdatedSubscription?.Dispose(); - BranchCreatedSubscription?.Dispose(); - BranchUpdatedSubscription?.Dispose(); - BranchDeletedSubscription?.Dispose(); - CommitCreatedSubscription?.Dispose(); - CommitUpdatedSubscription?.Dispose(); - CommitDeletedSubscription?.Dispose(); - CommentActivitySubscription?.Dispose(); GQLClient.Dispose(); } catch (Exception ex) when (!ex.IsFatal()) { } } - internal async Task ExecuteWithResiliencePolicies(Func> func) - { - var delay = Backoff.DecorrelatedJitterBackoffV2(TimeSpan.FromSeconds(1), 5); - var graphqlRetry = Policy - .Handle() - .WaitAndRetryAsync( - delay, - (ex, 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); - } + internal async Task ExecuteWithResiliencePolicies(Func> func) => + await GraphQLRetry + .ExecuteAsync( + func, + ( + (ex, timeout) => + { + _logger.LogDebug( + ex, + "The previous attempt at executing function to get {resultType} failed with {exceptionMessage}. Retrying after {timeout}", + typeof(T).Name, + ex.Message, + timeout + ); + } + ) + ) + .ConfigureAwait(false); /// public async Task ExecuteGraphQLRequest(GraphQLRequest request, CancellationToken cancellationToken = default) { - using var activity = SpeckleActivityFactory.Start(); + using var activity = _activityFactory.Start(); + activity?.SetTag("responseType", typeof(T)); + activity?.SetTag("request.query", request.Query); + activity?.SetTag("request.operationName", request.OperationName); + activity?.SetTag("request.variables", request.Variables); + activity?.SetTag("request.extensions", request.Extensions); + activity?.SetTag("clientOptions.endPoint", GQLClient.Options.EndPoint); + activity?.SetTag("clientOptions.medaType", GQLClient.Options.MediaType); + activity?.SetTag("clientOptions.webSocketEndPoint", GQLClient.Options.WebSocketEndPoint); + activity?.SetTag("clientOptions.webSocketProtocol", GQLClient.Options.WebSocketProtocol); + try { var ret = await ExecuteWithResiliencePolicies(async () => @@ -113,185 +117,63 @@ public sealed partial class Client : ISpeckleGraphQLClient, IDisposable GraphQLResponse result = await GQLClient .SendMutationAsync(request, cancellationToken) .ConfigureAwait(false); - MaybeThrowFromGraphQLErrors(request, result); + result.EnsureGraphQLSuccess(); return result.Data; }) .ConfigureAwait(false); - activity?.SetStatus(SpeckleActivityStatusCode.Ok); + activity?.SetStatus(SdkActivityStatusCode.Ok); return ret; } - catch (Exception e) + catch (Exception ex) { - activity?.SetStatus(SpeckleActivityStatusCode.Error); - activity?.RecordException(e); + activity?.SetStatus(SdkActivityStatusCode.Error); + activity?.RecordException(ex); throw; } } - internal void MaybeThrowFromGraphQLErrors(GraphQLRequest request, GraphQLResponse response) - { - // The errors reflect the Apollo server v2 API, which is deprecated. It is bound to change, - // once we migrate to a newer version. - var errors = response.Errors; - if (errors != null && errors.Length != 0) - { - if ( - errors.Any(e => - e.Extensions != null - && ( - e.Extensions.Contains(new KeyValuePair("code", "FORBIDDEN")) - || e.Extensions.Contains(new KeyValuePair("code", "UNAUTHENTICATED")) - ) - ) - ) - { - throw new SpeckleGraphQLForbiddenException(request, response); - } - - if ( - errors.Any(e => - e.Extensions != null && e.Extensions.Contains(new KeyValuePair("code", "STREAM_NOT_FOUND")) - ) - ) - { - throw new SpeckleGraphQLStreamNotFoundException(request, response); - } - - if ( - errors.Any(e => - e.Extensions != null - && e.Extensions.Contains(new KeyValuePair("code", "INTERNAL_SERVER_ERROR")) - ) - ) - { - throw new SpeckleGraphQLInternalErrorException(request, response); - } - - throw new SpeckleGraphQLException("Request failed with errors", request, response); - } - } - - private Dictionary ConvertExpandoToDict(ExpandoObject expando) - { - var variables = new Dictionary(); - foreach (KeyValuePair kvp in expando) - { - object value; - if (kvp.Value is ExpandoObject ex) - { - value = ConvertExpandoToDict(ex); - } - else - { - value = kvp.Value; - } - - variables[kvp.Key] = value; - } - return variables; - } - - /* private ILogEventEnricher[] CreateEnrichers(GraphQLRequest request) - { - // i know this is double (de)serializing, but we need a recursive convert to - // dict here - var expando = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(request.Variables)); - var variables = request.Variables != null && expando != null ? ConvertExpandoToDict(expando) : null; - return new ILogEventEnricher[] - { - new PropertyEnricher("serverUrl", ServerUrl), - new PropertyEnricher("graphqlQuery", request.Query), - new PropertyEnricher("graphqlVariables", variables), - new PropertyEnricher("resultType", typeof(T).Name) - }; - }*/ - IDisposable ISpeckleGraphQLClient.SubscribeTo(GraphQLRequest request, Action callback) => SubscribeTo(request, callback); /// - internal IDisposable SubscribeTo(GraphQLRequest request, Action callback) + private IDisposable SubscribeTo(GraphQLRequest request, Action callback) { - //using (LogContext.Push(CreateEnrichers(request))) + try { - try - { - var res = GQLClient.CreateSubscriptionStream(request); - return res.Subscribe( - response => + var res = GQLClient.CreateSubscriptionStream(request); + return res.Subscribe( + response => + { + try { - try - { - MaybeThrowFromGraphQLErrors(request, response); + response.EnsureGraphQLSuccess(); - if (response.Data != null) - { - callback(this, response.Data); - } - else - { - // Serilog.Log.ForContext("graphqlResponse", response) - SpeckleLog.Logger.Error( - "Cannot execute graphql callback for {resultType}, the response has no data.", - typeof(T).Name - ); - } - } - // we catch forbidden to rethrow, making sure its not logged. - catch (SpeckleGraphQLForbiddenException) - { - throw; - } - // anything else related to graphql gets logged - catch (SpeckleGraphQLException gqlException) - { - /* Speckle.Sdk.Logging..ForContext("graphqlResponse", gqlException.Response) - .ForContext("graphqlExtensions", gqlException.Extensions) - .ForContext("graphqlErrorMessages", gqlException.ErrorMessages.ToList())*/ - SpeckleLog.Logger.Warning( - gqlException, - "Execution of the graphql request to get {resultType} failed with {graphqlExceptionType} {exceptionMessage}.", - typeof(T).Name, - gqlException.GetType().Name, - gqlException.Message - ); - throw; - } - // we're not handling the bare Exception type here, - // since we have a response object on the callback, we know the Exceptions - // can only be thrown from the MaybeThrowFromGraphQLErrors which wraps - // every exception into SpeckleGraphQLException - }, - ex => - { - // we're logging this as an error for now, to keep track of failures - // so far we've swallowed these errors - SpeckleLog.Logger.Error( - ex, - "Subscription for {resultType} terminated unexpectedly with {exceptionMessage}", - typeof(T).Name, - ex.Message - ); - // we could be throwing like this: - // throw ex; + callback(this, response.Data); } - ); - } - catch (Exception ex) when (!ex.IsFatal()) - { - SpeckleLog.Logger.Warning( - ex, - "Subscribing to graphql {resultType} failed without a graphql response. Cause {exceptionMessage}", - typeof(T).Name, - ex.Message - ); - throw new SpeckleGraphQLException( - "The graphql request failed without a graphql response", - request, - null, - ex - ); - } + catch (AggregateException ex) + { + _logger.LogWarning(ex, "Subscription for {type} got a response with errors", typeof(T).Name); + throw; + } + }, + ex => + { + // we're logging this as an error for now, to keep track of failures + // so far we've swallowed these errors + _logger.LogError( + ex, + "Subscription for {resultType} terminated unexpectedly with {exceptionMessage}", + typeof(T).Name, + ex.Message + ); + // we could be throwing like this: + // throw ex; + } + ); + } + catch (Exception ex) when (!ex.IsFatal() && ex is not ObjectDisposedException) + { + throw new SpeckleGraphQLException($"Subscription for {typeof(T)} failed to start", ex); } } @@ -305,10 +187,23 @@ public sealed partial class Client : ISpeckleGraphQLClient, IDisposable WebSocketProtocol = "graphql-ws", ConfigureWebSocketConnectionInitPayload = _ => { - return Http.CanAddAuth(account.token, out string? authValue) ? new { Authorization = authValue } : null; + return SpeckleHttp.CanAddAuth(account.token, out string? authValue) + ? new { Authorization = authValue } + : null; }, }, - new NewtonsoftJsonSerializer(), + new NewtonsoftJsonSerializer( + new JsonSerializerSettings() + { + ContractResolver = new CamelCasePropertyNamesContractResolver { IgnoreIsSpecifiedMembers = true }, //(Default) + MissingMemberHandling = MissingMemberHandling.Error, //(not default) If you query for a member that doesn't exist, this will throw (except websocket responses see https://github.com/graphql-dotnet/graphql-client/issues/660) + Converters = + { + new ConstantCaseEnumConverter(), + } //(Default) enums will be serialized using the GraphQL const case standard + , + } + ), httpClient ); @@ -328,17 +223,14 @@ public sealed partial class Client : ISpeckleGraphQLClient, IDisposable return gQLClient; } - private static HttpClient CreateHttpClient(Account account) + private static HttpClient CreateHttpClient(ISpeckleApplication application, ISpeckleHttp speckleHttp, Account account) { - var httpClient = Http.GetHttpProxyClient( - new SpeckleHttpClientHandler(new HttpClientHandler(), Http.HttpAsyncPolicy(timeoutSeconds: 30)) - ); - Http.AddAuthHeader(httpClient, account.token); + var httpClient = speckleHttp.CreateHttpClient(timeoutSeconds: 30, authorizationToken: account.token); - httpClient.DefaultRequestHeaders.Add("apollographql-client-name", Setup.ApplicationVersion); + httpClient.DefaultRequestHeaders.Add("apollographql-client-name", application.ApplicationAndVersion); httpClient.DefaultRequestHeaders.Add( "apollographql-client-version", - Assembly.GetExecutingAssembly().GetName().Version.ToString() + Assembly.GetExecutingAssembly().GetName().Version?.ToString() ); return httpClient; } diff --git a/src/Speckle.Sdk/Api/GraphQL/ClientFactory.cs b/src/Speckle.Sdk/Api/GraphQL/ClientFactory.cs new file mode 100644 index 00000000..c2af3e78 --- /dev/null +++ b/src/Speckle.Sdk/Api/GraphQL/ClientFactory.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.Logging; +using Speckle.InterfaceGenerator; +using Speckle.Sdk.Credentials; +using Speckle.Sdk.Helpers; +using Speckle.Sdk.Logging; + +namespace Speckle.Sdk.Api; + +[GenerateAutoInterface] +public class ClientFactory( + ILoggerFactory loggerFactory, + ISdkActivityFactory activityFactory, + ISpeckleApplication application, + ISpeckleHttp speckleHttp +) : IClientFactory +{ + public Client Create(Account account) => + new(loggerFactory.CreateLogger(), activityFactory, application, speckleHttp, account); +} diff --git a/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectVisibility.cs b/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectVisibility.cs index 3f2fdb11..5e4b1440 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectVisibility.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Enums/ProjectVisibility.cs @@ -4,5 +4,5 @@ public enum ProjectVisibility { Private, Public, - Unlisted + Unlisted, } diff --git a/src/Speckle.Sdk/Api/GraphQL/Enums/ResourceType.cs b/src/Speckle.Sdk/Api/GraphQL/Enums/ResourceType.cs index 4358f927..d592e308 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Enums/ResourceType.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Enums/ResourceType.cs @@ -5,5 +5,5 @@ public enum ResourceType commit, stream, @object, - comment + comment, } diff --git a/src/Speckle.Sdk/Api/GraphQL/GraphQLErrorHandler.cs b/src/Speckle.Sdk/Api/GraphQL/GraphQLErrorHandler.cs new file mode 100644 index 00000000..5fff1eea --- /dev/null +++ b/src/Speckle.Sdk/Api/GraphQL/GraphQLErrorHandler.cs @@ -0,0 +1,49 @@ +using System.Diagnostics.Contracts; +using GraphQL; + +namespace Speckle.Sdk.Api.GraphQL; + +internal static class GraphQLErrorHandler +{ + /// + public static void EnsureGraphQLSuccess(this IGraphQLResponse response) => EnsureGraphQLSuccess(response.Errors); + + /// Containing a (or subclass of) for each graphql Error + public static void EnsureGraphQLSuccess(IReadOnlyCollection? errors) + { + // The errors reflect the Apollo server v2 API, which is deprecated. It is bound to change, + // once we migrate to a newer version. + if (errors == null || errors.Count == 0) + { + return; + } + + List exceptions = new(errors.Count); + foreach (var error in errors) + { + object? code = null; + _ = error.Extensions?.TryGetValue("code", out code); + + var message = FormatErrorMessage(error, code); + var ex = code switch + { + "GRAPHQL_PARSE_FAILED" or "GRAPHQL_VALIDATION_FAILED" => new SpeckleGraphQLInvalidQueryException(message), + "FORBIDDEN" or "UNAUTHENTICATED" => new SpeckleGraphQLForbiddenException(message), + "STREAM_NOT_FOUND" => new SpeckleGraphQLStreamNotFoundException(message), + "BAD_USER_INPUT" => new SpeckleGraphQLBadInputException(message), + "INTERNAL_SERVER_ERROR" => new SpeckleGraphQLInternalErrorException(message), + _ => new SpeckleGraphQLException(message), + }; + exceptions.Add(ex); + } + + throw new AggregateException("Request failed with GraphQL errors, see inner exceptions", exceptions); + } + + [Pure] + private static string FormatErrorMessage(GraphQLError error, object? code) + { + code ??= "ERROR"; + return $"{code}: {error.Message}"; + } +} diff --git a/src/Speckle.Sdk/Api/GraphQL/GraphQLHttpClientExtensions.cs b/src/Speckle.Sdk/Api/GraphQL/GraphQLHttpClientExtensions.cs index 7e24cc96..9fe5c7d5 100644 --- a/src/Speckle.Sdk/Api/GraphQL/GraphQLHttpClientExtensions.cs +++ b/src/Speckle.Sdk/Api/GraphQL/GraphQLHttpClientExtensions.cs @@ -9,46 +9,51 @@ public static class GraphQLHttpClientExtensions /// /// Gets the version of the current server. Useful for guarding against unsupported api calls on newer or older servers. /// - /// [Optional] defaults to an empty cancellation token - /// object excluding any strings (eg "2.7.2-alpha.6995" becomes "2.7.2.6995") - /// + /// + /// Expects the response to either be
+ /// - 1. The literal string dev, which will return 999.999.999
+ /// - 2. A 3 numeral semver (anything after the first - character will be ignored)
+ ///
+ /// + /// A 3 numeral object (e.g. 2.21.3.alpha123 becomes 2.21.3) + /// + /// Server responded with a server version, but it was not in an expected format public static async Task GetServerVersion( this GraphQLHttpClient client, CancellationToken cancellationToken = default ) { - var request = new GraphQLRequest - { - Query = - @"query Server { - serverInfo { - version - } - }" - }; + //lang=graphql + const string QUERY = """ + query Server { + data:serverInfo { + data:version + } + } + """; + var request = new GraphQLRequest { Query = QUERY }; - var response = await client.SendQueryAsync(request, cancellationToken).ConfigureAwait(false); + var response = await client + .SendQueryAsync>>(request, cancellationToken) + .ConfigureAwait(false); - if (response.Errors != null) + response.EnsureGraphQLSuccess(); + + string versionString = response.Data.data.data; + if (versionString == "dev") { - throw new SpeckleGraphQLException( - $"Query {nameof(GetServerVersion)} failed", - request, - response - ); + return new Version(999, 999, 999); } - if (string.IsNullOrWhiteSpace(response.Data.serverInfo.version)) - { - throw new SpeckleGraphQLException( - $"Query {nameof(GetServerVersion)} did not provide a valid server version", - request, - response - ); - } + string? semverString = versionString.Split('-').First(); - return response.Data.serverInfo.version == "dev" - ? new System.Version(999, 999, 999) - : new System.Version(response.Data.serverInfo.version.Split('-').First()); + if (Version.TryParse(semverString!, out Version? semver)) + { + return semver; + } + else + { + throw new FormatException($"Server responded with an invalid semver string \"{semverString}\""); + } } } diff --git a/src/Speckle.Sdk/Api/GraphQL/ISpeckleGraphQLClient.cs b/src/Speckle.Sdk/Api/GraphQL/ISpeckleGraphQLClient.cs index 8d0d99ec..49e46b27 100644 --- a/src/Speckle.Sdk/Api/GraphQL/ISpeckleGraphQLClient.cs +++ b/src/Speckle.Sdk/Api/GraphQL/ISpeckleGraphQLClient.cs @@ -1,17 +1,20 @@ using GraphQL; +using GraphQL.Client.Http; +using Speckle.Newtonsoft.Json; namespace Speckle.Sdk.Api.GraphQL; internal interface ISpeckleGraphQLClient { - /// "FORBIDDEN" on "UNAUTHORIZED" response from server - /// All other request errors + /// Request failed on the GraphQL layer, each GraphQL error will be a (or subclass of) as an inner exception + /// Request failed on the HTTP layer (non-successful response code) + /// The request failed due to an underlying issue such as network connectivity, DNS failure, server certificate validation or timeout /// The requested a cancel /// This already been disposed + /// The response failed to deserialize, probably because the server version is incompatible with this version of the SDK, or there is a mistake in a query (queried for a property that isn't in the C# model, or a required property was null) internal Task ExecuteGraphQLRequest(GraphQLRequest request, CancellationToken cancellationToken); - /// "FORBIDDEN" on "UNAUTHORIZED" response from server - /// All other request errors + /// Containing a (or subclass of) for each graphql Error /// This already been disposed internal IDisposable SubscribeTo(GraphQLRequest request, Action callback); } diff --git a/src/Speckle.Sdk/Api/GraphQL/Inputs/CommentInputs.cs b/src/Speckle.Sdk/Api/GraphQL/Inputs/CommentInputs.cs index ea217354..057f3058 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Inputs/CommentInputs.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Inputs/CommentInputs.cs @@ -1,6 +1,8 @@ namespace Speckle.Sdk.Api.GraphQL.Inputs; -public sealed record CreateCommentInput( +internal sealed record CommentContentInput(IReadOnlyCollection? blobIds, object? doc); + +internal sealed record CreateCommentInput( CommentContentInput content, string projectId, string resourceIdString, @@ -8,8 +10,10 @@ public sealed record CreateCommentInput( object? viewerState ); -public sealed record EditCommentInput(CommentContentInput content, string commentId); +internal sealed record EditCommentInput(CommentContentInput content, string commentId, string projectId); -public sealed record CreateCommentReplyInput(CommentContentInput content, string threadId); +internal sealed record CreateCommentReplyInput(CommentContentInput content, string threadId, string projectId); -public sealed record CommentContentInput(IReadOnlyCollection? blobIds, object? doc); +public sealed record MarkCommentViewedInput(string commentId, string projectId); + +public sealed record ArchiveCommentInput(string commentId, string projectId, bool archived = true); diff --git a/src/Speckle.Sdk/Api/GraphQL/Inputs/ProjectInputs.cs b/src/Speckle.Sdk/Api/GraphQL/Inputs/ProjectInputs.cs index 00e6a8d1..7a660be0 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Inputs/ProjectInputs.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Inputs/ProjectInputs.cs @@ -11,18 +11,12 @@ public sealed record ProjectInviteCreateInput(string? email, string? role, strin 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 + IReadOnlyList? contributors = null, + IReadOnlyList? excludeIds = null, + IReadOnlyList? ids = null, + bool? onlyWithVersions = null, + string? search = null, + IReadOnlyList? sourceApps = null ); public sealed record ProjectUpdateInput( diff --git a/src/Speckle.Sdk/Api/GraphQL/Inputs/UserInputs.cs b/src/Speckle.Sdk/Api/GraphQL/Inputs/UserInputs.cs new file mode 100644 index 00000000..58a86b96 --- /dev/null +++ b/src/Speckle.Sdk/Api/GraphQL/Inputs/UserInputs.cs @@ -0,0 +1,8 @@ +namespace Speckle.Sdk.Api.GraphQL.Inputs; + +public sealed record UserUpdateInput( + string? avatar = null, + string? bio = null, + string? company = null, + string? name = null +); diff --git a/src/Speckle.Sdk/Api/GraphQL/Inputs/VersionInputs.cs b/src/Speckle.Sdk/Api/GraphQL/Inputs/VersionInputs.cs index a9690796..af694dac 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Inputs/VersionInputs.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Inputs/VersionInputs.cs @@ -1,10 +1,10 @@ namespace Speckle.Sdk.Api.GraphQL.Inputs; -public sealed record UpdateVersionInput(string versionId, string? message); +public sealed record UpdateVersionInput(string versionId, string projectId, string? message); -public sealed record MoveVersionsInput(string targetModelName, IReadOnlyList versionIds); +public sealed record MoveVersionsInput(string projectId, string targetModelName, IReadOnlyList versionIds); -public sealed record DeleteVersionsInput(IReadOnlyList versionIds); +public sealed record DeleteVersionsInput(IReadOnlyList versionIds, string projectId); public sealed record CreateVersionInput( string objectId, diff --git a/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ActivityOperations.cs b/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ActivityOperations.cs deleted file mode 100644 index daa3130b..00000000 --- a/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ActivityOperations.cs +++ /dev/null @@ -1,64 +0,0 @@ -using GraphQL; - -namespace Speckle.Sdk.Api; - -public partial class Client -{ - //TODO: API gap - /// - /// Gets the activity of a stream - /// - /// Id of the stream to get the activity from - /// Only show activity after this DateTime - /// Only show activity before this DateTime - /// Time to filter the activity with - /// Time to filter the activity with - /// Max number of activity items to get - /// - /// - public async Task> StreamGetActivity( - string id, - DateTime? after = null, - DateTime? before = null, - DateTime? cursor = null, - string actionType = "", - int limit = ServerLimits.DEFAULT_PAGINATION_REQUEST, - CancellationToken cancellationToken = default - ) - { - var request = new GraphQLRequest - { - Query = - @"query Stream($id: String!, $before: DateTime,$after: DateTime, $cursor: DateTime, $activity: String, $limit: Int!) { - stream(id: $id) { - activity (actionType: $activity, after: $after, before: $before, cursor: $cursor, limit: $limit) { - totalCount - cursor - items { - actionType - userId - streamId - resourceId - resourceType - time - info - message - } - } - } - }", - Variables = new - { - id, - limit, - actionType, - after, - before, - cursor - } - }; - - var res = await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false); - return res.stream.activity.items; - } -} diff --git a/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.BranchOperations.cs b/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.BranchOperations.cs deleted file mode 100644 index ff1921b1..00000000 --- a/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.BranchOperations.cs +++ /dev/null @@ -1,238 +0,0 @@ -using GraphQL; -using Speckle.Sdk.Api.GraphQL.Resources; - -namespace Speckle.Sdk.Api; - -public partial class Client -{ - /// - /// Get branches from a given stream, first with a max of 500 and then with a max of 100. - /// This ensures that if the server API is limiting to 100 branches, that any failure will try again at the lower value. - /// - /// 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; - try - { - branches = await StreamGetBranches(streamId, ServerLimits.BRANCH_GET_LIMIT, commitsLimit).ConfigureAwait(true); - } - catch (SpeckleGraphQLException) - { - branches = await StreamGetBranches(streamId, ServerLimits.OLD_BRANCH_GET_LIMIT, commitsLimit) - .ConfigureAwait(true); - } - - return branches; - } - - /// - /// Get branches from a given stream - /// - /// Id of the stream to get the branches from - /// Max number of branches to retrieve - /// Max number of commits to retrieve - /// - /// - /// - /// - [Obsolete($"Use client.{nameof(Model)}.{nameof(ModelResource.GetModels)}")] - public async Task> StreamGetBranches( - string streamId, - int branchesLimit = 10, - int commitsLimit = 10, - CancellationToken cancellationToken = default - ) - { - var request = new GraphQLRequest - { - Query = - $@"query Stream ($streamId: String!) {{ - stream(id: $streamId) {{ - branches(limit: {branchesLimit}) {{ - items {{ - id - name - description - commits (limit: {commitsLimit}) {{ - totalCount - cursor - items {{ - id - referencedObject - sourceApplication - message - authorName - authorId - branchName - parents - createdAt - }} - }} - }} - }} - }} - }}", - Variables = new { streamId } - }; - var res = await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false); - return res.stream.branches.items; - } - - /// - /// Creates a branch on a stream. - /// - /// - /// - /// The branch id. - /// - [Obsolete($"Use client.{nameof(Model)}.{nameof(ModelResource.Create)}")] - public async Task BranchCreate(BranchCreateInput branchInput, CancellationToken cancellationToken = default) - { - var request = new GraphQLRequest - { - Query = @"mutation branchCreate($myBranch: BranchCreateInput!){ branchCreate(branch: $myBranch)}", - Variables = new { myBranch = branchInput } - }; - - var res = await ExecuteGraphQLRequest>(request, cancellationToken).ConfigureAwait(false); - return (string)res["branchCreate"]; - } - - /// - /// Gets a given branch from a stream. - /// - /// Id of the stream to get the branch from - /// 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, - int commitsLimit = 10, - CancellationToken cancellationToken = default - ) - { - var request = new GraphQLRequest - { - Query = - $@"query Stream($streamId: String!, $branchName: String!) {{ - stream(id: $streamId) {{ - branch(name: $branchName){{ - id, - name, - description, - commits (limit: {commitsLimit}) {{ - totalCount, - cursor, - items {{ - id, - referencedObject, - sourceApplication, - totalChildrenCount, - message, - authorName, - authorId, - branchName, - parents, - createdAt - }} - }} - }} - }} - }}", - Variables = new { streamId, branchName } - }; - - var res = await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false); - return res.stream.branch; - } - - /// - /// Gets a given model from a project. - /// - /// - /// 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 - { - Query = - $@"query ProjectModel($projectId: String!, $modelId: String!) {{ - project(id: $projectId) {{ - model(id: $modelId){{ - id, - name, - description - }} - }} - }}", - Variables = new { projectId, modelId } - }; - - var res = await ExecuteGraphQLRequest>>>( - request, - cancellationToken - ) - .ConfigureAwait(false); - var branch = new Branch - { - description = res["project"]["model"]["description"], - id = res["project"]["model"]["id"], - name = res["project"]["model"]["name"] - }; - return branch; - } - - /// - /// Updates a branch. - /// - /// - /// 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 - { - Query = @"mutation branchUpdate($myBranch: BranchUpdateInput!){ branchUpdate(branch: $myBranch)}", - Variables = new { myBranch = branchInput } - }; - - var res = await ExecuteGraphQLRequest>(request, cancellationToken).ConfigureAwait(false); - return (bool)res["branchUpdate"]; - } - - /// - /// Deletes a stream. - /// - /// - /// - /// - /// - [Obsolete($"Use client.{nameof(Model)}.{nameof(ModelResource.Delete)}")] - public async Task BranchDelete(BranchDeleteInput branchInput, CancellationToken cancellationToken = default) - { - var request = new GraphQLRequest - { - Query = @"mutation branchDelete($myBranch: BranchDeleteInput!){ branchDelete(branch: $myBranch)}", - Variables = new { myBranch = branchInput } - }; - - var res = await ExecuteGraphQLRequest>(request, cancellationToken).ConfigureAwait(false); - return (bool)res["branchDelete"]; - } -} diff --git a/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.CommentOperations.cs b/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.CommentOperations.cs deleted file mode 100644 index e71ee87e..00000000 --- a/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.CommentOperations.cs +++ /dev/null @@ -1,106 +0,0 @@ -using GraphQL; -using Speckle.Sdk.Api.GraphQL.Resources; - -namespace Speckle.Sdk.Api; - -public partial class Client -{ - /// - /// Gets the comments on a Stream - /// - /// Id of the stream to get the comments from - /// The number of comments to get - /// Time to filter the comments with - /// - /// - /// - [Obsolete($"Use client.{nameof(CommentResource)}.{nameof(CommentResource.GetProjectComments)}")] - public async Task StreamGetComments( - string streamId, - int limit = 25, - string? cursor = null, - CancellationToken cancellationToken = default - ) - { - var request = new GraphQLRequest - { - Query = - @"query Comments($streamId: String!, $cursor: String, $limit: Int!) { - comments(streamId: $streamId, cursor: $cursor, limit: $limit) { - totalCount - cursor - items { - id - authorId - archived - rawText - data - createdAt - updatedAt - viewedAt - reactions - resources { - resourceId - resourceType - } - replies { - totalCount - cursor - items { - id - authorId - archived - rawText - data - createdAt - updatedAt - viewedAt - } - } - } - } - }", - Variables = new - { - streamId, - cursor, - limit - } - }; - - var res = await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false); - return res.comments; - } - - /// - /// Gets the screenshot of a Comment - /// - /// Id of the comment - /// 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, - CancellationToken cancellationToken = default - ) - { - var request = new GraphQLRequest - { - Query = - @"query Comment($id: String!, $streamId: String!) { - comment(id: $id, streamId: $streamId) { - id - screenshot - } - } - ", - Variables = new { id, streamId } - }; - - var res = await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false); - return res.comment.screenshot; - } -} diff --git a/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.CommitOperations.cs b/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.CommitOperations.cs deleted file mode 100644 index 4d91be15..00000000 --- a/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.CommitOperations.cs +++ /dev/null @@ -1,171 +0,0 @@ -using GraphQL; -using Speckle.Sdk.Api.GraphQL.Resources; - -namespace Speckle.Sdk.Api; - -public partial class Client -{ - /// - /// Gets a given commit from a stream. - /// - /// Id of the stream to get the commit from - /// 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 - { - Query = - @"query Stream($streamId: String!, $commitId: String!) { - stream(id: $streamId) { - commit(id: $commitId){ - id, - message, - sourceApplication, - totalChildrenCount, - referencedObject, - branchName, - createdAt, - parents, - authorName - } - } - }", - Variables = new { streamId, commitId } - }; - - var res = await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false); - return res.stream.commit; - } - - /// - /// Gets the latest commits from a stream - /// - /// Id of the stream to get the commits from - /// 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, - CancellationToken cancellationToken = default - ) - { - var request = new GraphQLRequest - { - Query = - @"query Stream($streamId: String!, $limit: Int!) { - stream(id: $streamId) { - commits(limit: $limit) { - items { - id, - message, - branchName, - sourceApplication, - totalChildrenCount, - referencedObject, - createdAt, - parents, - authorName, - authorId, - authorAvatar - } - } - } - }", - Variables = new { streamId, limit } - }; - - var res = await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false); - return res.stream.commits.items; - } - - /// - /// Creates a commit on a branch. - /// - /// - /// The commit id. - /// - [Obsolete($"Use client.{nameof(VersionResource)}.{nameof(VersionResource.Create)}")] - public async Task CommitCreate(CommitCreateInput commitInput, CancellationToken cancellationToken = default) - { - var request = new GraphQLRequest - { - Query = @"mutation commitCreate($myCommit: CommitCreateInput!){ commitCreate(commit: $myCommit)}", - Variables = new { myCommit = commitInput } - }; - - var res = await ExecuteGraphQLRequest>(request, cancellationToken).ConfigureAwait(false); - return (string)res["commitCreate"]; - } - - /// - /// Updates a commit. - /// - /// - /// - /// 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 - { - Query = @"mutation commitUpdate($myCommit: CommitUpdateInput!){ commitUpdate(commit: $myCommit)}", - Variables = new { myCommit = commitInput } - }; - - var res = await ExecuteGraphQLRequest>(request, cancellationToken).ConfigureAwait(false); - return (bool)res["commitUpdate"]; - } - - /// - /// Deletes a commit. - /// - /// - /// - /// - /// - [Obsolete($"Use client.{nameof(VersionResource)}.{nameof(VersionResource.Delete)}")] - public async Task CommitDelete(CommitDeleteInput commitInput, CancellationToken cancellationToken = default) - { - var request = new GraphQLRequest - { - Query = @"mutation commitDelete($myCommit: CommitDeleteInput!){ commitDelete(commit: $myCommit)}", - Variables = new { myCommit = commitInput } - }; - - var res = await ExecuteGraphQLRequest>(request, cancellationToken).ConfigureAwait(false); - return (bool)res["commitDelete"]; - } - - /// - /// Sends a commitReceived mutation, affirming a commit has been received. - /// - /// Used for read receipts - /// - /// - /// - /// - [Obsolete($"Use client.{nameof(VersionResource)}.{nameof(VersionResource.Received)}")] - public async Task CommitReceived( - CommitReceivedInput commitReceivedInput, - CancellationToken cancellationToken = default - ) - { - var request = new GraphQLRequest - { - Query = @"mutation($myInput:CommitReceivedInput!){ commitReceive(input:$myInput) }", - Variables = new { myInput = commitReceivedInput } - }; - - var res = await ExecuteGraphQLRequest>(request, cancellationToken).ConfigureAwait(false); - - return (bool)res["commitReceive"]; - } -} diff --git a/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ObjectOperations.cs b/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ObjectOperations.cs deleted file mode 100644 index 2e900988..00000000 --- a/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ObjectOperations.cs +++ /dev/null @@ -1,69 +0,0 @@ -using GraphQL; - -namespace Speckle.Sdk.Api; - -public partial class Client -{ - /// - /// Gets data about the requested Speckle object from a stream. - /// - /// Id of the stream to get the object from - /// Id of the object to get - /// - /// - public async Task ObjectGet( - string streamId, - string objectId, - CancellationToken cancellationToken = default - ) - { - var request = new GraphQLRequest - { - Query = - @"query Stream($streamId: String!, $objectId: String!) { - stream(id: $streamId) { - object(id: $objectId){ - id - applicationId - createdAt - totalChildrenCount - } - } - }", - Variables = new { streamId, objectId } - }; - - var res = await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false); - return res.stream.@object; - } - - /// - /// Gets a given object from a stream. - /// - /// - /// - /// - /// - public async Task ObjectCountGet( - string streamId, - string objectId, - CancellationToken cancellationToken = default - ) - { - var request = new GraphQLRequest - { - Query = - @"query Stream($streamId: String!, $objectId: String!) { - stream(id: $streamId) { - object(id: $objectId){ - totalChildrenCount - } - } - }", - Variables = new { streamId, objectId } - }; - - var res = await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false); - return res.stream.@object; - } -} diff --git a/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ServerOperations.cs b/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ServerOperations.cs deleted file mode 100644 index 702353bf..00000000 --- a/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.ServerOperations.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Text.RegularExpressions; -using GraphQL; -using Speckle.Sdk.Api.GraphQL.Models.Responses; -using Speckle.Sdk.Logging; - -namespace Speckle.Sdk.Api; - -public partial class Client -{ - /// - /// Gets the version of the current server. Useful for guarding against unsupported api calls on newer or older servers. - /// - /// [Optional] defaults to an empty cancellation token - /// object excluding any strings (eg "2.7.2-alpha.6995" becomes "2.7.2.6995") - /// - [Obsolete("Use GraphQLHttpClient.GetServerVersion instead")] - public async Task GetServerVersion(CancellationToken cancellationToken = default) - { - var request = new GraphQLRequest - { - Query = - @"query Server { - serverInfo { - version - } - }" - }; - - var res = await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false); - - if (res.serverInfo.version.Contains("dev")) - { - return new System.Version(999, 999, 999); - } - - ServerVersion = new System.Version(Regex.Replace(res.serverInfo.version, "[-a-zA-Z]+", "")); - return ServerVersion; - } -} diff --git a/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.StreamOperations.cs b/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.StreamOperations.cs deleted file mode 100644 index aaeaec32..00000000 --- a/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.StreamOperations.cs +++ /dev/null @@ -1,543 +0,0 @@ -#nullable disable -using GraphQL; -using Speckle.Sdk.Api.GraphQL.Models; -using Speckle.Sdk.Api.GraphQL.Models.Responses; -using Speckle.Sdk.Api.GraphQL.Resources; -using Speckle.Sdk.Logging; - -namespace Speckle.Sdk.Api; - -public partial class Client -{ - /// - /// Checks if a stream exists by id. - /// - /// Id of the stream to get - /// - /// - public async Task IsStreamAccessible(string id, CancellationToken cancellationToken = default) - { - try - { - var request = new GraphQLRequest - { - Query = - $@"query Stream($id: String!) {{ - stream(id: $id) {{ - id - }} - }}", - Variables = new { id } - }; - var stream = (await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false)).stream; - - return stream.id == id; - } - catch (SpeckleGraphQLForbiddenException) - { - return false; - } - catch (SpeckleGraphQLStreamNotFoundException) - { - return false; - } - } - - /// - /// Gets a stream by id including basic branch info (id, name, description, and total commit count). - /// For detailed commit and branch info, use and respectively. - /// - /// Id of the stream to get - /// 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 - { - Query = - $@"query Stream($id: String!) {{ - stream(id: $id) {{ - id - name - description - isPublic - role - createdAt - updatedAt - commentCount - favoritedDate - favoritesCount - collaborators {{ - id - name - role - avatar - }}, - branches (limit: {branchesLimit}){{ - totalCount, - cursor, - items {{ - id, - name, - description, - commits {{ - totalCount - }} - }} - }} - }} - }}", - Variables = new { id } - }; - return (await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false)).stream; - } - - /// - /// Gets all streams for the current user - /// - /// 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 - { - Query = - $@"query User {{ - activeUser{{ - id, - email, - name, - bio, - company, - avatar, - verified, - profiles, - role, - streams(limit:{limit}) {{ - totalCount, - cursor, - items {{ - id, - name, - description, - isPublic, - role, - createdAt, - updatedAt, - favoritedDate, - commentCount - favoritesCount - collaborators {{ - id, - name, - role, - avatar - }} - }} - }} - }} - }}" - }; - - var res = await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false); - - if (res?.activeUser == null) - { - throw new SpeckleException( - "User is not authenticated, or the credentials were not valid. Check the provided account is still valid, remove it from manager and add it again." - ); - } - - return res.activeUser.streams.items; - } - - //TODO: API GAP - /// - /// Gets all favorite streams for the current user - /// - /// Max number of streams to return - /// - /// - public async Task> FavoriteStreamsGet(int limit = 10, CancellationToken cancellationToken = default) - { - var request = new GraphQLRequest - { - Query = - $@"query User {{ - activeUser{{ - id, - email, - name, - bio, - company, - avatar, - verified, - profiles, - role, - favoriteStreams(limit:{limit}) {{ - totalCount, - cursor, - items {{ - id, - name, - description, - isPublic, - role, - createdAt, - updatedAt, - favoritedDate, - commentCount - favoritesCount - collaborators {{ - id, - name, - role, - avatar - }} - }} - }} - }} - }}" - }; - return (await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false)) - .activeUser - .favoriteStreams - .items; - } - - /// - /// Searches the user's streams by name, description, and ID - /// - /// String query to search for - /// Max number of streams to return - /// - /// - /// - [Obsolete($"Use client.{nameof(ActiveUser)}.{nameof(ActiveUserResource.GetProjects)}")] - public async Task> StreamSearch( - string query, - int limit = 10, - CancellationToken cancellationToken = default - ) - { - var request = new GraphQLRequest - { - Query = - @"query Streams ($query: String!, $limit: Int!) { - streams(query: $query, limit: $limit) { - totalCount, - cursor, - items { - id, - name, - description, - isPublic, - role, - createdAt, - updatedAt, - commentCount - favoritesCount - collaborators { - id, - name, - role - } - } - } - }", - Variables = new { query, limit } - }; - - var res = await GQLClient.SendMutationAsync(request, cancellationToken).ConfigureAwait(false); //WARN: Why do we do this? - return (await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false)).streams.items; - } - - /// - /// Creates a stream. - /// - /// - /// - /// 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 - { - Query = @"mutation streamCreate($myStream: StreamCreateInput!) { streamCreate(stream: $myStream) }", - Variables = new { myStream = streamInput } - }; - var res = await ExecuteGraphQLRequest>(request, cancellationToken).ConfigureAwait(false); - return (string)res["streamCreate"]; - } - - /// - /// Updates a stream. - /// - /// 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 - { - Query = @"mutation streamUpdate($myStream: StreamUpdateInput!) { streamUpdate(stream:$myStream) }", - Variables = new { myStream = streamInput } - }; - - var res = await ExecuteGraphQLRequest>(request, cancellationToken).ConfigureAwait(false); - - return (bool)res["streamUpdate"]; - } - - /// - /// Deletes a stream. - /// - /// 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 - { - Query = @"mutation streamDelete($id: String!) { streamDelete(id:$id) }", - Variables = new { id } - }; - var res = await ExecuteGraphQLRequest>(request, cancellationToken).ConfigureAwait(false); - return (bool)res["streamDelete"]; - } - - /// - /// Revokes permissions of a user on a given stream. - /// - /// - /// - /// - public async Task StreamRevokePermission( - StreamRevokePermissionInput permissionInput, - CancellationToken cancellationToken = default - ) - { - var request = new GraphQLRequest - { - Query = - @"mutation streamRevokePermission($permissionParams: StreamRevokePermissionInput!) { - streamRevokePermission(permissionParams: $permissionParams) - }", - Variables = new { permissionParams = permissionInput } - }; - - var res = await ExecuteGraphQLRequest>(request, cancellationToken).ConfigureAwait(false); - return (bool)res["streamRevokePermission"]; - } - - /// - /// Updates permissions for a user on a given stream. - /// - /// includes the streamId, the userId of the user to update, and the user's new role - /// - /// - /// - /// - [Obsolete($"Use client.{nameof(Project)}.{nameof(ProjectResource.UpdateRole)}")] - public async Task StreamUpdatePermission( - StreamPermissionInput updatePermissionInput, - CancellationToken cancellationToken = default - ) - { - var request = new GraphQLRequest - { - Query = - @" - mutation streamUpdatePermission($permissionParams: StreamUpdatePermissionInput!) { - streamUpdatePermission(permissionParams:$permissionParams) - }", - Variables = new { permissionParams = updatePermissionInput } - }; - - var res = await ExecuteGraphQLRequest>(request, cancellationToken).ConfigureAwait(false); - return (bool)res["streamUpdatePermission"]; - } - - /// - /// Gets the pending collaborators of a stream by id. - /// Requires the user to be an owner of the stream. - /// - /// - /// - /// - /// - [Obsolete($"Use client.{nameof(Project)}.{nameof(ProjectResource.GetWithTeam)}")] - public async Task StreamGetPendingCollaborators( - string streamId, - CancellationToken cancellationToken = default - ) - { - var request = new GraphQLRequest - { - Query = - @"query Stream($id: String!) { - stream(id: $id) { - id - pendingCollaborators { - id - inviteId - title - role - user { - avatar - } - } - } - }", - Variables = new { id = streamId } - }; - var res = await GQLClient.SendMutationAsync(request, cancellationToken).ConfigureAwait(false); //WARN: Why do we do this? - return (await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false)).stream; - } - - /// - /// Sends an email invite to join a stream and assigns them a collaborator role. - /// - /// - /// - /// - /// - [Obsolete($"Use client.{nameof(ProjectInvite)}.{nameof(ProjectInviteResource.Create)}")] - public async Task StreamInviteCreate( - StreamInviteCreateInput inviteCreateInput, - CancellationToken cancellationToken = default - ) - { - if ((inviteCreateInput.email == null) & (inviteCreateInput.userId == null)) - { - throw new ArgumentException("You must provide either an email or a user id to create a stream invite"); - } - - var request = new GraphQLRequest - { - Query = - @" - mutation streamInviteCreate($input: StreamInviteCreateInput!) { - streamInviteCreate(input: $input) - }", - Variables = new { input = inviteCreateInput } - }; - - var res = await ExecuteGraphQLRequest>(request, cancellationToken).ConfigureAwait(false); - return (bool)res["streamInviteCreate"]; - } - - /// - /// Cancels an invite to join a stream. - /// - /// Id of the stream - /// Id of the invite to cancel - /// - /// - /// - [Obsolete($"Use client.{nameof(ProjectInvite)}.{nameof(ProjectInviteResource.Cancel)}")] - public async Task StreamInviteCancel( - string streamId, - string inviteId, - CancellationToken cancellationToken = default - ) - { - var request = new GraphQLRequest - { - Query = - @" - mutation streamInviteCancel( $streamId: String!, $inviteId: String! ) { - streamInviteCancel(streamId: $streamId, inviteId: $inviteId) - }", - Variables = new { streamId, inviteId } - }; - - var res = await ExecuteGraphQLRequest>(request, cancellationToken).ConfigureAwait(false); - return (bool)res["streamInviteCancel"]; - } - - /// - /// Accept or decline a stream invite. - /// - /// - /// - /// - /// - /// - /// - /// - [Obsolete($"Use client.{nameof(ProjectInvite)}.{nameof(ProjectInviteResource.Use)}")] - public async Task StreamInviteUse( - string streamId, - string token, - bool accept = true, - CancellationToken cancellationToken = default - ) - { - var request = new GraphQLRequest - { - Query = - @" - mutation streamInviteUse( $accept: Boolean!, $streamId: String!, $token: String! ) { - streamInviteUse(accept: $accept, streamId: $streamId, token: $token) - }", - Variables = new - { - streamId, - token, - accept - } - }; - - var res = await ExecuteGraphQLRequest>(request, cancellationToken).ConfigureAwait(false); - return (bool)res["streamInviteUse"]; - } - - /// - /// - /// - /// - /// - /// - [Obsolete($"Use client.{nameof(ActiveUser)}.{nameof(ActiveUserResource.ProjectInvites)}")] - public async Task> GetAllPendingInvites(CancellationToken cancellationToken = default) - { - var request = new GraphQLRequest - { - Query = - @" - query StreamInvites { - streamInvites{ - id - token - inviteId - streamId - streamName - title - role - invitedBy { - id - name - company - avatar - } - } - }" - }; - - var res = await ExecuteGraphQLRequest(request, cancellationToken).ConfigureAwait(false); - return res.streamInvites; - } -} diff --git a/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.UserOperations.cs b/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.UserOperations.cs deleted file mode 100644 index 42a41f6f..00000000 --- a/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.UserOperations.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Speckle.Sdk.Api.GraphQL.Models; -using Speckle.Sdk.Api.GraphQL.Resources; - -namespace Speckle.Sdk.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.Sdk/Api/GraphQL/Legacy/Client.Subscriptions/Client.Subscriptions.Branch.cs b/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.Subscriptions/Client.Subscriptions.Branch.cs deleted file mode 100644 index 64510c81..00000000 --- a/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.Subscriptions/Client.Subscriptions.Branch.cs +++ /dev/null @@ -1,85 +0,0 @@ -#nullable disable -using GraphQL; - -namespace Speckle.Sdk.Api; - -public partial class Client -{ - #region BranchCreated - - public delegate void BranchCreatedHandler(object sender, BranchInfo e); - - public event BranchCreatedHandler OnBranchCreated; - public IDisposable BranchCreatedSubscription { get; private set; } - - /// - /// Subscribe to events of branch created for a stream - /// - /// - public void SubscribeBranchCreated(string streamId) - { - var request = new GraphQLRequest { Query = $@"subscription {{ branchCreated (streamId: ""{streamId}"") }}" }; - - BranchCreatedSubscription = SubscribeTo( - request, - (sender, result) => OnBranchCreated?.Invoke(sender, result.branchCreated) - ); - } - - public bool HasSubscribedBranchCreated => BranchCreatedSubscription != null; - - #endregion - - - #region BranchUpdated - - public delegate void BranchUpdatedHandler(object sender, BranchInfo e); - - public event BranchUpdatedHandler OnBranchUpdated; - public IDisposable BranchUpdatedSubscription { get; private set; } - - /// - /// Subscribe to events of branch updated for a stream - /// - /// - public void SubscribeBranchUpdated(string streamId, string branchId = null) - { - var request = new GraphQLRequest - { - Query = $@"subscription {{ branchUpdated (streamId: ""{streamId}"", branchId: ""{branchId}"") }}" - }; - BranchUpdatedSubscription = SubscribeTo( - request, - (sender, result) => OnBranchUpdated?.Invoke(sender, result.branchUpdated) - ); - } - - public bool HasSubscribedBranchUpdated => BranchUpdatedSubscription != null; - - #endregion - - #region BranchDeleted - - public delegate void BranchDeletedHandler(object sender, BranchInfo e); - - public event BranchDeletedHandler OnBranchDeleted; - public IDisposable BranchDeletedSubscription { get; private set; } - - /// - /// Subscribe to events of branch deleted for a stream - /// - /// - public void SubscribeBranchDeleted(string streamId) - { - var request = new GraphQLRequest { Query = $@"subscription {{ branchDeleted (streamId: ""{streamId}"") }}" }; - - BranchDeletedSubscription = SubscribeTo( - request, - (sender, result) => OnBranchDeleted?.Invoke(sender, result.branchDeleted) - ); - } - - public bool HasSubscribedBranchDeleted => BranchDeletedSubscription != null; - - #endregion -} diff --git a/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.Subscriptions/Client.Subscriptions.Commit.cs b/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.Subscriptions/Client.Subscriptions.Commit.cs deleted file mode 100644 index 02b4817f..00000000 --- a/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.Subscriptions/Client.Subscriptions.Commit.cs +++ /dev/null @@ -1,85 +0,0 @@ -#nullable disable -using GraphQL; - -namespace Speckle.Sdk.Api; - -public partial class Client -{ - #region CommitCreated - - public delegate void CommitCreatedHandler(object sender, CommitInfo e); - - public event CommitCreatedHandler OnCommitCreated; - public IDisposable CommitCreatedSubscription; - - /// - /// Subscribe to events of commit created for a stream - /// - /// - public void SubscribeCommitCreated(string streamId) - { - var request = new GraphQLRequest { Query = $@"subscription {{ commitCreated (streamId: ""{streamId}"") }}" }; - - CommitCreatedSubscription = SubscribeTo( - request, - (sender, result) => OnCommitCreated?.Invoke(sender, result.commitCreated) - ); - } - - public bool HasSubscribedCommitCreated => CommitCreatedSubscription != null; - - #endregion - - #region CommitUpdated - - public delegate void CommitUpdatedHandler(object sender, CommitInfo e); - - public event CommitUpdatedHandler OnCommitUpdated; - public IDisposable CommitUpdatedSubscription; - - /// - /// Subscribe to events of commit updated for a stream - /// - /// - public void SubscribeCommitUpdated(string streamId, string commitId = null) - { - var request = new GraphQLRequest - { - Query = $@"subscription {{ commitUpdated (streamId: ""{streamId}"", commitId: ""{commitId}"") }}" - }; - - var res = GQLClient.CreateSubscriptionStream(request); - CommitUpdatedSubscription = SubscribeTo( - request, - (sender, result) => OnCommitUpdated?.Invoke(sender, result.commitUpdated) - ); - } - - public bool HasSubscribedCommitUpdated => CommitUpdatedSubscription != null; - - #endregion - - #region CommitDeleted - - public delegate void CommitDeletedHandler(object sender, CommitInfo e); - - public event CommitDeletedHandler OnCommitDeleted; - public IDisposable CommitDeletedSubscription; - - /// - /// Subscribe to events of commit updated for a stream - /// - /// - public void SubscribeCommitDeleted(string streamId) - { - var request = new GraphQLRequest { Query = $@"subscription {{ commitDeleted (streamId: ""{streamId}"") }}" }; - CommitDeletedSubscription = SubscribeTo( - request, - (sender, result) => OnCommitDeleted?.Invoke(sender, result.commitDeleted) - ); - } - - public bool HasSubscribedCommitDeleted => CommitDeletedSubscription != null; - - #endregion -} diff --git a/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.Subscriptions/Client.Subscriptions.Stream.cs b/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.Subscriptions/Client.Subscriptions.Stream.cs deleted file mode 100644 index a969fc4d..00000000 --- a/src/Speckle.Sdk/Api/GraphQL/Legacy/Client.Subscriptions/Client.Subscriptions.Stream.cs +++ /dev/null @@ -1,108 +0,0 @@ -#nullable disable -using GraphQL; - -namespace Speckle.Sdk.Api; - -public partial class Client -{ - #region UserStreamAdded - - public delegate void UserStreamAddedHandler(object sender, StreamInfo e); - - public event UserStreamAddedHandler OnUserStreamAdded; - public IDisposable UserStreamAddedSubscription; - - /// - /// Subscribe to events of streams added for the current user - /// - /// - public void SubscribeUserStreamAdded() - { - var request = new GraphQLRequest { Query = @"subscription { userStreamAdded }" }; - - UserStreamAddedSubscription = SubscribeTo( - request, - (sender, result) => OnUserStreamAdded?.Invoke(sender, result.userStreamAdded) - ); - } - - public bool HasSubscribedUserStreamAdded => UserStreamAddedSubscription != null; - - #endregion - - #region StreamUpdated - - public delegate void StreamUpdatedHandler(object sender, StreamInfo e); - - public event StreamUpdatedHandler OnStreamUpdated; - public IDisposable StreamUpdatedSubscription; - - /// - /// Subscribe to events of streams updated for a specific streamId - /// - /// streamId - public void SubscribeStreamUpdated(string id) - { - var request = new GraphQLRequest { Query = $@"subscription {{ streamUpdated( streamId: ""{id}"") }}" }; - StreamUpdatedSubscription = SubscribeTo( - request, - (sender, result) => OnStreamUpdated?.Invoke(sender, result.streamUpdated) - ); - } - - public bool HasSubscribedStreamUpdated => StreamUpdatedSubscription != null; - - #endregion - - #region StreamRemoved - - public delegate void UserStreamRemovedHandler(object sender, StreamInfo e); - - public event UserStreamRemovedHandler OnUserStreamRemoved; - public IDisposable UserStreamRemovedSubscription; - - /// - /// Subscribe to events of streams removed for the current user - /// - public void SubscribeUserStreamRemoved() - { - var request = new GraphQLRequest { Query = @"subscription { userStreamRemoved }" }; - - UserStreamRemovedSubscription = SubscribeTo( - request, - (sender, result) => OnUserStreamRemoved?.Invoke(sender, result.userStreamRemoved) - ); - } - - public bool HasSubscribedUserStreamRemoved => UserStreamRemovedSubscription != null; - - #endregion - - #region CommentActivity - - public delegate void CommentActivityHandler(object sender, CommentItem e); - - public event CommentActivityHandler OnCommentActivity; - public IDisposable CommentActivitySubscription; - - /// - /// Subscribe to new comment events - /// - /// - public void SubscribeCommentActivity(string streamId) - { - var request = new GraphQLRequest - { - Query = - $@"subscription {{ commentActivity( streamId: ""{streamId}"") {{ type comment {{ id authorId archived screenshot rawText }} }} }}" - }; - CommentActivitySubscription = SubscribeTo( - request, - (sender, result) => OnCommentActivity?.Invoke(sender, result.commentActivity.comment) - ); - } - - public bool HasSubscribedCommentActivity => CommentActivitySubscription != null; - - #endregion -} diff --git a/src/Speckle.Sdk/Api/GraphQL/Legacy/LegacyGraphQLModels.cs b/src/Speckle.Sdk/Api/GraphQL/Legacy/LegacyGraphQLModels.cs deleted file mode 100644 index 431943ce..00000000 --- a/src/Speckle.Sdk/Api/GraphQL/Legacy/LegacyGraphQLModels.cs +++ /dev/null @@ -1,401 +0,0 @@ -#nullable disable -using Speckle.Sdk.Api.GraphQL.Enums; -using Speckle.Sdk.Api.GraphQL.Models; - -namespace Speckle.Sdk.Api; - -#region inputs - -internal static class DeprecationMessages -{ - public const string FE1_DEPRECATION_MESSAGE = - $"Stream/Branch/Commit API is now deprecated, Use the new Project/Model/Version API functions in {nameof(Client)}"; -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class StreamCreateInput -{ - public string name { get; set; } - public string description { get; set; } - public bool isPublic { get; set; } = true; -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class StreamUpdateInput -{ - public string id { get; set; } - public string name { get; set; } - public string description { get; set; } - public bool isPublic { get; set; } = true; -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class StreamPermissionInput -{ - public string streamId { get; set; } - public string userId { get; set; } - public string role { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class StreamRevokePermissionInput -{ - public string streamId { get; set; } - public string userId { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class StreamInviteCreateInput -{ - public string streamId { get; set; } - public string userId { get; set; } - public string email { get; set; } - public string message { get; set; } - public string role { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class BranchCreateInput -{ - public string streamId { get; set; } - public string name { get; set; } - public string description { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class BranchUpdateInput -{ - public string streamId { get; set; } - public string id { get; set; } - public string name { get; set; } - public string description { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class BranchDeleteInput -{ - public string streamId { get; set; } - public string id { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class CommitCreateInput -{ - public string streamId { get; set; } - public string branchName { get; set; } - public string objectId { get; set; } - public string message { get; set; } - public string sourceApplication { get; set; } = ".net"; - public int totalChildrenCount { get; set; } - public List parents { get; set; } - - [Obsolete("Please use the parents property. This property will be removed in later versions")] - public List previousCommitIds { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class CommitUpdateInput -{ - public string streamId { get; set; } - public string id { get; set; } - public string message { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class CommitDeleteInput -{ - public string streamId { get; set; } - public string id { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class CommitReceivedInput -{ - public string streamId { get; set; } - public string commitId { get; set; } - public string sourceApplication { get; set; } - public string message { get; set; } -} - -#endregion - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class Stream -{ - public string id { get; set; } - public string name { get; set; } - public string description { get; set; } - - public bool isPublic { get; set; } - public string role { get; set; } - public DateTime createdAt { get; set; } - public DateTime updatedAt { get; set; } - public string favoritedDate { get; set; } - - public int commentCount { get; set; } - public int favoritesCount { get; set; } - - public List collaborators { get; set; } - public List pendingCollaborators { get; set; } = new(); - public Branches branches { get; set; } - - /// - /// Set only in the case that you've requested this through . - /// - public Branch branch { get; set; } - - /// - /// Set only in the case that you've requested this through . - /// - public Commit commit { get; set; } - - /// - /// Set only in the case that you've requested this through - /// - public Commits commits { get; set; } - - public Activity activity { get; set; } - - public SpeckleObject @object { get; set; } - - public override string ToString() - { - return $"Stream ({name} | {id})"; - } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class Collaborator -{ - public string id { get; set; } - public string name { get; set; } - public string role { get; set; } - public string avatar { get; set; } - - public override string ToString() - { - return $"Collaborator ({name} | {role} | {id})"; - } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class StreamInvitesResponse -{ - public List streamInvites { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class Branches -{ - public int totalCount { get; set; } - public string cursor { get; set; } - public List items { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class Commits -{ - public int totalCount { get; set; } - public string cursor { get; set; } - public List items { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class Commit -{ - public string id { get; set; } - public string message { get; set; } - public string branchName { get; set; } - public string authorName { get; set; } - public string authorId { get; set; } - public string authorAvatar { get; set; } - public DateTime createdAt { get; set; } - public string sourceApplication { get; set; } - - public string referencedObject { get; set; } - public int totalChildrenCount { get; set; } - public List parents { get; set; } - - public override string ToString() - { - return $"Commit ({message} | {id})"; - } -} - -public class Activity -{ - public int totalCount { get; set; } - public DateTime cursor { get; set; } - public List items { get; set; } -} - -public class ActivityItem -{ - public string actionType { get; set; } - public string userId { get; set; } - public string streamId { get; set; } - public string resourceId { get; set; } - public string resourceType { get; set; } - public DateTime time { get; set; } - public Info info { get; set; } - public string message { get; set; } -} - -public class Info -{ - public string message { get; set; } - public string sourceApplication { get; set; } - - public InfoCommit commit { get; set; } -} - -public class InfoCommit -{ - public string message { get; set; } - public string sourceApplication { get; set; } - public string branchName { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class SpeckleObject -{ - public string id { get; set; } - public string speckleType { get; set; } - public string applicationId { get; set; } - public int totalChildrenCount { get; set; } - public DateTime createdAt { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class Branch -{ - public string id { get; set; } - public string name { get; set; } - public string description { get; set; } - public Commits commits { get; set; } - - public override string ToString() - { - return $"Branch ({name} | {id})"; - } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class Streams -{ - public int totalCount { get; set; } - public string cursor { get; set; } - public List items { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class Resource -{ - public string resourceId { get; set; } - public ResourceType resourceType { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class Location -{ - public double x { get; set; } - public double y { get; set; } - public double z { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class UserSearchData -{ - public UserSearch userSearch { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class UserSearch -{ - public string cursor { get; set; } - public List items { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class StreamData -{ - public Stream stream { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class StreamsData -{ - public Streams streams { get; set; } -} - -#region comments -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class Comments -{ - public int totalCount { get; set; } - public DateTime? cursor { get; set; } - public List items { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public sealed class CommentData -{ - 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.FE1_DEPRECATION_MESSAGE)] -public class CommentItem -{ - public string id { get; set; } - public string authorId { get; set; } - public bool archived { get; set; } - public string screenshot { get; set; } - public string rawText { get; set; } - public CommentData data { get; set; } - public DateTime createdAt { get; set; } - public DateTime updatedAt { get; set; } - public DateTime? viewedAt { get; set; } - public object reactions { get; set; } - public Comments replies { get; set; } - public List resources { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class ContentContent -{ - public string Type { get; set; } - - //public Mark[] Marks { get; set; } - public string Text { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class CommentsData -{ - public Comments comments { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class CommentItemData -{ - public CommentItem comment { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class CommentActivityMessage -{ - public string type { get; set; } - public CommentItem comment { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class CommentActivityResponse -{ - public CommentActivityMessage commentActivity { get; set; } -} -#endregion diff --git a/src/Speckle.Sdk/Api/GraphQL/Legacy/Manager.cs b/src/Speckle.Sdk/Api/GraphQL/Legacy/Manager.cs deleted file mode 100644 index fe46ab7c..00000000 --- a/src/Speckle.Sdk/Api/GraphQL/Legacy/Manager.cs +++ /dev/null @@ -1,96 +0,0 @@ -#nullable disable -using System.Text.Json.Serialization; - -namespace Speckle.Sdk.Api; - -#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.Sdk/Api/GraphQL/Legacy/SubscriptionModels.cs b/src/Speckle.Sdk/Api/GraphQL/Legacy/SubscriptionModels.cs deleted file mode 100644 index 454eba57..00000000 --- a/src/Speckle.Sdk/Api/GraphQL/Legacy/SubscriptionModels.cs +++ /dev/null @@ -1,100 +0,0 @@ -#nullable disable -namespace Speckle.Sdk.Api; - -#region streams -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class StreamInfo -{ - public string id { get; set; } - public string name { get; set; } - public string description { get; set; } - public string sharedBy { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class UserStreamAddedResult -{ - public StreamInfo userStreamAdded { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class StreamUpdatedResult -{ - public StreamInfo streamUpdated { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class UserStreamRemovedResult -{ - public StreamInfo userStreamRemoved { get; set; } -} -#endregion - -#region branches - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class BranchInfo -{ - public string id { get; set; } - public string name { get; set; } - public string description { get; set; } - public string streamId { get; set; } - public string authorId { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class BranchCreatedResult -{ - public BranchInfo branchCreated { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class BranchUpdatedResult -{ - public BranchInfo branchUpdated { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class BranchDeletedResult -{ - public BranchInfo branchDeleted { get; set; } -} -#endregion - -#region commits - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class CommitInfo -{ - public string id { get; set; } - public string streamId { get; set; } - public string branchName { get; set; } - public string objectId { get; set; } - public string authorId { get; set; } - public string message { get; set; } - public string sourceApplication { get; set; } - public int? totalChildrenCount { get; set; } - public IList parents { get; set; } - - [Obsolete("Please use the parents property. This property will be removed in later versions")] - public IList previousCommitIds { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class CommitCreatedResult -{ - public CommitInfo commitCreated { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class CommitUpdatedResult -{ - public CommitInfo commitUpdated { get; set; } -} - -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class CommitDeletedResult -{ - public CommitInfo commitDeleted { get; set; } -} -#endregion diff --git a/src/Speckle.Sdk/Api/GraphQL/Models/Collections.cs b/src/Speckle.Sdk/Api/GraphQL/Models/Collections.cs index 663ef7a4..10b3d525 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Models/Collections.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Models/Collections.cs @@ -1,17 +1,39 @@ -namespace Speckle.Sdk.Api.GraphQL.Models; +using Speckle.Newtonsoft.Json; + +namespace Speckle.Sdk.Api.GraphQL.Models; public class ResourceCollection { + [property: JsonProperty(Required = Required.Always)] public int totalCount { get; init; } + [property: JsonProperty(Required = Required.Always)] public List items { get; init; } + [property: JsonProperty(Required = Required.AllowNull)] public string? cursor { get; init; } } -public sealed class CommentReplyAuthorCollection : ResourceCollection { } +public sealed class CommentReplyAuthorCollection +{ + [property: JsonProperty(Required = Required.Always)] + public int totalCount { get; init; } + + [property: JsonProperty(Required = Required.Always)] + public List items { get; init; } +} + +public sealed class UserSearchResultCollection +{ + [property: JsonProperty(Required = Required.Always)] + public List items { get; init; } + + [property: JsonProperty(Required = Required.AllowNull)] + public string? cursor { get; init; } +} public sealed class ProjectCommentCollection : ResourceCollection { + [property: JsonProperty(Required = Required.Always)] public int totalArchivedCount { get; init; } } diff --git a/src/Speckle.Sdk/Api/GraphQL/Models/Comment.cs b/src/Speckle.Sdk/Api/GraphQL/Models/Comment.cs index 3ca53e81..01da6610 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Models/Comment.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Models/Comment.cs @@ -1,4 +1,4 @@ -#nullable disable +using Speckle.Newtonsoft.Json; namespace Speckle.Sdk.Api.GraphQL.Models; @@ -10,13 +10,42 @@ public sealed class Comment public DateTime createdAt { get; init; } public bool hasParent { get; init; } public string id { get; init; } - public Comment parent { 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 string? screenshot { get; init; } public DateTime updatedAt { get; init; } public DateTime? viewedAt { get; init; } public List viewerResources { get; init; } + public SerializedViewerState viewerState { get; init; } +} + +/// +/// See SerializedViewerState in /shared/src/viewer/helpers/state.ts +/// +/// +/// Note, there are many FE/Viewer specific properties on this object that are not reflected here (hence the override) +/// We can add them as needed, keeping in mind flexiblity for breaking changes (these classes are intentionally not documented in our schema!) +/// +[JsonObject(MissingMemberHandling = MissingMemberHandling.Ignore)] +public sealed class SerializedViewerState +{ + public ViewerStateUI ui { get; init; } +} + +[JsonObject(MissingMemberHandling = MissingMemberHandling.Ignore)] +public sealed class ViewerStateUI +{ + public ViewerStateCamera camera { get; init; } +} + +[JsonObject(MissingMemberHandling = MissingMemberHandling.Ignore)] +public sealed class ViewerStateCamera +{ + public List position { get; init; } + public List target { get; init; } + public bool isOrthoProjection { get; init; } + public double zoom { get; init; } } diff --git a/src/Speckle.Sdk/Api/GraphQL/Models/FileUpload.cs b/src/Speckle.Sdk/Api/GraphQL/Models/FileUpload.cs index 4a6f3b53..09bfccb8 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Models/FileUpload.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Models/FileUpload.cs @@ -1,29 +1,22 @@ -#nullable disable - -using Speckle.Sdk.Api.GraphQL.Enums; +using Speckle.Sdk.Api.GraphQL.Enums; namespace Speckle.Sdk.Api.GraphQL.Models; public sealed class FileUpload { - public string convertedCommitId { get; init; } + public string? convertedCommitId { get; init; } public DateTime convertedLastUpdate { get; init; } + public string? convertedMessage { 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 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.FE1_DEPRECATION_MESSAGE)] - public string branchName { get; init; } - - [Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] - public string streamId { get; init; } } diff --git a/src/Speckle.Sdk/Api/GraphQL/Models/Model.cs b/src/Speckle.Sdk/Api/GraphQL/Models/Model.cs index 4d7acb20..2d838b31 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Models/Model.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Models/Model.cs @@ -1,19 +1,18 @@ -#nullable disable -namespace Speckle.Sdk.Api.GraphQL.Models; +namespace Speckle.Sdk.Api.GraphQL.Models; -public sealed class Model +public class Model { - public LimitedUser author { get; init; } - public List childrenTree { get; init; } - public ResourceCollection commentThreads { get; init; } + public LimitedUser? author { get; init; } public DateTime createdAt { get; init; } - public string description { 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 Uri? previewUrl { get; init; } public DateTime updatedAt { get; init; } - public ResourceCollection versions { get; init; } - public Version version { get; init; } +} + +public sealed class ModelWithVersions : Model +{ + public ResourceCollection versions { get; init; } } diff --git a/src/Speckle.Sdk/Api/GraphQL/Models/ModelsTreeItem.cs b/src/Speckle.Sdk/Api/GraphQL/Models/ModelsTreeItem.cs index 2b93e210..74bf6a82 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Models/ModelsTreeItem.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Models/ModelsTreeItem.cs @@ -1,6 +1,4 @@ -#nullable disable - -namespace Speckle.Sdk.Api.GraphQL.Models; +namespace Speckle.Sdk.Api.GraphQL.Models; public sealed class ModelsTreeItem { @@ -8,7 +6,7 @@ public sealed class ModelsTreeItem public string fullName { get; init; } public bool hasChildren { get; init; } public string id { get; init; } - public Model model { get; init; } + public Model? model { get; init; } public string name { get; init; } public DateTime updatedAt { get; init; } } diff --git a/src/Speckle.Sdk/Api/GraphQL/Models/PendingStreamCollaborator.cs b/src/Speckle.Sdk/Api/GraphQL/Models/PendingStreamCollaborator.cs index 57de53f2..4398944f 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Models/PendingStreamCollaborator.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Models/PendingStreamCollaborator.cs @@ -1,6 +1,4 @@ -#nullable disable - -namespace Speckle.Sdk.Api.GraphQL.Models; +namespace Speckle.Sdk.Api.GraphQL.Models; public sealed class PendingStreamCollaborator { @@ -13,12 +11,6 @@ public sealed class PendingStreamCollaborator 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.FE1_DEPRECATION_MESSAGE)] - public string streamId { get; init; } - - [Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] - public string streamName { get; init; } + public LimitedUser? user { get; init; } + public string? token { get; init; } } diff --git a/src/Speckle.Sdk/Api/GraphQL/Models/Project.cs b/src/Speckle.Sdk/Api/GraphQL/Models/Project.cs index 18b4eb9d..f2ca4344 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Models/Project.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Models/Project.cs @@ -1,28 +1,28 @@ -#nullable disable -using Speckle.Sdk.Api.GraphQL.Enums; +using Speckle.Sdk.Api.GraphQL.Enums; namespace Speckle.Sdk.Api.GraphQL.Models; -public sealed class Project +public class Project { - public bool AllowPublicComments { get; init; } - public ProjectCommentCollection commentThreads { get; init; } + public bool allowPublicComments { get; init; } public DateTime createdAt { get; init; } - public string description { 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 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; } + public string? workspaceId { get; init; } +} + +public sealed class ProjectWithModels : Project +{ + public ResourceCollection models { get; init; } +} + +public sealed class ProjectWithTeam : Project +{ + public List invitedTeam { get; init; } + public List team { get; init; } } diff --git a/src/Speckle.Sdk/Api/GraphQL/Models/ProjectCollaborator.cs b/src/Speckle.Sdk/Api/GraphQL/Models/ProjectCollaborator.cs index 232eb82e..81c02af1 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Models/ProjectCollaborator.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Models/ProjectCollaborator.cs @@ -1,9 +1,8 @@ -#nullable disable - -namespace Speckle.Sdk.Api.GraphQL.Models; +namespace Speckle.Sdk.Api.GraphQL.Models; public sealed class ProjectCollaborator { + public string id { get; init; } public string role { get; init; } public LimitedUser user { get; init; } } diff --git a/src/Speckle.Sdk/Api/GraphQL/Models/ResourceIdentifier.cs b/src/Speckle.Sdk/Api/GraphQL/Models/ResourceIdentifier.cs index adeb2252..af9dda29 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Models/ResourceIdentifier.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Models/ResourceIdentifier.cs @@ -1,5 +1,4 @@ -#nullable disable -using Speckle.Sdk.Api.GraphQL.Enums; +using Speckle.Sdk.Api.GraphQL.Enums; namespace Speckle.Sdk.Api.GraphQL.Models; diff --git a/src/Speckle.Sdk/Api/GraphQL/Models/Responses/MutationResponses.cs b/src/Speckle.Sdk/Api/GraphQL/Models/Responses/MutationResponses.cs deleted file mode 100644 index d5096444..00000000 --- a/src/Speckle.Sdk/Api/GraphQL/Models/Responses/MutationResponses.cs +++ /dev/null @@ -1,44 +0,0 @@ -namespace Speckle.Sdk.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 Version create { get; init; } - public bool delete { get; init; } - public bool markReceived { 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.Sdk/Api/GraphQL/Models/Responses/Responses.cs b/src/Speckle.Sdk/Api/GraphQL/Models/Responses/Responses.cs index e84ffd70..8c032b4c 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Models/Responses/Responses.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Models/Responses/Responses.cs @@ -2,29 +2,17 @@ namespace Speckle.Sdk.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 +/// +/// With the power of GraphQL Aliasing, we can avoid having to craft individual response classes for each query +/// Instead, we can alias the query object as data, and use either or +/// To deserialize the response +/// +/// +/// +internal record RequiredResponse([property: JsonProperty(Required = Required.Always)] T data); -internal record ProjectResponse([property: JsonRequired] Project project); +/// +internal record NullableResponse([property: JsonProperty(Required = Required.AllowNull)] T? data); -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); +//TODO: replace with RequiredResponse{T} +internal record ServerInfoResponse([property: JsonProperty(Required = Required.Always)] ServerInfo serverInfo); diff --git a/src/Speckle.Sdk/Api/GraphQL/Models/ServerInfo.cs b/src/Speckle.Sdk/Api/GraphQL/Models/ServerInfo.cs index 707a0dce..8f044732 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Models/ServerInfo.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Models/ServerInfo.cs @@ -1,17 +1,20 @@ -#nullable disable -using System.Runtime.InteropServices; +namespace Speckle.Sdk.Api.GraphQL.Models; -namespace Speckle.Sdk.Api.GraphQL.Models; +public sealed class AuthStrategy +{ + public string? color { get; init; } + public string icon { get; init; } + public string id { get; init; } + public string name { get; init; } + public string url { get; init; } +} -[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; } + public string? company { get; init; } + public string? version { get; init; } + public string? description { get; init; } /// /// This field is not returned from the GQL API, @@ -27,18 +30,18 @@ public sealed class ServerInfo /// public string url { get; set; } - public ServerMigration migration { get; init; } + 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; } + public Uri? movedFrom { get; set; } + + /// + /// New URI where this server is now deployed + /// + public Uri? movedTo { get; set; } } diff --git a/src/Speckle.Sdk/Api/GraphQL/Models/SubscriptionMessages.cs b/src/Speckle.Sdk/Api/GraphQL/Models/SubscriptionMessages.cs index 9d4fe88a..327bac77 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Models/SubscriptionMessages.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Models/SubscriptionMessages.cs @@ -77,7 +77,8 @@ public sealed class ProjectVersionsUpdatedMessage : EventArgs [JsonRequired] public ProjectVersionsUpdatedMessageType type { get; init; } - public string? modelId { get; init; } + [JsonRequired] + public string modelId { get; init; } public Version? version { get; init; } } diff --git a/src/Speckle.Sdk/Api/GraphQL/Models/User.cs b/src/Speckle.Sdk/Api/GraphQL/Models/User.cs index 7bc1d3da..57fef330 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Models/User.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Models/User.cs @@ -1,27 +1,14 @@ -#nullable disable - -namespace Speckle.Sdk.Api.GraphQL.Models; +namespace Speckle.Sdk.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? 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 string? role { get; init; } public bool? verified { get; init; } - - [Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] - public int totalOwnedStreamsFavorites { get; init; } - - [Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] - public ResourceCollection commits { get; init; } - - [Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] - public ResourceCollection streams { get; init; } } public sealed class LimitedUser : UserBase @@ -35,15 +22,12 @@ public sealed class LimitedUser : UserBase public sealed class User : UserBase { public DateTime? createdAt { get; init; } - public string email { 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.FE1_DEPRECATION_MESSAGE)] - public ResourceCollection favoriteStreams { get; init; } - public override string ToString() { return $"User ({email} | {name} | {id})"; diff --git a/src/Speckle.Sdk/Api/GraphQL/Models/Version.cs b/src/Speckle.Sdk/Api/GraphQL/Models/Version.cs index b0ec7854..ecc5d3e8 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Models/Version.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Models/Version.cs @@ -1,16 +1,12 @@ -#nullable disable - -namespace Speckle.Sdk.Api.GraphQL.Models; +namespace Speckle.Sdk.Api.GraphQL.Models; public sealed class Version { - public LimitedUser authorUser { get; init; } - public ResourceCollection commentThreads { get; init; } + public LimitedUser? authorUser { get; init; } public DateTime createdAt { get; init; } public string id { get; init; } - public string message { get; init; } - public Model model { get; init; } + public string? message { get; init; } public Uri previewUrl { get; init; } public string referencedObject { get; init; } - public string sourceApplication { get; init; } + public string? sourceApplication { get; init; } } diff --git a/src/Speckle.Sdk/Api/GraphQL/Models/ViewerResourceGroup.cs b/src/Speckle.Sdk/Api/GraphQL/Models/ViewerResourceGroup.cs index 7dbd6533..d38de688 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Models/ViewerResourceGroup.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Models/ViewerResourceGroup.cs @@ -1,6 +1,4 @@ -#nullable disable - -namespace Speckle.Sdk.Api.GraphQL.Models; +namespace Speckle.Sdk.Api.GraphQL.Models; public class ViewerResourceGroup { diff --git a/src/Speckle.Sdk/Api/GraphQL/Models/ViewerResourceItem.cs b/src/Speckle.Sdk/Api/GraphQL/Models/ViewerResourceItem.cs index d87d8af5..e61a9a1f 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Models/ViewerResourceItem.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Models/ViewerResourceItem.cs @@ -1,10 +1,8 @@ -#nullable disable - -namespace Speckle.Sdk.Api.GraphQL.Models; +namespace Speckle.Sdk.Api.GraphQL.Models; public class ViewerResourceItem { - public string modelId { get; init; } + public string? modelId { get; init; } public string objectId { get; init; } - public string versionId { get; init; } + public string? versionId { get; init; } } diff --git a/src/Speckle.Sdk/Api/GraphQL/Resources/ActiveUserResource.cs b/src/Speckle.Sdk/Api/GraphQL/Resources/ActiveUserResource.cs index a9748e82..9a6bf0e4 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Resources/ActiveUserResource.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Resources/ActiveUserResource.cs @@ -15,37 +15,68 @@ public sealed class ActiveUserResource } /// - /// Gets the currently active user profile. + /// Gets the currently active user profile (as extracted from the authorization header) /// /// /// - /// the requested user, or null if the user does not exist (i.e. was initialised with an unauthenticated account) + /// the requested user, or null if 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, + data:activeUser { + id + email + name + bio + company + avatar + verified + role } } """; var request = new GraphQLRequest { Query = QUERY }; var response = await _client - .ExecuteGraphQLRequest(request, cancellationToken) + .ExecuteGraphQLRequest>(request, cancellationToken) .ConfigureAwait(false); - return response.activeUser; + return response.data; + } + + /// + /// + /// + public async Task Update(UserUpdateInput input, CancellationToken cancellationToken = default) + { + //todo:test + //language=graphql + const string QUERY = """ + mutation ActiveUserMutations($input: UserUpdateInput!) { + data:activeUserMutations { + data:update(user: $input) { + id + email + name + bio + company + avatar + verified + role + } + } + } + """; + var request = new GraphQLRequest { Query = QUERY, Variables = new { input } }; + + var response = await _client + .ExecuteGraphQLRequest>>(request, cancellationToken) + .ConfigureAwait(false); + + return response.data.data; } /// Max number of projects to fetch @@ -64,9 +95,10 @@ public sealed class ActiveUserResource //language=graphql const string QUERY = """ query User($limit : Int!, $cursor: String, $filter: UserProjectsFilter) { - activeUser { - projects(limit: $limit, cursor: $cursor, filter: $filter) { + data:activeUser { + data:projects(limit: $limit, cursor: $cursor, filter: $filter) { totalCount + cursor items { id name @@ -77,6 +109,7 @@ public sealed class ActiveUserResource createdAt updatedAt sourceApps + workspaceId } } } @@ -89,32 +122,36 @@ public sealed class ActiveUserResource { limit, cursor, - filter - } + filter, + }, }; var response = await _client - .ExecuteGraphQLRequest(request, cancellationToken) + .ExecuteGraphQLRequest>?>>( + request, + cancellationToken + ) .ConfigureAwait(false); - if (response.activeUser is null) + if (response.data is null) { throw new SpeckleGraphQLException("GraphQL response indicated that the ActiveUser could not be found"); } - return response.activeUser.projects; + return response.data.data; } /// /// /// - public async Task> ProjectInvites(CancellationToken cancellationToken = default) + /// The ActiveUser could not be found (e.g. the client is not authenticated) + public async Task> GetProjectInvites(CancellationToken cancellationToken = default) { //language=graphql const string QUERY = """ query ProjectInvites { - activeUser { - projectInvites { + data:activeUser { + data:projectInvites { id inviteId invitedBy { @@ -132,12 +169,13 @@ public sealed class ActiveUserResource title token user { - id, - name, - bio, - company, - verified, - role, + id + name + bio + company + verified + avatar + role } } } @@ -147,14 +185,22 @@ public sealed class ActiveUserResource var request = new GraphQLRequest { Query = QUERY }; var response = await _client - .ExecuteGraphQLRequest(request, cancellationToken) + .ExecuteGraphQLRequest>?>>( + request, + cancellationToken + ) .ConfigureAwait(false); - if (response.activeUser is null) + if (response.data is null) { - throw new SpeckleGraphQLException("GraphQL response indicated that the ActiveUser could not be found"); + throw new SpeckleException("GraphQL response indicated that the ActiveUser could not be found"); } - return response.activeUser.projectInvites; + return response.data.data; } + + /// + [Obsolete($"Renamed to {nameof(GetProjectInvites)}")] + public async Task> ProjectInvites(CancellationToken cancellationToken = default) => + await GetProjectInvites(cancellationToken).ConfigureAwait(false); } diff --git a/src/Speckle.Sdk/Api/GraphQL/Resources/CommentResource.cs b/src/Speckle.Sdk/Api/GraphQL/Resources/CommentResource.cs index bcb3c8ba..30aed5e9 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Resources/CommentResource.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Resources/CommentResource.cs @@ -14,6 +14,82 @@ public sealed class CommentResource _client = client; } + /// + /// + /// Max number of comment replies to fetch + /// Optional cursor for pagination + /// + /// + /// + public async Task Get( + string commentId, + string projectId, + int repliesLimit = ServerLimits.DEFAULT_PAGINATION_REQUEST, + string? repliesCursor = null, + CancellationToken cancellationToken = default + ) + { + //language=graphql + const string QUERY = """ + query CommentThreads($projectId: String!, $commentId: String!, $repliesLimit: Int, $repliesCursor: String) { + data:project(id: $projectId) { + data:comment(id: $commentId) { + 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 + } + } + } + } + """; + + GraphQLRequest request = new() + { + Query = QUERY, + Variables = new + { + commentId, + projectId, + repliesLimit, + repliesCursor, + }, + }; + + var response = await _client + .ExecuteGraphQLRequest>>(request, cancellationToken) + .ConfigureAwait(false); + + return response.data.data; + } + /// /// Max number of comments to fetch /// Optional cursor for pagination @@ -23,7 +99,7 @@ public sealed class CommentResource /// /// /// - public async Task> GetProjectComments( + public async Task GetProjectComments( string projectId, int limit = ServerLimits.DEFAULT_PAGINATION_REQUEST, string? cursor = null, @@ -36,8 +112,8 @@ public sealed class CommentResource //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) { + data:project(id: $projectId) { + data:commentThreads(cursor: $cursor, limit: $limit, filter: $filter) { cursor totalArchivedCount totalCount @@ -74,33 +150,32 @@ public sealed class CommentResource objectId versionId } - data + viewerState } } } } """; - GraphQLRequest request = - new() + GraphQLRequest request = new() + { + Query = QUERY, + Variables = new { - Query = QUERY, - Variables = new - { - projectId, - cursor, - limit, - filter, - repliesLimit, - repliesCursor, - } - }; + projectId, + cursor, + limit, + filter, + repliesLimit, + repliesCursor, + }, + }; var response = await _client - .ExecuteGraphQLRequest(request, cancellationToken) + .ExecuteGraphQLRequest>>(request, cancellationToken) .ConfigureAwait(false); - return response.project.commentThreads; + return response.data.data; } /// @@ -117,7 +192,7 @@ public sealed class CommentResource const string QUERY = """ mutation Mutation($input: CreateCommentInput!) { data:commentMutations { - create(input: $input) { + data:create(input: $input) { archived authorId createdAt @@ -136,16 +211,15 @@ public sealed class CommentResource objectId versionId } - data } } } """; GraphQLRequest request = new(QUERY, variables: new { input }); var res = await _client - .ExecuteGraphQLRequest>(request, cancellationToken) + .ExecuteGraphQLRequest>>(request, cancellationToken) .ConfigureAwait(false); - return res.data.create; + return res.data.data; } /// @@ -159,7 +233,7 @@ public sealed class CommentResource const string QUERY = """ mutation Mutation($input: EditCommentInput!) { data:commentMutations { - edit(input: $input) { + data:edit(input: $input) { archived authorId createdAt @@ -178,59 +252,67 @@ public sealed class CommentResource objectId versionId } - data } } } """; GraphQLRequest request = new(QUERY, variables: new { input }); var res = await _client - .ExecuteGraphQLRequest>(request, cancellationToken) + .ExecuteGraphQLRequest>>(request, cancellationToken) .ConfigureAwait(false); - return res.data.edit; + return res.data.data; } - /// - /// + /// /// /// /// - public async Task Archive(string commentId, bool archive = true, CancellationToken cancellationToken = default) + public async Task Archive(ArchiveCommentInput input, CancellationToken cancellationToken = default) { //language=graphql const string QUERY = """ - mutation Mutation($commentId: String!, $archive: Boolean!) { + mutation Mutation($input: ArchiveCommentInput!) { data:commentMutations { - archive(commentId: $commentId, archived: $archive) + data:archive(input: $input) } } """; - GraphQLRequest request = new(QUERY, variables: new { commentId, archive }); + GraphQLRequest request = new(QUERY, variables: new { input }); var res = await _client - .ExecuteGraphQLRequest>(request, cancellationToken) + .ExecuteGraphQLRequest>>(request, cancellationToken) .ConfigureAwait(false); - return res.data.archive; + + if (!res.data.data) + { + //This should never happen, the server should never return `false` without providing a reason + throw new InvalidOperationException("GraphQL data did not indicate success, but no GraphQL error was provided"); + } } - /// + /// /// /// /// - public async Task MarkViewed(string commentId, CancellationToken cancellationToken = default) + public async Task MarkViewed(MarkCommentViewedInput input, CancellationToken cancellationToken = default) { //language=graphql const string QUERY = """ - mutation Mutation($commentId: String!) { + mutation Mutation($input: MarkCommentViewedInput!) { data:commentMutations { - markViewed(commentId: $commentId) + data:markViewed(input: $input) } } """; - GraphQLRequest request = new(QUERY, variables: new { commentId }); + GraphQLRequest request = new(QUERY, variables: new { input }); var res = await _client - .ExecuteGraphQLRequest>(request, cancellationToken) + .ExecuteGraphQLRequest>>(request, cancellationToken) .ConfigureAwait(false); - return res.data.markViewed; + + if (!res.data.data) + { + //This should never happen, the server should never return `false` without providing a reason + throw new InvalidOperationException("GraphQL data did not indicate success, but no GraphQL error was provided"); + } } /// @@ -244,7 +326,7 @@ public sealed class CommentResource const string QUERY = """ mutation Mutation($input: CreateCommentReplyInput!) { data:commentMutations { - reply(input: $input) { + data:reply(input: $input) { archived authorId createdAt @@ -263,15 +345,14 @@ public sealed class CommentResource objectId versionId } - data } } } """; GraphQLRequest request = new(QUERY, variables: new { input }); var res = await _client - .ExecuteGraphQLRequest>(request, cancellationToken) + .ExecuteGraphQLRequest>>(request, cancellationToken) .ConfigureAwait(false); - return res.data.reply; + return res.data.data; } } diff --git a/src/Speckle.Sdk/Api/GraphQL/Resources/ModelResource.cs b/src/Speckle.Sdk/Api/GraphQL/Resources/ModelResource.cs index 1d297bd3..87607ee8 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Resources/ModelResource.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Resources/ModelResource.cs @@ -25,8 +25,8 @@ public sealed class ModelResource //language=graphql const string QUERY = """ query ModelGet($modelId: String!, $projectId: String!) { - project(id: $projectId) { - model(id: $modelId) { + data:project(id: $projectId) { + data:model(id: $modelId) { id name previewUrl @@ -50,10 +50,10 @@ public sealed class ModelResource var request = new GraphQLRequest { Query = QUERY, Variables = new { modelId, projectId } }; var response = await _client - .ExecuteGraphQLRequest(request, cancellationToken) + .ExecuteGraphQLRequest>>(request, cancellationToken) .ConfigureAwait(false); - return response.project.model; + return response.data.data; } /// @@ -65,7 +65,7 @@ public sealed class ModelResource /// /// /// - public async Task GetWithVersions( + public async Task GetWithVersions( string modelId, string projectId, int versionsLimit = ServerLimits.DEFAULT_PAGINATION_REQUEST, @@ -77,8 +77,8 @@ public sealed class ModelResource //language=graphql const string QUERY = """ query ModelGetWithVersions($modelId: String!, $projectId: String!, $versionsLimit: Int!, $versionsCursor: String, $versionsFilter: ModelVersionsFilter) { - project(id: $projectId) { - model(id: $modelId) { + data:project(id: $projectId) { + data:model(id: $modelId) { id name previewUrl @@ -92,6 +92,7 @@ public sealed class ModelResource createdAt previewUrl authorUser { + avatar id name bio @@ -130,14 +131,14 @@ public sealed class ModelResource versionsLimit, versionsCursor, versionsFilter, - } + }, }; var response = await _client - .ExecuteGraphQLRequest(request, cancellationToken) + .ExecuteGraphQLRequest>>(request, cancellationToken) .ConfigureAwait(false); - return response.project.model; + return response.data.data; } /// @@ -158,8 +159,8 @@ public sealed class ModelResource //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) { + data:project(id: $projectId) { + data:models(limit: $modelsLimit, cursor: $modelsCursor, filter: $modelsFilter) { items { id name @@ -168,6 +169,15 @@ public sealed class ModelResource displayName description createdAt + author { + avatar + bio + company + id + name + role + verified + } } totalCount cursor @@ -175,23 +185,22 @@ public sealed class ModelResource } } """; - GraphQLRequest request = - new() + GraphQLRequest request = new() + { + Query = QUERY, + Variables = new { - Query = QUERY, - Variables = new - { - projectId, - modelsLimit, - modelsCursor, - modelsFilter - } - }; + projectId, + modelsLimit, + modelsCursor, + modelsFilter, + }, + }; var response = await _client - .ExecuteGraphQLRequest(request, cancellationToken) + .ExecuteGraphQLRequest>>>(request, cancellationToken) .ConfigureAwait(false); - return response.project.models; + return response.data.data; } /// @@ -203,8 +212,8 @@ public sealed class ModelResource //language=graphql const string QUERY = """ mutation ModelCreate($input: CreateModelInput!) { - modelMutations { - create(input: $input) { + data:modelMutations { + data:create(input: $input) { id displayName name @@ -229,23 +238,23 @@ public sealed class ModelResource GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; var res = await _client - .ExecuteGraphQLRequest(request, cancellationToken) + .ExecuteGraphQLRequest>>(request, cancellationToken) .ConfigureAwait(false); - return res.modelMutations.create; + return res.data.data; } /// /// /// /// - public async Task Delete(DeleteModelInput input, CancellationToken cancellationToken = default) + public async Task Delete(DeleteModelInput input, CancellationToken cancellationToken = default) { //language=graphql const string QUERY = """ mutation ModelDelete($input: DeleteModelInput!) { - modelMutations { - delete(input: $input) + data:modelMutations { + data:delete(input: $input) } } """; @@ -253,10 +262,14 @@ public sealed class ModelResource GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; var res = await _client - .ExecuteGraphQLRequest(request, cancellationToken) + .ExecuteGraphQLRequest>>(request, cancellationToken) .ConfigureAwait(false); - return res.modelMutations.delete; + if (!res.data.data) + { + //This should never happen, the server should never return `false` without providing a reason + throw new InvalidOperationException("GraphQL data did not indicate success, but no GraphQL error was provided"); + } } /// @@ -268,8 +281,8 @@ public sealed class ModelResource //language=graphql const string QUERY = """ mutation ModelUpdate($input: UpdateModelInput!) { - modelMutations { - update(input: $input) { + data:modelMutations { + data:update(input: $input) { id name displayName @@ -294,9 +307,9 @@ public sealed class ModelResource GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; var res = await _client - .ExecuteGraphQLRequest(request, cancellationToken) + .ExecuteGraphQLRequest>>(request, cancellationToken) .ConfigureAwait(false); - return res.modelMutations.update; + return res.data.data; } } diff --git a/src/Speckle.Sdk/Api/GraphQL/Resources/OtherUserResource.cs b/src/Speckle.Sdk/Api/GraphQL/Resources/OtherUserResource.cs index 47ed1c34..309d1255 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Resources/OtherUserResource.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Resources/OtherUserResource.cs @@ -25,14 +25,14 @@ public sealed class OtherUserResource //language=graphql const string QUERY = """ query LimitedUser($id: String!) { - otherUser(id: $id){ - id, - name, - bio, - company, - avatar, - verified, - role, + data:otherUser(id: $id) { + id + name + bio + company + avatar + verified + role } } """; @@ -40,14 +40,14 @@ public sealed class OtherUserResource var request = new GraphQLRequest { Query = QUERY, Variables = new { id } }; var response = await _client - .ExecuteGraphQLRequest(request, cancellationToken) + .ExecuteGraphQLRequest>(request, cancellationToken) .ConfigureAwait(false); - return response.otherUser; + return response.data; } /// - /// Searches for a user on the server. + /// Searches for a user on the server, by name or email /// /// String to search for. Must be at least 3 characters /// Max number of users to fetch @@ -57,7 +57,7 @@ public sealed class OtherUserResource /// /// /// - public async Task> UserSearch( + public async Task UserSearch( string query, int limit = ServerLimits.DEFAULT_PAGINATION_REQUEST, string? cursor = null, @@ -69,8 +69,8 @@ public sealed class OtherUserResource //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, + data:userSearch(query: $query, limit: $limit, cursor: $cursor, archived: $archived, emailOnly: $emailOnly) { + cursor items { id name @@ -93,14 +93,14 @@ public sealed class OtherUserResource limit, cursor, archived, - emailOnly - } + emailOnly, + }, }; var response = await _client - .ExecuteGraphQLRequest(request, cancellationToken) + .ExecuteGraphQLRequest>(request, cancellationToken) .ConfigureAwait(false); - return response.userSearch; + return response.data; } } diff --git a/src/Speckle.Sdk/Api/GraphQL/Resources/ProjectInviteResource.cs b/src/Speckle.Sdk/Api/GraphQL/Resources/ProjectInviteResource.cs index cd122a59..2acc1d79 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Resources/ProjectInviteResource.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Resources/ProjectInviteResource.cs @@ -19,7 +19,7 @@ public sealed class ProjectInviteResource /// /// /// - public async Task Create( + public async Task Create( string projectId, ProjectInviteCreateInput input, CancellationToken cancellationToken = default @@ -28,9 +28,9 @@ public sealed class ProjectInviteResource //language=graphql const string QUERY = """ mutation ProjectInviteCreate($projectId: ID!, $input: ProjectInviteCreateInput!) { - projectMutations { - invites { - create(projectId: $projectId, input: $input) { + data:projectMutations { + data:invites { + data:create(projectId: $projectId, input: $input) { id name description @@ -39,7 +39,10 @@ public sealed class ProjectInviteResource role createdAt updatedAt + workspaceId + sourceApps team { + id role user { id @@ -86,24 +89,27 @@ public sealed class ProjectInviteResource GraphQLRequest request = new() { Query = QUERY, Variables = new { projectId, input } }; var response = await _client - .ExecuteGraphQLRequest(request, cancellationToken) + .ExecuteGraphQLRequest>>>( + request, + cancellationToken + ) .ConfigureAwait(false); - return response.projectMutations.invites.create; + return response.data.data.data; } /// /// /// /// - public async Task Use(ProjectInviteUseInput input, CancellationToken cancellationToken = default) + public async Task Use(ProjectInviteUseInput input, CancellationToken cancellationToken = default) { //language=graphql const string QUERY = """ mutation ProjectInviteUse($input: ProjectInviteUseInput!) { - projectMutations { - invites { - use(input: $input) + data:projectMutations { + data:invites { + data:use(input: $input) } } } @@ -111,9 +117,14 @@ public sealed class ProjectInviteResource GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; var response = await _client - .ExecuteGraphQLRequest(request, cancellationToken) + .ExecuteGraphQLRequest>>>(request, cancellationToken) .ConfigureAwait(false); - return response.projectMutations.invites.use; + + if (!response.data.data.data) + { + //This should never happen, the server should never return `false` without providing a reason + throw new InvalidOperationException("GraphQL data did not indicate success, but no GraphQL error was provided"); + } } /// @@ -130,7 +141,7 @@ public sealed class ProjectInviteResource //language=graphql const string QUERY = """ query ProjectInvite($projectId: String!, $token: String) { - projectInvite(projectId: $projectId, token: $token) { + data:projectInvite(projectId: $projectId, token: $token) { id inviteId invitedBy { @@ -162,10 +173,10 @@ public sealed class ProjectInviteResource GraphQLRequest request = new() { Query = QUERY, Variables = new { projectId, token } }; var response = await _client - .ExecuteGraphQLRequest(request, cancellationToken) + .ExecuteGraphQLRequest>(request, cancellationToken) .ConfigureAwait(false); - return response.projectInvite; + return response.data; } /// @@ -173,14 +184,18 @@ public sealed class ProjectInviteResource /// /// /// - public async Task Cancel(string projectId, string inviteId, CancellationToken cancellationToken = default) + 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) { + data:projectMutations { + data:invites { + data:cancel(projectId: $projectId, inviteId: $inviteId) { id name description @@ -189,7 +204,10 @@ public sealed class ProjectInviteResource role createdAt updatedAt + sourceApps + workspaceId team { + id role user { id @@ -236,9 +254,12 @@ public sealed class ProjectInviteResource GraphQLRequest request = new() { Query = QUERY, Variables = new { projectId, inviteId } }; var response = await _client - .ExecuteGraphQLRequest(request, cancellationToken) + .ExecuteGraphQLRequest>>>( + request, + cancellationToken + ) .ConfigureAwait(false); - return response.projectMutations.invites.cancel; + return response.data.data.data; } } diff --git a/src/Speckle.Sdk/Api/GraphQL/Resources/ProjectResource.cs b/src/Speckle.Sdk/Api/GraphQL/Resources/ProjectResource.cs index ddf05398..0a09b644 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Resources/ProjectResource.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Resources/ProjectResource.cs @@ -25,25 +25,26 @@ public sealed class ProjectResource //language=graphql const string QUERY = """ query Project($projectId: String!) { - project(id: $projectId) { + data:project(id: $projectId) { + allowPublicComments + createdAt + description id name - description - visibility - allowPublicComments role - createdAt - updatedAt sourceApps + updatedAt + visibility + workspaceId } } """; GraphQLRequest request = new() { Query = QUERY, Variables = new { projectId } }; var response = await _client - .ExecuteGraphQLRequest(request, cancellationToken) + .ExecuteGraphQLRequest>(request, cancellationToken) .ConfigureAwait(false); - return response.project; + return response.data; } /// @@ -55,7 +56,7 @@ public sealed class ProjectResource /// /// /// - public async Task GetWithModels( + public async Task GetWithModels( string projectId, int modelsLimit = ServerLimits.DEFAULT_PAGINATION_REQUEST, string? modelsCursor = null, @@ -66,7 +67,7 @@ public sealed class ProjectResource //language=graphql const string QUERY = """ query ProjectGetWithModels($projectId: String!, $modelsLimit: Int!, $modelsCursor: String, $modelsFilter: ProjectModelsFilter) { - project(id: $projectId) { + data:project(id: $projectId) { id name description @@ -76,6 +77,7 @@ public sealed class ProjectResource createdAt updatedAt sourceApps + workspaceId models(limit: $modelsLimit, cursor: $modelsCursor, filter: $modelsFilter) { items { id @@ -92,23 +94,22 @@ public sealed class ProjectResource } } """; - GraphQLRequest request = - new() + GraphQLRequest request = new() + { + Query = QUERY, + Variables = new { - Query = QUERY, - Variables = new - { - projectId, - modelsLimit, - modelsCursor, - modelsFilter - } - }; + projectId, + modelsLimit, + modelsCursor, + modelsFilter, + }, + }; var response = await _client - .ExecuteGraphQLRequest(request, cancellationToken) + .ExecuteGraphQLRequest>(request, cancellationToken) .ConfigureAwait(false); - return response.project; + return response.data; } /// @@ -117,12 +118,12 @@ public sealed class ProjectResource /// /// /// - public async Task GetWithTeam(string projectId, CancellationToken cancellationToken = default) + public async Task GetWithTeam(string projectId, CancellationToken cancellationToken = default) { //language=graphql const string QUERY = """ query ProjectGetWithTeam($projectId: String!) { - project(id: $projectId) { + data:project(id: $projectId) { id name description @@ -131,7 +132,10 @@ public sealed class ProjectResource role createdAt updatedAt + workspaceId + sourceApps team { + id role user { id @@ -170,15 +174,16 @@ public sealed class ProjectResource role } } + workspaceId } } """; GraphQLRequest request = new() { Query = QUERY, Variables = new { projectId } }; var response = await _client - .ExecuteGraphQLRequest(request, cancellationToken) + .ExecuteGraphQLRequest>(request, cancellationToken) .ConfigureAwait(false); - return response.project; + return response.data; } /// @@ -190,8 +195,8 @@ public sealed class ProjectResource //language=graphql const string QUERY = """ mutation ProjectCreate($input: ProjectCreateInput) { - projectMutations { - create(input: $input) { + data:projectMutations { + data:create(input: $input) { id name description @@ -201,6 +206,7 @@ public sealed class ProjectResource createdAt updatedAt sourceApps + workspaceId } } } @@ -208,9 +214,9 @@ public sealed class ProjectResource GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; var response = await _client - .ExecuteGraphQLRequest(request, cancellationToken) + .ExecuteGraphQLRequest>>(request, cancellationToken) .ConfigureAwait(false); - return response.projectMutations.create; + return response.data.data; } /// @@ -222,16 +228,18 @@ public sealed class ProjectResource //language=graphql const string QUERY = """ mutation ProjectUpdate($input: ProjectUpdateInput!) { - projectMutations{ - update(update: $input) { + data:projectMutations{ + data:update(update: $input) { + allowPublicComments + createdAt + description id name - description - visibility - allowPublicComments role - createdAt + sourceApps updatedAt + visibility + workspaceId } } } @@ -239,43 +247,51 @@ public sealed class ProjectResource GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; var response = await _client - .ExecuteGraphQLRequest(request, cancellationToken) + .ExecuteGraphQLRequest>>(request, cancellationToken) .ConfigureAwait(false); - return response.projectMutations.update; + return response.data.data; } /// The id of the Project to delete /// /// /// - public async Task Delete(string projectId, CancellationToken cancellationToken = default) + public async Task Delete(string projectId, CancellationToken cancellationToken = default) { //language=graphql const string QUERY = """ mutation ProjectDelete($projectId: String!) { - projectMutations { - delete(id: $projectId) + data:projectMutations { + data:delete(id: $projectId) } } """; GraphQLRequest request = new() { Query = QUERY, Variables = new { projectId } }; var response = await _client - .ExecuteGraphQLRequest(request, cancellationToken) + .ExecuteGraphQLRequest>>(request, cancellationToken) .ConfigureAwait(false); - return response.projectMutations.delete; + + if (!response.data.data) + { + //This should never happen, the server should never return `false` without providing a reason + throw new InvalidOperationException("GraphQL data did not indicate success, but no GraphQL error was provided"); + } } /// /// /// - public async Task UpdateRole(ProjectUpdateRoleInput input, CancellationToken cancellationToken = default) + public async Task UpdateRole( + ProjectUpdateRoleInput input, + CancellationToken cancellationToken = default + ) { //language=graphql const string QUERY = """ mutation ProjectUpdateRole($input: ProjectUpdateRoleInput!) { - projectMutations { - updateRole(input: $input) { + data:projectMutations { + data:updateRole(input: $input) { id name description @@ -284,7 +300,10 @@ public sealed class ProjectResource role createdAt updatedAt + sourceApps + workspaceId team { + id role user { id @@ -330,8 +349,8 @@ public sealed class ProjectResource GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; var response = await _client - .ExecuteGraphQLRequest(request, cancellationToken) + .ExecuteGraphQLRequest>>(request, cancellationToken) .ConfigureAwait(false); - return response.projectMutations.updateRole; + return response.data.data; } } diff --git a/src/Speckle.Sdk/Api/GraphQL/Resources/SubscriptionResource.cs b/src/Speckle.Sdk/Api/GraphQL/Resources/SubscriptionResource.cs index 916768f7..f23edae9 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Resources/SubscriptionResource.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Resources/SubscriptionResource.cs @@ -51,6 +51,15 @@ public sealed class SubscriptionResource : IDisposable id project { id + name + description + visibility + allowPublicComments + role + createdAt + updatedAt + sourceApps + workspaceId } type } @@ -151,6 +160,7 @@ public sealed class SubscriptionResource : IDisposable createdAt updatedAt sourceApps + workspaceId } type } diff --git a/src/Speckle.Sdk/Api/GraphQL/Resources/VersionResource.cs b/src/Speckle.Sdk/Api/GraphQL/Resources/VersionResource.cs index 8e5b851e..08aad4a9 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Resources/VersionResource.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Resources/VersionResource.cs @@ -16,60 +16,42 @@ public sealed class VersionResource } /// - /// /// /// /// /// - public async Task Get( - string versionId, - string modelId, - string projectId, - CancellationToken cancellationToken = default - ) + public async Task Get(string versionId, 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) { + query VersionGet($projectId: String!, $versionId: String!) { + data:project(id: $projectId) { + data:version(id: $versionId) { + id + referencedObject + message + sourceApplication + createdAt + previewUrl + authorUser { id - referencedObject - message - sourceApplication - createdAt - previewUrl - authorUser { - id - name - bio - company - verified - role - avatar - } + name + bio + company + verified + role + avatar } } } } """; - GraphQLRequest request = - new() - { - Query = QUERY, - Variables = new - { - projectId, - modelId, - versionId - } - }; + GraphQLRequest request = new() { Query = QUERY, Variables = new { projectId, versionId } }; var response = await _client - .ExecuteGraphQLRequest(request, cancellationToken) + .ExecuteGraphQLRequest>>(request, cancellationToken) .ConfigureAwait(false); - return response.project.model.version; + return response.data.data; } /// @@ -91,9 +73,9 @@ public sealed class VersionResource //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) { + data:project(id: $projectId) { + data:model(id: $modelId) { + data:versions(limit: $limit, cursor: $cursor, filter: $filter) { items { id referencedObject @@ -119,61 +101,39 @@ public sealed class VersionResource } """; - GraphQLRequest request = - new() + GraphQLRequest request = new() + { + Query = QUERY, + Variables = new { - Query = QUERY, - Variables = new - { - projectId, - modelId, - limit, - cursor, - filter, - } - }; + projectId, + modelId, + limit, + cursor, + filter, + }, + }; var response = await _client - .ExecuteGraphQLRequest(request, cancellationToken) + .ExecuteGraphQLRequest>>>>( + request, + cancellationToken + ) .ConfigureAwait(false); - return response.project.model.versions; + return response.data.data.data; } /// /// - /// id of the created + /// The created /// - public async Task Create(CreateVersionInput input, CancellationToken cancellationToken = default) + public async Task Create(CreateVersionInput input, CancellationToken cancellationToken = default) { //language=graphql const string QUERY = """ mutation Create($input: CreateVersionInput!) { - versionMutations { - create(input: $input) { - id - } - } - } - """; - - GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; - - var response = await _client - .ExecuteGraphQLRequest(request, cancellationToken) - .ConfigureAwait(false); - return response.versionMutations.create.id; - } - - /// - /// - /// - public async Task Update(UpdateVersionInput input, CancellationToken cancellationToken = default) - { - //language=graphql - const string QUERY = """ - mutation VersionUpdate($input: UpdateVersionInput!) { - versionMutations { - update(input: $input) { + data:versionMutations { + data:create(input: $input) { id referencedObject message @@ -193,12 +153,50 @@ public sealed class VersionResource } } """; - GraphQLRequest request = new() { Query = QUERY, Variables = new { input, } }; + + GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; var response = await _client - .ExecuteGraphQLRequest(request, cancellationToken) + .ExecuteGraphQLRequest>>(request, cancellationToken) .ConfigureAwait(false); - return response.versionMutations.update; + return response.data.data; + } + + /// + /// + /// + public async Task Update(UpdateVersionInput input, CancellationToken cancellationToken = default) + { + //language=graphql + const string QUERY = """ + mutation VersionUpdate($input: UpdateVersionInput!) { + data:versionMutations { + data:update(input: $input) { + id + referencedObject + message + sourceApplication + createdAt + previewUrl + authorUser { + 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.data.data; } /// @@ -210,64 +208,80 @@ public sealed class VersionResource //language=graphql const string QUERY = """ mutation VersionMoveToModel($input: MoveVersionsInput!) { - versionMutations { - moveToModel(input: $input) { - id + data:versionMutations { + data:moveToModel(input: $input) { + data:id } } } """; - GraphQLRequest request = new() { Query = QUERY, Variables = new { input, } }; + GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; var response = await _client - .ExecuteGraphQLRequest(request, cancellationToken) + .ExecuteGraphQLRequest>>>(request, cancellationToken) .ConfigureAwait(false); - return response.versionMutations.moveToModel.id; + return response.data.data.data; } /// /// /// /// - public async Task Delete(DeleteVersionsInput input, CancellationToken cancellationToken = default) + public async Task Delete(DeleteVersionsInput input, CancellationToken cancellationToken = default) { //language=graphql const string QUERY = """ mutation VersionDelete($input: DeleteVersionsInput!) { - versionMutations { - delete(input: $input) + data:versionMutations { + data:delete(input: $input) } } """; GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; var response = await _client - .ExecuteGraphQLRequest(request, cancellationToken) + .ExecuteGraphQLRequest>>(request, cancellationToken) .ConfigureAwait(false); - return response.versionMutations.delete; + if (!response.data.data) + { + //This should never happen, the server should never return `false` without providing a reason + throw new InvalidOperationException("GraphQL data did not indicate success, but no GraphQL error was provided"); + } } /// /// /// /// - public async Task Received(MarkReceivedVersionInput input, CancellationToken cancellationToken = default) + public async Task Received(MarkReceivedVersionInput input, CancellationToken cancellationToken = default) { //language=graphql const string QUERY = """ mutation MarkReceived($input: MarkReceivedVersionInput!) { - versionMutations { - markReceived(input: $input) + data:versionMutations { + data:markReceived(input: $input) } } """; GraphQLRequest request = new() { Query = QUERY, Variables = new { input } }; var response = await _client - .ExecuteGraphQLRequest(request, cancellationToken) + .ExecuteGraphQLRequest>>(request, cancellationToken) .ConfigureAwait(false); - return response.versionMutations.markReceived; + if (!response.data.data) + { + //This should never happen, the server should never return `false` without providing a reason + throw new InvalidOperationException("GraphQL data did not indicate success, but no GraphQL error was provided"); + } } + + [Obsolete("modelId is no longer required, use the overload that doesn't specify a model id", true)] + public Task Get( + string versionId, + string modelId, + string projectId, + CancellationToken cancellationToken = default + ) => throw new NotImplementedException(); } diff --git a/src/Speckle.Sdk/Api/GraphQL/Serializer/ConstantCaseEnumConverter.cs b/src/Speckle.Sdk/Api/GraphQL/Serializer/ConstantCaseEnumConverter.cs index 0abc3614..3d7eb29c 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Serializer/ConstantCaseEnumConverter.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Serializer/ConstantCaseEnumConverter.cs @@ -1,5 +1,3 @@ -#nullable disable - using System.Reflection; using GraphQL.Client.Abstractions.Utilities; using Speckle.Newtonsoft.Json; @@ -9,7 +7,7 @@ namespace Speckle.Sdk.Api.GraphQL.Serializer; internal class ConstantCaseEnumConverter : StringEnumConverter { - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { if (value == null) { @@ -18,7 +16,7 @@ internal class ConstantCaseEnumConverter : StringEnumConverter else { var enumString = ((Enum)value).ToString("G"); - var memberName = value + string? memberName = value .GetType() .GetMember(enumString, BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public) .FirstOrDefault() @@ -34,7 +32,9 @@ internal class ConstantCaseEnumConverter : StringEnumConverter } else { +#pragma warning disable CS8604 // Possible null reference argument. //never null? writer.WriteValue(memberName.ToConstantCase()); +#pragma warning restore CS8604 // Possible null reference argument. } } } diff --git a/src/Speckle.Sdk/Api/GraphQL/Serializer/MapConverter.cs b/src/Speckle.Sdk/Api/GraphQL/Serializer/MapConverter.cs index 919ff1d7..c552f1bf 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Serializer/MapConverter.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Serializer/MapConverter.cs @@ -1,4 +1,3 @@ -#nullable disable using System.Diagnostics.CodeAnalysis; using GraphQL; using Speckle.Newtonsoft.Json; @@ -8,7 +7,7 @@ namespace Speckle.Sdk.Api.GraphQL.Serializer; internal sealed class MapConverter : JsonConverter { - public override void WriteJson(JsonWriter writer, Map value, JsonSerializer serializer) + public override void WriteJson(JsonWriter writer, Map? value, JsonSerializer serializer) { throw new NotImplementedException( "This converter currently is only intended to be used to read a JSON object into a strongly-typed representation." @@ -18,7 +17,7 @@ internal sealed class MapConverter : JsonConverter public override Map ReadJson( JsonReader reader, Type objectType, - Map existingValue, + Map? existingValue, bool hasExistingValue, JsonSerializer serializer ) @@ -39,16 +38,23 @@ internal sealed class MapConverter : JsonConverter )] private object ReadToken(JToken token) { - return token switch + switch (token) { - JObject jObject => ReadDictionary(jObject, new Dictionary()), - JArray jArray => ReadArray(jArray).ToList(), - JValue jValue => jValue.Value, - JConstructor => throw new ArgumentOutOfRangeException(nameof(token), "cannot deserialize a JSON constructor"), - JProperty => throw new ArgumentOutOfRangeException(nameof(token), "cannot deserialize a JSON property"), - JContainer => throw new ArgumentOutOfRangeException(nameof(token), "cannot deserialize a JSON comment"), - _ => throw new ArgumentOutOfRangeException(nameof(token), $"Invalid token type {token?.Type}") - }; + case JObject jObject: + return ReadDictionary(jObject, new Dictionary()); + case JArray jArray: + return ReadArray(jArray).ToList(); + case JValue jValue: + return jValue.Value ?? string.Empty; + case JConstructor: + throw new ArgumentOutOfRangeException(nameof(token), "cannot deserialize a JSON constructor"); + case JProperty: + throw new ArgumentOutOfRangeException(nameof(token), "cannot deserialize a JSON property"); + case JContainer: + throw new ArgumentOutOfRangeException(nameof(token), "cannot deserialize a JSON comment"); + default: + throw new ArgumentOutOfRangeException(nameof(token), $"Invalid token type {token?.Type}"); + } } private Dictionary ReadDictionary(JToken element, Dictionary to) diff --git a/src/Speckle.Sdk/Api/GraphQL/Serializer/NewtonsoftJsonSerializer.cs b/src/Speckle.Sdk/Api/GraphQL/Serializer/NewtonsoftJsonSerializer.cs index ce82b9a2..80239e9d 100644 --- a/src/Speckle.Sdk/Api/GraphQL/Serializer/NewtonsoftJsonSerializer.cs +++ b/src/Speckle.Sdk/Api/GraphQL/Serializer/NewtonsoftJsonSerializer.cs @@ -1,10 +1,11 @@ -#nullable disable using System.Text; using GraphQL; using GraphQL.Client.Abstractions; using GraphQL.Client.Abstractions.Websocket; using Speckle.Newtonsoft.Json; using Speckle.Newtonsoft.Json.Serialization; +using Speckle.Sdk.Common; +using Speckle.Sdk.Serialisation; namespace Speckle.Sdk.Api.GraphQL.Serializer; @@ -27,7 +28,7 @@ internal sealed class NewtonsoftJsonSerializer : IGraphQLWebsocketJsonSerializer { ContractResolver = new CamelCasePropertyNamesContractResolver { IgnoreIsSpecifiedMembers = true }, MissingMemberHandling = MissingMemberHandling.Ignore, - Converters = { new ConstantCaseEnumConverter() } + Converters = { new ConstantCaseEnumConverter() }, }; public JsonSerializerSettings JsonSerializerSettings { get; } @@ -45,15 +46,14 @@ internal sealed class NewtonsoftJsonSerializer : IGraphQLWebsocketJsonSerializer public Task DeserializeToWebsocketResponseWrapperAsync(System.IO.Stream stream) { - return DeserializeFromUtf8Stream(stream); + return DeserializeFromUtf8Stream(stream, DefaultJsonSerializerSettings); //Ignoring the custom JsonSerializerSettings here, see https://github.com/graphql-dotnet/graphql-client/issues/660 } public GraphQLWebSocketResponse DeserializeToWebsocketResponse(byte[] bytes) { - return JsonConvert.DeserializeObject>( - Encoding.UTF8.GetString(bytes), - JsonSerializerSettings - ); + return JsonConvert + .DeserializeObject>(Encoding.UTF8.GetString(bytes), JsonSerializerSettings) + .NotNull(); } public Task> DeserializeFromUtf8StreamAsync( @@ -61,7 +61,7 @@ internal sealed class NewtonsoftJsonSerializer : IGraphQLWebsocketJsonSerializer CancellationToken cancellationToken ) { - return DeserializeFromUtf8Stream>(stream); + return DeserializeFromUtf8Stream>(stream, JsonSerializerSettings); } // deserialize extensions to Dictionary @@ -70,11 +70,14 @@ internal sealed class NewtonsoftJsonSerializer : IGraphQLWebsocketJsonSerializer JsonSerializerSettings.Converters.Insert(0, new MapConverter()); } - private Task DeserializeFromUtf8Stream(System.IO.Stream stream) + private static Task DeserializeFromUtf8Stream( + System.IO.Stream stream, + JsonSerializerSettings serializerSettings + ) { using var sr = new StreamReader(stream); - using JsonReader reader = new JsonTextReader(sr); - var serializer = JsonSerializer.Create(JsonSerializerSettings); - return Task.FromResult(serializer.Deserialize(reader)); + using JsonReader reader = SpeckleObjectSerializerPool.Instance.GetJsonTextReader(sr); + var serializer = JsonSerializer.Create(serializerSettings); + return Task.FromResult(serializer.Deserialize(reader) ?? throw new ArgumentException("Serialized data is null")); } } diff --git a/src/Speckle.Sdk/Api/GraphQL/graphql.config.yml b/src/Speckle.Sdk/Api/GraphQL/graphql.config.yml new file mode 100644 index 00000000..64c50ab2 --- /dev/null +++ b/src/Speckle.Sdk/Api/GraphQL/graphql.config.yml @@ -0,0 +1,2 @@ +schema: https://app.speckle.systems/graphql +documents: '**/*.graphql' diff --git a/src/Speckle.Sdk/Api/Helpers.cs b/src/Speckle.Sdk/Api/Helpers.cs deleted file mode 100644 index a0d1b038..00000000 --- a/src/Speckle.Sdk/Api/Helpers.cs +++ /dev/null @@ -1,197 +0,0 @@ -using System.Collections.Concurrent; -using System.Diagnostics.Contracts; -using System.Reflection; -using System.Runtime.InteropServices; -using Speckle.Newtonsoft.Json; -using Speckle.Sdk.Api.GraphQL.Models; -using Speckle.Sdk.Common; -using Speckle.Sdk.Credentials; -using Speckle.Sdk.Helpers; -using Speckle.Sdk.Host; -using Speckle.Sdk.Logging; -using Speckle.Sdk.Models; -using Speckle.Sdk.Transports; - -namespace Speckle.Sdk.Api; - -public static class Helpers -{ - public const string RELEASES_URL = "https://releases.speckle.dev"; - private const string FEEDS_ENDPOINT = RELEASES_URL + "/manager2/feeds"; - - /// - /// Helper method to Receive from a Speckle Server. - /// - /// Stream URL or Id to receive from. If the URL contains branchName, commitId or objectId those will be used, otherwise the latest commit from main will be received. - /// Account to use. If not provided the default account will be used. - /// Action invoked on progress iterations. - /// Action invoked once the total count of objects is known. - /// - public static async Task Receive( - this IServerTransportFactory serverTransportFactory, - string stream, - Account? account = null, - Action>? onProgressAction = null, - Action? onTotalChildrenCountKnown = null - ) - { - var sw = new StreamWrapper(stream); - - try - { - account ??= await sw.GetAccount().ConfigureAwait(false); - } - catch (SpeckleException) - { - if (string.IsNullOrEmpty(sw.StreamId)) - { - throw; - } - - //Fallback to a non authed account - account = new Account - { - token = "", - serverInfo = new ServerInfo { url = sw.ServerUrl }, - userInfo = new UserInfo() - }; - } - - using var client = new Client(account); - using var transport = serverTransportFactory.Create(client.Account, sw.StreamId); - - string objectId = ""; - Commit? commit = null; - - //OBJECT URL - if (!string.IsNullOrEmpty(sw.ObjectId)) - { - objectId = sw.ObjectId.NotNull(); - } - //COMMIT URL - else if (!string.IsNullOrEmpty(sw.CommitId)) - { - commit = await client.CommitGet(sw.StreamId, sw.CommitId.NotNull()).ConfigureAwait(false); - objectId = commit.referencedObject; - } - //BRANCH URL OR STREAM URL - else - { - var branchName = string.IsNullOrEmpty(sw.BranchName) ? "main" : sw.BranchName; - - var branch = await client.BranchGet(sw.StreamId, branchName.NotNull(), 1).ConfigureAwait(false); - if (branch.commits.items.Count == 0) - { - throw new SpeckleException("The selected branch has no commits."); - } - - commit = branch.commits.items[0]; - objectId = branch.commits.items[0].referencedObject; - } - - Analytics.TrackEvent( - client.Account, - Analytics.Events.Receive, - new Dictionary - { - { "sourceHostApp", HostApplications.GetHostAppFromString(commit.NotNull().sourceApplication).Slug }, - { "sourceHostAppVersion", commit.sourceApplication } - } - ); - - var receiveRes = await Operations - .Receive( - objectId, - transport, - onProgressAction: onProgressAction, - onTotalChildrenCountKnown: onTotalChildrenCountKnown - ) - .ConfigureAwait(false); - - try - { - await client - .CommitReceived( - new CommitReceivedInput - { - streamId = sw.StreamId, - commitId = commit?.id, - message = commit?.message, - sourceApplication = "Other" - } - ) - .ConfigureAwait(false); - } - catch (Exception ex) when (!ex.IsFatal()) - { - // Do nothing! - } - return receiveRes; - } - - /// - /// value to fallback to if the given is - public static string TimeAgo(DateTime? timestamp, string fallback = "Never") - { - return timestamp.HasValue ? TimeAgo(timestamp.Value) : fallback; - } - - /// Formats the given difference between the current system time and the provided - /// into a human readable string - /// - /// - /// A Human readable string - public static string TimeAgo(DateTime timestamp) - { - TimeSpan timeAgo; - - timeAgo = DateTime.UtcNow.Subtract(timestamp); - - if (timeAgo.TotalSeconds < 60) - { - return "just now"; - } - - if (timeAgo.TotalMinutes < 60) - { - return $"{timeAgo.Minutes} minute{PluralS(timeAgo.Minutes)} ago"; - } - - if (timeAgo.TotalHours < 24) - { - return $"{timeAgo.Hours} hour{PluralS(timeAgo.Hours)} ago"; - } - - if (timeAgo.TotalDays < 7) - { - return $"{timeAgo.Days} day{PluralS(timeAgo.Days)} ago"; - } - - if (timeAgo.TotalDays < 30) - { - return $"{timeAgo.Days / 7} week{PluralS(timeAgo.Days / 7)} ago"; - } - - if (timeAgo.TotalDays < 365) - { - return $"{timeAgo.Days / 30} month{PluralS(timeAgo.Days / 30)} ago"; - } - - if (timestamp <= new DateTime(1800, 1, 1)) - { - SpeckleLog.Logger.Warning( - "Tried to calculate {functionName} of a DateTime value that was way in the past: {dateTimeValue}", - nameof(TimeAgo), - timestamp - ); - // We assume this was an error, Likely a non-nullable DateTime was initialized/deserialized to the default - // Instead of potentially lying to the user, lets tell them we don't know what happened. - return "Unknown"; - } - - return $"{timeAgo.Days / 365} year{PluralS(timeAgo.Days / 365)} ago"; - } - - [Pure] - public static string PluralS(int num) => num != 1 ? "s" : ""; -} diff --git a/src/Speckle.Sdk/Api/Operations/Operations.Receive.cs b/src/Speckle.Sdk/Api/Operations/Operations.Receive.cs index 6f9fd10b..35aebb4a 100644 --- a/src/Speckle.Sdk/Api/Operations/Operations.Receive.cs +++ b/src/Speckle.Sdk/Api/Operations/Operations.Receive.cs @@ -1,5 +1,4 @@ -using System.Collections.Concurrent; -using System.Diagnostics; +using Microsoft.Extensions.Logging; using Speckle.Sdk.Logging; using Speckle.Sdk.Models; using Speckle.Sdk.Serialisation; @@ -7,8 +6,54 @@ using Speckle.Sdk.Transports; namespace Speckle.Sdk.Api; -public static partial class Operations +public partial class Operations { + /// + /// Receives a Object to the provided URL and Caches the results + /// + /// + /// No transports were specified + /// The was + /// Serialization or Send operation was unsuccessful + /// The requested cancellation + public async Task Receive2( + Uri url, + string streamId, + string objectId, + string? authorizationToken, + IProgress? onProgressAction, + CancellationToken cancellationToken + ) + { + using var receiveActivity = activityFactory.Start("Operations.Receive"); + metricsFactory.CreateCounter("Receive").Add(1); + + receiveActivity?.SetTag("objectId", objectId); + var process = serializeProcessFactory.CreateDeserializeProcess( + url, + streamId, + authorizationToken, + onProgressAction, + cancellationToken + ); + try + { + var result = await process.Deserialize(objectId).ConfigureAwait(false); + receiveActivity?.SetStatus(SdkActivityStatusCode.Ok); + return result; + } + catch (Exception ex) + { + receiveActivity?.SetStatus(SdkActivityStatusCode.Error); + receiveActivity?.RecordException(ex); + throw; + } + finally + { + await process.DisposeAsync().ConfigureAwait(false); + } + } + /// /// Receives an object (and all its sub-children) from the two provided s. ///
@@ -24,26 +69,57 @@ public static partial class Operations /// The remote transport (slower). If , will assume all objects are present in /// The local transport (faster). If , will use a default cache /// Action invoked on progress iterations - /// Action invoked once the total count of objects is known /// /// Failed to retrieve objects from the provided transport(s) /// Deserialization of the requested object(s) failed /// requested cancel /// The requested Speckle Object - public static async Task Receive( + public async Task Receive( string objectId, ITransport? remoteTransport = null, ITransport? localTransport = null, - Action>? onProgressAction = null, - Action? onTotalChildrenCountKnown = null, + IProgress? onProgressAction = null, CancellationToken cancellationToken = default ) { - // Setup Progress Reporting - var internalProgressAction = GetInternalProgressAction(onProgressAction); + using var receiveActivity = activityFactory.Start("Operations.Receive"); + metricsFactory.CreateCounter("Receive").Add(1); + if (remoteTransport != null) + { + receiveActivity?.SetTags("remoteTransportContext", remoteTransport.TransportContext); + } + receiveActivity?.SetTag("objectId", objectId); + + try + { + using IDisposable? d1 = UseDefaultTransportIfNull(localTransport, out localTransport); + receiveActivity?.SetTags("localTransportContext", localTransport.TransportContext); + + var result = await ReceiveImpl(objectId, remoteTransport, localTransport, onProgressAction, cancellationToken) + .ConfigureAwait(false); + + receiveActivity?.SetStatus(SdkActivityStatusCode.Ok); + return result; + } + catch (Exception ex) + { + receiveActivity?.SetStatus(SdkActivityStatusCode.Error); + receiveActivity?.RecordException(ex); + throw; + } + } + + /// + private async Task ReceiveImpl( + string objectId, + ITransport? remoteTransport, + ITransport localTransport, + IProgress? internalProgressAction, + CancellationToken cancellationToken + ) + { // Setup Local Transport - using IDisposable? d1 = UseDefaultTransportIfNull(localTransport, out localTransport); localTransport.OnProgressAction = internalProgressAction; localTransport.CancellationToken = cancellationToken; @@ -55,97 +131,57 @@ public static partial class Operations } // Setup Serializer - BaseObjectDeserializerV2 serializerV2 = - new() - { - ReadTransport = localTransport, - OnProgressAction = internalProgressAction, - CancellationToken = cancellationToken, - BlobStorageFolder = (remoteTransport as IBlobCapableTransport)?.BlobStorageFolder - }; - - // Setup Logging - using var receiveActivity = SpeckleActivityFactory.Start(); - receiveActivity?.SetTag("remoteTransportContext", remoteTransport?.TransportContext); - receiveActivity?.SetTag("localTransportContext", localTransport.TransportContext); - receiveActivity?.SetTag("objectId", objectId); - var timer = Stopwatch.StartNew(); - - // Receive Json - SpeckleLog.Logger.Information( - "Starting receive {objectId} from transports {localTransport} / {remoteTransport}", - objectId, - localTransport.TransportName, - remoteTransport?.TransportName - ); + SpeckleObjectDeserializer serializer = new() + { + ReadTransport = localTransport, + OnProgressAction = internalProgressAction, + CancellationToken = cancellationToken, + BlobStorageFolder = (remoteTransport as IBlobCapableTransport)?.BlobStorageFolder, + }; // Try Local Receive - string? objString = LocalReceive(objectId, localTransport, onTotalChildrenCountKnown); + string? objString = await LocalReceive(objectId, localTransport).ConfigureAwait(false); if (objString is null) { // Fall back to remote if (remoteTransport is null) { - var ex = new TransportException( + throw new TransportException( $"Could not find specified object using the local transport {localTransport.TransportName}, and you didn't provide a fallback remote from which to pull it." ); - - SpeckleLog.Logger.Error(ex, "Cannot receive object from the given transports {exceptionMessage}", ex.Message); - throw ex; } - SpeckleLog.Logger.Debug( + logger.LogDebug( "Cannot find object {objectId} in the local transport, hitting remote {transportName}", objectId, remoteTransport.TransportName ); - objString = await RemoteReceive(objectId, remoteTransport, localTransport, onTotalChildrenCountKnown) - .ConfigureAwait(false); + objString = await RemoteReceive(objectId, remoteTransport, localTransport).ConfigureAwait(false); } - using var activity = SpeckleActivityFactory.Start("Deserialize"); + using var serializerActivity = activityFactory.Start(); + // Proceed to deserialize the object, now safely knowing that all its children are present in the local (fast) transport. - Base res = serializerV2.Deserialize(objString); - - timer.Stop(); - SpeckleLog.Logger.Information( - "Finished receiving {objectId} from {source} in {elapsed} seconds", - objectId, - remoteTransport?.TransportName, - timer.Elapsed.TotalSeconds - ); - - return res; + return await DeserializeActivity(objString, serializer).ConfigureAwait(false); } /// /// Try and get the object from the local transport. If it's there, we assume all its children are there - /// This assumption is hard-wired into the + /// This assumption is hard-wired into the /// /// /// - /// /// /// - internal static string? LocalReceive( - string objectId, - ITransport localTransport, - Action? onTotalChildrenCountKnown - ) + internal static async Task LocalReceive(string objectId, ITransport localTransport) { - string? objString = localTransport.GetObject(objectId); + string? objString = await localTransport.GetObject(objectId).ConfigureAwait(false); if (objString is null) { return null; } - - // Shoot out the total children count - var closures = TransportHelpers.GetClosureTable(objString); - - onTotalChildrenCountKnown?.Invoke(closures?.Count ?? 0); - return objString; } @@ -156,19 +192,15 @@ public static partial class Operations /// /// /// - /// /// /// Remote transport was not specified private static async Task RemoteReceive( string objectId, ITransport remoteTransport, - ITransport localTransport, - Action? onTotalChildrenCountKnown + ITransport localTransport ) { - var objString = await remoteTransport - .CopyObjectAndChildren(objectId, localTransport, onTotalChildrenCountKnown) - .ConfigureAwait(false); + var objString = await remoteTransport.CopyObjectAndChildren(objectId, localTransport).ConfigureAwait(false); // DON'T THINK THIS IS NEEDED CopyObjectAndChildren should call this // Wait for the local transport to finish "writing" - in this case, it signifies that the remote transport has done pushing copying objects into it. (TODO: I can see some scenarios where latency can screw things up, and we should rather wait on the remote transport). diff --git a/src/Speckle.Sdk/Api/Operations/Operations.Send.cs b/src/Speckle.Sdk/Api/Operations/Operations.Send.cs index 8334a690..a7bb2e5c 100644 --- a/src/Speckle.Sdk/Api/Operations/Operations.Send.cs +++ b/src/Speckle.Sdk/Api/Operations/Operations.Send.cs @@ -1,31 +1,114 @@ -using System.Collections.Concurrent; using System.Diagnostics; +using Microsoft.Extensions.Logging; using Speckle.Newtonsoft.Json.Linq; using Speckle.Sdk.Logging; using Speckle.Sdk.Models; using Speckle.Sdk.Serialisation; +using Speckle.Sdk.Serialisation.V2.Send; using Speckle.Sdk.Transports; namespace Speckle.Sdk.Api; -public static partial class Operations +public partial class Operations { + /// + /// Sends a Speckle Object to the provided URL and Caches the results + /// + /// + /// No transports were specified + /// The was + /// Serialization or Send operation was unsuccessful + /// The requested cancellation + public async Task Send2( + Uri url, + string streamId, + string? authorizationToken, + Base value, + IProgress? onProgressAction, + CancellationToken cancellationToken + ) + { + using var receiveActivity = activityFactory.Start("Operations.Send"); + metricsFactory.CreateCounter("Send").Add(1); + + var process = serializeProcessFactory.CreateSerializeProcess( + url, + streamId, + authorizationToken, + onProgressAction, + cancellationToken + ); + try + { + var results = await process.Serialize(value).ConfigureAwait(false); + + receiveActivity?.SetStatus(SdkActivityStatusCode.Ok); + return results; + } + catch (Exception ex) + { + receiveActivity?.SetStatus(SdkActivityStatusCode.Error); + receiveActivity?.RecordException(ex); + throw; + } + finally + { + await process.DisposeAsync().ConfigureAwait(false); + } + } + /// /// Sends a Speckle Object to the provided and (optionally) the default local cache /// /// - /// + /// /// When , an additional will be included /// The or was /// /// using ServerTransport destination = new(account, streamId); /// var (objectId, references) = await Send(mySpeckleObject, destination, true); /// - public static async Task<(string rootObjId, IReadOnlyDictionary convertedReferences)> Send( + public async Task<(string rootObjId, IReadOnlyDictionary convertedReferences)> Send( + Base value, + IServerTransport transport, + bool useDefaultCache, + IProgress? onProgressAction = null, + CancellationToken cancellationToken = default + ) + { + if (transport is null) + { + throw new ArgumentNullException(nameof(transport), "Expected a transport to be explicitly specified"); + } + + List transports = new() { transport }; + using SQLiteTransport2? localCache = useDefaultCache + ? new SQLiteTransport2(transport.StreamId) { TransportName = "LC2" } + : null; + if (localCache is not null) + { + transports.Add(localCache); + } + + return await Send(value, transports, onProgressAction, cancellationToken).ConfigureAwait(false); + } + + /// + /// Sends a Speckle Object to the provided and (optionally) the default local cache + /// + /// + /// + /// When , an additional will be included + /// The or was + /// + /// using ServerTransport destination = new(account, streamId); + /// var (objectId, references) = await Send(mySpeckleObject, destination, true); + /// + public async Task<(string rootObjId, IReadOnlyDictionary convertedReferences)> Send( Base value, ITransport transport, bool useDefaultCache, - Action>? onProgressAction = null, + IProgress? onProgressAction = null, CancellationToken cancellationToken = default ) { @@ -58,17 +141,19 @@ public static partial class Operations /// Serialization or Send operation was unsuccessful /// One or more failed to send /// The requested cancellation - public static async Task<(string rootObjId, IReadOnlyDictionary convertedReferences)> Send( + public async Task<(string rootObjId, IReadOnlyDictionary convertedReferences)> Send( Base value, IReadOnlyCollection transports, - Action>? onProgressAction = null, + IProgress? onProgressAction = null, CancellationToken cancellationToken = default ) { +#pragma warning disable CA1510 if (value is null) { throw new ArgumentNullException(nameof(value)); } +#pragma warning restore CA1510 if (transports.Count == 0) { @@ -76,19 +161,18 @@ public static partial class Operations } // make sure all logs in the operation have the proper context - using var activity = SpeckleActivityFactory.Start(); + metricsFactory.CreateCounter("Send").Add(1); + using var activity = activityFactory.Start(); activity?.SetTag("correlationId", Guid.NewGuid().ToString()); { var sendTimer = Stopwatch.StartNew(); - SpeckleLog.Logger.Information("Starting send operation"); + logger.LogDebug("Starting send operation"); - var internalProgressAction = GetInternalProgressAction(onProgressAction); - - BaseObjectSerializerV2 serializerV2 = new(transports, internalProgressAction, true, cancellationToken); + SpeckleObjectSerializer serializerV2 = new(transports, onProgressAction, true, cancellationToken); foreach (var t in transports) { - t.OnProgressAction = internalProgressAction; + t.OnProgressAction = onProgressAction; t.CancellationToken = cancellationToken; t.BeginWrite(); } @@ -98,13 +182,13 @@ public static partial class Operations var rootObjectId = await SerializerSend(value, serializerV2, cancellationToken).ConfigureAwait(false); sendTimer.Stop(); - activity?.SetTag("transportElapsedBreakdown", transports.ToDictionary(t => t.TransportName, t => t.Elapsed)); + activity?.SetTags("transportElapsedBreakdown", transports.ToDictionary(t => t.TransportName, t => t.Elapsed)); activity?.SetTag( "note", "the elapsed summary doesn't need to add up to the total elapsed... Threading magic..." ); activity?.SetTag("serializerElapsed", serializerV2.Elapsed); - SpeckleLog.Logger.Information( + logger.LogDebug( "Finished sending objects after {elapsed}, result {objectId}", sendTimer.Elapsed.TotalSeconds, rootObjectId @@ -114,11 +198,7 @@ public static partial class Operations } catch (Exception ex) when (!ex.IsFatal()) { - SpeckleLog.Logger.Information( - ex, - "Send operation failed after {elapsed} seconds", - sendTimer.Elapsed.TotalSeconds - ); + logger.LogInformation(ex, "Send operation failed after {elapsed} seconds", sendTimer.Elapsed.TotalSeconds); if (ex is OperationCanceledException or SpeckleException) { throw; @@ -136,10 +216,10 @@ public static partial class Operations } } - /// + /// internal static async Task SerializerSend( Base value, - BaseObjectSerializerV2 serializer, + SpeckleObjectSerializer serializer, CancellationToken cancellationToken = default ) { diff --git a/src/Speckle.Sdk/Api/Operations/Operations.Serialize.cs b/src/Speckle.Sdk/Api/Operations/Operations.Serialize.cs index 41678963..075776f2 100644 --- a/src/Speckle.Sdk/Api/Operations/Operations.Serialize.cs +++ b/src/Speckle.Sdk/Api/Operations/Operations.Serialize.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Speckle.Newtonsoft.Json; using Speckle.Sdk.Logging; using Speckle.Sdk.Models; @@ -6,7 +7,7 @@ using Speckle.Sdk.Transports; namespace Speckle.Sdk.Api; -public static partial class Operations +public partial class Operations { /// /// Serializes a given object. @@ -14,32 +15,50 @@ public static partial class Operations /// /// If you want to save and persist an object to Speckle Transport or Server, /// please use any of the "Send" methods. - /// + /// /// /// The object to serialise /// /// A json string representation of the object. - public static string Serialize(Base value, CancellationToken cancellationToken = default) + public string Serialize(Base value, CancellationToken cancellationToken = default) { - var serializer = new BaseObjectSerializerV2 { CancellationToken = cancellationToken }; + var serializer = new SpeckleObjectSerializer { CancellationToken = cancellationToken }; return serializer.Serialize(value); } /// /// Note: if you want to pull an object from a Speckle Transport or Server, /// please use - /// + /// /// /// The json string representation of a speckle object that you want to deserialize /// - /// + /// /// was null /// was not valid JSON /// cannot be deserialised to type /// contains closure references (see Remarks) - public static Base Deserialize(string value, CancellationToken cancellationToken = default) + public async Task DeserializeAsync(string value, CancellationToken cancellationToken = default) { - var deserializer = new BaseObjectDeserializerV2 { CancellationToken = cancellationToken }; - return deserializer.Deserialize(value); + var deserializer = new SpeckleObjectDeserializer { CancellationToken = cancellationToken }; + return await DeserializeActivity(value, deserializer).ConfigureAwait(false); + } + + /// + private async Task DeserializeActivity([NotNull] string? objString, SpeckleObjectDeserializer deserializer) + { + using var activity = activityFactory.Start(); + try + { + Base res = await deserializer.DeserializeAsync(objString).ConfigureAwait(false); + activity?.SetStatus(SdkActivityStatusCode.Ok); + return res; + } + catch (Exception ex) + { + activity?.SetStatus(SdkActivityStatusCode.Error); + activity?.RecordException(ex); + throw; + } } } diff --git a/src/Speckle.Sdk/Api/Operations/Operations.cs b/src/Speckle.Sdk/Api/Operations/Operations.cs index 2a961774..bb0664f8 100644 --- a/src/Speckle.Sdk/Api/Operations/Operations.cs +++ b/src/Speckle.Sdk/Api/Operations/Operations.cs @@ -1,5 +1,7 @@ -using System.Collections.Concurrent; -using Speckle.Sdk.Transports; +using Microsoft.Extensions.Logging; +using Speckle.InterfaceGenerator; +using Speckle.Sdk.Logging; +using Speckle.Sdk.Serialisation.V2; namespace Speckle.Sdk.Api; @@ -8,26 +10,10 @@ namespace Speckle.Sdk.Api; /// Serialize/Deserialize /// Push/Pull (methods to serialize and send data to one or more servers) /// -public static partial class Operations -{ - /// - /// Factory for progress actions used internally inside send and receive methods. - /// - /// - /// - private static Action? GetInternalProgressAction(Action>? onProgressAction) - { - if (onProgressAction is null) - { - return null; - } - - return (args) => - { - var localProgressDict = new ConcurrentBag(); - localProgressDict.Add(args); - - onProgressAction.Invoke(localProgressDict); - }; - } -} +[GenerateAutoInterface] +public partial class Operations( + ILogger logger, + ISdkActivityFactory activityFactory, + ISdkMetricsFactory metricsFactory, + ISerializeProcessFactory serializeProcessFactory +) : IOperations; diff --git a/src/Speckle.Sdk/Common/Exceptions.cs b/src/Speckle.Sdk/Common/Exceptions.cs deleted file mode 100644 index b008e8c1..00000000 --- a/src/Speckle.Sdk/Common/Exceptions.cs +++ /dev/null @@ -1,110 +0,0 @@ -using Speckle.Sdk.Logging; - -namespace Speckle.Sdk.Common; - -/// -/// Exception thrown when conversion of an object was not successful -/// -/// -/// Ideally this exception contains a meaningful message, and reference to the object that failed to be converted. -/// This exception can be used for both ToSpeckle and ToNative conversion -/// -public class ConversionException : SpeckleException -{ - private object? ObjectThatFailed { get; } - - public ConversionException(string? message, object? objectToConvert, Exception? innerException = null) - : base(message, innerException) - { - ObjectThatFailed = objectToConvert; - } - - public ConversionException(string? message, Exception? innerException) - : base(message, innerException) { } - - public ConversionException(string? message) - : base(message) { } - - public ConversionException() { } -} - -/// -/// Exception used when an object could not be converted, because we don't support a specific conversion. -/// -/// -/// This Exception should be thrown as part of a pre-emptive check in conversions (not as part reactive error handling) -/// and usage (throwing) should not be dependent on external state: -/// i.e. given the same object and converter state, the outcome (exception throw or not) should be the same. -/// -/// -/// It can be used for: -///
    -///
  • objects who's we don't support (e.g. "Walls are not supported")
  • -///
  • objects with a property who's value we don't support (e.g. "Beams with shape type of Circular are not supported")
  • -///
  • complex object requirements (e.g. "We don't support walls with zero width and no displayValue")
  • -///
-/// It should NOT be used for: -///
    -///
  • Invalid Speckle Objects (e.g. "We don't support walls with null lines")
  • -///
  • Objects that we have already converted, and therefore now skip (e.g. "A Wall with the same name was already converted")
  • -///
  • Reactive error handling (e.g. "Failed to convert wall, I guess it wasn't supported")
  • -///
-///
-public class ConversionNotSupportedException : ConversionException -{ - public ConversionNotSupportedException(string? message, object? objectToConvert, Exception? innerException = null) - : base(message, objectToConvert, innerException) { } - - public ConversionNotSupportedException(string message, Exception innerException) - : base(message, innerException) { } - - public ConversionNotSupportedException(string message) - : base(message) { } - - public ConversionNotSupportedException() { } -} - -/// -/// Exception thrown when an object was desirably skipped
-///
-/// -/// Avoid throwing this exception Type!
-/// As it introduces some bad patterns for exception handling. -///
-/// Namely, it encodes how the exception WILL be handled, Not simply what HAS happened. -/// Exceptions shouldn't care how they are handled. -///
-/// We were also misusing this exception in Revit, to correct for ambiguity in the way certain objects should be traversed, -/// by selectively skipping objects that were already converted by other means. -///
-[Obsolete("Avoid using this type. Use " + nameof(ConversionNotSupportedException) + " instead, if appropriate")] -public class ConversionSkippedException : ConversionException -{ - public ConversionSkippedException(string? message, object? objectToConvert, Exception? innerException = null) - : base(message, objectToConvert, innerException) { } - - public ConversionSkippedException(string message, Exception innerException) - : base(message, innerException) { } - - public ConversionSkippedException(string message) - : base(message) { } - - public ConversionSkippedException() { } -} - -/// -/// Exception thrown when an object was not ready to be baked into the document (i.e. the element's host doesn't exist yet) -/// -public class ConversionNotReadyException : ConversionException -{ - public ConversionNotReadyException(string? message, object? objectToConvert, Exception? innerException = null) - : base(message, objectToConvert, innerException) { } - - public ConversionNotReadyException(string message, Exception innerException) - : base(message, innerException) { } - - public ConversionNotReadyException(string message) - : base(message) { } - - public ConversionNotReadyException() { } -} diff --git a/src/Speckle.Sdk/Common/Exceptions/ConversionException.cs b/src/Speckle.Sdk/Common/Exceptions/ConversionException.cs new file mode 100644 index 00000000..ab22cf06 --- /dev/null +++ b/src/Speckle.Sdk/Common/Exceptions/ConversionException.cs @@ -0,0 +1,19 @@ +namespace Speckle.Sdk.Common.Exceptions; + +/// +/// Exception thrown when conversion of an object was not successful +/// +/// +/// Ideally this exception contains a meaningful message. +/// This exception can be used for both ToSpeckle and ToNative conversion +/// +public class ConversionException : SpeckleException +{ + public ConversionException(string? message, Exception? innerException) + : base(message, innerException) { } + + public ConversionException(string? message) + : base(message) { } + + public ConversionException() { } +} diff --git a/src/Speckle.Sdk/Common/Exceptions/ConversionNotSupportedException.cs b/src/Speckle.Sdk/Common/Exceptions/ConversionNotSupportedException.cs new file mode 100644 index 00000000..fb7c78b9 --- /dev/null +++ b/src/Speckle.Sdk/Common/Exceptions/ConversionNotSupportedException.cs @@ -0,0 +1,28 @@ +namespace Speckle.Sdk.Common.Exceptions; + +/// +/// Exception used when an object could not be converted, because we don't support a specific conversion. +/// +/// +/// This Exception should be thrown only when a top-level converter does not exist. +/// +/// It should NOT be used for: +///
    +///
  • objects who's we don't support (e.g. "Walls are not supported")
  • +///
  • objects with a property whose value we don't support (e.g. "Beams with shape type of Circular are not supported")
  • +///
  • complex object requirements (e.g. "We don't support walls with zero width and no displayValue")
  • +///
  • Invalid Speckle Objects (e.g. "We don't support walls with null lines")
  • +///
  • Objects that we have already converted, and therefore now skip (e.g. "A Wall with the same name was already converted")
  • +///
  • Reactive error handling (e.g. "Failed to convert wall, I guess it wasn't supported")
  • +///
+///
+public class ConversionNotSupportedException : ConversionException +{ + public ConversionNotSupportedException(string message, Exception innerException) + : base(message, innerException) { } + + public ConversionNotSupportedException(string message) + : base(message) { } + + public ConversionNotSupportedException() { } +} diff --git a/src/Speckle.Sdk/Common/Exceptions/UnitNotSupportedException.cs b/src/Speckle.Sdk/Common/Exceptions/UnitNotSupportedException.cs new file mode 100644 index 00000000..198a59eb --- /dev/null +++ b/src/Speckle.Sdk/Common/Exceptions/UnitNotSupportedException.cs @@ -0,0 +1,15 @@ +namespace Speckle.Sdk.Common.Exceptions; + +/// +/// Exception thrown when a unit is encountered that is not supported, either by Speckle or the host app. +/// +public class UnitNotSupportedException : SpeckleException +{ + public UnitNotSupportedException() { } + + public UnitNotSupportedException(string message) + : base(message) { } + + public UnitNotSupportedException(string message, Exception innerException) + : base(message, innerException) { } +} diff --git a/src/Speckle.Sdk/Common/Exceptions/ValidationException.cs b/src/Speckle.Sdk/Common/Exceptions/ValidationException.cs new file mode 100644 index 00000000..4b496ec0 --- /dev/null +++ b/src/Speckle.Sdk/Common/Exceptions/ValidationException.cs @@ -0,0 +1,30 @@ +namespace Speckle.Sdk.Common.Exceptions; + +/// +/// Exception thrown when conversion cannot be performed +/// +/// Ideally, this exception should be thrown when we can pre-emptively check for invalid data that is known to cause an exception under normal circumstances +/// +/// It can be used for: +///
    +///
  • Invalid Speckle Objects (e.g. "We don't support walls with null lines")
  • +///
  • objects with a property whose value we don't support (e.g. "Beams with shape type of Circular are not supported")
  • +///
  • complex object requirements (e.g. "We don't support walls with zero width and no displayValue")
  • +///
+/// It should NOT be used for: +///
    +///
  • Objects we have no top-level converter for
  • +///
  • Objects that we have already converted, and therefore now skip (e.g. "A Wall with the same name was already converted")
  • +///
  • Reactive error handling (e.g. "Failed to convert wall, I guess it wasn't supported")
  • +///
+///
+public class ValidationException : SpeckleException +{ + public ValidationException() { } + + public ValidationException(string message) + : base(message) { } + + public ValidationException(string message, Exception innerException) + : base(message, innerException) { } +} diff --git a/src/Speckle.Sdk/Common/HashCode.cs b/src/Speckle.Sdk/Common/HashCode.cs index b1a43d24..be44eed1 100644 --- a/src/Speckle.Sdk/Common/HashCode.cs +++ b/src/Speckle.Sdk/Common/HashCode.cs @@ -1,3 +1,4 @@ +#if NETSTANDARD2_0 using System.ComponentModel; namespace Speckle.Sdk.Common; @@ -7,21 +8,21 @@ namespace Speckle.Sdk.Common; ///
public readonly struct HashCode : IEquatable { - private const int EmptyCollectionPrimeNumber = 19; - private readonly int value; + private const int EMPTY_COLLECTION_PRIME_NUMBER = 19; + private readonly int _value; /// /// Initializes a new instance of the struct. /// /// The value. - private HashCode(int value) => this.value = value; + private HashCode(int value) => _value = value; /// /// Performs an implicit conversion from to . /// /// The hash code. /// The result of the conversion. - public static implicit operator int(HashCode hashCode) => hashCode.value; + public static implicit operator int(HashCode hashCode) => hashCode.ToInt32(); /// /// Implements the operator ==. @@ -53,7 +54,7 @@ public readonly struct HashCode : IEquatable /// The type of the items. /// The collection. /// The new hash code. - public static HashCode OfEach(IEnumerable items) => + public static HashCode OfEach(IEnumerable? items) => items == null ? new HashCode(0) : new HashCode(GetHashCode(items, 0)); /// @@ -62,7 +63,7 @@ public readonly struct HashCode : IEquatable /// The type of the item. /// The item. /// The new hash code. - public HashCode And(T item) => new HashCode(CombineHashCodes(this.value, GetHashCode(item))); + public HashCode And(T item) => new HashCode(CombineHashCodes(_value, GetHashCode(item))); /// /// Adds the hash code of the specified items in the collection. @@ -70,38 +71,32 @@ public readonly struct HashCode : IEquatable /// The type of the items. /// The collection. /// The new hash code. - public HashCode AndEach(IEnumerable items) + public HashCode AndEach(IEnumerable? items) { if (items == null) { - return new HashCode(this.value); + return new HashCode(_value); } - return new HashCode(GetHashCode(items, this.value)); + return new HashCode(GetHashCode(items, _value)); } /// - public bool Equals(HashCode other) => this.value.Equals(other.value); + public bool Equals(HashCode other) => _value.Equals(other._value); /// - public override bool Equals(object obj) + public override bool Equals(object? obj) { - if (obj is HashCode) + if (obj is HashCode code) { - return this.Equals((HashCode)obj); + return Equals(code); } return false; } - /// - /// Throws . - /// - /// Does not return. - /// Implicitly convert this struct to an to get the hash code. [EditorBrowsable(EditorBrowsableState.Never)] - public override int GetHashCode() => - throw new NotSupportedException("Implicitly convert this struct to an int to get the hash code."); + public override int GetHashCode() => ToInt32(); private static int CombineHashCodes(int h1, int h2) { @@ -130,9 +125,15 @@ public readonly struct HashCode : IEquatable } else { - temp = CombineHashCodes(temp, EmptyCollectionPrimeNumber); + temp = CombineHashCodes(temp, EMPTY_COLLECTION_PRIME_NUMBER); } return temp; } + + public int ToInt32() + { + return _value; + } } +#endif diff --git a/src/Speckle.Sdk/Common/NotNullExtensions.cs b/src/Speckle.Sdk/Common/NotNullExtensions.cs index b3540309..7d07c704 100644 --- a/src/Speckle.Sdk/Common/NotNullExtensions.cs +++ b/src/Speckle.Sdk/Common/NotNullExtensions.cs @@ -5,6 +5,37 @@ namespace Speckle.Sdk.Common; public static class NotNullExtensions { + /// Thrown when the awaited returns + public static async ValueTask NotNull( + this ValueTask task, + [CallerArgumentExpression(nameof(task))] string? message = null + ) + where T : class + { + var x = await task.ConfigureAwait(false); + if (x is null) + { + throw new ArgumentNullException(message ?? "Value is null"); + } + return x; + } + + /// + public static async ValueTask NotNull( + this ValueTask task, + [CallerArgumentExpression(nameof(task))] string? message = null + ) + where T : struct + { + var x = await task.ConfigureAwait(false); + if (x is null) + { + throw new ArgumentNullException(message ?? "Value is null"); + } + return x.Value; + } + + /// public static async Task NotNull( this Task task, [CallerArgumentExpression(nameof(task))] string? message = null @@ -19,6 +50,7 @@ public static class NotNullExtensions return x; } + /// public static async Task NotNull( this Task task, [CallerArgumentExpression(nameof(task))] string? message = null @@ -33,6 +65,7 @@ public static class NotNullExtensions return x.Value; } + /// Thrown when is public static T NotNull([NotNull] this T? obj, [CallerArgumentExpression(nameof(obj))] string? paramName = null) where T : class { @@ -43,6 +76,7 @@ public static class NotNullExtensions return obj; } + /// public static T NotNull([NotNull] this T? obj, [CallerArgumentExpression(nameof(obj))] string? paramName = null) where T : struct { diff --git a/src/Speckle.Sdk/Common/Units.cs b/src/Speckle.Sdk/Common/Units.cs index 03880253..f4a7e12f 100644 --- a/src/Speckle.Sdk/Common/Units.cs +++ b/src/Speckle.Sdk/Common/Units.cs @@ -1,4 +1,5 @@ using System.Diagnostics.Contracts; +using Speckle.Sdk.Dependencies; namespace Speckle.Sdk.Common; @@ -19,8 +20,18 @@ public static class Units /// US Survey foot, now not supported by Speckle, kept privately for backwards compatibility private const string USFeet = "us_ft"; - internal static readonly List SupportedUnits = - new() { Millimeters, Centimeters, Meters, Kilometers, Inches, Feet, Yards, Miles, None }; + internal static readonly IReadOnlyCollection SupportedUnits = new[] + { + Millimeters, + Centimeters, + Meters, + Kilometers, + Inches, + Feet, + Yards, + Miles, + None, + }.Freeze(); /// /// if is a recognised/supported unit string, otherwise diff --git a/src/Speckle.Sdk/Credentials/Account.cs b/src/Speckle.Sdk/Credentials/Account.cs index 99fd42c1..b58b9c21 100644 --- a/src/Speckle.Sdk/Credentials/Account.cs +++ b/src/Speckle.Sdk/Credentials/Account.cs @@ -1,7 +1,5 @@ -#nullable disable using System.Runtime.InteropServices; using Speckle.Sdk.Api.GraphQL.Models; -using Speckle.Sdk.Common; using Speckle.Sdk.Helpers; namespace Speckle.Sdk.Credentials; @@ -49,7 +47,7 @@ public class Account : IEquatable private static string CleanURL(string server) { - if (Uri.TryCreate(server, UriKind.Absolute, out Uri newUri)) + if (Uri.TryCreate(server, UriKind.Absolute, out Uri? newUri)) { server = newUri.Authority; } @@ -73,28 +71,29 @@ public class Account : IEquatable return Crypt.Md5(CleanURL(url), "X2"); } - public async Task Validate() - { - Uri server = new(serverInfo.url); - return await AccountManager.GetUserInfo(token, server).ConfigureAwait(false); - } - public override string ToString() { return $"Account ({userInfo.email} | {serverInfo.url})"; } - public bool Equals(Account other) + public bool Equals(Account? other) { return other is not null && other.userInfo.email == userInfo.email && other.serverInfo.url == serverInfo.url; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is Account acc && Equals(acc); } - public override int GetHashCode() => HashCode.Of(userInfo.email).And(serverInfo.url); + public override int GetHashCode() + { +#if NETSTANDARD2_0 + return Speckle.Sdk.Common.HashCode.Of(userInfo.email).And(serverInfo.url); +#else + return HashCode.Combine(userInfo.email, serverInfo.url); +#endif + } #endregion diff --git a/src/Speckle.Sdk/Credentials/AccountManager.cs b/src/Speckle.Sdk/Credentials/AccountManager.cs index e817d913..2009ebbf 100644 --- a/src/Speckle.Sdk/Credentials/AccountManager.cs +++ b/src/Speckle.Sdk/Credentials/AccountManager.cs @@ -6,8 +6,9 @@ using System.Text; using System.Text.RegularExpressions; using GraphQL; using GraphQL.Client.Http; +using Microsoft.Extensions.Logging; +using Speckle.InterfaceGenerator; using Speckle.Newtonsoft.Json; -using Speckle.Sdk.Api; using Speckle.Sdk.Api.GraphQL; using Speckle.Sdk.Api.GraphQL.Models; using Speckle.Sdk.Api.GraphQL.Models.Responses; @@ -15,36 +16,55 @@ using Speckle.Sdk.Api.GraphQL.Serializer; using Speckle.Sdk.Common; using Speckle.Sdk.Helpers; using Speckle.Sdk.Logging; -using Speckle.Sdk.Transports; +using Speckle.Sdk.SQLite; using Stream = System.IO.Stream; namespace Speckle.Sdk.Credentials; +public partial interface IAccountManager : IDisposable; + /// /// Manage accounts locally for desktop applications. /// -public static class AccountManager +[GenerateAutoInterface] +public sealed class AccountManager( + ISpeckleApplication application, + ILogger logger, + ISpeckleHttp speckleHttp, + ISqLiteJsonCacheManagerFactory sqLiteJsonCacheManagerFactory +) : IAccountManager { public const string DEFAULT_SERVER_URL = "https://app.speckle.systems"; - private static readonly SQLiteTransport s_accountStorage = new(scope: "Accounts"); + private readonly ISqLiteJsonCacheManager _accountStorage = sqLiteJsonCacheManagerFactory.CreateForUser("Accounts"); private static volatile bool s_isAddingAccount; - private static readonly SQLiteTransport s_accountAddLockStorage = new(scope: "AccountAddFlow"); + private readonly ISqLiteJsonCacheManager _accountAddLockStorage = sqLiteJsonCacheManagerFactory.CreateForUser( + "AccountAddFlow" + ); + + [AutoInterfaceIgnore] + public void Dispose() + { + _accountStorage.Dispose(); + _accountAddLockStorage.Dispose(); + } /// /// Gets the basic information about a server. /// - /// Server URL + /// Server Information /// - public static async Task GetServerInfo(Uri server, CancellationToken cancellationToken = default) + /// Request failed on the HTTP layer (received a non-successful response code) + /// + public async Task GetServerInfo(Uri server, CancellationToken cancellationToken = default) { - using var httpClient = Http.GetHttpProxyClient(); + using var httpClient = speckleHttp.CreateHttpClient(); using var gqlClient = new GraphQLHttpClient( new GraphQLHttpClientOptions { EndPoint = new Uri(server, "/graphql"), - UseWebSocketForQueriesAndMutations = false + UseWebSocketForQueriesAndMutations = false, }, new NewtonsoftJsonSerializer(), httpClient @@ -74,14 +94,7 @@ public static class AccountManager var response = await gqlClient.SendQueryAsync(request, cancellationToken).ConfigureAwait(false); - if (response.Errors is not null) - { - throw new SpeckleGraphQLException( - $"GraphQL request {nameof(GetServerInfo)} failed", - request, - response - ); - } + response.EnsureGraphQLSuccess(); ServerInfo serverInfo = response.Data.serverInfo; serverInfo.url = server.ToString().TrimEnd('/'); @@ -96,14 +109,11 @@ public static class AccountManager /// /// Server URL /// - public static async Task GetUserInfo( - string token, - Uri server, - CancellationToken cancellationToken = default - ) + /// Request failed on the HTTP layer (received a non-successful response code) + /// + public async Task GetUserInfo(string token, Uri server, CancellationToken cancellationToken = default) { - using var httpClient = Http.GetHttpProxyClient(); - Http.AddAuthHeader(httpClient, token); + using var httpClient = speckleHttp.CreateHttpClient(authorizationToken: token); using var gqlClient = new GraphQLHttpClient( new GraphQLHttpClientOptions { EndPoint = new Uri(server, "/graphql") }, @@ -127,10 +137,7 @@ public static class AccountManager .SendQueryAsync>(request, cancellationToken) .ConfigureAwait(false); - if (response.Errors != null) - { - throw new SpeckleGraphQLException($"GraphQL request {nameof(GetUserInfo)} failed", request, response); - } + response.EnsureGraphQLSuccess(); return response.Data.data; } @@ -141,74 +148,58 @@ public static class AccountManager /// /// Server URL /// - internal static async Task GetUserServerInfo( + internal async Task GetUserServerInfo( string token, Uri server, CancellationToken ct = default ) { - try + using var httpClient = speckleHttp.CreateHttpClient(authorizationToken: token); + + using var client = new GraphQLHttpClient( + new GraphQLHttpClientOptions { EndPoint = new Uri(server, "/graphql") }, + new NewtonsoftJsonSerializer(), + httpClient + ); + + System.Version version = await client.GetServerVersion(ct).ConfigureAwait(false); + + // serverMigration property was added in 2.18.5, so only query for it + // if the server has been updated past that version + System.Version serverMigrationVersion = new(2, 18, 5); + + string queryString; + if (version >= serverMigrationVersion) { - using var httpClient = Http.GetHttpProxyClient(); - Http.AddAuthHeader(httpClient, token); - - using var client = new GraphQLHttpClient( - new GraphQLHttpClientOptions { EndPoint = new Uri(server, "/graphql") }, - new NewtonsoftJsonSerializer(), - httpClient - ); - - System.Version version = await client.GetServerVersion(ct).ConfigureAwait(false); - - // serverMigration property was added in 2.18.5, so only query for it - // if the server has been updated past that version - System.Version serverMigrationVersion = new(2, 18, 5); - - string queryString; - if (version >= serverMigrationVersion) - { - //language=graphql - queryString = - "query { activeUser { id name email company avatar streams { totalCount } commits { totalCount } } serverInfo { name company adminContact description version migration { movedFrom movedTo } } }"; - } - else - { - //language=graphql - queryString = - "query { activeUser { id name email company avatar streams { totalCount } commits { totalCount } } serverInfo { name company adminContact description version } }"; - } - - var request = new GraphQLRequest { Query = queryString }; - - var response = await client.SendQueryAsync(request, ct).ConfigureAwait(false); - - if (response.Errors != null) - { - throw new SpeckleGraphQLException( - $"Query {nameof(GetUserServerInfo)} failed", - request, - response - ); - } - - ServerInfo serverInfo = response.Data.serverInfo; - serverInfo.url = server.ToString().TrimEnd('/'); - serverInfo.frontend2 = await IsFrontend2Server(server).ConfigureAwait(false); - - return response.Data; + //language=graphql + queryString = + "query { activeUser { id name email company avatar streams { totalCount } commits { totalCount } } serverInfo { name company adminContact description version migration { movedFrom movedTo } } }"; } - catch (Exception ex) when (!ex.IsFatal()) + else { - throw new SpeckleException($"Failed to get user + server info from {server}", ex); + //language=graphql + queryString = + "query { activeUser { id name email company avatar streams { totalCount } commits { totalCount } } serverInfo { name company adminContact description version } }"; } + + var request = new GraphQLRequest { Query = queryString }; + + var response = await client.SendQueryAsync(request, ct).ConfigureAwait(false); + + response.EnsureGraphQLSuccess(); + + ServerInfo serverInfo = response.Data.serverInfo; + serverInfo.url = server.ToString().TrimEnd('/'); + serverInfo.frontend2 = await IsFrontend2Server(server).ConfigureAwait(false); + + return response.Data; } /// /// The Default Server URL for authentication, can be overridden by placing a file with the alternatrive url in the Speckle folder or with an ENV_VAR /// - public static string GetDefaultServerUrl() + public Uri GetDefaultServerUrl() { - var serverUrl = DEFAULT_SERVER_URL; var customServerUrl = ""; // first mechanism, check for local file @@ -227,20 +218,19 @@ public static class AccountManager if (!string.IsNullOrEmpty(customServerUrl)) { - Uri.TryCreate(customServerUrl, UriKind.Absolute, out Uri url); - if (url != null) + if (Uri.TryCreate(customServerUrl, UriKind.Absolute, out Uri? url)) { - serverUrl = customServerUrl.TrimEnd('/'); + return url; } } - return serverUrl; + return new Uri(DEFAULT_SERVER_URL); } /// The Id of the account to fetch /// /// Account with was not found - public static Account GetAccount(string id) + public Account GetAccount(string id) { return GetAccounts().FirstOrDefault(acc => acc.id == id) ?? throw new SpeckleAccountManagerException($"Account {id} not found"); @@ -250,11 +240,11 @@ public static class AccountManager /// Upgrades an account from the account.serverInfo.movedFrom account to the account.serverInfo.movedTo account /// /// Id of the account to upgrade - public static async Task UpgradeAccount(string id) + public void UpgradeAccount(string id) { Account account = GetAccount(id); - if (account.serverInfo.migration.movedTo is not Uri upgradeUri) + if (account.serverInfo.migration?.movedTo is not Uri upgradeUri) { throw new SpeckleAccountManagerException( $"Server with url {account.serverInfo.url} does not have information about the upgraded server" @@ -267,14 +257,13 @@ public static class AccountManager account.serverInfo.frontend2 = true; // setting the id to null will force it to be recreated - account.id = null; + account.id = null!; //TODO this is gross so remove when id is nullable RemoveAccount(id); - s_accountStorage.SaveObject(account.id.NotNull(), JsonConvert.SerializeObject(account)); - await s_accountStorage.WriteComplete().ConfigureAwait(false); + _accountStorage.SaveObject(account.id.NotNull(), JsonConvert.SerializeObject(account)); } - public static IEnumerable GetAccounts(string serverUrl) + public IEnumerable GetAccounts(string serverUrl) { return GetAccounts(new Uri(serverUrl)); } @@ -285,7 +274,7 @@ public static class AccountManager /// Accounts are deemed to be the same when the Account.Id matches. /// /// Uri for server. - public static IEnumerable GetAccounts(Uri serverUrl) + public IEnumerable GetAccounts(Uri serverUrl) { var accounts = GetAccounts().ToList(); List filtered = new(); @@ -315,7 +304,7 @@ public static class AccountManager /// Gets this environment's default account if any. If there is no default, the first found will be returned and set as default. /// /// The default account or null. - public static Account? GetDefaultAccount() + public Account? GetDefaultAccount() { var defaultAccount = GetAccounts().FirstOrDefault(acc => acc.isDefault); if (defaultAccount != null) @@ -326,7 +315,7 @@ public static class AccountManager var firstAccount = GetAccounts().FirstOrDefault(); if (firstAccount == null) { - SpeckleLog.Logger.Information("No Speckle accounts found. Visit the Speckle web app to create one"); + logger.LogInformation("No Speckle accounts found. Visit the Speckle web app to create one"); } return firstAccount; @@ -337,11 +326,11 @@ public static class AccountManager ///
/// This function does have potential side effects. Any invalid accounts found while enumerating will be removed /// Un-enumerated enumerable of accounts - public static IEnumerable GetAccounts() + public IEnumerable GetAccounts() { static bool IsInvalid(Account ac) => ac.userInfo == null || ac.serverInfo == null; - var sqlAccounts = s_accountStorage.GetAllObjects().Select(x => JsonConvert.DeserializeObject(x)); + var sqlAccounts = _accountStorage.GetAllObjects().Select(x => JsonConvert.DeserializeObject(x.Json)); var localAccounts = GetLocalAccounts(); foreach (var acc in sqlAccounts) @@ -368,7 +357,7 @@ public static class AccountManager /// These are accounts not handled by Manager and are stored in json format in a local directory /// /// - private static IList GetLocalAccounts() + private IList GetLocalAccounts() { var accountsDir = SpecklePathProvider.AccountsFolderPath; if (!Directory.Exists(accountsDir)) @@ -400,7 +389,7 @@ public static class AccountManager } catch (Exception ex) when (!ex.IsFatal()) { - SpeckleLog.Logger.Warning(ex, "Failed to load json account at {filePath}", file); + logger.LogWarning(ex, "Failed to load json account at {filePath}", file); } } @@ -411,7 +400,7 @@ public static class AccountManager /// Refetches user and server info for each account /// /// - public static async Task UpdateAccounts(CancellationToken ct = default) + public async Task UpdateAccounts(CancellationToken ct = default) { // need to ToList() the GetAccounts call or the UpdateObject call at the end of this method // will not work because sqlite does not support concurrent db calls @@ -452,7 +441,7 @@ public static class AccountManager } ct.ThrowIfCancellationRequested(); - s_accountStorage.UpdateObject(account.id, JsonConvert.SerializeObject(account)); + _accountStorage.SaveObject(account.id, JsonConvert.SerializeObject(account)); } } @@ -460,14 +449,14 @@ public static class AccountManager /// Removes an account /// /// ID of the account to remove - public static void RemoveAccount(string id) + public void RemoveAccount(string id) { //TODO: reset default account - s_accountStorage.DeleteObject(id); + _accountStorage.DeleteObject(id); - var accounts = GetAccounts(); - //BUG: Clearly this is a bug bug bug! - if (accounts.Any() && !accounts.Any(x => x.isDefault)) + var accounts = GetAccounts().ToArray(); + + if (accounts.Length != 0 && !accounts.Any(x => x.isDefault)) { ChangeDefaultAccount(accounts.First().id); } @@ -477,7 +466,7 @@ public static class AccountManager /// Changes the default account /// /// - public static void ChangeDefaultAccount(string id) + public void ChangeDefaultAccount(string id) { foreach (var account in GetAccounts()) { @@ -489,8 +478,7 @@ public static class AccountManager { account.isDefault = true; } - - s_accountStorage.UpdateObject(account.id, JsonConvert.SerializeObject(account)); + _accountStorage.SaveObject(account.id, JsonConvert.SerializeObject(account)); } } @@ -502,7 +490,7 @@ public static class AccountManager /// /// /// - public static Uri? GetLocalIdentifierForAccount(Account account) + public Uri? GetLocalIdentifierForAccount(Account account) { var identifier = account.GetLocalIdentifier(); @@ -512,12 +500,18 @@ public static class AccountManager return searchResult == null ? null : identifier; } + public async Task Validate(Account account) + { + Uri server = new(account.serverInfo.url); + return await GetUserInfo(account.token, server).ConfigureAwait(false); + } + /// /// Gets the account that corresponds to the given local identifier. /// /// The local identifier of the account. /// The account that matches the local identifier, or null if no match is found. - public static Account? GetAccountForLocalIdentifier(Uri localIdentifier) + public Account? GetAccountForLocalIdentifier(Uri localIdentifier) { var searchResult = GetAccounts() .FirstOrDefault(acc => @@ -529,34 +523,31 @@ public static class AccountManager return searchResult; } - private static string EnsureCorrectServerUrl(string server) + private Uri EnsureCorrectServerUrl(Uri? server) { var localUrl = server; - if (string.IsNullOrEmpty(localUrl)) + if (localUrl == null) { localUrl = GetDefaultServerUrl(); - SpeckleLog.Logger.Debug( - "The provided server url was null or empty. Changed to the default url {serverUrl}", - localUrl - ); + logger.LogDebug("The provided server url was null or empty. Changed to the default url {serverUrl}", localUrl); } - return localUrl.TrimEnd('/'); + return localUrl; } - private static void EnsureGetAccessCodeFlowIsSupported() + private void EnsureGetAccessCodeFlowIsSupported() { if (!HttpListener.IsSupported) { - SpeckleLog.Logger.Error("HttpListener not supported"); + logger.LogError("HttpListener not supported"); throw new PlatformNotSupportedException("Your operating system is not supported"); } } - private static async Task GetAccessCode(string server, string challenge, TimeSpan timeout) + private async Task GetAccessCode(Uri server, string challenge, TimeSpan timeout) { EnsureGetAccessCodeFlowIsSupported(); - SpeckleLog.Logger.Debug("Starting auth process for {server}/authn/verify/sca/{challenge}", server, challenge); + logger.LogDebug("Starting auth process for {server}/authn/verify/sca/{challenge}", server, challenge); var accessCode = ""; @@ -568,14 +559,14 @@ public static class AccountManager var localUrl = "http://localhost:29363/"; listener.Prefixes.Add(localUrl); listener.Start(); - SpeckleLog.Logger.Debug("Listening for auth redirects on {localUrl}", localUrl); + logger.LogDebug("Listening for auth redirects on {localUrl}", localUrl); // Note: The GetContext method blocks while waiting for a request. HttpListenerContext context = listener.GetContext(); HttpListenerRequest request = context.Request; HttpListenerResponse response = context.Response; accessCode = request.QueryString["access_code"]; - SpeckleLog.Logger.Debug("Got access code {accessCode}", accessCode); + logger.LogDebug("Got access code {accessCode}", accessCode); string message = accessCode != null @@ -589,7 +580,7 @@ public static class AccountManager Stream output = response.OutputStream; output.Write(buffer, 0, buffer.Length); output.Close(); - SpeckleLog.Logger.Debug("Processed finished processing the access code"); + logger.LogDebug("Processed finished processing the access code"); listener.Stop(); listener.Close(); }); @@ -599,37 +590,37 @@ public static class AccountManager // this is means the task timed out if (completedTask != task) { - SpeckleLog.Logger.Warning( + logger.LogWarning( "Local auth flow failed to complete within the timeout window. Access code is {accessCode}", accessCode ); - throw new Exception("Local auth flow failed to complete within the timeout window"); + throw new AuthFlowException("Local auth flow failed to complete within the timeout window"); } - if (task.IsFaulted) + if (task.IsFaulted && task.Exception is not null) { - SpeckleLog.Logger.Error( + logger.LogError( task.Exception, "Getting access code flow failed with {exceptionMessage}", task.Exception.Message ); - throw new Exception($"Auth flow failed: {task.Exception.Message}", task.Exception); + throw new AuthFlowException($"Auth flow failed: {task.Exception.Message}", task.Exception); } // task completed within timeout - SpeckleLog.Logger.Information( + logger.LogInformation( "Local auth flow completed successfully within the timeout window. Access code is {accessCode}", accessCode ); return accessCode; } - private static async Task CreateAccount(string accessCode, string challenge, string server) + private async Task CreateAccount(string accessCode, string challenge, Uri server) { try { var tokenResponse = await GetToken(accessCode, challenge, server).ConfigureAwait(false); - var userResponse = await GetUserServerInfo(tokenResponse.token, new(server)).ConfigureAwait(false); + var userResponse = await GetUserServerInfo(tokenResponse.token, server).ConfigureAwait(false); var account = new Account { @@ -637,9 +628,9 @@ public static class AccountManager refreshToken = tokenResponse.refreshToken, isDefault = !GetAccounts().Any(), serverInfo = userResponse.serverInfo, - userInfo = userResponse.activeUser + userInfo = userResponse.activeUser, }; - SpeckleLog.Logger.Information("Successfully created account for {serverUrl}", server); + logger.LogInformation("Successfully created account for {serverUrl}", server); return account; } @@ -649,7 +640,7 @@ public static class AccountManager } } - private static void TryLockAccountAddFlow(TimeSpan timespan) + private void TryLockAccountAddFlow(TimeSpan timespan) { // use a static variable to quickly // prevent launching this flow multiple times @@ -660,7 +651,7 @@ public static class AccountManager } // this uses the SQLite transport to store locks - var lockIds = s_accountAddLockStorage.GetAllObjects().OrderByDescending(d => d).ToList(); + var lockIds = _accountAddLockStorage.GetAllObjects().Select(x => x.Id).OrderByDescending(d => d).ToList(); var now = DateTime.Now; foreach (var l in lockIds) { @@ -680,21 +671,21 @@ public static class AccountManager } } - var lockId = Setup.ApplicationVersion + "@" + DateTime.Now.Add(timespan).ToString("o"); + var lockId = application.ApplicationAndVersion + "@" + DateTime.Now.Add(timespan).ToString("o"); // using the lock release time as an id and value // for ease of deletion and retrieval - s_accountAddLockStorage.SaveObjectSync(lockId, lockId); + _accountAddLockStorage.SaveObject(lockId, lockId); s_isAddingAccount = true; } - private static void UnlockAccountAddFlow() + private void UnlockAccountAddFlow() { s_isAddingAccount = false; // make sure all old locks are removed - foreach (var id in s_accountAddLockStorage.GetAllObjects()) + foreach (var (id, _) in _accountAddLockStorage.GetAllObjects()) { - s_accountAddLockStorage.DeleteObject(id); + _accountAddLockStorage.DeleteObject(id); } } @@ -703,9 +694,9 @@ public static class AccountManager /// /// Server to use to add the account, if not provied the default Server will be used /// - public static async Task AddAccount(string server = "") + public async Task AddAccount(Uri? server = null) { - SpeckleLog.Logger.Debug("Starting to add account for {serverUrl}", server); + logger.LogDebug("Starting to add account for {serverUrl}", server); server = EnsureCorrectServerUrl(server); @@ -727,18 +718,18 @@ public static class AccountManager var account = await CreateAccount(accessCode, challenge, server).ConfigureAwait(false); //if the account already exists it will not be added again - s_accountStorage.SaveObject(account.id, JsonConvert.SerializeObject(account)); - SpeckleLog.Logger.Debug("Finished adding account {accountId} for {serverUrl}", account.id, server); + _accountStorage.SaveObject(account.id, JsonConvert.SerializeObject(account)); + logger.LogDebug("Finished adding account {accountId} for {serverUrl}", account.id, server); } catch (SpeckleAccountManagerException ex) { - SpeckleLog.Logger.Fatal(ex, "Failed to add account: {exceptionMessage}", ex.Message); + logger.LogCritical(ex, "Failed to add account: {exceptionMessage}", ex.Message); // rethrowing any known errors throw; } catch (Exception ex) when (!ex.IsFatal()) { - SpeckleLog.Logger.Fatal(ex, "Failed to add account: {exceptionMessage}", ex.Message); + logger.LogCritical(ex, "Failed to add account: {exceptionMessage}", ex.Message); throw new SpeckleAccountManagerException($"Failed to add account: {ex.Message}", ex); } finally @@ -747,23 +738,23 @@ public static class AccountManager } } - private static async Task GetToken(string accessCode, string challenge, string server) + private async Task GetToken(string accessCode, string challenge, Uri server) { try { - using var client = Http.GetHttpProxyClient(); + using var client = speckleHttp.CreateHttpClient(); var body = new { appId = "sca", appSecret = "sca", accessCode, - challenge + challenge, }; using var content = new StringContent(JsonConvert.SerializeObject(body)); content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); - var response = await client.PostAsync($"{server}/auth/token", content).ConfigureAwait(false); + var response = await client.PostAsync(new Uri(server, "/auth/token"), content).ConfigureAwait(false); return JsonConvert .DeserializeObject(await response.Content.ReadAsStringAsync().ConfigureAwait(false)) @@ -775,17 +766,17 @@ public static class AccountManager } } - private static async Task GetRefreshedToken(string refreshToken, Uri server) + private async Task GetRefreshedToken(string refreshToken, Uri server) { try { - using var client = Http.GetHttpProxyClient(); + using var client = speckleHttp.CreateHttpClient(); var body = new { appId = "sca", appSecret = "sca", - refreshToken + refreshToken, }; using var content = new StringContent(JsonConvert.SerializeObject(body)); @@ -808,16 +799,16 @@ public static class AccountManager /// Server endpoint to get header /// if response contains FE2 header and the value was /// response contained FE2 header, but the value was , empty, or not parseable to a - /// Request to failed to send or response was not successful - private static async Task IsFrontend2Server(Uri server) + /// Request to failed to send or response was not successful + private async Task IsFrontend2Server(Uri server) { - using var httpClient = Http.GetHttpProxyClient(); + using var httpClient = speckleHttp.CreateHttpClient(); - var response = await Http.HttpPing(server).ConfigureAwait(false); + var response = await speckleHttp.HttpPing(server).ConfigureAwait(false); var headers = response.Headers; const string HEADER = "x-speckle-frontend-2"; - if (!headers.TryGetValues(HEADER, out IEnumerable values)) + if (!headers.TryGetValues(HEADER, out IEnumerable? values)) { return false; } @@ -836,10 +827,13 @@ public static class AccountManager private static string GenerateChallenge() { +#if NET8_0 + byte[] challengeData = RandomNumberGenerator.GetBytes(32); +#else using RNGCryptoServiceProvider rng = new(); byte[] challengeData = new byte[32]; rng.GetBytes(challengeData); - +#endif //escaped chars like % do not play nice with the server return Regex.Replace(Convert.ToBase64String(challengeData), @"[^\w\.@-]", ""); } diff --git a/src/Speckle.Sdk/Credentials/AuthFlowException.cs b/src/Speckle.Sdk/Credentials/AuthFlowException.cs new file mode 100644 index 00000000..cb720d00 --- /dev/null +++ b/src/Speckle.Sdk/Credentials/AuthFlowException.cs @@ -0,0 +1,14 @@ +namespace Speckle.Sdk.Credentials; + +#pragma warning disable CA2237 +public sealed class AuthFlowException : Exception +#pragma warning restore CA2237 +{ + public AuthFlowException(string? message, Exception? innerException) + : base(message, innerException) { } + + public AuthFlowException(string? message) + : base(message) { } + + public AuthFlowException() { } +} diff --git a/src/Speckle.Sdk/Credentials/Exceptions.cs b/src/Speckle.Sdk/Credentials/Exceptions.cs index fbf29439..e33738a0 100644 --- a/src/Speckle.Sdk/Credentials/Exceptions.cs +++ b/src/Speckle.Sdk/Credentials/Exceptions.cs @@ -1,5 +1,3 @@ -using Speckle.Sdk.Logging; - namespace Speckle.Sdk.Credentials; public class SpeckleAccountManagerException : SpeckleException diff --git a/src/Speckle.Sdk/Credentials/Responses.cs b/src/Speckle.Sdk/Credentials/Responses.cs index 65574da0..45d65f69 100644 --- a/src/Speckle.Sdk/Credentials/Responses.cs +++ b/src/Speckle.Sdk/Credentials/Responses.cs @@ -1,5 +1,3 @@ -using System.Runtime.InteropServices; -using Speckle.Sdk.Api; using Speckle.Sdk.Api.GraphQL.Models; namespace Speckle.Sdk.Credentials; @@ -16,8 +14,6 @@ internal sealed class TokenExchangeResponse public string refreshToken { get; init; } } -[ClassInterface(ClassInterfaceType.AutoDual)] -[ComVisible(true)] public sealed class UserInfo { public string id { get; init; } @@ -25,26 +21,4 @@ public sealed class UserInfo public string email { get; init; } public string? company { get; init; } public string? avatar { get; init; } - - [Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] - public Streams streams { get; init; } - - [Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] - public Commits commits { get; init; } -} - -[ClassInterface(ClassInterfaceType.AutoDual)] -[ComVisible(true)] -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class Streams -{ - public int totalCount { get; set; } -} - -[ClassInterface(ClassInterfaceType.AutoDual)] -[ComVisible(true)] -[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)] -public class Commits -{ - public int totalCount { get; set; } } diff --git a/src/Speckle.Sdk/Credentials/StreamWrapper.cs b/src/Speckle.Sdk/Credentials/StreamWrapper.cs deleted file mode 100644 index 95248c82..00000000 --- a/src/Speckle.Sdk/Credentials/StreamWrapper.cs +++ /dev/null @@ -1,502 +0,0 @@ -using System.Text.RegularExpressions; -using System.Web; -using Speckle.Sdk.Api; -using Speckle.Sdk.Api.GraphQL; -using Speckle.Sdk.Common; -using Speckle.Sdk.Helpers; -using Speckle.Sdk.Logging; - -namespace Speckle.Sdk.Credentials; - -public class StreamWrapper -{ - private Account? _account; - - public StreamWrapper() { } - - /// - /// Creates a StreamWrapper from a stream url or a stream id - /// - /// Stream Url eg: http://speckle.server/streams/8fecc9aa6d/commits/76a23d7179 or stream ID eg: 8fecc9aa6d - /// - public StreamWrapper(string streamUrlOrId) - { - OriginalInput = streamUrlOrId; - - if (!Uri.TryCreate(streamUrlOrId, UriKind.Absolute, out _)) - { - StreamWrapperFromId(streamUrlOrId); - } - else - { - StreamWrapperFromUrl(streamUrlOrId); - } - } - - /// - /// Creates a StreamWrapper by streamId, userId and serverUrl - /// - /// - /// - /// - public StreamWrapper(string streamId, string? userId, string serverUrl) - { - UserId = userId; - ServerUrl = serverUrl; - StreamId = streamId; - - OriginalInput = $"{ServerUrl}/streams/{StreamId}{(UserId != null ? "?u=" + UserId : "")}"; - } - - //this needs to be public so it's serialized and stored in Dynamo - public string? OriginalInput { get; set; } - - public string? UserId { get; set; } - public string ServerUrl { get; set; } - public string StreamId { get; set; } - public string? CommitId { get; set; } - - /// May be an ID instead for FE2 urls - public string? BranchName { get; set; } - public string? ObjectId { get; set; } - - /// - /// Determines if the current stream wrapper contains a valid stream. - /// - public bool IsValid => Type != StreamWrapperType.Undefined; - - public StreamWrapperType Type - { - // Quick solution to determine whether a wrapper points to a branch, commit or stream. - get - { - if (!string.IsNullOrEmpty(ObjectId)) - { - return StreamWrapperType.Object; - } - - if (!string.IsNullOrEmpty(CommitId)) - { - return StreamWrapperType.Commit; - } - - if (!string.IsNullOrEmpty(BranchName)) - { - return StreamWrapperType.Branch; - } - - // If we reach here and there is no stream id, it means that the stream is invalid for some reason. - return !string.IsNullOrEmpty(StreamId) ? StreamWrapperType.Stream : StreamWrapperType.Undefined; - } - } - - private void StreamWrapperFromId(string streamId) - { - Account? account = AccountManager.GetDefaultAccount(); - - if (account == null) - { - throw new SpeckleException("You do not have any account. Please create one or add it to the Speckle Manager."); - } - - ServerUrl = account.serverInfo.url; - UserId = account.userInfo.id; - StreamId = streamId; - } - - /// - /// The ReGex pattern to determine if a URL's AbsolutePath is a Frontend2 URL or not. - /// This is used in conjunction with to extract the correct values into the instance. - /// - private static readonly Regex s_fe2UrlRegex = - new( - @"/projects/(?[\w\d]+)(?:/models/(?[\w\d]+(?:@[\w\d]+)?)(?:,(?[\w\d]+(?:@[\w\d]+)?))*)?" - ); - - /// - /// Parses a FrontEnd2 URL Regex match and assigns it's data to this StreamWrapper instance. - /// - /// A regex match coming from - /// Will throw when the URL is not properly formatted. - /// Will throw when the URL is correct, but is not currently supported by the StreamWrapper class. - private void ParseFe2RegexMatch(Match match) - { - var projectId = match.Groups["projectId"]; - var model = match.Groups["model"]; - var additionalModels = match.Groups["additionalModels"]; - - if (!projectId.Success) - { - throw new SpeckleException("The provided url is not a valid Speckle url"); - } - - if (!model.Success) - { - throw new SpeckleException("The provided url is not pointing to any model in the project."); - } - - if (additionalModels.Success || model.Value == "all") - { - throw new NotSupportedException("Multi-model urls are not supported yet"); - } - - if (model.Value.StartsWith("$")) - { - throw new NotSupportedException("Federation model urls are not supported"); - } - - var modelRes = ParseFe2ModelValue(model.Value); - - // INFO: The Branch endpoint is being updated to fallback to checking a branch ID if no name is found. - // Assigning the BranchID as the BranchName is a workaround to support FE2 links in the old StreamWrapper. - // A better solution must be redesigned taking into account all the new Frontend2 URL features. - StreamId = projectId.Value; - BranchName = modelRes.branchId; - CommitId = modelRes.commitId; - ObjectId = modelRes.objectId; - } - - /// - /// Parses the segment of the FE2 URL that represents a modelID, modelID@versionID or objectID. - /// It is meant to parse a single value. If url is multi-model it should be used once per model. - /// - /// The a single value of the model url segment - /// A tuple containing the branch, commit and object information for that value. Each value can be null - /// Determines if a modelValue is an ObjectId by checking it's length is exactly 32 chars long. - private static (string? branchId, string? commitId, string? objectId) ParseFe2ModelValue(string modelValue) - { - if (modelValue.Length == 32) - { - return (null, null, modelValue); // Model value is an ObjectID - } - - if (!modelValue.Contains('@')) - { - return (modelValue, null, null); // Model has no version attached - } - - var res = modelValue.Split('@'); - return (res[0], res[1], null); // Model has version attached - } - - private void StreamWrapperFromUrl(string streamUrl) - { - Uri uri = new(streamUrl); - ServerUrl = uri.GetLeftPart(UriPartial.Authority); - - var fe2Match = s_fe2UrlRegex.Match(uri.AbsolutePath); - if (fe2Match.Success) - { - //NEW FRONTEND URL! - ParseFe2RegexMatch(fe2Match); - return; - } - - // Note: this is a hack. It's because new Uri() is parsed escaped in .net framework; wheareas in .netstandard it's not. - // Tests pass in Speckle.Sdk without this hack. - if (uri.Segments.Length >= 4 && uri.Segments[3]?.ToLowerInvariant() == "branches/") - { - StreamId = uri.Segments[2].Replace("/", ""); - if (uri.Segments.Length > 5) - { - var branchSegs = uri.Segments.ToList().GetRange(4, uri.Segments.Length - 4); - BranchName = Uri.UnescapeDataString(string.Concat(branchSegs)); - } - else - { - BranchName = Uri.UnescapeDataString(uri.Segments[4]); - } - } - else - { - switch (uri.Segments.Length) - { - case 3: // ie http://speckle.server/streams/8fecc9aa6d - if (uri.Segments[1].ToLowerInvariant() != "streams/") - { - throw new SpeckleException($"Cannot parse {uri} into a stream wrapper class."); - } - else - { - StreamId = uri.Segments[2].Replace("/", ""); - } - - break; - case 4: // ie https://speckle.server/streams/0c6ad366c4/globals/ - if (uri.Segments[3].ToLowerInvariant().StartsWith("globals")) - { - StreamId = uri.Segments[2].Replace("/", ""); - BranchName = Uri.UnescapeDataString(uri.Segments[3].Replace("/", "")); - } - else - { - throw new SpeckleException($"Cannot parse {uri} into a stream wrapper class"); - } - - break; - case 5: // ie http://speckle.server/streams/8fecc9aa6d/commits/76a23d7179 - switch (uri.Segments[3].ToLowerInvariant()) - { - // NOTE: this is a good practice reminder on how it should work - case "commits/": - StreamId = uri.Segments[2].Replace("/", ""); - CommitId = uri.Segments[4].Replace("/", ""); - break; - case "globals/": - StreamId = uri.Segments[2].Replace("/", ""); - BranchName = Uri.UnescapeDataString(uri.Segments[3].Replace("/", "")); - CommitId = uri.Segments[4].Replace("/", ""); - break; - case "branches/": - StreamId = uri.Segments[2].Replace("/", ""); - BranchName = Uri.UnescapeDataString(uri.Segments[4].Replace("/", "")); - break; - case "objects/": - StreamId = uri.Segments[2].Replace("/", ""); - ObjectId = uri.Segments[4].Replace("/", ""); - break; - default: - throw new SpeckleException($"Cannot parse {uri} into a stream wrapper class."); - } - - break; - - default: - throw new SpeckleException($"Cannot parse {uri} into a stream wrapper class."); - } - } - - var queryDictionary = HttpUtility.ParseQueryString(uri.Query); - UserId = queryDictionary["u"]; - } - - /// - /// Gets a valid account for this stream wrapper. - /// Note: this method ensures that the stream exists and/or that the user has an account which has access to that stream. If used in a sync manner, make sure it's not blocking. - /// - /// Throws exception if account fetching failed. This could be due to non-existent account or stream. - /// The valid account object for this stream. - public async Task GetAccount() - { - if (_account != null) - { - return _account; - } - - // Step 1: check if direct account id (?u=) - if (OriginalInput != null && OriginalInput.Contains("?u=")) - { - var userId = OriginalInput.Split(new[] { "?u=" }, StringSplitOptions.None)[1]; - var acc = AccountManager.GetAccounts().FirstOrDefault(acc => acc.userInfo.id == userId); - if (acc != null) - { - await ValidateWithAccount(acc).ConfigureAwait(false); - _account = acc; - return acc; - } - } - - // Step 2: check the default - var defAcc = AccountManager.GetDefaultAccount(); - List err = new(); - try - { - defAcc.NotNull(); - await ValidateWithAccount(defAcc).ConfigureAwait(false); - _account = defAcc; - return defAcc; - } - catch (Exception ex) when (!ex.IsFatal()) - { - err.Add(new SpeckleException($"Account {defAcc?.userInfo?.email} failed to auth stream wrapper", ex)); - } - - // Step 3: all the rest - var accs = AccountManager.GetAccounts(ServerUrl).ToList(); - if (accs.Count == 0) - { - throw new SpeckleException($"You don't have any accounts for {ServerUrl}."); - } - - foreach (var acc in accs) - { - try - { - await ValidateWithAccount(acc).ConfigureAwait(false); - _account = acc; - return acc; - } - catch (Exception ex) when (!ex.IsFatal()) - { - err.Add(new SpeckleException($"Account {acc} failed to auth stream wrapper", ex)); - } - } - - AggregateException inner = new(null, err); - throw new SpeckleException("Failed to validate stream wrapper", inner); - } - - public void SetAccount(Account acc) - { - _account = acc; - UserId = _account.userInfo.id; - } - - public bool Equals(StreamWrapper? wrapper) - { - if (wrapper == null) - { - return false; - } - - if (Type != wrapper.Type) - { - return false; - } - - return Type == wrapper.Type - && ServerUrl == wrapper.ServerUrl - && UserId == wrapper.UserId - && StreamId == wrapper.StreamId - && Type == StreamWrapperType.Branch - && BranchName == wrapper.BranchName - || Type == StreamWrapperType.Object && ObjectId == wrapper.ObjectId - || Type == StreamWrapperType.Commit && CommitId == wrapper.CommitId; - } - - /// - /// Verifies that the state of the stream wrapper represents a valid Speckle resource e.g. points to a valid stream/branch etc. - /// - /// The account to use to verify the current state of the stream wrapper - /// The of the provided is invalid or does not match the 's - /// You are not connected to the internet - /// Verification of the current state of the stream wrapper with provided was unsuccessful. The could be invalid, or lack permissions for the , or the or are invalid - public async Task ValidateWithAccount(Account acc) - { - Uri url; - try - { - url = new(ServerUrl); - } - catch (UriFormatException ex) - { - throw new ArgumentException("Server Url is improperly formatted", nameof(acc), ex); - } - - if (ServerUrl != acc.serverInfo.url && url != acc.serverInfo.migration?.movedFrom) - { - throw new ArgumentException($"Account is not from server {ServerUrl}", nameof(acc)); - } - - try - { - await Http.HttpPing(url).ConfigureAwait(false); - } - catch (HttpRequestException ex) - { - throw new HttpRequestException("You are not connected to the internet.", ex); - } - - using var client = new Client(acc); - // First check if the stream exists - try - { - await client.StreamGet(StreamId).ConfigureAwait(false); - } - catch (Exception ex) when (!ex.IsFatal()) - { - throw new SpeckleException( - $"You don't have access to stream {StreamId} on server {ServerUrl}, or the stream does not exist.", - ex - ); - } - - // Check if the branch exists - if (Type == StreamWrapperType.Branch) - { - var branch = await client.BranchGet(StreamId, BranchName.NotNull(), 1).ConfigureAwait(false); - if (branch == null) - { - throw new SpeckleException( - $"The branch with name '{BranchName}' doesn't exist in stream {StreamId} on server {ServerUrl}" - ); - } - } - } - - public Uri ToServerUri() - { - if (_account != null) - { - return _account.serverInfo.frontend2 ? ToProjectUri() : ToStreamUri(); - } - - if (OriginalInput != null) - { - Uri uri = new(OriginalInput); - var fe2Match = s_fe2UrlRegex.Match(uri.AbsolutePath); - return fe2Match.Success ? ToProjectUri() : ToStreamUri(); - } - - // Default to old FE1 - return ToStreamUri(); - } - - private Uri ToProjectUri() - { - var uri = new Uri(ServerUrl); - - // TODO: THis has to be the branch ID or it won't work. - var branchID = BranchName; - var leftPart = $"projects/{StreamId}/models/"; - switch (Type) - { - case StreamWrapperType.Commit: - leftPart += $"{branchID}@{CommitId}"; - break; - case StreamWrapperType.Branch: - leftPart += $"{branchID}"; - break; - case StreamWrapperType.Object: - leftPart += $"{ObjectId}"; - break; - } - var acc = $"{(UserId != null ? "?u=" + UserId : "")}"; - - var finalUri = new Uri(uri, leftPart + acc); - return finalUri; - } - - private Uri ToStreamUri() - { - var uri = new Uri(ServerUrl); - var leftPart = $"streams/{StreamId}"; - switch (Type) - { - case StreamWrapperType.Commit: - leftPart += $"/commits/{CommitId}"; - break; - case StreamWrapperType.Branch: - leftPart += $"/branches/{BranchName}"; - break; - case StreamWrapperType.Object: - leftPart += $"/objects/{ObjectId}"; - break; - } - var acc = $"{(UserId != null ? "?u=" + UserId : "")}"; - - var finalUri = new Uri(uri, leftPart + acc); - return finalUri; - } - - public override string ToString() => ToServerUri().ToString(); -} - -public enum StreamWrapperType -{ - Undefined, - Stream, - Commit, - Branch, - Object -} diff --git a/src/Speckle.Sdk/ExceptionHelpers.cs b/src/Speckle.Sdk/ExceptionHelpers.cs index 7e2b077f..1e43fca0 100644 --- a/src/Speckle.Sdk/ExceptionHelpers.cs +++ b/src/Speckle.Sdk/ExceptionHelpers.cs @@ -37,8 +37,7 @@ public static class ExceptionHelpers or InvalidProgramException or AccessViolationException or AppDomainUnloadedException - or BadImageFormatException - => true, + or BadImageFormatException => true, _ => false, }; } diff --git a/src/Speckle.Sdk/Helpers/Constants.cs b/src/Speckle.Sdk/Helpers/Constants.cs index e8045ec7..13f340c9 100644 --- a/src/Speckle.Sdk/Helpers/Constants.cs +++ b/src/Speckle.Sdk/Helpers/Constants.cs @@ -1,5 +1,3 @@ -using System.Text.RegularExpressions; - namespace Speckle.Sdk.Helpers; public static class Constants @@ -7,6 +5,4 @@ public static class Constants public const double EPS = 1e-5; public const double SMALL_EPS = 1e-8; public const double EPS_SQUARED = EPS * EPS; - - public static readonly Regex ChunkPropertyNameRegex = new(@"^@\((\d*)\)"); //TODO: Experiment with compiled flag } diff --git a/src/Speckle.Sdk/Helpers/Crypt.cs b/src/Speckle.Sdk/Helpers/Crypt.cs index 6046a1cd..66aaa7c8 100644 --- a/src/Speckle.Sdk/Helpers/Crypt.cs +++ b/src/Speckle.Sdk/Helpers/Crypt.cs @@ -2,25 +2,62 @@ using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Security.Cryptography; using System.Text; +#if NET6_0_OR_GREATER +using System.Runtime.InteropServices; +#endif namespace Speckle.Sdk.Helpers; public static class Crypt { +#if NET6_0_OR_GREATER /// the value to hash - /// NumericFormat - /// - /// + /// "x2" for lower case, "X2" for uppercase. + /// Desired length of the returned string. Must be 2 ≤ Length ≤ 64, and must be a multiple of 2 + /// + [Pure] + public static string Sha256( + ReadOnlySpan input, + [StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format = "x2", + int length = SHA256.HashSizeInBytes * sizeof(char) + ) + { + ReadOnlySpan inputBytes = MemoryMarshal.AsBytes(input); + + Span hash = stackalloc byte[SHA256.HashSizeInBytes]; + SHA256.HashData(inputBytes, hash); + + Span output = stackalloc char[length]; + + for (int i = 0, j = 0; j < length; i += sizeof(byte), j += sizeof(char)) + { + hash[i].TryFormat(output[j..], out _, format); + } + + return new string(output); + } +#endif + + /// the value to hash + /// "x2" for lower case, "X2" for uppercase. + /// Desired length of the returned string /// the hash string /// is not a recognised numeric format /// [Pure] - public static string Sha256(string input, string? format = "x2", int startIndex = 0, int length = 64) + public static string Sha256( + string input, + [StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format = "x2", + int length = 64 + ) { - var inputBytes = Encoding.UTF8.GetBytes(input); - + var inputBytes = Encoding.Unicode.GetBytes(input); +#if NET6_0_OR_GREATER + byte[] hash = SHA256.HashData(inputBytes); +#else using var sha256 = SHA256.Create(); byte[] hash = sha256.ComputeHash(inputBytes); +#endif StringBuilder sb = new(64); foreach (byte b in hash) @@ -28,25 +65,32 @@ public static class Crypt sb.Append(b.ToString(format)); } - return sb.ToString(startIndex, length); + return sb.ToString(0, length); } - /// + /// /// MD5 is a broken cryptographic algorithm and should be used subject to review see CA5351 [Pure] [SuppressMessage("Security", "CA5351:Do Not Use Broken Cryptographic Algorithms")] - public static string Md5(string input, string? format = "x2", int startIndex = 0, int length = 32) + public static string Md5( + string input, + [StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format = "x2", + int length = 32 + ) { - using MD5 md5 = MD5.Create(); byte[] inputBytes = Encoding.ASCII.GetBytes(input.ToLowerInvariant()); +#if NETSTANDARD2_0 + using MD5 md5 = MD5.Create(); byte[] hashBytes = md5.ComputeHash(inputBytes); - +#else + byte[] hashBytes = MD5.HashData(inputBytes); +#endif StringBuilder sb = new(32); for (int i = 0; i < hashBytes.Length; i++) { sb.Append(hashBytes[i].ToString(format)); } - return sb.ToString(startIndex, length); + return sb.ToString(0, length); } } diff --git a/src/Speckle.Sdk/Helpers/Http.cs b/src/Speckle.Sdk/Helpers/Http.cs index b1b38811..a468abd0 100644 --- a/src/Speckle.Sdk/Helpers/Http.cs +++ b/src/Speckle.Sdk/Helpers/Http.cs @@ -1,167 +1,56 @@ using System.Net; -using System.Net.NetworkInformation; -using System.Net.Sockets; -using Polly; -using Polly.Contrib.WaitAndRetry; -using Polly.Extensions.Http; -using Polly.Timeout; +using Microsoft.Extensions.Logging; +using Speckle.InterfaceGenerator; using Speckle.Sdk.Common; -using Speckle.Sdk.Credentials; -using Speckle.Sdk.Logging; namespace Speckle.Sdk.Helpers; -public static class Http +[GenerateAutoInterface] +public class SpeckleHttp(ILogger logger, ISpeckleHttpClientHandlerFactory speckleHttpClientHandlerFactory) + : ISpeckleHttp { - public const int DEFAULT_TIMEOUT_SECONDS = 60; - - public static IEnumerable DefaultDelay() - { - return Backoff.DecorrelatedJitterBackoffV2(TimeSpan.FromMilliseconds(200), 5); - } - - public static IAsyncPolicy HttpAsyncPolicy( - IEnumerable? delay = null, - int timeoutSeconds = DEFAULT_TIMEOUT_SECONDS - ) - { - var retryPolicy = HttpPolicyExtensions - .HandleTransientHttpError() - .Or() - .WaitAndRetryAsync( - delay ?? DefaultDelay(), - (ex, timeSpan, retryAttempt, context) => - { - context.Remove("retryCount"); - context.Add("retryCount", retryAttempt); - } - ); - - var timeoutPolicy = Policy.TimeoutAsync(timeoutSeconds); - - return Policy.WrapAsync(retryPolicy, timeoutPolicy); - } - - /// - /// Checks if the user has a valid internet connection by first pinging cloudfare (fast) - /// and then trying get from the default Speckle server (slower) - /// - /// True if the user is connected to the internet, false otherwise. - public static async Task UserHasInternet() - { - string? defaultServer = null; - try - { - //Perform a quick ping test e.g. to cloudflaire dns, as is quicker than pinging server - if (await Ping("1.1.1.1").ConfigureAwait(false)) - { - return true; - } - - defaultServer = AccountManager.GetDefaultServerUrl(); - Uri serverUrl = new(defaultServer); - await HttpPing(serverUrl).ConfigureAwait(false); - return true; - } - catch (HttpRequestException ex) - { - using var activity = SpeckleActivityFactory.Start(); - activity?.SetTag("defaultServer", defaultServer); - SpeckleLog.Logger.Warning(ex, "Failed to ping internet"); - - return false; - } - } - - /// - /// Pings a specific url to verify it's accessible. Retries 3 times. - /// - /// The hostname or address to ping. - /// True if the the status code is 200, false otherwise. - public static async Task Ping(string hostnameOrAddress) - { - SpeckleLog.Logger.Information("Pinging {hostnameOrAddress}", hostnameOrAddress); - var policy = Policy - .Handle() - .Or() - .WaitAndRetryAsync( - DefaultDelay(), - (ex, timeSpan, retryAttempt, context) => { - //Log.Information( - // ex, - // "The http request failed with {exceptionType} exception retrying after {cooldown} milliseconds. This is retry attempt {retryAttempt}", - // ex.GetType().Name, - // timeSpan.TotalSeconds * 1000, - // retryAttempt - //); - } - ); - var policyResult = await policy - .ExecuteAndCaptureAsync(async () => - { - Ping myPing = new(); - var hostname = - Uri.CheckHostName(hostnameOrAddress) != UriHostNameType.Unknown - ? hostnameOrAddress - : new Uri(hostnameOrAddress).DnsSafeHost; - byte[] buffer = new byte[32]; - int timeout = 1000; - PingOptions pingOptions = new(); - PingReply reply = await myPing.SendPingAsync(hostname, timeout, buffer, pingOptions).ConfigureAwait(false); - if (reply.Status != IPStatus.Success) - { - throw new SpeckleException($"The ping operation failed with status {reply.Status}"); - } - - return true; - }) - .ConfigureAwait(false); - if (policyResult.Outcome == OutcomeType.Successful) - { - return true; - } - - SpeckleLog.Logger.Warning( - policyResult.FinalException, - "Failed to ping {hostnameOrAddress} cause: {exceptionMessage}", - policyResult.FinalException.Message - ); - return false; - } - /// /// Sends a GET request to the provided /// /// The URI that should be pinged - /// Request to failed - public static async Task HttpPing(Uri uri) + /// Request to failed + public async Task HttpPing(Uri uri) { try { - using var httpClient = GetHttpProxyClient(); + using var httpClient = CreateHttpClient(); HttpResponseMessage response = await httpClient.GetAsync(uri).ConfigureAwait(false); response.EnsureSuccessStatusCode(); - SpeckleLog.Logger.Information("Successfully pinged {uri}", uri); + logger.LogInformation("Successfully pinged {uri}", uri); return response; } catch (HttpRequestException ex) { - SpeckleLog.Logger.Warning(ex, "Ping to {uri} was unsuccessful: {message}", uri, ex.Message); + logger.LogWarning(ex, "Ping to {uri} was unsuccessful: {message}", uri, ex.Message); throw new HttpRequestException($"Ping to {uri} was unsuccessful", ex); } } - public static HttpClient GetHttpProxyClient(SpeckleHttpClientHandler? speckleHttpClientHandler = null) + public const int DEFAULT_TIMEOUT_SECONDS = 60; + + public HttpClient CreateHttpClient( + HttpMessageHandler? innerHandler = null, + int timeoutSeconds = DEFAULT_TIMEOUT_SECONDS, + string? authorizationToken = null + ) { IWebProxy proxy = WebRequest.GetSystemWebProxy(); proxy.Credentials = CredentialCache.DefaultCredentials; - speckleHttpClientHandler ??= new SpeckleHttpClientHandler(new HttpClientHandler(), HttpAsyncPolicy()); + var speckleHandler = speckleHttpClientHandlerFactory.Create(innerHandler, timeoutSeconds); - var client = new HttpClient(speckleHttpClientHandler) + var client = new HttpClient(speckleHandler) { - Timeout = Timeout.InfiniteTimeSpan //timeout is configured on the SpeckleHttpClientHandler through policy + Timeout = + Timeout.InfiniteTimeSpan //timeout is configured on the SpeckleHttpClientHandler through policy + , }; + AddAuthHeader(client, authorizationToken); return client; } @@ -169,7 +58,9 @@ public static class Http { if (!string.IsNullOrEmpty(authToken)) { - bearerHeader = authToken.NotNull().ToLowerInvariant().Contains("bearer") ? authToken : $"Bearer {authToken}"; + bearerHeader = authToken.NotNull().StartsWith("bearer", StringComparison.InvariantCultureIgnoreCase) + ? authToken + : $"Bearer {authToken}"; return true; } @@ -177,7 +68,7 @@ public static class Http return false; } - public static void AddAuthHeader(HttpClient client, string? authToken) + private static void AddAuthHeader(HttpClient client, string? authToken) { if (CanAddAuth(authToken, out string? value)) { diff --git a/src/Speckle.Sdk/Helpers/PropNameValidator.cs b/src/Speckle.Sdk/Helpers/PropNameValidator.cs new file mode 100644 index 00000000..c147e444 --- /dev/null +++ b/src/Speckle.Sdk/Helpers/PropNameValidator.cs @@ -0,0 +1,43 @@ +using System.Diagnostics.Contracts; +using System.Text.RegularExpressions; + +namespace Speckle.Sdk.Helpers; + +public static +#if NET7_0_OR_GREATER +partial +#endif +class PropNameValidator +{ + private const string CHUNK_PROPERTY_NAME_REGEX_STRING = @"^@\((\d*)\)"; + +#if NET7_0_OR_GREATER + [GeneratedRegex(CHUNK_PROPERTY_NAME_REGEX_STRING)] + private static partial Regex ChunkRegex(); + + private static readonly Regex ChunkPropertyNameRegex = ChunkRegex(); +#else + private static readonly Regex ChunkPropertyNameRegex = new(CHUNK_PROPERTY_NAME_REGEX_STRING); +#endif + + public static bool IsChunkable(string propName, out int chunkSize) + { + if (ChunkPropertyNameRegex.IsMatch(propName)) + { + var match = ChunkPropertyNameRegex.Match(propName); + var isChunkable = int.TryParse(match.Groups[^1].Value, out chunkSize); + return isChunkable; + } + + chunkSize = -1; + return false; + } + + [Pure] + public static bool IsDetached(string propName) => +#if NETSTANDARD2_0 + propName.StartsWith("@"); +#else + propName.StartsWith('@'); +#endif +} diff --git a/src/Speckle.Sdk/Helpers/SerializerIdWriter.cs b/src/Speckle.Sdk/Helpers/SerializerIdWriter.cs new file mode 100644 index 00000000..10dc3d03 --- /dev/null +++ b/src/Speckle.Sdk/Helpers/SerializerIdWriter.cs @@ -0,0 +1,621 @@ +using System.Text; +using Speckle.Newtonsoft.Json; +using Speckle.Sdk.Dependencies; +using Speckle.Sdk.Serialisation; + +namespace Speckle.Sdk.Helpers; + +public sealed class SerializerIdWriter : JsonWriter +{ + private readonly JsonWriter _jsonWriter; +#pragma warning disable CA2213 + private readonly JsonWriter _jsonIdWriter; + private readonly StringWriter _idWriter; + private readonly StringBuilder _stringBuilder; +#pragma warning restore CA2213 + + public SerializerIdWriter(JsonWriter jsonWriter) + { + _jsonWriter = jsonWriter; + _stringBuilder = Pools.StringBuilders.Get(); + _idWriter = new StringWriter(_stringBuilder); + _jsonIdWriter = SpeckleObjectSerializerPool.Instance.GetJsonTextWriter(_idWriter); + } + + public (Json, JsonWriter) FinishIdWriter() + { + _jsonIdWriter.WriteEndObject(); + _jsonIdWriter.Flush(); + var json = new Json(_idWriter.ToString()); + Pools.StringBuilders.Return(_stringBuilder); + return (json, _jsonWriter); + } + + public override void WriteValue(string? value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteStartArray() + { + _jsonWriter.WriteStartArray(); + _jsonIdWriter.WriteStartArray(); + } + + public override void WriteEndArray() + { + _jsonWriter.WriteEndArray(); + _jsonIdWriter.WriteEndArray(); + } + + public override void WriteStartObject() + { + _jsonWriter.WriteStartObject(); + _jsonIdWriter.WriteStartObject(); + } + + public override void WriteEndObject() + { + _jsonWriter.WriteEndObject(); + _jsonIdWriter.WriteEndObject(); + } + + public override void WriteComment(string? text) + { + _jsonWriter.WriteComment(text); + _jsonIdWriter.WriteComment(text); + } + + public override void WritePropertyName(string name) + { + _jsonWriter.WritePropertyName(name); + _jsonIdWriter.WritePropertyName(name); + } + + public override void WriteNull() + { + _jsonWriter.WriteNull(); + _jsonIdWriter.WriteNull(); + } + + public override void WriteUndefined() + { + _jsonWriter.WriteUndefined(); + _jsonIdWriter.WriteUndefined(); + } + + public override void WriteRaw(string? json) + { + _jsonWriter.WriteRaw(json); + _jsonIdWriter.WriteRaw(json); + } + + public override void WriteRawValue(string? json) + { + _jsonWriter.WriteRawValue(json); + _jsonIdWriter.WriteRawValue(json); + } + + public override void WriteValue(bool value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(bool? value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(byte value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(byte? value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(char value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(char? value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(DateTime value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(DateTime? value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(DateTimeOffset value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(DateTimeOffset? value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(decimal value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(decimal? value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(double value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(double? value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(float value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(float? value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(Guid value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(Guid? value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(int value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(int? value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(long value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(long? value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(sbyte value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(sbyte? value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(short value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(short? value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(TimeSpan value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(TimeSpan? value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(uint value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(uint? value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(ulong value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(ulong? value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(Uri? value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(ushort value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(ushort? value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(byte[]? value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void WriteValue(object? value) + { + _jsonWriter.WriteValue(value); + _jsonIdWriter.WriteValue(value); + } + + public override void Flush() + { + _jsonWriter.Flush(); + _jsonIdWriter.Flush(); + } + + public override async Task WriteValueAsync(string? value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteStartArrayAsync(CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteStartArrayAsync(cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteStartArrayAsync(cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteEndArrayAsync(CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteEndArrayAsync(cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteEndArrayAsync(cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteStartObjectAsync(CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteStartObjectAsync(cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteStartObjectAsync(cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteEndObjectAsync(CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteEndObjectAsync(cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteEndObjectAsync(cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteCommentAsync(string? text, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteCommentAsync(text, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteCommentAsync(text, cancellationToken).ConfigureAwait(false); + } + + public override async Task WritePropertyNameAsync(string name, CancellationToken cancellationToken = default) + { + await _jsonWriter.WritePropertyNameAsync(name, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WritePropertyNameAsync(name, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteNullAsync(CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteNullAsync(cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteNullAsync(cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteUndefinedAsync(CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteUndefinedAsync(cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteUndefinedAsync(cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteRawAsync(string? json, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteRawAsync(json, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteRawAsync(json, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteRawValueAsync(string? json, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteRawValueAsync(json, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteRawValueAsync(json, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(bool value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(bool? value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(byte value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(byte? value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(char value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(char? value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(DateTime value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(DateTime? value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(DateTimeOffset value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(DateTimeOffset? value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(decimal value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(decimal? value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(double value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(double? value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(float value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(float? value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(Guid value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(Guid? value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(int value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(int? value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(long value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(long? value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(sbyte value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(sbyte? value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(short value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(short? value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(TimeSpan value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(TimeSpan? value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(uint value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(uint? value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(ulong value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(ulong? value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(Uri? value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(ushort value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(ushort? value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(byte[]? value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task WriteValueAsync(object? value, CancellationToken cancellationToken = default) + { + await _jsonWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.WriteValueAsync(value, cancellationToken).ConfigureAwait(false); + } + + public override async Task FlushAsync(CancellationToken cancellationToken = default) + { + await _jsonWriter.FlushAsync(cancellationToken).ConfigureAwait(false); + await _jsonIdWriter.FlushAsync(cancellationToken).ConfigureAwait(false); + } +} diff --git a/src/Speckle.Sdk/Helpers/State.cs b/src/Speckle.Sdk/Helpers/State.cs deleted file mode 100644 index 657b7225..00000000 --- a/src/Speckle.Sdk/Helpers/State.cs +++ /dev/null @@ -1,87 +0,0 @@ -#nullable disable -namespace Speckle.Sdk.Helpers; - -public class State : IDisposable - where T : State, new() -{ - private static T root; - private static T current; - private T previous = current; - - private static readonly object StateChangeLock = new(); - - protected State() - { - lock (StateChangeLock) - { - current = (T)this; - if (root == null) - { - root = (T)this; - } - } - } - - void IDisposable.Dispose() - { - lock (StateChangeLock) - { - if (previous == null) - { - return; // Already disposed or root - } - - if (current == this) - { - current = previous; - } - else - { - // If this state is still in the stack is safe to pop it - var state = this; - do - { - if (state == root) - { - current = previous; - break; - } - - state = state.previous; - } while (state != null); - } - - previous = null; - } - } - - public static T Peek => current; - - public static T Push() - { - lock (StateChangeLock) - { - var peek = current ?? new T(); - var top = (T)peek.MemberwiseClone(); - top.previous = current; - - return current = top; - } - } - - public static void Pop() - { - lock (StateChangeLock) - { - ((IDisposable)current).Dispose(); - } - } - - protected void Pull() - { - lock (StateChangeLock) - { - ((IDisposable)this).Dispose(); - } - } -} diff --git a/src/Speckle.Sdk/Host/Attributes.cs b/src/Speckle.Sdk/Host/Attributes.cs index 26273628..5887f6af 100644 --- a/src/Speckle.Sdk/Host/Attributes.cs +++ b/src/Speckle.Sdk/Host/Attributes.cs @@ -1,13 +1,12 @@ -#nullable disable namespace Speckle.Sdk.Host; [AttributeUsage(AttributeTargets.Constructor)] -public sealed class SchemaInfo : Attribute +public sealed class SchemaInfoAttribute : Attribute { - public SchemaInfo(string name, string description) + public SchemaInfoAttribute(string name, string description) : this(name, description, null, null) { } - public SchemaInfo(string name, string description, string category, string subcategory) + public SchemaInfoAttribute(string name, string description, string? category, string? subcategory) { Name = name; Description = description; @@ -15,9 +14,9 @@ public sealed class SchemaInfo : Attribute Subcategory = subcategory; } - public string Subcategory { get; } + public string? Subcategory { get; } - public string Category { get; } + public string? Category { get; } public string Description { get; } @@ -25,12 +24,12 @@ public sealed class SchemaInfo : Attribute } [AttributeUsage(AttributeTargets.Constructor)] -public sealed class SchemaDeprecated : Attribute { } +public sealed class SchemaDeprecatedAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter)] -public sealed class SchemaParamInfo : Attribute +public sealed class SchemaParamInfoAttribute : Attribute { - public SchemaParamInfo(string description) + public SchemaParamInfoAttribute(string description) { Description = description; } @@ -42,7 +41,7 @@ public sealed class SchemaParamInfo : Attribute /// Used to indicate which is the main input parameter of the schema builder component. Schema info will be attached to this object. /// [AttributeUsage(AttributeTargets.Parameter)] -public sealed class SchemaMainParam : Attribute { } +public sealed class SchemaMainParamAttribute : Attribute { } // TODO: this could be nuked, as it's only used to hide props on Base, // which we might want to expose anyways... @@ -50,7 +49,7 @@ public sealed class SchemaMainParam : Attribute { } /// Used to ignore properties from expand objects etc /// [AttributeUsage(AttributeTargets.Property)] -public sealed class SchemaIgnore : Attribute { } +public sealed class SchemaIgnoreAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method)] public sealed class SchemaComputedAttribute : Attribute diff --git a/src/Speckle.Sdk/Host/HostAppVersion.cs b/src/Speckle.Sdk/Host/HostAppVersion.cs index cd6c41ba..2dc6c540 100644 --- a/src/Speckle.Sdk/Host/HostAppVersion.cs +++ b/src/Speckle.Sdk/Host/HostAppVersion.cs @@ -13,9 +13,11 @@ public enum HostAppVersion v2023, v2024, v2025, + v21, + v22, v25, v26, v715, v716, - v717 + v717, } diff --git a/src/Speckle.Sdk/Host/TypeLoader.cs b/src/Speckle.Sdk/Host/TypeLoader.cs index 1464a977..18159abe 100644 --- a/src/Speckle.Sdk/Host/TypeLoader.cs +++ b/src/Speckle.Sdk/Host/TypeLoader.cs @@ -14,13 +14,13 @@ public static class TypeLoader private static ConcurrentDictionary s_cachedTypes = new(); private static ConcurrentDictionary s_fullTypeStrings = new(); - private static ConcurrentDictionary s_jsonPropertyAttribute = new(); + private static ConcurrentDictionary s_jsonPropertyAttribute = new(); private static ConcurrentDictionary> s_propInfoCache = new(); public static IEnumerable Types => s_availableTypes; public static JsonPropertyAttribute? GetJsonPropertyAttribute(PropertyInfo property) => - s_jsonPropertyAttribute.GetOrAdd(property, (p) => p.GetCustomAttribute(true)); + s_jsonPropertyAttribute.GetOrAdd(property, p => p.GetCustomAttribute(true)); public static void Initialize(params Assembly[] assemblies) { @@ -171,6 +171,10 @@ public static class TypeLoader throw new InvalidOperationException($"{type.FullName} inherits from Base has no SpeckleTypeAttribute"); } var deprecatedSpeckleTypes = type.GetCustomAttributes(); - return new LoadedType(speckleType.Name, type, deprecatedSpeckleTypes.Select(x => x.Name).ToList()); + return new LoadedType( + speckleType.SpeckleTypeName, + type, + deprecatedSpeckleTypes.Select(x => x.SpeckleTypeName).ToList() + ); } } diff --git a/src/Speckle.Sdk/Logging/Analytics.cs b/src/Speckle.Sdk/Logging/Analytics.cs deleted file mode 100644 index 7e35564c..00000000 --- a/src/Speckle.Sdk/Logging/Analytics.cs +++ /dev/null @@ -1,342 +0,0 @@ -#nullable disable -using System.Diagnostics; -using System.Net.Http.Headers; -using System.Net.NetworkInformation; -using System.Reflection; -using System.Runtime.InteropServices; -using System.Text; -using System.Web; -using Speckle.Newtonsoft.Json; -using Speckle.Sdk.Credentials; -using Speckle.Sdk.Helpers; - -namespace Speckle.Sdk.Logging; - -/// -/// Anonymous telemetry to help us understand how to make a better Speckle. -/// This really helps us to deliver a better open source project and product! -/// -public static class Analytics -{ - /// - /// Default Mixpanel events - /// - public enum Events - { - /// - /// Event triggered when data is sent to a Speckle Server - /// - Send, - - /// - /// Event triggered when data is received from a Speckle Server - /// - Receive, - - /// - /// Event triggered when a node is executed in a visual programming environment, it should contain the name of the action and the host application - /// - NodeRun, - - /// - /// Event triggered when an action is executed in Desktop UI, it should contain the name of the action and the host application - /// - DUIAction, - - /// - /// Event triggered when a node is first created in a visual programming environment, it should contain the name of the action and the host application - /// - NodeCreate, - - /// - /// Event triggered when the import/export alert is launched or closed - /// - ImportExportAlert, - - /// - /// Event triggered when the connector is registered - /// - Registered, - - /// - /// Event triggered by the Mapping Tool - /// - MappingsAction, - - /// - /// Event triggered when user selects object to convert to Speckle on Send - /// - ConvertToSpeckle, - - /// - /// Event triggered when user selects object to convert to Native on Receive - /// - ConvertToNative - } - - private const string MIXPANEL_TOKEN = "acd87c5a50b56df91a795e999812a3a4"; - private const string MIXPANEL_SERVER = "https://analytics.speckle.systems"; - - /// - /// Cached email - /// - private static string LastEmail { get; set; } - - /// - /// Cached server URL - /// - private static string LastServer { get; set; } - - /// - /// when the DEBUG pre-processor directive is , otherwise - /// - /// This must be kept as a computed property, not a compile time const - internal static bool IsReleaseMode => -#if DEBUG - false; -#else - true; -#endif - - /// - /// Tracks an event without specifying the email and server. - /// It's not always possible to know which account the user has selected, especially in visual programming. - /// Therefore we are caching the email and server values so that they can be used also when nodes such as "Serialize" are used. - /// If no account info is cached, we use the default account data. - /// - /// Name of the even - /// Additional parameters to pass in to event - /// True if it's an action performed by a logged user - public static void TrackEvent( - Events eventName, - Dictionary customProperties = null, - bool isAction = true - ) - { - string email; - string server; - - if (LastEmail != null && LastServer != null && LastServer != "no-account-server") - { - email = LastEmail; - server = LastServer; - } - else - { - var acc = AccountManager.GetDefaultAccount(); - if (acc == null) - { - var macAddr = NetworkInterface - .GetAllNetworkInterfaces() - .Where(nic => - nic.OperationalStatus == OperationalStatus.Up && nic.NetworkInterfaceType != NetworkInterfaceType.Loopback - ) - .Select(nic => nic.GetPhysicalAddress().ToString()) - .FirstOrDefault(); - - email = macAddr; - server = "no-account-server"; - isAction = false; - } - else - { - email = acc.GetHashedEmail(); - server = acc.GetHashedServer(); - } - } - - TrackEvent(email, server, eventName, customProperties, isAction); - } - - /// - /// Tracks an event from a specified account, anonymizes personal information - /// - /// Account to use, it will be anonymized - /// Name of the event - /// Additional parameters to pass to the event - /// True if it's an action performed by a logged user - public static void TrackEvent( - Account account, - Events eventName, - Dictionary customProperties = null, - bool isAction = true - ) - { - if (account == null) - { - TrackEvent(eventName, customProperties, isAction); - } - else - { - TrackEvent(account.GetHashedEmail(), account.GetHashedServer(), eventName, customProperties, isAction); - } - } - - /// - /// Tracks an event from a specified email and server, anonymizes personal information - /// - /// Email of the user anonymized - /// Server URL anonymized - /// Name of the event - /// Additional parameters to pass to the event - /// True if it's an action performed by a logged user - private static void TrackEvent( - string hashedEmail, - string hashedServer, - Events eventName, - Dictionary customProperties = null, - bool isAction = true - ) - { - LastEmail = hashedEmail; - LastServer = hashedServer; - - if (!IsReleaseMode) - { - //only track in prod - return; - } - - Task.Run(async () => - { - using var activity = SpeckleActivityFactory.Start(); - activity?.SetTag("isAction", isAction); - activity?.SetTag("eventName", eventName.ToString()); - try - { - var executingAssembly = Assembly.GetExecutingAssembly(); - var properties = new Dictionary - { - { "distinct_id", hashedEmail }, - { "server_id", hashedServer }, - { "token", MIXPANEL_TOKEN }, - { "hostApp", Setup.Application }, - { "hostAppVersion", Setup.Version }, - { - "core_version", - FileVersionInfo.GetVersionInfo(executingAssembly.Location).ProductVersion - ?? executingAssembly.GetName().Version.ToString() - }, - { "$os", GetOs() } - }; - - if (isAction) - { - properties.Add("type", "action"); - } - - if (customProperties != null) - { - foreach (KeyValuePair customProp in customProperties) - { - properties[customProp.Key] = customProp.Value; - } - } - - string json = JsonConvert.SerializeObject(new { @event = eventName.ToString(), properties }); - - var query = new StreamContent(new MemoryStream(Encoding.UTF8.GetBytes("data=" + HttpUtility.UrlEncode(json)))); - - using HttpClient client = new(); - client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain")); - query.Headers.ContentType = new MediaTypeHeaderValue("application/json"); - var res = await client.PostAsync(MIXPANEL_SERVER + "/track?ip=1", query).ConfigureAwait(false); - res.EnsureSuccessStatusCode(); - } - catch (Exception ex) when (!ex.IsFatal()) - { - SpeckleLog.Logger.Warning(ex, "Analytics event failed {exceptionMessage}", ex.Message); - } - }); - } - - internal static void AddConnectorToProfile(string hashedEmail, string connector) - { - Task.Run(async () => - { - try - { - var data = new Dictionary - { - { "$token", MIXPANEL_TOKEN }, - { "$distinct_id", hashedEmail }, - { - "$union", - new Dictionary - { - { - "Connectors", - new List { connector } - } - } - } - }; - string json = JsonConvert.SerializeObject(data); - - var query = new StreamContent(new MemoryStream(Encoding.UTF8.GetBytes("data=" + HttpUtility.UrlEncode(json)))); - using HttpClient client = Http.GetHttpProxyClient(); - client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain")); - query.Headers.ContentType = new MediaTypeHeaderValue("application/json"); - var res = await client.PostAsync(MIXPANEL_SERVER + "/engage#profile-union", query).ConfigureAwait(false); - res.EnsureSuccessStatusCode(); - } - catch (Exception ex) when (!ex.IsFatal()) - { - //.ForContext("connector", connector) - SpeckleLog.Logger.Warning(ex, "Failed add connector to profile"); - } - }); - } - - internal static void IdentifyProfile(string hashedEmail, string connector) - { - Task.Run(async () => - { - try - { - var data = new Dictionary - { - { "$token", MIXPANEL_TOKEN }, - { "$distinct_id", hashedEmail }, - { - "$set", - new Dictionary { { "Identified", true } } - } - }; - string json = JsonConvert.SerializeObject(data); - - var query = new StreamContent(new MemoryStream(Encoding.UTF8.GetBytes("data=" + HttpUtility.UrlEncode(json)))); - using HttpClient client = Http.GetHttpProxyClient(); - client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain")); - query.Headers.ContentType = new MediaTypeHeaderValue("application/json"); - var res = await client.PostAsync(MIXPANEL_SERVER + "/engage#profile-set", query).ConfigureAwait(false); - res.EnsureSuccessStatusCode(); - } - catch (Exception ex) when (!ex.IsFatal()) - { - //.ForContext("connector", connector) - SpeckleLog.Logger.Warning(ex, "Failed identify profile"); - } - }); - } - - private static string GetOs() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - return "Windows"; - } - - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - return "Mac OS X"; - } - - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - return "Linux"; - } - - return "Unknown"; - } -} diff --git a/src/Speckle.Sdk/Logging/ISdkCounter.cs b/src/Speckle.Sdk/Logging/ISdkCounter.cs new file mode 100644 index 00000000..5566e2fa --- /dev/null +++ b/src/Speckle.Sdk/Logging/ISdkCounter.cs @@ -0,0 +1,16 @@ +namespace Speckle.Sdk.Logging; + +public interface ISdkCounter + where T : struct +{ + void Add(T value); + void Add(T value, KeyValuePair tag); + void Add(T value, KeyValuePair tag1, KeyValuePair tag2); + void Add( + T value, + KeyValuePair tag1, + KeyValuePair tag2, + KeyValuePair tag3 + ); + void Add(T value, params KeyValuePair[] tag); +} diff --git a/src/Speckle.Sdk/Logging/ISdkMetricsFactory.cs b/src/Speckle.Sdk/Logging/ISdkMetricsFactory.cs new file mode 100644 index 00000000..ecfcb3f1 --- /dev/null +++ b/src/Speckle.Sdk/Logging/ISdkMetricsFactory.cs @@ -0,0 +1,7 @@ +namespace Speckle.Sdk.Logging; + +public interface ISdkMetricsFactory +{ + ISdkCounter CreateCounter(string name, string? unit = default, string? description = default) + where T : struct; +} diff --git a/src/Speckle.Sdk/Logging/ISpeckleActivityExtensions.cs b/src/Speckle.Sdk/Logging/ISpeckleActivityExtensions.cs new file mode 100644 index 00000000..7b5cd862 --- /dev/null +++ b/src/Speckle.Sdk/Logging/ISpeckleActivityExtensions.cs @@ -0,0 +1,12 @@ +namespace Speckle.Sdk.Logging; + +public static class ISpeckleActivityExtensions +{ + public static void SetTags(this ISdkActivity activity, string prefix, IReadOnlyDictionary tags) + { + foreach (var tag in tags) + { + activity.SetTag(tag.Key, $"{prefix}.{tag.Value}"); + } + } +} diff --git a/src/Speckle.Sdk/Logging/NullActivityFactory.cs b/src/Speckle.Sdk/Logging/NullActivityFactory.cs new file mode 100644 index 00000000..580cc5ef --- /dev/null +++ b/src/Speckle.Sdk/Logging/NullActivityFactory.cs @@ -0,0 +1,8 @@ +namespace Speckle.Sdk.Logging; + +public sealed class NullActivityFactory : ISdkActivityFactory +{ + public void Dispose() { } + + public ISdkActivity? Start(string? name = default, string source = "") => null; +} diff --git a/src/Speckle.Sdk/Logging/NullSdkCounter.cs b/src/Speckle.Sdk/Logging/NullSdkCounter.cs new file mode 100644 index 00000000..b1e6a6e0 --- /dev/null +++ b/src/Speckle.Sdk/Logging/NullSdkCounter.cs @@ -0,0 +1,20 @@ +namespace Speckle.Sdk.Logging; + +public sealed class NullSdkCounter : ISdkCounter + where T : struct +{ + public void Add(T value) { } + + public void Add(T value, KeyValuePair tag) { } + + public void Add(T value, KeyValuePair tag1, KeyValuePair tag2) { } + + public void Add( + T value, + KeyValuePair tag1, + KeyValuePair tag2, + KeyValuePair tag3 + ) { } + + public void Add(T value, params KeyValuePair[] tag) { } +} diff --git a/src/Speckle.Sdk/Logging/NullSdkMetricsFactory.cs b/src/Speckle.Sdk/Logging/NullSdkMetricsFactory.cs new file mode 100644 index 00000000..9496d3e6 --- /dev/null +++ b/src/Speckle.Sdk/Logging/NullSdkMetricsFactory.cs @@ -0,0 +1,7 @@ +namespace Speckle.Sdk.Logging; + +public sealed class NullSdkMetricsFactory : ISdkMetricsFactory +{ + public ISdkCounter CreateCounter(string name, string? unit = default, string? description = default) + where T : struct => new NullSdkCounter(); +} diff --git a/src/Speckle.Sdk/Logging/SpecklePathProvider.cs b/src/Speckle.Sdk/Logging/SpecklePathProvider.cs new file mode 100644 index 00000000..e7d72d37 --- /dev/null +++ b/src/Speckle.Sdk/Logging/SpecklePathProvider.cs @@ -0,0 +1,99 @@ +using System.Reflection; + +namespace Speckle.Sdk.Logging; + +/// +/// Helper class dedicated for Speckle specific Path operations. +/// +public static class SpecklePathProvider +{ + private const string APPLICATION_NAME = "Speckle"; + + private const string BLOB_FOLDER_NAME = "Blobs"; + + private const string ACCOUNTS_FOLDER_NAME = "Accounts"; + + private static string UserDataPathEnvVar => "SPECKLE_USERDATA_PATH"; + private static string? Path => Environment.GetEnvironmentVariable(UserDataPathEnvVar); + + /// + /// Get the installation path. + /// + public static string InstallApplicationDataPath => + Assembly.GetExecutingAssembly().Location.Contains("ProgramData") + ? Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + : UserApplicationDataPath(); + + /// + /// Get the folder where the user's Speckle data should be stored. + /// + public static string UserSpeckleFolderPath => EnsureFolderExists(UserApplicationDataPath(), APPLICATION_NAME); + + /// + /// Get the folder where the Speckle accounts data should be stored. + /// + public static string AccountsFolderPath => EnsureFolderExists(UserSpeckleFolderPath, ACCOUNTS_FOLDER_NAME); + + /// + /// Get the platform specific user configuration folder path.
+ /// will be the path e.g.: + /// In cases such as linux servers where the above path is not permissive, we will fall back to + ///
+ /// + /// path usually maps to + ///
    + ///
  • win: %appdata%/
  • + ///
  • MacOS: ~/.config/
  • + ///
  • Linux: ~/.config/
  • + ///
+ ///
+ /// Both and paths are inaccessible + public static string UserApplicationDataPath() + { + // if we have an override, just return that + var pathOverride = Path; + if (pathOverride != null && !string.IsNullOrEmpty(pathOverride)) + { + return pathOverride; + } + + // on desktop linux and macos we use the appdata. + // but we might not have write access to the disk + // so the catch falls back to the user profile + try + { + return Environment.GetFolderPath( + Environment.SpecialFolder.ApplicationData, + // if the folder doesn't exist, we get back an empty string on OSX, + // which in turn, breaks other stuff down the line. + // passing in the Create option ensures that this directory exists, + // which is not a given on all OS-es. + Environment.SpecialFolderOption.Create + ); + } + catch (SystemException ex) when (ex is PlatformNotSupportedException or ArgumentException) + { + //Adding this log just so we confidently know which Exception type to catch here. + // TODO: Must re-add log call when (and if) this get's made as a service + //SpeckleLog.Logger.Warning(ex, "Falling back to user profile path"); + + // on server linux, there might not be a user setup, things can run under root + // in that case, the appdata variable is most probably not set up + // we fall back to the value of the home folder + return Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + } + } + + /// + /// Get the folder where the user's Speckle blobs should be stored. + /// + public static string BlobStoragePath(string? path = null) => + EnsureFolderExists(path ?? UserSpeckleFolderPath, BLOB_FOLDER_NAME); + + private static string EnsureFolderExists(params string[] folderName) + { + var path = System.IO.Path.Combine(folderName); + Directory.CreateDirectory(path); + return path; + } +} diff --git a/src/Speckle.Sdk/Models/ApplicationObject.cs b/src/Speckle.Sdk/Models/ApplicationObject.cs deleted file mode 100644 index e8b4827c..00000000 --- a/src/Speckle.Sdk/Models/ApplicationObject.cs +++ /dev/null @@ -1,138 +0,0 @@ -#nullable disable -using Speckle.Newtonsoft.Json; - -namespace Speckle.Sdk.Models; - -/// -/// A simple wrapper to keep track of the relationship between speckle objects and their host-application siblings in cases where the -/// cannot correspond with the (ie, on receiving operations). -/// -public class ApplicationObject -{ - public enum State - { - Unknown = default, - Created, // Speckle object created on send, or native objects created on receive - Skipped, // Speckle or Application object is not going to be sent or received - Updated, // Application object is replacing an existing object in the application - Failed, // Tried to convert & send or convert & bake but something went wrong - Removed, //Removed object from application - } - - public ApplicationObject(string id, string type) - { - OriginalId = id; - Descriptor = type; - Status = State.Unknown; - } - - /// - /// ID of the object from host application that generated it. - /// - public string applicationId { get; set; } - - /// - /// The container for the object in the native application - /// - public string Container { get; set; } - - /// - /// Indicates if conversion is supported by the converter - /// - public bool Convertible { get; set; } - - /// - /// The fallback values if direct conversion is not available, typically displayValue - /// - [JsonIgnore] - public List Fallback { get; set; } = new(); - - /// - /// The Speckle id (on receive) or native id (on send) - /// - /// - /// Used to retrieve this object in ProgressReport.GetReportObject(), typically to pass between connectors and converters - /// - public string OriginalId { get; set; } - - /// - /// A descriptive string to describe the object. Use the object type as default. - /// - public string Descriptor { get; set; } - - /// - /// The created object ids associated with this object - /// - /// - /// On send, this is currently left empty as generating Speckle ids would be performance expensive - /// - public List CreatedIds { get; set; } = new(); - - /// - /// Conversion status of object - /// - public State Status { get; set; } - - /// - /// Conversion notes or other important information to expose to the user - /// - public List Log { get; set; } = new(); - - /// - /// Converted objects corresponding to this object - /// - /// - /// Used during receive for convenience, corresponds to CreatedIds - /// - [JsonIgnore] - public List Converted { get; set; } = new(); - - public void Update( - string createdId = null, - List createdIds = null, - State? status = null, - string container = null, - List log = null, - string logItem = null, - List converted = null, - object convertedItem = null, - string descriptor = null - ) - { - createdIds?.Where(o => !string.IsNullOrEmpty(o) && !CreatedIds.Contains(o))?.ToList().ForEach(CreatedIds.Add); - - if (createdId != null && !CreatedIds.Contains(createdId)) - { - CreatedIds.Add(createdId); - } - - if (status.HasValue) - { - Status = status.Value; - } - - log?.Where(o => !string.IsNullOrEmpty(o) && !Log.Contains(o))?.ToList().ForEach(Log.Add); - - if (!string.IsNullOrEmpty(logItem) && !Log.Contains(logItem)) - { - Log.Add(logItem); - } - - if (convertedItem != null && !Converted.Contains(convertedItem)) - { - Converted.Add(convertedItem); - } - - converted?.Where(o => o != null && !Converted.Contains(o))?.ToList().ForEach(Converted.Add); - - if (!string.IsNullOrEmpty(container)) - { - Container = container; - } - - if (!string.IsNullOrEmpty(descriptor)) - { - Descriptor = descriptor; - } - } -} diff --git a/src/Speckle.Sdk/Models/Attributes.cs b/src/Speckle.Sdk/Models/Attributes.cs index a8720216..808c24b8 100644 --- a/src/Speckle.Sdk/Models/Attributes.cs +++ b/src/Speckle.Sdk/Models/Attributes.cs @@ -6,7 +6,7 @@ namespace Speckle.Sdk.Models; /// Only applies to properties of types derived from the Base class. /// [AttributeUsage(AttributeTargets.Property)] -public sealed class DetachProperty : Attribute +public sealed class DetachPropertyAttribute : Attribute { /// /// Flags an object's property as being detachable. @@ -14,7 +14,7 @@ public sealed class DetachProperty : Attribute /// Only applies to properties of types derived from the Base class. /// /// Whether to detach the property or not. - public DetachProperty(bool detachable = true) + public DetachPropertyAttribute(bool detachable = true) { Detachable = detachable; } @@ -23,14 +23,14 @@ public sealed class DetachProperty : Attribute } /// -/// Flags a list or array as splittable into chunks during serialisation. These chunks will be recomposed on deserialisation into the original list. Note: this attribute should be used in conjunction with . +/// Flags a list or array as splittable into chunks during serialisation. These chunks will be recomposed on deserialisation into the original list. Note: this attribute should be used in conjunction with . /// Use this attribute on properties that can become very long and are not worth detaching into individual elements. /// Objects per chunk: for simple types, like numbers, use a high value (>10000); for other objects, use a more conservative number depending on their serialised size. /// [AttributeUsage(AttributeTargets.Property)] -public sealed class Chunkable : Attribute +public sealed class ChunkableAttribute : Attribute { - public Chunkable(int maxObjCountPerChunk = 1000) + public ChunkableAttribute(int maxObjCountPerChunk = 1000) { MaxObjCountPerChunk = maxObjCountPerChunk; } diff --git a/src/Speckle.Sdk/Models/Base.cs b/src/Speckle.Sdk/Models/Base.cs index 58b6b5a8..45b8efed 100644 --- a/src/Speckle.Sdk/Models/Base.cs +++ b/src/Speckle.Sdk/Models/Base.cs @@ -1,13 +1,11 @@ -#nullable disable using System.Collections; using System.Diagnostics.CodeAnalysis; using System.Reflection; -using System.Text.RegularExpressions; using Speckle.Newtonsoft.Json; using Speckle.Newtonsoft.Json.Linq; +using Speckle.Sdk.Common; using Speckle.Sdk.Helpers; using Speckle.Sdk.Host; -using Speckle.Sdk.Logging; using Speckle.Sdk.Serialisation; using Speckle.Sdk.Transports; @@ -25,17 +23,13 @@ namespace Speckle.Sdk.Models; [SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Serialized property names are camelCase by design")] public class Base : DynamicBase, ISpeckleObject { - private static readonly Regex s_chunkSyntax = Constants.ChunkPropertyNameRegex; - - private string _type; + private string? _type; /// - /// A speckle object's id is an unique hash based on its properties. NOTE: this field will be null unless the object was deserialised from a source. Use the function to get it. + /// A speckle object's id is an unique hash based on its properties. NOTE: this field will be null unless the object was deserialised from a source. Use the function to get it. /// [SchemaIgnore] - public virtual string id { get; set; } - -#nullable enable //Starting nullability syntax here so that `id` null oblivious, + public virtual string? id { get; set; } /// /// Secondary, ideally host application driven, object identifier. @@ -72,8 +66,9 @@ public class Base : DynamicBase, ISpeckleObject /// the resulting id (hash) public string GetId(bool decompose = false) { + //TODO remove me var transports = decompose ? [new MemoryTransport()] : Array.Empty(); - var serializer = new BaseObjectSerializerV2(transports); + var serializer = new SpeckleObjectSerializer(transports); string obj = serializer.Serialize(this); return JObject.Parse(obj).GetValue(nameof(id))?.ToString() ?? string.Empty; @@ -91,13 +86,11 @@ public class Base : DynamicBase, ISpeckleObject private static long CountDescendants(Base @base, ISet parsed) { - if (parsed.Contains(@base.GetHashCode())) + if (!parsed.Add(@base.GetHashCode())) { return 0; } - parsed.Add(@base.GetHashCode()); - long count = 0; var typedProps = @base.GetInstanceMembers(); foreach (var prop in typedProps.Where(p => p.CanRead)) @@ -109,13 +102,13 @@ public class Base : DynamicBase, ISpeckleObject continue; } - var detachAttribute = prop.GetCustomAttribute(true); + var detachAttribute = prop.GetCustomAttribute(true); - object value = prop.GetValue(@base); + object? value = prop.GetValue(@base); if (detachAttribute is { Detachable: true }) { - var chunkAttribute = prop.GetCustomAttribute(true); + var chunkAttribute = prop.GetCustomAttribute(true); if (chunkAttribute == null) { count += HandleObjectCount(value, parsed); @@ -131,20 +124,17 @@ public class Base : DynamicBase, ISpeckleObject } } - var dynamicProps = @base.GetDynamicPropertyKeys(); + var dynamicProps = @base.DynamicPropertyKeys; foreach (var propName in dynamicProps) { - if (!propName.StartsWith("@")) + if (!PropNameValidator.IsDetached(propName)) { continue; } // Simplfied dynamic prop chunking handling - if (s_chunkSyntax.IsMatch(propName)) + if (PropNameValidator.IsChunkable(propName, out int chunkSize)) { - var match = s_chunkSyntax.Match(propName); - _ = int.TryParse(match.Groups[match.Groups.Count - 1].Value, out int chunkSize); - if (chunkSize != -1 && @base[propName] is IList asList) { count += asList.Count / chunkSize; @@ -216,7 +206,7 @@ public class Base : DynamicBase, ISpeckleObject public Base ShallowCopy() { Type type = GetType(); - Base myDuplicate = (Base)Activator.CreateInstance(type); + Base myDuplicate = (Base)Activator.CreateInstance(type).NotNull(); myDuplicate.id = id; myDuplicate.applicationId = applicationId; @@ -231,24 +221,7 @@ public class Base : DynamicBase, ISpeckleObject { continue; } - - try - { - myDuplicate[kvp.Key] = kvp.Value; - } - catch (Exception ex) when (!ex.IsFatal()) - { - // avoids any last ditch unsettable or strange props. - SpeckleLog.Logger.Warning( - "Shallow copy of {type} failed to copy {propertyName} of type {propertyType} with value {valueType} - CanWrite / CanRead", - type, - kvp.Key, - propertyInfo?.PropertyType, - kvp.Value?.GetType(), - propertyInfo?.CanWrite, - propertyInfo?.CanRead - ); - } + myDuplicate[kvp.Key] = kvp.Value; } return myDuplicate; diff --git a/src/Speckle.Sdk/Models/Blob.cs b/src/Speckle.Sdk/Models/Blob.cs index b9626e63..540d1ca0 100644 --- a/src/Speckle.Sdk/Models/Blob.cs +++ b/src/Speckle.Sdk/Models/Blob.cs @@ -1,6 +1,4 @@ -#nullable disable -using System.Diagnostics.CodeAnalysis; -using System.Runtime.Serialization; +using System.Runtime.Serialization; using Speckle.Newtonsoft.Json; namespace Speckle.Sdk.Models; @@ -39,13 +37,13 @@ public class Blob : Base /// /// For blobs, the id is the same as the file hash. Please note, when deserialising, the id will be set from the original hash generated on sending. /// - public override string id + public override string? id { get => GetFileHash(); set => base.id = value; } - public string GetFileHash() + public string? GetFileHash() { if ((_isHashExpired || _hash == null) && filePath != null) { @@ -64,6 +62,7 @@ public class Blob : Base public string GetLocalDestinationPath(string blobStorageFolder) { var fileName = Path.GetFileName(filePath); - return Path.Combine(blobStorageFolder, $"{id.Substring(0, 10)}-{fileName}"); + var x = id ?? throw new ArgumentException("id is empty"); + return Path.Combine(blobStorageFolder, $"{x[..10]}-{fileName}"); } } diff --git a/src/Speckle.Sdk/Models/CommitObjectBuilder.cs b/src/Speckle.Sdk/Models/CommitObjectBuilder.cs deleted file mode 100644 index df6f64af..00000000 --- a/src/Speckle.Sdk/Models/CommitObjectBuilder.cs +++ /dev/null @@ -1,170 +0,0 @@ -using System.Collections; -using Speckle.Sdk.Logging; -using Speckle.Sdk.Models.Collections; -using Speckle.Sdk.Models.Extensions; - -namespace Speckle.Sdk.Models; - -/// -/// Abstract Builder class for a root commit object. -/// -/// The native object data type needed as input for building -/// -/// It is designed to be inherited by a host app specific implementation, -/// to give connectors flexibility in constructing their objects. -/// Inheritors should also create some function to add -/// -public abstract class CommitObjectBuilder -{ - /// Special appId symbol for the root object - protected const string ROOT = "__Root"; - private const string ELEMENTS = nameof(Collection.elements); - - /// app id -> base - protected IDictionary Converted { get; } - - /// Base -> NestingInstructions ordered by priority - private readonly Dictionary> _nestingInstructions = new(); - - protected CommitObjectBuilder() - { - Converted = new Dictionary(); - } - - /// - /// Given the parameters, builds connector specific - /// to be applied when is called. - /// - /// - /// - public abstract void IncludeObject(Base conversionResult, TNativeObjectData nativeElement); - - /// - /// Iterates through the converted objects applying - /// - /// - /// Can be overriden to adjust exactly which objects get automatically applied, - /// or to inject additional items into the dict that should not be automatically applied. - /// - /// - public virtual void BuildCommitObject(Base rootCommitObject) - { - ApplyRelationships(Converted.Values, rootCommitObject); - } - - protected void SetRelationship(Base conversionResult, NestingInstructions nestingInstructions) - { - SetRelationship(conversionResult, new List { nestingInstructions }); - } - - /// - /// Sets information on how a given object should be nested in the commit tree. - /// encodes the order in which we should try and nest the given - /// when is called - /// - /// The object to be nested - /// Information about how the object ideally should be nested, in order of priority - protected void SetRelationship(Base conversionResult, IList nestingInstructionsList) - { - string? appId = conversionResult.applicationId; - if (appId != null) - { - Converted[appId] = conversionResult; - } - _nestingInstructions[conversionResult] = nestingInstructionsList; - } - - /// - /// For each object in - /// - /// - /// - /// - protected void ApplyRelationships(IEnumerable toAdd, Base rootCommitObject) - { - foreach (Base c in toAdd) - { - try - { - ApplyRelationship(c, rootCommitObject); - } - catch (Exception ex) when (!ex.IsFatal()) - { - // This should never happen, we should be ensuring that at least one of the parents is valid. - SpeckleLog.Logger.Fatal(ex, "Failed to add object {speckleType} to commit object", c?.GetType()); - } - } - } - - /// - /// Will attempt to find and nest the object - /// under the first valid parent according to the dictionary. - /// - /// - /// A parent is considered valid if - /// 1. Is non null - /// 2. Is in the dictionary - /// 3. Has (or can dynamically accept) a typed property with the propName specified by the item - /// 4. Said can accept the object's type - /// - /// - /// - /// Thrown when no valid parent was found for given - protected void ApplyRelationship(Base current, Base rootCommitObject) - { - var instructions = _nestingInstructions[current]; - foreach (var instruction in instructions) - { - if (instruction.ParentApplicationId is null) - { - continue; - } - - Base? parent; - if (instruction.ParentApplicationId == ROOT) - { - parent = rootCommitObject; - } - else - { - Converted.TryGetValue(instruction.ParentApplicationId, out parent); - } - - if (parent is null) - { - continue; - } - - try - { - instruction.Nest(parent, current); - return; - } - catch (Exception ex) when (!ex.IsFatal()) - { - // A parent was found, but it was invalid (Likely because of a type mismatch on a `elements` property) - SpeckleLog.Logger.Warning(ex, "Failed to add object {speckleType} to a converted parent", current.GetType()); - } - } - - throw new InvalidOperationException( - $"Could not find a valid parent for object of type {current.GetType()}. Checked {instructions.Count} potential parent, and non were converted!" - ); - } - - protected static void NestUnderElementsProperty(Base parent, Base child) - { - NestUnderProperty(parent, child, ELEMENTS); - } - - protected static void NestUnderProperty(Base parent, Base child, string property) - { - if (parent.GetDetachedProp(property) is not IList elements) - { - elements = new List(); - parent.SetDetachedProp(property, elements); - } - - elements.Add(child); - } -} diff --git a/src/Speckle.Sdk/Models/DeprecatedSpeckleTypeAttribute.cs b/src/Speckle.Sdk/Models/DeprecatedSpeckleTypeAttribute.cs index 46087c3f..cbdf97a3 100644 --- a/src/Speckle.Sdk/Models/DeprecatedSpeckleTypeAttribute.cs +++ b/src/Speckle.Sdk/Models/DeprecatedSpeckleTypeAttribute.cs @@ -1,8 +1,7 @@ -#nullable disable namespace Speckle.Sdk.Models; [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] -public class DeprecatedSpeckleTypeAttribute(string speckleTypeName) : Attribute +public sealed class DeprecatedSpeckleTypeAttribute(string speckleTypeName) : Attribute { - public string Name => speckleTypeName; + public string SpeckleTypeName => speckleTypeName; } diff --git a/src/Speckle.Sdk/Models/DynamicBase.cs b/src/Speckle.Sdk/Models/DynamicBase.cs index c069aa9b..483bda4a 100644 --- a/src/Speckle.Sdk/Models/DynamicBase.cs +++ b/src/Speckle.Sdk/Models/DynamicBase.cs @@ -1,7 +1,8 @@ using System.Dynamic; using System.Reflection; +using Speckle.Newtonsoft.Json; +using Speckle.Sdk.Common; using Speckle.Sdk.Host; -using Speckle.Sdk.Logging; namespace Speckle.Sdk.Models; @@ -72,6 +73,7 @@ public class DynamicBase : DynamicObject, IDynamicMetaObjectProvider _properties[key] = value; return; } + try { prop.SetValue(this, value); @@ -112,7 +114,7 @@ public class DynamicBase : DynamicObject, IDynamicMetaObjectProvider return valid; } - private static readonly HashSet s_disallowedPropNameChars = new() { '.', '/' }; + private static readonly char[] s_disallowedPropNameChars = { '.', '/' }; public static string RemoveDisallowedPropNameChars(string name) { @@ -124,37 +126,45 @@ public class DynamicBase : DynamicObject, IDynamicMetaObjectProvider return name; } - public bool IsPropNameValid(string name, out string reason) + //apparently used a lot so optimize the check + public unsafe bool IsPropNameValid(string name, out string reason) { - if (string.IsNullOrEmpty(name) || name == "@") + if (string.IsNullOrEmpty(name) || name.Equals("@", StringComparison.Ordinal)) { reason = "Found empty prop name"; return false; } - if (name.StartsWith("@@")) + if (name.StartsWith("@@", StringComparison.Ordinal)) { reason = "Only one leading '@' char is allowed. This signals the property value should be detached."; return false; } - foreach (char c in name) + int len = name.Length; + fixed (char* ptr = name) { - if (s_disallowedPropNameChars.Contains(c)) + for (int i = 0; i < len; i++) { - reason = $"Prop with name '{name}' contains invalid characters. The following characters are not allowed: ./"; - return false; + for (int j = 0; j < s_disallowedPropNameChars.Length; j++) + { + if (s_disallowedPropNameChars[j] == ptr[i]) + { + reason = + $"Prop with name '{name}' contains invalid characters. The following characters are not allowed: ./"; + return false; + } + } } + // talk to ptr[0] etc; DO NOT go outside of ptr[0] <---> ptr[len-1] } - - reason = ""; + reason = string.Empty; return true; } /// /// Gets all of the property names on this class, dynamic or not. /// - [Obsolete("Use `GetMembers(DynamicBaseMemberType.All).Keys` instead")] public override IEnumerable GetDynamicMemberNames() { var pinfos = TypeLoader.GetBaseProperties(GetType()); @@ -223,7 +233,7 @@ public class DynamicBase : DynamicObject, IDynamicMetaObjectProvider .GetBaseProperties(GetType()) .Where(x => { - var hasIgnored = x.IsDefined(typeof(SchemaIgnore), true); + var hasIgnored = x.IsDefined(typeof(SchemaIgnoreAttribute), true); var hasObsolete = x.IsDefined(typeof(ObsoleteAttribute), true); // If obsolete is false and prop has obsolete attr @@ -251,16 +261,8 @@ public class DynamicBase : DynamicObject, IDynamicMetaObjectProvider .ToList() .ForEach(e => { - var attr = e.GetCustomAttribute(); - try - { - dic[attr.Name] = e.Invoke(this, null); - } - catch (Exception ex) when (!ex.IsFatal()) - { - SpeckleLog.Logger.Warning(ex, "Failed to get computed member: {name}", attr.Name); - dic[attr.Name] = null; - } + var attr = e.GetCustomAttribute().NotNull(); + dic[attr.Name] = e.Invoke(this, null); }); } @@ -271,10 +273,8 @@ public class DynamicBase : DynamicObject, IDynamicMetaObjectProvider /// Gets the dynamically added property names only. /// /// - public IReadOnlyCollection GetDynamicPropertyKeys() - { - return _properties.Keys; - } + [JsonIgnore] + public IReadOnlyCollection DynamicPropertyKeys => _properties.Keys; } /// diff --git a/src/Speckle.Sdk/Models/DynamicBaseMemberType.cs b/src/Speckle.Sdk/Models/DynamicBaseMemberType.cs index ba9d60b2..3403bf2a 100644 --- a/src/Speckle.Sdk/Models/DynamicBaseMemberType.cs +++ b/src/Speckle.Sdk/Models/DynamicBaseMemberType.cs @@ -24,7 +24,7 @@ public enum DynamicBaseMemberType Obsolete = 4, /// - /// The typed members flagged with attribute. + /// The typed members flagged with attribute. /// SchemaIgnored = 8, @@ -34,12 +34,12 @@ public enum DynamicBaseMemberType SchemaComputed = 16, /// - /// All the typed members, including ones with or attributes. + /// All the typed members, including ones with or attributes. /// InstanceAll = Instance + Obsolete + SchemaIgnored, /// - /// All the members, including dynamic and instance members flagged with or attributes + /// All the members, including dynamic and instance members flagged with or attributes /// - All = InstanceAll + Dynamic + All = InstanceAll + Dynamic, } diff --git a/src/Speckle.Sdk/Models/Extensions/BaseExtensions.cs b/src/Speckle.Sdk/Models/Extensions/BaseExtensions.cs index fd2ac7c6..009c67a8 100644 --- a/src/Speckle.Sdk/Models/Extensions/BaseExtensions.cs +++ b/src/Speckle.Sdk/Models/Extensions/BaseExtensions.cs @@ -1,4 +1,6 @@ using System.Collections; +using System.Diagnostics.Contracts; +using Speckle.Sdk.Common; using Speckle.Sdk.Models.Collections; namespace Speckle.Sdk.Models.Extensions; @@ -30,7 +32,7 @@ public static class BaseExtensions root, b => { - if (!cache.Add(b.id)) + if (!cache.Add(b.id.NotNull())) { return true; } @@ -41,7 +43,7 @@ public static class BaseExtensions foreach (var b in traversal) { - if (!cache.Contains(b.id)) + if (!cache.Contains(b.id.NotNull())) { yield return b; } @@ -200,7 +202,7 @@ public static class BaseExtensions T b => new List { b }, IReadOnlyList list => list, IEnumerable enumerable => enumerable.OfType().ToList(), - _ => null + _ => null, }; } @@ -288,4 +290,13 @@ public static class BaseExtensions } } } + + /// + /// totalChildrenCount was a property in v2 on all Base objects, + /// it has since been removed, so is a reliable albeit hacky way to tell if a was from v2 + /// + /// + /// true if the is likley to have come from a v2 data source + [Pure] + public static bool SmellsLikeV2Data(this Base speckleObject) => speckleObject["totalChildrenCount"] is not null; } diff --git a/src/Speckle.Sdk/Models/Extras.cs b/src/Speckle.Sdk/Models/Extras.cs index 6d5844ba..4ba31292 100644 --- a/src/Speckle.Sdk/Models/Extras.cs +++ b/src/Speckle.Sdk/Models/Extras.cs @@ -1,5 +1,3 @@ -#nullable disable - namespace Speckle.Sdk.Models; /// @@ -9,15 +7,14 @@ namespace Speckle.Sdk.Models; [SpeckleType("Speckle.Core.Models.DataChunk")] public sealed class DataChunk : Base { - public List data { get; set; } = new(); + public required List data { get; init; } } -[SpeckleType("Speckle.Core.Models.ObjectReference")] +[DeprecatedSpeckleType("Speckle.Core.Models.ObjectReference")] +[SpeckleType("reference")] public sealed class ObjectReference : Base { - public new string speckle_type = "reference"; + public required string referencedId { get; init; } - public string referencedId { get; set; } - - public Dictionary closure { get; set; } + public Dictionary? closure { get; set; } } diff --git a/src/Speckle.Sdk/Models/GraphTraversal/GraphTraversal.cs b/src/Speckle.Sdk/Models/GraphTraversal/GraphTraversal.cs index 16962b31..a9fbd637 100644 --- a/src/Speckle.Sdk/Models/GraphTraversal/GraphTraversal.cs +++ b/src/Speckle.Sdk/Models/GraphTraversal/GraphTraversal.cs @@ -13,6 +13,44 @@ public class GraphTraversal : GraphTraversal { return new TraversalContext(current, propName, parent); } + + /// + /// Traverses supported Collections yielding objects. + /// Does not traverse , only (potentially nested) collections. + /// + /// The value to traverse + public static IEnumerable TraverseMember(object? value) + { + //TODO we should benchmark this, as yield returning like this could be suboptimal + switch (value) + { + case Base o: + yield return o; + break; + case IList list: + { + foreach (object? obj in list) + { + foreach (Base o in TraverseMember(obj)) + { + yield return o; + } + } + break; + } + case IDictionary dictionary: + { + foreach (object? obj in dictionary.Values) + { + foreach (Base o in TraverseMember(obj)) + { + yield return o; + } + } + break; + } + } + } } public abstract class GraphTraversal @@ -92,44 +130,6 @@ public abstract class GraphTraversal protected abstract T NewContext(Base current, string? propName, T? parent); - /// - /// Traverses supported Collections yielding objects. - /// Does not traverse , only (potentially nested) collections. - /// - /// The value to traverse - public static IEnumerable TraverseMember(object? value) - { - //TODO we should benchmark this, as yield returning like this could be suboptimal - switch (value) - { - case Base o: - yield return o; - break; - case IList list: - { - foreach (object? obj in list) - { - foreach (Base o in TraverseMember(obj)) - { - yield return o; - } - } - break; - } - case IDictionary dictionary: - { - foreach (object? obj in dictionary.Values) - { - foreach (Base o in TraverseMember(obj)) - { - yield return o; - } - } - break; - } - } - } - private ITraversalRule GetActiveRuleOrDefault(Base o) { return GetActiveRule(o) ?? DefaultRule.Instance; diff --git a/src/Speckle.Sdk/Models/GraphTraversal/RuleBuilder.cs b/src/Speckle.Sdk/Models/GraphTraversal/RuleBuilder.cs index e5f175b5..435e35b5 100644 --- a/src/Speckle.Sdk/Models/GraphTraversal/RuleBuilder.cs +++ b/src/Speckle.Sdk/Models/GraphTraversal/RuleBuilder.cs @@ -20,7 +20,7 @@ public sealed class TraversalRule : ITraversalBuilderReturn, ITraversalBuilderTr public ITraversalBuilderReturn ContinueTraversing(SelectMembers membersToTraverse) { - this._membersToTraverse = membersToTraverse; + _membersToTraverse = membersToTraverse; return this; } diff --git a/src/Speckle.Sdk/Models/HashUtility.cs b/src/Speckle.Sdk/Models/HashUtility.cs index a129aa3b..b4f5497d 100644 --- a/src/Speckle.Sdk/Models/HashUtility.cs +++ b/src/Speckle.Sdk/Models/HashUtility.cs @@ -8,7 +8,7 @@ public static class HashUtility public enum HashingFunctions { SHA256, - MD5 + MD5, } public const int HASH_LENGTH = 32; diff --git a/src/Speckle.Sdk/Models/ISpeckleObject.cs b/src/Speckle.Sdk/Models/ISpeckleObject.cs index 5516c367..55a7e74d 100644 --- a/src/Speckle.Sdk/Models/ISpeckleObject.cs +++ b/src/Speckle.Sdk/Models/ISpeckleObject.cs @@ -2,10 +2,7 @@ public interface ISpeckleObject { -#nullable disable - public string id { get; } - -#nullable enable //Starting nullability syntax here so that `id` null oblivious, + public string? id { get; } public string? applicationId { get; } diff --git a/src/Speckle.Sdk/Models/Instances/InstanceDefinitionProxy.cs b/src/Speckle.Sdk/Models/Instances/InstanceDefinitionProxy.cs index 616c99e9..1ff2e1e4 100644 --- a/src/Speckle.Sdk/Models/Instances/InstanceDefinitionProxy.cs +++ b/src/Speckle.Sdk/Models/Instances/InstanceDefinitionProxy.cs @@ -11,12 +11,12 @@ public class InstanceDefinitionProxy : Base, IInstanceComponent, IProxyCollectio /// /// 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 required List objects { get; set; } // source app application ids for the objects - public int maxDepth { get; set; } + public required int maxDepth { get; set; } /// /// Name of the instance definition proxy collection which is unique for rhino, autocad and sketchup /// - public string name { get; set; } + public required string name { get; set; } } diff --git a/src/Speckle.Sdk/Models/Instances/InstanceProxy.cs b/src/Speckle.Sdk/Models/Instances/InstanceProxy.cs index e45628b4..0b08430f 100644 --- a/src/Speckle.Sdk/Models/Instances/InstanceProxy.cs +++ b/src/Speckle.Sdk/Models/Instances/InstanceProxy.cs @@ -1,5 +1,4 @@ using Speckle.DoubleNumerics; -using Speckle.Sdk.Common; namespace Speckle.Sdk.Models.Instances; @@ -12,17 +11,17 @@ 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; } + public required string definitionId { get; set; } /// /// The transform of the instance reference. /// - public Matrix4x4 transform { get; set; } + public required Matrix4x4 transform { get; set; } /// /// The units of the host application file. /// - public string units { get; set; } = Units.Meters; + public required string units { get; set; } - public int maxDepth { get; set; } + public required int maxDepth { get; set; } } diff --git a/src/Speckle.Sdk/Models/InvalidPropNameException.cs b/src/Speckle.Sdk/Models/InvalidPropNameException.cs index 486dbab0..f814c2a6 100644 --- a/src/Speckle.Sdk/Models/InvalidPropNameException.cs +++ b/src/Speckle.Sdk/Models/InvalidPropNameException.cs @@ -1,5 +1,3 @@ -using Speckle.Sdk.Logging; - namespace Speckle.Sdk.Models; public class InvalidPropNameException : SpeckleException diff --git a/src/Speckle.Sdk/Models/Proxies/ColorProxy.cs b/src/Speckle.Sdk/Models/Proxies/ColorProxy.cs index d6b691b1..6f302a07 100644 --- a/src/Speckle.Sdk/Models/Proxies/ColorProxy.cs +++ b/src/Speckle.Sdk/Models/Proxies/ColorProxy.cs @@ -6,24 +6,15 @@ namespace Speckle.Sdk.Models.Proxies; [SpeckleType("Speckle.Core.Models.Proxies.ColorProxy")] public class ColorProxy : Base, IProxyCollection { - public ColorProxy() { } - - public ColorProxy(int color, string applicationId, string? name) - { - value = color; - this.applicationId = applicationId; - this.name = name; - } - - public List objects { get; set; } + public required List objects { get; set; } /// /// The argb int of the color /// - public int value { get; set; } + public required int value { get; set; } /// /// The name, if any, of the color /// - public string? name { get; set; } + public required string? name { get; set; } } diff --git a/src/Speckle.Sdk/Models/Proxies/GroupProxy.cs b/src/Speckle.Sdk/Models/Proxies/GroupProxy.cs index 939e58e1..4b97843d 100644 --- a/src/Speckle.Sdk/Models/Proxies/GroupProxy.cs +++ b/src/Speckle.Sdk/Models/Proxies/GroupProxy.cs @@ -8,10 +8,10 @@ namespace Speckle.Sdk.Models.Proxies; [DeprecatedSpeckleType("Speckle.Core.Models.Instances.GroupProxy")] public class GroupProxy : Base, IProxyCollection { - public List objects { get; set; } + public required List objects { get; set; } /// /// Name of the group proxy collection which is unique for rhino, autocad and sketchup /// - public string name { get; set; } + public required string name { get; set; } } diff --git a/src/Speckle.Sdk/Models/SpeckleTypeAttribute.cs b/src/Speckle.Sdk/Models/SpeckleTypeAttribute.cs index d9439471..af5c14a2 100644 --- a/src/Speckle.Sdk/Models/SpeckleTypeAttribute.cs +++ b/src/Speckle.Sdk/Models/SpeckleTypeAttribute.cs @@ -1,8 +1,7 @@ -#nullable disable namespace Speckle.Sdk.Models; [AttributeUsage(AttributeTargets.Class, Inherited = false)] -public class SpeckleTypeAttribute(string speckleTypeName) : Attribute +public sealed class SpeckleTypeAttribute(string speckleTypeName) : Attribute { - public string Name => speckleTypeName; + public string SpeckleTypeName => speckleTypeName; } diff --git a/src/Speckle.Sdk/SQLite/CacheDbCommandPool.cs b/src/Speckle.Sdk/SQLite/CacheDbCommandPool.cs new file mode 100644 index 00000000..cd8e3613 --- /dev/null +++ b/src/Speckle.Sdk/SQLite/CacheDbCommandPool.cs @@ -0,0 +1,110 @@ +using System.Collections.Concurrent; +using Microsoft.Data.Sqlite; + +namespace Speckle.Sdk.SQLite; + +//inspired by https://github.com/neosmart/SqliteCache/blob/master/SqliteCache/DbCommandPool.cs +public sealed class CacheDbCommandPool : IDisposable +{ + private readonly ConcurrentBag[] _commands = new ConcurrentBag[CacheDbCommands.Count]; + private readonly ConcurrentBag _connections = new(); + private readonly string _connectionString; + + public CacheDbCommandPool(string connectionString, int concurrency) + { + _connectionString = connectionString; + for (int i = 0; i < _commands.Length; ++i) + { + _commands[i] = new ConcurrentBag(); + } + for (int i = 0; i < concurrency; ++i) + { + var connection = new SqliteConnection(_connectionString); + connection.Open(); + _connections.Add(connection); + } + } + + public void Use(Action handler) => + Use(conn => + { + handler(conn); + return true; + }); + + public void Use(CacheOperation type, Action handler) => + Use( + type, + cmd => + { + handler(cmd); + return true; + } + ); + + private T Use(Func handler) + { + if (!_connections.TryTake(out var db)) + { + db = new SqliteConnection(_connectionString); + db.Open(); + } + + try + { + return handler(db); + } + catch (SqliteException se) + { + throw SqLiteJsonCacheException.Create(se); + } + finally + { + _connections.Add(db); + } + } + + public T Use(CacheOperation type, Func handler) => + Use(conn => + { + var pool = _commands[(int)type]; + if (!pool.TryTake(out var command)) + { +#pragma warning disable CA2100 + command = new SqliteCommand(CacheDbCommands.Commands[(int)type], conn); +#pragma warning restore CA2100 + } + + try + { + command.Connection = conn; + return handler(command); + } + catch (SqliteException se) + { + throw SqLiteJsonCacheException.Create(se); + } + finally + { + command.Connection = null; + command.Parameters.Clear(); + pool.Add(command); + } + }); + + public void Dispose() + { + foreach (var pool in _commands) + { + while (pool.TryTake(out var cmd)) + { + cmd.Dispose(); + } + } + while (_connections.TryTake(out var conn)) + { + conn.Close(); + conn.Dispose(); + } + } +} diff --git a/src/Speckle.Sdk/SQLite/CacheDbCommands.cs b/src/Speckle.Sdk/SQLite/CacheDbCommands.cs new file mode 100644 index 00000000..33a28771 --- /dev/null +++ b/src/Speckle.Sdk/SQLite/CacheDbCommands.cs @@ -0,0 +1,35 @@ +namespace Speckle.Sdk.SQLite; + +public enum CacheOperation +{ + InsertOrIgnore, + InsertOrReplace, + Has, + Get, + Delete, + GetAll, + BulkInsertOrIgnore, +} + +public static class CacheDbCommands +{ + public static readonly string[] Commands; + public static readonly int Count = Enum.GetValues(typeof(CacheOperation)).Length; + +#pragma warning disable CA1810 + static CacheDbCommands() +#pragma warning restore CA1810 + { + Commands = new string[Count]; + + Commands[(int)CacheOperation.InsertOrIgnore] = + "INSERT OR IGNORE INTO objects(hash, content) VALUES(@hash, @content)"; + Commands[(int)CacheOperation.InsertOrReplace] = "REPLACE INTO objects(hash, content) VALUES(@hash, @content)"; + Commands[(int)CacheOperation.Has] = "SELECT 1 FROM objects WHERE hash = @hash LIMIT 1"; + Commands[(int)CacheOperation.Get] = "SELECT content FROM objects WHERE hash = @hash LIMIT 1"; + Commands[(int)CacheOperation.Delete] = "DELETE FROM objects WHERE hash = @hash"; + Commands[(int)CacheOperation.GetAll] = "SELECT hash, content FROM objects"; + + Commands[(int)CacheOperation.BulkInsertOrIgnore] = "INSERT OR IGNORE INTO objects (hash, content) VALUES "; + } +} diff --git a/src/Speckle.Sdk/SQLite/SQLiteJsonCacheManager.cs b/src/Speckle.Sdk/SQLite/SQLiteJsonCacheManager.cs new file mode 100644 index 00000000..91620a20 --- /dev/null +++ b/src/Speckle.Sdk/SQLite/SQLiteJsonCacheManager.cs @@ -0,0 +1,182 @@ +using System.Text; +using Microsoft.Data.Sqlite; +using Speckle.InterfaceGenerator; +using Speckle.Sdk.Dependencies; + +namespace Speckle.Sdk.SQLite; + +public partial interface ISqLiteJsonCacheManager : IDisposable; + +[GenerateAutoInterface] +public sealed class SqLiteJsonCacheManager : ISqLiteJsonCacheManager +{ + private readonly CacheDbCommandPool _pool; + + public SqLiteJsonCacheManager(string path, int concurrency) + { + //disable pooling as we pool ourselves + var builder = new SqliteConnectionStringBuilder { Pooling = false, DataSource = path }; + _pool = new CacheDbCommandPool(builder.ToString(), concurrency); + Initialize(); + } + + [AutoInterfaceIgnore] + public void Dispose() => _pool.Dispose(); + + private void Initialize() + { + // NOTE: used for creating partioned object tables. + //string[] HexChars = new string[] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" }; + //var cart = new List(); + //foreach (var str in HexChars) + // foreach (var str2 in HexChars) + // cart.Add(str + str2); + + _pool.Use(c => + { + const string COMMAND_TEXT = + @" + CREATE TABLE IF NOT EXISTS objects( + hash TEXT PRIMARY KEY, + content TEXT + ) WITHOUT ROWID; + "; + using (var command = new SqliteCommand(COMMAND_TEXT, c)) + { + command.ExecuteNonQuery(); + } + + // Insert Optimisations + + //Note / Hack: This setting has the potential to corrupt the db. + //cmd = new SqliteCommand("PRAGMA synchronous=OFF;", Connection); + //cmd.ExecuteNonQuery(); + + using (SqliteCommand cmd1 = new("PRAGMA count_changes=OFF;", c)) + { + cmd1.ExecuteNonQuery(); + } + + using (SqliteCommand cmd2 = new("PRAGMA temp_store=MEMORY;", c)) + { + cmd2.ExecuteNonQuery(); + } + + using (SqliteCommand cmd3 = new("PRAGMA mmap_size = 30000000000;", c)) + { + cmd3.ExecuteNonQuery(); + } + + using (SqliteCommand cmd4 = new("PRAGMA page_size = 32768;", c)) + { + cmd4.ExecuteNonQuery(); + } + + using (SqliteCommand cmd0 = new("PRAGMA journal_mode='wal';", c)) + { + cmd0.ExecuteNonQuery(); + } + }); + } + + public IReadOnlyCollection<(string Id, string Json)> GetAllObjects() => + _pool.Use( + CacheOperation.GetAll, + command => + { + var list = new HashSet<(string, string)>(); + using var reader = command.ExecuteReader(); + while (reader.Read()) + { + list.Add((reader.GetString(0), reader.GetString(1))); + } + return list; + } + ); + + public void DeleteObject(string id) => + _pool.Use( + CacheOperation.Delete, + command => + { + command.Parameters.AddWithValue("@hash", id); + command.ExecuteNonQuery(); + } + ); + + public string? GetObject(string id) => + _pool.Use( + CacheOperation.Get, + command => + { + command.Parameters.AddWithValue("@hash", id); + return (string?)command.ExecuteScalar(); + } + ); + + //This does an insert or ignores if already exists + public void SaveObject(string id, string json) => + _pool.Use( + CacheOperation.InsertOrIgnore, + command => + { + command.Parameters.AddWithValue("@hash", id); + command.Parameters.AddWithValue("@content", json); + command.ExecuteNonQuery(); + } + ); + + //This does an insert or replaces if already exists + public void UpdateObject(string id, string json) => + _pool.Use( + CacheOperation.InsertOrReplace, + command => + { + command.Parameters.AddWithValue("@hash", id); + command.Parameters.AddWithValue("@content", json); + command.ExecuteNonQuery(); + } + ); + + public void SaveObjects(IEnumerable<(string id, string json)> items) => + _pool.Use( + CacheOperation.BulkInsertOrIgnore, + cmd => + { + CreateBulkInsert(cmd, items); + return cmd.ExecuteNonQuery(); + } + ); + + private void CreateBulkInsert(SqliteCommand cmd, IEnumerable<(string id, string json)> items) + { + StringBuilder sb = Pools.StringBuilders.Get(); + sb.AppendLine(CacheDbCommands.Commands[(int)CacheOperation.BulkInsertOrIgnore]); + int i = 0; + foreach (var (id, json) in items) + { + sb.Append($"(@key{i}, @value{i}),"); + cmd.Parameters.AddWithValue($"@key{i}", id); + cmd.Parameters.AddWithValue($"@value{i}", json); + i++; + } + sb.Remove(sb.Length - 1, 1); + sb.Append(';'); +#pragma warning disable CA2100 + cmd.CommandText = sb.ToString(); +#pragma warning restore CA2100 + Pools.StringBuilders.Return(sb); + } + + public bool HasObject(string objectId) => + _pool.Use( + CacheOperation.Has, + command => + { + command.Parameters.AddWithValue("@hash", objectId); + using var reader = command.ExecuteReader(); + bool rowFound = reader.Read(); + return rowFound; + } + ); +} diff --git a/src/Speckle.Sdk/SQLite/SqLiteJsonCacheException.cs b/src/Speckle.Sdk/SQLite/SqLiteJsonCacheException.cs new file mode 100644 index 00000000..b98de798 --- /dev/null +++ b/src/Speckle.Sdk/SQLite/SqLiteJsonCacheException.cs @@ -0,0 +1,35 @@ +using Microsoft.Data.Sqlite; + +namespace Speckle.Sdk.SQLite; + +public class SqLiteJsonCacheException : SpeckleException +{ + public SqLiteJsonCacheException() { } + + public SqLiteJsonCacheException(string message) + : base(message) { } + + public SqLiteJsonCacheException(string message, Exception inner) + : base(message, inner) { } + + public static SqLiteJsonCacheException Create(SqliteException inner) + { + if (!SqliteExceptions.SqliteErrorCodes.TryGetValue(inner.SqliteErrorCode, out string? errorMessage)) + { + errorMessage = $"An error occurred while executing a SQLite command: {inner.SqliteErrorCode}"; + } + if ( + !SqliteExceptions.SqliteExtendedResultCodes.TryGetValue( + inner.SqliteExtendedErrorCode, + out string? detailedMessage + ) + ) + { + detailedMessage = $"Detail: {inner.SqliteExtendedErrorCode}"; + } + return new SqLiteJsonCacheException( + $"An error occured with the SQLite cache: {inner.Message}{Environment.NewLine}{errorMessage}{Environment.NewLine}{detailedMessage}", + inner + ); + } +} diff --git a/src/Speckle.Sdk/SQLite/SqLiteJsonCacheManagerFactory.cs b/src/Speckle.Sdk/SQLite/SqLiteJsonCacheManagerFactory.cs new file mode 100644 index 00000000..f8fedca4 --- /dev/null +++ b/src/Speckle.Sdk/SQLite/SqLiteJsonCacheManagerFactory.cs @@ -0,0 +1,19 @@ +using Speckle.InterfaceGenerator; +using Speckle.Sdk.Logging; +using Speckle.Sdk.Serialisation.Utilities; + +namespace Speckle.Sdk.SQLite; + +[GenerateAutoInterface] +public class SqLiteJsonCacheManagerFactory : ISqLiteJsonCacheManagerFactory +{ + public const int INITIAL_CONCURRENCY = 4; + + private ISqLiteJsonCacheManager Create(string path, int concurrency) => new SqLiteJsonCacheManager(path, concurrency); + + public ISqLiteJsonCacheManager CreateForUser(string scope) => + Create(Path.Combine(SpecklePathProvider.UserApplicationDataPath(), "Speckle", $"{scope}.db"), 1); + + public ISqLiteJsonCacheManager CreateFromStream(string streamId) => + Create(SqlitePaths.GetDBPath(streamId), INITIAL_CONCURRENCY); +} diff --git a/src/Speckle.Sdk/SQLite/SqliteExceptions.cs b/src/Speckle.Sdk/SQLite/SqliteExceptions.cs new file mode 100644 index 00000000..eec7738c --- /dev/null +++ b/src/Speckle.Sdk/SQLite/SqliteExceptions.cs @@ -0,0 +1,107 @@ +namespace Speckle.Sdk.SQLite; + +internal static class SqliteExceptions +{ + public static readonly IReadOnlyDictionary SqliteErrorCodes = new Dictionary + { + { 0, "Successful result" }, + { 1, "Generic error" }, + { 2, "Internal logic error in SQLite" }, + { 3, "Access permission denied" }, + { 4, "Callback routine requested an abort" }, + { 5, "The database file is locked" }, + { 6, "A table in the database is locked" }, + { 7, "A malloc() failed" }, + { 8, "Attempt to write a readonly database" }, + { 9, "Operation terminated by sqlite3_interrupt()" }, + { 10, "Some kind of disk I/O error occurred" }, + { 11, "The database disk image is malformed" }, + { 12, "Unknown opcode in sqlite3_file_control()" }, + { 13, "Insertion failed because database is full" }, + { 14, "Unable to open the database file" }, + { 15, "Database lock protocol error" }, + { 16, "Internal use only" }, + { 17, "The database schema changed" }, + { 18, "String or BLOB exceeds size limit" }, + { 19, "Abort due to constraint violation" }, + { 20, "Data type mismatch" }, + { 21, "Library used incorrectly" }, + { 22, "Uses OS features not supported on host" }, + { 23, "Authorization denied" }, + { 24, "Not used" }, + { 25, "2nd parameter to sqlite3_bind out of range" }, + { 26, "File opened that is not a database file" }, + { 27, "Notifications from sqlite3_log()" }, + { 28, "Warnings from sqlite3_log()" }, + { 100, "sqlite3_step() has another row ready" }, + { 101, "sqlite3_step() has finished executing" }, + }; + + public static readonly IReadOnlyDictionary SqliteExtendedResultCodes = new Dictionary() + { + { 516, "SQLITE_ABORT_ROLLBACK: A rollback occurred due to a constraint violation." }, + { 279, "SQLITE_AUTH_USER: Authorization denied by a user authentication callback." }, + { 261, "SQLITE_BUSY_RECOVERY: The database file is locked because another connection is recovering the WAL." }, + { 517, "SQLITE_BUSY_SNAPSHOT: The database file is locked because another connection has a conflicting snapshot." }, + { 773, "SQLITE_BUSY_TIMEOUT: A blocking operation was interrupted by a call to sqlite3_interrupt()." }, + { 1038, "SQLITE_CANTOPEN_CONVPATH: Unable to open the database file due to a conversion error." }, + { + 1294, + "SQLITE_CANTOPEN_DIRTYWAL: The database file cannot be opened because the Write-Ahead Log contains uncommitted changes." + }, + { 782, "SQLITE_CANTOPEN_FULLPATH: Unable to open the database file with the full pathname." }, + { 526, "SQLITE_CANTOPEN_ISDIR: The database file cannot be opened because it is a directory." }, + { + 270, + "SQLITE_CANTOPEN_NOTEMPDIR: Unable to open a temporary database file because a temporary directory is not available." + }, + { 1550, "SQLITE_CANTOPEN_SYMLINK: The database file cannot be opened because it is a symbolic link." }, + { 275, "SQLITE_CONSTRAINT_CHECK: A CHECK constraint failed." }, + { 531, "SQLITE_CONSTRAINT_COMMITHOOK: A commit hook caused the transaction to roll back." }, + { 3091, "SQLITE_CONSTRAINT_DATATYPE: A datatype mismatch occurred." }, + { 787, "SQLITE_CONSTRAINT_FOREIGNKEY: A foreign key constraint failed." }, + { 1043, "SQLITE_CONSTRAINT_FUNCTION: A function constraint failed." }, + { 1299, "SQLITE_CONSTRAINT_NOTNULL: A NOT NULL constraint failed." }, + { 1555, "SQLITE_CONSTRAINT_PRIMARYKEY: A PRIMARY KEY constraint failed." }, + { 1803, "SQLITE_CONSTRAINT_TRIGGER: A trigger constraint failed." }, + { 2059, "SQLITE_CONSTRAINT_UNIQUE: A UNIQUE constraint failed." }, + { 2315, "SQLITE_CONSTRAINT_VTAB: A virtual table constraint failed." }, + { 2571, "SQLITE_CONSTRAINT_ROWID: A rowid constraint failed." }, + { 1034, "SQLITE_IOERR_FSYNC: An I/O error occurred during the fsync() system call." }, + { 6410, "SQLITE_IOERR_GETTEMPPATH: An I/O error occurred while trying to get the temporary file path." }, + { 3850, "SQLITE_IOERR_LOCK: An I/O error occurred while trying to lock the database file." }, + { 6154, "SQLITE_IOERR_MMAP: An I/O error occurred during memory mapping." }, + { 3082, "SQLITE_IOERR_NOMEM: An I/O error occurred due to a memory allocation failure." }, + { 2314, "SQLITE_IOERR_RDLOCK: An I/O error occurred while trying to read-lock the database file." }, + { 266, "SQLITE_IOERR_READ: An I/O error occurred while reading from the database file." }, + { 7946, "SQLITE_IOERR_ROLLBACK_ATOMIC: An I/O error occurred during an atomic rollback." }, + { 5642, "SQLITE_IOERR_SEEK: An I/O error occurred while seeking in the database file." }, + { 5130, "SQLITE_IOERR_SHMLOCK: An I/O error occurred while locking a shared memory segment." }, + { 5386, "SQLITE_IOERR_SHMMAP: An I/O error occurred while mapping a shared memory segment." }, + { 4618, "SQLITE_IOERR_SHMOPEN: An I/O error occurred while opening a shared memory segment." }, + { 4874, "SQLITE_IOERR_SHMSIZE: An I/O error occurred while setting the size of a shared memory segment." }, + { 522, "SQLITE_IOERR_SHORT_READ: An I/O error occurred due to a short read." }, + { 1546, "SQLITE_IOERR_TRUNCATE: An I/O error occurred while truncating the database file." }, + { 2058, "SQLITE_IOERR_UNLOCK: An I/O error occurred while unlocking the database file." }, + { 6922, "SQLITE_IOERR_VNODE: A virtual node I/O error occurred." }, + { 778, "SQLITE_IOERR_WRITE: An I/O error occurred while writing to the database file." }, + { + 262, + "SQLITE_LOCKED_SHAREDCACHE: A write operation could not continue due to a conflict within the shared cache." + }, + { 518, "SQLITE_LOCKED_VTAB: A virtual table is locked." }, + { 539, "SQLITE_NOTICE_RECOVER_ROLLBACK: A rollback was performed to recover from a previous error." }, + { 283, "SQLITE_NOTICE_RECOVER_WAL: Recovery was performed from the Write-Ahead Log." }, + { 256, "SQLITE_OK_LOAD_PERMANENTLY: Operation completed successfully; the extension was loaded permanently." }, + { + 1288, + "SQLITE_READONLY_CANTINIT: Attempt to write to a read-only database failed because initialization is not allowed." + }, + { 520, "SQLITE_READONLY_CANTLOCK: A read-only database cannot be locked." }, + { 1032, "SQLITE_READONLY_DBMOVED: The database file has been moved, making it read-only." }, + { 1544, "SQLITE_READONLY_DIRECTORY: The database is read-only because it is a directory." }, + { 264, "SQLITE_READONLY_RECOVERY: The database is read-only due to recovery mode." }, + { 776, "SQLITE_READONLY_ROLLBACK: The database is read-only because a rollback is required." }, + { 284, "SQLITE_WARNING_AUTOINDEX: Automatic indexing is in use." }, + }; +} diff --git a/src/Speckle.Sdk/ScrutorFunctions.cs b/src/Speckle.Sdk/ScrutorFunctions.cs new file mode 100644 index 00000000..93457b30 --- /dev/null +++ b/src/Speckle.Sdk/ScrutorFunctions.cs @@ -0,0 +1,113 @@ +using System.Runtime.CompilerServices; + +namespace Speckle.Sdk; + +/// +/// This is from the Scrutor project (https://github.com/khellang/Scrutor) but we don't want to pull in the dependencies +/// +public static class ScrutorFunctions +{ + public static bool IsNonAbstractClass(this Type type) + { + if (type.IsSpecialName) + { + return false; + } + + if (type.IsClass && !type.IsAbstract) + { + if (type.HasAttribute()) + { + return false; + } + + return type.IsPublic || type.IsNestedPublic; + } + + return false; + } + + public static bool HasAttribute(this Type type) + where T : Attribute + { + return type.HasAttribute(typeof(T)); + } + + public static bool HasAttribute(this Type type, Type attributeType) + { + return type.IsDefined(attributeType, inherit: true); + } + + public static IEnumerable FindMatchingInterface(this Type type) + { + var matchingInterfaceName = $"I{type.Name}"; + + var matchedInterfaces = GetImplementedInterfacesToMap(type) + .Where(x => string.Equals(x.Name, matchingInterfaceName, StringComparison.Ordinal)) + .ToArray(); + + if (matchedInterfaces.Length == 0) + { + yield break; + } + + Type? matchingType = matchedInterfaces.FirstOrDefault(); + + if (matchingType is null) + { + yield break; + } + + yield return matchingType; + } + + private static IEnumerable GetImplementedInterfacesToMap(Type type) + { + if (!type.IsGenericType) + { + return type.GetInterfaces(); + } + + if (!type.IsGenericTypeDefinition) + { + return type.GetInterfaces(); + } + + return FilterMatchingGenericInterfaces(type); + } + + private static IEnumerable FilterMatchingGenericInterfaces(Type type) + { + var genericArguments = type.GetGenericArguments(); + + foreach (var current in type.GetInterfaces()) + { + if ( + current.IsGenericType + && current.ContainsGenericParameters + && GenericParametersMatch(genericArguments, current.GetGenericArguments()) + ) + { + yield return current.GetGenericTypeDefinition(); + } + } + } + + private static bool GenericParametersMatch(IReadOnlyList parameters, IReadOnlyList interfaceArguments) + { + if (parameters.Count != interfaceArguments.Count) + { + return false; + } + + for (var i = 0; i < parameters.Count; i++) + { + if (parameters[i] != interfaceArguments[i]) + { + return false; + } + } + + return true; + } +} diff --git a/src/Speckle.Sdk/Serialisation/BaseObjectDeserializerV2.cs b/src/Speckle.Sdk/Serialisation/BaseObjectDeserializerV2.cs deleted file mode 100644 index da7c618c..00000000 --- a/src/Speckle.Sdk/Serialisation/BaseObjectDeserializerV2.cs +++ /dev/null @@ -1,369 +0,0 @@ -using System.Diagnostics; -using System.Reflection; -using Speckle.Newtonsoft.Json; -using Speckle.Newtonsoft.Json.Linq; -using Speckle.Sdk.Common; -using Speckle.Sdk.Host; -using Speckle.Sdk.Logging; -using Speckle.Sdk.Models; -using Speckle.Sdk.Serialisation.Utilities; -using Speckle.Sdk.Transports; - -namespace Speckle.Sdk.Serialisation; - -public sealed class BaseObjectDeserializerV2 -{ - private bool _isBusy; - private readonly object _callbackLock = new(); - private readonly object?[] _invokeNull = [null]; - - // id -> Base if already deserialized or id -> Task if was handled by a bg thread - private Dictionary? _deserializedObjects; - - /// - /// Property that describes the type of the object. - /// - private const string TYPE_DISCRIMINATOR = nameof(Base.speckle_type); - - private DeserializationWorkerThreads? _workerThreads; - - public CancellationToken CancellationToken { get; set; } - - /// - /// The sync transport. This transport will be used synchronously. - /// - public ITransport ReadTransport { get; set; } - - public Action? OnProgressAction { get; set; } - - public string? BlobStorageFolder { get; set; } - public TimeSpan Elapsed { get; private set; } - public int WorkerThreadCount { get; set; } = Math.Min(Environment.ProcessorCount, 6); //6 threads seems the sweet spot, see performance test project; - - /// The JSON string of the object to be deserialized - /// A typed object deserialized from the - /// Thrown when - /// was null - /// cannot be deserialised to type - // /// did not contain the required json objects (closures) - public Base Deserialize(string rootObjectJson) - { - if (_isBusy) - { - throw new InvalidOperationException( - "A deserializer instance can deserialize only 1 object at a time. Consider creating multiple deserializer instances" - ); - } - - try - { - _isBusy = true; - var stopwatch = Stopwatch.StartNew(); - _deserializedObjects = new(); - _workerThreads = new DeserializationWorkerThreads(this, WorkerThreadCount); - _workerThreads.Start(); - - var closures = ClosureParser.GetClosures(rootObjectJson); - int i = 0; - foreach (var closure in closures) - { - string objId = closure.Item1; - // pausing for getting object from the transport - stopwatch.Stop(); - string? objJson = ReadTransport.GetObject(objId); - - //TODO: We should fail loudly when a closure can't be found (objJson is null) - //but adding throw here breaks blobs tests, see CNX-8541 - - stopwatch.Start(); - object? deserializedOrPromise = DeserializeTransportObjectProxy(objJson, i++, closures.Count); - lock (_deserializedObjects) - { - _deserializedObjects[objId] = deserializedOrPromise; - } - } - - object? ret; - try - { - ret = DeserializeTransportObject(rootObjectJson, null, null); - } - catch (JsonReaderException ex) - { - throw new SpeckleDeserializeException("Failed to deserialize json", ex); - } - - stopwatch.Stop(); - Elapsed += stopwatch.Elapsed; - if (ret is not Base b) - { - throw new SpeckleDeserializeException( - $"Expected {nameof(rootObjectJson)} to be deserialized to type {nameof(Base)} but was {ret}" - ); - } - - return b; - } - finally - { - _deserializedObjects = null; - _workerThreads?.Dispose(); - _workerThreads = null; - _isBusy = false; - } - } - - private object? DeserializeTransportObjectProxy(string? objectJson, long? current, long? total) - { - if (objectJson is null) - { - return null; - } - // Try background work - Task? bgResult = _workerThreads?.TryStartTask( - WorkerThreadTaskType.Deserialize, - objectJson, - current, - total - ); //BUG: Because we don't guarantee this task will ever be awaited, this may lead to unobserved exceptions! - if (bgResult != null) - { - return bgResult; - } - - // SyncS - return DeserializeTransportObject(objectJson, current, total); - } - - internal object? DeserializeTransportObject(string objectJson, long? currentObjectCount, long? totalObjectCount) - { - if (objectJson is null) - { - throw new ArgumentNullException(nameof(objectJson), $"Cannot deserialize {nameof(objectJson)}, value was null"); - } - // Apparently this automatically parses DateTimes in strings if it matches the format: - // JObject doc1 = JObject.Parse(objectJson); - - // This is equivalent code that doesn't parse datetimes: - JObject doc1; - using (JsonReader reader = new JsonTextReader(new StringReader(objectJson))) - { - reader.DateParseHandling = DateParseHandling.None; - doc1 = JObject.Load(reader); - } - - object? converted; - try - { - converted = ConvertJsonElement(doc1, currentObjectCount, totalObjectCount); - } - catch (Exception ex) when (!ex.IsFatal() && ex is not OperationCanceledException) - { - throw new SpeckleDeserializeException($"Failed to deserialize {doc1} as {doc1.Type}", ex); - } - - lock (_callbackLock) - { - OnProgressAction?.Invoke(new ProgressArgs(ProgressEvent.DeserializeObject, currentObjectCount, totalObjectCount)); - } - - return converted; - } - - private object? ConvertJsonElement(JToken doc, long? currentObjectCount, long? totalObjectCount) - { - CancellationToken.ThrowIfCancellationRequested(); - - switch (doc.Type) - { - case JTokenType.Undefined: - case JTokenType.Null: - case JTokenType.None: - return null; - case JTokenType.Boolean: - return (bool)doc; - case JTokenType.Integer: - try - { - return (long)doc; - } - catch (OverflowException ex) - { - var v = (object)(double)doc; - SpeckleLog.Logger.Debug( - ex, - "Json property {tokenType} failed to deserialize {value} to {targetType}, will be deserialized as {fallbackType}", - doc.Type, - v, - typeof(long), - typeof(double) - ); - return v; - } - case JTokenType.Float: - return (double)doc; - case JTokenType.String: - return (string?)doc; - case JTokenType.Date: - return (DateTime)doc; - case JTokenType.Array: - JArray docAsArray = (JArray)doc; - List jsonList = new(docAsArray.Count); - int retListCount = 0; - foreach (JToken value in docAsArray) - { - object? convertedValue = ConvertJsonElement(value, currentObjectCount, totalObjectCount); - retListCount += convertedValue is DataChunk chunk ? chunk.data.Count : 1; - jsonList.Add(convertedValue); - } - - List retList = new(retListCount); - foreach (object? jsonObj in jsonList) - { - if (jsonObj is DataChunk chunk) - { - retList.AddRange(chunk.data); - } - else - { - retList.Add(jsonObj); - } - } - - return retList; - case JTokenType.Object: - var jObject = (JContainer)doc; - Dictionary dict = new(jObject.Count); - - foreach (JToken propJToken in jObject) - { - JProperty prop = (JProperty)propJToken; - if (prop.Name == "__closure") - { - continue; - } - - dict[prop.Name] = ConvertJsonElement(prop.Value, currentObjectCount, totalObjectCount); - } - - if (!dict.TryGetValue(TYPE_DISCRIMINATOR, out object? speckleType)) - { - return dict; - } - - if (speckleType as string == "reference" && dict.TryGetValue("referencedId", out object? referencedId)) - { - var objId = (string)referencedId.NotNull(); - object? deserialized = null; - _deserializedObjects.NotNull(); - lock (_deserializedObjects) - { - if (_deserializedObjects.TryGetValue(objId, out object? o)) - { - deserialized = o; - } - } - - if (deserialized is Task task) - { - try - { - deserialized = task.Result; - } - catch (AggregateException ex) - { - throw new SpeckleDeserializeException("Failed to deserialize reference object", ex); - } - lock (_deserializedObjects) - { - _deserializedObjects[objId] = deserialized; - } - } - - if (deserialized != null) - { - return deserialized; - } - - // This reference was not already deserialized. Do it now in sync mode - string? objectJson = ReadTransport.GetObject(objId); - if (objectJson is null) - { - throw new TransportException($"Failed to fetch object id {objId} from {ReadTransport} "); - } - - deserialized = DeserializeTransportObject(objectJson, currentObjectCount, totalObjectCount); - - lock (_deserializedObjects) - { - _deserializedObjects[objId] = deserialized; - } - - return deserialized; - } - - return Dict2Base(dict); - default: - throw new ArgumentException("Json value not supported: " + doc.Type, nameof(doc)); - } - } - - private Base Dict2Base(Dictionary dictObj) - { - string typeName = (string)dictObj[TYPE_DISCRIMINATOR].NotNull(); - Type type = TypeLoader.GetType(typeName); - Base baseObj = (Base)Activator.CreateInstance(type); - - dictObj.Remove(TYPE_DISCRIMINATOR); - dictObj.Remove("__closure"); - - var staticProperties = TypeCache.GetTypeProperties(typeName); - foreach (var entry in dictObj) - { - if (staticProperties.TryGetValue(entry.Key, out PropertyInfo? value) && value.CanWrite) - { - if (entry.Value == null) - { - // Check for JsonProperty(NullValueHandling = NullValueHandling.Ignore) attribute - JsonPropertyAttribute? attr = TypeLoader.GetJsonPropertyAttribute(value); - if (attr is { NullValueHandling: NullValueHandling.Ignore }) - { - continue; - } - } - - Type targetValueType = value.PropertyType; - bool conversionOk = ValueConverter.ConvertValue(targetValueType, entry.Value, out object? convertedValue); - if (conversionOk) - { - value.SetValue(baseObj, convertedValue); - } - else - { - // Cannot convert the value in the json to the static property type - throw new SpeckleDeserializeException( - $"Cannot deserialize {entry.Value?.GetType().FullName} to {targetValueType.FullName}" - ); - } - } - else - { - // No writable property with this name - CallSiteCache.SetValue(entry.Key, baseObj, entry.Value); - } - } - - if (baseObj is Blob bb && BlobStorageFolder != null) - { - bb.filePath = bb.GetLocalDestinationPath(BlobStorageFolder); - } - - var onDeserializedCallbacks = TypeCache.GetOnDeserializedCallbacks(typeName); - foreach (MethodInfo onDeserialized in onDeserializedCallbacks) - { - onDeserialized.Invoke(baseObj, _invokeNull); - } - - return baseObj; - } -} diff --git a/src/Speckle.Sdk/Serialisation/IdGenerator.cs b/src/Speckle.Sdk/Serialisation/IdGenerator.cs new file mode 100644 index 00000000..b732c7e7 --- /dev/null +++ b/src/Speckle.Sdk/Serialisation/IdGenerator.cs @@ -0,0 +1,19 @@ +using System.Diagnostics.Contracts; +using Speckle.Sdk.Helpers; +using Speckle.Sdk.Models; + +namespace Speckle.Sdk.Serialisation; + +public static class IdGenerator +{ + [Pure] + public static Id ComputeId(Json serialized) + { +#if NET6_0_OR_GREATER + string hash = Crypt.Sha256(serialized.Value.AsSpan(), length: HashUtility.HASH_LENGTH); +#else + string hash = Crypt.Sha256(serialized.Value, length: HashUtility.HASH_LENGTH); +#endif + return new Id(hash); + } +} diff --git a/src/Speckle.Sdk/Serialisation/SerializationResult.cs b/src/Speckle.Sdk/Serialisation/SerializationResult.cs new file mode 100644 index 00000000..570e73bc --- /dev/null +++ b/src/Speckle.Sdk/Serialisation/SerializationResult.cs @@ -0,0 +1,25 @@ +namespace Speckle.Sdk.Serialisation; + +public readonly record struct SerializationResult(Json Json, Id? Id); + +public readonly record struct Json(string Value) +{ + public override string ToString() => Value; +} + +public readonly record struct Id(string Value) +{ + public override string ToString() => Value; + + public bool Equals(Id? other) + { + if (other is null) + { + return false; + } + + return string.Equals(Value, other.Value.Value, StringComparison.OrdinalIgnoreCase); + } + + public override int GetHashCode() => Value.GetHashCode(); +} diff --git a/src/Speckle.Sdk/Serialisation/SpeckleDeserializeException.cs b/src/Speckle.Sdk/Serialisation/SpeckleDeserializeException.cs index 4ab2cec3..e19ea5aa 100644 --- a/src/Speckle.Sdk/Serialisation/SpeckleDeserializeException.cs +++ b/src/Speckle.Sdk/Serialisation/SpeckleDeserializeException.cs @@ -2,9 +2,11 @@ public class SpeckleDeserializeException : SpeckleException { - public SpeckleDeserializeException(string message, Exception? inner = null) + public SpeckleDeserializeException(string? message, Exception? inner = null) : base(message, inner) { } - public SpeckleDeserializeException(string message) + public SpeckleDeserializeException(string? message) : base(message) { } + + public SpeckleDeserializeException() { } } diff --git a/src/Speckle.Sdk/Serialisation/SpeckleObjectDeserializer.cs b/src/Speckle.Sdk/Serialisation/SpeckleObjectDeserializer.cs new file mode 100644 index 00000000..549e8a34 --- /dev/null +++ b/src/Speckle.Sdk/Serialisation/SpeckleObjectDeserializer.cs @@ -0,0 +1,334 @@ +using System.Collections.Concurrent; +using System.Diagnostics.CodeAnalysis; +using System.Numerics; +using System.Reflection; +using Speckle.Newtonsoft.Json; +using Speckle.Sdk.Common; +using Speckle.Sdk.Host; +using Speckle.Sdk.Models; +using Speckle.Sdk.Serialisation.Utilities; +using Speckle.Sdk.Transports; + +namespace Speckle.Sdk.Serialisation; + +public sealed class SpeckleObjectDeserializer +{ + private volatile bool _isBusy; + private readonly object?[] _invokeNull = [null]; + + // id -> Base if already deserialized or id -> ValueTask if was handled by a bg thread + private readonly ConcurrentDictionary _deserializedObjects = new(StringComparer.Ordinal); + private long _total; + + /// + /// Property that describes the type of the object. + /// + private const string TYPE_DISCRIMINATOR = nameof(Base.speckle_type); + + public CancellationToken CancellationToken { get; set; } + + /// + /// The sync transport. This transport will be used synchronously. + /// + public ITransport ReadTransport { get; set; } + + public IProgress? OnProgressAction { get; set; } + + public string? BlobStorageFolder { get; set; } + + /// The JSON string of the object to be deserialized + /// A typed object deserialized from the + /// Thrown when + /// was null + /// cannot be deserialised to type + // /// did not contain the required json objects (closures) + public async ValueTask DeserializeAsync([NotNull] string? rootObjectJson) + { + if (_isBusy) + { + throw new InvalidOperationException( + "A deserializer instance can deserialize only 1 object at a time. Consider creating multiple deserializer instances" + ); + } + + try + { + if (rootObjectJson is null) + { + throw new ArgumentNullException( + nameof(rootObjectJson), + $"Cannot deserialize {nameof(rootObjectJson)}, value was null" + ); + } + + _isBusy = true; + + var result = (Base)await DeserializeJsonAsyncInternal(rootObjectJson).NotNull().ConfigureAwait(false); + return result; + } + finally + { + _isBusy = false; + } + } + + private async ValueTask DeserializeJsonAsyncInternal(string objectJson) + { + // Apparently this automatically parses DateTimes in strings if it matches the format: + // JObject doc1 = JObject.Parse(objectJson); + + // This is equivalent code that doesn't parse datetimes: + using JsonTextReader reader = SpeckleObjectSerializerPool.Instance.GetJsonTextReader(new StringReader(objectJson)); + reader.DateParseHandling = DateParseHandling.None; + + object? converted; + try + { + reader.Read(); + converted = await ReadObjectAsync(reader, CancellationToken).ConfigureAwait(false); + } + catch (Exception ex) when (!ex.IsFatal() && ex is not OperationCanceledException) + { + throw new SpeckleDeserializeException("Failed to deserialize", ex); + } + + OnProgressAction?.Report(new ProgressArgs(ProgressEvent.DeserializeObject, _deserializedObjects.Count, _total)); + + return converted; + } + + //this should be buffered + private async ValueTask> ReadArrayAsync(JsonReader reader, CancellationToken ct) + { + reader.Read(); + List retList = new(); + while (reader.TokenType != JsonToken.EndArray) + { + object? convertedValue = await ReadPropertyAsync(reader, ct).ConfigureAwait(false); + if (convertedValue is DataChunk chunk) + { + retList.AddRange(chunk.data); + } + else + { + retList.Add(convertedValue); + } + reader.Read(); //goes to next + } + return retList; + } + + private async ValueTask ReadObjectAsync(JsonReader reader, CancellationToken ct) + { + reader.Read(); + Dictionary dict = new(); + while (reader.TokenType != JsonToken.EndObject) + { + switch (reader.TokenType) + { + case JsonToken.PropertyName: + { + string propName = (reader.Value?.ToString()).NotNull(); + if (propName == "__closure") + { + reader.Read(); //goes to prop value + var closures = ClosureParser.GetClosures(reader, CancellationToken); + if (closures.Any()) + { + _total = 0; + foreach (var closure in closures) + { + string objId = closure.Item1; + //don't do anything with return value but later check if null + // https://linear.app/speckle/issue/CXPLA-54/when-deserializing-dont-allow-closures-that-arent-downloadable + await TryGetDeserializedAsync(objId).ConfigureAwait(false); + } + } + + reader.Read(); //goes to next + continue; + } + reader.Read(); //goes prop value + object? convertedValue = await ReadPropertyAsync(reader, ct).ConfigureAwait(false); + dict[propName] = convertedValue; + reader.Read(); //goes to next + } + break; + default: + throw new InvalidOperationException($"Unknown {reader.ValueType} with {reader.Value}"); + } + } + + if (!dict.TryGetValue(TYPE_DISCRIMINATOR, out object? speckleType)) + { + return dict; + } + + if (speckleType as string == "reference" && dict.TryGetValue("referencedId", out object? referencedId)) + { + var objId = (string)referencedId.NotNull(); + object? deserialized = await TryGetDeserializedAsync(objId).ConfigureAwait(false); + return deserialized; + } + + return Dict2Base(dict); + } + + private async ValueTask TryGetDeserializedAsync(string objId) + { + object? deserialized = null; + _deserializedObjects.NotNull(); + if (_deserializedObjects.TryGetValue(objId, out object? o)) + { + deserialized = o; + } + + if (deserialized is Task task) + { + try + { + deserialized = await task.ConfigureAwait(false); + } + catch (AggregateException ex) + { + throw new SpeckleDeserializeException("Failed to deserialize reference object", ex); + } + + _deserializedObjects.TryAdd(objId, deserialized); + } + if (deserialized is ValueTask valueTask) + { + try + { + deserialized = await valueTask.ConfigureAwait(false); + } + catch (AggregateException ex) + { + throw new SpeckleDeserializeException("Failed to deserialize reference object", ex); + } + + _deserializedObjects.TryAdd(objId, deserialized); + } + + if (deserialized != null) + { + return deserialized; + } + + // This reference was not already deserialized. Do it now in sync mode + string? objectJson = await ReadTransport.GetObject(objId).ConfigureAwait(false); + if (objectJson is null) + { + return null; + } + + deserialized = await DeserializeJsonAsyncInternal(objectJson).ConfigureAwait(false); + + _deserializedObjects.TryAdd(objId, deserialized); + + return deserialized; + } + + private async ValueTask ReadPropertyAsync(JsonReader reader, CancellationToken ct) + { + ct.ThrowIfCancellationRequested(); + switch (reader.TokenType) + { + case JsonToken.Undefined: + case JsonToken.Null: + case JsonToken.None: + return null; + case JsonToken.Boolean: + return (bool)reader.Value.NotNull(); + case JsonToken.Integer: + if (reader.Value is long longValue) + { + return longValue; + } + if (reader.Value is BigInteger bitInt) + { + // This is behaviour carried over from v2 to facilitate large numbers from Python + // This is quite hacky, as it's a bit questionable exactly what numbers are supported, and with what tolerance + // For this reason, this can be considered undocumented behaviour, and is only for values within the range of a 64bit integer. + return (double)bitInt; + } + + throw new ArgumentException( + $"Found an unsupported integer type {reader.Value?.GetType()} with value {reader.Value}" + ); + case JsonToken.Float: + return (double)reader.Value.NotNull(); + case JsonToken.String: + return (string?)reader.Value.NotNull(); + case JsonToken.Date: + return (DateTime)reader.Value.NotNull(); + case JsonToken.StartArray: + return await ReadArrayAsync(reader, ct).ConfigureAwait(false); + case JsonToken.StartObject: + var dict = await ReadObjectAsync(reader, ct).ConfigureAwait(false); + return dict; + + default: + throw new ArgumentException("Json value not supported: " + reader.ValueType); + } + } + + private Base Dict2Base(Dictionary dictObj) + { + string typeName = (string)dictObj[TYPE_DISCRIMINATOR].NotNull(); + Type type = TypeLoader.GetType(typeName); + Base baseObj = (Base)Activator.CreateInstance(type).NotNull(); + + dictObj.Remove(TYPE_DISCRIMINATOR); + dictObj.Remove("__closure"); + + var staticProperties = TypeCache.GetTypeProperties(typeName); + foreach (var entry in dictObj) + { + if (staticProperties.TryGetValue(entry.Key, out PropertyInfo? value) && value.CanWrite) + { + if (entry.Value == null) + { + // Check for JsonProperty(NullValueHandling = NullValueHandling.Ignore) attribute + JsonPropertyAttribute? attr = TypeLoader.GetJsonPropertyAttribute(value); + if (attr is { NullValueHandling: NullValueHandling.Ignore }) + { + continue; + } + } + + Type targetValueType = value.PropertyType; + bool conversionOk = ValueConverter.ConvertValue(targetValueType, entry.Value, out object? convertedValue); + if (conversionOk) + { + value.SetValue(baseObj, convertedValue); + } + else + { + // Cannot convert the value in the json to the static property type + throw new SpeckleDeserializeException( + $"Cannot deserialize {entry.Value?.GetType().FullName} to {targetValueType.FullName}" + ); + } + } + else + { + // No writable property with this name + CallSiteCache.SetValue(entry.Key, baseObj, entry.Value); + } + } + + if (baseObj is Blob bb && BlobStorageFolder != null) + { + bb.filePath = bb.GetLocalDestinationPath(BlobStorageFolder); + } + + var onDeserializedCallbacks = TypeCache.GetOnDeserializedCallbacks(typeName); + foreach (MethodInfo onDeserialized in onDeserializedCallbacks) + { + onDeserialized.Invoke(baseObj, _invokeNull); + } + + return baseObj; + } +} diff --git a/src/Speckle.Sdk/Serialisation/BaseObjectSerializerV2.cs b/src/Speckle.Sdk/Serialisation/SpeckleObjectSerializer.cs similarity index 63% rename from src/Speckle.Sdk/Serialisation/BaseObjectSerializerV2.cs rename to src/Speckle.Sdk/Serialisation/SpeckleObjectSerializer.cs index 25a01fc3..618f7e6f 100644 --- a/src/Speckle.Sdk/Serialisation/BaseObjectSerializerV2.cs +++ b/src/Speckle.Sdk/Serialisation/SpeckleObjectSerializer.cs @@ -1,30 +1,30 @@ using System.Collections; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Drawing; using System.Globalization; using System.Reflection; using Speckle.DoubleNumerics; using Speckle.Newtonsoft.Json; using Speckle.Sdk.Common; +using Speckle.Sdk.Dependencies; using Speckle.Sdk.Helpers; -using Speckle.Sdk.Logging; using Speckle.Sdk.Models; +using Speckle.Sdk.Serialisation.Utilities; using Speckle.Sdk.Transports; -using Constants = Speckle.Sdk.Helpers.Constants; namespace Speckle.Sdk.Serialisation; -public class BaseObjectSerializerV2 +public class SpeckleObjectSerializer { private readonly Stopwatch _stopwatch = new(); private volatile bool _isBusy; private List> _parentClosures = new(); private HashSet _parentObjects = new(); private readonly Dictionary> _typedPropertiesCache = new(); - private readonly Action? _onProgressAction; + private readonly IProgress? _onProgressAction; private readonly bool _trackDetachedChildren; + private int _serializedCount; /// /// Keeps track of all detached children created during serialisation that have an applicationId (provided this serializer instance has been told to track detached children). @@ -40,7 +40,7 @@ public class BaseObjectSerializerV2 /// The current total elapsed time spent serializing public TimeSpan Elapsed => _stopwatch.Elapsed; - public BaseObjectSerializerV2() + public SpeckleObjectSerializer() : this(Array.Empty()) { } /// @@ -50,9 +50,9 @@ public class BaseObjectSerializerV2 /// Used to track progress. /// Whether to store all detachable objects while serializing. They can be retrieved via post serialization. /// - public BaseObjectSerializerV2( + public SpeckleObjectSerializer( IReadOnlyCollection writeTransports, - Action? onProgressAction = null, + IProgress? onProgressAction = null, bool trackDetachedChildren = false, CancellationToken cancellationToken = default ) @@ -82,24 +82,16 @@ public class BaseObjectSerializerV2 { _stopwatch.Start(); _isBusy = true; - IReadOnlyDictionary converted; try { - int count = 0; - var x = PreserializeBase(baseObj, ref count, true); - if (x is null) - { - throw new SpeckleSerializeException("Already serialized"); - } - converted = x; + var result = SerializeBase(baseObj, true).NotNull(); + StoreObject(result.Id.NotNull(), result.Json); + return result.Json.Value; } catch (Exception ex) when (!ex.IsFatal() && ex is not OperationCanceledException) { throw new SpeckleSerializeException($"Failed to extract (pre-serialize) properties from the {baseObj}", ex); } - string serialized = Dict2Json(converted); - StoreObject((string)converted["id"].NotNull(), serialized); - return serialized; } finally { @@ -112,9 +104,9 @@ public class BaseObjectSerializerV2 // `Preserialize` means transforming all objects into the final form that will appear in json, with basic .net objects // (primitives, lists and dictionaries with string keys) - public object? PreserializeObject( + private void SerializeProperty( object? obj, - ref int count, + JsonWriter writer, bool computeClosures = false, PropertyAttributeInfo inheritedDetachInfo = default ) @@ -123,12 +115,14 @@ public class BaseObjectSerializerV2 if (obj == null) { - return null; + writer.WriteNull(); + return; } if (obj.GetType().IsPrimitive || obj is string) { - return obj; + writer.WriteValue(obj); + return; } switch (obj) @@ -137,14 +131,12 @@ public class BaseObjectSerializerV2 // Note: this change was needed as we've made the ObjectReference type inherit from Base for // the purpose of the "do not convert unchanged previously converted objects" POC. case ObjectReference r: - { - Dictionary ret = - new() - { - ["speckle_type"] = r.speckle_type, - ["referencedId"] = r.referencedId, - ["__closure"] = r.closure - }; + Dictionary ret = new() + { + ["speckle_type"] = r.speckle_type, + ["referencedId"] = r.referencedId, + ["__closure"] = r.closure, + }; if (r.closure is not null) { foreach (var kvp in r.closure) @@ -153,100 +145,93 @@ public class BaseObjectSerializerV2 } } UpdateParentClosures(r.referencedId); - return ret; - } - // Complex enough to deserve its own function + SerializeProperty(ret, writer); + break; case Base b: - return PreserializeBase(b, ref count, computeClosures, inheritedDetachInfo); + var result = SerializeBase(b, computeClosures, inheritedDetachInfo); + if (result is not null) + { + writer.WriteRawValue(result.Value.Json.Value); + } + else + { + writer.WriteNull(); + } + break; case IDictionary d: - { - Dictionary ret = new(d.Count); - foreach (DictionaryEntry kvp in d) { - object? converted = PreserializeObject(kvp.Value, ref count, inheritedDetachInfo: inheritedDetachInfo); - if (converted != null) + writer.WriteStartObject(); + + foreach (DictionaryEntry kvp in d) { - ret[kvp.Key.ToString()] = converted; + if (kvp.Key is not string key) + { + throw new ArgumentException( + "Serializing dictionaries that are not string based keys is not supported", + nameof(obj) + ); + } + + writer.WritePropertyName(key); + SerializeProperty(kvp.Value, writer, inheritedDetachInfo: inheritedDetachInfo); } + writer.WriteEndObject(); } - return ret; - } - case IEnumerable e: - { - //TODO: handle IReadonlyDictionary - int preSize = (e is IList list) ? list.Count : 0; - - List ret = new(preSize); - - foreach (object? element in e) + break; + case ICollection e: { - ret.Add(PreserializeObject(element, ref count, inheritedDetachInfo: inheritedDetachInfo)); + writer.WriteStartArray(); + foreach (object? element in e) + { + SerializeProperty(element, writer, inheritedDetachInfo: inheritedDetachInfo); + } + writer.WriteEndArray(); } - - return ret; - } + break; case Enum: - return (int)obj; + writer.WriteValue((int)obj); + break; // Support for simple types case Guid g: - return g.ToString(); + writer.WriteValue(g.ToString()); + break; case Color c: - return c.ToArgb(); + writer.WriteValue(c.ToArgb()); + break; case DateTime t: - return t.ToString("o", CultureInfo.InvariantCulture); + writer.WriteValue(t.ToString("o", CultureInfo.InvariantCulture)); + break; case Matrix4x4 md: - return new List - { - md.M11, - md.M12, - md.M13, - md.M14, - md.M21, - md.M22, - md.M23, - md.M24, - md.M31, - md.M32, - md.M33, - md.M34, - md.M41, - md.M42, - md.M43, - md.M44 - }; + writer.WriteStartArray(); + + writer.WriteValue(md.M11); + writer.WriteValue(md.M12); + writer.WriteValue(md.M13); + writer.WriteValue(md.M14); + writer.WriteValue(md.M21); + writer.WriteValue(md.M22); + writer.WriteValue(md.M23); + writer.WriteValue(md.M24); + writer.WriteValue(md.M31); + writer.WriteValue(md.M32); + writer.WriteValue(md.M33); + writer.WriteValue(md.M34); + writer.WriteValue(md.M41); + writer.WriteValue(md.M42); + writer.WriteValue(md.M43); + writer.WriteValue(md.M44); + writer.WriteEndArray(); + break; //BACKWARDS COMPATIBILITY: matrix4x4 changed from System.Numerics float to System.DoubleNumerics double in release 2.16 - case System.Numerics.Matrix4x4 ms: - SpeckleLog.Logger.Warning( - "This kept for backwards compatibility, no one should be using {this}", - "BaseObjectSerializerV2 serialize System.Numerics.Matrix4x4" - ); - return new List - { - ms.M11, - ms.M12, - ms.M13, - ms.M14, - ms.M21, - ms.M22, - ms.M23, - ms.M24, - ms.M31, - ms.M32, - ms.M33, - ms.M34, - ms.M41, - ms.M42, - ms.M43, - ms.M44 - }; + case System.Numerics.Matrix4x4: + throw new ArgumentException("Please use Speckle.DoubleNumerics.Matrix4x4 instead", nameof(obj)); default: - throw new ArgumentException($"Unsupported value in serialization: {obj.GetType()}"); + throw new ArgumentException($"Unsupported value in serialization: {obj.GetType()}", nameof(obj)); } } - private IReadOnlyDictionary? PreserializeBase( + internal SerializationResult? SerializeBase( Base baseObj, - ref int count, bool computeClosures = false, PropertyAttributeInfo inheritedDetachInfo = default ) @@ -264,7 +249,12 @@ public class BaseObjectSerializerV2 _parentClosures.Add(closure); } - IReadOnlyDictionary convertedBase = PreserializeBaseProperties(baseObj, ref count, closure); + var stringBuilder = Pools.StringBuilders.Get(); + using var writer = new StringWriter(); + using var jsonWriter = SpeckleObjectSerializerPool.Instance.GetJsonTextWriter(writer); + var id = SerializeBaseObject(baseObj, jsonWriter, closure); + var json = new Json(writer.ToString()); + Pools.StringBuilders.Return(stringBuilder); if (computeClosures || inheritedDetachInfo.IsDetachable || baseObj is Blob) { @@ -276,44 +266,43 @@ public class BaseObjectSerializerV2 if (baseObj is Blob myBlob) { StoreBlob(myBlob); - UpdateParentClosures($"blob:{convertedBase["id"]}"); - return convertedBase; + UpdateParentClosures($"blob:{id}"); + return new(json, id); } if (inheritedDetachInfo.IsDetachable && WriteTransports.Count > 0) { - var json = Dict2Json(convertedBase); - var id = (string)convertedBase["id"].NotNull(); StoreObject(id, json); - ObjectReference objRef = new() { referencedId = id }; - var objRefConverted = (IReadOnlyDictionary?)PreserializeObject(objRef, ref count); - UpdateParentClosures(id); - _onProgressAction?.Invoke(new(ProgressEvent.SerializeObject, ++count, null)); + + var json2 = ReferenceGenerator.CreateReference(id); + UpdateParentClosures(id.Value); + + _onProgressAction?.Report(new(ProgressEvent.SerializeObject, ++_serializedCount, null)); // add to obj refs to return if (baseObj.applicationId != null && _trackDetachedChildren) // && baseObj is not DataChunk && baseObj is not Abstract) // not needed, as data chunks will never have application ids, and abstract objs are not really used. { ObjectReferences[baseObj.applicationId] = new ObjectReference() { - referencedId = id, + referencedId = id.Value, applicationId = baseObj.applicationId, - closure = closure + closure = closure, }; } - - return objRefConverted; + return new(json2, null); } - return convertedBase; + return new(json, id); } - private IReadOnlyDictionary ExtractAllProperties(Base baseObj) + private Dictionary ExtractAllProperties(Base baseObj) { IReadOnlyList<(PropertyInfo, PropertyAttributeInfo)> typedProperties = GetTypedPropertiesWithCache(baseObj); - IReadOnlyCollection dynamicProperties = baseObj.GetDynamicPropertyKeys(); + IReadOnlyCollection dynamicProperties = baseObj.DynamicPropertyKeys; // propertyName -> (originalValue, isDetachable, isChunkable, chunkSize) - Dictionary allProperties = - new(typedProperties.Count + dynamicProperties.Count); + Dictionary allProperties = new( + typedProperties.Count + dynamicProperties.Count + ); // Construct `allProperties`: Add typed properties foreach ((PropertyInfo propertyInfo, PropertyAttributeInfo detachInfo) in typedProperties) @@ -331,68 +320,83 @@ public class BaseObjectSerializerV2 } object? baseValue = baseObj[propName]; - bool isDetachable = propName.StartsWith("@"); - bool isChunkable = false; - int chunkSize = 1000; - if (Constants.ChunkPropertyNameRegex.IsMatch(propName)) - { - var match = Constants.ChunkPropertyNameRegex.Match(propName); - isChunkable = int.TryParse(match.Groups[^1].Value, out chunkSize); - } + bool isDetachable = PropNameValidator.IsDetached(propName); + + int chunkSize = 1000; + bool isChunkable = isDetachable && PropNameValidator.IsChunkable(propName, out chunkSize); + allProperties[propName] = (baseValue, new PropertyAttributeInfo(isDetachable, isChunkable, chunkSize, null)); } return allProperties; } - private IReadOnlyDictionary PreserializeBaseProperties( - Base baseObj, - ref int count, - IReadOnlyDictionary closure - ) + private Id SerializeBaseObject(Base baseObj, JsonWriter writer, IReadOnlyDictionary closure) { var allProperties = ExtractAllProperties(baseObj); - Dictionary convertedBase = new(allProperties.Count + 2); + if (baseObj is not Blob) + { + writer = new SerializerIdWriter(writer); + } + + writer.WriteStartObject(); // Convert all properties foreach (var prop in allProperties) { - object? convertedValue = PreserializeBasePropertyValue(prop.Value.Item1, ref count, prop.Value.Item2); - - if ( - convertedValue == null - && prop.Value.Item2.JsonPropertyInfo is { NullValueHandling: NullValueHandling.Ignore } - ) + if (prop.Value.info.JsonPropertyInfo is { NullValueHandling: NullValueHandling.Ignore }) { continue; } - convertedBase[prop.Key] = convertedValue; + writer.WritePropertyName(prop.Key); + SerializeProperty(prop.Value.value, writer, prop.Value.info); } - convertedBase["id"] = baseObj is Blob blob ? blob.id : ComputeId(convertedBase); + Id id; + if (writer is SerializerIdWriter serializerIdWriter) + { + (var json, writer) = serializerIdWriter.FinishIdWriter(); + id = IdGenerator.ComputeId(json); + } + else + { + id = new Id(((Blob)baseObj).id.NotNull()); + } + writer.WritePropertyName("id"); + writer.WriteValue(id.Value); + baseObj.id = id.Value; if (closure.Count > 0) { - convertedBase["__closure"] = closure; + writer.WritePropertyName("__closure"); + writer.WriteStartObject(); + foreach (var c in closure) + { + writer.WritePropertyName(c.Key); + writer.WriteValue(c.Value); + } + writer.WriteEndObject(); } - return convertedBase; + writer.WriteEndObject(); + return id; } - private object? PreserializeBasePropertyValue(object? baseValue, ref int count, PropertyAttributeInfo detachInfo) + private void SerializeProperty(object? baseValue, JsonWriter jsonWriter, PropertyAttributeInfo detachInfo) { // If there are no WriteTransports, keep everything attached. if (WriteTransports.Count == 0) { - return PreserializeObject(baseValue, ref count, inheritedDetachInfo: detachInfo); + SerializeProperty(baseValue, jsonWriter, inheritedDetachInfo: detachInfo); + return; } if (baseValue is IEnumerable chunkableCollection && detachInfo.IsChunkable) { - List chunks = new(); - DataChunk crtChunk = new() { data = new List(detachInfo.ChunkSize) }; + List chunks = new(); + DataChunk crtChunk = new() { data = new List(detachInfo.ChunkSize) }; foreach (object element in chunkableCollection) { @@ -400,7 +404,7 @@ public class BaseObjectSerializerV2 if (crtChunk.data.Count >= detachInfo.ChunkSize) { chunks.Add(crtChunk); - crtChunk = new DataChunk { data = new List(detachInfo.ChunkSize) }; + crtChunk = new DataChunk { data = new List(detachInfo.ChunkSize) }; } } @@ -408,15 +412,11 @@ public class BaseObjectSerializerV2 { chunks.Add(crtChunk); } - - return PreserializeObject( - chunks, - ref count, - inheritedDetachInfo: new PropertyAttributeInfo(true, false, 0, null) - ); + SerializeProperty(chunks, jsonWriter, inheritedDetachInfo: new PropertyAttributeInfo(true, false, 0, null)); + return; } - return PreserializeObject(baseValue, ref count, inheritedDetachInfo: detachInfo); + SerializeProperty(baseValue, jsonWriter, inheritedDetachInfo: detachInfo); } private void UpdateParentClosures(string objectId) @@ -433,30 +433,12 @@ public class BaseObjectSerializerV2 } } - [Pure] - private static string ComputeId(IReadOnlyDictionary obj) - { - string serialized = JsonConvert.SerializeObject(obj); - string hash = Crypt.Sha256(serialized, length: HashUtility.HASH_LENGTH); - return hash; - } - - private static string Dict2Json(IReadOnlyDictionary? obj) - { - if (obj is null) - { - return string.Empty; - } - string serialized = JsonConvert.SerializeObject(obj); - return serialized; - } - - private void StoreObject(string objectId, string objectJson) + private void StoreObject(Id objectId, Json objectJson) { _stopwatch.Stop(); foreach (var transport in WriteTransports) { - transport.SaveObject(objectId, objectJson); + transport.SaveObject(objectId.Value, objectJson.Value); } _stopwatch.Start(); @@ -491,7 +473,12 @@ public class BaseObjectSerializerV2 { Type type = baseObj.GetType(); - if (_typedPropertiesCache.TryGetValue(type.FullName, out List<(PropertyInfo, PropertyAttributeInfo)>? cached)) + if ( + _typedPropertiesCache.TryGetValue( + type.FullName.NotNull(), + out List<(PropertyInfo, PropertyAttributeInfo)>? cached + ) + ) { return cached; } @@ -514,8 +501,12 @@ public class BaseObjectSerializerV2 _ = typedProperty.GetValue(baseObj); - List detachableAttributes = typedProperty.GetCustomAttributes(true).ToList(); - List chunkableAttributes = typedProperty.GetCustomAttributes(true).ToList(); + List detachableAttributes = typedProperty + .GetCustomAttributes(true) + .ToList(); + List chunkableAttributes = typedProperty + .GetCustomAttributes(true) + .ToList(); bool isDetachable = detachableAttributes.Count > 0 && detachableAttributes[0].Detachable; bool isChunkable = chunkableAttributes.Count > 0; int chunkSize = isChunkable ? chunkableAttributes[0].MaxObjCountPerChunk : 1000; @@ -527,7 +518,7 @@ public class BaseObjectSerializerV2 return ret; } - public readonly struct PropertyAttributeInfo + internal readonly struct PropertyAttributeInfo { public PropertyAttributeInfo( bool isDetachable, diff --git a/src/Speckle.Sdk/Serialisation/SpeckleObjectSerializerPool.cs b/src/Speckle.Sdk/Serialisation/SpeckleObjectSerializerPool.cs new file mode 100644 index 00000000..cedba016 --- /dev/null +++ b/src/Speckle.Sdk/Serialisation/SpeckleObjectSerializerPool.cs @@ -0,0 +1,25 @@ +using System.Buffers; +using Speckle.Newtonsoft.Json; +using Speckle.Sdk.Common; + +namespace Speckle.Sdk.Serialisation; + +public class SpeckleObjectSerializerPool +{ + public static readonly SpeckleObjectSerializerPool Instance = new(); + + private SpeckleObjectSerializerPool() { } + + public JsonTextWriter GetJsonTextWriter(TextWriter writer) => new(writer) { ArrayPool = _charPool }; + + public JsonTextReader GetJsonTextReader(TextReader reader) => new(reader) { ArrayPool = _charPool }; + + private readonly SerializerPool _charPool = new(ArrayPool.Create()); //use default values + + private class SerializerPool(ArrayPool pool) : IArrayPool + { + public T[] Rent(int minimumLength) => pool.Rent(minimumLength); + + public void Return(T[]? array) => pool.Return(array.NotNull()); + } +} diff --git a/src/Speckle.Sdk/Serialisation/SpeckleSerializerException.cs b/src/Speckle.Sdk/Serialisation/SpeckleSerializerException.cs index 68704da2..282f5b74 100644 --- a/src/Speckle.Sdk/Serialisation/SpeckleSerializerException.cs +++ b/src/Speckle.Sdk/Serialisation/SpeckleSerializerException.cs @@ -2,9 +2,11 @@ public class SpeckleSerializeException : SpeckleException { - public SpeckleSerializeException(string message, Exception? inner = null) + public SpeckleSerializeException(string? message, Exception? inner = null) : base(message, inner) { } - public SpeckleSerializeException(string message) + public SpeckleSerializeException(string? message) : base(message) { } + + public SpeckleSerializeException() { } } diff --git a/src/Speckle.Sdk/Serialisation/Utilities/CallsiteCache.cs b/src/Speckle.Sdk/Serialisation/Utilities/CallsiteCache.cs index a6b31778..6cf4432e 100644 --- a/src/Speckle.Sdk/Serialisation/Utilities/CallsiteCache.cs +++ b/src/Speckle.Sdk/Serialisation/Utilities/CallsiteCache.cs @@ -28,7 +28,7 @@ internal static class CallSiteCache new List { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), - CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), } ); return CallSite>.Create(binder); diff --git a/src/Speckle.Sdk/Serialisation/Utilities/ClosureParser.cs b/src/Speckle.Sdk/Serialisation/Utilities/ClosureParser.cs index a586f96a..3d33abcb 100644 --- a/src/Speckle.Sdk/Serialisation/Utilities/ClosureParser.cs +++ b/src/Speckle.Sdk/Serialisation/Utilities/ClosureParser.cs @@ -5,11 +5,11 @@ namespace Speckle.Sdk.Serialisation.Utilities; public static class ClosureParser { - public static IReadOnlyList<(string, int)> GetClosures(string rootObjectJson) + public static IReadOnlyList<(string, int)> GetClosures(string json, CancellationToken cancellationToken) { try { - using JsonTextReader reader = new(new StringReader(rootObjectJson)); + using JsonTextReader reader = SpeckleObjectSerializerPool.Instance.GetJsonTextReader(new StringReader(json)); reader.Read(); while (reader.TokenType != JsonToken.EndObject) { @@ -17,13 +17,8 @@ public static class ClosureParser { case JsonToken.StartObject: { - var closureList = ReadObject(reader); - if (closureList?.Any() ?? false) - { - closureList.Sort((a, b) => b.Item2.CompareTo(a.Item2)); - return closureList; - } - return Array.Empty<(string, int)>(); + var closureList = ReadObject(reader, cancellationToken); + return closureList; } default: reader.Read(); @@ -33,14 +28,18 @@ public static class ClosureParser } } catch (Exception ex) when (!ex.IsFatal()) { } - return Array.Empty<(string, int)>(); + return []; } - private static List<(string, int)>? ReadObject(JsonTextReader reader) + public static IEnumerable GetChildrenIds(string json, CancellationToken cancellationToken) => + GetClosures(json, cancellationToken).Select(x => x.Item1); + + private static IReadOnlyList<(string, int)> ReadObject(JsonTextReader reader, CancellationToken cancellationToken) { reader.Read(); while (reader.TokenType != JsonToken.EndObject) { + cancellationToken.ThrowIfCancellationRequested(); switch (reader.TokenType) { case JsonToken.PropertyName: @@ -48,7 +47,7 @@ public static class ClosureParser if (reader.Value as string == "__closure") { reader.Read(); //goes to prop vale - var closureList = ReadClosureList(reader); + var closureList = ReadClosureEnumerable(reader, cancellationToken); return closureList; } reader.Read(); //goes to prop vale @@ -63,21 +62,33 @@ public static class ClosureParser break; } } - return null; + return []; } - private static List<(string, int)> ReadClosureList(JsonTextReader reader) + public static IReadOnlyList<(string, int)> GetClosures(JsonReader reader, CancellationToken cancellationToken) + { + if (reader.TokenType != JsonToken.StartObject) + { + return Array.Empty<(string, int)>(); + } + + var closureList = ReadClosureEnumerable(reader, cancellationToken); + closureList.Sort((a, b) => b.Item2.CompareTo(a.Item2)); + return closureList; + } + + private static List<(string, int)> ReadClosureEnumerable(JsonReader reader, CancellationToken cancellationToken) { List<(string, int)> closureList = new(); reader.Read(); //startobject while (reader.TokenType != JsonToken.EndObject) { + cancellationToken.ThrowIfCancellationRequested(); var childId = (reader.Value as string).NotNull(); // propertyName - int childMinDepth = reader.ReadAsInt32().NotNull(); //propertyValue + int childMinDepth = (reader.ReadAsInt32()).NotNull(); //propertyValue reader.Read(); closureList.Add((childId, childMinDepth)); } - return closureList; } } diff --git a/src/Speckle.Sdk/Serialisation/Utilities/DeserializationWorkerThreads.cs b/src/Speckle.Sdk/Serialisation/Utilities/DeserializationWorkerThreads.cs deleted file mode 100644 index 33684f50..00000000 --- a/src/Speckle.Sdk/Serialisation/Utilities/DeserializationWorkerThreads.cs +++ /dev/null @@ -1,109 +0,0 @@ -using Speckle.Sdk.Common; - -namespace Speckle.Sdk.Serialisation.Utilities; - -internal enum WorkerThreadTaskType -{ - NoOp = default, - Deserialize, -} - -internal sealed class DeserializationWorkerThreads : ParallelOperationExecutor -{ - private int _freeThreadCount; - - private readonly object _lockFreeThreads = new(); - private readonly BaseObjectDeserializerV2 _serializer; - - public DeserializationWorkerThreads(BaseObjectDeserializerV2 serializer, int threadCount) - { - _serializer = serializer; - NumThreads = threadCount; - } - - public override void Dispose() - { - lock (_lockFreeThreads) - { - _freeThreadCount -= NumThreads; - } - - base.Dispose(); - } - - protected override void ThreadMain() - { - while (true) - { - lock (_lockFreeThreads) - { - _freeThreadCount++; - } - - var (taskType, inputValue, tcs) = Tasks.Take(); - if (taskType == WorkerThreadTaskType.NoOp || tcs == null) - { - return; - } - - try - { - (string objectJson, long? current, long? total) = ((string, long?, long?))inputValue.NotNull(); - var result = RunOperation(taskType, objectJson, current, total, _serializer); - tcs.SetResult(result); - } - catch (Exception ex) - { - tcs.SetException(ex); - - if (ex.IsFatal()) - { - throw; - } - } - } - } - - private static object? RunOperation( - WorkerThreadTaskType taskType, - string objectJson, - long? current, - long? total, - BaseObjectDeserializerV2 serializer - ) - { - switch (taskType) - { - case WorkerThreadTaskType.Deserialize: - var converted = serializer.DeserializeTransportObject(objectJson, current, total); - return converted; - default: - throw new ArgumentException( - $"No implementation for {nameof(WorkerThreadTaskType)} with value {taskType}", - nameof(taskType) - ); - } - } - - internal Task? TryStartTask(WorkerThreadTaskType taskType, string? objectJson, long? current, long? total) - { - bool canStartTask = false; - lock (_lockFreeThreads) - { - if (_freeThreadCount > 0) - { - canStartTask = true; - _freeThreadCount--; - } - } - - if (!canStartTask) - { - return null; - } - - TaskCompletionSource tcs = new(TaskCreationOptions.RunContinuationsAsynchronously); - Tasks.Add(new(taskType, (objectJson, current, total), tcs)); - return tcs.Task; - } -} diff --git a/src/Speckle.Sdk/Serialisation/Utilities/ParallelOperationExecutor.cs b/src/Speckle.Sdk/Serialisation/Utilities/ParallelOperationExecutor.cs index 0598e937..eaf94c8c 100644 --- a/src/Speckle.Sdk/Serialisation/Utilities/ParallelOperationExecutor.cs +++ b/src/Speckle.Sdk/Serialisation/Utilities/ParallelOperationExecutor.cs @@ -12,6 +12,8 @@ internal abstract class ParallelOperationExecutor : IDisposable public int NumThreads { get; protected set; } public bool HasStarted => Threads.Count > 0; + protected volatile bool IsDisposed; + protected abstract void ThreadMain(); protected virtual void Stop() @@ -56,6 +58,7 @@ internal abstract class ParallelOperationExecutor : IDisposable Stop(); } + IsDisposed = true; Tasks.Dispose(); } } diff --git a/src/Speckle.Sdk/Serialisation/Utilities/ReferenceGenerator.cs b/src/Speckle.Sdk/Serialisation/Utilities/ReferenceGenerator.cs new file mode 100644 index 00000000..03b9ecc2 --- /dev/null +++ b/src/Speckle.Sdk/Serialisation/Utilities/ReferenceGenerator.cs @@ -0,0 +1,9 @@ +namespace Speckle.Sdk.Serialisation.Utilities; + +public static class ReferenceGenerator +{ + private const string REFERENCE_JSON_START = "{\"speckle_type\":\"reference\",\"referencedId\":\""; + private const string REFERENCE_JSON_END = "\",\"__closure\":null}"; //TODO: remove null but ID calculation changes + + public static Json CreateReference(Id id) => new(REFERENCE_JSON_START + id.Value + REFERENCE_JSON_END); +} diff --git a/src/Speckle.Sdk/Serialisation/Utilities/SqlitePaths.cs b/src/Speckle.Sdk/Serialisation/Utilities/SqlitePaths.cs new file mode 100644 index 00000000..3d0b0f37 --- /dev/null +++ b/src/Speckle.Sdk/Serialisation/Utilities/SqlitePaths.cs @@ -0,0 +1,30 @@ +using Speckle.Sdk.Logging; +using Speckle.Sdk.Transports; + +namespace Speckle.Sdk.Serialisation.Utilities; + +public static class SqlitePaths +{ + private const string APPLICATION_NAME = "Speckle"; + private const string DATA_FOLDER = "Projects"; + private static readonly string basePath = SpecklePathProvider.UserApplicationDataPath(); + + public static string BlobStorageFolder => + SpecklePathProvider.BlobStoragePath(Path.Combine(basePath, APPLICATION_NAME)); + + public static string GetDBPath(string streamId) + { + var dir = Path.Combine(basePath, APPLICATION_NAME, DATA_FOLDER); + var db = Path.Combine(dir, $"{streamId}.db"); + try + { + Directory.CreateDirectory(dir); //ensure dir is there + return db; + } + catch (Exception ex) + when (ex is ArgumentException or IOException or UnauthorizedAccessException or NotSupportedException) + { + throw new TransportException($"Path was invalid or could not be created {db}", ex); + } + } +} diff --git a/src/Speckle.Sdk/Serialisation/Utilities/ValueConverter.cs b/src/Speckle.Sdk/Serialisation/Utilities/ValueConverter.cs index 53692ddf..aca99be6 100644 --- a/src/Speckle.Sdk/Serialisation/Utilities/ValueConverter.cs +++ b/src/Speckle.Sdk/Serialisation/Utilities/ValueConverter.cs @@ -1,17 +1,22 @@ -using System.Buffers; using System.Collections; +using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Drawing; using System.Globalization; using Speckle.DoubleNumerics; -using Speckle.Sdk.Logging; +using Speckle.Sdk.Common; namespace Speckle.Sdk.Serialisation.Utilities; internal static class ValueConverter { - private static object[] _singleValue = new object[1]; + private static readonly object[] s_singleValue = new object[1]; + [SuppressMessage( + "Maintainability", + "CA1502:Avoid excessive complexity", + Justification = "To fix this requires rewrite of serializaiton" + )] public static bool ConvertValue(Type type, object? value, out object? convertedValue) { // TODO: Document list of supported values in the SDK. (and grow it as needed) @@ -169,9 +174,9 @@ internal static class ValueConverter var targetType = typeof(List<>).MakeGenericType(type.GenericTypeArguments); Type listElementType = type.GenericTypeArguments[0]; - _singleValue[0] = valueList.Count; + s_singleValue[0] = valueList.Count; //reuse array to avoid params array allocation - IList ret = (IList)Activator.CreateInstance(targetType, _singleValue); + IList ret = (IList)Activator.CreateInstance(targetType, s_singleValue).NotNull(); foreach (object inputListElement in valueList) { @@ -200,7 +205,7 @@ internal static class ValueConverter } Type dictValueType = type.GenericTypeArguments[1]; - IDictionary ret = (IDictionary)Activator.CreateInstance(type); + IDictionary ret = (IDictionary)Activator.CreateInstance(type).NotNull(); foreach (KeyValuePair kv in valueDict) { @@ -261,33 +266,9 @@ internal static class ValueConverter } #region BACKWARDS COMPATIBILITY: matrix4x4 changed from System.Numerics float to System.DoubleNumerics double in release 2.16 - if (type == typeof(System.Numerics.Matrix4x4) && value is IReadOnlyList lMatrix) + if (type == typeof(System.Numerics.Matrix4x4) && value is IReadOnlyList) { - SpeckleLog.Logger.Warning( - "This kept for backwards compatibility, no one should be using {this}", - "ValueConverter deserialize to System.Numerics.Matrix4x4" - ); - convertedValue = new System.Numerics.Matrix4x4( - I(0), - I(1), - I(2), - I(3), - I(4), - I(5), - I(6), - I(7), - I(8), - I(9), - I(10), - I(11), - I(12), - I(13), - I(14), - I(15) - ); - return true; - - float I(int index) => Convert.ToSingle(lMatrix[index]); + throw new ArgumentException("Only Speckle.DoubleNumerics.Matrix4x4 is supported.", nameof(type)); } #endregion diff --git a/src/Speckle.Sdk/Serialisation/V2/AsyncExtensions.cs b/src/Speckle.Sdk/Serialisation/V2/AsyncExtensions.cs new file mode 100644 index 00000000..f2ae05f8 --- /dev/null +++ b/src/Speckle.Sdk/Serialisation/V2/AsyncExtensions.cs @@ -0,0 +1,14 @@ +namespace Speckle.Sdk.Serialisation.V2; + +public static class AsyncExtensions +{ + public static async ValueTask FirstAsync(this IAsyncEnumerable source) + { + var e = source.GetAsyncEnumerator(); + if (await e.MoveNextAsync().ConfigureAwait(false)) + { + return e.Current; + } + throw new InvalidOperationException("Sequence contains no elements"); + } +} diff --git a/src/Speckle.Sdk/Serialisation/V2/DummySendServerObjectManager.cs b/src/Speckle.Sdk/Serialisation/V2/DummySendServerObjectManager.cs new file mode 100644 index 00000000..9d1c096a --- /dev/null +++ b/src/Speckle.Sdk/Serialisation/V2/DummySendServerObjectManager.cs @@ -0,0 +1,68 @@ +using System.Text; +using Speckle.Sdk.Serialisation.V2.Send; +using Speckle.Sdk.SQLite; +using Speckle.Sdk.Transports; + +namespace Speckle.Sdk.Serialisation.V2; + +#pragma warning disable CA1063 +public class DummySqLiteJsonCacheManager : ISqLiteJsonCacheManager +#pragma warning restore CA1063 +{ +#pragma warning disable CA1816 +#pragma warning disable CA1063 + public void Dispose() { } +#pragma warning restore CA1063 +#pragma warning restore CA1816 + + public IReadOnlyCollection<(string, string)> GetAllObjects() => throw new NotImplementedException(); + + public void DeleteObject(string id) => throw new NotImplementedException(); + + public string? GetObject(string id) => null; + + public void SaveObject(string id, string json) => throw new NotImplementedException(); + + public void UpdateObject(string id, string json) => throw new NotImplementedException(); + + public virtual void SaveObjects(IEnumerable<(string id, string json)> items) => throw new NotImplementedException(); + + public bool HasObject(string objectId) => false; +} + +public class DummySendServerObjectManager : IServerObjectManager +{ + public IAsyncEnumerable<(string, string)> DownloadObjects( + IReadOnlyCollection objectIds, + IProgress? progress, + CancellationToken cancellationToken + ) => throw new NotImplementedException(); + + public Task DownloadSingleObject( + string objectId, + IProgress? progress, + CancellationToken cancellationToken + ) => throw new NotImplementedException(); + + public Task> HasObjects( + IReadOnlyCollection objectIds, + CancellationToken cancellationToken + ) => throw new NotImplementedException(); + + public Task UploadObjects( + IReadOnlyList objects, + bool compressPayloads, + IProgress? progress, + CancellationToken cancellationToken + ) + { + long totalBytes = 0; + foreach (var item in objects) + { + totalBytes += Encoding.Default.GetByteCount(item.Json.Value); + } + + progress?.Report(new(ProgressEvent.UploadBytes, totalBytes, totalBytes)); + return Task.CompletedTask; + } +} diff --git a/src/Speckle.Sdk/Serialisation/V2/PriorityScheduler.cs b/src/Speckle.Sdk/Serialisation/V2/PriorityScheduler.cs new file mode 100644 index 00000000..a1fd670a --- /dev/null +++ b/src/Speckle.Sdk/Serialisation/V2/PriorityScheduler.cs @@ -0,0 +1,103 @@ +using System.Collections.Concurrent; +using Microsoft.Extensions.Logging; + +namespace Speckle.Sdk.Serialisation.V2; + +public sealed class PriorityScheduler( + ILogger logger, + ThreadPriority priority, + int maximumConcurrencyLevel, + CancellationToken cancellationToken +) : TaskScheduler, IAsyncDisposable +{ + private readonly BlockingCollection _tasks = new(); + private Thread[]? _threads; + + public override int MaximumConcurrencyLevel => maximumConcurrencyLevel; + + protected override IEnumerable GetScheduledTasks() => _tasks; + + public async ValueTask DisposeAsync() + { + await WaitForCompletion().ConfigureAwait(false); + _tasks.Dispose(); + } + + public async ValueTask WaitForCompletion() + { + if (_tasks.IsCompleted && _threads is null) + { + return; + } + _tasks.CompleteAdding(); + while (_threads != null && _threads.Any(x => x.IsAlive)) + { + await Task.Delay(TimeSpan.FromMilliseconds(500)).ConfigureAwait(false); + } + _threads = null; + } + + protected override void QueueTask(Task task) + { + _tasks.Add(task); + + if (_threads != null) + { + return; + } + + _threads = new Thread[maximumConcurrencyLevel]; + for (int i = 0; i < _threads.Length; i++) + { + _threads[i] = new Thread(() => + { + try + { + while (true) + { + //we're done so leave + if (_tasks.IsCompleted || cancellationToken.IsCancellationRequested) + { + break; + } + var success = _tasks.TryTake(out var t, TimeSpan.FromSeconds(1)); + //no task and we're done so leave + if (success && _tasks.IsCompleted) + { + break; + } + //cancelled just leave + if (cancellationToken.IsCancellationRequested) + { + break; + } + //didn't get a task but just timed out so continue + if (!success) + { + continue; + } + TryExecuteTask(t ?? throw new InvalidOperationException("Task was null")); + } + } + catch (OperationCanceledException) + { + //cancelling so end thread + } +#pragma warning disable CA1031 + catch (Exception e) +#pragma warning restore CA1031 + { + logger.LogError(e, "{name} had an exception", Thread.CurrentThread.Name); + } + }) + { + Name = $"{priority}: {i}", + Priority = priority, + IsBackground = true, + }; + _threads[i].Start(); + } + } + + protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) => false; // we might not want to execute task that should schedule as high or low priority inline +} diff --git a/src/Speckle.Sdk/Serialisation/V2/Receive/BaseDeserializer.cs b/src/Speckle.Sdk/Serialisation/V2/Receive/BaseDeserializer.cs new file mode 100644 index 00000000..7948f8f6 --- /dev/null +++ b/src/Speckle.Sdk/Serialisation/V2/Receive/BaseDeserializer.cs @@ -0,0 +1,26 @@ +using System.Collections.Concurrent; +using Speckle.InterfaceGenerator; +using Speckle.Sdk.Models; + +namespace Speckle.Sdk.Serialisation.V2.Receive; + +[GenerateAutoInterface] +public class BaseDeserializer(IObjectDeserializerFactory objectDeserializerFactory) : IBaseDeserializer +{ + public Base Deserialise( + ConcurrentDictionary baseCache, + Id id, + Json json, + IReadOnlyCollection closures, + CancellationToken cancellationToken + ) + { + if (baseCache.TryGetValue(id, out var baseObject)) + { + return baseObject; + } + + var deserializer = objectDeserializerFactory.Create(id, closures, baseCache); + return deserializer.Deserialize(json, cancellationToken); + } +} diff --git a/src/Speckle.Sdk/Serialisation/V2/Receive/DeserializeProcess.cs b/src/Speckle.Sdk/Serialisation/V2/Receive/DeserializeProcess.cs new file mode 100644 index 00000000..e0e62922 --- /dev/null +++ b/src/Speckle.Sdk/Serialisation/V2/Receive/DeserializeProcess.cs @@ -0,0 +1,192 @@ +using System.Collections.Concurrent; +using Microsoft.Extensions.Logging; +using Speckle.InterfaceGenerator; +using Speckle.Sdk.Dependencies; +using Speckle.Sdk.Models; +using Speckle.Sdk.Serialisation.Utilities; +using Speckle.Sdk.SQLite; +using Speckle.Sdk.Transports; + +namespace Speckle.Sdk.Serialisation.V2.Receive; + +public record DeserializeProcessOptions( + bool SkipCache = false, + bool ThrowOnMissingReferences = true, + bool SkipInvalidConverts = false, + int? MaxParallelism = null, + bool SkipServer = false +); + +public partial interface IDeserializeProcess : IAsyncDisposable; + +[GenerateAutoInterface] +public sealed class DeserializeProcess( + IObjectLoader objectLoader, + IProgress? progress, + IBaseDeserializer baseDeserializer, + ILoggerFactory loggerFactory, + CancellationToken cancellationToken, + DeserializeProcessOptions? options = null +) : IDeserializeProcess +{ + public DeserializeProcess( + ISqLiteJsonCacheManager sqLiteJsonCacheManager, + IServerObjectManager serverObjectManager, + IProgress? progress, + IBaseDeserializer baseDeserializer, + ILoggerFactory loggerFactory, + CancellationToken cancellationToken, + DeserializeProcessOptions? options = null + ) + : +#pragma warning disable CA2000 + this( + new ObjectLoader(sqLiteJsonCacheManager, serverObjectManager, progress, cancellationToken), + progress, + baseDeserializer, + loggerFactory, + cancellationToken, + options + ) +#pragma warning restore CA2000 + { } + + private readonly PriorityScheduler _belowNormal = new( + loggerFactory.CreateLogger(), + ThreadPriority.BelowNormal, + Environment.ProcessorCount * 2, + cancellationToken + ); + + private readonly DeserializeProcessOptions _options = options ?? new(); + + private readonly ConcurrentDictionary)> _closures = new(); + private readonly ConcurrentDictionary _baseCache = new(); + private readonly ConcurrentDictionary _activeTasks = new(); + + public IReadOnlyDictionary BaseCache => _baseCache; + public long Total { get; private set; } + + [AutoInterfaceIgnore] + public async ValueTask DisposeAsync() + { + objectLoader.Dispose(); + await _belowNormal.WaitForCompletion().ConfigureAwait(false); + await _belowNormal.DisposeAsync().ConfigureAwait(false); + } + + /// + /// All meaningful ids in the upcoming version + /// + private IReadOnlyCollection _allIds = []; + + public async Task Deserialize(string rootId) + { + var (rootJson, childrenIds) = await objectLoader.GetAndCache(rootId, _options).ConfigureAwait(false); + var root = new Id(rootId); + //childrenIds is already frozen but need to just add root? + _allIds = childrenIds.Concat([root]).Freeze(); + Total = childrenIds.Count; + Total++; + _closures.TryAdd(root, (rootJson, childrenIds)); + progress?.Report(new(ProgressEvent.DeserializeObject, _baseCache.Count, childrenIds.Count)); + await Traverse(root).ConfigureAwait(false); + await _belowNormal.WaitForCompletion().ConfigureAwait(false); + return _baseCache[root]; + } + + private async Task Traverse(Id id) + { + // It doesn't make sense to try traverse id if it is not in the root, if this is the case object is serialized wrong in the first place. + // This happened with datachunks that having weird __closures + if (!_allIds.Contains(id)) + { + return; + } + + if (_baseCache.ContainsKey(id)) + { + return; + } + var (_, childIds) = GetClosures(id); + var tasks = new List(); + foreach (var childId in childIds) + { + if (_baseCache.ContainsKey(childId)) + { + continue; + } + + if (_activeTasks.TryGetValue(childId, out var task)) + { + tasks.Add(task); + } + else + { + // tmp is necessary because of the way closures close over loop variables + var tmpId = childId; + cancellationToken.ThrowIfCancellationRequested(); + Task t = Task + .Factory.StartNew( + () => Traverse(tmpId), + cancellationToken, + TaskCreationOptions.AttachedToParent | TaskCreationOptions.PreferFairness, + _belowNormal + ) + .Unwrap(); + tasks.Add(t); + _activeTasks.TryAdd(childId, t); + } + } + + if (tasks.Count > 0) + { + await Task.WhenAll(tasks).ConfigureAwait(false); + } + + //don't redo things if the id is decoded already in the cache + if (!_baseCache.ContainsKey(id)) + { + DecodeOrEnqueueChildren(id); + progress?.Report(new(ProgressEvent.DeserializeObject, _baseCache.Count, Total)); + } + } + + private (Json, IReadOnlyCollection) GetClosures(Id id) + { + if (!_closures.TryGetValue(id, out var closures)) + { + var j = objectLoader.LoadId(id.Value); + + if (j == null) + { + throw new SpeckleException($"Missing object id in SQLite cache: {id}"); + } + + var json = new Json(j); + var childrenIds = ClosureParser + .GetClosures(json.Value, cancellationToken) + .OrderByDescending(x => x.Item2) + .Select(x => new Id(x.Item1)) + .Freeze(); + closures = (json, childrenIds); + _closures.TryAdd(id, closures); + } + + return closures; + } + + public void DecodeOrEnqueueChildren(Id id) + { + if (_baseCache.ContainsKey(id)) + { + return; + } + (Json json, IReadOnlyCollection closures) = GetClosures(id); + var @base = baseDeserializer.Deserialise(_baseCache, id, json, closures, cancellationToken); + _baseCache.TryAdd(id, @base); + //remove from JSON cache because we've finally made the Base + _closures.TryRemove(id, out _); + _activeTasks.TryRemove(id, out _); + } +} diff --git a/src/Speckle.Sdk/Serialisation/V2/Receive/DictionaryConverter.cs b/src/Speckle.Sdk/Serialisation/V2/Receive/DictionaryConverter.cs new file mode 100644 index 00000000..f1b1e07d --- /dev/null +++ b/src/Speckle.Sdk/Serialisation/V2/Receive/DictionaryConverter.cs @@ -0,0 +1,83 @@ +using System.Reflection; +using Speckle.Newtonsoft.Json; +using Speckle.Sdk.Common; +using Speckle.Sdk.Host; +using Speckle.Sdk.Models; +using Speckle.Sdk.Serialisation.Utilities; + +namespace Speckle.Sdk.Serialisation.V2.Receive; + +public static class DictionaryConverter +{ + /// + /// Property that describes the type of the object. + /// + public const string TYPE_DISCRIMINATOR = nameof(Base.speckle_type); + private static readonly object?[] s_invokeNull = [null]; + + public static string? BlobStorageFolder { get; set; } + + public static Base Dict2Base(Dictionary dictObj, bool skipInvalidConverts) + { + string typeName = (string)dictObj[TYPE_DISCRIMINATOR].NotNull(); + Type type = TypeLoader.GetType(typeName); + Base baseObj = (Base)Activator.CreateInstance(type).NotNull(); + + dictObj.Remove(TYPE_DISCRIMINATOR); + dictObj.Remove("__closure"); + + var staticProperties = TypeCache.GetTypeProperties(typeName); + foreach (var entry in dictObj) + { + if (staticProperties.TryGetValue(entry.Key, out PropertyInfo? value) && value.CanWrite) + { + if (entry.Value == null) + { + // Check for JsonProperty(NullValueHandling = NullValueHandling.Ignore) attribute + JsonPropertyAttribute? attr = TypeLoader.GetJsonPropertyAttribute(value); + if (attr is { NullValueHandling: NullValueHandling.Ignore }) + { + continue; + } + } + + Type targetValueType = value.PropertyType; + bool conversionOk = ValueConverter.ConvertValue( + targetValueType, + entry.Value, + skipInvalidConverts, + out object? convertedValue + ); + if (conversionOk) + { + value.SetValue(baseObj, convertedValue); + } + else + { + // Cannot convert the value in the json to the static property type + throw new SpeckleDeserializeException( + $"Cannot deserialize {entry.Value?.GetType().FullName} to {targetValueType.FullName}" + ); + } + } + else + { + // No writable property with this name + CallSiteCache.SetValue(entry.Key, baseObj, entry.Value); + } + } + + if (baseObj is Blob bb && BlobStorageFolder != null) + { + bb.filePath = bb.GetLocalDestinationPath(BlobStorageFolder); + } + + var onDeserializedCallbacks = TypeCache.GetOnDeserializedCallbacks(typeName); + foreach (MethodInfo onDeserialized in onDeserializedCallbacks) + { + onDeserialized.Invoke(baseObj, s_invokeNull); + } + + return baseObj; + } +} diff --git a/src/Speckle.Sdk/Serialisation/V2/Receive/ObjectDeserializer.cs b/src/Speckle.Sdk/Serialisation/V2/Receive/ObjectDeserializer.cs new file mode 100644 index 00000000..40266713 --- /dev/null +++ b/src/Speckle.Sdk/Serialisation/V2/Receive/ObjectDeserializer.cs @@ -0,0 +1,165 @@ +using System.Numerics; +using Speckle.InterfaceGenerator; +using Speckle.Newtonsoft.Json; +using Speckle.Sdk.Common; +using Speckle.Sdk.Dependencies; +using Speckle.Sdk.Models; + +namespace Speckle.Sdk.Serialisation.V2.Receive; + +[GenerateAutoInterface] +public sealed class ObjectDeserializer( + Id currentId, + IReadOnlyCollection currentClosures, + IReadOnlyDictionary references, + SpeckleObjectSerializerPool pool, + DeserializeProcessOptions? options = null +) : IObjectDeserializer +{ + /// The JSON string of the object to be deserialized + /// A typed object deserialized from the + /// was null + /// cannot be deserialised to type + // /// did not contain the required json objects (closures) + public Base Deserialize(Json objectJson, CancellationToken cancellationToken) + { + // Apparently this automatically parses DateTimes in strings if it matches the format: + // JObject doc1 = JObject.Parse(objectJson); + + // This is equivalent code that doesn't parse datetimes: + using var stringReader = new StringReader(objectJson.Value); + using JsonTextReader reader = pool.GetJsonTextReader(stringReader); + + reader.DateParseHandling = DateParseHandling.None; + + Base? converted; + try + { + reader.Read(); + converted = (Base)ReadObject(reader, cancellationToken).NotNull(); + } + catch (Exception ex) when (!ex.IsFatal() && ex is not OperationCanceledException) + { + throw new SpeckleDeserializeException("Failed to deserialize", ex); + } + + return converted; + } + + private List ReadArrayAsync(JsonReader reader, CancellationToken cancellationToken) + { + reader.Read(); + List retList = new(); + while (reader.TokenType != JsonToken.EndArray) + { + object? convertedValue = ReadProperty(reader, cancellationToken); + if (convertedValue is DataChunk chunk) + { + retList.AddRange(chunk.data); + } + else + { + retList.Add(convertedValue); + } + reader.Read(); //goes to next + } + return retList; + } + + private object? ReadObject(JsonReader reader, CancellationToken cancellationToken) + { + reader.Read(); + Dictionary dict = Pools.ObjectDictionaries.Get(); + while (reader.TokenType != JsonToken.EndObject) + { + switch (reader.TokenType) + { + case JsonToken.PropertyName: + { + var propName = reader.Value.NotNull().ToString().NotNull(); + reader.Read(); //goes prop value + object? convertedValue = ReadProperty(reader, cancellationToken); + dict[propName] = convertedValue; + reader.Read(); //goes to next + } + break; + default: + throw new InvalidOperationException($"Unknown {reader.ValueType} with {reader.Value}"); + } + } + + if (!dict.TryGetValue(DictionaryConverter.TYPE_DISCRIMINATOR, out object? speckleType)) + { + return dict; + } + + if (speckleType as string == "reference" && dict.TryGetValue("referencedId", out object? referencedId)) + { + var objId = new Id((string)referencedId.NotNull()); + cancellationToken.ThrowIfCancellationRequested(); + if (!currentClosures.Contains(objId) && (options is null || options.ThrowOnMissingReferences)) + { + throw new InvalidOperationException($"current Id: {currentId} has missing closure: {objId}"); + } + + if (references.TryGetValue(objId, out Base? closure)) + { + return closure; + } + + if (options is null || options.ThrowOnMissingReferences) + { + throw new InvalidOperationException($"current Id: {currentId} has missing reference: {objId}"); + } + //since we don't throw on missing references, return null + return null; + } + + var b = DictionaryConverter.Dict2Base(dict, options?.SkipInvalidConverts ?? false); + Pools.ObjectDictionaries.Return(dict); + return b; + } + + private object? ReadProperty(JsonReader reader, CancellationToken cancellationToken) + { + switch (reader.TokenType) + { + case JsonToken.Undefined: + case JsonToken.Null: + case JsonToken.None: + return null; + case JsonToken.Boolean: + return (bool)reader.Value.NotNull(); + case JsonToken.Integer: + if (reader.Value is long longValue) + { + return longValue; + } + if (reader.Value is BigInteger bitInt) + { + // This is behaviour carried over from v2 to facilitate large numbers from Python + // This is quite hacky, as it's a bit questionable exactly what numbers are supported, and with what tolerance + // For this reason, this can be considered undocumented behaviour, and is only for values within the range of a 64bit integer. + return (double)bitInt; + } + + throw new ArgumentException( + $"Found an unsupported integer type {reader.Value?.GetType()} with value {reader.Value}" + ); + case JsonToken.Float: + return (double)reader.Value.NotNull(); + case JsonToken.String: + return (string?)reader.Value.NotNull(); + case JsonToken.Date: + return (DateTime)reader.Value.NotNull(); + case JsonToken.StartArray: + return ReadArrayAsync(reader, cancellationToken); + case JsonToken.StartObject: + var dict = ReadObject(reader, cancellationToken); + return dict; + + default: + throw new ArgumentException("Json value not supported: " + reader.ValueType); + } + } +} diff --git a/src/Speckle.Sdk/Serialisation/V2/Receive/ObjectDeserializerFactory.cs b/src/Speckle.Sdk/Serialisation/V2/Receive/ObjectDeserializerFactory.cs new file mode 100644 index 00000000..68e2fd7a --- /dev/null +++ b/src/Speckle.Sdk/Serialisation/V2/Receive/ObjectDeserializerFactory.cs @@ -0,0 +1,15 @@ +using Speckle.InterfaceGenerator; +using Speckle.Sdk.Models; + +namespace Speckle.Sdk.Serialisation.V2.Receive; + +[GenerateAutoInterface] +public class ObjectDeserializerFactory : IObjectDeserializerFactory +{ + public IObjectDeserializer Create( + Id currentId, + IReadOnlyCollection currentClosures, + IReadOnlyDictionary references, + DeserializeProcessOptions? options = null + ) => new ObjectDeserializer(currentId, currentClosures, references, SpeckleObjectSerializerPool.Instance, options); +} diff --git a/src/Speckle.Sdk/Serialisation/V2/Receive/ObjectLoader.cs b/src/Speckle.Sdk/Serialisation/V2/Receive/ObjectLoader.cs new file mode 100644 index 00000000..7f0280f3 --- /dev/null +++ b/src/Speckle.Sdk/Serialisation/V2/Receive/ObjectLoader.cs @@ -0,0 +1,152 @@ +using Speckle.InterfaceGenerator; +using Speckle.Sdk.Common; +using Speckle.Sdk.Dependencies; +using Speckle.Sdk.Dependencies.Serialization; +using Speckle.Sdk.Serialisation.Utilities; +using Speckle.Sdk.Serialisation.V2.Send; +using Speckle.Sdk.SQLite; +using Speckle.Sdk.Transports; + +namespace Speckle.Sdk.Serialisation.V2.Receive; + +public partial interface IObjectLoader : IDisposable; + +[GenerateAutoInterface] +public sealed class ObjectLoader( + ISqLiteJsonCacheManager sqLiteJsonCacheManager, + IServerObjectManager serverObjectManager, + IProgress? progress, + CancellationToken cancellationToken +#pragma warning disable CS9107 // Parameter is captured into the state of the enclosing type and its value is also passed to the base constructor. The value might be captured by the base class as well. +) : ChannelLoader(cancellationToken), IObjectLoader +#pragma warning restore CS9107 // Parameter is captured into the state of the enclosing type and its value is also passed to the base constructor. The value might be captured by the base class as well. +{ + private int? _allChildrenCount; + private long _checkCache; + private long _cached; + private long _downloaded; + private long _totalToDownload; + private DeserializeProcessOptions _options = new(); + + [AutoInterfaceIgnore] + public void Dispose() => sqLiteJsonCacheManager.Dispose(); + + public async Task<(Json, IReadOnlyCollection)> GetAndCache(string rootId, DeserializeProcessOptions options) + { + try + { + _options = options; + string? rootJson; + if (!options.SkipCache) + { + rootJson = sqLiteJsonCacheManager.GetObject(rootId); + if (rootJson != null) + { + //assume everything exists as the root is there. + var allChildren = ClosureParser.GetChildrenIds(rootJson, cancellationToken).Select(x => new Id(x)).ToList(); + //this probably yields away from the Main thread to let host apps update progress + //in any case, this fixes a Revit only issue for this situation + await Task.Yield(); + return (new(rootJson), allChildren); + } + } + + if (!options.SkipServer) + { + rootJson = await serverObjectManager + .DownloadSingleObject(rootId, progress, cancellationToken) + .NotNull() + .ConfigureAwait(false); + IReadOnlyCollection allChildrenIds = ClosureParser + .GetClosures(rootJson, cancellationToken) + .OrderByDescending(x => x.Item2) + .Select(x => new Id(x.Item1)) + .Where(x => !x.Value.StartsWith("blob", StringComparison.Ordinal)) + .Freeze(); + _allChildrenCount = allChildrenIds.Count; + ThrowIfFailed(); + await GetAndCache(allChildrenIds.Select(x => x.Value), _options.MaxParallelism).ConfigureAwait(false); + ThrowIfFailed(); + //save the root last to shortcut later + if (!options.SkipCache) + { + sqLiteJsonCacheManager.SaveObject(rootId, rootJson); + } + ThrowIfFailed(); + return (new(rootJson), allChildrenIds); + } + } + catch (TaskCanceledException) + { + ThrowIfFailed(); + throw; + } + + throw new SpeckleException("Cannot skip server and cache. Please choose one."); + } + + [AutoInterfaceIgnore] + public override string? CheckCache(string id) + { + _checkCache++; + progress?.Report(new(ProgressEvent.CacheCheck, _checkCache, _allChildrenCount)); + if (!_options.SkipCache && !sqLiteJsonCacheManager.HasObject(id)) + { + Interlocked.Increment(ref _totalToDownload); + return id; + } + + return null; + } + + [AutoInterfaceIgnore] + protected override async Task> DownloadInternal(List ids) + { + var toCache = new List(); + await foreach ( + var (id, json) in serverObjectManager.DownloadObjects( + ids.Select(x => x.NotNull()).ToList(), + progress, + cancellationToken + ) + ) + { + cancellationToken.ThrowIfCancellationRequested(); + Interlocked.Increment(ref _downloaded); + progress?.Report(new(ProgressEvent.DownloadObjects, _downloaded, _totalToDownload)); + toCache.Add(new(new(id), new(json), true, null)); + } + + if (toCache.Count != ids.Count) + { + throw new SpeckleException( + $"Objects in batch missing: {string.Join(",", ids.Except(toCache.Select(y => y.Id.Value)).Take(10))}" + ); + } + return toCache; + } + + [AutoInterfaceIgnore] + protected override void SaveToCacheInternal(List batch) + { + if (!_options.SkipCache) + { + cancellationToken.ThrowIfCancellationRequested(); + sqLiteJsonCacheManager.SaveObjects(batch.Select(x => (x.Id.Value, x.Json.Value))); + Interlocked.Exchange(ref _cached, _cached + batch.Count); + progress?.Report(new(ProgressEvent.CachedToLocal, _cached, _allChildrenCount)); + } + } + + public string? LoadId(string id) => sqLiteJsonCacheManager.GetObject(id); + + private void ThrowIfFailed() + { + //always check for cancellation first + cancellationToken.ThrowIfCancellationRequested(); + if (Exception is not null) + { + throw new SpeckleException("Error while sending", Exception); + } + } +} diff --git a/src/Speckle.Sdk/Serialisation/V2/Receive/ValueConverter.cs b/src/Speckle.Sdk/Serialisation/V2/Receive/ValueConverter.cs new file mode 100644 index 00000000..3269b2bc --- /dev/null +++ b/src/Speckle.Sdk/Serialisation/V2/Receive/ValueConverter.cs @@ -0,0 +1,330 @@ +using System.Collections; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Contracts; +using System.Drawing; +using System.Globalization; +using Speckle.DoubleNumerics; +using Speckle.Sdk.Common; + +namespace Speckle.Sdk.Serialisation.V2.Receive; + +internal static class ValueConverter +{ + private static readonly object[] s_singleValue = new object[1]; + + [SuppressMessage( + "Maintainability", + "CA1502:Avoid excessive complexity", + Justification = "To fix this requires rewrite of serializaiton" + )] + public static bool ConvertValue(Type type, object? value, bool skipInvalidConverts, out object? convertedValue) + { + // TODO: Document list of supported values in the SDK. (and grow it as needed) + + convertedValue = null; + if (value == null) + { + return true; + } + + Type valueType = value.GetType(); + + if (type.IsAssignableFrom(valueType)) + { + convertedValue = value; + return true; + } + + //strings + if (type == typeof(string)) + { + convertedValue = Convert.ToString(value); + return true; + } + + #region Enum + if (type.IsEnum) + { + if (valueType != typeof(long)) + { + return false; + } + + convertedValue = Enum.ToObject(type, (long)value); + return true; + } + #endregion + + switch (type.Name) + { + case "Nullable`1": + return ConvertValue(type.GenericTypeArguments[0], value, skipInvalidConverts, out convertedValue); + #region Numbers + case "Int64": + if (valueType == typeof(long)) + { + convertedValue = (long)value; + return true; + } + + return false; + case "Int32": + if (valueType == typeof(long)) + { + convertedValue = (int)(long)value; + return true; + } + + return false; + case "Int16": + if (valueType == typeof(long)) + { + convertedValue = (short)(long)value; + return true; + } + + return false; + case "UInt64": + if (valueType == typeof(long)) + { + convertedValue = (ulong)(long)value; + return true; + } + + return false; + case "UInt32": + if (valueType == typeof(long)) + { + convertedValue = (uint)(long)value; + return true; + } + + return false; + case "UInt16": + if (valueType == typeof(long)) + { + convertedValue = (ushort)(long)value; + return true; + } + + return false; + case "Double": + if (valueType == typeof(double)) + { + convertedValue = (double)value; + return true; + } + if (valueType == typeof(long)) + { + convertedValue = (double)(long)value; + return true; + } + switch (value) + { + case "NaN": + convertedValue = double.NaN; + return true; + case "Infinity": + convertedValue = double.PositiveInfinity; + return true; + case "-Infinity": + convertedValue = double.NegativeInfinity; + return true; + default: + return false; + } + + case "Single": + if (valueType == typeof(double)) + { + convertedValue = (float)(double)value; + return true; + } + if (valueType == typeof(long)) + { + convertedValue = (float)(long)value; + return true; + } + switch (value) + { + case "NaN": + convertedValue = float.NaN; + return true; + case "Infinity": + convertedValue = float.PositiveInfinity; + return true; + case "-Infinity": + convertedValue = float.NegativeInfinity; + return true; + default: + return false; + } + + #endregion + } + + // 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]; + + s_singleValue[0] = valueList.Count; + //reuse array to avoid params array allocation + IList ret = (IList)Activator.CreateInstance(targetType, s_singleValue).NotNull(); + + foreach (object inputListElement in valueList) + { + if (!ConvertValue(listElementType, inputListElement, skipInvalidConverts, out object? convertedListElement)) + { + if (skipInvalidConverts) + { + continue; + } + return false; + } + + ret.Add(convertedListElement); + } + convertedValue = ret; + return true; + } + + // Handle Dictionary + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>)) + { + if (value is not Dictionary valueDict) + { + return false; + } + + if (type.GenericTypeArguments[0] != typeof(string)) + { + throw new ArgumentException("Dictionaries with non-string keys are not supported", nameof(type)); + } + + Type dictValueType = type.GenericTypeArguments[1]; + IDictionary ret = (IDictionary)Activator.CreateInstance(type).NotNull(); + + foreach (KeyValuePair kv in valueDict) + { + if (!ConvertValue(dictValueType, kv.Value, skipInvalidConverts, out object? convertedDictValue)) + { + if (skipInvalidConverts) + { + continue; + } + return false; + } + + ret[kv.Key] = convertedDictValue; + } + convertedValue = ret; + return true; + } + + // Handle arrays + if (type.IsArray) + { + if (value is not List valueList) + { + return false; + } + + Type arrayElementType = + type.GetElementType() ?? throw new ArgumentException("IsArray yet not valid element type", nameof(type)); + + Array ret = Array.CreateInstance(arrayElementType, valueList.Count); + for (int i = 0; i < valueList.Count; i++) + { + object inputListElement = valueList[i]; + if (!ConvertValue(arrayElementType, inputListElement, skipInvalidConverts, out object? convertedListElement)) + { + if (skipInvalidConverts) + { + continue; + } + return false; + } + + ret.SetValue(convertedListElement, i); + } + convertedValue = ret; + return true; + } + + // Handle simple classes/structs + if (type == typeof(Guid) && value is string str) + { + convertedValue = Guid.Parse(str); + return true; + } + + if (type == typeof(Color) && value is long integer) + { + convertedValue = Color.FromArgb((int)integer); + return true; + } + + if (type == typeof(DateTime) && value is string s) + { + convertedValue = DateTime.ParseExact(s, "o", CultureInfo.InvariantCulture); + return true; + } + + #region BACKWARDS COMPATIBILITY: matrix4x4 changed from System.Numerics float to System.DoubleNumerics double in release 2.16 + if (type == typeof(System.Numerics.Matrix4x4) && value is IReadOnlyList) + { + throw new ArgumentException("Only Speckle.DoubleNumerics.Matrix4x4 is supported.", nameof(type)); + } + #endregion + + if (type == typeof(Matrix4x4) && value is IReadOnlyList l) + { + double I(int index) => Convert.ToDouble(l[index]); + convertedValue = new Matrix4x4( + I(0), + I(1), + I(2), + I(3), + I(4), + I(5), + I(6), + I(7), + I(8), + I(9), + I(10), + I(11), + I(12), + I(13), + I(14), + I(15) + ); + return true; + } + + 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.Sdk/Serialisation/V2/Send/BaseChildFinder.cs b/src/Speckle.Sdk/Serialisation/V2/Send/BaseChildFinder.cs new file mode 100644 index 00000000..e9e1b149 --- /dev/null +++ b/src/Speckle.Sdk/Serialisation/V2/Send/BaseChildFinder.cs @@ -0,0 +1,44 @@ +using System.Collections; +using Speckle.InterfaceGenerator; +using Speckle.Sdk.Models; + +namespace Speckle.Sdk.Serialisation.V2.Send; + +[GenerateAutoInterface] +public class BaseChildFinder(IBasePropertyGatherer propertyGatherer) : IBaseChildFinder +{ + public IEnumerable GetChildProperties(Base obj) => + propertyGatherer.ExtractAllProperties(obj).Where(x => x.PropertyAttributeInfo.IsDetachable); + + public IEnumerable GetChildren(Base obj) + { + var props = GetChildProperties(obj); + foreach (var kvp in props) + { + if (kvp.Value is Base child) + { + yield return child; + } + if (kvp.Value is ICollection c) + { + foreach (var childC in c) + { + if (childC is Base b) + { + yield return b; + } + } + } + if (kvp.Value is IDictionary d) + { + foreach (DictionaryEntry de in d) + { + if (de.Value is Base b) + { + yield return b; + } + } + } + } + } +} diff --git a/src/Speckle.Sdk/Serialisation/V2/Send/BaseItem.cs b/src/Speckle.Sdk/Serialisation/V2/Send/BaseItem.cs new file mode 100644 index 00000000..f5c39f4b --- /dev/null +++ b/src/Speckle.Sdk/Serialisation/V2/Send/BaseItem.cs @@ -0,0 +1,20 @@ +using System.Text; + +namespace Speckle.Sdk.Serialisation.V2.Send; + +public readonly record struct BaseItem(Id Id, Json Json, bool NeedsStorage, Dictionary? Closures) + : IHasByteSize +{ + public int ByteSize { get; } = Encoding.UTF8.GetByteCount(Json.Value); + + public bool Equals(BaseItem? other) + { + if (other is null) + { + return false; + } + return string.Equals(Id.Value, other.Value.Id.Value, StringComparison.OrdinalIgnoreCase); + } + + public override int GetHashCode() => Id.GetHashCode(); +} diff --git a/src/Speckle.Sdk/Serialisation/V2/Send/BasePropertyGatherer.cs b/src/Speckle.Sdk/Serialisation/V2/Send/BasePropertyGatherer.cs new file mode 100644 index 00000000..578615ee --- /dev/null +++ b/src/Speckle.Sdk/Serialisation/V2/Send/BasePropertyGatherer.cs @@ -0,0 +1,97 @@ +using System.Collections.Concurrent; +using System.Reflection; +using Speckle.InterfaceGenerator; +using Speckle.Newtonsoft.Json; +using Speckle.Sdk.Common; +using Speckle.Sdk.Helpers; +using Speckle.Sdk.Models; + +namespace Speckle.Sdk.Serialisation.V2.Send; + +public readonly record struct Property(string Name, object? Value, PropertyAttributeInfo PropertyAttributeInfo); + +[GenerateAutoInterface] +public class BasePropertyGatherer : IBasePropertyGatherer +{ + private readonly record struct TypeProperty(PropertyInfo PropertyInfo, PropertyAttributeInfo PropertyAttributeInfo); + + private readonly ConcurrentDictionary> _typedPropertiesCache = new(); + + public IEnumerable ExtractAllProperties(Base baseObj) + { + IReadOnlyList typedProperties = GetTypedPropertiesWithCache(baseObj); + IReadOnlyCollection dynamicProperties = baseObj.DynamicPropertyKeys; + + // Construct `allProperties`: Add typed properties + foreach ((PropertyInfo propertyInfo, PropertyAttributeInfo detachInfo) in typedProperties) + { + object? baseValue = propertyInfo.GetValue(baseObj); + yield return new(propertyInfo.Name, baseValue, detachInfo); + } + + // Construct `allProperties`: Add dynamic properties + foreach (string propName in dynamicProperties) + { + if (propName.StartsWith("__")) + { + continue; + } + + object? baseValue = baseObj[propName]; + + bool isDetachable = PropNameValidator.IsDetached(propName); + + int chunkSize = 1000; + bool isChunkable = isDetachable && PropNameValidator.IsChunkable(propName, out chunkSize); + + yield return new(propName, baseValue, new PropertyAttributeInfo(isDetachable, isChunkable, chunkSize, null)); + } + } + + // (propertyInfo, isDetachable, isChunkable, chunkSize, JsonPropertyAttribute) + private IReadOnlyList GetTypedPropertiesWithCache(Base baseObj) + { + Type type = baseObj.GetType(); + + if (_typedPropertiesCache.TryGetValue(type.FullName.NotNull(), out List? cached)) + { + return cached; + } + + var typedProperties = baseObj.GetInstanceMembers().ToList(); + List ret = new(typedProperties.Count); + + foreach (PropertyInfo typedProperty in typedProperties) + { + if (typedProperty.Name.StartsWith("__") || typedProperty.Name == "id") + { + continue; + } + + bool jsonIgnore = typedProperty.IsDefined(typeof(JsonIgnoreAttribute), false); + if (jsonIgnore) + { + continue; + } + + _ = typedProperty.GetValue(baseObj); + + List detachableAttributes = typedProperty + .GetCustomAttributes(true) + .ToList(); + List chunkableAttributes = typedProperty + .GetCustomAttributes(true) + .ToList(); + bool isDetachable = detachableAttributes.Count > 0 && detachableAttributes[0].Detachable; + bool isChunkable = chunkableAttributes.Count > 0; + int chunkSize = isChunkable ? chunkableAttributes[0].MaxObjCountPerChunk : 1000; + JsonPropertyAttribute? jsonPropertyAttribute = typedProperty.GetCustomAttribute(); + ret.Add( + new(typedProperty, new PropertyAttributeInfo(isDetachable, isChunkable, chunkSize, jsonPropertyAttribute)) + ); + } + + _typedPropertiesCache[type.FullName] = ret; + return ret; + } +} diff --git a/src/Speckle.Sdk/Serialisation/V2/Send/BaseSerializer.cs b/src/Speckle.Sdk/Serialisation/V2/Send/BaseSerializer.cs new file mode 100644 index 00000000..178519eb --- /dev/null +++ b/src/Speckle.Sdk/Serialisation/V2/Send/BaseSerializer.cs @@ -0,0 +1,76 @@ +using System.Collections.Concurrent; +using Speckle.InterfaceGenerator; +using Speckle.Sdk.Common; +using Speckle.Sdk.Dependencies; +using Speckle.Sdk.Models; +using Speckle.Sdk.SQLite; +using Closures = System.Collections.Generic.Dictionary; + +namespace Speckle.Sdk.Serialisation.V2.Send; + +[GenerateAutoInterface] +public class BaseSerializer( + ISqLiteJsonCacheManager sqLiteJsonCacheManager, + IObjectSerializerFactory objectSerializerFactory +) : IBaseSerializer +{ + private readonly Pool> _pool = Pools.CreateListPool<(Id, Json, Closures)>(); + + private readonly ConcurrentDictionary _objectReferences = new(); + + public IReadOnlyDictionary ObjectReferences => _objectReferences; + + //leave this sync + public IEnumerable Serialise( + Base obj, + IReadOnlyDictionary childInfo, + bool skipCacheRead, + CancellationToken cancellationToken + ) + { + if (!skipCacheRead && obj.id != null) + { + var cachedJson = sqLiteJsonCacheManager.GetObject(obj.id); + if (cachedJson != null) + { + yield return new BaseItem(new(obj.id.NotNull()), new(cachedJson), false, null); + yield break; + } + } + + using var serializer2 = objectSerializerFactory.Create(childInfo, cancellationToken); + var items = _pool.Get(); + try + { + items.AddRange(serializer2.Serialize(obj)); + foreach (var kvp in serializer2.ObjectReferences) + { + _objectReferences.TryAdd(kvp.Key, kvp.Value); + } + + var (id, json, closures) = items.First(); + yield return CheckCache(id, json, closures, skipCacheRead); + foreach (var (cid, cJson, cClosures) in items.Skip(1)) + { + yield return CheckCache(cid, cJson, cClosures, skipCacheRead); + } + } + finally + { + _pool.Return(items); + } + } + + private BaseItem CheckCache(Id id, Json json, Dictionary closures, bool skipCacheRead) + { + if (!skipCacheRead) + { + var cachedJson = sqLiteJsonCacheManager.GetObject(id.Value); + if (cachedJson != null) + { + return new BaseItem(id, new(cachedJson), false, null); + } + } + return new BaseItem(id, json, true, closures); + } +} diff --git a/src/Speckle.Sdk/Serialisation/V2/Send/EmptyDictionary.cs b/src/Speckle.Sdk/Serialisation/V2/Send/EmptyDictionary.cs new file mode 100644 index 00000000..7059231a --- /dev/null +++ b/src/Speckle.Sdk/Serialisation/V2/Send/EmptyDictionary.cs @@ -0,0 +1,47 @@ +using System.Collections; +using System.Diagnostics.CodeAnalysis; + +namespace Speckle.Sdk.Serialisation.V2.Send; + +public class EmptyDictionary : IDictionary +{ + public IEnumerator> GetEnumerator() => throw new NotImplementedException(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public void Add(KeyValuePair item) { } + + public void Clear() => throw new NotImplementedException(); + + public bool Contains(KeyValuePair item) => false; + + public void CopyTo(KeyValuePair[] array, int arrayIndex) => throw new NotImplementedException(); + + public bool Remove(KeyValuePair item) => false; + + public int Count => 0; + public bool IsReadOnly => false; + + public void Add(TKey key, TValue value) { } + + public bool ContainsKey(TKey key) => false; + + public bool Remove(TKey key) => false; + + public bool TryGetValue(TKey key, [UnscopedRef] out TValue value) + { + value = default!; + return false; + } + + public TValue this[TKey key] + { +#pragma warning disable CA1065 + get => throw new NotImplementedException(); +#pragma warning restore CA1065 + set { } + } + + public ICollection Keys { get; } + public ICollection Values { get; } +} diff --git a/src/Speckle.Sdk/Serialisation/V2/Send/ObjectSerializer.cs b/src/Speckle.Sdk/Serialisation/V2/Send/ObjectSerializer.cs new file mode 100644 index 00000000..5f85353a --- /dev/null +++ b/src/Speckle.Sdk/Serialisation/V2/Send/ObjectSerializer.cs @@ -0,0 +1,436 @@ +using System.Collections; +using System.Drawing; +using System.Globalization; +using Speckle.DoubleNumerics; +using Speckle.InterfaceGenerator; +using Speckle.Newtonsoft.Json; +using Speckle.Sdk.Common; +using Speckle.Sdk.Dependencies; +using Speckle.Sdk.Helpers; +using Speckle.Sdk.Models; +using Speckle.Sdk.Serialisation.Utilities; +using Closures = System.Collections.Generic.Dictionary; + +namespace Speckle.Sdk.Serialisation.V2.Send; + +public readonly record struct NodeInfo(Json Json, Closures? C) +{ + public Closures GetClosures(CancellationToken cancellationToken) => + C ?? ClosureParser.GetClosures(Json.Value, cancellationToken).ToDictionary(x => new Id(x.Item1), x => x.Item2); +} + +public partial interface IObjectSerializer : IDisposable; + +[GenerateAutoInterface] +public sealed class ObjectSerializer : IObjectSerializer +{ + private HashSet _parentObjects = new(); + private readonly Dictionary _currentClosures = new(); + + private readonly IReadOnlyDictionary _childCache; + + private readonly IBasePropertyGatherer _propertyGatherer; + private readonly CancellationToken _cancellationToken; + + /// + /// Keeps track of all detached children created during serialisation that have an applicationId (provided this serializer instance has been told to track detached children). + /// This is currently used to cache previously converted objects and avoid their conversion if they haven't changed. See the DUI3 send bindings in rhino or another host app. + /// + public Dictionary ObjectReferences { get; } = new(); + + private readonly List<(Id, Json, Closures)> _chunks; + private readonly Pool> _chunksPool; + + private readonly List> _chunks2 = new(); + private readonly Pool> _chunks2Pool; + + private readonly List> _chunks3 = new(); + private readonly Pool> _chunks3Pool; + + /// + /// Creates a new Serializer instance. + /// + /// + public ObjectSerializer( + IBasePropertyGatherer propertyGatherer, + IReadOnlyDictionary childCache, + Pool> chunksPool, + Pool> chunks2Pool, + Pool> chunks3Pool, + CancellationToken cancellationToken + ) + { + _propertyGatherer = propertyGatherer; + _childCache = childCache; + _chunksPool = chunksPool; + _chunks2Pool = chunks2Pool; + _chunks3Pool = chunks3Pool; + _cancellationToken = cancellationToken; + _chunks = chunksPool.Get(); + } + + [AutoInterfaceIgnore] + public void Dispose() + { + _chunksPool.Return(_chunks); + foreach (var c2 in _chunks2) + { + _chunks2Pool.Return(c2); + } + foreach (var c3 in _chunks3) + { + _chunks3Pool.Return(c3); + } + } + + /// The object to serialize + /// The serialized JSON + /// The serializer is busy (already serializing an object) + /// Failed to extract (pre-serialize) properties from the + public IEnumerable<(Id, Json, Closures)> Serialize(Base baseObj) + { + try + { + (Id, Json) item; + try + { + item = SerializeBase(baseObj, true, default).NotNull(); + } + catch (Exception ex) when (!ex.IsFatal() && ex is not OperationCanceledException) + { + throw new SpeckleSerializeException($"Failed to extract (pre-serialize) properties from the {baseObj}", ex); + } + yield return (item.Item1, item.Item2, _currentClosures); + foreach (var chunk in _chunks) + { + yield return chunk; + } + } + finally + { + _parentObjects = new HashSet(); + } + } + + // `Preserialize` means transforming all objects into the final form that will appear in json, with basic .net objects + // (primitives, lists and dictionaries with string keys) + private void SerializeProperty(object? obj, JsonWriter writer, PropertyAttributeInfo propertyAttributeInfo) + { + _cancellationToken.ThrowIfCancellationRequested(); + + if (obj == null) + { + writer.WriteNull(); + return; + } + + switch (obj) + { + case double d: + writer.WriteValue(d); + return; + case string d: + writer.WriteValue(d); + return; + case bool d: + writer.WriteValue(d); + return; + case int d: + writer.WriteValue(d); + return; + case long d: + writer.WriteValue(d); + return; + } + + if (obj.GetType().IsPrimitive || obj is string) + { + writer.WriteValue(obj); + return; + } + + switch (obj) + { + // Start with object references so they're not captured by the Base class case below + // Note: this change was needed as we've made the ObjectReference type inherit from Base for + // the purpose of the send object (connector/conversion level) caching. + case ObjectReference r: + Dictionary ret = new() + { + ["speckle_type"] = r.speckle_type, + ["referencedId"] = r.referencedId, + ["__closure"] = r.closure, + }; + //references can be externally provided and need to know the ids in the closure and reference here + //AddClosure can take the same value twice + foreach (var kvp in r.closure.Empty()) + { + AddClosure(new(kvp.Key)); + } + AddClosure(new(r.referencedId)); + SerializeProperty(ret, writer, default); + break; + case Base b: + var result = SerializeBase(b, false, propertyAttributeInfo); + if (result is not null) + { + writer.WriteRawValue(result.Value.Item2.Value); + } + else + { + writer.WriteNull(); + } + break; + case IDictionary d: + { + writer.WriteStartObject(); + + foreach (DictionaryEntry kvp in d) + { + if (kvp.Key is not string key) + { + throw new ArgumentException( + "Serializing dictionaries that are not string based keys is not supported", + nameof(obj) + ); + } + + writer.WritePropertyName(key); + SerializeProperty(kvp.Value, writer, propertyAttributeInfo); + } + writer.WriteEndObject(); + } + break; + case ICollection e: + { + writer.WriteStartArray(); + foreach (object? element in e) + { + SerializeProperty(element, writer, propertyAttributeInfo); + } + writer.WriteEndArray(); + } + break; + case Enum: + writer.WriteValue((int)obj); + break; + // Support for simple types + case Guid g: + writer.WriteValue(g.ToString()); + break; + case Color c: + writer.WriteValue(c.ToArgb()); + break; + case DateTime t: + writer.WriteValue(t.ToString("o", CultureInfo.InvariantCulture)); + break; + case Matrix4x4 md: + writer.WriteStartArray(); + + writer.WriteValue(md.M11); + writer.WriteValue(md.M12); + writer.WriteValue(md.M13); + writer.WriteValue(md.M14); + writer.WriteValue(md.M21); + writer.WriteValue(md.M22); + writer.WriteValue(md.M23); + writer.WriteValue(md.M24); + writer.WriteValue(md.M31); + writer.WriteValue(md.M32); + writer.WriteValue(md.M33); + writer.WriteValue(md.M34); + writer.WriteValue(md.M41); + writer.WriteValue(md.M42); + writer.WriteValue(md.M43); + writer.WriteValue(md.M44); + writer.WriteEndArray(); + break; + //BACKWARDS COMPATIBILITY: matrix4x4 changed from System.Numerics float to System.DoubleNumerics double in release 2.16 + case System.Numerics.Matrix4x4: + throw new ArgumentException("Please use Speckle.DoubleNumerics.Matrix4x4 instead", nameof(obj)); + default: + throw new ArgumentException($"Unsupported value in serialization: {obj.GetType()}", nameof(obj)); + } + } + + private (Id, Json)? SerializeBase(Base baseObj, bool isRoot, PropertyAttributeInfo inheritedDetachInfo) + { + // handle circular references + bool alreadySerialized = !_parentObjects.Add(baseObj); + if (alreadySerialized) + { + return null; + } + + _parentObjects.Remove(baseObj); + + if (baseObj is Blob) + { + throw new NotSupportedException(); + /*StoreBlob(myBlob); + UpdateParentClosures($"blob:{id}"); + return new(json, id);*/ + } + + var isDataChunk = baseObj is DataChunk; + + if (inheritedDetachInfo.IsDetachable) + { + Closures childClosures; + Id id; + Json json; + //avoid multiple serialization to get closures + if (baseObj.id != null && _childCache.TryGetValue(new(baseObj.id), out var info)) + { + id = new Id(baseObj.id); + childClosures = info.GetClosures(_cancellationToken); + json = info.Json; + MergeClosures(_currentClosures, childClosures); + } + else + { + if (isDataChunk) //datachunks never have child closures + { + childClosures = []; + } + else + { + childClosures = isRoot || inheritedDetachInfo.IsDetachable ? _currentClosures : []; + } + var sb = Pools.StringBuilders.Get(); + using var writer = new StringWriter(sb); + using var jsonWriter = SpeckleObjectSerializerPool.Instance.GetJsonTextWriter(writer); + id = SerializeBaseObject(baseObj, jsonWriter, childClosures); + json = new Json(writer.ToString()); + Pools.StringBuilders.Return(sb); + } + var json2 = ReferenceGenerator.CreateReference(id); + AddClosure(id); + // add to obj refs to return + if (baseObj.applicationId != null) // && baseObj is not DataChunk && baseObj is not Abstract) // not needed, as data chunks will never have application ids, and abstract objs are not really used. + { + ObjectReferences[new(baseObj.applicationId)] = new ObjectReference() + { + referencedId = id.Value, + applicationId = baseObj.applicationId, + closure = childClosures.ToDictionary(x => x.Key.Value, x => x.Value), + }; + } + _chunks.Add(new(id, json, [])); + return new(id, json2); + } + else + { + var childClosures = isRoot || inheritedDetachInfo.IsDetachable ? _currentClosures : []; + var sb = Pools.StringBuilders.Get(); + using var writer = new StringWriter(sb); + using var jsonWriter = SpeckleObjectSerializerPool.Instance.GetJsonTextWriter(writer); + var id = SerializeBaseObject(baseObj, jsonWriter, childClosures); + var json = new Json(writer.ToString()); + Pools.StringBuilders.Return(sb); + return new(id, json); + } + } + + private Id SerializeBaseObject(Base baseObj, JsonWriter writer, Closures closure) + { + if (baseObj is not Blob) + { + writer = new SerializerIdWriter(writer); + } + + writer.WriteStartObject(); + // Convert all properties + foreach (var prop in _propertyGatherer.ExtractAllProperties(baseObj)) + { + if (prop.PropertyAttributeInfo.JsonPropertyInfo is { NullValueHandling: NullValueHandling.Ignore }) + { + continue; + } + + writer.WritePropertyName(prop.Name); + SerializeOrChunkProperty(prop.Value, writer, prop.PropertyAttributeInfo); + } + + Id id; + if (writer is SerializerIdWriter serializerIdWriter) + { + (var json, writer) = serializerIdWriter.FinishIdWriter(); + id = IdGenerator.ComputeId(json); + } + else + { + id = new Id(((Blob)baseObj).id.NotNull()); + } + writer.WritePropertyName("id"); + writer.WriteValue(id.Value); + baseObj.id = id.Value; + + if (closure.Count > 0) + { + writer.WritePropertyName("__closure"); + writer.WriteStartObject(); + foreach (var c in closure) + { + writer.WritePropertyName(c.Key.Value); + writer.WriteValue(c.Value); + } + writer.WriteEndObject(); + } + + writer.WriteEndObject(); + return id; + } + + private List GetChunk() + { + var chunk = _chunks3Pool.Get(); + _chunks3.Add(chunk); + return chunk; + } + + private void SerializeOrChunkProperty( + object? baseValue, + JsonWriter jsonWriter, + PropertyAttributeInfo propertyAttributeInfo + ) + { + if (baseValue is IEnumerable chunkableCollection && propertyAttributeInfo.IsChunkable) + { + List chunks = _chunks2Pool.Get(); + _chunks2.Add(chunks); + + DataChunk crtChunk = new() { data = GetChunk() }; + + foreach (object element in chunkableCollection) + { + crtChunk.data.Add(element); + if (crtChunk.data.Count >= propertyAttributeInfo.ChunkSize) + { + chunks.Add(crtChunk); + crtChunk = new DataChunk { data = GetChunk() }; + } + } + + if (crtChunk.data.Count > 0) + { + chunks.Add(crtChunk); + } + + SerializeProperty(chunks, jsonWriter, new PropertyAttributeInfo(true, false, 0, null)); + return; + } + + SerializeProperty(baseValue, jsonWriter, propertyAttributeInfo); + } + + private static void MergeClosures(Dictionary current, Closures child) + { + foreach (var closure in child) + { + current[closure.Key] = 100; + } + } + + private void AddClosure(Id id) => _currentClosures[id] = 100; +} diff --git a/src/Speckle.Sdk/Serialisation/V2/Send/ObjectSerializerFactory.cs b/src/Speckle.Sdk/Serialisation/V2/Send/ObjectSerializerFactory.cs new file mode 100644 index 00000000..e96b98bf --- /dev/null +++ b/src/Speckle.Sdk/Serialisation/V2/Send/ObjectSerializerFactory.cs @@ -0,0 +1,17 @@ +using Speckle.InterfaceGenerator; +using Speckle.Sdk.Dependencies; +using Speckle.Sdk.Models; +using Closures = System.Collections.Generic.Dictionary; + +namespace Speckle.Sdk.Serialisation.V2.Send; + +[GenerateAutoInterface] +public class ObjectSerializerFactory(IBasePropertyGatherer propertyGatherer) : IObjectSerializerFactory +{ + private readonly Pool> _chunkPool = Pools.CreateListPool<(Id, Json, Closures)>(); + private readonly Pool> _chunk2Pool = Pools.CreateListPool(); + private readonly Pool> _chunk3Pool = Pools.CreateListPool(); + + public IObjectSerializer Create(IReadOnlyDictionary baseCache, CancellationToken cancellationToken) => + new ObjectSerializer(propertyGatherer, baseCache, _chunkPool, _chunk2Pool, _chunk3Pool, cancellationToken); +} diff --git a/src/Speckle.Sdk/Serialisation/V2/Send/PropertyAttributeInfo.cs b/src/Speckle.Sdk/Serialisation/V2/Send/PropertyAttributeInfo.cs new file mode 100644 index 00000000..26f37a79 --- /dev/null +++ b/src/Speckle.Sdk/Serialisation/V2/Send/PropertyAttributeInfo.cs @@ -0,0 +1,24 @@ +using Speckle.Newtonsoft.Json; + +namespace Speckle.Sdk.Serialisation.V2.Send; + +public readonly struct PropertyAttributeInfo +{ + public PropertyAttributeInfo( + bool isDetachable, + bool isChunkable, + int chunkSize, + JsonPropertyAttribute? jsonPropertyAttribute + ) + { + IsDetachable = isDetachable || isChunkable; + IsChunkable = isChunkable; + ChunkSize = chunkSize; + JsonPropertyInfo = jsonPropertyAttribute; + } + + public readonly bool IsDetachable; + public readonly bool IsChunkable; + public readonly int ChunkSize; + public readonly JsonPropertyAttribute? JsonPropertyInfo; +} diff --git a/src/Speckle.Sdk/Serialisation/V2/Send/SerializeProcess.cs b/src/Speckle.Sdk/Serialisation/V2/Send/SerializeProcess.cs new file mode 100644 index 00000000..04edb89e --- /dev/null +++ b/src/Speckle.Sdk/Serialisation/V2/Send/SerializeProcess.cs @@ -0,0 +1,261 @@ +using System.Collections.Concurrent; +using System.Diagnostics.CodeAnalysis; +using Microsoft.Extensions.Logging; +using Speckle.InterfaceGenerator; +using Speckle.Sdk.Common; +using Speckle.Sdk.Dependencies; +using Speckle.Sdk.Dependencies.Serialization; +using Speckle.Sdk.Models; +using Speckle.Sdk.SQLite; +using Speckle.Sdk.Transports; + +namespace Speckle.Sdk.Serialisation.V2.Send; + +public record SerializeProcessOptions( + bool SkipCacheRead = false, + bool SkipCacheWrite = false, + bool SkipServer = false, + bool SkipFindTotalObjects = false +); + +public readonly record struct SerializeProcessResults( + string RootId, + IReadOnlyDictionary ConvertedReferences +); + +public partial interface ISerializeProcess : IAsyncDisposable; + +[GenerateAutoInterface] +public sealed class SerializeProcess( + IProgress? progress, + ISqLiteJsonCacheManager sqLiteJsonCacheManager, + IServerObjectManager serverObjectManager, + IBaseChildFinder baseChildFinder, + IBaseSerializer baseSerializer, + ILoggerFactory loggerFactory, + CancellationToken cancellationToken, + SerializeProcessOptions? options = null +#pragma warning disable CS9107 +#pragma warning disable CA2254 +) + : ChannelSaver(x => loggerFactory.CreateLogger().LogWarning(x), cancellationToken), + ISerializeProcess +#pragma warning restore CA2254 +#pragma warning restore CS9107 +{ + //async dispose + [SuppressMessage("Usage", "CA2213:Disposable fields should be disposed")] + private readonly PriorityScheduler _highest = new( + loggerFactory.CreateLogger(), + ThreadPriority.Highest, + 2, + cancellationToken + ); + + //async dispose + [SuppressMessage("Usage", "CA2213:Disposable fields should be disposed")] + private readonly PriorityScheduler _belowNormal = new( + loggerFactory.CreateLogger(), + ThreadPriority.BelowNormal, + Environment.ProcessorCount * 2, + cancellationToken + ); + + private readonly SerializeProcessOptions _options = options ?? new(); + private readonly ILogger _logger = loggerFactory.CreateLogger(); + + private readonly Pool> _currentClosurePool = Pools.CreateDictionaryPool(); + private readonly Pool> _childClosurePool = Pools.CreateConcurrentDictionaryPool< + Id, + NodeInfo + >(); + + private long _objectCount; + private long _objectsFound; + + private long _objectsSerialized; + + private long _uploaded; + private long _cached; + + [AutoInterfaceIgnore] + public async ValueTask DisposeAsync() + { + await WaitForSchedulerCompletion().ConfigureAwait(false); + await _highest.DisposeAsync().ConfigureAwait(false); + await _belowNormal.DisposeAsync().ConfigureAwait(false); + sqLiteJsonCacheManager.Dispose(); + } + + public void ThrowIfFailed() + { + if (Exception is not null) + { + throw new SpeckleException("Error while sending", Exception); + } + cancellationToken.ThrowIfCancellationRequested(); + } + + private async Task WaitForSchedulerCompletion() + { + await _highest.WaitForCompletion().ConfigureAwait(false); + await _belowNormal.WaitForCompletion().ConfigureAwait(false); + } + + public async Task Serialize(Base root) + { + try + { + var channelTask = Start(); + var findTotalObjectsTask = Task.CompletedTask; + if (!_options.SkipFindTotalObjects) + { + ThrowIfFailed(); + findTotalObjectsTask = Task.Factory.StartNew( + () => TraverseTotal(root), + cancellationToken, + TaskCreationOptions.AttachedToParent | TaskCreationOptions.PreferFairness, + _highest + ); + } + + await Traverse(root).ConfigureAwait(false); + DoneTraversing(); + await Task.WhenAll(findTotalObjectsTask, channelTask).ConfigureAwait(false); + ThrowIfFailed(); + await DoneSaving().ConfigureAwait(false); + ThrowIfFailed(); + await WaitForSchedulerCompletion().ConfigureAwait(false); + ThrowIfFailed(); + return new(root.id.NotNull(), baseSerializer.ObjectReferences.Freeze()); + } + catch (TaskCanceledException) + { + ThrowIfFailed(); + throw; + } + } + + private void TraverseTotal(Base obj) + { + foreach (var child in baseChildFinder.GetChildren(obj)) + { + _objectsFound++; + progress?.Report(new(ProgressEvent.FindingChildren, _objectsFound, null)); + TraverseTotal(child); + } + } + + private async Task> Traverse(Base obj) + { + var tasks = new List>>(); + foreach (var child in baseChildFinder.GetChildren(obj)) + { + // tmp is necessary because of the way closures close over loop variables + var tmp = child; + cancellationToken.ThrowIfCancellationRequested(); + var t = Task + .Factory.StartNew( + async () => await Traverse(tmp).ConfigureAwait(false), + cancellationToken, + TaskCreationOptions.AttachedToParent | TaskCreationOptions.PreferFairness, + _belowNormal + ) + .Unwrap(); + tasks.Add(t); + } + + Dictionary[] taskClosures = []; + if (tasks.Count > 0) + { + taskClosures = await Task.WhenAll(tasks).ConfigureAwait(false); + } + var childClosures = _childClosurePool.Get(); + foreach (var childClosure in taskClosures) + { + foreach (var kvp in childClosure) + { + childClosures[kvp.Key] = kvp.Value; + } + _currentClosurePool.Return(childClosure); + } + + var items = baseSerializer.Serialise(obj, childClosures, _options.SkipCacheRead, cancellationToken); + + var currentClosures = _currentClosurePool.Get(); + Interlocked.Increment(ref _objectCount); + progress?.Report(new(ProgressEvent.FromCacheOrSerialized, _objectCount, Math.Max(_objectCount, _objectsFound))); + foreach (var item in items) + { + if (item.NeedsStorage) + { + Interlocked.Increment(ref _objectsSerialized); + await Save(item).ConfigureAwait(false); + } + + if (!currentClosures.ContainsKey(item.Id)) + { + currentClosures.Add(item.Id, new NodeInfo(item.Json, item.Closures)); + } + } + _childClosurePool.Return(childClosures); + return currentClosures; + } + + protected override async Task SendToServerInternal(Batch batch) + { + try + { + if (!_options.SkipServer && batch.Items.Count != 0) + { + var objectBatch = batch.Items.Distinct().ToList(); + var hasObjects = await serverObjectManager + .HasObjects(objectBatch.Select(x => x.Id.Value).Freeze(), cancellationToken) + .ConfigureAwait(false); + objectBatch = batch.Items.Where(x => !hasObjects[x.Id.Value]).ToList(); + if (objectBatch.Count != 0) + { + await serverObjectManager.UploadObjects(objectBatch, true, progress, cancellationToken).ConfigureAwait(false); + Interlocked.Exchange(ref _uploaded, _uploaded + batch.Items.Count); + } + + progress?.Report(new(ProgressEvent.UploadedObjects, _uploaded, null)); + } + } + catch (OperationCanceledException) + { + throw; + } +#pragma warning disable CA1031 + catch (Exception e) +#pragma warning restore CA1031 + { + _logger.LogError(e, "Error sending objects to server"); + throw; + } + } + + public override void SaveToCache(List batch) + { + try + { + if (!_options.SkipCacheWrite && batch.Count != 0) + { + sqLiteJsonCacheManager.SaveObjects(batch.Select(x => (x.Id.Value, x.Json.Value))); + Interlocked.Exchange(ref _cached, _cached + batch.Count); + progress?.Report(new(ProgressEvent.CachedToLocal, _cached, _objectsSerialized)); + } + } + catch (OperationCanceledException) + { + throw; + } +#pragma warning disable CA1031 + catch (Exception e) +#pragma warning restore CA1031 + { + _logger.LogError(e, "Error sending objects to server"); + throw; + } + } +} diff --git a/src/Speckle.Sdk/Serialisation/V2/SerializeProcessFactory.cs b/src/Speckle.Sdk/Serialisation/V2/SerializeProcessFactory.cs new file mode 100644 index 00000000..863d7026 --- /dev/null +++ b/src/Speckle.Sdk/Serialisation/V2/SerializeProcessFactory.cs @@ -0,0 +1,82 @@ +using Microsoft.Extensions.Logging; +using Speckle.Sdk.Serialisation.V2.Receive; +using Speckle.Sdk.Serialisation.V2.Send; +using Speckle.Sdk.SQLite; +using Speckle.Sdk.Transports; + +namespace Speckle.Sdk.Serialisation.V2; + +public interface ISerializeProcessFactory +{ + ISerializeProcess CreateSerializeProcess( + Uri url, + string streamId, + string? authorizationToken, + IProgress? progress, + CancellationToken cancellationToken, + SerializeProcessOptions? options = null + ); + IDeserializeProcess CreateDeserializeProcess( + Uri url, + string streamId, + string? authorizationToken, + IProgress? progress, + CancellationToken cancellationToken, + DeserializeProcessOptions? options = null + ); +} + +public class SerializeProcessFactory( + IBaseChildFinder baseChildFinder, + IObjectSerializerFactory objectSerializerFactory, + IBaseDeserializer baseDeserializer, + ISqLiteJsonCacheManagerFactory sqLiteJsonCacheManagerFactory, + IServerObjectManagerFactory serverObjectManagerFactory, + ILoggerFactory loggerFactory +) : ISerializeProcessFactory +{ + public ISerializeProcess CreateSerializeProcess( + Uri url, + string streamId, + string? authorizationToken, + IProgress? progress, + CancellationToken cancellationToken, + SerializeProcessOptions? options = null + ) + { + var sqLiteJsonCacheManager = sqLiteJsonCacheManagerFactory.CreateFromStream(streamId); + var serverObjectManager = serverObjectManagerFactory.Create(url, streamId, authorizationToken); + return new SerializeProcess( + progress, + sqLiteJsonCacheManager, + serverObjectManager, + baseChildFinder, + new BaseSerializer(sqLiteJsonCacheManager, objectSerializerFactory), + loggerFactory, + cancellationToken, + options + ); + } + + public IDeserializeProcess CreateDeserializeProcess( + Uri url, + string streamId, + string? authorizationToken, + IProgress? progress, + CancellationToken cancellationToken, + DeserializeProcessOptions? options = null + ) + { + var sqLiteJsonCacheManager = sqLiteJsonCacheManagerFactory.CreateFromStream(streamId); + var serverObjectManager = serverObjectManagerFactory.Create(url, streamId, authorizationToken); + return new DeserializeProcess( + sqLiteJsonCacheManager, + serverObjectManager, + progress, + baseDeserializer, + loggerFactory, + cancellationToken, + options + ); + } +} diff --git a/src/Speckle.Sdk/Serialisation/V2/ServerObjectManager.cs b/src/Speckle.Sdk/Serialisation/V2/ServerObjectManager.cs new file mode 100644 index 00000000..895a4a6f --- /dev/null +++ b/src/Speckle.Sdk/Serialisation/V2/ServerObjectManager.cs @@ -0,0 +1,212 @@ +using System.Net; +using System.Net.Http.Headers; +using System.Runtime.CompilerServices; +using System.Text; +using Speckle.InterfaceGenerator; +using Speckle.Newtonsoft.Json; +using Speckle.Sdk.Common; +using Speckle.Sdk.Helpers; +using Speckle.Sdk.Logging; +using Speckle.Sdk.Serialisation.V2.Send; +using Speckle.Sdk.Transports; +using Speckle.Sdk.Transports.ServerUtils; + +namespace Speckle.Sdk.Serialisation.V2; + +[GenerateAutoInterface] +public class ServerObjectManager : IServerObjectManager +{ + private static readonly char[] s_separator = ['\t']; + + private readonly ISdkActivityFactory _activityFactory; + private readonly HttpClient _client; + private readonly string _streamId; + + public ServerObjectManager( + ISpeckleHttp speckleHttp, + ISdkActivityFactory activityFactory, + Uri baseUri, + string streamId, + string? authorizationToken, + int timeoutSeconds = 120 + ) + { + _activityFactory = activityFactory; + _client = speckleHttp.CreateHttpClient( + new HttpClientHandler { AutomaticDecompression = DecompressionMethods.GZip }, + timeoutSeconds: timeoutSeconds, + authorizationToken: authorizationToken + ); + _client.BaseAddress = baseUri; + _streamId = streamId; + } + + public async IAsyncEnumerable<(string, string)> DownloadObjects( + IReadOnlyCollection objectIds, + IProgress? progress, + [EnumeratorCancellation] CancellationToken cancellationToken + ) + { + using var _ = _activityFactory.Start(); + cancellationToken.ThrowIfCancellationRequested(); + + using var childrenHttpMessage = new HttpRequestMessage(); + childrenHttpMessage.RequestUri = new Uri($"/api/getobjects/{_streamId}", UriKind.Relative); + childrenHttpMessage.Method = HttpMethod.Post; + + Dictionary postParameters = new() { { "objects", JsonConvert.SerializeObject(objectIds) } }; + string serializedPayload = JsonConvert.SerializeObject(postParameters); + childrenHttpMessage.Content = new StringContent(serializedPayload, Encoding.UTF8, "application/json"); + childrenHttpMessage.Headers.Add("Accept", "text/plain"); + + HttpResponseMessage childrenHttpResponse = await _client + .SendAsync(childrenHttpMessage, HttpCompletionOption.ResponseContentRead, cancellationToken) + .ConfigureAwait(false); + + await foreach (var (id, json) in ResponseProgress(childrenHttpResponse, progress, false, cancellationToken)) + { + if (id is not null) + { + yield return (id, json); + } + } + } + + public async Task DownloadSingleObject( + string objectId, + IProgress? progress, + CancellationToken cancellationToken + ) + { + using var _ = _activityFactory.Start(); + cancellationToken.ThrowIfCancellationRequested(); + + // Get root object + using var rootHttpMessage = new HttpRequestMessage(); + rootHttpMessage.RequestUri = new Uri($"/objects/{_streamId}/{objectId}/single", UriKind.Relative); + rootHttpMessage.Method = HttpMethod.Get; + + HttpResponseMessage rootHttpResponse = await _client + .SendAsync(rootHttpMessage, HttpCompletionOption.ResponseContentRead, cancellationToken) + .ConfigureAwait(false); + + var (_, json) = await ResponseProgress(rootHttpResponse, progress, true, cancellationToken) + .FirstAsync() + .ConfigureAwait(false); + return json; + } + + private async IAsyncEnumerable<(string?, string)> ResponseProgress( + HttpResponseMessage childrenHttpResponse, + IProgress? progress, + bool isSingle, + [EnumeratorCancellation] CancellationToken cancellationToken + ) + { + childrenHttpResponse.EnsureSuccessStatusCode(); + var length = childrenHttpResponse.Content.Headers.ContentLength; +#if NET8_0_OR_GREATER + using Stream childrenStream = await childrenHttpResponse + .Content.ReadAsStreamAsync(cancellationToken) + .ConfigureAwait(false); +#else + using Stream childrenStream = await childrenHttpResponse.Content.ReadAsStreamAsync().ConfigureAwait(false); +#endif + + using var reader = new StreamReader(new ProgressStream(childrenStream, length, progress, true), Encoding.UTF8); + +#if NET8_0_OR_GREATER + while (await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false) is { } line) +#else + while (await reader.ReadLineAsync().ConfigureAwait(false) is { } line) +#endif + { + cancellationToken.ThrowIfCancellationRequested(); + if (!isSingle) + { + var pcs = line.Split(s_separator, 2); + yield return (pcs[0], pcs[1]); + } + else + { + yield return (string.Empty, line); + } + } + } + + public async Task> HasObjects( + IReadOnlyCollection objectIds, + CancellationToken cancellationToken + ) + { + cancellationToken.ThrowIfCancellationRequested(); + + // Stopwatch sw = new Stopwatch(); sw.Start(); + + string objectsPostParameter = JsonConvert.SerializeObject(objectIds); + var payload = new Dictionary { { "objects", objectsPostParameter } }; + string serializedPayload = JsonConvert.SerializeObject(payload); + var uri = new Uri($"/api/diff/{_streamId}", UriKind.Relative); + + using StringContent stringContent = new(serializedPayload, Encoding.UTF8, "application/json"); + using HttpResponseMessage response = await _client + .PostAsync(uri, stringContent, cancellationToken) + .ConfigureAwait(false); + + response.EnsureSuccessStatusCode(); +#if NET8_0_OR_GREATER + var hasObjects = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); +#else + var hasObjects = await response.Content.ReadAsStringAsync().ConfigureAwait(false); +#endif + return JsonConvert.DeserializeObject>(hasObjects).NotNull(); + } + + public async Task UploadObjects( + IReadOnlyList objects, + bool compressPayloads, + IProgress? progress, + CancellationToken cancellationToken + ) + { + cancellationToken.ThrowIfCancellationRequested(); + + using HttpRequestMessage message = new() + { + RequestUri = new Uri($"/objects/{_streamId}", UriKind.Relative), + Method = HttpMethod.Post, + }; + + MultipartFormDataContent multipart = new(); + + int mpId = 0; + var ctBuilder = new StringBuilder("["); + for (int i = 0; i < objects.Count; i++) + { + if (i > 0) + { + ctBuilder.Append(','); + } + + ctBuilder.Append(objects[i].Json); + } + ctBuilder.Append(']'); + string ct = ctBuilder.ToString(); + + if (compressPayloads) + { + var content = new GzipContent(new StringContent(ct, Encoding.UTF8)); + content.Headers.ContentType = new MediaTypeHeaderValue("application/gzip"); + multipart.Add(content, $"batch-{mpId}", $"batch-{mpId}"); + } + else + { + multipart.Add(new StringContent(ct, Encoding.UTF8), $"batch-{mpId}", $"batch-{mpId}"); + } + + message.Content = new ProgressContent(multipart, progress); + HttpResponseMessage response = await _client.SendAsync(message, cancellationToken).ConfigureAwait(false); + + response.EnsureSuccessStatusCode(); + } +} diff --git a/src/Speckle.Sdk/Serialisation/V2/ServerObjectManagerFactory.cs b/src/Speckle.Sdk/Serialisation/V2/ServerObjectManagerFactory.cs new file mode 100644 index 00000000..071a62b5 --- /dev/null +++ b/src/Speckle.Sdk/Serialisation/V2/ServerObjectManagerFactory.cs @@ -0,0 +1,13 @@ +using Speckle.InterfaceGenerator; +using Speckle.Sdk.Helpers; +using Speckle.Sdk.Logging; + +namespace Speckle.Sdk.Serialisation.V2; + +[GenerateAutoInterface] +public class ServerObjectManagerFactory(ISpeckleHttp speckleHttp, ISdkActivityFactory activityFactory) + : IServerObjectManagerFactory +{ + public IServerObjectManager Create(Uri url, string streamId, string? authorizationToken, int timeoutSeconds = 120) => + new ServerObjectManager(speckleHttp, activityFactory, url, streamId, authorizationToken, timeoutSeconds); +} diff --git a/src/Speckle.Sdk/ServiceRegistration.cs b/src/Speckle.Sdk/ServiceRegistration.cs new file mode 100644 index 00000000..703810dc --- /dev/null +++ b/src/Speckle.Sdk/ServiceRegistration.cs @@ -0,0 +1,53 @@ +using System.Reflection; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Speckle.Sdk.Dependencies; +using Speckle.Sdk.Host; +using Speckle.Sdk.Logging; + +namespace Speckle.Sdk; + +public static class ServiceRegistration +{ + public static IServiceCollection AddSpeckleSdk( + this IServiceCollection serviceCollection, + HostApplication application, + HostAppVersion version, + string speckleVersion + ) + { + serviceCollection.AddLogging(); + string name = application.Name; + + serviceCollection.AddSingleton( + new SpeckleApplication + { + HostApplication = name, + SpeckleVersion = speckleVersion, + HostApplicationVersion = HostApplications.GetVersion(version), + Slug = application.Slug, + } + ); + serviceCollection.TryAddSingleton(); + serviceCollection.TryAddSingleton(); + serviceCollection.AddMatchingInterfacesAsTransient(Assembly.GetExecutingAssembly()); + serviceCollection.AddMatchingInterfacesAsTransient(typeof(GraphQLRetry).Assembly); + return serviceCollection; + } + + public static IServiceCollection AddMatchingInterfacesAsTransient( + this IServiceCollection serviceCollection, + Assembly assembly + ) + { + foreach (var type in assembly.ExportedTypes.Where(t => t.IsNonAbstractClass())) + { + foreach (var matchingInterface in type.FindMatchingInterface()) + { + serviceCollection.TryAddTransient(matchingInterface, type); + } + } + + return serviceCollection; + } +} diff --git a/src/Speckle.Sdk/Setup.cs b/src/Speckle.Sdk/Setup.cs deleted file mode 100644 index 324c4ac3..00000000 --- a/src/Speckle.Sdk/Setup.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using Speckle.Sdk.Credentials; -using Speckle.Sdk.Helpers; -using Speckle.Sdk.Host; -using Speckle.Sdk.Logging; - -namespace Speckle.Sdk; - -/// -/// Anonymous telemetry to help us understand how to make a better Speckle. -/// This really helps us to deliver a better open source project and product! -/// -public static class Setup -{ - public static Mutex Mutex { get; set; } - - private static bool s_initialized; - - static Setup() - { - //Set fallback values - try - { - Application = Process.GetCurrentProcess().ProcessName; - } - catch (InvalidOperationException) - { - Application = "other (.NET)"; - } - } - - /// - /// Set from the connectors, defines which current host application we're running on. - /// - internal static string Application { get; private set; } - internal static string Version { get; private set; } - internal static string ApplicationVersion => $"{Application} {Version}"; - - /// - /// Set from the connectors, defines which current host application we're running on - includes the version. - /// - internal static string Slug { get; private set; } = HostApplications.Other.Slug; - - public static IDisposable? Initialize(SpeckleConfiguration configuration) - { - if (s_initialized) - { - SpeckleLog.Logger.Information("Setup was already initialized with {currentHostApp}", configuration.Application); - throw new InvalidOperationException(); - } - - s_initialized = true; - Application = configuration.Application.Name; - Version = HostApplications.GetVersion(configuration.Version); - Slug = configuration.Application.Slug; - - //start mutex so that Manager can detect if this process is running - Mutex = new Mutex(false, "SpeckleConnector-" + configuration.Application); - - foreach (var account in AccountManager.GetAccounts()) - { - Analytics.AddConnectorToProfile(account.GetHashedEmail(), Application); - Analytics.IdentifyProfile(account.GetHashedEmail(), Application); - } - return LogBuilder.Initialize( - GetUserIdFromDefaultAccount(), - ApplicationVersion, - Slug, - configuration.Logging, - configuration.Tracing - ); - } - - private static string GetUserIdFromDefaultAccount() - { - var machineName = Environment.MachineName; - var userName = Environment.UserName; - var id = Crypt.Md5($"{machineName}:{userName}", "X2"); - try - { - var defaultAccount = AccountManager.GetDefaultAccount(); - if (defaultAccount != null) - { - id = defaultAccount.GetHashedEmail(); - } - } - catch (Exception ex) when (!ex.IsFatal()) - { - // To log it after Logger initialized as deferred action. - } - return id; - } -} diff --git a/src/Speckle.Sdk/Speckle.Sdk.csproj b/src/Speckle.Sdk/Speckle.Sdk.csproj index dfa25f43..97406570 100644 --- a/src/Speckle.Sdk/Speckle.Sdk.csproj +++ b/src/Speckle.Sdk/Speckle.Sdk.csproj @@ -1,39 +1,50 @@ - - netstandard2.0 - Speckle.Sdk + + + netstandard2.0;net8.0 + Debug;Release;Local + true + + + Speckle.Sdk - Speckle.Sdk The .NET SDK for Speckle $(PackageTags) core sdk + $(NoWarn);CS8618 + + + true - System.Runtime.CompilerServices.RequiresLocationAttribute - Debug;Release;Local true snupkg - - + + - + - - - - + - + + + + + + + + + - + diff --git a/src/Speckle.Sdk/SpeckleApplication.cs b/src/Speckle.Sdk/SpeckleApplication.cs new file mode 100644 index 00000000..7aeccb81 --- /dev/null +++ b/src/Speckle.Sdk/SpeckleApplication.cs @@ -0,0 +1,14 @@ +using Speckle.InterfaceGenerator; + +namespace Speckle.Sdk; + +[GenerateAutoInterface] +public class SpeckleApplication : ISpeckleApplication +{ + public string HostApplication { get; init; } + public string HostApplicationVersion { get; init; } + public string Slug { get; init; } + public string SpeckleVersion { get; init; } + + public string ApplicationAndVersion => $"{HostApplication} {HostApplicationVersion}"; +} diff --git a/src/Speckle.Sdk/SpeckleConfiguration.cs b/src/Speckle.Sdk/SpeckleConfiguration.cs deleted file mode 100644 index 2b711a05..00000000 --- a/src/Speckle.Sdk/SpeckleConfiguration.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Speckle.Sdk.Host; -using Speckle.Sdk.Logging; - -namespace Speckle.Sdk; - -public record SpeckleConfiguration( - HostApplication Application, - HostAppVersion Version, - SpeckleLogging? Logging = null, - SpeckleTracing? Tracing = null -); diff --git a/src/Speckle.Sdk/SpeckleException.cs b/src/Speckle.Sdk/SpeckleException.cs index 77a61eb9..19e18fe4 100644 --- a/src/Speckle.Sdk/SpeckleException.cs +++ b/src/Speckle.Sdk/SpeckleException.cs @@ -1,6 +1,8 @@ namespace Speckle.Sdk; +#pragma warning disable CA2237 public class SpeckleException : Exception +#pragma warning restore CA2237 { public SpeckleException() { } diff --git a/src/Speckle.Sdk/Transports/DiskTransport.cs b/src/Speckle.Sdk/Transports/DiskTransport.cs index 06133e3b..2c60a36d 100644 --- a/src/Speckle.Sdk/Transports/DiskTransport.cs +++ b/src/Speckle.Sdk/Transports/DiskTransport.cs @@ -28,7 +28,7 @@ public class DiskTransport : ICloneable, ITransport CancellationToken = CancellationToken, OnErrorAction = OnErrorAction, OnProgressAction = OnProgressAction, - TransportName = TransportName + TransportName = TransportName, }; } @@ -39,12 +39,12 @@ public class DiskTransport : ICloneable, ITransport { { "name", TransportName }, { "type", GetType().Name }, - { "basePath", RootPath } + { "basePath", RootPath }, }; public CancellationToken CancellationToken { get; set; } - public Action? OnProgressAction { get; set; } + public IProgress? OnProgressAction { get; set; } public Action? OnErrorAction { get; set; } @@ -59,17 +59,17 @@ public class DiskTransport : ICloneable, ITransport public void EndWrite() { } - public string? GetObject(string id) + public Task GetObject(string id) { CancellationToken.ThrowIfCancellationRequested(); var filePath = Path.Combine(RootPath, id); if (File.Exists(filePath)) { - return File.ReadAllText(filePath, Encoding.UTF8); + return Task.FromResult(File.ReadAllText(filePath, Encoding.UTF8)); } - return null; + return Task.FromResult(null); } public void SaveObject(string id, string serializedObject) @@ -93,7 +93,6 @@ public class DiskTransport : ICloneable, ITransport } SavedObjectCount++; - OnProgressAction?.Invoke(new(ProgressEvent.DownloadObject, SavedObjectCount, null)); stopwatch.Stop(); Elapsed += stopwatch.Elapsed; } @@ -103,20 +102,12 @@ public class DiskTransport : ICloneable, ITransport return Task.CompletedTask; } - public Task CopyObjectAndChildren( - string id, - ITransport targetTransport, - Action? onTotalChildrenCountKnown = null - ) + public async Task CopyObjectAndChildren(string id, ITransport targetTransport) { - string res = TransportHelpers.CopyObjectAndChildrenSync( - id, - this, - targetTransport, - onTotalChildrenCountKnown, - CancellationToken - ); - return Task.FromResult(res); + string res = await TransportHelpers + .CopyObjectAndChildrenAsync(id, this, targetTransport, CancellationToken) + .ConfigureAwait(false); + return res; } public Task> HasObjects(IReadOnlyList objectIds) diff --git a/src/Speckle.Sdk/Transports/Exceptions.cs b/src/Speckle.Sdk/Transports/Exceptions.cs index 2a9d6183..90cd81b7 100644 --- a/src/Speckle.Sdk/Transports/Exceptions.cs +++ b/src/Speckle.Sdk/Transports/Exceptions.cs @@ -1,5 +1,3 @@ -using Speckle.Sdk.Logging; - namespace Speckle.Sdk.Transports; public class TransportException : SpeckleException diff --git a/src/Speckle.Sdk/Transports/ITransport.cs b/src/Speckle.Sdk/Transports/ITransport.cs index c997e6da..6d315966 100644 --- a/src/Speckle.Sdk/Transports/ITransport.cs +++ b/src/Speckle.Sdk/Transports/ITransport.cs @@ -2,18 +2,6 @@ using Speckle.Sdk.Models; namespace Speckle.Sdk.Transports; -public enum ProgressEvent -{ - DownloadBytes, - UploadBytes, - DownloadObject, - UploadObject, - DeserializeObject, - SerializeObject, -} - -public record ProgressArgs(ProgressEvent ProgressEvent, long? Count, long? Total); - /// /// Interface defining the contract for transport implementations. /// @@ -42,7 +30,7 @@ public interface ITransport /// /// Used to report progress during the transport's longer operations. /// - public Action? OnProgressAction { get; set; } + public IProgress? OnProgressAction { get; set; } /// /// Signals to the transport that writes are about to begin. @@ -72,23 +60,18 @@ public interface ITransport /// The object's hash. /// The serialized object data, or if the transport cannot find the object /// requested cancel - public string? GetObject(string id); + public Task GetObject(string id); /// /// Copies the parent object and all its children to the provided transport. /// /// The id of the object you want to copy. /// The transport you want to copy the object to. - /// (Optional) an that will be invoked once, when the number of object children to be copied over is known. /// The string representation of the root object. /// The provided arguments are not valid /// The transport could not complete the operation /// requested cancel - public Task CopyObjectAndChildren( - string id, - ITransport targetTransport, - Action? onTotalChildrenCountKnown = null - ); + public Task CopyObjectAndChildren(string id, ITransport targetTransport); /// /// Checks if objects are present in the transport diff --git a/src/Speckle.Sdk/Transports/Memory.cs b/src/Speckle.Sdk/Transports/Memory.cs deleted file mode 100644 index 760be056..00000000 --- a/src/Speckle.Sdk/Transports/Memory.cs +++ /dev/null @@ -1,111 +0,0 @@ -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using Speckle.Sdk.Logging; - -namespace Speckle.Sdk.Transports; - -/// -/// An in memory storage of speckle objects. -/// -public sealed class MemoryTransport : ITransport, ICloneable -{ - public IDictionary Objects { get; } - - public MemoryTransport() - : this(new Dictionary()) { } - - public MemoryTransport(IDictionary objects) - { - Objects = objects; - SpeckleLog.Logger.Debug("Creating a new Memory Transport"); - } - - public object Clone() - { - return new MemoryTransport(Objects) - { - TransportName = TransportName, - OnProgressAction = OnProgressAction, - CancellationToken = CancellationToken, - SavedObjectCount = SavedObjectCount - }; - } - - public CancellationToken CancellationToken { get; set; } - - public string TransportName { get; set; } = "Memory"; - - public Action? OnProgressAction { get; set; } - - public int SavedObjectCount { get; private set; } - - public Dictionary TransportContext => new() { { "name", TransportName }, { "type", GetType().Name } }; - - public TimeSpan Elapsed { get; private set; } = TimeSpan.Zero; - - public void BeginWrite() - { - SavedObjectCount = 0; - } - - public void EndWrite() { } - - public void SaveObject(string id, string serializedObject) - { - CancellationToken.ThrowIfCancellationRequested(); - var stopwatch = Stopwatch.StartNew(); - - Objects[id] = serializedObject; - - SavedObjectCount++; - OnProgressAction?.Invoke(new(ProgressEvent.UploadObject, 1, 1)); - stopwatch.Stop(); - Elapsed += stopwatch.Elapsed; - } - - public string? GetObject(string id) - { - var stopwatch = Stopwatch.StartNew(); - var ret = Objects.TryGetValue(id, out string o) ? o : null; - stopwatch.Stop(); - Elapsed += stopwatch.Elapsed; - return ret; - } - - public Task CopyObjectAndChildren( - string id, - ITransport targetTransport, - Action? onTotalChildrenCountKnown = null - ) - { - string res = TransportHelpers.CopyObjectAndChildrenSync( - id, - this, - targetTransport, - onTotalChildrenCountKnown, - CancellationToken - ); - return Task.FromResult(res); - } - - public Task WriteComplete() - { - return Task.CompletedTask; - } - - public Task> HasObjects(IReadOnlyList objectIds) - { - Dictionary ret = new(objectIds.Count); - foreach (string objectId in objectIds) - { - ret[objectId] = Objects.ContainsKey(objectId); - } - - return Task.FromResult(ret); - } - - public override string ToString() - { - return $"Memory Transport {TransportName}"; - } -} diff --git a/src/Speckle.Sdk/Transports/MemoryTransport.cs b/src/Speckle.Sdk/Transports/MemoryTransport.cs new file mode 100644 index 00000000..cc6aa1d5 --- /dev/null +++ b/src/Speckle.Sdk/Transports/MemoryTransport.cs @@ -0,0 +1,142 @@ +using System.Collections.Concurrent; +using System.Diagnostics; +using Speckle.Sdk.Logging; +using Speckle.Sdk.Models; + +namespace Speckle.Sdk.Transports; + +/// +/// An in memory storage of speckle objects. +/// +public sealed class MemoryTransport : ITransport, ICloneable, IBlobCapableTransport +{ + private readonly string _basePath; + private readonly string _applicationName; + private readonly bool _blobStorageEnabled; + public IReadOnlyDictionary Objects => _objects; + private readonly ConcurrentDictionary _objects; + + public MemoryTransport( + ConcurrentDictionary? objects = null, + bool blobStorageEnabled = false, + string? basePath = null, + string? applicationName = null + ) + { + _objects = objects ?? new ConcurrentDictionary(StringComparer.Ordinal); + _blobStorageEnabled = blobStorageEnabled; + _basePath = basePath ?? SpecklePathProvider.UserApplicationDataPath(); + _applicationName = applicationName ?? "Speckle"; + var dir = Path.Combine(_basePath, _applicationName); + try + { + Directory.CreateDirectory(dir); //ensure dir is there + } + catch (Exception ex) + when (ex is ArgumentException or IOException or UnauthorizedAccessException or NotSupportedException) + { + throw new TransportException($"Path was invalid or could not be created {dir}", ex); + } + } + + public object Clone() + { + return new MemoryTransport(_objects, _blobStorageEnabled, _basePath, _applicationName) + { + TransportName = TransportName, + OnProgressAction = OnProgressAction, + CancellationToken = CancellationToken, + SavedObjectCount = SavedObjectCount, + }; + } + + public CancellationToken CancellationToken { get; set; } + + public string TransportName { get; set; } = "Memory"; + + public IProgress? OnProgressAction { get; set; } + + public int SavedObjectCount { get; private set; } + + public Dictionary TransportContext => + new() + { + { "name", TransportName }, + { "type", GetType().Name }, + { "basePath", _basePath }, + { "applicationName", _applicationName }, + { "blobStorageFolder", BlobStorageFolder }, + }; + + public TimeSpan Elapsed { get; private set; } = TimeSpan.Zero; + + public void BeginWrite() + { + SavedObjectCount = 0; + } + + public void EndWrite() { } + + public void SaveObject(string id, string serializedObject) + { + CancellationToken.ThrowIfCancellationRequested(); + var stopwatch = Stopwatch.StartNew(); + + _objects[id] = serializedObject; + + SavedObjectCount++; + stopwatch.Stop(); + Elapsed += stopwatch.Elapsed; + } + + public Task GetObject(string id) + { + var stopwatch = Stopwatch.StartNew(); + var ret = Objects.TryGetValue(id, out string? o) ? o : null; + stopwatch.Stop(); + Elapsed += stopwatch.Elapsed; + return Task.FromResult(ret); + } + + public async Task CopyObjectAndChildren(string id, ITransport targetTransport) + { + string res = await TransportHelpers + .CopyObjectAndChildrenAsync(id, this, targetTransport, CancellationToken) + .ConfigureAwait(false); + return res; + } + + public Task WriteComplete() + { + return Task.CompletedTask; + } + + public Task> HasObjects(IReadOnlyList objectIds) + { + Dictionary ret = new(objectIds.Count); + foreach (string objectId in objectIds) + { + ret[objectId] = Objects.ContainsKey(objectId); + } + + return Task.FromResult(ret); + } + + public override string ToString() + { + return $"Memory Transport {TransportName}"; + } + + public string BlobStorageFolder => SpecklePathProvider.BlobStoragePath(Path.Combine(_basePath, _applicationName)); + + public void SaveBlob(Blob obj) + { + if (!_blobStorageEnabled) + { + return; + } + var blobPath = obj.originalPath; + var targetPath = obj.GetLocalDestinationPath(BlobStorageFolder); + File.Copy(blobPath, targetPath, true); + } +} diff --git a/src/Speckle.Sdk/Transports/ProgressArgs.cs b/src/Speckle.Sdk/Transports/ProgressArgs.cs new file mode 100644 index 00000000..b7660374 --- /dev/null +++ b/src/Speckle.Sdk/Transports/ProgressArgs.cs @@ -0,0 +1,20 @@ +namespace Speckle.Sdk.Transports; + +public readonly record struct ProgressArgs(ProgressEvent ProgressEvent, long Count, long? Total); + +public enum ProgressEvent +{ + CachedToLocal, //send and receive + + FromCacheOrSerialized, + FindingChildren, + UploadBytes, + UploadedObjects, + + CacheCheck, + DownloadBytes, + DownloadObjects, + DeserializeObject, + + SerializeObject, // old +} diff --git a/src/Speckle.Sdk/Transports/SQLite.cs b/src/Speckle.Sdk/Transports/SQLiteTransport.cs similarity index 90% rename from src/Speckle.Sdk/Transports/SQLite.cs rename to src/Speckle.Sdk/Transports/SQLiteTransport.cs index 127d5e22..46b2985d 100644 --- a/src/Speckle.Sdk/Transports/SQLite.cs +++ b/src/Speckle.Sdk/Transports/SQLiteTransport.cs @@ -1,6 +1,5 @@ using System.Collections.Concurrent; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Text; using System.Timers; using Microsoft.Data.Sqlite; @@ -59,7 +58,7 @@ public sealed class SQLiteTransport : IDisposable, ICloneable, ITransport, IBlob { AutoReset = true, Enabled = false, - Interval = POLL_INTERVAL + Interval = POLL_INTERVAL, }; _writeTimer.Elapsed += WriteTimerElapsed; } @@ -72,7 +71,7 @@ public sealed class SQLiteTransport : IDisposable, ICloneable, ITransport, IBlob private readonly string _connectionString; private SqliteConnection Connection { get; set; } - private object ConnectionLock { get; set; } + private readonly SemaphoreSlim _connectionLock = new(1, 1); public string BlobStorageFolder => SpecklePathProvider.BlobStoragePath(Path.Combine(_basePath, _applicationName)); @@ -88,7 +87,7 @@ public sealed class SQLiteTransport : IDisposable, ICloneable, ITransport, IBlob return new SQLiteTransport(_basePath, _applicationName, _scope) { OnProgressAction = OnProgressAction, - CancellationToken = CancellationToken + CancellationToken = CancellationToken, }; } @@ -98,6 +97,7 @@ public sealed class SQLiteTransport : IDisposable, ICloneable, ITransport, IBlob Connection.Close(); Connection.Dispose(); _writeTimer.Dispose(); + _connectionLock.Dispose(); } public string TransportName { get; set; } = "SQLite"; @@ -110,12 +110,12 @@ public sealed class SQLiteTransport : IDisposable, ICloneable, ITransport, IBlob { "basePath", _basePath }, { "applicationName", _applicationName }, { "scope", _scope }, - { "blobStorageFolder", BlobStorageFolder } + { "blobStorageFolder", BlobStorageFolder }, }; public CancellationToken CancellationToken { get; set; } - public Action? OnProgressAction { get; set; } + public IProgress? OnProgressAction { get; set; } public int SavedObjectCount { get; private set; } @@ -206,7 +206,6 @@ public sealed class SQLiteTransport : IDisposable, ICloneable, ITransport, IBlob Connection = new SqliteConnection(_connectionString); Connection.Open(); - ConnectionLock = new object(); } /// @@ -282,7 +281,7 @@ public sealed class SQLiteTransport : IDisposable, ICloneable, ITransport, IBlob /// public bool WriteCompletionStatus => _queue.IsEmpty && !_isWriting; - private void WriteTimerElapsed(object sender, ElapsedEventArgs e) + private void WriteTimerElapsed(object? sender, ElapsedEventArgs e) { _writeTimer.Enabled = false; @@ -331,8 +330,6 @@ public sealed class SQLiteTransport : IDisposable, ICloneable, ITransport, IBlob CancellationToken.ThrowIfCancellationRequested(); } - OnProgressAction?.Invoke(new(ProgressEvent.DownloadObject, saved, _queue.Count + 1)); - CancellationToken.ThrowIfCancellationRequested(); if (!_queue.IsEmpty) @@ -401,41 +398,35 @@ public sealed class SQLiteTransport : IDisposable, ICloneable, ITransport, IBlob /// /// /// - public string? GetObject(string id) + public async Task GetObject(string id) { CancellationToken.ThrowIfCancellationRequested(); - lock (ConnectionLock) + await _connectionLock.WaitAsync(CancellationToken).ConfigureAwait(false); + var startTime = Stopwatch.GetTimestamp(); + try { - var startTime = Stopwatch.GetTimestamp(); - using (var command = new SqliteCommand("SELECT * FROM objects WHERE hash = @hash LIMIT 1 ", Connection)) + using var command = new SqliteCommand("SELECT * FROM objects WHERE hash = @hash LIMIT 1 ", Connection); + command.Parameters.AddWithValue("@hash", id); + using var reader = command.ExecuteReader(); + if (reader.Read()) { - command.Parameters.AddWithValue("@hash", id); - using var reader = command.ExecuteReader(); - - while (reader.Read()) - { - return reader.GetString(1); - } + return reader.GetString(1); } + } + finally + { Elapsed += LoggingHelpers.GetElapsedTime(startTime, Stopwatch.GetTimestamp()); + _connectionLock.Release(); } return null; // pass on the duty of null checks to consumers } - public Task CopyObjectAndChildren( - string id, - ITransport targetTransport, - Action? onTotalChildrenCountKnown = null - ) + public async Task CopyObjectAndChildren(string id, ITransport targetTransport) { - string res = TransportHelpers.CopyObjectAndChildrenSync( - id, - this, - targetTransport, - onTotalChildrenCountKnown, - CancellationToken - ); - return Task.FromResult(res); + string res = await TransportHelpers + .CopyObjectAndChildrenAsync(id, this, targetTransport, CancellationToken) + .ConfigureAwait(false); + return res; } #endregion diff --git a/src/Speckle.Sdk/Transports/SQLiteTransport2.cs b/src/Speckle.Sdk/Transports/SQLiteTransport2.cs new file mode 100644 index 00000000..4dbaccd8 --- /dev/null +++ b/src/Speckle.Sdk/Transports/SQLiteTransport2.cs @@ -0,0 +1,408 @@ +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Text; +using System.Timers; +using Microsoft.Data.Sqlite; +using Speckle.Sdk.Logging; +using Speckle.Sdk.Models; +using Speckle.Sdk.Serialisation.Utilities; +using Timer = System.Timers.Timer; + +namespace Speckle.Sdk.Transports; + +public sealed class SQLiteTransport2 : IDisposable, ICloneable, ITransport, IBlobCapableTransport +{ + private readonly string _streamId; + private bool _isWriting; + private const int MAX_TRANSACTION_SIZE = 1000; + private const int POLL_INTERVAL = 500; + + private ConcurrentQueue<(string id, string serializedObject, int byteCount)> _queue = new(); + + /// + /// Timer that ensures queue is consumed if less than MAX_TRANSACTION_SIZE objects are being sent. + /// + private readonly Timer _writeTimer; + + public SQLiteTransport2(string streamId) + { + _streamId = streamId; + + _rootPath = SqlitePaths.GetDBPath(streamId); + + _connectionString = $"Data Source={_rootPath};"; + + Initialize(); + + _writeTimer = new Timer + { + AutoReset = true, + Enabled = false, + Interval = POLL_INTERVAL, + }; + _writeTimer.Elapsed += WriteTimerElapsed; + } + + private readonly string _rootPath; + + private readonly string _connectionString; + + private SqliteConnection Connection { get; set; } + private readonly SemaphoreSlim _connectionLock = new(1, 1); + + public string BlobStorageFolder => SqlitePaths.BlobStorageFolder; + + public void SaveBlob(Blob obj) + { + var blobPath = obj.originalPath; + var targetPath = obj.GetLocalDestinationPath(BlobStorageFolder); + File.Copy(blobPath, targetPath, true); + } + + public object Clone() + { + return new SQLiteTransport2(_streamId) + { + OnProgressAction = OnProgressAction, + CancellationToken = CancellationToken, + }; + } + + public void Dispose() + { + // TODO: Check if it's still writing? + Connection.Close(); + Connection.Dispose(); + _writeTimer.Dispose(); + _connectionLock.Dispose(); + } + + public string TransportName { get; set; } = "SQLite"; + + public Dictionary TransportContext => + new() + { + { "name", TransportName }, + { "type", GetType().Name }, + { "streamId", _streamId }, + { "blobStorageFolder", BlobStorageFolder }, + }; + + public CancellationToken CancellationToken { get; set; } + + public IProgress? OnProgressAction { get; set; } + + public int SavedObjectCount { get; private set; } + + public TimeSpan Elapsed { get; private set; } + + public void BeginWrite() + { + _queue = new(); + SavedObjectCount = 0; + } + + public void EndWrite() { } + + public Task> HasObjects(IReadOnlyList objectIds) + { + Dictionary ret = new(objectIds.Count); + // Initialize with false so that canceled queries still return a dictionary item for every object id + foreach (string objectId in objectIds) + { + ret[objectId] = false; + } + + try + { + const string COMMAND_TEXT = "SELECT 1 FROM objects WHERE hash = @hash LIMIT 1 "; + using var command = new SqliteCommand(COMMAND_TEXT, Connection); + + foreach (string objectId in objectIds) + { + CancellationToken.ThrowIfCancellationRequested(); + + command.Parameters.Clear(); + command.Parameters.AddWithValue("@hash", objectId); + + using var reader = command.ExecuteReader(); + bool rowFound = reader.Read(); + ret[objectId] = rowFound; + } + } + catch (SqliteException ex) + { + throw new TransportException("SQLite transport failed", ex); + } + + return Task.FromResult(ret); + } + + /// Failed to initialize connection to the SQLite DB + private void Initialize() + { + // NOTE: used for creating partioned object tables. + //string[] HexChars = new string[] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" }; + //var cart = new List(); + //foreach (var str in HexChars) + // foreach (var str2 in HexChars) + // cart.Add(str + str2); + + using (var c = new SqliteConnection(_connectionString)) + { + c.Open(); + const string COMMAND_TEXT = + @" + CREATE TABLE IF NOT EXISTS objects( + hash TEXT PRIMARY KEY, + content TEXT + ) WITHOUT ROWID; + "; + using (var command = new SqliteCommand(COMMAND_TEXT, c)) + { + command.ExecuteNonQuery(); + } + + // Insert Optimisations + + using SqliteCommand cmd0 = new("PRAGMA journal_mode='wal';", c); + cmd0.ExecuteNonQuery(); + + //Note / Hack: This setting has the potential to corrupt the db. + //cmd = new SqliteCommand("PRAGMA synchronous=OFF;", Connection); + //cmd.ExecuteNonQuery(); + + using SqliteCommand cmd1 = new("PRAGMA count_changes=OFF;", c); + cmd1.ExecuteNonQuery(); + + using SqliteCommand cmd2 = new("PRAGMA temp_store=MEMORY;", c); + cmd2.ExecuteNonQuery(); + } + + Connection = new SqliteConnection(_connectionString); + Connection.Open(); + } + + /// + /// Returns all the objects in the store. Note: do not use for large collections. + /// + /// + /// This function uses a separate so is safe to call concurrently (unlike most other transport functions) + internal IEnumerable GetAllObjects() + { + CancellationToken.ThrowIfCancellationRequested(); + + using SqliteConnection connection = new(_connectionString); + connection.Open(); + + using var command = new SqliteCommand("SELECT * FROM objects", connection); + + using var reader = command.ExecuteReader(); + while (reader.Read()) + { + CancellationToken.ThrowIfCancellationRequested(); + yield return reader.GetString(1); + } + } + + /// + /// Deletes an object. Note: do not use for any speckle object transport, as it will corrupt the database. + /// + /// + public void DeleteObject(string hash) + { + CancellationToken.ThrowIfCancellationRequested(); + + using var command = new SqliteCommand("DELETE FROM objects WHERE hash = @hash", Connection); + command.Parameters.AddWithValue("@hash", hash); + command.ExecuteNonQuery(); + } + + /// + /// Updates an object. + /// + /// + /// + public void UpdateObject(string hash, string serializedObject) + { + CancellationToken.ThrowIfCancellationRequested(); + + using var c = new SqliteConnection(_connectionString); + c.Open(); + const string COMMAND_TEXT = "REPLACE INTO objects(hash, content) VALUES(@hash, @content)"; + using var command = new SqliteCommand(COMMAND_TEXT, c); + command.Parameters.AddWithValue("@hash", hash); + command.Parameters.AddWithValue("@content", serializedObject); + command.ExecuteNonQuery(); + } + + public override string ToString() + { + return $"Sqlite Transport @{_rootPath}"; + } + + #region Writes + + /// + /// Awaits untill write completion (ie, the current queue is fully consumed). + /// + /// + public async Task WriteComplete() => + await Utilities.WaitUntil(() => WriteCompletionStatus, 500).ConfigureAwait(false); + + /// + /// Returns true if the current write queue is empty and comitted. + /// + /// + public bool WriteCompletionStatus => _queue.IsEmpty && !_isWriting; + + private void WriteTimerElapsed(object? sender, ElapsedEventArgs e) + { + _writeTimer.Enabled = false; + + if (CancellationToken.IsCancellationRequested) + { + _queue = new ConcurrentQueue<(string, string, int)>(); + return; + } + + if (!_isWriting && !_queue.IsEmpty) + { + ConsumeQueue(); + } + } + + private void ConsumeQueue() + { + var stopwatch = Stopwatch.StartNew(); + _isWriting = true; + try + { + CancellationToken.ThrowIfCancellationRequested(); + + var i = 0; //BUG: This never gets incremented! + + var saved = 0; + + using (var c = new SqliteConnection(_connectionString)) + { + c.Open(); + using var t = c.BeginTransaction(); + const string COMMAND_TEXT = "INSERT OR IGNORE INTO objects(hash, content) VALUES(@hash, @content)"; + + while (i < MAX_TRANSACTION_SIZE && _queue.TryPeek(out var result)) + { + using var command = new SqliteCommand(COMMAND_TEXT, c, t); + _queue.TryDequeue(out result); + command.Parameters.AddWithValue("@hash", result.id); + command.Parameters.AddWithValue("@content", result.serializedObject); + command.ExecuteNonQuery(); + + saved++; + } + + t.Commit(); + CancellationToken.ThrowIfCancellationRequested(); + } + + CancellationToken.ThrowIfCancellationRequested(); + + if (!_queue.IsEmpty) + { + ConsumeQueue(); + } + } + catch (SqliteException ex) + { + throw new TransportException(this, "SQLite Command Failed", ex); + } + catch (OperationCanceledException) + { + _queue = new(); + } + finally + { + stopwatch.Stop(); + Elapsed += stopwatch.Elapsed; + _isWriting = false; + } + } + + /// + /// Adds an object to the saving queue. + /// + /// + /// + public void SaveObject(string id, string serializedObject) + { + CancellationToken.ThrowIfCancellationRequested(); + _queue.Enqueue((id, serializedObject, Encoding.UTF8.GetByteCount(serializedObject))); + + _writeTimer.Enabled = true; + _writeTimer.Start(); + } + + /// + /// Directly saves the object in the db. + /// + /// + /// + public void SaveObjectSync(string hash, string serializedObject) + { + const string COMMAND_TEXT = "INSERT OR IGNORE INTO objects(hash, content) VALUES(@hash, @content)"; + + try + { + using var command = new SqliteCommand(COMMAND_TEXT, Connection); + command.Parameters.AddWithValue("@hash", hash); + command.Parameters.AddWithValue("@content", serializedObject); + command.ExecuteNonQuery(); + } + catch (SqliteException ex) + { + throw new TransportException(this, "SQLite Command Failed", ex); + } + } + + #endregion + + #region Reads + + /// + /// Gets an object. + /// + /// + /// + public async Task GetObject(string id) + { + CancellationToken.ThrowIfCancellationRequested(); + await _connectionLock.WaitAsync(CancellationToken).ConfigureAwait(false); + var startTime = Stopwatch.GetTimestamp(); + try + { + using var command = new SqliteCommand("SELECT * FROM objects WHERE hash = @hash LIMIT 1 ", Connection); + command.Parameters.AddWithValue("@hash", id); + using var reader = command.ExecuteReader(); + if (reader.Read()) + { + return reader.GetString(1); + } + } + finally + { + Elapsed += LoggingHelpers.GetElapsedTime(startTime, Stopwatch.GetTimestamp()); + _connectionLock.Release(); + } + return null; // pass on the duty of null checks to consumers + } + + public async Task CopyObjectAndChildren(string id, ITransport targetTransport) + { + string res = await TransportHelpers + .CopyObjectAndChildrenAsync(id, this, targetTransport, CancellationToken) + .ConfigureAwait(false); + return res; + } + + #endregion +} diff --git a/src/Speckle.Sdk/Transports/ServerV2.cs b/src/Speckle.Sdk/Transports/ServerTransport.cs similarity index 81% rename from src/Speckle.Sdk/Transports/ServerV2.cs rename to src/Speckle.Sdk/Transports/ServerTransport.cs index 6e0f23ff..7ca7536c 100644 --- a/src/Speckle.Sdk/Transports/ServerV2.cs +++ b/src/Speckle.Sdk/Transports/ServerTransport.cs @@ -1,15 +1,18 @@ using System.Diagnostics; -using Speckle.Newtonsoft.Json.Linq; using Speckle.Sdk.Common; using Speckle.Sdk.Credentials; +using Speckle.Sdk.Helpers; using Speckle.Sdk.Logging; using Speckle.Sdk.Models; +using Speckle.Sdk.Serialisation.Utilities; using Speckle.Sdk.Transports.ServerUtils; namespace Speckle.Sdk.Transports; public sealed class ServerTransport : IServerTransport { + private readonly ISpeckleHttp _http; + private readonly ISdkActivityFactory _activityFactory; private readonly object _elapsedLock = new(); private Exception? _exception; @@ -28,20 +31,30 @@ public sealed class ServerTransport : IServerTransport /// /// Defaults to /// was not formatted as valid stream id - public ServerTransport(Account account, string streamId, int timeoutSeconds = 60, string? blobStorageFolder = null) + public ServerTransport( + ISpeckleHttp http, + ISdkActivityFactory activityFactory, + Account account, + string streamId, + int timeoutSeconds = 60, + string? blobStorageFolder = null + ) { if (string.IsNullOrWhiteSpace(streamId)) { throw new ArgumentException($"{streamId} is not a valid id", streamId); } + _http = http; + _activityFactory = activityFactory; + Account = account; BaseUri = new(account.serverInfo.url); StreamId = streamId; AuthorizationToken = account.token; TimeoutSeconds = timeoutSeconds; BlobStorageFolder = blobStorageFolder ?? SpecklePathProvider.BlobStoragePath(); - Initialize(account.serverInfo.url); + Api = new ParallelServerApi(http, activityFactory, BaseUri, AuthorizationToken, BlobStorageFolder, TimeoutSeconds); Directory.CreateDirectory(BlobStorageFolder); } @@ -74,7 +87,7 @@ public sealed class ServerTransport : IServerTransport public object Clone() { - return new ServerTransport(Account, StreamId, TimeoutSeconds, BlobStorageFolder) + return new ServerTransport(_http, _activityFactory, Account, StreamId, TimeoutSeconds, BlobStorageFolder) { OnProgressAction = OnProgressAction, CancellationToken = CancellationToken, @@ -100,18 +113,14 @@ public sealed class ServerTransport : IServerTransport { "type", GetType().Name }, { "streamId", StreamId }, { "serverUrl", BaseUri }, - { "blobStorageFolder", BlobStorageFolder } + { "blobStorageFolder", BlobStorageFolder }, }; public CancellationToken CancellationToken { get; set; } - public Action? OnProgressAction { get; set; } + public IProgress? OnProgressAction { get; set; } public TimeSpan Elapsed { get; private set; } = TimeSpan.Zero; - public async Task CopyObjectAndChildren( - string id, - ITransport targetTransport, - Action? onTotalChildrenCountKnown = null - ) + public async Task CopyObjectAndChildren(string id, ITransport targetTransport) { if (string.IsNullOrEmpty(id)) { @@ -120,25 +129,30 @@ public sealed class ServerTransport : IServerTransport CancellationToken.ThrowIfCancellationRequested(); - using ParallelServerApi api = new(BaseUri, AuthorizationToken, BlobStorageFolder, TimeoutSeconds); + using ParallelServerApi api = new( + _http, + _activityFactory, + BaseUri, + AuthorizationToken, + BlobStorageFolder, + TimeoutSeconds + ); var stopwatch = Stopwatch.StartNew(); api.CancellationToken = CancellationToken; string? rootObjectJson = await api.DownloadSingleObject(StreamId, id, OnProgressAction).ConfigureAwait(false); - IList allIds = ParseChildrenIds(rootObjectJson.NotNull()); + var allIds = ClosureParser.GetChildrenIds(rootObjectJson.NotNull(), CancellationToken).ToList(); - List childrenIds = allIds.Where(x => !x.Contains("blob:")).ToList(); - List blobIds = allIds.Where(x => x.Contains("blob:")).Select(x => x.Remove(0, 5)).ToList(); - - onTotalChildrenCountKnown?.Invoke(allIds.Count); + var childrenIds = allIds.Where(x => !x.Contains("blob:")); + var blobIds = allIds.Where(x => x.Contains("blob:")).Select(x => x.Remove(0, 5)); // // Objects download // // Check which children are not already in the local transport - var childrenFoundMap = await targetTransport.HasObjects(childrenIds).ConfigureAwait(false); + var childrenFoundMap = await targetTransport.HasObjects(childrenIds.ToList()).ConfigureAwait(false); List newChildrenIds = new(from objId in childrenFoundMap.Keys where !childrenFoundMap[objId] select objId); targetTransport.BeginWrite(); @@ -171,11 +185,11 @@ public sealed class ServerTransport : IServerTransport .GetFiles(BlobStorageFolder) .Select(fileName => fileName.Split(Path.DirectorySeparatorChar).Last()) .Where(fileName => fileName.Length > 10) - .Select(fileName => fileName.Substring(0, Blob.LocalHashPrefixLength)) + .Select(fileName => fileName[..Blob.LocalHashPrefixLength]) .ToList(); var newBlobIds = blobIds - .Where(blobId => !localBlobTrimmedHashes.Contains(blobId.Substring(0, Blob.LocalHashPrefixLength))) + .Where(blobId => !localBlobTrimmedHashes.Contains(blobId[..Blob.LocalHashPrefixLength])) .ToList(); await api.DownloadBlobs(StreamId, newBlobIds, OnProgressAction).ConfigureAwait(false); @@ -185,14 +199,14 @@ public sealed class ServerTransport : IServerTransport return rootObjectJson; } - public string GetObject(string id) + public async Task GetObject(string id) { CancellationToken.ThrowIfCancellationRequested(); var stopwatch = Stopwatch.StartNew(); - var result = Api.DownloadSingleObject(StreamId, id, OnProgressAction).Result; + var result = await Api.DownloadSingleObject(StreamId, id, OnProgressAction).ConfigureAwait(false); stopwatch.Stop(); Elapsed += stopwatch.Elapsed; - return result.NotNull(); + return result; } public async Task> HasObjects(IReadOnlyList objectIds) @@ -262,37 +276,11 @@ public sealed class ServerTransport : IServerTransport _sendingThread = null; } - private void Initialize(string baseUri) - { - SpeckleLog.Logger.Information("Initializing a new Remote Transport for {baseUri}", baseUri); - - Api = new ParallelServerApi(BaseUri, AuthorizationToken, BlobStorageFolder, TimeoutSeconds); - } - public override string ToString() { return $"Server Transport @{Account.serverInfo.url}"; } - private static IList ParseChildrenIds(string json) - { - List childrenIds = new(); - - JObject doc1 = JObject.Parse(json); - JToken? closures = doc1["__closure"]; - if (closures == null) - { - return Array.Empty(); - } - - foreach (JToken prop in closures) - { - childrenIds.Add(((JProperty)prop).Name); - } - - return childrenIds; - } - private async void SendingThreadMain() { while (true) diff --git a/src/Speckle.Sdk/Transports/ServerTransportFactory.cs b/src/Speckle.Sdk/Transports/ServerTransportFactory.cs index d67da531..0710cfe6 100644 --- a/src/Speckle.Sdk/Transports/ServerTransportFactory.cs +++ b/src/Speckle.Sdk/Transports/ServerTransportFactory.cs @@ -1,17 +1,19 @@ using System.Diagnostics.CodeAnalysis; using Speckle.InterfaceGenerator; using Speckle.Sdk.Credentials; +using Speckle.Sdk.Helpers; +using Speckle.Sdk.Logging; namespace Speckle.Sdk.Transports; [GenerateAutoInterface] [ExcludeFromCodeCoverage] //factories don't need coverage -public class ServerTransportFactory : IServerTransportFactory +public class ServerTransportFactory(ISpeckleHttp http, ISdkActivityFactory activityFactory) : IServerTransportFactory { - public IServerTransport Create( + public ServerTransport Create( Account account, string streamId, int timeoutSeconds = 60, string? blobStorageFolder = null - ) => new ServerTransport(account, streamId, timeoutSeconds, blobStorageFolder); + ) => new ServerTransport(http, activityFactory, account, streamId, timeoutSeconds, blobStorageFolder); } diff --git a/src/Speckle.Sdk/Transports/ServerUtils/IServerApi.cs b/src/Speckle.Sdk/Transports/ServerUtils/IServerApi.cs index 4ea9dd47..0fa65d33 100644 --- a/src/Speckle.Sdk/Transports/ServerUtils/IServerApi.cs +++ b/src/Speckle.Sdk/Transports/ServerUtils/IServerApi.cs @@ -4,12 +4,12 @@ public delegate void CbObjectDownloaded(string id, string json); internal interface IServerApi { - public Task DownloadSingleObject(string streamId, string objectId, Action? progress); + public Task DownloadSingleObject(string streamId, string objectId, IProgress? progress); public Task DownloadObjects( string streamId, IReadOnlyList objectIds, - Action? progress, + IProgress? progress, CbObjectDownloaded onObjectCallback ); @@ -18,14 +18,14 @@ internal interface IServerApi public Task UploadObjects( string streamId, IReadOnlyList<(string id, string data)> objects, - Action? progress + IProgress? progress ); public Task UploadBlobs( string streamId, IReadOnlyList<(string id, string data)> objects, - Action? progress + IProgress? progress ); - public Task DownloadBlobs(string streamId, IReadOnlyList blobIds, Action? progress); + public Task DownloadBlobs(string streamId, IReadOnlyList blobIds, IProgress? progress); } diff --git a/src/Speckle.Sdk/Transports/ServerUtils/ParallelServerAPI.cs b/src/Speckle.Sdk/Transports/ServerUtils/ParallelServerAPI.cs index b2772553..7e048b40 100644 --- a/src/Speckle.Sdk/Transports/ServerUtils/ParallelServerAPI.cs +++ b/src/Speckle.Sdk/Transports/ServerUtils/ParallelServerAPI.cs @@ -23,11 +23,15 @@ internal class ParallelServerApi : ParallelOperationExecutor { private readonly string _authToken; + private readonly ISpeckleHttp _http; + private readonly ISdkActivityFactory _activityFactory; private readonly Uri _baseUri; private readonly int _timeoutSeconds; public ParallelServerApi( + ISpeckleHttp http, + ISdkActivityFactory activityFactory, Uri baseUri, string authorizationToken, string blobStorageFolder, @@ -36,6 +40,8 @@ internal class ParallelServerApi : ParallelOperationExecutor int numBufferedOperations = 8 ) { + _http = http; + _activityFactory = activityFactory; _baseUri = baseUri; _authToken = authorizationToken; _timeoutSeconds = timeoutSeconds; @@ -91,7 +97,7 @@ internal class ParallelServerApi : ParallelOperationExecutor return ret; } - public async Task DownloadSingleObject(string streamId, string objectId, Action? progress) + public async Task DownloadSingleObject(string streamId, string objectId, IProgress? progress) { EnsureStarted(); Task op = QueueOperation(ServerApiOperation.DownloadSingleObject, (streamId, objectId, progress)); @@ -102,7 +108,7 @@ internal class ParallelServerApi : ParallelOperationExecutor public async Task DownloadObjects( string streamId, IReadOnlyList objectIds, - Action? progress, + IProgress? progress, CbObjectDownloaded onObjectCallback ) { @@ -138,7 +144,7 @@ internal class ParallelServerApi : ParallelOperationExecutor public async Task UploadObjects( string streamId, IReadOnlyList<(string, string)> objects, - Action? progress + IProgress? progress ) { EnsureStarted(); @@ -171,14 +177,18 @@ internal class ParallelServerApi : ParallelOperationExecutor await Task.WhenAll(tasks.ToArray()).ConfigureAwait(false); } - public async Task UploadBlobs(string streamId, IReadOnlyList<(string, string)> blobs, Action? progress) + public async Task UploadBlobs( + string streamId, + IReadOnlyList<(string, string)> blobs, + IProgress? progress + ) { EnsureStarted(); var op = QueueOperation(ServerApiOperation.UploadBlobs, (streamId, blobs, progress)); await op.ConfigureAwait(false); } - public async Task DownloadBlobs(string streamId, IReadOnlyList blobIds, Action? progress) + public async Task DownloadBlobs(string streamId, IReadOnlyList blobIds, IProgress? progress) { EnsureStarted(); var op = QueueOperation(ServerApiOperation.DownloadBlobs, (streamId, blobIds, progress)); @@ -206,13 +216,19 @@ internal class ParallelServerApi : ParallelOperationExecutor protected override void ThreadMain() { - using ServerApi serialApi = new(_baseUri, _authToken, BlobStorageFolder, _timeoutSeconds); + using ServerApi serialApi = new(_http, _activityFactory, _baseUri, _authToken, BlobStorageFolder, _timeoutSeconds); serialApi.CancellationToken = CancellationToken; serialApi.CompressPayloads = CompressPayloads; while (true) { + if (IsDisposed) + { + return; + } + var (operation, inputValue, tcs) = Tasks.Take(); + if (operation == ServerApiOperation.NoOp || tcs == null) { return; @@ -240,13 +256,13 @@ internal class ParallelServerApi : ParallelOperationExecutor switch (operation) { case ServerApiOperation.DownloadSingleObject: - var (dsoStreamId, dsoObjectId, progress) = ((string, string, Action?))inputValue; + var (dsoStreamId, dsoObjectId, progress) = ((string, string, IProgress?))inputValue; return await serialApi.DownloadSingleObject(dsoStreamId, dsoObjectId, progress).ConfigureAwait(false); case ServerApiOperation.DownloadObjects: var (doStreamId, doObjectIds, progress2, doCallback) = (( string, IReadOnlyList, - Action?, + IProgress?, CbObjectDownloaded ))inputValue; await serialApi.DownloadObjects(doStreamId, doObjectIds, progress2, doCallback).ConfigureAwait(false); @@ -258,7 +274,7 @@ internal class ParallelServerApi : ParallelOperationExecutor var (uoStreamId, uoObjects, progress3) = (( string, IReadOnlyList<(string, string)>, - Action? + IProgress? ))inputValue; await serialApi.UploadObjects(uoStreamId, uoObjects, progress3).ConfigureAwait(false); return null; @@ -266,7 +282,7 @@ internal class ParallelServerApi : ParallelOperationExecutor var (ubStreamId, ubBlobs, progress4) = (( string, IReadOnlyList<(string, string)>, - Action? + IProgress? ))inputValue; await serialApi.UploadBlobs(ubStreamId, ubBlobs, progress4).ConfigureAwait(false); return null; @@ -276,7 +292,7 @@ internal class ParallelServerApi : ParallelOperationExecutor .HasBlobs(hbStreamId, hBlobs.Select(b => b.Item1.Split(':')[1]).ToList()) .ConfigureAwait(false); case ServerApiOperation.DownloadBlobs: - var (dbStreamId, blobIds, progress5) = ((string, IReadOnlyList, Action?))inputValue; + var (dbStreamId, blobIds, progress5) = ((string, IReadOnlyList, IProgress?))inputValue; await serialApi.DownloadBlobs(dbStreamId, blobIds, progress5).ConfigureAwait(false); return null; default: diff --git a/src/Speckle.Sdk/Transports/ServerUtils/ProgressContent.cs b/src/Speckle.Sdk/Transports/ServerUtils/ProgressContent.cs index eddd09d3..e5e7bd4c 100644 --- a/src/Speckle.Sdk/Transports/ServerUtils/ProgressContent.cs +++ b/src/Speckle.Sdk/Transports/ServerUtils/ProgressContent.cs @@ -5,9 +5,9 @@ namespace Speckle.Sdk.Transports.ServerUtils; internal class ProgressContent : HttpContent { private readonly HttpContent _innerContent; - private readonly Action? _progress; + private readonly IProgress? _progress; - public ProgressContent(HttpContent innerContent, Action? progress) + public ProgressContent(HttpContent innerContent, IProgress? progress) { _innerContent = innerContent; _progress = progress; @@ -15,7 +15,7 @@ internal class ProgressContent : HttpContent innerContent.Headers.CopyTo(Headers); } - protected override Task SerializeToStreamAsync(Stream stream, TransportContext context) + protected override Task SerializeToStreamAsync(Stream stream, TransportContext? context) { ProgressStream progressStream = new(stream, _innerContent.Headers.ContentLength, _progress, false); return _innerContent.CopyToAsync(progressStream); diff --git a/src/Speckle.Sdk/Transports/ServerUtils/ProgressStream.cs b/src/Speckle.Sdk/Transports/ServerUtils/ProgressStream.cs index 25591dbf..ac15cb86 100644 --- a/src/Speckle.Sdk/Transports/ServerUtils/ProgressStream.cs +++ b/src/Speckle.Sdk/Transports/ServerUtils/ProgressStream.cs @@ -1,6 +1,11 @@ namespace Speckle.Sdk.Transports; -internal class ProgressStream(Stream input, long? streamLength, Action? progress, bool useBuffer) : Stream +internal sealed class ProgressStream( + Stream input, + long? streamLength, + IProgress? progress, + bool useBuffer +) : Stream { private long _position; private readonly Stream _stream = useBuffer ? new BufferedStream(input, 80 * 1024) : input; @@ -15,7 +20,7 @@ internal class ProgressStream(Stream input, long? streamLength, Action true; @@ -35,4 +40,10 @@ internal class ProgressStream(Stream input, long? streamLength, Action _position; set => throw new NotImplementedException(); } + + protected override void Dispose(bool disposed) + { + _stream.Dispose(); + base.Dispose(disposed); + } } diff --git a/src/Speckle.Sdk/Transports/ServerUtils/ServerAPI.cs b/src/Speckle.Sdk/Transports/ServerUtils/ServerAPI.cs index ac1cb36a..68ac2a74 100644 --- a/src/Speckle.Sdk/Transports/ServerUtils/ServerAPI.cs +++ b/src/Speckle.Sdk/Transports/ServerUtils/ServerAPI.cs @@ -12,6 +12,7 @@ namespace Speckle.Sdk.Transports.ServerUtils; public sealed class ServerApi : IDisposable, IServerApi { + private readonly ISdkActivityFactory _activityFactory; private const int BATCH_SIZE_GET_OBJECTS = 10000; private const int BATCH_SIZE_HAS_OBJECTS = 100000; @@ -26,21 +27,26 @@ public sealed class ServerApi : IDisposable, IServerApi private readonly HttpClient _client; - public ServerApi(Uri baseUri, string? authorizationToken, string blobStorageFolder, int timeoutSeconds = 120) + public ServerApi( + ISpeckleHttp speckleHttp, + ISdkActivityFactory activityFactory, + Uri baseUri, + string? authorizationToken, + string blobStorageFolder, + int timeoutSeconds = 120 + ) { + _activityFactory = activityFactory; CancellationToken = CancellationToken.None; BlobStorageFolder = blobStorageFolder; - _client = Http.GetHttpProxyClient( - new SpeckleHttpClientHandler( - new HttpClientHandler { AutomaticDecompression = DecompressionMethods.GZip }, - Http.HttpAsyncPolicy(timeoutSeconds: timeoutSeconds) - ) + _client = speckleHttp.CreateHttpClient( + new HttpClientHandler { AutomaticDecompression = DecompressionMethods.GZip }, + timeoutSeconds: timeoutSeconds, + authorizationToken: authorizationToken ); _client.BaseAddress = baseUri; - - Http.AddAuthHeader(_client, authorizationToken); } public CancellationToken CancellationToken { get; set; } @@ -53,19 +59,19 @@ public sealed class ServerApi : IDisposable, IServerApi _client.Dispose(); } - public async Task DownloadSingleObject(string streamId, string objectId, Action? progress) + public async Task DownloadSingleObject(string streamId, string objectId, IProgress? progress) { - using var _ = SpeckleActivityFactory.Start(); + using var _ = _activityFactory.Start(); CancellationToken.ThrowIfCancellationRequested(); // Get root object using var rootHttpMessage = new HttpRequestMessage { RequestUri = new Uri($"/objects/{streamId}/{objectId}/single", UriKind.Relative), - Method = HttpMethod.Get + Method = HttpMethod.Get, }; - HttpResponseMessage rootHttpResponse = await _client + var rootHttpResponse = await _client .SendAsync(rootHttpMessage, HttpCompletionOption.ResponseContentRead, CancellationToken) .ConfigureAwait(false); @@ -77,7 +83,7 @@ public sealed class ServerApi : IDisposable, IServerApi public async Task DownloadObjects( string streamId, IReadOnlyList objectIds, - Action? progress, + IProgress? progress, CbObjectDownloaded onObjectCallback ) { @@ -85,7 +91,7 @@ public sealed class ServerApi : IDisposable, IServerApi { return; } - using var _ = SpeckleActivityFactory.Start(); + using var _ = _activityFactory.Start(); if (objectIds.Count < BATCH_SIZE_GET_OBJECTS) { @@ -143,7 +149,7 @@ public sealed class ServerApi : IDisposable, IServerApi public async Task UploadObjects( string streamId, IReadOnlyList<(string, string)> objects, - Action? progress + IProgress? progress ) { if (objects.Count == 0) @@ -213,7 +219,7 @@ public sealed class ServerApi : IDisposable, IServerApi public async Task UploadBlobs( string streamId, IReadOnlyList<(string, string)> objects, - Action? progress + IProgress? progress ) { CancellationToken.ThrowIfCancellationRequested(); @@ -235,16 +241,14 @@ public sealed class ServerApi : IDisposable, IServerApi multipartFormDataContent.Add(fsc, $"hash:{hash}", fileName); } - using var message = new HttpRequestMessage - { - RequestUri = new Uri($"/api/stream/{streamId}/blob", UriKind.Relative), - Method = HttpMethod.Post, - Content = new ProgressContent(multipartFormDataContent, progress) - }; + using var message = new HttpRequestMessage(); + message.RequestUri = new Uri($"/api/stream/{streamId}/blob", UriKind.Relative); + message.Method = HttpMethod.Post; + message.Content = new ProgressContent(multipartFormDataContent, progress); try { - HttpResponseMessage response = await _client.SendAsync(message, CancellationToken).ConfigureAwait(false); + var response = await _client.SendAsync(message, CancellationToken).ConfigureAwait(false); response.EnsureSuccessStatusCode(); @@ -262,30 +266,25 @@ public sealed class ServerApi : IDisposable, IServerApi } } - public async Task DownloadBlobs(string streamId, IReadOnlyList blobIds, Action? progress) + public async Task DownloadBlobs(string streamId, IReadOnlyList blobIds, IProgress? progress) { foreach (var blobId in blobIds) { try { - using var blobMessage = new HttpRequestMessage - { - RequestUri = new Uri($"api/stream/{streamId}/blob/{blobId}", UriKind.Relative), - Method = HttpMethod.Get - }; + using var blobMessage = new HttpRequestMessage(); + blobMessage.RequestUri = new Uri($"api/stream/{streamId}/blob/{blobId}", UriKind.Relative); + blobMessage.Method = HttpMethod.Get; using var response = await _client.SendAsync(blobMessage, CancellationToken).ConfigureAwait(false); response.Content.Headers.TryGetValues("Content-Disposition", out IEnumerable? cdHeaderValues); - var cdHeader = cdHeaderValues.First(); - var fileName = cdHeader.Split(s_filenameSeparator, StringSplitOptions.None)[1].TrimStart('"').TrimEnd('"'); + var cdHeader = cdHeaderValues?.FirstOrDefault(); + string? fileName = cdHeader?.Split(s_filenameSeparator, StringSplitOptions.None)[1].TrimStart('"').TrimEnd('"'); - string fileLocation = Path.Combine( - BlobStorageFolder, - $"{blobId.Substring(0, Blob.LocalHashPrefixLength)}-{fileName}" - ); + string fileLocation = Path.Combine(BlobStorageFolder, $"{blobId[..Blob.LocalHashPrefixLength]}-{fileName}"); using var source = new ProgressStream( - await response.Content.ReadAsStreamAsync(), + await response.Content.ReadAsStreamAsync().ConfigureAwait(false), response.Content.Headers.ContentLength, progress, true @@ -303,7 +302,7 @@ public sealed class ServerApi : IDisposable, IServerApi private async Task DownloadObjectsImpl( string streamId, IReadOnlyList objectIds, - Action? progress, + IProgress? progress, CbObjectDownloaded onObjectCallback ) { @@ -314,7 +313,7 @@ public sealed class ServerApi : IDisposable, IServerApi using var childrenHttpMessage = new HttpRequestMessage { RequestUri = new Uri($"/api/getobjects/{streamId}", UriKind.Relative), - Method = HttpMethod.Post + Method = HttpMethod.Post, }; Dictionary postParameters = new() { { "objects", JsonConvert.SerializeObject(objectIds) } }; @@ -331,7 +330,7 @@ public sealed class ServerApi : IDisposable, IServerApi private async Task ResponseProgress( HttpResponseMessage childrenHttpResponse, - Action? progress, + IProgress? progress, CbObjectDownloaded onObjectCallback, bool isSingle ) @@ -370,7 +369,7 @@ public sealed class ServerApi : IDisposable, IServerApi var uri = new Uri($"/api/diff/{streamId}", UriKind.Relative); using StringContent stringContent = new(serializedPayload, Encoding.UTF8, "application/json"); - HttpResponseMessage response = await _client.PostAsync(uri, stringContent, CancellationToken).ConfigureAwait(false); + var response = await _client.PostAsync(uri, stringContent, CancellationToken).ConfigureAwait(false); response.EnsureSuccessStatusCode(); @@ -382,22 +381,22 @@ public sealed class ServerApi : IDisposable, IServerApi { hasObjects[prop.Key] = (bool)prop.Value.NotNull(); } - - // Console.WriteLine($"ServerApi::HasObjects({objectIds.Count}) request in {sw.ElapsedMilliseconds / 1000.0} sec"); - return hasObjects; } private async Task UploadObjectsImpl( string streamId, List> multipartedObjects, - Action? progress + IProgress? progress ) { CancellationToken.ThrowIfCancellationRequested(); - using HttpRequestMessage message = - new() { RequestUri = new Uri($"/objects/{streamId}", UriKind.Relative), Method = HttpMethod.Post }; + using HttpRequestMessage message = new() + { + RequestUri = new Uri($"/objects/{streamId}", UriKind.Relative), + Method = HttpMethod.Post, + }; MultipartFormDataContent multipart = new(); @@ -431,7 +430,7 @@ public sealed class ServerApi : IDisposable, IServerApi } } message.Content = new ProgressContent(multipart, progress); - HttpResponseMessage response = await _client.SendAsync(message, CancellationToken).ConfigureAwait(false); + var response = await _client.SendAsync(message, CancellationToken).ConfigureAwait(false); response.EnsureSuccessStatusCode(); } @@ -445,7 +444,7 @@ public sealed class ServerApi : IDisposable, IServerApi using StringContent stringContent = new(payload, Encoding.UTF8, "application/json"); - HttpResponseMessage response = await _client.PostAsync(uri, stringContent, CancellationToken).ConfigureAwait(false); + var response = await _client.PostAsync(uri, stringContent, CancellationToken).ConfigureAwait(false); response.EnsureSuccessStatusCode(); diff --git a/src/Speckle.Sdk/Transports/TransportHelpers.cs b/src/Speckle.Sdk/Transports/TransportHelpers.cs index 9dfdfda7..cdc52c98 100644 --- a/src/Speckle.Sdk/Transports/TransportHelpers.cs +++ b/src/Speckle.Sdk/Transports/TransportHelpers.cs @@ -1,16 +1,14 @@ using System.Diagnostics.CodeAnalysis; -using Speckle.Newtonsoft.Json; -using Speckle.Sdk.Serialisation; +using Speckle.Sdk.Serialisation.Utilities; namespace Speckle.Sdk.Transports; public static class TransportHelpers { - public static string CopyObjectAndChildrenSync( + public static async Task CopyObjectAndChildrenAsync( string id, ITransport sourceTransport, ITransport targetTransport, - Action? onTotalChildrenCountKnown, CancellationToken cancellationToken ) { @@ -21,7 +19,7 @@ public static class TransportHelpers cancellationToken.ThrowIfCancellationRequested(); - var parent = sourceTransport.GetObject(id); + var parent = await sourceTransport.GetObject(id).ConfigureAwait(false); if (parent is null) { throw new TransportException( @@ -31,49 +29,31 @@ public static class TransportHelpers targetTransport.SaveObject(id, parent); - var closures = GetClosureTable(parent); + var closures = ClosureParser.GetChildrenIds(parent, cancellationToken).ToList(); - onTotalChildrenCountKnown?.Invoke(closures?.Count ?? 0); - - if (closures is not null) + foreach (var closure in closures) { - int i = 0; - foreach (var kvp in closures) + cancellationToken.ThrowIfCancellationRequested(); + + //skips blobs because ServerTransport downloads things separately + if (closure.StartsWith("blob:")) { - cancellationToken.ThrowIfCancellationRequested(); - - var child = sourceTransport.GetObject(kvp.Key); - if (child is null) - { - throw new TransportException( - $"Closure id {kvp.Key} was not found within this transport {sourceTransport.TransportName}" - ); - } - - targetTransport.SaveObject(kvp.Key, child); - var count = i++; - sourceTransport.OnProgressAction?.Invoke(new ProgressArgs(ProgressEvent.UploadObject, count, closures.Count)); + continue; } + var child = await sourceTransport.GetObject(closure).ConfigureAwait(false); + if (child is null) + { + throw new TransportException( + $"Closure id {closure} was not found within this transport {sourceTransport.TransportName}" + ); + } + + targetTransport.SaveObject(closure, child); } return parent; } - /// The Json object - /// The closure table - /// Failed to deserialize the object into - internal static Dictionary? GetClosureTable(string objString) //TODO: Unit Test - { - var partial = JsonConvert.DeserializeObject(objString); - - if (partial is null) - { - throw new SpeckleDeserializeException($"Failed to deserialize {nameof(objString)} into {nameof(Placeholder)}"); - } - - return partial.__closure; - } - [SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Deserialization target for DTO")] internal sealed class Placeholder { diff --git a/src/Speckle.Sdk/packages.lock.json b/src/Speckle.Sdk/packages.lock.json index 7c9cef4d..919f5109 100644 --- a/src/Speckle.Sdk/packages.lock.json +++ b/src/Speckle.Sdk/packages.lock.json @@ -2,12 +2,6 @@ "version": 2, "dependencies": { ".NETStandard,Version=v2.0": { - "GitVersion.MsBuild": { - "type": "Direct", - "requested": "[5.12.0, )", - "resolved": "5.12.0", - "contentHash": "dJuigXycpJNOiLT9or7mkHSkGFHgGW3/p6cNNYEKZBa7Hhp1FdX/cvqYWWYhRLpfoZOedeA7aRbYiOB3vW/dvA==" - }, "GraphQL.Client": { "type": "Direct", "requested": "[6.0.0, )", @@ -19,6 +13,15 @@ "System.Reactive": "5.0.0" } }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Direct", + "requested": "[5.0.0, )", + "resolved": "5.0.0", + "contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, "Microsoft.CSharp": { "type": "Direct", "requested": "[4.7.0, )", @@ -27,14 +30,32 @@ }, "Microsoft.Data.Sqlite": { "type": "Direct", - "requested": "[7.0.7, )", - "resolved": "7.0.7", - "contentHash": "tiNmV1oPy+Z2R7Wd0bPB/FxCr8B+/5q11OpDMG751GA/YuOL7MZrBFfzv5oFRlFe08K6sjrnbrauzzGIeNrzLQ==", + "requested": "[7.0.5, )", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", "dependencies": { - "Microsoft.Data.Sqlite.Core": "7.0.7", + "Microsoft.Data.Sqlite.Core": "7.0.5", "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" } }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Direct", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Logging": { + "type": "Direct", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -54,38 +75,17 @@ "Microsoft.NETCore.Platforms": "1.1.0" } }, - "Polly": { - "type": "Direct", - "requested": "[7.2.3, )", - "resolved": "7.2.3", - "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" - }, - "Polly.Contrib.WaitAndRetry": { - "type": "Direct", - "requested": "[1.1.1, )", - "resolved": "1.1.1", - "contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA==" - }, - "Polly.Extensions.Http": { - "type": "Direct", - "requested": "[3.0.0, )", - "resolved": "3.0.0", - "contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==", - "dependencies": { - "Polly": "7.1.0" - } - }, "PolySharp": { "type": "Direct", - "requested": "[1.14.1, )", - "resolved": "1.14.1", - "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g==" }, "Speckle.DoubleNumerics": { "type": "Direct", - "requested": "[4.0.1, )", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" }, "Speckle.InterfaceGenerator": { "type": "Direct", @@ -99,21 +99,6 @@ "resolved": "13.0.2", "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" }, - "System.Text.Json": { - "type": "Direct", - "requested": "[5.0.2, )", - "resolved": "5.0.2", - "contentHash": "I47dVIGiV6SfAyppphxqupertT/5oZkYLDCX6vC3HpOI4ZLjyoKAreUoem2ie6G0RbRuFrlqz/PcTQjfb2DOfQ==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "5.0.0", - "System.Buffers": "4.5.1", - "System.Memory": "4.5.4", - "System.Numerics.Vectors": "4.5.0", - "System.Runtime.CompilerServices.Unsafe": "5.0.0", - "System.Text.Encodings.Web": "5.0.1", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, "GraphQL.Client.Abstractions": { "type": "Transitive", "resolved": "6.0.0", @@ -135,14 +120,6 @@ "resolved": "6.0.0", "contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA==" }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==", - "dependencies": { - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, "Microsoft.Build.Tasks.Git": { "type": "Transitive", "resolved": "8.0.0", @@ -150,12 +127,60 @@ }, "Microsoft.Data.Sqlite.Core": { "type": "Transitive", - "resolved": "7.0.7", - "contentHash": "21FRzcJhaTrlv7kTrqr/ltFcSQM2TyuTTPhUcjO8H73od7Bb3QraNW90c7lUucNI/245XPkKZG4fp7/7OsKCSg==", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", "dependencies": { "SQLitePCLRaw.core": "2.1.4" } }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "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.NETCore.Platforms": { "type": "Transitive", "resolved": "1.1.0", @@ -203,23 +228,28 @@ }, "System.Buffers": { "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + "resolved": "4.4.0", + "contentHash": "AwarXzzoDwX6BgrhjoJsk6tUezZEozOT5Y9QKF94Gl4JK91I4PIIBkBco9068Y9/Dra8Dkbie99kXB8+1BaYKw==" + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==", + "resolved": "4.5.3", + "contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==", "dependencies": { - "System.Buffers": "4.5.1", + "System.Buffers": "4.4.0", "System.Numerics.Vectors": "4.4.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" + "System.Runtime.CompilerServices.Unsafe": "4.5.2" } }, "System.Numerics.Vectors": { "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + "resolved": "4.4.0", + "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" }, "System.Reactive": { "type": "Transitive", @@ -241,8 +271,8 @@ }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" + "resolved": "4.5.3", + "contentHash": "3TIsJhD1EiiT0w2CcDMN/iSSwnNnsrnbzeVHSKkaEgV85txMprmuO+Yq2AdSbeVGcg28pdNDTPK87tJhX7VFHw==" }, "System.Runtime.InteropServices.WindowsRuntime": { "type": "Transitive", @@ -252,15 +282,6 @@ "System.Runtime": "4.3.0" } }, - "System.Text.Encodings.Web": { - "type": "Transitive", - "resolved": "5.0.1", - "contentHash": "KmJ+CJXizDofbq6mpqDoRRLcxgOd2z9X3XoFNULSbvbqVRZkFX3istvr+MUjL6Zw1RT+RNdoI4GYidIINtgvqQ==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Memory": "4.5.4" - } - }, "System.Threading.Tasks.Extensions": { "type": "Transitive", "resolved": "4.5.4", @@ -269,7 +290,228 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.3" } }, - "speckle.sdk.logging": { + "speckle.sdk.dependencies": { + "type": "Project" + } + }, + "net8.0": { + "GraphQL.Client": { + "type": "Direct", + "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": "Direct", + "requested": "[4.7.0, )", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "Direct", + "requested": "[7.0.5, )", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Direct", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Logging": { + "type": "Direct", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.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" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g==" + }, + "Speckle.DoubleNumerics": { + "type": "Direct", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "Speckle.Newtonsoft.Json": { + "type": "Direct", + "requested": "[13.0.2, )", + "resolved": "13.0.2", + "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" + }, + "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.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "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.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "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.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw==" + }, + "speckle.sdk.dependencies": { "type": "Project" } } diff --git a/tests/Speckle.LongSendProblem/Program.cs b/tests/Speckle.LongSendProblem/Program.cs deleted file mode 100644 index 7e7fdfee..00000000 --- a/tests/Speckle.LongSendProblem/Program.cs +++ /dev/null @@ -1,70 +0,0 @@ -using Speckle.Objects.Geometry; -using Speckle.Sdk; -using Speckle.Sdk.Api; -using Speckle.Sdk.Credentials; -using Speckle.Sdk.Host; -using Speckle.Sdk.Logging; -using Speckle.Sdk.Models; -using Speckle.Sdk.Transports; - -//Replace this with a brand new model URL -Uri modelUrl = new("https://testing1.speckle.dev/projects/cdedc63e6d/models/2d68380f1d"); - -//Keep this as-is, copy the Data.db into %appdata%/longsendtest/Data.db -const string OBJECT_ID = "5cbf84a0061172102ef8a66ae914f232"; - -SetupSpeckle(); -var testData = await GetSampleData(OBJECT_ID); -await SendToSpeckle(testData, modelUrl); - -return; - -static async Task SendToSpeckle(Base testData, Uri modelUrl) -{ - SpeckleLog.Logger.Information("Starting Long Send Test Send"); - var destinationTransport = await GetDestination(modelUrl); - - var (res, _) = await Operations.Send(testData, new[] { destinationTransport }); - SpeckleLog.Logger.Information("Starting Send was successful: {objectId}", res); -} - -static async Task GetDestination(Uri modelUrl) -{ - StreamWrapper sw = new(modelUrl.ToString()); - var acc = await sw.GetAccount(); - return new ServerTransport(acc, sw.StreamId); -} - -static async Task GetSampleData(string objectId) -{ - SpeckleLog.Logger.Information("Gathering Sample Data Set"); - using SQLiteTransport source = new(SpecklePathProvider.UserApplicationDataPath(), "longsendtest"); - MemoryTransport memoryTransport = new(); - return await Operations.Receive(objectId, source, memoryTransport); -} - -static void SetupSpeckle() -{ - TypeLoader.Initialize(typeof(Base).Assembly, typeof(Point).Assembly); - var config = new SpeckleConfiguration( - new("Long Send Problem Test Script", "LongSend"), - default, - new( - MinimumLevel: SpeckleLogLevel.Debug, - Console: true, - File: new(Path: "SpeckleCoreLog.txt"), - Otel: new( - Endpoint: "https://seq.speckle.systems/ingest/otlp/v1/logs", - Headers: new() { { "X-Seq-ApiKey", "agZqxG4jQELxQQXh0iZQ" } } - ) - ), - new( - Console: false, - Otel: new( - Endpoint: "https://seq.speckle.systems/ingest/otlp/v1/traces", - Headers: new() { { "X-Seq-ApiKey", "agZqxG4jQELxQQXh0iZQ" } } - ) - ) - ); - Setup.Initialize(config); -} diff --git a/tests/Speckle.LongSendProblem/Speckle.LongSendProblem.csproj b/tests/Speckle.LongSendProblem/Speckle.LongSendProblem.csproj deleted file mode 100644 index 3b260bac..00000000 --- a/tests/Speckle.LongSendProblem/Speckle.LongSendProblem.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - - Exe - net8.0 - enable - enable - - - - - - - diff --git a/tests/Speckle.LongSendProblem/packages.lock.json b/tests/Speckle.LongSendProblem/packages.lock.json deleted file mode 100644 index 457984c7..00000000 --- a/tests/Speckle.LongSendProblem/packages.lock.json +++ /dev/null @@ -1,204 +0,0 @@ -{ - "version": 2, - "dependencies": { - "net8.0": { - "GitVersion.MsBuild": { - "type": "Direct", - "requested": "[5.12.0, )", - "resolved": "5.12.0", - "contentHash": "dJuigXycpJNOiLT9or7mkHSkGFHgGW3/p6cNNYEKZBa7Hhp1FdX/cvqYWWYhRLpfoZOedeA7aRbYiOB3vW/dvA==" - }, - "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" - } - }, - "PolySharp": { - "type": "Direct", - "requested": "[1.14.1, )", - "resolved": "1.14.1", - "contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ==" - }, - "Speckle.InterfaceGenerator": { - "type": "Direct", - "requested": "[0.9.6, )", - "resolved": "0.9.6", - "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" - }, - "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.Build.Tasks.Git": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" - }, - "Microsoft.Data.Sqlite.Core": { - "type": "Transitive", - "resolved": "7.0.7", - "contentHash": "21FRzcJhaTrlv7kTrqr/ltFcSQM2TyuTTPhUcjO8H73od7Bb3QraNW90c7lUucNI/245XPkKZG4fp7/7OsKCSg==", - "dependencies": { - "SQLitePCLRaw.core": "2.1.4" - } - }, - "Microsoft.SourceLink.Common": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" - }, - "SQLitePCLRaw.bundle_e_sqlite3": { - "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", - "dependencies": { - "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", - "SQLitePCLRaw.provider.e_sqlite3": "2.1.4" - } - }, - "SQLitePCLRaw.core": { - "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", - "dependencies": { - "System.Memory": "4.5.3" - } - }, - "SQLitePCLRaw.lib.e_sqlite3": { - "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" - }, - "SQLitePCLRaw.provider.e_sqlite3": { - "type": "Transitive", - "resolved": "2.1.4", - "contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==", - "dependencies": { - "SQLitePCLRaw.core": "2.1.4" - } - }, - "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==" - }, - "speckle.objects": { - "type": "Project", - "dependencies": { - "Speckle.Sdk": "[1.0.0, )" - } - }, - "speckle.sdk": { - "type": "Project", - "dependencies": { - "GraphQL.Client": "[6.0.0, )", - "Microsoft.CSharp": "[4.7.0, )", - "Microsoft.Data.Sqlite": "[7.0.7, )", - "Polly": "[7.2.3, )", - "Polly.Contrib.WaitAndRetry": "[1.1.1, )", - "Polly.Extensions.Http": "[3.0.0, )", - "Speckle.DoubleNumerics": "[4.0.1, )", - "Speckle.Newtonsoft.Json": "[13.0.2, )", - "Speckle.Sdk.Logging": "[1.0.0, )", - "System.Text.Json": "[5.0.2, )" - } - }, - "speckle.sdk.logging": { - "type": "Project" - }, - "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": "[7.0.7, )", - "resolved": "7.0.7", - "contentHash": "tiNmV1oPy+Z2R7Wd0bPB/FxCr8B+/5q11OpDMG751GA/YuOL7MZrBFfzv5oFRlFe08K6sjrnbrauzzGIeNrzLQ==", - "dependencies": { - "Microsoft.Data.Sqlite.Core": "7.0.7", - "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" - } - }, - "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" - } - }, - "Speckle.DoubleNumerics": { - "type": "CentralTransitive", - "requested": "[4.0.1, )", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" - }, - "Speckle.Newtonsoft.Json": { - "type": "CentralTransitive", - "requested": "[13.0.2, )", - "resolved": "13.0.2", - "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" - }, - "System.Text.Json": { - "type": "CentralTransitive", - "requested": "[5.0.2, )", - "resolved": "5.0.2", - "contentHash": "I47dVIGiV6SfAyppphxqupertT/5oZkYLDCX6vC3HpOI4ZLjyoKAreUoem2ie6G0RbRuFrlqz/PcTQjfb2DOfQ==" - } - } - } -} \ No newline at end of file diff --git a/tests/Speckle.Objects.Tests.Unit/Geometry/ArcTests.cs b/tests/Speckle.Objects.Tests.Unit/Geometry/ArcTests.cs index e7a2cffc..a7ab2d8c 100644 --- a/tests/Speckle.Objects.Tests.Unit/Geometry/ArcTests.cs +++ b/tests/Speckle.Objects.Tests.Unit/Geometry/ArcTests.cs @@ -1,32 +1,77 @@ -using System; -using NUnit.Framework; +using FluentAssertions; using Speckle.Objects.Geometry; using Speckle.Sdk.Common; -namespace Objects.Tests.Unit.Geometry; +namespace Speckle.Objects.Tests.Unit.Geometry; -[TestFixture, TestOf(typeof(Arc))] public class ArcTests { - private Plane TestPlane => + private Plane TestPlaneCounterClockwise => new() { - origin = new Point(0, 0), - normal = new Vector(0, 0, 1), - xdir = new Vector(1, 0, 0), - ydir = new Vector(0, 1, 0), + origin = new Point(0, 0, 0, Units.Meters), + normal = new Vector(0, 0, 1, Units.Meters), + xdir = new Vector(1, 0, 0, Units.Meters), + ydir = new Vector(0, 1, 0, Units.Meters), units = Units.Meters, }; - [Test] - public void CanCreateArc_HalfCircle() + private Plane TestPlaneClockwise => + new() + { + origin = new Point(0, 0, 0, Units.Meters), + normal = new Vector(0, 0, -1, Units.Meters), + xdir = new Vector(-1, 0, 0, Units.Meters), + ydir = new Vector(0, 1, 0, Units.Meters), + units = Units.Meters, + }; + + [Fact] + public void CanCreateArc_HalfCircle_CounterClockwise() { - var arc = new Arc(TestPlane, new Point(-5, 5), new Point(5, 5), Math.PI); + var counterClockwiseArc = new Arc() + { + plane = TestPlaneCounterClockwise, + startPoint = new Point(1, 0, 0, Units.Meters), + endPoint = new Point(-1, 0, 0, Units.Meters), + midPoint = new Point(0, 1, 0, Units.Meters), + units = Units.Meters, + }; - Assert.That(arc.startAngle, Is.EqualTo(0)); - Assert.That(arc.endAngle, Is.EqualTo(Math.PI)); + Point.Distance(counterClockwiseArc.midPoint, new Point(0, 1, 0, Units.Meters)).Should().BeApproximately(0, 0.0001); - Assert.That(Point.Distance(arc.midPoint, new Point(0, 0)), Is.EqualTo(0).Within(0.0001)); - Assert.That(Point.Distance(arc.plane.origin, new Point(0, 5)), Is.EqualTo(0).Within(0.0001)); + Point + .Distance(counterClockwiseArc.plane.origin, new Point(0, 0, 0, Units.Meters)) + .Should() + .BeApproximately(0, 0.0001); + + (counterClockwiseArc.measure - Math.PI).Should().BeApproximately(0, 0.0001); + + counterClockwiseArc.radius.Should().BeApproximately(1, 0.0001); + + counterClockwiseArc.length.Should().BeApproximately(Math.PI, 0.0001); + } + + [Fact] + public void CanCreateArc_HalfCircle_Clockwise() + { + var clockwiseArc = new Arc() + { + plane = TestPlaneClockwise, + endPoint = new Point(1, 0, 0, Units.Meters), + startPoint = new Point(-1, 0, 0, Units.Meters), + midPoint = new Point(0, 1, 0, Units.Meters), + units = Units.Meters, + }; + + Point.Distance(clockwiseArc.midPoint, new Point(0, 1, 0, Units.Meters)).Should().BeApproximately(0, 0.0001); + + Point.Distance(clockwiseArc.plane.origin, new Point(0, 0, 0, Units.Meters)).Should().BeApproximately(0, 0.0001); + + (clockwiseArc.measure - Math.PI).Should().BeApproximately(0, 0.0001); + + clockwiseArc.radius.Should().BeApproximately(1, 0.0001); + + clockwiseArc.length.Should().BeApproximately(Math.PI, 0.0001); } } diff --git a/tests/Speckle.Objects.Tests.Unit/Geometry/BoxTests.CanCreateBox.verified.json b/tests/Speckle.Objects.Tests.Unit/Geometry/BoxTests.CanCreateBox.verified.json new file mode 100644 index 00000000..25d25b00 --- /dev/null +++ b/tests/Speckle.Objects.Tests.Unit/Geometry/BoxTests.CanCreateBox.verified.json @@ -0,0 +1,90 @@ +{ + "basePlane": { + "origin": { + "units": "m", + "speckle_type": "Objects.Geometry.Point", + "DynamicPropertyKeys": [] + }, + "normal": { + "units": "m", + "z": 1.0, + "Length": 1.0, + "speckle_type": "Objects.Geometry.Vector", + "DynamicPropertyKeys": [] + }, + "xdir": { + "units": "m", + "x": 1.0, + "Length": 1.0, + "speckle_type": "Objects.Geometry.Vector", + "DynamicPropertyKeys": [] + }, + "ydir": { + "units": "m", + "y": 1.0, + "Length": 1.0, + "speckle_type": "Objects.Geometry.Vector", + "DynamicPropertyKeys": [] + }, + "units": "m", + "speckle_type": "Objects.Geometry.Plane", + "DynamicPropertyKeys": [] + }, + "plane": { + "origin": { + "units": "m", + "speckle_type": "Objects.Geometry.Point", + "DynamicPropertyKeys": [] + }, + "normal": { + "units": "m", + "z": 1.0, + "Length": 1.0, + "speckle_type": "Objects.Geometry.Vector", + "DynamicPropertyKeys": [] + }, + "xdir": { + "units": "m", + "x": 1.0, + "Length": 1.0, + "speckle_type": "Objects.Geometry.Vector", + "DynamicPropertyKeys": [] + }, + "ydir": { + "units": "m", + "y": 1.0, + "Length": 1.0, + "speckle_type": "Objects.Geometry.Vector", + "DynamicPropertyKeys": [] + }, + "units": "m", + "speckle_type": "Objects.Geometry.Plane", + "DynamicPropertyKeys": [] + }, + "xSize": { + "start": -1.0, + "end": 1.0, + "Length": 2.0, + "speckle_type": "Objects.Primitive.Interval", + "DynamicPropertyKeys": [] + }, + "ySize": { + "start": -2.0, + "end": 2.0, + "Length": 4.0, + "speckle_type": "Objects.Primitive.Interval", + "DynamicPropertyKeys": [] + }, + "zSize": { + "start": -3.0, + "end": 3.0, + "Length": 6.0, + "speckle_type": "Objects.Primitive.Interval", + "DynamicPropertyKeys": [] + }, + "units": "m", + "area": 88.0, + "volume": 48.0, + "speckle_type": "Objects.Geometry.Box", + "DynamicPropertyKeys": [] +} diff --git a/tests/Speckle.Objects.Tests.Unit/Geometry/BoxTests.cs b/tests/Speckle.Objects.Tests.Unit/Geometry/BoxTests.cs new file mode 100644 index 00000000..716ecd32 --- /dev/null +++ b/tests/Speckle.Objects.Tests.Unit/Geometry/BoxTests.cs @@ -0,0 +1,32 @@ +using Speckle.Objects.Geometry; +using Speckle.Sdk.Common; + +namespace Speckle.Objects.Tests.Unit.Geometry; + +public class BoxTests +{ + private Plane TestPlane => + new() + { + origin = new Point(0, 0, 0, Units.Meters), + normal = new Vector(0, 0, 1, Units.Meters), + xdir = new Vector(1, 0, 0, Units.Meters), + ydir = new Vector(0, 1, 0, Units.Meters), + units = Units.Meters, + }; + + [Fact] + public async Task CanCreateBox() + { + const string UNITS = Units.Meters; + var box = new Box() + { + plane = TestPlane, + xSize = new() { start = -1, end = 1 }, + ySize = new() { start = -2, end = 2 }, + zSize = new() { start = -3, end = 3 }, + units = UNITS, + }; + await Verify(box); + } +} diff --git a/tests/Speckle.Objects.Tests.Unit/Geometry/CircleTests.cs b/tests/Speckle.Objects.Tests.Unit/Geometry/CircleTests.cs new file mode 100644 index 00000000..6b3af807 --- /dev/null +++ b/tests/Speckle.Objects.Tests.Unit/Geometry/CircleTests.cs @@ -0,0 +1,40 @@ +using FluentAssertions; +using Speckle.Objects.Geometry; +using Speckle.Sdk.Common; + +namespace Speckle.Objects.Tests.Unit.Geometry; + +public class CircleTests +{ + private Plane TestPlane + { + get + { + const string UNITS = Units.Meters; + return new Plane + { + origin = new Point(0, 0, 0, UNITS), + normal = new Vector(0, 0, 1, UNITS), + xdir = new Vector(1, 0, 0, UNITS), + ydir = new Vector(0, 1, 0, UNITS), + units = UNITS, + }; + } + } + + [Fact] + public void CanCreateCircle() + { + const string UNITS = Units.Meters; + var circle = new Circle + { + plane = TestPlane, + radius = 5, + units = UNITS, + }; + + // Use Shouldly assertions + circle.length.Should().BeApproximately(2 * Math.PI * 5, 0.0001); + circle.area.Should().BeApproximately(Math.PI * 5 * 5, 0.0001); + } +} diff --git a/tests/Speckle.Objects.Tests.Unit/Geometry/MeshTests.cs b/tests/Speckle.Objects.Tests.Unit/Geometry/MeshTests.cs index 25f18338..31a44bd4 100644 --- a/tests/Speckle.Objects.Tests.Unit/Geometry/MeshTests.cs +++ b/tests/Speckle.Objects.Tests.Unit/Geometry/MeshTests.cs @@ -1,44 +1,71 @@ -using NUnit.Framework; +using FluentAssertions; using Speckle.Objects.Geometry; +using Speckle.Sdk.Common; -namespace Objects.Tests.Unit.Geometry; +namespace Speckle.Objects.Tests.Unit.Geometry; -[TestFixture, TestOf(typeof(Mesh))] public class MeshTests { - private static Mesh[] s_testCaseSource = { CreateBlenderStylePolygon(), CreateRhinoStylePolygon() }; + private static readonly Mesh[] TestCaseSource = { CreateRhinoStylePolygon(), CreateEmpty() }; - [Test, TestCaseSource(nameof(s_testCaseSource))] - public void CanAlignVertices(Mesh inPolygon) - { - inPolygon.AlignVerticesWithTexCoordsByIndex(); - - Assert.That(inPolygon.VerticesCount, Is.EqualTo(inPolygon.TextureCoordinatesCount)); - - var expectedPolygon = CreateRhinoStylePolygon(); - - Assert.That(inPolygon.vertices, Is.EquivalentTo(expectedPolygon.vertices)); - Assert.That(inPolygon.faces, Is.EquivalentTo(expectedPolygon.faces)); - Assert.That(inPolygon.textureCoordinates, Is.EquivalentTo(expectedPolygon.textureCoordinates)); - } + public static IEnumerable GetTestCaseSource() => TestCaseSource.Select(mesh => new object[] { mesh }); private static Mesh CreateRhinoStylePolygon() { return new Mesh { - vertices = { 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0 }, - faces = { 3, 0, 1, 2, 3, 3, 4, 5 }, - textureCoordinates = { 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0 } + vertices = new List { 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0 }, + faces = new List { 3, 0, 1, 2, 3, 3, 4, 5 }, + textureCoordinates = new List { 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0 }, + units = Units.Meters, }; } - private static Mesh CreateBlenderStylePolygon() + private static Mesh CreateEmpty() { return new Mesh { - vertices = { 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0 }, - faces = { 3, 0, 1, 2, 3, 0, 2, 3 }, - textureCoordinates = { 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0 } + vertices = [], + faces = [], + textureCoordinates = [], + units = Units.Meters, }; } + + [Theory] + [MemberData(nameof(GetTestCaseSource))] + public void GetTextureCoordinate_ReturnsCorrectUVValue(Mesh testCase) + { + for (int i = 0, j = 0; i < testCase.textureCoordinates.Count; i += 2, j++) + { + var (u, v) = testCase.GetTextureCoordinate(j); + + u.Should().Be(testCase.textureCoordinates[i]); + v.Should().Be(testCase.textureCoordinates[i + 1]); + } + + Assert.Throws(() => testCase.GetTextureCoordinate(testCase.textureCoordinates.Count)); + } + + [Theory] + [MemberData(nameof(GetTestCaseSource))] + public void GetPoints_ReturnsVerticesAsPoints(Mesh testCase) + { + testCase.VerticesCount.Should().Be(testCase.vertices.Count / 3); + + var getPoints = testCase.GetPoints(); + var getPoint = Enumerable.Range(0, testCase.VerticesCount).Select(i => testCase.GetPoint(i)); + + //Test each point has correct units + getPoints.Select(x => x.units).Should().AllBe(testCase.units).And.HaveCount(testCase.VerticesCount); + getPoints.Select(x => x.units).Should().AllBe(testCase.units).And.HaveCount(testCase.VerticesCount); + + //Convert back to flat list + var expected = testCase.vertices; + var getPointsList = getPoints.SelectMany(x => x.ToList()); + var getPointList = getPoint.SelectMany(x => x.ToList()); + + getPointsList.Should().BeEquivalentTo(expected); + getPointList.Should().BeEquivalentTo(expected); + } } diff --git a/tests/Speckle.Objects.Tests.Unit/Geometry/PointTests.cs b/tests/Speckle.Objects.Tests.Unit/Geometry/PointTests.cs index 0a556558..8de0cba0 100644 --- a/tests/Speckle.Objects.Tests.Unit/Geometry/PointTests.cs +++ b/tests/Speckle.Objects.Tests.Unit/Geometry/PointTests.cs @@ -1,53 +1,196 @@ using System.Diagnostics.CodeAnalysis; -using NUnit.Framework; +using FluentAssertions; +using Speckle.DoubleNumerics; using Speckle.Objects.Geometry; +using Speckle.Objects.Other; +using Speckle.Sdk.Common; -namespace Objects.Tests.Unit.Geometry; +namespace Speckle.Objects.Tests.Unit.Geometry; -[TestFixture, TestOf(typeof(Point))] public class PointTests { - [Test] + [Fact] [SuppressMessage( "Assertion", - "NUnit2010:Use EqualConstraint for better assertion messages in case of failure", - Justification = "Need to explicitly test equality operator" + "xUnit2013:Do not use equality check to assert boolean value", + Justification = "Explicit equality operator tests are necessary" )] public void TestNull() { Point? a = null; Point? b = null; - Point c = new(0, 0, 0); + Point c = new(0, 0, 0, Units.Meters); - Assert.Multiple(() => - { - Assert.That(a == b, Is.True); - Assert.That(a != b, Is.False); - Assert.That(b == a, Is.True); - Assert.That(b != a, Is.False); + a.Should().Be(b); + (a != b).Should().BeFalse(); - Assert.That(a == c, Is.False); - Assert.That(a != c, Is.True); - Assert.That(c == a, Is.False); - Assert.That(c != a, Is.True); - }); + b.Should().Be(a); + (b != a).Should().BeFalse(); + + (a == c).Should().BeFalse(); + (a != c).Should().BeTrue(); + + (c == a).Should().BeFalse(); + (c != a).Should().BeTrue(); } - [Test] - [TestCase(1, 1, 1, "m", 1, 1, 1, "m", ExpectedResult = true)] - [TestCase(1, 1, 1, "m", 0, 1, 1, "m", ExpectedResult = false)] - [TestCase(1, 1, 1, "m", 1, 0, 1, "m", ExpectedResult = false)] - [TestCase(1, 1, 1, "m", 1, 1, 0, "m", ExpectedResult = false)] - [TestCase(1, 1, 1, "", 1, 1, 1, "", ExpectedResult = true)] - [TestCase(1, 1, 1, null, 1, 1, 1, null, ExpectedResult = true)] - [TestCase(1, 1, 1, "m", 1, 1, 1, "meters", ExpectedResult = false)] - [TestCase(1, 1, 1, "m", 1, 1, 1, "M", ExpectedResult = false)] - // Units - public bool TestEqual(double x1, double y1, double z1, string units1, double x2, double y2, double z2, string units2) + //TODO: Should(). units be allowed to be string? + [Theory] + [InlineData(1, 1, 1, "m", 1, 1, 1, "m", true)] + [InlineData(1, 1, 1, "m", 0, 1, 1, "m", false)] + [InlineData(1, 1, 1, "m", 1, 0, 1, "m", false)] + [InlineData(1, 1, 1, "m", 1, 1, 0, "m", false)] + [InlineData(1, 1, 1, "", 1, 1, 1, "", false)] + [InlineData(1, 1, 1, null, 1, 1, 1, null, false)] + [InlineData(1, 1, 1, "m", 1, 1, 1, "meters", false)] + [InlineData(1, 1, 1, "m", 1, 1, 1, "M", false)] + public void TestEqual( + double x1, + double y1, + double z1, + string? units1, + double x2, + double y2, + double z2, + string? units2, + bool expectedResult + ) { + if (string.IsNullOrEmpty(units1) || string.IsNullOrEmpty(units2)) + { + expectedResult.Should().BeFalse(); + return; + } Point p1 = new(x1, y1, z1, units1); Point p2 = new(x2, y2, z2, units2); - return p1 == p2; + (p1 == p2).Should().Be(expectedResult); + } + + [Fact] + public void TestDistanceTo() + { + //Arrange + var p1 = new Point(1, 0, 0, units: Units.Meters); + var p2 = new Point(0, 0, 0, units: Units.Meters); + + //Act + var result = p1.DistanceTo(p2); + + //Assert + result.Should().Be(1); + } + + private static IReadOnlyList MatrixTestData => + [ + Matrix4x4.Identity, + Matrix4x4.CreateScale(1, 2, 3), + Matrix4x4.CreateTranslation(100, 10, 0), + Matrix4x4.CreateRotationZ(1), + ]; + + private static IReadOnlyList PointTestData => + [ + new(1, 2, 3, Units.Meters), + new(0.5, 100.5, 123.123, Units.Meters), + new(1, 2, 3, Units.Meters, applicationId: "Test me!"), + new(0, 0, 0, Units.Feet), + ]; + + public static TheoryData PointTestCases() => new(PointTestData); + + public static TheoryData TransformTestCases() + { + TheoryData testCases = new(); + for (int i = 0; i < PointTestData.Count; i++) + { + testCases.Add(MatrixTestData[i], PointTestData[i]); + } + return testCases; + } + + [Theory] + [MemberData(nameof(TransformTestCases))] + public void TransformPoint_SameUnits(Matrix4x4 matrix, Point point) + { + //Arrange + Transform t = new() { matrix = Matrix4x4.Transpose(matrix), units = point.units }; + + Vector3 expectedVector = Vector3.Transform(new(point.x, point.y, point.z), matrix); + var expectedResult = (expectedVector.X, expectedVector.Y, expectedVector.Z); + + //Act + point.TransformTo(t, out Point transformedPoint); + var actualResult = (transformedPoint.x, transformedPoint.y, transformedPoint.z); + + //Assert + actualResult.Should().Be(expectedResult); + transformedPoint.applicationId.Should().Be(point.applicationId); + + transformedPoint.applicationId.Should().Be(point.applicationId); + transformedPoint.id.Should().Be(point.id); + transformedPoint.units.Should().Be(point.units); + } + + [Fact(Skip = "Something clearly wrong with units!!!")] + public void TransformingPoint_ChangeOfUnits() + { + //Arrange + Point point = new(0, 0, 10, Units.Meters); + Transform t = new() + { + matrix = Matrix4x4.Transpose(Matrix4x4.CreateTranslation(1000, 0, 0)), + units = Units.Millimeters, + }; + Vector3 expected = new(1, 0, 10); + + //Act + point.TransformTo(t, out Point transformedPoint); + + //Assert + (double x, double y, double z) = transformedPoint; + transformedPoint.units.Should().Be(point.units); + transformedPoint.applicationId.Should().Be(point.applicationId); + + new Vector3(x, y, z).Should().Be(expected); + } + + [Theory] + [MemberData(nameof(PointTestCases))] + public void ToVector(Point testCase) + { + var expectedXYZ = (testCase.x, testCase.y, testCase.z); + var expectedUnits = testCase.units; + var expectedApplicationId = testCase.applicationId; + + var asVector = testCase.ToVector(); + var resultXYZ = (asVector.x, asVector.y, asVector.z); + + resultXYZ.Should().Be(expectedXYZ); + asVector.units.Should().Be(expectedUnits); + asVector.applicationId.Should().Be(expectedApplicationId); + } + + [Theory] + [MemberData(nameof(PointTestCases))] + public void Deconstruct_Double_Double_Double_String(Point testCase) + { + (double x, double y, double z, string? units) = testCase; + + x.Should().Be(testCase.x); + y.Should().Be(testCase.y); + z.Should().Be(testCase.z); + units.Should().Be(testCase.units); + } + + [Theory] + [MemberData(nameof(PointTestCases))] + public void Deconstruct_Double_Double_Double(Point testCase) + { + (double x, double y, double z) = testCase; + + x.Should().Be(testCase.x); + y.Should().Be(testCase.y); + z.Should().Be(testCase.z); } } diff --git a/tests/Speckle.Objects.Tests.Unit/Geometry/TransformTests.cs b/tests/Speckle.Objects.Tests.Unit/Geometry/TransformTests.cs index d9f1932d..ac9d61d8 100644 --- a/tests/Speckle.Objects.Tests.Unit/Geometry/TransformTests.cs +++ b/tests/Speckle.Objects.Tests.Unit/Geometry/TransformTests.cs @@ -1,129 +1,97 @@ -using System.Collections; -using NUnit.Framework; +using FluentAssertions; using Speckle.DoubleNumerics; using Speckle.Objects.Other; using Speckle.Sdk.Common; -namespace Objects.Tests.Unit.Geometry; +namespace Speckle.Objects.Tests.Unit.Geometry; -[TestFixture, TestOf(typeof(Transform))] public class TransformTests { private const float FLOAT_TOLERANCE = 1e-6f; - [Test, TestCaseSource(nameof(TransformTestCases))] + [Theory] + [MemberData(nameof(TransformTestCases))] public void ArrayBackAndForth(Matrix4x4 data) { - var start = new Transform(data); - var asArr = start.ToArray(); - var end = new Transform(asArr); + // Arrange + var start = new Transform() { matrix = data, units = Units.None }; - Assert.That(end.matrix, Is.EqualTo(data)); + // Act + var asArr = Transform.CreateMatrix(start.ToArray()); + var end = new Transform() { matrix = asArr, units = Units.None }; + + // Assert + end.matrix.Should().Be(data); } - [Test, TestCaseSource(nameof(TransformTestCases))] + [Theory] + [MemberData(nameof(TransformTestCases))] public void ConvertToUnits(Matrix4x4 data) { const float SF = 1000f; - var transpose = Matrix4x4.Transpose(data); //NOTE: Transform expects matrices transposed (translation in column 4) + // Arrange + var transpose = Matrix4x4.Transpose(data); // Transform expects matrices transposed (translation in column 4) var mm = Matrix4x4.Transpose( - Transform.CreateMatrix(new Transform(transpose, Units.Meters).ConvertToUnits(Units.Millimeters)) + Transform.CreateMatrix( + new Transform() { matrix = transpose, units = Units.Meters }.ConvertToUnits(Units.Millimeters) + ) ); + // Act Matrix4x4.Decompose(data, out var ms, out var mr, out var mt); Matrix4x4.Decompose(mm, out var mms, out var mmr, out var mmt); - Assert.Multiple(() => - { - Assert.That(mms.X, Is.EqualTo(ms.X).Within(FLOAT_TOLERANCE), "Expect scale x to be unchanged"); - Assert.That(mms.Y, Is.EqualTo(ms.Y).Within(FLOAT_TOLERANCE), "Expect scale y to be unchanged"); - Assert.That(mms.Z, Is.EqualTo(ms.Z).Within(FLOAT_TOLERANCE), "Expect scale z to be unchanged"); + // Assert + mms.X.Should().BeApproximately(ms.X, FLOAT_TOLERANCE, "Expect scale x to be unchanged"); + mms.Y.Should().BeApproximately(ms.Y, FLOAT_TOLERANCE, "Expect scale y to be unchanged"); + mms.Z.Should().BeApproximately(ms.Z, FLOAT_TOLERANCE, "Expect scale z to be unchanged"); - Assert.That(Quaternion.Dot(mr, mmr), Is.LessThan(1).Within(FLOAT_TOLERANCE), "Expect rot x to be equivalent"); + Quaternion.Dot(mr, mmr).Should().BeLessThan(1 + FLOAT_TOLERANCE, "Expect rotation to be equivalent"); - Assert.That(mmt.X, Is.EqualTo(mt.X * SF).Within(FLOAT_TOLERANCE), $"Expect translation x to be scaled by {SF}"); - Assert.That(mmt.Y, Is.EqualTo(mt.Y * SF).Within(FLOAT_TOLERANCE), $"Expect translation y to be scaled by {SF}"); - Assert.That(mmt.Z, Is.EqualTo(mt.Z * SF).Within(FLOAT_TOLERANCE), $"Expect translation z to be scaled by {SF}"); - }); - } - - [ - Test(Description = "Tests that Transform decompose matches the behaviour of Matrix4x4"), - TestCaseSource(nameof(TransformTestCases)) - ] - public void Decompose(Matrix4x4 data) - { - var transpose = Matrix4x4.Transpose(data); //NOTE: Transform expects matrices transposed (translation in column 4) - var sut = new Transform(transpose); - - sut.Decompose(out var s, out var r, out var t); - var actual = new Vector3(t.X, t.Y, t.Z); - Matrix4x4.Decompose(data, out var expectedS, out var expectedR, out var expectedT); - - Assert.Multiple(() => - { - Assert.That(s.X, Is.EqualTo(expectedS.X).Within(FLOAT_TOLERANCE), "Expect scale x to be unchanged"); - Assert.That(s.Y, Is.EqualTo(expectedS.Y).Within(FLOAT_TOLERANCE), "Expect scale y to be unchanged"); - Assert.That(s.Z, Is.EqualTo(expectedS.Z).Within(FLOAT_TOLERANCE), "Expect scale z to be unchanged"); - - Assert.That( - Quaternion.Dot(r, expectedR), - Is.LessThan(1).Within(FLOAT_TOLERANCE), - "Expect rot x to be equivalent" - ); - - Assert.That(actual.X, Is.EqualTo(expectedT.X).Within(FLOAT_TOLERANCE), "Expect translation x to be unchanged"); - Assert.That(actual.Y, Is.EqualTo(expectedT.Y).Within(FLOAT_TOLERANCE), "Expect translation y to be unchanged"); - Assert.That(actual.Z, Is.EqualTo(expectedT.Z).Within(FLOAT_TOLERANCE), "Expect translation z to be unchanged"); - }); + mmt.X.Should().BeApproximately(mt.X * SF, FLOAT_TOLERANCE, $"Expect translation x to be scaled by {SF}"); + mmt.Y.Should().BeApproximately(mt.Y * SF, FLOAT_TOLERANCE, $"Expect translation y to be scaled by {SF}"); + mmt.Z.Should().BeApproximately(mt.Z * SF, FLOAT_TOLERANCE, $"Expect translation z to be scaled by {SF}"); } /// /// Set of TRS transforms (row dominant i.e. translation in row 4) /// All with non-negative scale and rotation (for ease of testing scale and rot independently) /// - /// - private static IEnumerable TransformTestCases() + public static IEnumerable TransformTestCases() { var t = new Vector3(128.128f, 255.255f, 512.512f); var r = Quaternion.CreateFromYawPitchRoll(1.9f, 0.6666667f, 0.5f); var s = new Vector3(123f, 32f, 0.5f); - yield return new TestCaseData(Matrix4x4.Identity).SetName("{m} Identity Matrix"); + yield return [Matrix4x4.Identity]; - yield return new TestCaseData(Matrix4x4.CreateTranslation(t)).SetName("{m} Translation Only (positive)"); + yield return [Matrix4x4.CreateTranslation(t)]; - yield return new TestCaseData(Matrix4x4.CreateTranslation(t * -Vector3.UnitX)).SetName("{m} Translation Only -X"); + yield return [Matrix4x4.CreateTranslation(t * -Vector3.UnitX)]; - yield return new TestCaseData(Matrix4x4.CreateTranslation(t * -Vector3.UnitY)).SetName("{m} Translation Only -Y"); + yield return [Matrix4x4.CreateTranslation(t * -Vector3.UnitY)]; - yield return new TestCaseData(Matrix4x4.CreateTranslation(t * -Vector3.UnitZ)).SetName("{m} Translation Only -Z"); + yield return [Matrix4x4.CreateTranslation(t * -Vector3.UnitZ)]; - yield return new TestCaseData(Matrix4x4.CreateTranslation(-t)).SetName("{m} Translation Only -XYZ "); + yield return [Matrix4x4.CreateTranslation(-t)]; - yield return new TestCaseData(Matrix4x4.CreateFromYawPitchRoll(0.5f, 0.0f, 0.0f)).SetName("{m} Rotation Only X "); + yield return [Matrix4x4.CreateFromYawPitchRoll(0.5f, 0.0f, 0.0f)]; - yield return new TestCaseData(Matrix4x4.CreateFromYawPitchRoll(0.0f, 0.5f, 0.0f)).SetName("{m} Rotation Only Y "); + yield return [Matrix4x4.CreateFromYawPitchRoll(0.0f, 0.5f, 0.0f)]; - yield return new TestCaseData(Matrix4x4.CreateFromYawPitchRoll(0.0f, 0.0f, 0.5f)).SetName("{m} Rotation Only Z "); + yield return [Matrix4x4.CreateFromYawPitchRoll(0.0f, 0.0f, 0.5f)]; - yield return new TestCaseData(Matrix4x4.CreateFromYawPitchRoll(0.5f, 0.5f, 0.5f)).SetName("{m} Rotation Only XYZ "); + yield return [Matrix4x4.CreateFromYawPitchRoll(0.5f, 0.5f, 0.5f)]; - yield return new TestCaseData(Matrix4x4.CreateFromQuaternion(r)).SetName("{m} Rotation Only"); + yield return [Matrix4x4.CreateFromQuaternion(r)]; - yield return new TestCaseData(Matrix4x4.Identity + Matrix4x4.CreateScale(s)).SetName("{m} Scale Only"); + yield return [Matrix4x4.Identity + Matrix4x4.CreateScale(s)]; - yield return new TestCaseData(Matrix4x4.CreateTranslation(t) + Matrix4x4.CreateFromQuaternion(r)).SetName( - "{m} Translation + Rotation" - ); + yield return [Matrix4x4.CreateTranslation(t) + Matrix4x4.CreateFromQuaternion(r)]; - yield return new TestCaseData( - Matrix4x4.CreateTranslation(t) + Matrix4x4.CreateFromQuaternion(r) + Matrix4x4.CreateScale(s) - ).SetName("{m} Translation + Rotation + Scale"); + yield return [Matrix4x4.CreateTranslation(t) + Matrix4x4.CreateFromQuaternion(r) + Matrix4x4.CreateScale(s)]; - yield return new TestCaseData( - Matrix4x4.CreateTranslation(t) + Matrix4x4.CreateFromQuaternion(r) + Matrix4x4.CreateScale(-s) - ).SetName("{m} Translation + Rotation + -Scale"); + yield return [Matrix4x4.CreateTranslation(t) + Matrix4x4.CreateFromQuaternion(r) + Matrix4x4.CreateScale(-s)]; } } diff --git a/tests/Speckle.Objects.Tests.Unit/Geometry/VectorTests.cs b/tests/Speckle.Objects.Tests.Unit/Geometry/VectorTests.cs new file mode 100644 index 00000000..1631dd1e --- /dev/null +++ b/tests/Speckle.Objects.Tests.Unit/Geometry/VectorTests.cs @@ -0,0 +1,233 @@ +using FluentAssertions; +using Speckle.DoubleNumerics; +using Speckle.Objects.Geometry; + +namespace Speckle.Objects.Tests.Unit.Geometry; + +public class VectorTests +{ + private const float FLOAT_TOLERANCE = 1e-6f; + + public static TheoryData TestCases() => + new() + { + { 0d, 0d, 0d, "m" }, + { 1d, 2d, 3d, "ft" }, + { 0d, 0d, -1d, "km" }, + { 100d, 0d, -200d, "in" }, + { 123.123d, 456.456d, 5789.789d, "cm" }, + { -123.123d, -456.456d, -5789.789d, "mm" }, + }; + + [Theory] + [MemberData(nameof(TestCases))] + public void Constructors_AreTheSame(double x, double y, double z, string units) + { + const string appId = "asdfasdfasdf"; + var pctor = new Vector(x, y, z, units, applicationId: appId); + + pctor.x.Should().Be(x); + pctor.y.Should().Be(y); + pctor.z.Should().Be(z); + pctor.units.Should().Be(units); + pctor.applicationId.Should().Be(appId); + + var init = new Vector + { + x = x, + y = y, + z = z, + units = units, + applicationId = appId, + }; + + Assert.Equal(pctor.x, init.x); + Assert.Equal(pctor.y, init.y); + Assert.Equal(pctor.z, init.z); + Assert.Equal(pctor.units, init.units); + Assert.Equal(pctor.applicationId, init.applicationId); + } + + [Theory] + [InlineData(1d, 0d, 0d, 1d)] + [InlineData(0d, 2d, 0d, 2d)] + [InlineData(0d, 0d, -3d, 3d)] + [InlineData(1d, 1d, 0d, 1.4142135623730951d)] + public void LengthCalculated(double x, double y, double z, double expected) + { + var testCase = new Vector(x, y, z, ""); + var actual = testCase.Length; + actual.Should().Be(expected); + } + + [Theory] + [MemberData(nameof(TestCases))] + public void EncodingToAndFromList(double x, double y, double z, string units) + { + var testCase = new Vector(x, y, z, units); + + var encoded = testCase.ToList(); + encoded.Should().BeEquivalentTo([x, y, z]); + + const string NEW_UNIT = "something different..."; + var decoded = Vector.FromList(encoded, NEW_UNIT); + + decoded.x.Should().Be(x); + decoded.y.Should().Be(y); + decoded.z.Should().Be(z); + decoded.units.Should().Be(NEW_UNIT); + } + + [Theory] + [MemberData(nameof(TestCases))] + public void ToPoint(double x, double y, double z, string units) + { + var testCase = new Vector(x, y, z, units, "asdfasdf"); + + var asPoint = testCase.ToPoint(); + + asPoint.x.Should().Be(x); + asPoint.y.Should().Be(y); + asPoint.z.Should().Be(z); + asPoint.units.Should().Be(units); + asPoint.applicationId.Should().Be("asdfasdf"); + } + + [Theory] + [MemberData(nameof(TestCases))] + public void Normalize(double x, double y, double z, string units) + { + var sut = new Vector(x, y, z, units); + var originalLength = sut.Length; + sut.Normalize(); + + if (!(originalLength > 0)) + { + sut.Length.Should().Be(double.NaN); + return; + } + + sut.Length.Should().BeApproximately(1, FLOAT_TOLERANCE); + + var rescaled = sut * originalLength; + + rescaled.x.Should().BeApproximately(x, FLOAT_TOLERANCE); + rescaled.y.Should().BeApproximately(y, FLOAT_TOLERANCE); + rescaled.z.Should().BeApproximately(z, FLOAT_TOLERANCE); + rescaled.units.Should().Be(units); + } + + [Theory] + [MemberData(nameof(TestCases))] + public void Negate(double x, double y, double z, string units) + { + var sut = new Vector(x, y, z, units); + var originalLength = sut.Length; + sut.Negate(); + + sut.Length.Should().Be(originalLength); + var rescaled = sut.Negate(); + + rescaled.x.Should().BeApproximately(x, FLOAT_TOLERANCE); + rescaled.y.Should().BeApproximately(y, FLOAT_TOLERANCE); + rescaled.z.Should().BeApproximately(z, FLOAT_TOLERANCE); + rescaled.units.Should().Be(units); + } + + [Theory] + [MemberData(nameof(TestCases))] + public void TestAddition(double x, double y, double z, string units) + { + var operand1 = new Vector(x, y, z, units); + var operand2 = new Vector(x, y, z, units); + + var result = operand1 + operand2; + double[] expected = [x + x, y + y, z + z]; + + result.ToList().Should().BeEquivalentTo(expected); + result.units.Should().BeEquivalentTo(units); + } + + [Theory] + [MemberData(nameof(TestCases))] + public void TestSubtraction(double x, double y, double z, string units) + { + var operand1 = new Vector(x, y, z, units); + var operand2 = new Vector(x, y, z, units); + + var result = operand1 - operand2; + double[] expected = [x - x, y - y, z - z]; + + result.ToList().Should().BeEquivalentTo(expected); + result.units.Should().BeEquivalentTo(units); + } + + [Theory] + [MemberData(nameof(TestCases))] + public void TestDivision(double x, double y, double z, string units) + { + var operand1 = new Vector(x, y, z, units); + const int OPERAND2 = 2; + + var result = operand1 / OPERAND2; + double[] expected = [x / OPERAND2, y / OPERAND2, z / OPERAND2]; + + result.ToList().Should().BeEquivalentTo(expected); + result.units.Should().BeEquivalentTo(units); + } + + [Theory] + [MemberData(nameof(TestCases))] + public void TestMultiplication(double x, double y, double z, string units) + { + var operand1 = new Vector(x, y, z, units); + const int OPERAND2 = 2; + + var result = operand1 * OPERAND2; + double[] expected = [x * OPERAND2, y * OPERAND2, z * OPERAND2]; + + result.ToList().Should().BeEquivalentTo(expected); + result.units.Should().BeEquivalentTo(units); + } + + [Theory] + [MemberData(nameof(TestCases))] + public void TestDotProduct(double x, double y, double z, string units) + { + var operand1 = new Vector(x, y, z, units); + var operand2 = new Vector(x, y, z, units); + + var result = Vector.DotProduct(operand1, operand2); + double expected = Vector3.Dot(new Vector3(x, y, z), new Vector3(x, y, z)); + + result.Should().BeApproximately(expected, FLOAT_TOLERANCE); + } + + [Theory] + [MemberData(nameof(TestCases))] + public void TestCrossProduct(double x, double y, double z, string units) + { + var operand1 = new Vector(x, y, z, units); + var operand2 = new Vector(x, y, z, units); + + var result = Vector.CrossProduct(operand1, operand2); + var expected = Vector3.Cross(new Vector3(x, y, z), new Vector3(x, y, z)); + + result.x.Should().BeApproximately(expected.X, FLOAT_TOLERANCE); + result.y.Should().BeApproximately(expected.Y, FLOAT_TOLERANCE); + result.z.Should().BeApproximately(expected.Z, FLOAT_TOLERANCE); + } + + [Theory] + [MemberData(nameof(TestCases))] + [Obsolete("Tests Obsolete legacy behaviour to maintain backwards json compatibility with ~2.13? data")] + public void TestLegacyValueProp(double x, double y, double z, string _) + { + var vector = Activator.CreateInstance(); + vector.value = [x, y, z]; + + vector.x.Should().Be(x); + vector.y.Should().Be(y); + vector.z.Should().Be(z); + } +} diff --git a/tests/Speckle.Objects.Tests.Unit/Global.cs b/tests/Speckle.Objects.Tests.Unit/Global.cs new file mode 100644 index 00000000..c7fc3b1f --- /dev/null +++ b/tests/Speckle.Objects.Tests.Unit/Global.cs @@ -0,0 +1 @@ +[assembly: CollectionBehavior(DisableTestParallelization = true)] diff --git a/tests/Speckle.Objects.Tests.Unit/ModelPropertySupportedTypes.cs b/tests/Speckle.Objects.Tests.Unit/ModelPropertySupportedTypes.cs index 40f4ff8f..c3e8e805 100644 --- a/tests/Speckle.Objects.Tests.Unit/ModelPropertySupportedTypes.cs +++ b/tests/Speckle.Objects.Tests.Unit/ModelPropertySupportedTypes.cs @@ -1,18 +1,12 @@ -using System; -using System.Collections.Generic; using System.Drawing; -using System.Linq; -using System.Reflection; -using NUnit.Framework; +using FluentAssertions; using Speckle.DoubleNumerics; using Speckle.Newtonsoft.Json; -using Speckle.Objects; -using Speckle.Sdk.Common; using Speckle.Sdk.Host; using Speckle.Sdk.Models; using Speckle.Sdk.Serialisation; -namespace Objects.Tests.Unit; +namespace Speckle.Objects.Tests.Unit; /// /// Tests that all Base object models in the kit have properties that are an allowed type @@ -21,8 +15,7 @@ namespace Objects.Tests.Unit; /// public class ModelPropertySupportedTypes { - [SetUp] - public void Setup() + public ModelPropertySupportedTypes() { TypeLoader.Reset(); TypeLoader.Initialize(typeof(Base).Assembly, typeof(Speckle.Objects.Geometry.Arc).Assembly); @@ -35,41 +28,40 @@ public class ModelPropertySupportedTypes /// /// 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 + /// Check the /// (or is an interface where all concrete types are supported) /// You should also consider adding a test in SerializerNonBreakingChanges /// - private static 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), - }; + private static 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] + [Fact] public void TestObjects() { foreach ((string _, Type type, List _) in TypeLoader.Types) @@ -79,15 +71,24 @@ public class ModelPropertySupportedTypes 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"); + + _allowedTypes.Should().Contain(typeDef, $"{typeDef} was not in allowedTypes. (Origin: {type}.{prop.Name})"); } } } diff --git a/tests/Speckle.Objects.Tests.Unit/Module.cs b/tests/Speckle.Objects.Tests.Unit/Module.cs new file mode 100644 index 00000000..999f2e69 --- /dev/null +++ b/tests/Speckle.Objects.Tests.Unit/Module.cs @@ -0,0 +1,17 @@ +using System.Runtime.CompilerServices; +using Speckle.Objects.Geometry; +using Speckle.Sdk.Host; +using Speckle.Sdk.Testing; + +namespace Speckle.Objects.Tests.Unit; + +public static class Module +{ + [ModuleInitializer] + public static void Initialize() + { + SpeckleVerify.Initialize(); + TypeLoader.Reset(); + TypeLoader.Initialize(typeof(Polyline).Assembly); + } +} diff --git a/tests/Speckle.Objects.Tests.Unit/ObjectBaseValidityTests.cs b/tests/Speckle.Objects.Tests.Unit/ObjectBaseValidityTests.cs index d09c4964..051ee49c 100644 --- a/tests/Speckle.Objects.Tests.Unit/ObjectBaseValidityTests.cs +++ b/tests/Speckle.Objects.Tests.Unit/ObjectBaseValidityTests.cs @@ -1,43 +1,42 @@ -using NUnit.Framework; -using Shouldly; +using FluentAssertions; using Speckle.Objects.Geometry; using Speckle.Objects.Geometry.Autocad; -using Speckle.Objects.Structural.GSA.Geometry; using Speckle.Sdk.Host; using Speckle.Sdk.Models; +using Point = Speckle.Objects.Geometry.Point; -namespace Objects.Tests.Unit; +namespace Speckle.Objects.Tests.Unit; public class ObjectBaseValidityTests { - [Test] + [Fact] public void TestThatTypeWithoutAttributeFails() { TypeLoader.Reset(); - TypeLoader.Initialize(typeof(Base).Assembly, typeof(GSAAssembly).Assembly); + TypeLoader.Initialize(typeof(Base).Assembly, typeof(Point).Assembly); } - [Test] + [Fact] public void InheritanceTest_Disallow() { var exception = Assert.Throws(() => { TypeLoader.Reset(); - TypeLoader.Initialize(typeof(Base).Assembly, typeof(GSAAssembly).Assembly, typeof(Test).Assembly); + TypeLoader.Initialize(typeof(Base).Assembly, typeof(Point).Assembly, typeof(Test).Assembly); }); - exception.ShouldNotBeNull(); - exception.Message.ShouldBe( - "Objects.Tests.Unit.ObjectBaseValidityTests+Test inherits from Base has no SpeckleTypeAttribute" - ); + exception.Should().NotBeNull(); + exception + .Message.Should() + .Be("Speckle.Objects.Tests.Unit.ObjectBaseValidityTests+Test inherits from Base has no SpeckleTypeAttribute"); } - [Test] + [Fact] public void InheritanceTest_Allow() { TypeLoader.Reset(); - TypeLoader.Initialize(typeof(Base).Assembly, typeof(GSAAssembly).Assembly); + TypeLoader.Initialize(typeof(Base).Assembly, typeof(Point).Assembly); var fullTypeString = TypeLoader.GetFullTypeString(typeof(AutocadPolycurve)); - fullTypeString.ShouldBe("Objects.Geometry.Polycurve:Objects.Geometry.Autocad.AutocadPolycurve"); + fullTypeString.Should().Be("Objects.Geometry.Polycurve:Objects.Geometry.Autocad.AutocadPolycurve"); } public class Test : Polycurve; 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 fb22cc63..15eb3455 100644 --- a/tests/Speckle.Objects.Tests.Unit/Speckle.Objects.Tests.Unit.csproj +++ b/tests/Speckle.Objects.Tests.Unit/Speckle.Objects.Tests.Unit.csproj @@ -8,13 +8,14 @@ + - - - + + + diff --git a/tests/Speckle.Objects.Tests.Unit/Utils/MeshTriangulationHelperTests.cs b/tests/Speckle.Objects.Tests.Unit/Utils/MeshTriangulationHelperTests.cs index 43fe3842..cdd3cf2a 100644 --- a/tests/Speckle.Objects.Tests.Unit/Utils/MeshTriangulationHelperTests.cs +++ b/tests/Speckle.Objects.Tests.Unit/Utils/MeshTriangulationHelperTests.cs @@ -1,19 +1,27 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using NUnit.Framework; +using FluentAssertions; using Speckle.Objects.Geometry; using Speckle.Objects.Utils; +using Speckle.Sdk.Common; +using Speckle.Sdk.Dependencies; -namespace Objects.Tests.Unit.Utils; +namespace Speckle.Objects.Tests.Unit.Utils; -[TestFixture, TestOf(typeof(MeshTriangulationHelper))] public class MeshTriangulationHelperTests { - [Test] - public void PolygonTest([Range(3, 9)] int n, [Values] bool planar) + public static IEnumerable PolygonTestSource() { - //Test Setup + foreach (var x in EnumerableExtensions.RangeFrom(3, 9)) + { + yield return new object[] { x, true }; + yield return new object[] { x, false }; + } + } + + [Theory] + [MemberData(nameof(PolygonTestSource))] + public void PolygonTest(int n, bool planar) + { + // Test Setup List vertices = new(n) { 0, planar ? 0 : 1, 1 }; for (int i = 1; i < n; i++) { @@ -25,68 +33,87 @@ public class MeshTriangulationHelperTests List faces = new(n + 1) { n }; faces.AddRange(Enumerable.Range(0, n)); - Mesh mesh = new(vertices, faces); + Mesh mesh = new() + { + vertices = vertices, + faces = faces, + units = Units.Meters, + }; - //Test + // Test mesh.TriangulateMesh(); - //Results + // Results int numExpectedTriangles = n - 2; int expectedFaceCount = numExpectedTriangles * 4; - Assert.That(mesh.faces, Has.Count.EqualTo(expectedFaceCount)); + mesh.faces.Count.Should().Be(expectedFaceCount); + for (int i = 0; i < expectedFaceCount; i += 4) { - Assert.That(mesh.faces[i], Is.EqualTo(3)); - Assert.That(mesh.faces.GetRange(i + 1, 3), Is.Unique); + mesh.faces[i].Should().Be(3); + mesh.faces.GetRange(i + 1, 3).Should().OnlyHaveUniqueItems(); } - Assert.That(mesh.faces, Is.SupersetOf(Enumerable.Range(0, n))); - - Assert.That(mesh.faces, Is.All.GreaterThanOrEqualTo(0)); - Assert.That(mesh.faces, Is.All.LessThan(Math.Max(n, 4))); + var range = EnumerableExtensions.RangeFrom(0, n).ToList(); + mesh.faces.Should().BeSubsetOf(range); + mesh.faces.Should().AllSatisfy(x => x.Should().BeGreaterThanOrEqualTo(0).And.BeLessThan(Math.Max(n, 4))); } - [Test] + [Fact] public void DoesntFlipNormals() { - //Test Setup + // Test Setup List vertices = new() { 0, 0, 0, 1, 0, 0, 1, 0, 1 }; List faces = new() { 3, 0, 1, 2 }; - Mesh mesh = new(vertices, new List(faces)); + Mesh mesh = new() + { + vertices = vertices, + faces = new List(faces), + units = Units.Meters, + }; - //Test + // Test mesh.TriangulateMesh(); - //Results - + // Results List shift1 = faces; List shift2 = new() { 3, 1, 2, 0 }; List shift3 = new() { 3, 2, 0, 1 }; - Assert.That(mesh.faces, Is.AnyOf(shift1, shift2, shift3)); + new List[] { shift1, shift2, shift3 } + .Any(x => mesh.faces.SequenceEqual(x)) + .Should() + .BeTrue(); } - [Test] - public void PreserveQuads([Values] bool preserveQuads) + [Theory] + [InlineData(true)] + [InlineData(false)] + public void PreserveQuads(bool preserveQuads) { - //Test Setup + // Test Setup List vertices = new() { 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1 }; List faces = new() { 4, 0, 1, 2, 3 }; - Mesh mesh = new(vertices, new List(faces)); + Mesh mesh = new() + { + vertices = vertices, + faces = new List(faces), + units = Units.Meters, + }; - //Test + // Tests mesh.TriangulateMesh(preserveQuads); - //Results + // Results int expectedN = preserveQuads ? 4 : 3; int expectedFaceCount = preserveQuads ? 5 : 8; - Assert.That(mesh.faces, Has.Count.EqualTo(expectedFaceCount)); - Assert.That(mesh.faces[0], Is.EqualTo(expectedN)); + mesh.faces.Count.Should().Be(expectedFaceCount); + mesh.faces[0].Should().Be(expectedN); } } diff --git a/tests/Speckle.Objects.Tests.Unit/Utils/ShallowCopyTests.cs b/tests/Speckle.Objects.Tests.Unit/Utils/ShallowCopyTests.cs index da88569e..e7375eff 100644 --- a/tests/Speckle.Objects.Tests.Unit/Utils/ShallowCopyTests.cs +++ b/tests/Speckle.Objects.Tests.Unit/Utils/ShallowCopyTests.cs @@ -1,26 +1,48 @@ using System.Collections; -using System.Collections.Generic; -using NUnit.Framework; -using Speckle.Objects.BuiltElements; +using FluentAssertions; +using Speckle.Objects.Data; using Speckle.Objects.Geometry; using Speckle.Sdk.Common; +using Speckle.Sdk.Host; +using Speckle.Sdk.Models; -namespace Objects.Tests.Unit.Utils; +namespace Speckle.Objects.Tests.Unit.Utils; -[TestFixture] public class ShallowCopyTests { - [Test] + public ShallowCopyTests() + { + TypeLoader.Reset(); + TypeLoader.Initialize(typeof(Base).Assembly, typeof(Point).Assembly); + } + + [Fact] public void CanShallowCopy_Wall() { - var wall = new Wall(5, new Line(new Point(0, 0), new Point(3, 0))) + const string UNITS = Units.Meters; + var ds = new DataObject() { - units = Units.Meters, - displayValue = new List { new(), new() } + name = "directShape", + displayValue = new List + { + new Mesh + { + vertices = new(), + faces = new(), + units = UNITS, + }, + new Mesh + { + vertices = new(), + faces = new(), + units = UNITS, + }, + }, + properties = new Dictionary(), }; - var shallow = wall.ShallowCopy(); + var shallow = ds.ShallowCopy(); var displayValue = (IList)shallow["displayValue"].NotNull(); - Assert.That(wall.displayValue, Has.Count.EqualTo(displayValue.Count)); + ds.displayValue.Count.Should().Be(displayValue.Count); } } diff --git a/tests/Speckle.Objects.Tests.Unit/packages.lock.json b/tests/Speckle.Objects.Tests.Unit/packages.lock.json index 6b3de7b8..3eb30310 100644 --- a/tests/Speckle.Objects.Tests.Unit/packages.lock.json +++ b/tests/Speckle.Objects.Tests.Unit/packages.lock.json @@ -4,24 +4,24 @@ "net8.0": { "altcover": { "type": "Direct", - "requested": "[8.8.74, )", - "resolved": "8.8.74", - "contentHash": "e8RZNE0vZnuBk/gOAWu9K5wm3S7dOrOlZje3PHI9PJUHqvP1cxVJD1eXAAmddFVlixowB7C7/zvC16GnunC2LQ==" + "requested": "[9.0.1, )", + "resolved": "9.0.1", + "contentHash": "aadciFNDT5bnylaYUkKal+s5hF7yU/lmZxImQWAlk1438iPqK1Uf79H5ylELpyLIU49HL5ql+tnWBihp3WVLCA==" }, - "GitVersion.MsBuild": { + "AwesomeAssertions": { "type": "Direct", - "requested": "[5.12.0, )", - "resolved": "5.12.0", - "contentHash": "dJuigXycpJNOiLT9or7mkHSkGFHgGW3/p6cNNYEKZBa7Hhp1FdX/cvqYWWYhRLpfoZOedeA7aRbYiOB3vW/dvA==" + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "6fWiV7mGZUzZXzeiW3hWF0nJokuuNm4hnzuqbM3IXHqGYkWnHl65+wNpuQ73xfJXClX0fmfKcTdQ2Ula719IDg==" }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.10.0, )", - "resolved": "17.10.0", - "contentHash": "0/2HeACkaHEYU3wc83YlcD2Fi4LMtECJjqrtvw0lPi9DCEa35zSPt1j4fuvM8NagjDqJuh1Ja35WcRtn1Um6/A==", + "requested": "[17.13.0, )", + "resolved": "17.13.0", + "contentHash": "W19wCPizaIC9Zh47w8wWI/yxuqR7/dtABwOrc8r2jX/8mUNxM2vw4fXDh+DJTeogxV+KzKwg5jNNGQVwf3LXyA==", "dependencies": { - "Microsoft.CodeCoverage": "17.10.0", - "Microsoft.TestPlatform.TestHost": "17.10.0" + "Microsoft.CodeCoverage": "17.13.0", + "Microsoft.TestPlatform.TestHost": "17.13.0" } }, "Microsoft.SourceLink.GitHub": { @@ -34,33 +34,11 @@ "Microsoft.SourceLink.Common": "8.0.0" } }, - "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" - } + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g==" }, "Speckle.InterfaceGenerator": { "type": "Direct", @@ -68,19 +46,41 @@ "resolved": "0.9.6", "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" }, + "xunit.assert": { + "type": "Direct", + "requested": "[2.9.3, )", + "resolved": "2.9.3", + "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA==" + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[3.0.2, )", + "resolved": "3.0.2", + "contentHash": "oXbusR6iPq0xlqoikjdLvzh+wQDkMv9If58myz9MEzldS4nIcp442Btgs2sWbYWV+caEluMe2pQCZ0hUZgPiow==" + }, + "Argon": { + "type": "Transitive", + "resolved": "0.26.0", + "contentHash": "n7btGXdtRyprGnpLMpBs6rLScxlvPtVWwmTR8h7CtJvpZXBGhGvibEdZxRjeTZNrwf403jJ0ZPpt35Pz/NaNsw==" + }, "DiffEngine": { "type": "Transitive", - "resolved": "11.3.0", - "contentHash": "k0ZgZqd09jLZQjR8FyQbSQE86Q7QZnjEzq1LPHtj1R2AoWO8sjV5x+jlSisL7NZAbUOI4y+7Bog8gkr9WIRBGw==", + "resolved": "15.9.0", + "contentHash": "shE6+tO4w5BmQTX0z+WnUV4UfmPNn6oTqBINbkts6OP0Icyx5WROSDzjjb95EwVYC4IAS+PxxS4Vbapxz4hkdw==", "dependencies": { - "EmptyFiles": "4.4.0", - "System.Management": "6.0.1" + "EmptyFiles": "8.7.1", + "System.Management": "9.0.1" } }, "EmptyFiles": { "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "gwJEfIGS7FhykvtZoscwXj/XwW+mJY6UbAZk+qtLKFUGWC95kfKXnj8VkxsZQnWBxJemM/q664rGLN5nf+OHZw==" + "resolved": "8.7.1", + "contentHash": "C8pvg0TvG2Mkn5LGNFGkFgFu8SUgYFwiu8U3y34qGQnnwKmGnlQTfTIUrtzfSjPxA4q7L/kRu09U5p32otZ2Aw==" + }, + "FSharp.Core": { + "type": "Transitive", + "resolved": "7.0.300", + "contentHash": "8vvItREJ1l5lcp3vBCSJ1mFevVAhR48I34DuF/EoUa7o1KlFpQpagyuZkVYMAsHPIjdp47ZxM9sI4eqeXaeWkA==" }, "GraphQL.Client.Abstractions": { "type": "Transitive", @@ -110,17 +110,65 @@ }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "17.10.0", - "contentHash": "yC7oSlnR54XO5kOuHlVOKtxomNNN1BWXX8lK1G2jaPXT9sUok7kCOoA4Pgs0qyFaCtMrNsprztYMeoEGqCm4uA==" + "resolved": "17.13.0", + "contentHash": "9LIUy0y+DvUmEPtbRDw6Bay3rzwqFV8P4efTrK4CZhQle3M/QwLPjISghfcolmEGAPWxuJi6m98ZEfk4VR4Lfg==" }, "Microsoft.Data.Sqlite.Core": { "type": "Transitive", - "resolved": "7.0.7", - "contentHash": "21FRzcJhaTrlv7kTrqr/ltFcSQM2TyuTTPhUcjO8H73od7Bb3QraNW90c7lUucNI/245XPkKZG4fp7/7OsKCSg==", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", "dependencies": { "SQLitePCLRaw.core": "2.1.4" } }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "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.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -128,18 +176,18 @@ }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.10.0", - "contentHash": "KkwhjQevuDj0aBRoPLY6OLAhGqbPUEBuKLbaCs0kUVw29qiOYncdORd4mLVJbn9vGZ7/iFGQ/+AoJl0Tu5Umdg==", + "resolved": "17.13.0", + "contentHash": "bt0E0Dx+iqW97o4A59RCmUmz/5NarJ7LRL+jXbSHod72ibL5XdNm1Ke+UO5tFhBG4VwHLcSjqq9BUSblGNWamw==", "dependencies": { "System.Reflection.Metadata": "1.6.0" } }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.10.0", - "contentHash": "LWpMdfqhHvcUkeMCvNYJO8QlPLlYz9XPPb+ZbaXIKhdmjAV0wqTSrTiW5FLaf7RRZT50AQADDOYMOe0HxDxNgA==", + "resolved": "17.13.0", + "contentHash": "9GGw08Dc3AXspjekdyTdZ/wYWFlxbgcF0s7BKxzVX+hzAwpifDOdxM+ceVaaJSQOwqt3jtuNlHn3XTpKUS9x9Q==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.10.0", + "Microsoft.TestPlatform.ObjectModel": "17.13.0", "Newtonsoft.Json": "13.0.1" } }, @@ -148,6 +196,20 @@ "resolved": "13.0.1", "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" }, + "Quibble": { + "type": "Transitive", + "resolved": "0.3.1", + "contentHash": "LD6bz2p+4O/BQnmD4mqFZrmdN/IjsPo1wUvfmcH46Q05ng+dyMLl3d2ylj0x412F4fpJEtm0Z3EaCAx4FqgNuQ==", + "dependencies": { + "FSharp.Core": "7.0.300", + "System.Text.Json": "7.0.3" + } + }, + "SimpleInfoName": { + "type": "Transitive", + "resolved": "3.1.0", + "contentHash": "j+ENh86NhxrgDc6T1ueqIR2QOdDkSJY2dbTFyPN/JvIXifB4GHAunlMw/x7P6m7XaXEHr3s+SMZfKBlmnmkO6g==" + }, "SQLitePCLRaw.bundle_e_sqlite3": { "type": "Transitive", "resolved": "2.1.4", @@ -180,15 +242,25 @@ }, "System.CodeDom": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA==" + "resolved": "9.0.1", + "contentHash": "2J5uq+2smnj+u1jlyVJ6BGGqaK9fHcK/EwN7mbsuPqTI6dZr86br8Cg6o/5B+icQ9ANTvTDpJjnhDNtYYZijHQ==" + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.IO.Hashing": { + "type": "Transitive", + "resolved": "9.0.1", + "contentHash": "jY+E/PElNWQiazN0YHqZGvcSedcZ4Wt0Os1nnJ2SzR3gWZlhNRDkSXOhuHJcLuImD8SrJQQ8TfU0W4mVcit2hg==" }, "System.Management": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "10J1D0h/lioojphfJ4Fuh5ZUThT/xOVHdV9roGBittKKNP2PMjrvibEdbVTGZcPra1399Ja3tqIJLyQrc5Wmhg==", + "resolved": "9.0.1", + "contentHash": "CLEo9O6FuO4GQ3ZQkGssg9CJ2w2TN7GMFf3wHTc7YVWJV4xoyJRPw+XIDQnCcSUJCrHhrAWOO60cAX29EV5LFQ==", "dependencies": { - "System.CodeDom": "6.0.0" + "System.CodeDom": "9.0.1" } }, "System.Memory": { @@ -206,6 +278,56 @@ "resolved": "1.6.0", "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "8.0.4", + "contentHash": "bAkhgDJ88XTsqczoxEMliSrpijKZHhbJQldhAmObj/RbrN3sU5dcokuXmWJWsdQAhiMJ9bTayWsL1C9fbbCRhw==", + "dependencies": { + "System.Text.Encodings.Web": "8.0.0" + } + }, + "Verify": { + "type": "Transitive", + "resolved": "28.10.1", + "contentHash": "2B/VtFN5jtF5g28kaM4GdJZTwb3pisd4+wL2NEPi9ZYe2lghWsCzS30V6LF1ILApLBfAorAstkU/Vw3sDWRqrg==", + "dependencies": { + "Argon": "0.26.0", + "DiffEngine": "15.9.0", + "SimpleInfoName": "3.1.0", + "System.IO.Hashing": "9.0.1" + } + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.extensibility.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==", + "dependencies": { + "xunit.abstractions": "2.0.3" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]" + } + }, "speckle.objects": { "type": "Project", "dependencies": { @@ -217,19 +339,27 @@ "dependencies": { "GraphQL.Client": "[6.0.0, )", "Microsoft.CSharp": "[4.7.0, )", - "Microsoft.Data.Sqlite": "[7.0.7, )", - "Polly": "[7.2.3, )", - "Polly.Contrib.WaitAndRetry": "[1.1.1, )", - "Polly.Extensions.Http": "[3.0.0, )", - "Speckle.DoubleNumerics": "[4.0.1, )", + "Microsoft.Data.Sqlite": "[7.0.5, )", + "Microsoft.Extensions.DependencyInjection.Abstractions": "[2.2.0, )", + "Microsoft.Extensions.Logging": "[2.2.0, )", + "Speckle.DoubleNumerics": "[4.1.0, )", "Speckle.Newtonsoft.Json": "[13.0.2, )", - "Speckle.Sdk.Logging": "[1.0.0, )", - "System.Text.Json": "[5.0.2, )" + "Speckle.Sdk.Dependencies": "[1.0.0, )" } }, - "speckle.sdk.logging": { + "speckle.sdk.dependencies": { "type": "Project" }, + "speckle.sdk.testing": { + "type": "Project", + "dependencies": { + "Microsoft.NET.Test.Sdk": "[17.13.0, )", + "Speckle.Sdk": "[1.0.0, )", + "Verify.Quibble": "[2.1.1, )", + "Verify.Xunit": "[28.10.1, )", + "xunit.runner.visualstudio": "[3.0.2, )" + } + }, "GraphQL.Client": { "type": "CentralTransitive", "requested": "[6.0.0, )", @@ -249,40 +379,37 @@ }, "Microsoft.Data.Sqlite": { "type": "CentralTransitive", - "requested": "[7.0.7, )", - "resolved": "7.0.7", - "contentHash": "tiNmV1oPy+Z2R7Wd0bPB/FxCr8B+/5q11OpDMG751GA/YuOL7MZrBFfzv5oFRlFe08K6sjrnbrauzzGIeNrzLQ==", + "requested": "[7.0.5, )", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", "dependencies": { - "Microsoft.Data.Sqlite.Core": "7.0.7", + "Microsoft.Data.Sqlite.Core": "7.0.5", "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" } }, - "Polly": { + "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "CentralTransitive", - "requested": "[7.2.3, )", - "resolved": "7.2.3", - "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" }, - "Polly.Contrib.WaitAndRetry": { + "Microsoft.Extensions.Logging": { "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==", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", "dependencies": { - "Polly": "7.1.0" + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" } }, "Speckle.DoubleNumerics": { "type": "CentralTransitive", - "requested": "[4.0.1, )", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" }, "Speckle.Newtonsoft.Json": { "type": "CentralTransitive", @@ -290,11 +417,31 @@ "resolved": "13.0.2", "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" }, - "System.Text.Json": { + "Verify.Quibble": { "type": "CentralTransitive", - "requested": "[5.0.2, )", - "resolved": "5.0.2", - "contentHash": "I47dVIGiV6SfAyppphxqupertT/5oZkYLDCX6vC3HpOI4ZLjyoKAreUoem2ie6G0RbRuFrlqz/PcTQjfb2DOfQ==" + "requested": "[2.1.1, )", + "resolved": "2.1.1", + "contentHash": "Z8bVwFICa3Dog6Mcnx0wlrn4Y+CFpQXx1f+ijfLn6/v4q00q+jLm9Gu/nVyUFuc75cjn6ieI08UrqXKcR9fTYw==", + "dependencies": { + "Quibble": "0.3.1", + "System.Text.Json": "8.0.4", + "Verify": "26.1.1" + } + }, + "Verify.Xunit": { + "type": "CentralTransitive", + "requested": "[28.10.1, )", + "resolved": "28.10.1", + "contentHash": "mkG7agMlx8oAEGcHoRY72hyDyNTdLIrzbfmniXFQgQ3yKulAHSYvYc9quzhpg0Sy+jb3svbdLqnRSg0VRhet3A==", + "dependencies": { + "Argon": "0.26.0", + "DiffEngine": "15.9.0", + "SimpleInfoName": "3.1.0", + "System.IO.Hashing": "9.0.1", + "Verify": "28.10.1", + "xunit.abstractions": "2.0.3", + "xunit.extensibility.execution": "2.9.3" + } } } } diff --git a/tests/Speckle.Sdk.Serialization.Testing/Program.cs b/tests/Speckle.Sdk.Serialization.Testing/Program.cs new file mode 100644 index 00000000..51b285eb --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Testing/Program.cs @@ -0,0 +1,70 @@ +#pragma warning disable CA1506 +using System.Reflection; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging.Abstractions; +using Speckle.Sdk; +using Speckle.Sdk.Credentials; +using Speckle.Sdk.Host; +using Speckle.Sdk.Models; +using Speckle.Sdk.Serialisation.V2; +using Speckle.Sdk.Serialisation.V2.Receive; +using Speckle.Sdk.Serialisation.V2.Send; +using Speckle.Sdk.Serialization.Testing; +using Speckle.Sdk.SQLite; + +const bool skipCacheReceive = false; +const bool skipCacheSendCheck = true; +const bool skipCacheSendSave = false; +TypeLoader.Reset(); +TypeLoader.Initialize(typeof(Base).Assembly, Assembly.GetExecutingAssembly()); + +var url = "https://latest.speckle.systems/projects/a3ac1b2706/models/59d3b0f3c6"; //small? +var streamId = "a3ac1b2706"; +var rootId = "7d53bcf28c6696ecac8781684a0aa006"; + +/* +var url = "https://latest.speckle.systems/"; //other? +var streamId = "368f598929"; +var rootId = "67374cfe689c43ff8be12090af122244";*/ + +/* +var url = "https://latest.speckle.systems/projects/2099ac4b5f/models/da511c4d1e"; //perf? +var streamId = "2099ac4b5f"; +var rootId = "30fb4cbe6eb2202b9e7b4a4fcc3dd2b6";*/ + +var serviceCollection = new ServiceCollection(); +serviceCollection.AddSpeckleSdk(HostApplications.Navisworks, HostAppVersion.v2023, "Test"); +var serviceProvider = serviceCollection.BuildServiceProvider(); + +Console.WriteLine("Attach"); + +var token = serviceProvider.GetRequiredService().GetDefaultAccount()?.token; +var progress = new Progress(true); + +var factory = new SerializeProcessFactory( + new BaseChildFinder(new BasePropertyGatherer()), + new ObjectSerializerFactory(new BasePropertyGatherer()), + new BaseDeserializer(new ObjectDeserializerFactory()), + serviceProvider.GetRequiredService(), + serviceProvider.GetRequiredService(), + new NullLoggerFactory() +); +var process = factory.CreateDeserializeProcess(new Uri(url), streamId, token, progress, default, new(skipCacheReceive)); +var @base = await process.Deserialize(rootId).ConfigureAwait(false); +Console.WriteLine("Deserialized"); +Console.ReadLine(); +Console.WriteLine("Executing"); + +var process2 = factory.CreateSerializeProcess( + new Uri(url), + streamId, + token, + progress, + default, + new SerializeProcessOptions(skipCacheSendCheck, skipCacheSendSave, true, true) +); +await process2.Serialize(@base).ConfigureAwait(false); +Console.WriteLine("Detach"); +Console.ReadLine(); +await process2.DisposeAsync().ConfigureAwait(false); +#pragma warning restore CA1506 diff --git a/tests/Speckle.Sdk.Serialization.Testing/Progress.cs b/tests/Speckle.Sdk.Serialization.Testing/Progress.cs new file mode 100644 index 00000000..03476f5a --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Testing/Progress.cs @@ -0,0 +1,36 @@ +using Speckle.Sdk.Transports; + +namespace Speckle.Sdk.Serialization.Testing; + +public class Progress(bool write) : IProgress +{ + private readonly TimeSpan DEBOUNCE = TimeSpan.FromSeconds(1); + private DateTime _lastTime = DateTime.UtcNow; + + private long _totalBytes; + + public void Report(ProgressArgs value) + { + if (write) + { + if (value.ProgressEvent == ProgressEvent.DownloadBytes) + { + Interlocked.Add(ref _totalBytes, value.Count); + } + var now = DateTime.UtcNow; + if (now - _lastTime >= DEBOUNCE) + { + if (value.ProgressEvent == ProgressEvent.DownloadBytes) + { + Console.WriteLine(value.ProgressEvent + " t " + _totalBytes); + } + else + { + Console.WriteLine(value.ProgressEvent + " c " + value.Count + " t " + value.Total); + } + + _lastTime = now; + } + } + } +} diff --git a/tests/Speckle.Sdk.Serialization.Testing/Speckle.Sdk.Serialization.Testing.csproj b/tests/Speckle.Sdk.Serialization.Testing/Speckle.Sdk.Serialization.Testing.csproj new file mode 100644 index 00000000..3d97bc78 --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Testing/Speckle.Sdk.Serialization.Testing.csproj @@ -0,0 +1,17 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + + diff --git a/tests/Speckle.Sdk.Serialization.Testing/packages.lock.json b/tests/Speckle.Sdk.Serialization.Testing/packages.lock.json new file mode 100644 index 00000000..34e09e80 --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Testing/packages.lock.json @@ -0,0 +1,438 @@ +{ + "version": 2, + "dependencies": { + "net8.0": { + "Microsoft.Extensions.DependencyInjection": { + "type": "Direct", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.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" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "BenchmarkDotNet.Annotations": { + "type": "Transitive", + "resolved": "0.14.0", + "contentHash": "CUDCg6bgHrDzhjnA+IOBl5gAo8Y5hZ2YSs7MBXrYMlMKpBZqrD5ez0537uDveOkcf+YWAoK+S4sMcuWPbIz8bw==" + }, + "CommandLineParser": { + "type": "Transitive", + "resolved": "2.9.1", + "contentHash": "OE0sl1/sQ37bjVsPKKtwQlWDgqaxWgtme3xZz7JssWUzg5JpMIyHgCTY9MVMxOg48fJ1AgGT3tgdH5m/kQ5xhA==" + }, + "Gee.External.Capstone": { + "type": "Transitive", + "resolved": "2.3.0", + "contentHash": "2ap/rYmjtzCOT8hxrnEW/QeiOt+paD8iRrIcdKX0cxVwWLFa1e+JDBNeECakmccXrSFeBQuu5AV8SNkipFMMMw==" + }, + "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==" + }, + "Iced": { + "type": "Transitive", + "resolved": "1.17.0", + "contentHash": "8x+HCVTl/HHTGpscH3vMBhV8sknN/muZFw9s3TsI8SA6+c43cOTCi2+jE4KsU8pNLbJ++iF2ZFcpcXHXtDglnw==" + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CodeAnalysis.Analyzers": { + "type": "Transitive", + "resolved": "3.3.3", + "contentHash": "j/rOZtLMVJjrfLRlAMckJLPW/1rze9MT1yfWqSIbUPGRu1m1P0fuo9PmqapwsmePfGB5PJrudQLvmUOAMF0DqQ==" + }, + "Microsoft.CodeAnalysis.Common": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "bNzTyxP3iD5FPFHfVDl15Y6/wSoI7e3MeV0lOaj9igbIKTjgrmuw6LoVJ06jUNFA7+KaDC/OIsStWl/FQJz6sQ==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.3.3", + "System.Collections.Immutable": "5.0.0", + "System.Memory": "4.5.4", + "System.Reflection.Metadata": "5.0.0", + "System.Runtime.CompilerServices.Unsafe": "5.0.0", + "System.Text.Encoding.CodePages": "4.5.1", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.CodeAnalysis.CSharp": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "sbu6kDGzo9bfQxuqWpeEE7I9P30bSuZEnpDz9/qz20OU6pm79Z63+/BsAzO2e/R/Q97kBrpj647wokZnEVr97w==", + "dependencies": { + "Microsoft.CodeAnalysis.Common": "[4.1.0]" + } + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Diagnostics.NETCore.Client": { + "type": "Transitive", + "resolved": "0.2.251802", + "contentHash": "bqnYl6AdSeboeN4v25hSukK6Odm6/54E3Y2B8rBvgqvAW0mF8fo7XNRVE2DMOG7Rk0fiuA079QIH28+V+W1Zdg==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "1.1.0", + "Microsoft.Extensions.Logging": "2.1.1" + } + }, + "Microsoft.Diagnostics.Runtime": { + "type": "Transitive", + "resolved": "2.2.332302", + "contentHash": "Hp84ivxSKIMTBzYSATxmUsm3YSXHWivcwiRRbsydGmqujMUK8BAueLN0ssAVEOkOBmh0vjUBhrq7YcroT7VCug==", + "dependencies": { + "Microsoft.Diagnostics.NETCore.Client": "0.2.251802", + "System.Collections.Immutable": "5.0.0", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, + "Microsoft.Diagnostics.Tracing.TraceEvent": { + "type": "Transitive", + "resolved": "3.1.8", + "contentHash": "kl3UMrZKSeSEYZ8rt/GjLUQToREjgQABqfg6PzQBmSlYHTZOKE9ePEOS2xptROQ9SVvngg3QGX51TIT11iZ0wA==", + "dependencies": { + "Microsoft.Win32.Registry": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, + "Microsoft.DotNet.PlatformAbstractions": { + "type": "Transitive", + "resolved": "3.1.6", + "contentHash": "jek4XYaQ/PGUwDKKhwR8K47Uh1189PFzMeLqO83mXrXQVIpARZCcfuDedH50YDTepBkfijCZN5U/vZi++erxtg==" + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "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.NETCore.Platforms": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "Perfolizer": { + "type": "Transitive", + "resolved": "0.3.17", + "contentHash": "FQgtCoF2HFwvzKWulAwBS5BGLlh8pgbrJtOp47jyBwh2CW16juVtacN1azOA2BqdrJXkXTNLNRMo7ZlHHiuAnA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "JPJArwA1kdj8qDAkY2XGjSWoYnqiM7q/3yRNkt6n28Mnn95MuEGkZXUbPBf7qc3IjwrGY5ttQon7yqHZyQJmOQ==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==" + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.Management": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "MF1CHaRcC+MLFdnDthv4/bKWBZnlnSpkGqa87pKukQefgEdwtb9zFW6zs0GjPp73qtpYYg4q6PEKbzJbxCpKfw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "Microsoft.Win32.Registry": "5.0.0", + "System.CodeDom": "5.0.0" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==" + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "System.Text.Encoding.CodePages": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "4J2JQXbftjPMppIHJ7IC+VXQ9XfEagN92vZZNoG12i+zReYlim5dMoXFC1Zzg7tsnKDM7JPo5bYfFK4Jheq44w==", + "dependencies": { + "Microsoft.NETCore.Platforms": "2.1.2", + "System.Runtime.CompilerServices.Unsafe": "4.5.2" + } + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==" + }, + "speckle.objects": { + "type": "Project", + "dependencies": { + "Speckle.Sdk": "[1.0.0, )" + } + }, + "speckle.sdk": { + "type": "Project", + "dependencies": { + "GraphQL.Client": "[6.0.0, )", + "Microsoft.CSharp": "[4.7.0, )", + "Microsoft.Data.Sqlite": "[7.0.5, )", + "Microsoft.Extensions.DependencyInjection.Abstractions": "[2.2.0, )", + "Microsoft.Extensions.Logging": "[2.2.0, )", + "Speckle.DoubleNumerics": "[4.1.0, )", + "Speckle.Newtonsoft.Json": "[13.0.2, )", + "Speckle.Sdk.Dependencies": "[1.0.0, )" + } + }, + "speckle.sdk.dependencies": { + "type": "Project" + }, + "speckle.sdk.tests.performance": { + "type": "Project", + "dependencies": { + "BenchmarkDotNet": "[0.14.0, )", + "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", + "Speckle.Objects": "[1.0.0, )" + } + }, + "BenchmarkDotNet": { + "type": "CentralTransitive", + "requested": "[0.14.0, )", + "resolved": "0.14.0", + "contentHash": "eIPSDKi3oni734M1rt/XJAwGQQOIf9gLjRRKKJ0HuVy3vYd7gnmAIX1bTjzI9ZbAY/nPddgqqgM/TeBYitMCIg==", + "dependencies": { + "BenchmarkDotNet.Annotations": "0.14.0", + "CommandLineParser": "2.9.1", + "Gee.External.Capstone": "2.3.0", + "Iced": "1.17.0", + "Microsoft.CodeAnalysis.CSharp": "4.1.0", + "Microsoft.Diagnostics.Runtime": "2.2.332302", + "Microsoft.Diagnostics.Tracing.TraceEvent": "3.1.8", + "Microsoft.DotNet.PlatformAbstractions": "3.1.6", + "Perfolizer": "[0.3.17]", + "System.Management": "5.0.0" + } + }, + "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.Bcl.AsyncInterfaces": { + "type": "CentralTransitive", + "requested": "[5.0.0, )", + "resolved": "1.1.0", + "contentHash": "1Am6l4Vpn3/K32daEqZI+FFr96OlZkgwK2LcT3pZ2zWubR5zTPW3/FkO1Rat9kb7oQOa4rxgl9LJHc5tspCWfg==" + }, + "Microsoft.CSharp": { + "type": "CentralTransitive", + "requested": "[4.7.0, )", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "CentralTransitive", + "requested": "[7.0.5, )", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "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.Sdk.Serialization.Tests/CancellationSqLiteJsonCacheManager.cs b/tests/Speckle.Sdk.Serialization.Tests/CancellationSqLiteJsonCacheManager.cs new file mode 100644 index 00000000..c4d8d355 --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/CancellationSqLiteJsonCacheManager.cs @@ -0,0 +1,51 @@ +using Speckle.Sdk.Serialisation.V2; +using Speckle.Sdk.Serialisation.V2.Send; +using Speckle.Sdk.Testing.Framework; +using Speckle.Sdk.Transports; + +namespace Speckle.Sdk.Serialization.Tests; + +public sealed class CancellationSqLiteJsonCacheManager(CancellationTokenSource cancellationTokenSource) + : DummySqLiteJsonCacheManager +{ + public override void SaveObjects(IEnumerable<(string id, string json)> items) + { + cancellationTokenSource.Cancel(); + cancellationTokenSource.Token.ThrowIfCancellationRequested(); + } +} + +public class CancellationSqLiteSendManager(CancellationTokenSource cancellationTokenSource) : DummySqLiteSendManager +{ + public override void SaveObjects(IEnumerable<(string id, string json)> items) + { + cancellationTokenSource.Cancel(); + cancellationTokenSource.Token.ThrowIfCancellationRequested(); + } +} + +public class CancellationServerObjectManager(CancellationTokenSource cancellationTokenSource) : DummyServerObjectManager +{ + public override Task UploadObjects( + IReadOnlyList objects, + bool compressPayloads, + IProgress? progress, + CancellationToken cancellationToken + ) + { + cancellationTokenSource.Cancel(); + cancellationTokenSource.Token.ThrowIfCancellationRequested(); + return base.UploadObjects(objects, compressPayloads, progress, cancellationToken); + } + + public override Task DownloadSingleObject( + string objectId, + IProgress? progress, + CancellationToken cancellationToken + ) + { + cancellationTokenSource.Cancel(); + cancellationTokenSource.Token.ThrowIfCancellationRequested(); + return base.DownloadSingleObject(objectId, progress, cancellationToken); + } +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/CancellationTests.Cancellation_Receive_Cache.verified.json b/tests/Speckle.Sdk.Serialization.Tests/CancellationTests.Cancellation_Receive_Cache.verified.json new file mode 100644 index 00000000..94cd413d --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/CancellationTests.Cancellation_Receive_Cache.verified.json @@ -0,0 +1,5 @@ +{ + "Type": "System.OperationCanceledException", + "Message": "The operation was canceled.", + "Source": "System.Private.CoreLib" +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/CancellationTests.Cancellation_Receive_Deserialize.verified.json b/tests/Speckle.Sdk.Serialization.Tests/CancellationTests.Cancellation_Receive_Deserialize.verified.json new file mode 100644 index 00000000..94cd413d --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/CancellationTests.Cancellation_Receive_Deserialize.verified.json @@ -0,0 +1,5 @@ +{ + "Type": "System.OperationCanceledException", + "Message": "The operation was canceled.", + "Source": "System.Private.CoreLib" +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/CancellationTests.Cancellation_Receive_Server.verified.json b/tests/Speckle.Sdk.Serialization.Tests/CancellationTests.Cancellation_Receive_Server.verified.json new file mode 100644 index 00000000..94cd413d --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/CancellationTests.Cancellation_Receive_Server.verified.json @@ -0,0 +1,5 @@ +{ + "Type": "System.OperationCanceledException", + "Message": "The operation was canceled.", + "Source": "System.Private.CoreLib" +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/CancellationTests.Cancellation_Save_Server.verified.json b/tests/Speckle.Sdk.Serialization.Tests/CancellationTests.Cancellation_Save_Server.verified.json new file mode 100644 index 00000000..94cd413d --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/CancellationTests.Cancellation_Save_Server.verified.json @@ -0,0 +1,5 @@ +{ + "Type": "System.OperationCanceledException", + "Message": "The operation was canceled.", + "Source": "System.Private.CoreLib" +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/CancellationTests.Cancellation_Save_Sqlite.verified.json b/tests/Speckle.Sdk.Serialization.Tests/CancellationTests.Cancellation_Save_Sqlite.verified.json new file mode 100644 index 00000000..94cd413d --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/CancellationTests.Cancellation_Save_Sqlite.verified.json @@ -0,0 +1,5 @@ +{ + "Type": "System.OperationCanceledException", + "Message": "The operation was canceled.", + "Source": "System.Private.CoreLib" +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/CancellationTests.Cancellation_Serialize.verified.json b/tests/Speckle.Sdk.Serialization.Tests/CancellationTests.Cancellation_Serialize.verified.json new file mode 100644 index 00000000..94cd413d --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/CancellationTests.Cancellation_Serialize.verified.json @@ -0,0 +1,5 @@ +{ + "Type": "System.OperationCanceledException", + "Message": "The operation was canceled.", + "Source": "System.Private.CoreLib" +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/CancellationTests.cs b/tests/Speckle.Sdk.Serialization.Tests/CancellationTests.cs new file mode 100644 index 00000000..5e2995a0 --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/CancellationTests.cs @@ -0,0 +1,189 @@ +using System.Collections.Concurrent; +using FluentAssertions; +using Microsoft.Extensions.Logging.Abstractions; +using Speckle.Objects.Geometry; +using Speckle.Sdk.Host; +using Speckle.Sdk.Models; +using Speckle.Sdk.Serialisation; +using Speckle.Sdk.Serialisation.V2.Receive; +using Speckle.Sdk.Serialisation.V2.Send; +using Speckle.Sdk.Serialization.Tests.Framework; +using Speckle.Sdk.Testing.Framework; + +namespace Speckle.Sdk.Serialization.Tests; + +public class CancellationTests +{ + public CancellationTests() + { + TypeLoader.Reset(); + TypeLoader.Initialize(typeof(Base).Assembly, typeof(DetachedTests).Assembly, typeof(Polyline).Assembly); + } + + [Fact] + public async Task Cancellation_Serialize() + { + var testClass = new TestClass() { RegularProperty = "Hello" }; + + using var cancellationSource = new CancellationTokenSource(); + await using var serializeProcess = new SerializeProcess( + null, + new DummySqLiteSendManager(), + new DummyServerObjectManager(), + new BaseChildFinder(new BasePropertyGatherer()), + new BaseSerializer(new DummySqLiteSendManager(), new ObjectSerializerFactory(new BasePropertyGatherer())), + new NullLoggerFactory(), + cancellationSource.Token, + new SerializeProcessOptions(true, true, false, true) + ); + await cancellationSource.CancelAsync(); + var ex = await Assert.ThrowsAsync( + async () => await serializeProcess.Serialize(testClass) + ); + await Verify(ex); + cancellationSource.IsCancellationRequested.Should().BeTrue(); + } + + [Fact] + public async Task Cancellation_Save_Server() + { + var testClass = new TestClass() { RegularProperty = "Hello" }; + + using var cancellationSource = new CancellationTokenSource(); + await using var serializeProcess = new SerializeProcess( + null, + new DummySqLiteSendManager(), + new CancellationServerObjectManager(cancellationSource), + new BaseChildFinder(new BasePropertyGatherer()), + new BaseSerializer(new DummySqLiteSendManager(), new ObjectSerializerFactory(new BasePropertyGatherer())), + new NullLoggerFactory(), + cancellationSource.Token, + new SerializeProcessOptions(true, false, false, true) + ); + var ex = await Assert.ThrowsAsync( + async () => await serializeProcess.Serialize(testClass) + ); + await Verify(ex); + cancellationSource.IsCancellationRequested.Should().BeTrue(); + } + + [Fact] + public async Task Cancellation_Save_Sqlite() + { + var testClass = new TestClass() { RegularProperty = "Hello" }; + + using var cancellationSource = new CancellationTokenSource(); + await using var serializeProcess = new SerializeProcess( + null, + new CancellationSqLiteSendManager(cancellationSource), + new DummyServerObjectManager(), + new BaseChildFinder(new BasePropertyGatherer()), + new BaseSerializer(new DummySqLiteSendManager(), new ObjectSerializerFactory(new BasePropertyGatherer())), + new NullLoggerFactory(), + cancellationSource.Token, + new SerializeProcessOptions(true, false, false, true) + ); + var ex = await Assert.ThrowsAsync( + async () => await serializeProcess.Serialize(testClass) + ); + await Verify(ex); + cancellationSource.IsCancellationRequested.Should().BeTrue(); + } + + [Theory] + [InlineData("RevitObject.json.gz", "3416d3fe01c9196115514c4a2f41617b", 7818)] + public async Task Cancellation_Receive_Cache(string fileName, string rootId, int oldCount) + { + var closures = await TestFileManager.GetFileAsClosures(fileName); + closures.Count.Should().Be(oldCount); + + using var cancellationSource = new CancellationTokenSource(); + await using var process = new DeserializeProcess( + new CancellationSqLiteJsonCacheManager(cancellationSource), + new DummyReceiveServerObjectManager(closures), + null, + new BaseDeserializer(new ObjectDeserializerFactory()), + new NullLoggerFactory(), + cancellationSource.Token, + new(MaxParallelism: 1) + ); + + var ex = await Assert.ThrowsAsync(async () => + { + var root = await process.Deserialize(rootId); + }); + + await Verify(ex); + cancellationSource.IsCancellationRequested.Should().BeTrue(); + } + + [Theory] + [InlineData("RevitObject.json.gz", "3416d3fe01c9196115514c4a2f41617b", 7818)] + public async Task Cancellation_Receive_Server(string fileName, string rootId, int oldCount) + { + var closures = await TestFileManager.GetFileAsClosures(fileName); + closures.Count.Should().Be(oldCount); + + using var cancellationSource = new CancellationTokenSource(); + await using var process = new DeserializeProcess( + new DummyCancellationSqLiteSendManager(), + new CancellationServerObjectManager(cancellationSource), + null, + new BaseDeserializer(new ObjectDeserializerFactory()), + new NullLoggerFactory(), + cancellationSource.Token, + new(MaxParallelism: 1) + ); + + var ex = await Assert.ThrowsAsync(async () => + { + var root = await process.Deserialize(rootId); + }); + + await Verify(ex); + cancellationSource.IsCancellationRequested.Should().BeTrue(); + } + + [Theory] + [InlineData("RevitObject.json.gz", "3416d3fe01c9196115514c4a2f41617b", 7818)] + public async Task Cancellation_Receive_Deserialize(string fileName, string rootId, int oldCount) + { + var closures = await TestFileManager.GetFileAsClosures(fileName); + closures.Count.Should().Be(oldCount); + + using var cancellationSource = new CancellationTokenSource(); + await using var process = new DeserializeProcess( + new DummySqLiteReceiveManager(closures), + new DummyReceiveServerObjectManager(closures), + null, + new CancellationBaseDeserializer(cancellationSource), + new NullLoggerFactory(), + cancellationSource.Token, + new(MaxParallelism: 1) + ); + + var ex = await Assert.ThrowsAsync(async () => + { + var root = await process.Deserialize(rootId); + }); + + await Verify(ex); + cancellationSource.IsCancellationRequested.Should().BeTrue(); + } +} + +public class CancellationBaseDeserializer(CancellationTokenSource cancellationTokenSource) : IBaseDeserializer +{ + public Base Deserialise( + ConcurrentDictionary baseCache, + Id id, + Json json, + IReadOnlyCollection closures, + CancellationToken cancellationToken + ) + { + cancellationTokenSource.Cancel(); + cancellationTokenSource.Token.ThrowIfCancellationRequested(); + throw new NotImplementedException(); + } +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/DetachedTests.CanSerialize_New_Detached.verified.json b/tests/Speckle.Sdk.Serialization.Tests/DetachedTests.CanSerialize_New_Detached.verified.json new file mode 100644 index 00000000..77406846 --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/DetachedTests.CanSerialize_New_Detached.verified.json @@ -0,0 +1,31 @@ +{ + "9ff8efb13c62fa80f3d1c4519376ba13": { + "__closure": { + "d3dd4621b2f68c3058c2b9c023a9de19": 100 + }, + "applicationId": null, + "arr": null, + "attachedProp": { + "applicationId": null, + "id": "90d58b65c9036a8bc50743f4c71c1c2e", + "name": "attachedProp", + "speckle_type": "Speckle.Core.Tests.Unit.Models.BaseTests+SamplePropBase" + }, + "crazyProp": null, + "detachedProp": { + "__closure": null, + "referencedId": "d3dd4621b2f68c3058c2b9c023a9de19", + "speckle_type": "reference" + }, + "dynamicProp": 123, + "id": "9ff8efb13c62fa80f3d1c4519376ba13", + "list": [], + "speckle_type": "Speckle.Core.Tests.Unit.Models.BaseTests+SampleObjectBase" + }, + "d3dd4621b2f68c3058c2b9c023a9de19": { + "applicationId": null, + "id": "d3dd4621b2f68c3058c2b9c023a9de19", + "name": "detachedProp", + "speckle_type": "Speckle.Core.Tests.Unit.Models.BaseTests+SamplePropBase" + } +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/DetachedTests.CanSerialize_New_Detached2.verified.json b/tests/Speckle.Sdk.Serialization.Tests/DetachedTests.CanSerialize_New_Detached2.verified.json new file mode 100644 index 00000000..be3c516f --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/DetachedTests.CanSerialize_New_Detached2.verified.json @@ -0,0 +1,187 @@ +{ + "027a7c5ffcf8d8efe432899c729a954c": { + "__closure": { + "045cbee36837d589b17f9d8483c90763": 100, + "1afc694774efa5913d0077302cd37888": 100, + "32a385e7ddeda810e037b21ab26381b7": 100, + "4ba53b5e84e956fb076bc8b0a03ca879": 100, + "5b86b66b61c556ead500915b05852875": 100, + "8d27f5c7fac36d985d89bb6d6d8acddc": 100, + "c3858f47dd3e7a308a1b465375f1645f": 100 + }, + "applicationId": null, + "area": 0.0, + "bbox": null, + "closed": false, + "domain": { + "applicationId": null, + "end": 1.0, + "id": "4d47df83148d8bad7bd2657bd9158b4c", + "speckle_type": "Objects.Primitive.Interval", + "start": 0.0 + }, + "id": "027a7c5ffcf8d8efe432899c729a954c", + "length": 0.0, + "speckle_type": "Objects.Geometry.Polyline", + "units": "test", + "value": [ + { + "__closure": null, + "referencedId": "5b86b66b61c556ead500915b05852875", + "speckle_type": "reference" + } + ] + }, + "045cbee36837d589b17f9d8483c90763": { + "__closure": { + "1afc694774efa5913d0077302cd37888": 100 + }, + "applicationId": null, + "area": 0.0, + "bbox": null, + "closed": false, + "domain": { + "applicationId": null, + "end": 1.0, + "id": "4d47df83148d8bad7bd2657bd9158b4c", + "speckle_type": "Objects.Primitive.Interval", + "start": 0.0 + }, + "id": "045cbee36837d589b17f9d8483c90763", + "length": 0.0, + "speckle_type": "Objects.Geometry.Polyline", + "units": "test", + "value": [ + { + "__closure": null, + "referencedId": "1afc694774efa5913d0077302cd37888", + "speckle_type": "reference" + } + ] + }, + "1afc694774efa5913d0077302cd37888": { + "applicationId": null, + "data": [ + 3.0, + 2.0 + ], + "id": "1afc694774efa5913d0077302cd37888", + "speckle_type": "Speckle.Core.Models.DataChunk" + }, + "2ebfd4f317754fce14cadd001151441e": { + "__closure": { + "027a7c5ffcf8d8efe432899c729a954c": 100, + "045cbee36837d589b17f9d8483c90763": 100, + "1afc694774efa5913d0077302cd37888": 100, + "32a385e7ddeda810e037b21ab26381b7": 100, + "4ba53b5e84e956fb076bc8b0a03ca879": 100, + "5b86b66b61c556ead500915b05852875": 100, + "8d27f5c7fac36d985d89bb6d6d8acddc": 100, + "c3858f47dd3e7a308a1b465375f1645f": 100 + }, + "applicationId": "1", + "arr": null, + "attachedProp": { + "applicationId": "4", + "id": "c5dd540ee1299c0349829d045c04ef2d", + "line": { + "__closure": null, + "referencedId": "027a7c5ffcf8d8efe432899c729a954c", + "speckle_type": "reference" + }, + "name": "attachedProp", + "speckle_type": "Speckle.Core.Tests.Unit.Models.BaseTests+SamplePropBase2" + }, + "crazyProp": null, + "detachedProp": { + "__closure": null, + "referencedId": "32a385e7ddeda810e037b21ab26381b7", + "speckle_type": "reference" + }, + "detachedProp2": { + "__closure": null, + "referencedId": "c3858f47dd3e7a308a1b465375f1645f", + "speckle_type": "reference" + }, + "dynamicProp": 123, + "id": "2ebfd4f317754fce14cadd001151441e", + "list": [], + "list2": null, + "speckle_type": "Speckle.Core.Tests.Unit.Models.BaseTests+SampleObjectBase2" + }, + "32a385e7ddeda810e037b21ab26381b7": { + "__closure": { + "4ba53b5e84e956fb076bc8b0a03ca879": 100, + "8d27f5c7fac36d985d89bb6d6d8acddc": 100 + }, + "applicationId": "2", + "id": "32a385e7ddeda810e037b21ab26381b7", + "line": { + "__closure": null, + "referencedId": "4ba53b5e84e956fb076bc8b0a03ca879", + "speckle_type": "reference" + }, + "name": "detachedProp", + "speckle_type": "Speckle.Core.Tests.Unit.Models.BaseTests+SamplePropBase2" + }, + "4ba53b5e84e956fb076bc8b0a03ca879": { + "__closure": { + "8d27f5c7fac36d985d89bb6d6d8acddc": 100 + }, + "applicationId": null, + "area": 0.0, + "bbox": null, + "closed": false, + "domain": { + "applicationId": null, + "end": 1.0, + "id": "4d47df83148d8bad7bd2657bd9158b4c", + "speckle_type": "Objects.Primitive.Interval", + "start": 0.0 + }, + "id": "4ba53b5e84e956fb076bc8b0a03ca879", + "length": 0.0, + "speckle_type": "Objects.Geometry.Polyline", + "units": "test", + "value": [ + { + "__closure": null, + "referencedId": "8d27f5c7fac36d985d89bb6d6d8acddc", + "speckle_type": "reference" + } + ] + }, + "5b86b66b61c556ead500915b05852875": { + "applicationId": null, + "data": [ + 3.0, + 4.0 + ], + "id": "5b86b66b61c556ead500915b05852875", + "speckle_type": "Speckle.Core.Models.DataChunk" + }, + "8d27f5c7fac36d985d89bb6d6d8acddc": { + "applicationId": null, + "data": [ + 1.0, + 2.0 + ], + "id": "8d27f5c7fac36d985d89bb6d6d8acddc", + "speckle_type": "Speckle.Core.Models.DataChunk" + }, + "c3858f47dd3e7a308a1b465375f1645f": { + "__closure": { + "045cbee36837d589b17f9d8483c90763": 100, + "1afc694774efa5913d0077302cd37888": 100 + }, + "applicationId": "3", + "id": "c3858f47dd3e7a308a1b465375f1645f", + "line": { + "__closure": null, + "referencedId": "045cbee36837d589b17f9d8483c90763", + "speckle_type": "reference" + }, + "name": "detachedProp2", + "speckle_type": "Speckle.Core.Tests.Unit.Models.BaseTests+SamplePropBase2" + } +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/DetachedTests.CanSerialize_New_Detached_With_DataChunks2.verified.json b/tests/Speckle.Sdk.Serialization.Tests/DetachedTests.CanSerialize_New_Detached_With_DataChunks2.verified.json new file mode 100644 index 00000000..ec12e5c4 --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/DetachedTests.CanSerialize_New_Detached_With_DataChunks2.verified.json @@ -0,0 +1,63 @@ +{ + "0e61e61edee00404ec6e0f9f594bce24": { + "applicationId": null, + "data": [ + 1.0, + 2.0, + 3.0, + 4.0, + 5.0, + 6.0, + 7.0, + 8.0, + 9.0, + 10.0 + ], + "id": "0e61e61edee00404ec6e0f9f594bce24", + "speckle_type": "Speckle.Core.Models.DataChunk" + }, + "525b1e9eef4d07165abb4ffc518395fc": { + "__closure": { + "0e61e61edee00404ec6e0f9f594bce24": 100, + "f70738e3e3e593ac11099a6ed6b71154": 100 + }, + "applicationId": "1", + "arr": [ + { + "__closure": null, + "referencedId": "f70738e3e3e593ac11099a6ed6b71154", + "speckle_type": "reference" + } + ], + "attachedProp": null, + "crazyProp": null, + "detachedProp": null, + "detachedProp2": null, + "dynamicProp": 123, + "id": "525b1e9eef4d07165abb4ffc518395fc", + "list": [ + { + "__closure": null, + "referencedId": "0e61e61edee00404ec6e0f9f594bce24", + "speckle_type": "reference" + } + ], + "list2": [ + { + "__closure": null, + "referencedId": "f70738e3e3e593ac11099a6ed6b71154", + "speckle_type": "reference" + } + ], + "speckle_type": "Speckle.Core.Tests.Unit.Models.BaseTests+SampleObjectBase2" + }, + "f70738e3e3e593ac11099a6ed6b71154": { + "applicationId": null, + "data": [ + 1.0, + 10.0 + ], + "id": "f70738e3e3e593ac11099a6ed6b71154", + "speckle_type": "Speckle.Core.Models.DataChunk" + } +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/DetachedTests.CanSerialize_Old_Detached.verified.json b/tests/Speckle.Sdk.Serialization.Tests/DetachedTests.CanSerialize_Old_Detached.verified.json new file mode 100644 index 00000000..f10c987a --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/DetachedTests.CanSerialize_Old_Detached.verified.json @@ -0,0 +1,31 @@ +{ + "9ff8efb13c62fa80f3d1c4519376ba13": { + "__closure": { + "d3dd4621b2f68c3058c2b9c023a9de19": 1 + }, + "applicationId": null, + "arr": null, + "attachedProp": { + "applicationId": null, + "id": "90d58b65c9036a8bc50743f4c71c1c2e", + "name": "attachedProp", + "speckle_type": "Speckle.Core.Tests.Unit.Models.BaseTests+SamplePropBase" + }, + "crazyProp": null, + "detachedProp": { + "__closure": null, + "referencedId": "d3dd4621b2f68c3058c2b9c023a9de19", + "speckle_type": "reference" + }, + "dynamicProp": 123, + "id": "9ff8efb13c62fa80f3d1c4519376ba13", + "list": [], + "speckle_type": "Speckle.Core.Tests.Unit.Models.BaseTests+SampleObjectBase" + }, + "d3dd4621b2f68c3058c2b9c023a9de19": { + "applicationId": null, + "id": "d3dd4621b2f68c3058c2b9c023a9de19", + "name": "detachedProp", + "speckle_type": "Speckle.Core.Tests.Unit.Models.BaseTests+SamplePropBase" + } +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/DetachedTests.GetPropertiesExpected_All.verified.json b/tests/Speckle.Sdk.Serialization.Tests/DetachedTests.GetPropertiesExpected_All.verified.json new file mode 100644 index 00000000..9525e06d --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/DetachedTests.GetPropertiesExpected_All.verified.json @@ -0,0 +1,88 @@ +[ + { + "Name": "list", + "PropertyAttributeInfo": { + "ChunkSize": 1000, + "IsChunkable": true, + "IsDetachable": true + }, + "Value": [] + }, + { + "Name": "arr", + "PropertyAttributeInfo": { + "ChunkSize": 300, + "IsChunkable": true, + "IsDetachable": true + } + }, + { + "Name": "detachedProp", + "PropertyAttributeInfo": { + "ChunkSize": 1000, + "IsChunkable": false, + "IsDetachable": true + }, + "Value": { + "name": "detachedProp", + "speckle_type": "Speckle.Core.Tests.Unit.Models.BaseTests+SamplePropBase", + "DynamicPropertyKeys": [] + } + }, + { + "Name": "attachedProp", + "PropertyAttributeInfo": { + "ChunkSize": 1000, + "IsChunkable": false, + "IsDetachable": false + }, + "Value": { + "name": "attachedProp", + "speckle_type": "Speckle.Core.Tests.Unit.Models.BaseTests+SamplePropBase", + "DynamicPropertyKeys": [] + } + }, + { + "Name": "crazyProp", + "PropertyAttributeInfo": { + "ChunkSize": 1000, + "IsChunkable": false, + "IsDetachable": false + } + }, + { + "Name": "applicationId", + "PropertyAttributeInfo": { + "ChunkSize": 1000, + "IsChunkable": false, + "IsDetachable": false + } + }, + { + "Name": "speckle_type", + "PropertyAttributeInfo": { + "ChunkSize": 1000, + "IsChunkable": false, + "IsDetachable": false + }, + "Value": "Speckle.Core.Tests.Unit.Models.BaseTests+SampleObjectBase" + }, + { + "Name": "dynamicProp", + "PropertyAttributeInfo": { + "ChunkSize": 1000, + "IsChunkable": false, + "IsDetachable": false + }, + "Value": 123 + }, + { + "Name": "@prop2", + "PropertyAttributeInfo": { + "ChunkSize": -1, + "IsChunkable": false, + "IsDetachable": true + }, + "Value": 2 + } +] diff --git a/tests/Speckle.Sdk.Serialization.Tests/DetachedTests.GetPropertiesExpected_Detached.verified.json b/tests/Speckle.Sdk.Serialization.Tests/DetachedTests.GetPropertiesExpected_Detached.verified.json new file mode 100644 index 00000000..0c3b268e --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/DetachedTests.GetPropertiesExpected_Detached.verified.json @@ -0,0 +1,41 @@ +[ + { + "Name": "list", + "PropertyAttributeInfo": { + "ChunkSize": 1000, + "IsChunkable": true, + "IsDetachable": true + }, + "Value": [] + }, + { + "Name": "arr", + "PropertyAttributeInfo": { + "ChunkSize": 300, + "IsChunkable": true, + "IsDetachable": true + } + }, + { + "Name": "detachedProp", + "PropertyAttributeInfo": { + "ChunkSize": 1000, + "IsChunkable": false, + "IsDetachable": true + }, + "Value": { + "name": "detachedProp", + "speckle_type": "Speckle.Core.Tests.Unit.Models.BaseTests+SamplePropBase", + "DynamicPropertyKeys": [] + } + }, + { + "Name": "@prop2", + "PropertyAttributeInfo": { + "ChunkSize": -1, + "IsChunkable": false, + "IsDetachable": true + }, + "Value": 2 + } +] diff --git a/tests/Speckle.Sdk.Serialization.Tests/DetachedTests.cs b/tests/Speckle.Sdk.Serialization.Tests/DetachedTests.cs new file mode 100644 index 00000000..add4126d --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/DetachedTests.cs @@ -0,0 +1,355 @@ +using System.Collections.Concurrent; +using System.Text; +using FluentAssertions; +using Microsoft.Extensions.Logging.Abstractions; +using Speckle.Newtonsoft.Json.Linq; +using Speckle.Objects.Geometry; +using Speckle.Sdk.Host; +using Speckle.Sdk.Models; +using Speckle.Sdk.Serialisation; +using Speckle.Sdk.Serialisation.V2; +using Speckle.Sdk.Serialisation.V2.Send; +using Speckle.Sdk.SQLite; +using Speckle.Sdk.Transports; + +namespace Speckle.Sdk.Serialization.Tests; + +public class DetachedTests +{ + public DetachedTests() + { + TypeLoader.Reset(); + TypeLoader.Initialize(typeof(Base).Assembly, typeof(DetachedTests).Assembly, typeof(Polyline).Assembly); + } + + [Fact] + public async Task CanSerialize_New_Detached() + { + var @base = new SampleObjectBase(); + @base["dynamicProp"] = 123; + @base.detachedProp = new SamplePropBase() { name = "detachedProp" }; + @base.attachedProp = new SamplePropBase() { name = "attachedProp" }; + + var objects = new Dictionary(); + + await using var process2 = new SerializeProcess( + null, + new DummySendCacheManager(objects), + new DummyServerObjectManager(), + new BaseChildFinder(new BasePropertyGatherer()), + new BaseSerializer(new DummySendCacheManager(objects), new ObjectSerializerFactory(new BasePropertyGatherer())), + new NullLoggerFactory(), + default, + new SerializeProcessOptions(false, false, true, true) + ); + await process2.Serialize(@base); + + await VerifyJsonDictionary(objects); + } + + [Fact] + public async Task CanSerialize_Old_Detached() + { + var @base = new SampleObjectBase(); + @base["dynamicProp"] = 123; + @base.detachedProp = new SamplePropBase() { name = "detachedProp" }; + @base.attachedProp = new SamplePropBase() { name = "attachedProp" }; + + var objects = new ConcurrentDictionary(); + var serializer = new SpeckleObjectSerializer(new[] { new MemoryTransport(objects) }); + serializer.Serialize(@base); + + await VerifyJsonDictionary(objects); + } + + [Fact] + public async Task GetPropertiesExpected_Detached() + { + var @base = new SampleObjectBase(); + @base["dynamicProp"] = 123; + @base["@prop2"] = 2; + @base["__prop3"] = 3; + @base.detachedProp = new SamplePropBase() { name = "detachedProp" }; + @base.attachedProp = new SamplePropBase() { name = "attachedProp" }; + + var children = new BaseChildFinder(new BasePropertyGatherer()).GetChildProperties(@base).ToList(); + await Verify(children); + } + + [Fact] + public async Task GetPropertiesExpected_All() + { + var @base = new SampleObjectBase(); + @base["dynamicProp"] = 123; + @base["@prop2"] = 2; + @base["__prop3"] = 3; + @base.detachedProp = new SamplePropBase() { name = "detachedProp" }; + @base.attachedProp = new SamplePropBase() { name = "attachedProp" }; + + var children = new BasePropertyGatherer().ExtractAllProperties(@base).ToList(); + await Verify(children); + } + + [Fact] + public async Task CanSerialize_New_Detached2() + { + var @base = new SampleObjectBase2(); + @base["dynamicProp"] = 123; + @base.applicationId = "1"; + @base.detachedProp = new SamplePropBase2() + { + name = "detachedProp", + applicationId = "2", + line = new Polyline() { units = "test", value = [1.0, 2.0] }, + }; + @base.detachedProp2 = new SamplePropBase2() + { + name = "detachedProp2", + applicationId = "3", + line = new Polyline() { units = "test", value = [3.0, 2.0] }, + }; + @base.attachedProp = new SamplePropBase2() + { + name = "attachedProp", + applicationId = "4", + line = new Polyline() { units = "test", value = [3.0, 4.0] }, + }; + + var objects = new Dictionary(); + + await using var process2 = new SerializeProcess( + null, + new DummySendCacheManager(objects), + new DummyServerObjectManager(), + new BaseChildFinder(new BasePropertyGatherer()), + new BaseSerializer(new DummySendCacheManager(objects), new ObjectSerializerFactory(new BasePropertyGatherer())), + new NullLoggerFactory(), + default, + new SerializeProcessOptions(false, false, true, true) + ); + var results = await process2.Serialize(@base); + + await VerifyJsonDictionary(objects); + } + + [Fact] + public async Task CanSerialize_New_Detached_With_DataChunks() + { + var root = """ + { + "list" : [ { + "speckle_type" : "reference", + "referencedId" : "0e61e61edee00404ec6e0f9f594bce24", + "__closure" : null + } ], + "list2" : [ { + "speckle_type" : "reference", + "referencedId" : "f70738e3e3e593ac11099a6ed6b71154", + "__closure" : null + } ], + "arr" : null, + "detachedProp" : null, + "detachedProp2" : null, + "attachedProp" : null, + "crazyProp" : null, + "applicationId" : "1", + "speckle_type" : "Speckle.Core.Tests.Unit.Models.BaseTests+SampleObjectBase2", + "dynamicProp" : 123, + "id" : "efeadaca70a85ae6d3acfc93a8b380db", + "__closure" : { + "0e61e61edee00404ec6e0f9f594bce24" : 100, + "f70738e3e3e593ac11099a6ed6b71154" : 100 + } + } + """; + + var list1 = """ + { + "data" : [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0 ], + "applicationId" : null, + "speckle_type" : "Speckle.Core.Models.DataChunk", + "id" : "0e61e61edee00404ec6e0f9f594bce24" + } + """; + var list2 = """ + { + "data" : [ 1.0, 10.0 ], + "applicationId" : null, + "speckle_type" : "Speckle.Core.Models.DataChunk", + "id" : "f70738e3e3e593ac11099a6ed6b71154" + } + """; + var @base = new SampleObjectBase2(); + @base["dynamicProp"] = 123; + @base.applicationId = "1"; + @base.list = new List() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + @base.list2 = new List() { 1, 10 }; + + var objects = new Dictionary(); + + await using var process2 = new SerializeProcess( + null, + new DummySendCacheManager(objects), + new DummyServerObjectManager(), + new BaseChildFinder(new BasePropertyGatherer()), + new BaseSerializer(new DummySendCacheManager(objects), new ObjectSerializerFactory(new BasePropertyGatherer())), + new NullLoggerFactory(), + default, + new SerializeProcessOptions(false, false, true, true) + ); + var results = await process2.Serialize(@base); + + objects.Count.Should().Be(3); + var x = JObject.Parse(objects["efeadaca70a85ae6d3acfc93a8b380db"]); + JToken.DeepEquals(JObject.Parse(root), x).Should().BeTrue(); + + x = JObject.Parse(objects["0e61e61edee00404ec6e0f9f594bce24"]); + JToken.DeepEquals(JObject.Parse(list1), x).Should().BeTrue(); + + x = JObject.Parse(objects["f70738e3e3e593ac11099a6ed6b71154"]); + JToken.DeepEquals(JObject.Parse(list2), x).Should().BeTrue(); + } + + [Fact] + public async Task CanSerialize_New_Detached_With_DataChunks2() + { + var @base = new SampleObjectBase2(); + @base["dynamicProp"] = 123; + @base.applicationId = "1"; + @base.list = new List() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + @base.list2 = new List() { 1, 10 }; + @base.arr = [1, 10]; + + var objects = new Dictionary(); + + await using var process2 = new SerializeProcess( + null, + new DummySendCacheManager(objects), + new DummyServerObjectManager(), + new BaseChildFinder(new BasePropertyGatherer()), + new BaseSerializer(new DummySendCacheManager(objects), new ObjectSerializerFactory(new BasePropertyGatherer())), + new NullLoggerFactory(), + default, + new SerializeProcessOptions(false, false, true, true) + ); + var results = await process2.Serialize(@base); + await VerifyJsonDictionary(objects); + } +} + +[SpeckleType("Speckle.Core.Tests.Unit.Models.BaseTests+SampleObjectBase")] +public class SampleObjectBase : Base +{ + [Chunkable, DetachProperty] + public List list { get; set; } = new(); + + [Chunkable(300), DetachProperty] + public double[] arr { get; set; } + + [DetachProperty] + public SamplePropBase detachedProp { get; set; } + + public SamplePropBase attachedProp { get; set; } + + public string crazyProp { get; set; } +} + +[SpeckleType("Speckle.Core.Tests.Unit.Models.BaseTests+SamplePropBase")] +public class SamplePropBase : Base +{ + public string name { get; set; } +} + +[SpeckleType("Speckle.Core.Tests.Unit.Models.BaseTests+SampleObjectBase2")] +public class SampleObjectBase2 : Base +{ + [Chunkable, DetachProperty] + public List list { get; set; } = new(); + + [Chunkable, DetachProperty] + public List list2 { get; set; } = null!; + + [Chunkable(300), DetachProperty] + public double[] arr { get; set; } + + [DetachProperty] + public SamplePropBase2 detachedProp { get; set; } + + [DetachProperty] + public SamplePropBase2 detachedProp2 { get; set; } + + public SamplePropBase2 attachedProp { get; set; } + + public string crazyProp { get; set; } +} + +[SpeckleType("Speckle.Core.Tests.Unit.Models.BaseTests+SamplePropBase2")] +public class SamplePropBase2 : Base +{ + public string name { get; set; } + + [DetachProperty] + public Polyline line { get; set; } +} + +public class DummyServerObjectManager : IServerObjectManager +{ + public IAsyncEnumerable<(string, string)> DownloadObjects( + IReadOnlyCollection objectIds, + IProgress? progress, + CancellationToken cancellationToken + ) => throw new NotImplementedException(); + + public virtual Task DownloadSingleObject( + string objectId, + IProgress? progress, + CancellationToken cancellationToken + ) => throw new NotImplementedException(); + + public Task> HasObjects( + IReadOnlyCollection objectIds, + CancellationToken cancellationToken + ) => Task.FromResult(objectIds.ToDictionary(x => x, _ => false)); + + public virtual Task UploadObjects( + IReadOnlyList objects, + bool compressPayloads, + IProgress? progress, + CancellationToken cancellationToken + ) + { + long totalBytes = 0; + foreach (var item in objects) + { + totalBytes += Encoding.Default.GetByteCount(item.Json.Value); + } + + progress?.Report(new(ProgressEvent.UploadBytes, totalBytes, totalBytes)); + return Task.CompletedTask; + } +} + +public class DummySendCacheManager(Dictionary objects) : ISqLiteJsonCacheManager +{ + public void Dispose() { } + + public IReadOnlyCollection<(string, string)> GetAllObjects() => throw new NotImplementedException(); + + public void DeleteObject(string id) => throw new NotImplementedException(); + + public string? GetObject(string id) => null; + + public void SaveObject(string id, string json) => throw new NotImplementedException(); + + public void UpdateObject(string id, string json) => throw new NotImplementedException(); + + public bool HasObject(string objectId) => false; + + public void SaveObjects(IEnumerable<(string id, string json)> items) + { + foreach (var (id, json) in items) + { + objects[id] = json; + } + } +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/DummyCancellationSqLiteSendManager.cs b/tests/Speckle.Sdk.Serialization.Tests/DummyCancellationSqLiteSendManager.cs new file mode 100644 index 00000000..2d6ca588 --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/DummyCancellationSqLiteSendManager.cs @@ -0,0 +1,22 @@ +using Speckle.Sdk.SQLite; + +namespace Speckle.Sdk.Serialization.Tests; + +public class DummyCancellationSqLiteSendManager : ISqLiteJsonCacheManager +{ + public string? GetObject(string id) => null; + + public void SaveObject(string id, string json) => throw new NotImplementedException(); + + public void UpdateObject(string id, string json) => throw new NotImplementedException(); + + public virtual void SaveObjects(IEnumerable<(string id, string json)> items) => throw new NotImplementedException(); + + public bool HasObject(string objectId) => throw new NotImplementedException(); + + public IReadOnlyCollection<(string, string)> GetAllObjects() => throw new NotImplementedException(); + + public void DeleteObject(string id) => throw new NotImplementedException(); + + public void Dispose() { } +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/ExceptionTests.Test_Exceptions_Cache.verified.json b/tests/Speckle.Sdk.Serialization.Tests/ExceptionTests.Test_Exceptions_Cache.verified.json new file mode 100644 index 00000000..a4c25449 --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/ExceptionTests.Test_Exceptions_Cache.verified.json @@ -0,0 +1,5 @@ +{ + "Type": "Speckle.Sdk.SpeckleException", + "Message": "Error while sending", + "Source": "Speckle.Sdk" +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/ExceptionTests.Test_Exceptions_Receive_Cache_fileName=False.verified.json b/tests/Speckle.Sdk.Serialization.Tests/ExceptionTests.Test_Exceptions_Receive_Cache_fileName=False.verified.json new file mode 100644 index 00000000..a4c25449 --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/ExceptionTests.Test_Exceptions_Receive_Cache_fileName=False.verified.json @@ -0,0 +1,5 @@ +{ + "Type": "Speckle.Sdk.SpeckleException", + "Message": "Error while sending", + "Source": "Speckle.Sdk" +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/ExceptionTests.Test_Exceptions_Receive_Cache_fileName=True.verified.json b/tests/Speckle.Sdk.Serialization.Tests/ExceptionTests.Test_Exceptions_Receive_Cache_fileName=True.verified.json new file mode 100644 index 00000000..1b778cf5 --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/ExceptionTests.Test_Exceptions_Receive_Cache_fileName=True.verified.json @@ -0,0 +1,5 @@ +{ + "Type": "System.NotImplementedException", + "Message": "The method or operation is not implemented.", + "Source": "Speckle.Sdk.Serialization.Tests" +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/ExceptionTests.Test_Exceptions_Receive_Server.verified.json b/tests/Speckle.Sdk.Serialization.Tests/ExceptionTests.Test_Exceptions_Receive_Server.verified.json new file mode 100644 index 00000000..1b778cf5 --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/ExceptionTests.Test_Exceptions_Receive_Server.verified.json @@ -0,0 +1,5 @@ +{ + "Type": "System.NotImplementedException", + "Message": "The method or operation is not implemented.", + "Source": "Speckle.Sdk.Serialization.Tests" +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/ExceptionTests.Test_Exceptions_Receive_Server_Skip_Both.verified.json b/tests/Speckle.Sdk.Serialization.Tests/ExceptionTests.Test_Exceptions_Receive_Server_Skip_Both.verified.json new file mode 100644 index 00000000..ec4502d8 --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/ExceptionTests.Test_Exceptions_Receive_Server_Skip_Both.verified.json @@ -0,0 +1,5 @@ +{ + "Type": "Speckle.Sdk.SpeckleException", + "Message": "Cannot skip server and cache. Please choose one.", + "Source": "Speckle.Sdk" +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/ExceptionTests.Test_Exceptions_Upload.verified.json b/tests/Speckle.Sdk.Serialization.Tests/ExceptionTests.Test_Exceptions_Upload.verified.json new file mode 100644 index 00000000..a4c25449 --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/ExceptionTests.Test_Exceptions_Upload.verified.json @@ -0,0 +1,5 @@ +{ + "Type": "Speckle.Sdk.SpeckleException", + "Message": "Error while sending", + "Source": "Speckle.Sdk" +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/ExceptionTests.cs b/tests/Speckle.Sdk.Serialization.Tests/ExceptionTests.cs new file mode 100644 index 00000000..a9315b8a --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/ExceptionTests.cs @@ -0,0 +1,147 @@ +using FluentAssertions; +using Microsoft.Extensions.Logging.Abstractions; +using Speckle.Objects.Geometry; +using Speckle.Sdk.Host; +using Speckle.Sdk.Models; +using Speckle.Sdk.Serialisation.V2.Receive; +using Speckle.Sdk.Serialisation.V2.Send; +using Speckle.Sdk.Serialization.Tests.Framework; +using Speckle.Sdk.Testing.Framework; + +namespace Speckle.Sdk.Serialization.Tests; + +public class ExceptionTests +{ + public ExceptionTests() + { + TypeLoader.Reset(); + TypeLoader.Initialize(typeof(Base).Assembly, typeof(DetachedTests).Assembly, typeof(Polyline).Assembly); + } + + [Fact] + public async Task Test_Exceptions_Upload() + { + var testClass = new TestClass() { RegularProperty = "Hello" }; + + var objects = new Dictionary(); + await using var process2 = new SerializeProcess( + null, + new DummySendCacheManager(objects), + new ExceptionServerObjectManager(), + new BaseChildFinder(new BasePropertyGatherer()), + new BaseSerializer(new DummySendCacheManager(objects), new ObjectSerializerFactory(new BasePropertyGatherer())), + new NullLoggerFactory(), + default, + new SerializeProcessOptions(false, false, false, true) + ); + + //4 exceptions are fine because we use 4 threads for saving cache + var ex = await Assert.ThrowsAsync(async () => await process2.Serialize(testClass)); + await Verify(ex); + } + + [Fact] + public async Task Test_Exceptions_Cache() + { + var testClass = new TestClass() { RegularProperty = "Hello" }; + + await using var process2 = new SerializeProcess( + null, + new ExceptionSendCacheManager(), + new DummyServerObjectManager(), + new BaseChildFinder(new BasePropertyGatherer()), + new BaseSerializer(new ExceptionSendCacheManager(), new ObjectSerializerFactory(new BasePropertyGatherer())), + new NullLoggerFactory(), + default, + new SerializeProcessOptions(false, false, false, true) + ); + + var ex = await Assert.ThrowsAsync(async () => await process2.Serialize(testClass)); + await Verify(ex); + } + + [Fact] + public async Task Test_Exceptions_Receive_Server_Skip_Both() + { + var o = new ObjectLoader( + new DummySqLiteReceiveManager(new Dictionary()), + new ExceptionServerObjectManager(), + null, + default + ); + await using var process = new DeserializeProcess( + o, + null, + new BaseDeserializer(new ObjectDeserializerFactory()), + new NullLoggerFactory(), + default, + new(SkipCache: true, MaxParallelism: 1, SkipServer: true) + ); + + var ex = await Assert.ThrowsAsync(async () => + { + var root = await process.Deserialize(Guid.NewGuid().ToString()); + }); + await Verify(ex); + } + + [Theory] + [InlineData("RevitObject.json.gz", "3416d3fe01c9196115514c4a2f41617b", 7818)] + public async Task Test_Exceptions_Receive_Server(string fileName, string rootId, int oldCount) + { + var closures = await TestFileManager.GetFileAsClosures(fileName); + closures.Count.Should().Be(oldCount); + + await using var process = new DeserializeProcess( + new DummySqLiteReceiveManager(closures), + new ExceptionServerObjectManager(), + null, + new BaseDeserializer(new ObjectDeserializerFactory()), + new NullLoggerFactory(), + default, + new(true, MaxParallelism: 1) + ); + + var ex = await Assert.ThrowsAsync(async () => + { + var root = await process.Deserialize(rootId); + }); + await Verify(ex); + } + + [Theory] + [InlineData("RevitObject.json.gz", "3416d3fe01c9196115514c4a2f41617b", 7818, false)] + [InlineData("RevitObject.json.gz", "3416d3fe01c9196115514c4a2f41617b", 7818, true)] + public async Task Test_Exceptions_Receive_Cache(string fileName, string rootId, int oldCount, bool? hasObject) + { + var closures = await TestFileManager.GetFileAsClosures(fileName); + closures.Count.Should().Be(oldCount); + + await using var process = new DeserializeProcess( + new ExceptionSendCacheManager(hasObject), + new DummyReceiveServerObjectManager(closures), + null, + new BaseDeserializer(new ObjectDeserializerFactory()), + new NullLoggerFactory(), + default, + new(MaxParallelism: 1) + ); + + Exception ex; + if (hasObject == true) + { + ex = await Assert.ThrowsAsync(async () => + { + var root = await process.Deserialize(rootId); + }); + } + else + { + ex = await Assert.ThrowsAsync(async () => + { + var root = await process.Deserialize(rootId); + }); + } + await Verify(ex).UseParameters(hasObject); + } +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/ExplicitInterfaceTests.Test_ExtractAllProperties.verified.json b/tests/Speckle.Sdk.Serialization.Tests/ExplicitInterfaceTests.Test_ExtractAllProperties.verified.json new file mode 100644 index 00000000..d4b13fa0 --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/ExplicitInterfaceTests.Test_ExtractAllProperties.verified.json @@ -0,0 +1,28 @@ +[ + { + "Name": "RegularProperty", + "PropertyAttributeInfo": { + "ChunkSize": 1000, + "IsChunkable": false, + "IsDetachable": false + }, + "Value": "Hello" + }, + { + "Name": "applicationId", + "PropertyAttributeInfo": { + "ChunkSize": 1000, + "IsChunkable": false, + "IsDetachable": false + } + }, + { + "Name": "speckle_type", + "PropertyAttributeInfo": { + "ChunkSize": 1000, + "IsChunkable": false, + "IsDetachable": false + }, + "Value": "Speckle.Core.Serialisation.TestClass" + } +] diff --git a/tests/Speckle.Sdk.Serialization.Tests/ExplicitInterfaceTests.Test_Json.verified.json b/tests/Speckle.Sdk.Serialization.Tests/ExplicitInterfaceTests.Test_Json.verified.json new file mode 100644 index 00000000..b3a68535 --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/ExplicitInterfaceTests.Test_Json.verified.json @@ -0,0 +1,8 @@ +{ + "daaa67cfd73a957247cf2d631b7ca4f3": { + "applicationId": null, + "id": "daaa67cfd73a957247cf2d631b7ca4f3", + "RegularProperty": "Hello", + "speckle_type": "Speckle.Core.Serialisation.TestClass" + } +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/ExplicitInterfaceTests.cs b/tests/Speckle.Sdk.Serialization.Tests/ExplicitInterfaceTests.cs new file mode 100644 index 00000000..0bd05a14 --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/ExplicitInterfaceTests.cs @@ -0,0 +1,59 @@ +using Microsoft.Extensions.Logging.Abstractions; +using Speckle.Sdk.Host; +using Speckle.Sdk.Models; +using Speckle.Sdk.Serialisation.V2.Send; + +namespace Speckle.Sdk.Serialization.Tests; + +public class ExplicitInterfaceTests +{ + public ExplicitInterfaceTests() + { + TypeLoader.Reset(); + TypeLoader.Initialize(typeof(Base).Assembly, typeof(TestClass).Assembly); + } + + [Fact] + public async Task Test_Json() + { + var testClass = new TestClass() { RegularProperty = "Hello" }; + + var objects = new Dictionary(); + await using var process2 = new SerializeProcess( + null, + new DummySendCacheManager(objects), + new DummyServerObjectManager(), + new BaseChildFinder(new BasePropertyGatherer()), + new BaseSerializer(new DummySendCacheManager(objects), new ObjectSerializerFactory(new BasePropertyGatherer())), + new NullLoggerFactory(), + default, + new SerializeProcessOptions(false, false, true, true) + ); + + await process2.Serialize(testClass); + + await VerifyJsonDictionary(objects); + } + + [Fact] + public async Task Test_ExtractAllProperties() + { + var testClass = new TestClass() { RegularProperty = "Hello" }; + + var gatherer = new BasePropertyGatherer(); + var properties = gatherer.ExtractAllProperties(testClass).ToList(); + await Verify(properties); + } +} + +[SpeckleType("Speckle.Core.Serialisation.TestClass")] +public sealed class TestClass : Base, ITestInterface +{ + public string RegularProperty { get; set; } + string ITestInterface.TestProperty => RegularProperty; +} + +public interface ITestInterface +{ + string TestProperty { get; } +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/ExternalIdTests.ExternalIdTest_Detached.verified.json b/tests/Speckle.Sdk.Serialization.Tests/ExternalIdTests.ExternalIdTest_Detached.verified.json new file mode 100644 index 00000000..04349a0b --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/ExternalIdTests.ExternalIdTest_Detached.verified.json @@ -0,0 +1,38 @@ +{ + "8d27f5c7fac36d985d89bb6d6d8acddc": { + "applicationId": null, + "data": [ + 1.0, + 2.0 + ], + "id": "8d27f5c7fac36d985d89bb6d6d8acddc", + "speckle_type": "Speckle.Core.Models.DataChunk" + }, + "cfaf7ae0dfc5a7cf3343bb6db46ed238": { + "__closure": { + "8d27f5c7fac36d985d89bb6d6d8acddc": 100 + }, + "applicationId": null, + "area": 0.0, + "bbox": null, + "closed": false, + "domain": { + "applicationId": null, + "end": 1.0, + "id": "4d47df83148d8bad7bd2657bd9158b4c", + "speckle_type": "Objects.Primitive.Interval", + "start": 0.0 + }, + "id": "cfaf7ae0dfc5a7cf3343bb6db46ed238", + "length": 0.0, + "speckle_type": "Objects.Geometry.Polyline", + "units": "cm", + "value": [ + { + "__closure": null, + "referencedId": "8d27f5c7fac36d985d89bb6d6d8acddc", + "speckle_type": "reference" + } + ] + } +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/ExternalIdTests.ExternalIdTest_Detached_Nested.verified.json b/tests/Speckle.Sdk.Serialization.Tests/ExternalIdTests.ExternalIdTest_Detached_Nested.verified.json new file mode 100644 index 00000000..f7db9127 --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/ExternalIdTests.ExternalIdTest_Detached_Nested.verified.json @@ -0,0 +1,70 @@ +{ + "8d27f5c7fac36d985d89bb6d6d8acddc": { + "applicationId": null, + "data": [ + 1.0, + 2.0 + ], + "id": "8d27f5c7fac36d985d89bb6d6d8acddc", + "speckle_type": "Speckle.Core.Models.DataChunk" + }, + "93737bb0800970f29d68748ee206399d": { + "__closure": { + "8d27f5c7fac36d985d89bb6d6d8acddc": 100, + "cfaf7ae0dfc5a7cf3343bb6db46ed238": 100 + }, + "applicationId": null, + "area": 0.0, + "bbox": null, + "closed": false, + "degree": 1, + "displayValue": { + "__closure": null, + "referencedId": "cfaf7ae0dfc5a7cf3343bb6db46ed238", + "speckle_type": "reference" + }, + "domain": { + "applicationId": null, + "end": 1.0, + "id": "4d47df83148d8bad7bd2657bd9158b4c", + "speckle_type": "Objects.Primitive.Interval", + "start": 0.0 + }, + "id": "93737bb0800970f29d68748ee206399d", + "knots": [], + "length": 0.0, + "periodic": false, + "points": [], + "rational": false, + "speckle_type": "Objects.Geometry.Curve", + "units": "cm", + "weights": [] + }, + "cfaf7ae0dfc5a7cf3343bb6db46ed238": { + "__closure": { + "8d27f5c7fac36d985d89bb6d6d8acddc": 100 + }, + "applicationId": null, + "area": 0.0, + "bbox": null, + "closed": false, + "domain": { + "applicationId": null, + "end": 1.0, + "id": "4d47df83148d8bad7bd2657bd9158b4c", + "speckle_type": "Objects.Primitive.Interval", + "start": 0.0 + }, + "id": "cfaf7ae0dfc5a7cf3343bb6db46ed238", + "length": 0.0, + "speckle_type": "Objects.Geometry.Polyline", + "units": "cm", + "value": [ + { + "__closure": null, + "referencedId": "8d27f5c7fac36d985d89bb6d6d8acddc", + "speckle_type": "reference" + } + ] + } +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/ExternalIdTests.ExternalIdTest_Detached_Nested_More.verified.json b/tests/Speckle.Sdk.Serialization.Tests/ExternalIdTests.ExternalIdTest_Detached_Nested_More.verified.json new file mode 100644 index 00000000..96d651cd --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/ExternalIdTests.ExternalIdTest_Detached_Nested_More.verified.json @@ -0,0 +1,89 @@ +{ + "050585d98934e7a4d199e07bd92598a5": { + "__closure": { + "8d27f5c7fac36d985d89bb6d6d8acddc": 100, + "cfaf7ae0dfc5a7cf3343bb6db46ed238": 100 + }, + "applicationId": null, + "area": 0.0, + "bbox": null, + "closed": false, + "domain": { + "applicationId": null, + "end": 1.0, + "id": "4d47df83148d8bad7bd2657bd9158b4c", + "speckle_type": "Objects.Primitive.Interval", + "start": 0.0 + }, + "id": "050585d98934e7a4d199e07bd92598a5", + "length": 0.0, + "segments": [ + { + "applicationId": null, + "area": 0.0, + "bbox": null, + "closed": false, + "degree": 1, + "displayValue": { + "__closure": null, + "referencedId": "cfaf7ae0dfc5a7cf3343bb6db46ed238", + "speckle_type": "reference" + }, + "domain": { + "applicationId": null, + "end": 1.0, + "id": "4d47df83148d8bad7bd2657bd9158b4c", + "speckle_type": "Objects.Primitive.Interval", + "start": 0.0 + }, + "id": "93737bb0800970f29d68748ee206399d", + "knots": [], + "length": 0.0, + "periodic": false, + "points": [], + "rational": false, + "speckle_type": "Objects.Geometry.Curve", + "units": "cm", + "weights": [] + } + ], + "speckle_type": "Objects.Geometry.Polycurve", + "units": "cm" + }, + "8d27f5c7fac36d985d89bb6d6d8acddc": { + "applicationId": null, + "data": [ + 1.0, + 2.0 + ], + "id": "8d27f5c7fac36d985d89bb6d6d8acddc", + "speckle_type": "Speckle.Core.Models.DataChunk" + }, + "cfaf7ae0dfc5a7cf3343bb6db46ed238": { + "__closure": { + "8d27f5c7fac36d985d89bb6d6d8acddc": 100 + }, + "applicationId": null, + "area": 0.0, + "bbox": null, + "closed": false, + "domain": { + "applicationId": null, + "end": 1.0, + "id": "4d47df83148d8bad7bd2657bd9158b4c", + "speckle_type": "Objects.Primitive.Interval", + "start": 0.0 + }, + "id": "cfaf7ae0dfc5a7cf3343bb6db46ed238", + "length": 0.0, + "speckle_type": "Objects.Geometry.Polyline", + "units": "cm", + "value": [ + { + "__closure": null, + "referencedId": "8d27f5c7fac36d985d89bb6d6d8acddc", + "speckle_type": "reference" + } + ] + } +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/ExternalIdTests.ExternalIdTest_Detached_Nested_More_Too.verified.json b/tests/Speckle.Sdk.Serialization.Tests/ExternalIdTests.ExternalIdTest_Detached_Nested_More_Too.verified.json new file mode 100644 index 00000000..2b821b93 --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/ExternalIdTests.ExternalIdTest_Detached_Nested_More_Too.verified.json @@ -0,0 +1,104 @@ +{ + "050585d98934e7a4d199e07bd92598a5": { + "__closure": { + "8d27f5c7fac36d985d89bb6d6d8acddc": 100, + "cfaf7ae0dfc5a7cf3343bb6db46ed238": 100 + }, + "applicationId": null, + "area": 0.0, + "bbox": null, + "closed": false, + "domain": { + "applicationId": null, + "end": 1.0, + "id": "4d47df83148d8bad7bd2657bd9158b4c", + "speckle_type": "Objects.Primitive.Interval", + "start": 0.0 + }, + "id": "050585d98934e7a4d199e07bd92598a5", + "length": 0.0, + "segments": [ + { + "applicationId": null, + "area": 0.0, + "bbox": null, + "closed": false, + "degree": 1, + "displayValue": { + "__closure": null, + "referencedId": "cfaf7ae0dfc5a7cf3343bb6db46ed238", + "speckle_type": "reference" + }, + "domain": { + "applicationId": null, + "end": 1.0, + "id": "4d47df83148d8bad7bd2657bd9158b4c", + "speckle_type": "Objects.Primitive.Interval", + "start": 0.0 + }, + "id": "93737bb0800970f29d68748ee206399d", + "knots": [], + "length": 0.0, + "periodic": false, + "points": [], + "rational": false, + "speckle_type": "Objects.Geometry.Curve", + "units": "cm", + "weights": [] + } + ], + "speckle_type": "Objects.Geometry.Polycurve", + "units": "cm" + }, + "4314c177603b00228261d6f0f4686395": { + "__closure": { + "050585d98934e7a4d199e07bd92598a5": 100, + "8d27f5c7fac36d985d89bb6d6d8acddc": 100, + "cfaf7ae0dfc5a7cf3343bb6db46ed238": 100 + }, + "@profile": { + "__closure": null, + "referencedId": "050585d98934e7a4d199e07bd92598a5", + "speckle_type": "reference" + }, + "applicationId": null, + "id": "4314c177603b00228261d6f0f4686395", + "speckle_type": "Base" + }, + "8d27f5c7fac36d985d89bb6d6d8acddc": { + "applicationId": null, + "data": [ + 1.0, + 2.0 + ], + "id": "8d27f5c7fac36d985d89bb6d6d8acddc", + "speckle_type": "Speckle.Core.Models.DataChunk" + }, + "cfaf7ae0dfc5a7cf3343bb6db46ed238": { + "__closure": { + "8d27f5c7fac36d985d89bb6d6d8acddc": 100 + }, + "applicationId": null, + "area": 0.0, + "bbox": null, + "closed": false, + "domain": { + "applicationId": null, + "end": 1.0, + "id": "4d47df83148d8bad7bd2657bd9158b4c", + "speckle_type": "Objects.Primitive.Interval", + "start": 0.0 + }, + "id": "cfaf7ae0dfc5a7cf3343bb6db46ed238", + "length": 0.0, + "speckle_type": "Objects.Geometry.Polyline", + "units": "cm", + "value": [ + { + "__closure": null, + "referencedId": "8d27f5c7fac36d985d89bb6d6d8acddc", + "speckle_type": "reference" + } + ] + } +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/ExternalIdTests.cs b/tests/Speckle.Sdk.Serialization.Tests/ExternalIdTests.cs new file mode 100644 index 00000000..ea228242 --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/ExternalIdTests.cs @@ -0,0 +1,109 @@ +using Speckle.Objects.Geometry; +using Speckle.Objects.Primitive; +using Speckle.Sdk.Host; +using Speckle.Sdk.Models; +using Speckle.Sdk.Models.Extensions; +using Speckle.Sdk.Serialisation; +using Speckle.Sdk.Serialisation.V2.Send; + +namespace Speckle.Sdk.Serialization.Tests; + +public class ExternalIdTests +{ + public ExternalIdTests() + { + TypeLoader.Reset(); + TypeLoader.Initialize(typeof(Base).Assembly, typeof(Polyline).Assembly); + } + + [Fact] + public async Task ExternalIdTest_Detached() + { + var p = new Polyline() { units = "cm", value = [1, 2] }; + using var serializer = new ObjectSerializerFactory(new BasePropertyGatherer()).Create( + new Dictionary(), + default + ); + var objects = serializer.Serialize(p).ToDictionary(x => x.Item1, x => x.Item2); + + await VerifyJsonDictionary(objects); + } + + [Fact] + public async Task ExternalIdTest_Detached_Nested() + { + var curve = new Curve() + { + closed = false, + displayValue = new Polyline() { units = "cm", value = [1, 2] }, + domain = new Interval() { start = 0, end = 1 }, + units = "cm", + degree = 1, + periodic = false, + rational = false, + points = [], + knots = [], + weights = [], + }; + using var serializer = new ObjectSerializerFactory(new BasePropertyGatherer()).Create( + new Dictionary(), + default + ); + var objects = serializer.Serialize(curve).ToDictionary(x => x.Item1, x => x.Item2); + + await VerifyJsonDictionary(objects); + } + + [Fact] + public async Task ExternalIdTest_Detached_Nested_More() + { + var curve = new Curve() + { + closed = false, + displayValue = new Polyline() { units = "cm", value = [1, 2] }, + domain = new Interval() { start = 0, end = 1 }, + units = "cm", + degree = 1, + periodic = false, + rational = false, + points = [], + knots = [], + weights = [], + }; + var polycurve = new Polycurve() { segments = [curve], units = "cm" }; + using var serializer = new ObjectSerializerFactory(new BasePropertyGatherer()).Create( + new Dictionary(), + default + ); + var objects = serializer.Serialize(polycurve).ToDictionary(x => x.Item1, x => x.Item2); + + await VerifyJsonDictionary(objects); + } + + [Fact] + public async Task ExternalIdTest_Detached_Nested_More_Too() + { + var curve = new Curve() + { + closed = false, + displayValue = new Polyline() { units = "cm", value = [1, 2] }, + domain = new Interval() { start = 0, end = 1 }, + units = "cm", + degree = 1, + periodic = false, + rational = false, + points = [], + knots = [], + weights = [], + }; + var polycurve = new Polycurve() { segments = [curve], units = "cm" }; + var @base = new Base(); + @base.SetDetachedProp("profile", polycurve); + using var serializer = new ObjectSerializerFactory(new BasePropertyGatherer()).Create( + new Dictionary(), + default + ); + var objects = serializer.Serialize(@base).ToDictionary(x => x.Item1, x => x.Item2); + await VerifyJsonDictionary(objects); + } +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/Framework/ExceptionSendCacheManager.cs b/tests/Speckle.Sdk.Serialization.Tests/Framework/ExceptionSendCacheManager.cs new file mode 100644 index 00000000..0534a477 --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/Framework/ExceptionSendCacheManager.cs @@ -0,0 +1,22 @@ +using Speckle.Sdk.SQLite; + +namespace Speckle.Sdk.Serialization.Tests.Framework; + +public class ExceptionSendCacheManager(bool? hasObject = null) : ISqLiteJsonCacheManager +{ + public void Dispose() { } + + public IReadOnlyCollection<(string Id, string Json)> GetAllObjects() => throw new NotImplementedException(); + + public void DeleteObject(string id) => throw new NotImplementedException(); + + public string? GetObject(string id) => null; + + public void SaveObject(string id, string json) => throw new NotImplementedException(); + + public void UpdateObject(string id, string json) => throw new NotImplementedException(); + + public void SaveObjects(IEnumerable<(string id, string json)> items) => throw new NotImplementedException(); + + public bool HasObject(string objectId) => hasObject ?? throw new NotImplementedException(); +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/Framework/ExceptionServerObjectManager.cs b/tests/Speckle.Sdk.Serialization.Tests/Framework/ExceptionServerObjectManager.cs new file mode 100644 index 00000000..e6afc2a8 --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/Framework/ExceptionServerObjectManager.cs @@ -0,0 +1,32 @@ +using Speckle.Sdk.Serialisation.V2; +using Speckle.Sdk.Serialisation.V2.Send; +using Speckle.Sdk.Transports; + +namespace Speckle.Sdk.Serialization.Tests.Framework; + +public class ExceptionServerObjectManager : IServerObjectManager +{ + public IAsyncEnumerable<(string, string)> DownloadObjects( + IReadOnlyCollection objectIds, + IProgress? progress, + CancellationToken cancellationToken + ) => throw new NotImplementedException(); + + public Task DownloadSingleObject( + string objectId, + IProgress? progress, + CancellationToken cancellationToken + ) => throw new NotImplementedException(); + + public Task> HasObjects( + IReadOnlyCollection objectIds, + CancellationToken cancellationToken + ) => throw new NotImplementedException(); + + public Task UploadObjects( + IReadOnlyList objects, + bool compressPayloads, + IProgress? progress, + CancellationToken cancellationToken + ) => throw new NotImplementedException(); +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/Framework/TestFileManager.cs b/tests/Speckle.Sdk.Serialization.Tests/Framework/TestFileManager.cs new file mode 100644 index 00000000..34b738e1 --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/Framework/TestFileManager.cs @@ -0,0 +1,60 @@ +using System.IO.Compression; +using System.Reflection; +using Speckle.Newtonsoft.Json.Linq; +using Speckle.Objects.Data; +using Speckle.Sdk.Common; +using Speckle.Sdk.Host; +using Speckle.Sdk.Models; + +namespace Speckle.Sdk.Serialization.Tests.Framework; + +public static class TestFileManager +{ + static TestFileManager() + { + TypeLoader.Reset(); + TypeLoader.Initialize(typeof(Base).Assembly, typeof(DataObject).Assembly, _assembly); + } + + private static readonly Assembly _assembly = Assembly.GetExecutingAssembly(); + private static readonly Dictionary> _objects = new(); + + public static async Task> GetFileAsClosures(string fileName) + { + if (!_objects.TryGetValue(fileName, out var closure)) + { + var fullName = _assembly.GetManifestResourceNames().Single(x => x.EndsWith(fileName)); + var json = await ReadJson(fullName); + closure = ReadAsObjects(json); + _objects.Add(fileName, closure); + } + return closure; + } + + private static async Task ReadJson(string fullName) + { + await using var stream = _assembly.GetManifestResourceStream(fullName).NotNull(); + if (fullName.EndsWith(".gz")) + { + await using var z = new GZipStream(stream, CompressionMode.Decompress); + using var reader2 = new StreamReader(z); + return await reader2.ReadToEndAsync(); + } + using var reader = new StreamReader(stream); + return await reader.ReadToEndAsync(); + } + + private static Dictionary ReadAsObjects(string json) + { + var jsonObjects = new Dictionary(); + var array = JArray.Parse(json); + foreach (var obj in array) + { + if (obj is JObject jobj) + { + jsonObjects.Add(jobj["id"].NotNull().Value().NotNull(), jobj.ToString()); + } + } + return jsonObjects; + } +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/Framework/TestTransport.cs b/tests/Speckle.Sdk.Serialization.Tests/Framework/TestTransport.cs new file mode 100644 index 00000000..0ca8d458 --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/Framework/TestTransport.cs @@ -0,0 +1,35 @@ +using Speckle.Sdk.Transports; + +namespace Speckle.Sdk.Serialization.Tests.Framework; + +public class TestTransport(IReadOnlyDictionary objects) : ITransport +{ + public string TransportName + { + get => "Test"; + set { } + } + + public Dictionary TransportContext { get; } + public TimeSpan Elapsed { get; } + public int SavedObjectCount { get; } + public CancellationToken CancellationToken { get; set; } + public IProgress? OnProgressAction { get; set; } + public Action? OnErrorAction { get; set; } + + public void BeginWrite() => throw new NotImplementedException(); + + public void EndWrite() => throw new NotImplementedException(); + + public void SaveObject(string id, string serializedObject) => throw new NotImplementedException(); + + public Task WriteComplete() => throw new NotImplementedException(); + + public Task GetObject(string id) => Task.FromResult(objects.GetValueOrDefault(id)); + + public Task CopyObjectAndChildren(string id, ITransport targetTransport) => + throw new NotImplementedException(); + + public Task> HasObjects(IReadOnlyList objectIds) => + throw new NotImplementedException(); +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/TestTransport.cs b/tests/Speckle.Sdk.Serialization.Tests/Framework/TestTransport2.cs similarity index 56% rename from tests/Speckle.Sdk.Serialization.Tests/TestTransport.cs rename to tests/Speckle.Sdk.Serialization.Tests/Framework/TestTransport2.cs index 5b237e2b..e9a3f1f6 100644 --- a/tests/Speckle.Sdk.Serialization.Tests/TestTransport.cs +++ b/tests/Speckle.Sdk.Serialization.Tests/Framework/TestTransport2.cs @@ -1,15 +1,10 @@ -using Speckle.Sdk.Transports; +using Speckle.Sdk.Transports; -namespace Speckle.Sdk.Serialization.Tests; +namespace Speckle.Sdk.Serialization.Tests.Framework; -public class TestTransport : ITransport +public class TestTransport2(IDictionary objects) : ITransport { - public IDictionary Objects { get; } - - public TestTransport(IDictionary objects) - { - Objects = objects; - } + public IDictionary Objects { get; } = objects; public string TransportName { @@ -21,7 +16,7 @@ public class TestTransport : ITransport public TimeSpan Elapsed { get; } public int SavedObjectCount { get; } public CancellationToken CancellationToken { get; set; } - public Action? OnProgressAction { get; set; } + public IProgress? OnProgressAction { get; set; } public Action? OnErrorAction { get; set; } public void BeginWrite() => throw new NotImplementedException(); @@ -32,13 +27,10 @@ public class TestTransport : ITransport public Task WriteComplete() => throw new NotImplementedException(); - public string? GetObject(string id) => Objects.TryGetValue(id, out string? o) ? o : null; + public Task GetObject(string id) => Task.FromResult(Objects.TryGetValue(id, out string? o) ? o : null); - public Task CopyObjectAndChildren( - string id, - ITransport targetTransport, - Action? onTotalChildrenCountKnown = null - ) => throw new NotImplementedException(); + public Task CopyObjectAndChildren(string id, ITransport targetTransport) => + throw new NotImplementedException(); public Task> HasObjects(IReadOnlyList objectIds) => throw new NotImplementedException(); diff --git a/tests/Speckle.Sdk.Serialization.Tests/Global.cs b/tests/Speckle.Sdk.Serialization.Tests/Global.cs new file mode 100644 index 00000000..0dee07ae --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/Global.cs @@ -0,0 +1,3 @@ +global using static Speckle.Sdk.Testing.SpeckleVerify; + +[assembly: CollectionBehavior(DisableTestParallelization = true)] diff --git a/tests/Speckle.Sdk.Serialization.Tests/Module.cs b/tests/Speckle.Sdk.Serialization.Tests/Module.cs new file mode 100644 index 00000000..51b9682e --- /dev/null +++ b/tests/Speckle.Sdk.Serialization.Tests/Module.cs @@ -0,0 +1,13 @@ +using System.Runtime.CompilerServices; +using Speckle.Sdk.Testing; + +namespace Speckle.Sdk.Serialization.Tests; + +public static class Module +{ + [ModuleInitializer] + public static void Initialize() + { + SpeckleVerify.Initialize(); + } +} diff --git a/tests/Speckle.Sdk.Serialization.Tests/RevitObject.json b/tests/Speckle.Sdk.Serialization.Tests/RevitObject.json deleted file mode 100644 index 48aec422..00000000 --- a/tests/Speckle.Sdk.Serialization.Tests/RevitObject.json +++ /dev/null @@ -1,5913 +0,0 @@ -[ - { - "id": "551513ff4f3596024547fc818f1f3f70", - "type": "Concrete-Commercial 362mm", - "level": { - "id": "13ec7a96382822d854cebf49f12e37f8", - "name": "Level 1", - "units": "cm", - "category": "Levels", - "elementId": "311", - "elevation": 0, - "worksetId": "0", - "createView": true, - "parameters": { - "id": "183527059ef874eecdd4bb74b9ff762f", - "IFC_GUID": { - "id": "253f76685236e94d17544a66f6849bf6", - "name": "IfcGUID", - "units": null, - "value": "3Zu5Bv0LOHrPC10026FoQQ", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_GUID" - }, - "DATUM_TEXT": { - "id": "9dff18eef47fbfcc195b15e01a076e28", - "name": "Name", - "units": null, - "value": "Level 1", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "DATUM_TEXT" - }, - "LEVEL_ELEV": { - "id": "c863e6214242197d74a3a28590f14681", - "name": "Elevation", - "units": "cm", - "value": 0, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:centimeters-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "LEVEL_ELEV" - }, - "speckle_type": "Base", - "applicationId": null, - "ELEM_TYPE_PARAM": { - "id": "b90ad7d6650e182f4b21325bd8e96545", - "name": "Type", - "units": null, - "value": "305", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ELEM_TYPE_PARAM" - }, - "SYMBOL_ID_PARAM": { - "id": "3ca4e3044444c496aff73993b39cee41", - "name": "Type Id", - "units": null, - "value": "305", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "SYMBOL_ID_PARAM" - }, - "DESIGN_OPTION_ID": { - "id": "226f48801e3084d3491eb079e8dd0af8", - "name": "Design Option", - "units": null, - "value": "-1", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "DESIGN_OPTION_ID" - }, - "ELEM_FAMILY_PARAM": { - "id": "775e7c470bc83edf158eb2afd5169271", - "name": "Family", - "units": null, - "value": "305", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ELEM_FAMILY_PARAM" - }, - "LEVEL_UP_TO_LEVEL": { - "id": "2c98ab93a27aba3d81fd68d8848d8813", - "name": "Story Above", - "units": null, - "value": "-1", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "LEVEL_UP_TO_LEVEL" - }, - "IFC_EXPORT_ELEMENT": { - "id": "9433ec034c7dc84e9769c9f9ba332bb5", - "name": "Export to IFC", - "units": null, - "value": 0, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_EXPORT_ELEMENT" - }, - "totalChildrenCount": 0, - "ALL_MODEL_TYPE_NAME": { - "id": "82132d22cc87843fba383206476c52a6", - "name": "Type Name", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_TYPE_NAME" - }, - "ELEM_CATEGORY_PARAM": { - "id": "06675cc605d2289783137e5f409841df", - "name": "Category", - "units": null, - "value": "-2000240", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ELEM_CATEGORY_PARAM" - }, - "LEVEL_IS_STRUCTURAL": { - "id": "c90aaf2fd87636058ab60a26023fab1e", - "name": "Structural", - "units": null, - "value": false, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "LEVEL_IS_STRUCTURAL" - }, - "ALL_MODEL_FAMILY_NAME": { - "id": "7ef14a150734e2bbefb591fab775d23e", - "name": "Family Name", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_FAMILY_NAME" - }, - "ELEM_CATEGORY_PARAM_MT": { - "id": "93b264ba0193732c54d676799cae6c09", - "name": "Category", - "units": null, - "value": "-2000240", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ELEM_CATEGORY_PARAM_MT" - }, - "LEVEL_IS_BUILDING_STORY": { - "id": "1a16badbf32cc26bac1f63565f8c7727", - "name": "Building Story", - "units": null, - "value": true, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "LEVEL_IS_BUILDING_STORY" - }, - "DATUM_VOLUME_OF_INTEREST": { - "id": "5ef80c5931aed0c387b423cce5334d30", - "name": "Scope Box", - "units": null, - "value": "400175", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "DATUM_VOLUME_OF_INTEREST" - }, - "ELEM_FAMILY_AND_TYPE_PARAM": { - "id": "3050db1515d6afaf12d8e50f101eccc7", - "name": "Family and Type", - "units": null, - "value": "305", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ELEM_FAMILY_AND_TYPE_PARAM" - }, - "LEVEL_ROOM_COMPUTATION_HEIGHT": { - "id": "4bf2025d03a4d5cba6962772405fefd3", - "name": "Computation Height", - "units": "cm", - "value": 0, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:centimeters-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "LEVEL_ROOM_COMPUTATION_HEIGHT" - } - }, - "speckle_type": "Objects.BuiltElements.Level:Objects.BuiltElements.Revit.RevitLevel", - "applicationId": "e3e052f9-0156-11d5-9301-0000863f27ad-00000137", - "referenceOnly": false, - "builtInCategory": "OST_Levels", - "isRevitLinkedModel": false, - "materialQuantities": [], - "totalChildrenCount": 0, - "revitLinkedModelPath": "C:\\Users\\conno\\Downloads\\objects_R23.rvt" - }, - "slope": -0.20000000000000007, - "units": "cm", - "voids": [], - "family": "Floor", - "outline": { - "id": "ae890eeb90225b9f1938699ef5210ca6", - "area": 0, - "bbox": null, - "units": null, - "closed": false, - "domain": null, - "length": 0, - "segments": [ - { - "id": "2e7b50a5e8e21cd6ce7e4b56153be7e4", - "end": { - "x": 6349.655635530213, - "y": -1699.91494848239, - "z": 0, - "id": "c879c4f687288e4932ff75f902a2f786", - "bbox": null, - "units": "cm", - "speckle_type": "Objects.Geometry.Point", - "applicationId": null, - "totalChildrenCount": 0 - }, - "area": 0, - "bbox": null, - "start": { - "x": 6349.655635530213, - "y": -1199.9149484823902, - "z": 0, - "id": "ab1a7dc25381e01e5e36344a191e007c", - "bbox": null, - "units": "cm", - "speckle_type": "Objects.Geometry.Point", - "applicationId": null, - "totalChildrenCount": 0 - }, - "units": "cm", - "domain": { - "id": "e92a4ceb62fa3f9b53a9a2d533572665", - "end": 16.40419947506561, - "start": 0, - "speckle_type": "Objects.Primitive.Interval", - "applicationId": null, - "totalChildrenCount": 0 - }, - "length": 499.99999999999983, - "speckle_type": "Objects.Geometry.Line", - "applicationId": null, - "totalChildrenCount": 0 - }, - { - "id": "c13b5448e511ca19bfa24b3f7bf78a4e", - "end": { - "x": 5849.655635530213, - "y": -1699.91494848239, - "z": 0, - "id": "a260324e65099ddc4a0e76908c046119", - "bbox": null, - "units": "cm", - "speckle_type": "Objects.Geometry.Point", - "applicationId": null, - "totalChildrenCount": 0 - }, - "area": 0, - "bbox": null, - "start": { - "x": 6349.655635530213, - "y": -1699.91494848239, - "z": 0, - "id": "c879c4f687288e4932ff75f902a2f786", - "bbox": null, - "units": "cm", - "speckle_type": "Objects.Geometry.Point", - "applicationId": null, - "totalChildrenCount": 0 - }, - "units": "cm", - "domain": { - "id": "c7890bf1d837b6a4f777b946a7a08792", - "end": 49.21259842519686, - "start": 32.80839895013125, - "speckle_type": "Objects.Primitive.Interval", - "applicationId": null, - "totalChildrenCount": 0 - }, - "length": 499.99999999999983, - "speckle_type": "Objects.Geometry.Line", - "applicationId": null, - "totalChildrenCount": 0 - }, - { - "id": "a9a70455869aa1e52a90e6ab9c441c73", - "end": { - "x": 5849.655635530213, - "y": -1199.9149484823902, - "z": 0, - "id": "ef0e20c06d37dce848794b69d9ade282", - "bbox": null, - "units": "cm", - "speckle_type": "Objects.Geometry.Point", - "applicationId": null, - "totalChildrenCount": 0 - }, - "area": 0, - "bbox": null, - "start": { - "x": 5849.655635530213, - "y": -1699.91494848239, - "z": 0, - "id": "a260324e65099ddc4a0e76908c046119", - "bbox": null, - "units": "cm", - "speckle_type": "Objects.Geometry.Point", - "applicationId": null, - "totalChildrenCount": 0 - }, - "units": "cm", - "domain": { - "id": "6ecda648b800c62964653c99fc5a3382", - "end": 19.68503937007874, - "start": 3.2808398950131306, - "speckle_type": "Objects.Primitive.Interval", - "applicationId": null, - "totalChildrenCount": 0 - }, - "length": 499.99999999999983, - "speckle_type": "Objects.Geometry.Line", - "applicationId": null, - "totalChildrenCount": 0 - }, - { - "id": "845c03fc3a107e2f62654e221fee6055", - "end": { - "x": 6349.655635530213, - "y": -1199.9149484823902, - "z": 0, - "id": "ab1a7dc25381e01e5e36344a191e007c", - "bbox": null, - "units": "cm", - "speckle_type": "Objects.Geometry.Point", - "applicationId": null, - "totalChildrenCount": 0 - }, - "area": 0, - "bbox": null, - "start": { - "x": 5849.655635530213, - "y": -1199.9149484823902, - "z": 0, - "id": "ef0e20c06d37dce848794b69d9ade282", - "bbox": null, - "units": "cm", - "speckle_type": "Objects.Geometry.Point", - "applicationId": null, - "totalChildrenCount": 0 - }, - "units": "cm", - "domain": { - "id": "e92a4ceb62fa3f9b53a9a2d533572665", - "end": 16.40419947506561, - "start": 0, - "speckle_type": "Objects.Primitive.Interval", - "applicationId": null, - "totalChildrenCount": 0 - }, - "length": 499.99999999999983, - "speckle_type": "Objects.Geometry.Line", - "applicationId": null, - "totalChildrenCount": 0 - } - ], - "speckle_type": "Objects.Geometry.Polycurve", - "applicationId": null, - "totalChildrenCount": 0 - }, - "category": "Floors", - "elements": [ - { - "referencedId": "9407c61a13fdff3313e5535517d0820e", - "speckle_type": "reference" - } - ], - "__closure": { - "00d4a610a914f9dce457fad0260c3d73": 1, - "3d719768b25f185bedebf2865d77aaf1": 1, - "4e78f5d278ed4917de8fdad63512862d": 1, - "52c485bd16243d285dad61fa712421ce": 2, - "9407c61a13fdff3313e5535517d0820e": 1, - "9f9f7958ee36b2b582970b2153b96d05": 1, - "a27fe83ee1f177b67f4f3b08885d7ec3": 1, - "a724becac2af2d936a7c79349ce9b709": 2, - "e34b9581f041267f18092ba75dde35fb": 2, - "e56082f6410025dec8f3cc658a77e4d4": 2, - "e85bd8c0246c15998785930e21b9f291": 2, - "f4ff3dd4a33c3711164fcae43a2e1376": 1 - }, - "elementId": "350409", - "worksetId": "0", - "parameters": { - "id": "7d4c0fdec8c9a51c74747c1df33c1454", - "IFC_GUID": { - "id": "f02e1fa0a6f48fb24470718c71aacb94", - "name": "IfcGUID", - "units": null, - "value": "1cghwxWkP5hebx$jnaWsD_", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_GUID" - }, - "DOOR_COST": { - "id": "eb6b241b8a9d30a8d060ed3045af62ee", - "name": "Cost", - "units": "฿", - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:currency-1.0.0", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "DOOR_COST" - }, - "speckle_type": "Base", - "ALL_MODEL_URL": { - "id": "8abe9322516ea72e715da245b4303d09", - "name": "URL", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_URL" - }, - "IFC_TYPE_GUID": { - "id": "84de2a1f8c1c9ce60f592adc5a552cae", - "name": "Type IfcGUID", - "units": null, - "value": "3O98Cq8xiHrPCo0026FpEz", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_TYPE_GUID" - }, - "KEYNOTE_PARAM": { - "id": "bcf04d1e0a8e59168a66d0b63eeb1ab8", - "name": "Keynote", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "KEYNOTE_PARAM" - }, - "PHASE_CREATED": { - "id": "75ca890af17bf6c300fa8bf49a1faad3", - "name": "Phase Created", - "units": null, - "value": "86961", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHASE_CREATED" - }, - "applicationId": null, - "ALL_MODEL_MARK": { - "id": "ccdd2d167e1e58cb2e82700148409df7", - "name": "Mark", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_MARK" - }, - "FUNCTION_PARAM": { - "id": "649cd2a2df818753cfc8d60bac4f2a9b", - "name": "Function", - "units": null, - "value": 0, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "FUNCTION_PARAM" - }, - "UNIFORMAT_CODE": { - "id": "8ed423285d1d5d7118b7a43e40b32883", - "name": "Assembly Code", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "UNIFORMAT_CODE" - }, - "WINDOW_TYPE_ID": { - "id": "6b8a0975069f64a97adb326251fb057a", - "name": "Type Mark", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "WINDOW_TYPE_ID" - }, - "ALL_MODEL_IMAGE": { - "id": "061343fdcc12e983dfae8136fd33ab7e", - "name": "Image", - "units": null, - "value": "-1", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_IMAGE" - }, - "ALL_MODEL_MODEL": { - "id": "934bca477ca5e150978b7babe1483839", - "name": "Model", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_MODEL" - }, - "CLEAR_COVER_TOP": { - "id": "7b3dc68570e6e14b21fc4d5d09576a58", - "name": "Rebar Cover - Top Face", - "units": null, - "value": "99880", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "CLEAR_COVER_TOP" - }, - "ELEM_TYPE_PARAM": { - "id": "47cfc8fd357ed1a97f70ca43b21befd0", - "name": "Type", - "units": null, - "value": "5136", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ELEM_TYPE_PARAM" - }, - "RELATED_TO_MASS": { - "id": "b81c40eb493168f253851a2807de13d1", - "name": "Related to Mass", - "units": null, - "value": false, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "RELATED_TO_MASS" - }, - "SYMBOL_ID_PARAM": { - "id": "23ed15627295c7cf868b095dbbdbe8be", - "name": "Type Id", - "units": null, - "value": "5136", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "SYMBOL_ID_PARAM" - }, - "DESIGN_OPTION_ID": { - "id": "226f48801e3084d3491eb079e8dd0af8", - "name": "Design Option", - "units": null, - "value": "-1", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "DESIGN_OPTION_ID" - }, - "PHASE_DEMOLISHED": { - "id": "a6185b7ecb2668c79dcca9a47ca54206", - "name": "Phase Demolished", - "units": null, - "value": "-1", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHASE_DEMOLISHED" - }, - "CLEAR_COVER_OTHER": { - "id": "942d5fb2e131fdaec2dbfe4d46024bcb", - "name": "Rebar Cover - Other Faces", - "units": null, - "value": "99880", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "CLEAR_COVER_OTHER" - }, - "ELEM_FAMILY_PARAM": { - "id": "41856807000563ad3f8eb63a34c7e4d6", - "name": "Family", - "units": null, - "value": "5136", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ELEM_FAMILY_PARAM" - }, - "CLEAR_COVER_BOTTOM": { - "id": "f13df3a0436a49c235e3e2e3e841e1bd", - "name": "Rebar Cover - Bottom Face", - "units": null, - "value": "99880", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "CLEAR_COVER_BOTTOM" - }, - "HOST_AREA_COMPUTED": { - "id": "f99ecf86026e400dd7d10e49d7bff2d5", - "name": "Area", - "units": "m²", - "value": 24.527454725304192, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:squareMeters-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "HOST_AREA_COMPUTED" - }, - "IFC_EXPORT_ELEMENT": { - "id": "9433ec034c7dc84e9769c9f9ba332bb5", - "name": "Export to IFC", - "units": null, - "value": 0, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_EXPORT_ELEMENT" - }, - "totalChildrenCount": 0, - "ALL_MODEL_TYPE_NAME": { - "id": "82132d22cc87843fba383206476c52a6", - "name": "Type Name", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_TYPE_NAME" - }, - "ELEM_CATEGORY_PARAM": { - "id": "f5fcfc17b2b3d453e2f73615c7132c73", - "name": "Category", - "units": null, - "value": "-2000032", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ELEM_CATEGORY_PARAM" - }, - "ALL_MODEL_TYPE_IMAGE": { - "id": "e6417f0a96a3c38d9a622ab5494e8612", - "name": "Type Image", - "units": null, - "value": "-1", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_TYPE_IMAGE" - }, - "ANALYTICAL_ROUGHNESS": { - "id": "d78c0459d5885b0551440806a7da877a", - "name": "Roughness", - "units": null, - "value": 3, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ANALYTICAL_ROUGHNESS" - }, - "HOST_VOLUME_COMPUTED": { - "id": "fa3ae94f2b3abb16d383659327388292", - "name": "Volume", - "units": "m³", - "value": 8.578477190710895, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:cubicMeters-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "HOST_VOLUME_COMPUTED" - }, - "SCHEDULE_LEVEL_PARAM": { - "id": "cf9f641ea0d9a98fee310f8df288601d", - "name": "Level", - "units": null, - "value": "311", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "SCHEDULE_LEVEL_PARAM" - }, - "ALL_MODEL_DESCRIPTION": { - "id": "dc455c0fef6a720aff6c536385b6c433", - "name": "Description", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_DESCRIPTION" - }, - "ALL_MODEL_FAMILY_NAME": { - "id": "7ef14a150734e2bbefb591fab775d23e", - "name": "Family Name", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_FAMILY_NAME" - }, - "IFC_EXPORT_ELEMENT_AS": { - "id": "32da53c27657cacc0bd4d9bf2340705e", - "name": "Export to IFC As", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_EXPORT_ELEMENT_AS" - }, - "UNIFORMAT_DESCRIPTION": { - "id": "026207f8c83f70e3dccb1c9d4f0a195c", - "name": "Assembly Description", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "UNIFORMAT_DESCRIPTION" - }, - "ALL_MODEL_MANUFACTURER": { - "id": "03e476c9ecb3a8c39c42a4a99cd15782", - "name": "Manufacturer", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_MANUFACTURER" - }, - "ANALYTICAL_ABSORPTANCE": { - "id": "47c6a5057314a0842a9890f12c84e8d8", - "name": "Absorptance", - "units": null, - "value": 0.7, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:general-1.0.1", - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ANALYTICAL_ABSORPTANCE" - }, - "ELEM_CATEGORY_PARAM_MT": { - "id": "a9438efc13b73b18cb4b1a4b3edada21", - "name": "Category", - "units": null, - "value": "-2000032", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ELEM_CATEGORY_PARAM_MT" - }, - "ALL_MODEL_TYPE_COMMENTS": { - "id": "389eba7a64948e921f41b0f13cab0ff8", - "name": "Type Comments", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_TYPE_COMMENTS" - }, - "ANALYTICAL_THERMAL_MASS": { - "id": "13593ffccd841fb9f8cfa280346219bc", - "name": "Thermal Mass", - "units": "kJ/(m²·K)", - "value": 304.14, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:kilojoulesPerSquareMeterKelvin-1.0.0", - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ANALYTICAL_THERMAL_MASS" - }, - "HOST_PERIMETER_COMPUTED": { - "id": "e3e80465c2d1e626bc53fb7f377e32c4", - "name": "Perimeter", - "units": "cm", - "value": 1999.9999999999993, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:centimeters-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "HOST_PERIMETER_COMPUTED" - }, - "IFC_EXPORT_ELEMENT_TYPE": { - "id": "0c72bf98d9abf2f9db170f5045bf26e9", - "name": "Export Type to IFC", - "units": null, - "value": 0, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_EXPORT_ELEMENT_TYPE" - }, - "WALL_ATTR_ROOM_BOUNDING": { - "id": "d18936cfb67f3f7e2cb1db3b6866d9f8", - "name": "Room Bounding", - "units": null, - "value": true, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "WALL_ATTR_ROOM_BOUNDING" - }, - "FLOOR_STRUCTURE_ID_PARAM": { - "id": "39bb14fccdc96c18ea418385ecdf09f3", - "name": "Structure", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "FLOOR_STRUCTURE_ID_PARAM" - }, - "IFC_EXPORT_PREDEFINEDTYPE": { - "id": "1bbe93dc48596577683307830377663e", - "name": "IFC Predefined Type", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_EXPORT_PREDEFINEDTYPE" - }, - "STRUCTURAL_MATERIAL_PARAM": { - "id": "7b7f8ed1cbb4419f6d4248bc0ffd3074", - "name": "Structural Material", - "units": null, - "value": "523", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "STRUCTURAL_MATERIAL_PARAM" - }, - "ELEM_FAMILY_AND_TYPE_PARAM": { - "id": "51d38d015a689eb5014e76995b8d1695", - "name": "Family and Type", - "units": null, - "value": "5136", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ELEM_FAMILY_AND_TYPE_PARAM" - }, - "FLOOR_ATTR_THICKNESS_PARAM": { - "id": "3a850d548478f1f1f93d975c4a10016b", - "name": "Thickness", - "units": "cm", - "value": 36.2, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:centimeters-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "FLOOR_ATTR_THICKNESS_PARAM" - }, - "IFC_EXPORT_ELEMENT_TYPE_AS": { - "id": "cf49cd58a60d03878cf4c7475e053f7a", - "name": "Export Type to IFC As", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_EXPORT_ELEMENT_TYPE_AS" - }, - "ALL_MODEL_INSTANCE_COMMENTS": { - "id": "4025b5ee5e68c7b65fdfc179bef5eab1", - "name": "Comments", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_INSTANCE_COMMENTS" - }, - "STRUCTURAL_ELEVATION_AT_TOP": { - "id": "8fda5d9fdd5e387ab3be6f41d5069cb8", - "name": "Elevation at Top", - "units": "cm", - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:centimeters-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "STRUCTURAL_ELEVATION_AT_TOP" - }, - "FLOOR_HEIGHTABOVELEVEL_PARAM": { - "id": "9cef3a1fccce4e4c05b9750a7a89a225", - "name": "Height Offset From Level", - "units": "cm", - "value": 100, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:centimeters-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "FLOOR_HEIGHTABOVELEVEL_PARAM" - }, - "ANALYTICAL_THERMAL_RESISTANCE": { - "id": "30763d58dcd01bdaff629b9c9557b0bb", - "name": "Thermal Resistance (R)", - "units": "(m²·K)/W", - "value": 0.2270254844325219, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:squareMeterKelvinsPerWatt-1.0.1", - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ANALYTICAL_THERMAL_RESISTANCE" - }, - "IFC_EXPORT_PREDEFINEDTYPE_TYPE": { - "id": "b7398ab9ec911545bcf3ff348222774a", - "name": "Type IFC Predefined Type", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_EXPORT_PREDEFINEDTYPE_TYPE" - }, - "STRUCTURAL_ELEVATION_AT_BOTTOM": { - "id": "d0cddc2b7ec0a4c1518ba4441461fd26", - "name": "Elevation at Bottom", - "units": "cm", - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:centimeters-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "STRUCTURAL_ELEVATION_AT_BOTTOM" - }, - "COARSE_SCALE_FILL_PATTERN_COLOR": { - "id": "aba48af3ec57ff0388b53770453cb93a", - "name": "Coarse Scale Fill Color", - "units": null, - "value": 0, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "COARSE_SCALE_FILL_PATTERN_COLOR" - }, - "STRUCTURAL_FLOOR_CORE_THICKNESS": { - "id": "9c42a0acc890457eef93be7a217d540a", - "name": "Core Thickness", - "units": "cm", - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:centimeters-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "STRUCTURAL_FLOOR_CORE_THICKNESS" - }, - "STRUCTURAL_ELEVATION_AT_TOP_CORE": { - "id": "307d68bbab115b159e13da5172db6932", - "name": "Elevation at Top Core", - "units": "cm", - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:centimeters-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "STRUCTURAL_ELEVATION_AT_TOP_CORE" - }, - "ANALYTICAL_ELEMENT_HAS_ASSOCIATION": { - "id": "54e2d80569b97b2a9808586b01aeddff", - "name": "Has Association", - "units": null, - "value": true, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ANALYTICAL_ELEMENT_HAS_ASSOCIATION" - }, - "COARSE_SCALE_FILL_PATTERN_ID_PARAM": { - "id": "b67cbd5f09fefaa372748c1634ff0736", - "name": "Coarse Scale Fill Pattern", - "units": null, - "value": "-1", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "COARSE_SCALE_FILL_PATTERN_ID_PARAM" - }, - "FLOOR_ATTR_DEFAULT_THICKNESS_PARAM": { - "id": "945f536f34d0a73782600c22beebca04", - "name": "Default Thickness", - "units": "cm", - "value": 36.2, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:centimeters-1.0.1", - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "FLOOR_ATTR_DEFAULT_THICKNESS_PARAM" - }, - "STRUCTURAL_ELEVATION_AT_TOP_SURVEY": { - "id": "3841476454a9d8b24af5cd6d6af83142", - "name": "Elevation at Top Survey", - "units": "cm", - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:centimeters-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "STRUCTURAL_ELEVATION_AT_TOP_SURVEY" - }, - "STRUCTURAL_ELEVATION_AT_BOTTOM_CORE": { - "id": "32799a5f447724cc6d7189b8a290d392", - "name": "Elevation at Bottom Core", - "units": "cm", - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:centimeters-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "STRUCTURAL_ELEVATION_AT_BOTTOM_CORE" - }, - "30352725-b571-4b21-b9bc-7a05e9dbfd30": { - "id": "de9939167f0ff99a3b74dd14bbae1136", - "name": "UniFormat", - "units": null, - "value": null, - "isShared": true, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "30352725-b571-4b21-b9bc-7a05e9dbfd30" - }, - "5e83901c-127e-4cce-bab7-7ed956a7b598": { - "id": "a93c479ee2d706b499a52d8fcd2548a3", - "name": "Poz No", - "units": null, - "value": null, - "isShared": true, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "5e83901c-127e-4cce-bab7-7ed956a7b598" - }, - "ANALYTICAL_HEAT_TRANSFER_COEFFICIENT": { - "id": "39f5a87cf430427e4a081954b590e7da", - "name": "Heat Transfer Coefficient (U)", - "units": "W/(m²·K)", - "value": 4.404791834271923, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:wattsPerSquareMeterKelvin-1.0.1", - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ANALYTICAL_HEAT_TRANSFER_COEFFICIENT" - }, - "d8073d7a-703c-4a80-b634-0b91166cec35": { - "id": "eaa0547368452b5695735d690afb04fe", - "name": "Tip Kodu", - "units": null, - "value": null, - "isShared": true, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "d8073d7a-703c-4a80-b634-0b91166cec35" - }, - "STRUCTURAL_ELEVATION_AT_BOTTOM_SURVEY": { - "id": "9172c00ea1a3c19f65e356a4f185fd72", - "name": "Elevation at Bottom Survey", - "units": "cm", - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:centimeters-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "STRUCTURAL_ELEVATION_AT_BOTTOM_SURVEY" - } - }, - "structural": true, - "displayValue": [ - { - "referencedId": "9f9f7958ee36b2b582970b2153b96d05", - "speckle_type": "reference" - }, - { - "referencedId": "00d4a610a914f9dce457fad0260c3d73", - "speckle_type": "reference" - } - ], - "phaseCreated": "New Construction", - "speckle_type": "Objects.BuiltElements.Floor:Objects.BuiltElements.Revit.RevitFloor", - "applicationId": "66aabebb-82e6-45ae-897b-fedc64863bb7-000558c9", - "slopeDirection": { - "id": "a4a76e1eb11b8dc5589f53211ecb3903", - "end": { - "x": 6349.655635530213, - "y": -1449.9149484823915, - "z": 0, - "id": "3f97c69ee3f2df8523a2fe127500525d", - "bbox": null, - "units": "cm", - "speckle_type": "Objects.Geometry.Point", - "applicationId": null, - "totalChildrenCount": 0 - }, - "area": 0, - "bbox": null, - "start": { - "x": 5849.655635530213, - "y": -1449.9149484823902, - "z": 0, - "id": "f634eb9d1e2f985c0b69d4291b36df08", - "bbox": null, - "units": "cm", - "speckle_type": "Objects.Geometry.Point", - "applicationId": null, - "totalChildrenCount": 0 - }, - "units": "m", - "domain": null, - "length": 500, - "speckle_type": "Objects.Geometry.Line", - "applicationId": null, - "totalChildrenCount": 0 - }, - "builtInCategory": "OST_Floors", - "isRevitLinkedModel": false, - "materialQuantities": [ - { - "id": "c561c201b810180820c5cd3ce2e1db61", - "area": 243912.13310796264, - "units": "cm", - "volume": 0, - "material": { - "referencedId": "4e78f5d278ed4917de8fdad63512862d", - "speckle_type": "reference" - }, - "speckle_type": "Objects.Other.MaterialQuantity", - "applicationId": null, - "totalChildrenCount": 0 - }, - { - "id": "0fadd22657e3204d0816ea27ede981c9", - "area": 244013.0526742648, - "units": "cm", - "volume": 284614.69306879985, - "material": { - "referencedId": "a27fe83ee1f177b67f4f3b08885d7ec3", - "speckle_type": "reference" - }, - "speckle_type": "Objects.Other.MaterialQuantity", - "applicationId": null, - "totalChildrenCount": 0 - }, - { - "id": "e7e460d77711e5be173505d9c719c069", - "area": 243912.13310796264, - "units": "cm", - "volume": 4726244.8552871235, - "material": { - "referencedId": "3d719768b25f185bedebf2865d77aaf1", - "speckle_type": "reference" - }, - "speckle_type": "Objects.Other.MaterialQuantity", - "applicationId": null, - "totalChildrenCount": 0 - }, - { - "id": "232b2e387b7b3bd01cdd67927c2f6d4c", - "area": 245274.5472530419, - "units": "cm", - "volume": 3567617.6423549717, - "material": { - "referencedId": "f4ff3dd4a33c3711164fcae43a2e1376", - "speckle_type": "reference" - }, - "speckle_type": "Objects.Other.MaterialQuantity", - "applicationId": null, - "totalChildrenCount": 0 - } - ], - "totalChildrenCount": 12, - "revitLinkedModelPath": "C:\\Users\\conno\\Downloads\\objects_R23.rvt" - }, - { - "id": "00d4a610a914f9dce457fad0260c3d73", - "area": 0, - "bbox": null, - "faces": [ - { - "referencedId": "e56082f6410025dec8f3cc658a77e4d4", - "speckle_type": "reference" - } - ], - "units": "cm", - "colors": [], - "volume": 0, - "vertices": [ - { - "referencedId": "e34b9581f041267f18092ba75dde35fb", - "speckle_type": "reference" - } - ], - "__closure": { - "e34b9581f041267f18092ba75dde35fb": 1, - "e56082f6410025dec8f3cc658a77e4d4": 1 - }, - "speckle_type": "Objects.Geometry.Mesh", - "applicationId": null, - "renderMaterial": { - "id": "b794d36ae48f2eaeb90050afffa9fc5d", - "name": "Concrete, Cast In Situ", - "diffuse": -4144960, - "opacity": 1, - "emissive": -16777216, - "metalness": 0, - "roughness": 1, - "speckle_type": "Objects.Other.RenderMaterial", - "applicationId": null, - "totalChildrenCount": 0 - }, - "textureCoordinates": [], - "totalChildrenCount": 0 - }, - { - "id": "3d719768b25f185bedebf2865d77aaf1", - "name": "Concrete, Cast In Situ", - "units": "cm", - "category": "Materials", - "elementId": "523", - "shininess": 128, - "worksetId": "0", - "parameters": { - "id": "f9794fb8eccaeda5ed8a0a3a28e1d346", - "IFC_GUID": { - "id": "f2b3418b5940ab9a15e01e60665b7a41", - "name": "IfcGUID", - "units": null, - "value": "3Zu5C40LOHrPC10026FoMc", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_GUID" - }, - "DOOR_COST": { - "id": "70a90fcdc412ffb329242075e203a4d2", - "name": "Cost", - "units": "฿", - "value": 0, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:currency-1.0.0", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "DOOR_COST" - }, - "speckle_type": "Base", - "ALL_MODEL_URL": { - "id": "6bc4103dfaced7e000e0cf70a83947f9", - "name": "URL", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_URL" - }, - "KEYNOTE_PARAM": { - "id": "464e3bdcb7e701e6268d75b04aa3e4c8", - "name": "Keynote", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "KEYNOTE_PARAM" - }, - "MATERIAL_NAME": { - "id": "19d1745bf48e335c3017288e20a48d9c", - "name": "Name", - "units": null, - "value": "Concrete, Cast In Situ", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "MATERIAL_NAME" - }, - "applicationId": null, - "ALL_MODEL_MARK": { - "id": "bbee379d7cce40dbf051ca9ff1c082d4", - "name": "Mark", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_MARK" - }, - "ALL_MODEL_IMAGE": { - "id": "061343fdcc12e983dfae8136fd33ab7e", - "name": "Image", - "units": null, - "value": "-1", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_IMAGE" - }, - "ALL_MODEL_MODEL": { - "id": "71e1f3c0a017f967bfa10cb9bbba8339", - "name": "Model", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_MODEL" - }, - "DESIGN_OPTION_ID": { - "id": "226f48801e3084d3491eb079e8dd0af8", - "name": "Design Option", - "units": null, - "value": "-1", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "DESIGN_OPTION_ID" - }, - "IFC_EXPORT_ELEMENT": { - "id": "9433ec034c7dc84e9769c9f9ba332bb5", - "name": "Export to IFC", - "units": null, - "value": 0, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_EXPORT_ELEMENT" - }, - "totalChildrenCount": 0, - "ALL_MODEL_TYPE_NAME": { - "id": "82132d22cc87843fba383206476c52a6", - "name": "Type Name", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_TYPE_NAME" - }, - "ELEM_CATEGORY_PARAM": { - "id": "5c19163426b2c299f7163af68a41a3c9", - "name": "Category", - "units": null, - "value": "-2000700", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ELEM_CATEGORY_PARAM" - }, - "MATERIAL_PARAM_GLOW": { - "id": "f7948f94bb4cd4874fcfac385b99710a", - "name": "Glow", - "units": null, - "value": false, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "MATERIAL_PARAM_GLOW" - }, - "MATERIAL_PARAM_COLOR": { - "id": "8bec4726a7d961b2290607ad243044d4", - "name": "Color", - "units": null, - "value": 12632256, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "MATERIAL_PARAM_COLOR" - }, - "ALL_MODEL_DESCRIPTION": { - "id": "9d2301889c646c85444cfaaaf02191ae", - "name": "Description", - "units": null, - "value": "physical material", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_DESCRIPTION" - }, - "ALL_MODEL_FAMILY_NAME": { - "id": "7ef14a150734e2bbefb591fab775d23e", - "name": "Family Name", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_FAMILY_NAME" - }, - "IFC_EXPORT_ELEMENT_AS": { - "id": "32da53c27657cacc0bd4d9bf2340705e", - "name": "Export to IFC As", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_EXPORT_ELEMENT_AS" - }, - "ALL_MODEL_MANUFACTURER": { - "id": "4a3f5e11988e1dc8bc3a460da541f759", - "name": "Manufacturer", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_MANUFACTURER" - }, - "ELEM_CATEGORY_PARAM_MT": { - "id": "f19ce7e9ee16a8b4b1af534dc221364d", - "name": "Category", - "units": null, - "value": "-2000700", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ELEM_CATEGORY_PARAM_MT" - }, - "PHY_MATERIAL_PARAM_TYPE": { - "id": "c6e2d14e773b26d13aea996e387f60e6", - "name": "Material Type", - "units": null, - "value": 2, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_TYPE" - }, - "MATERIAL_PARAM_SHININESS": { - "id": "a9cec9768cfc309623f977f1f9893a47", - "name": "Shininess", - "units": null, - "value": 128, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "MATERIAL_PARAM_SHININESS" - }, - "PHY_MATERIAL_PARAM_CLASS": { - "id": "12d13621d8d2575a5250474d54f99597", - "name": "Class", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_CLASS" - }, - "IFC_EXPORT_PREDEFINEDTYPE": { - "id": "1bbe93dc48596577683307830377663e", - "name": "IFC Predefined Type", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_EXPORT_PREDEFINEDTYPE" - }, - "MATERIAL_PARAM_SMOOTHNESS": { - "id": "90d91b34c9ac4a57f91891af4807f73f", - "name": "Smoothness", - "units": null, - "value": 50, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "MATERIAL_PARAM_SMOOTHNESS" - }, - "ALL_MODEL_INSTANCE_COMMENTS": { - "id": "77da8d6ddd91fd48c9a5b29afc99c2e5", - "name": "Comments", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_INSTANCE_COMMENTS" - }, - "MATERIAL_PARAM_TRANSPARENCY": { - "id": "dfc016c6c3c3e7fae046c80082deffa7", - "name": "Transparency", - "units": null, - "value": 0, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "MATERIAL_PARAM_TRANSPARENCY" - }, - "PHY_MATERIAL_PARAM_BEHAVIOR": { - "id": "28207a248cf16ca498e3691e5d203efb", - "name": "Behavior", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_BEHAVIOR" - }, - "PHY_MATERIAL_PARAM_SUBCLASS": { - "id": "e47305dca132008d877248bc69f9847c", - "name": "Subclass", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_SUBCLASS" - }, - "PHY_MATERIAL_PARAM_EXP_COEFF1": { - "id": "2dbf6ba098193992b4a52f681ca6639d", - "name": "Thermal expansion coefficient X", - "units": "1/°C", - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:inverseDegreesCelsius-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_EXP_COEFF1" - }, - "PHY_MATERIAL_PARAM_EXP_COEFF2": { - "id": "524095403d54d9182ff4b85f29a1ad5e", - "name": "Thermal expansion coefficient Y", - "units": "1/°C", - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:inverseDegreesCelsius-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_EXP_COEFF2" - }, - "PHY_MATERIAL_PARAM_EXP_COEFF3": { - "id": "b97653d1544a1114ee7ec525af08adbd", - "name": "Thermal expansion coefficient Z", - "units": "1/°C", - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:inverseDegreesCelsius-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_EXP_COEFF3" - }, - "PHY_MATERIAL_PARAM_SHEAR_MOD1": { - "id": "54b7dbaffed47acc7eda2b8dff874714", - "name": "Shear modulus X", - "units": "MPa", - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:megapascals-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_SHEAR_MOD1" - }, - "PHY_MATERIAL_PARAM_SHEAR_MOD2": { - "id": "f9e8f4cc6dc5e91d9c9cd978638292cf", - "name": "Shear modulus Y", - "units": "MPa", - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:megapascals-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_SHEAR_MOD2" - }, - "PHY_MATERIAL_PARAM_SHEAR_MOD3": { - "id": "33b52e54420e7a2bb4be967df6de4937", - "name": "Shear modulus Z", - "units": "MPa", - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:megapascals-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_SHEAR_MOD3" - }, - "PHY_MATERIAL_PARAM_YOUNG_MOD1": { - "id": "8c7b2277766a934bd220ecccb66ad87b", - "name": "Young modulus X", - "units": "MPa", - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:megapascals-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_YOUNG_MOD1" - }, - "PHY_MATERIAL_PARAM_YOUNG_MOD2": { - "id": "d9cb280f196bfd9bb133b8a03ae1dec6", - "name": "Young modulus Y", - "units": "MPa", - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:megapascals-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_YOUNG_MOD2" - }, - "PHY_MATERIAL_PARAM_YOUNG_MOD3": { - "id": "595e6b4df9f150d65d6433ca74e38819", - "name": "Young modulus Z", - "units": "MPa", - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:megapascals-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_YOUNG_MOD3" - }, - "PHY_MATERIAL_PARAM_UNIT_WEIGHT": { - "id": "0365141133c0bec432d42c52dac5f98c", - "name": "Unit weight", - "units": "kN/m³", - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:kilonewtonsPerCubicMeter-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_UNIT_WEIGHT" - }, - "PHY_MATERIAL_PARAM_LIGHT_WEIGHT": { - "id": "40aa211e291501f42b7771d301f5b852", - "name": "Lightweight", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_LIGHT_WEIGHT" - }, - "PHY_MATERIAL_PARAM_POISSON_MOD1": { - "id": "7bbcf88ea51db9d8c93848a33b79f0dc", - "name": "Poisson ratio X", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:general-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_POISSON_MOD1" - }, - "PHY_MATERIAL_PARAM_POISSON_MOD2": { - "id": "0b7efc45684d1d826de70867f32fb215", - "name": "Poisson ratio Y", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:general-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_POISSON_MOD2" - }, - "PHY_MATERIAL_PARAM_POISSON_MOD3": { - "id": "56da38f49a6279c207c33d41a003ca23", - "name": "Poisson ratio Z", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:general-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_POISSON_MOD3" - }, - "1190574f-2634-4c09-9dfc-856fca09d674": { - "id": "9cc7f8905be5567c97374f704c5246ef", - "name": "Malzeme UniFormat", - "units": null, - "value": null, - "isShared": true, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "1190574f-2634-4c09-9dfc-856fca09d674" - }, - "ba4ad29c-d864-4b61-84ba-1b992bebb003": { - "id": "ba170ec00ae31cd20182394c6f55e44a", - "name": "Malzeme Poz No", - "units": null, - "value": null, - "isShared": true, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ba4ad29c-d864-4b61-84ba-1b992bebb003" - }, - "PHY_MATERIAL_PARAM_CONCRETE_COMPRESSION": { - "id": "f69a4c7d787d6ca9581eee90facc4874", - "name": "Concrete compression", - "units": "MPa", - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:megapascals-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_CONCRETE_COMPRESSION" - }, - "PHY_MATERIAL_PARAM_SHEAR_STRENGTH_REDUCTION": { - "id": "cd69930af5dc5740b53c24bacdd8730c", - "name": "Shear strength modification", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:general-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_SHEAR_STRENGTH_REDUCTION" - } - }, - "smoothness": 50, - "speckle_type": "Objects.Other.Material:Objects.Other.Revit.RevitMaterial", - "transparency": 0, - "applicationId": "e3e05304-0156-11d5-9301-0000863f27ad-0000020b", - "materialClass": "Concrete", - "renderMaterial": null, - "builtInCategory": "OST_Materials", - "materialCategory": "Concrete", - "isRevitLinkedModel": false, - "materialQuantities": [], - "totalChildrenCount": 0, - "revitLinkedModelPath": "C:\\Users\\conno\\Downloads\\objects_R23.rvt" - }, - { - "id": "4e78f5d278ed4917de8fdad63512862d", - "name": "Damp-proofing", - "units": "cm", - "category": "Materials", - "elementId": "422", - "shininess": 64, - "worksetId": "0", - "parameters": { - "id": "f958fb7f01492639bab7c645b6b9f426", - "IFC_GUID": { - "id": "77f63cf2c0bc8577e9bbbec55f9794e1", - "name": "IfcGUID", - "units": null, - "value": "3Zu5Bv0LOHrPC10026FoOB", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_GUID" - }, - "DOOR_COST": { - "id": "70a90fcdc412ffb329242075e203a4d2", - "name": "Cost", - "units": "฿", - "value": 0, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:currency-1.0.0", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "DOOR_COST" - }, - "speckle_type": "Base", - "ALL_MODEL_URL": { - "id": "6bc4103dfaced7e000e0cf70a83947f9", - "name": "URL", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_URL" - }, - "KEYNOTE_PARAM": { - "id": "464e3bdcb7e701e6268d75b04aa3e4c8", - "name": "Keynote", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "KEYNOTE_PARAM" - }, - "MATERIAL_NAME": { - "id": "a76f5ac3e56f130e4f6a0c28f65ba9aa", - "name": "Name", - "units": null, - "value": "Damp-proofing", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "MATERIAL_NAME" - }, - "applicationId": null, - "ALL_MODEL_MARK": { - "id": "bbee379d7cce40dbf051ca9ff1c082d4", - "name": "Mark", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_MARK" - }, - "ALL_MODEL_IMAGE": { - "id": "061343fdcc12e983dfae8136fd33ab7e", - "name": "Image", - "units": null, - "value": "-1", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_IMAGE" - }, - "ALL_MODEL_MODEL": { - "id": "71e1f3c0a017f967bfa10cb9bbba8339", - "name": "Model", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_MODEL" - }, - "DESIGN_OPTION_ID": { - "id": "226f48801e3084d3491eb079e8dd0af8", - "name": "Design Option", - "units": null, - "value": "-1", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "DESIGN_OPTION_ID" - }, - "IFC_EXPORT_ELEMENT": { - "id": "9433ec034c7dc84e9769c9f9ba332bb5", - "name": "Export to IFC", - "units": null, - "value": 0, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_EXPORT_ELEMENT" - }, - "totalChildrenCount": 0, - "ALL_MODEL_TYPE_NAME": { - "id": "82132d22cc87843fba383206476c52a6", - "name": "Type Name", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_TYPE_NAME" - }, - "ELEM_CATEGORY_PARAM": { - "id": "5c19163426b2c299f7163af68a41a3c9", - "name": "Category", - "units": null, - "value": "-2000700", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ELEM_CATEGORY_PARAM" - }, - "MATERIAL_PARAM_GLOW": { - "id": "f7948f94bb4cd4874fcfac385b99710a", - "name": "Glow", - "units": null, - "value": false, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "MATERIAL_PARAM_GLOW" - }, - "MATERIAL_PARAM_COLOR": { - "id": "49e6695ebca83e0c0409656a486578f6", - "name": "Color", - "units": null, - "value": 8355711, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "MATERIAL_PARAM_COLOR" - }, - "ALL_MODEL_DESCRIPTION": { - "id": "815bf0d5fcf81dd945059e4c714b45f0", - "name": "Description", - "units": null, - "value": "Damp-proofing", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_DESCRIPTION" - }, - "ALL_MODEL_FAMILY_NAME": { - "id": "7ef14a150734e2bbefb591fab775d23e", - "name": "Family Name", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_FAMILY_NAME" - }, - "IFC_EXPORT_ELEMENT_AS": { - "id": "32da53c27657cacc0bd4d9bf2340705e", - "name": "Export to IFC As", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_EXPORT_ELEMENT_AS" - }, - "ALL_MODEL_MANUFACTURER": { - "id": "4a3f5e11988e1dc8bc3a460da541f759", - "name": "Manufacturer", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_MANUFACTURER" - }, - "ELEM_CATEGORY_PARAM_MT": { - "id": "f19ce7e9ee16a8b4b1af534dc221364d", - "name": "Category", - "units": null, - "value": "-2000700", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ELEM_CATEGORY_PARAM_MT" - }, - "PHY_MATERIAL_PARAM_TYPE": { - "id": "4f8f1461e5fec1b303a4eb2c0dd80f61", - "name": "Material Type", - "units": null, - "value": 4, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_TYPE" - }, - "MATERIAL_PARAM_SHININESS": { - "id": "8cc49906e2c4369b07ebf333137ef10c", - "name": "Shininess", - "units": null, - "value": 64, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "MATERIAL_PARAM_SHININESS" - }, - "PHY_MATERIAL_PARAM_CLASS": { - "id": "12d13621d8d2575a5250474d54f99597", - "name": "Class", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_CLASS" - }, - "IFC_EXPORT_PREDEFINEDTYPE": { - "id": "1bbe93dc48596577683307830377663e", - "name": "IFC Predefined Type", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_EXPORT_PREDEFINEDTYPE" - }, - "MATERIAL_PARAM_SMOOTHNESS": { - "id": "90d91b34c9ac4a57f91891af4807f73f", - "name": "Smoothness", - "units": null, - "value": 50, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "MATERIAL_PARAM_SMOOTHNESS" - }, - "ALL_MODEL_INSTANCE_COMMENTS": { - "id": "77da8d6ddd91fd48c9a5b29afc99c2e5", - "name": "Comments", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_INSTANCE_COMMENTS" - }, - "MATERIAL_PARAM_TRANSPARENCY": { - "id": "dfc016c6c3c3e7fae046c80082deffa7", - "name": "Transparency", - "units": null, - "value": 0, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "MATERIAL_PARAM_TRANSPARENCY" - }, - "PHY_MATERIAL_PARAM_BEHAVIOR": { - "id": "28207a248cf16ca498e3691e5d203efb", - "name": "Behavior", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_BEHAVIOR" - }, - "PHY_MATERIAL_PARAM_SUBCLASS": { - "id": "e47305dca132008d877248bc69f9847c", - "name": "Subclass", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_SUBCLASS" - }, - "PHY_MATERIAL_PARAM_EXP_COEFF1": { - "id": "2dbf6ba098193992b4a52f681ca6639d", - "name": "Thermal expansion coefficient X", - "units": "1/°C", - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:inverseDegreesCelsius-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_EXP_COEFF1" - }, - "PHY_MATERIAL_PARAM_EXP_COEFF2": { - "id": "524095403d54d9182ff4b85f29a1ad5e", - "name": "Thermal expansion coefficient Y", - "units": "1/°C", - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:inverseDegreesCelsius-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_EXP_COEFF2" - }, - "PHY_MATERIAL_PARAM_EXP_COEFF3": { - "id": "b97653d1544a1114ee7ec525af08adbd", - "name": "Thermal expansion coefficient Z", - "units": "1/°C", - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:inverseDegreesCelsius-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_EXP_COEFF3" - }, - "PHY_MATERIAL_PARAM_SHEAR_MOD1": { - "id": "54b7dbaffed47acc7eda2b8dff874714", - "name": "Shear modulus X", - "units": "MPa", - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:megapascals-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_SHEAR_MOD1" - }, - "PHY_MATERIAL_PARAM_SHEAR_MOD2": { - "id": "f9e8f4cc6dc5e91d9c9cd978638292cf", - "name": "Shear modulus Y", - "units": "MPa", - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:megapascals-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_SHEAR_MOD2" - }, - "PHY_MATERIAL_PARAM_SHEAR_MOD3": { - "id": "33b52e54420e7a2bb4be967df6de4937", - "name": "Shear modulus Z", - "units": "MPa", - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:megapascals-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_SHEAR_MOD3" - }, - "PHY_MATERIAL_PARAM_YOUNG_MOD1": { - "id": "8c7b2277766a934bd220ecccb66ad87b", - "name": "Young modulus X", - "units": "MPa", - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:megapascals-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_YOUNG_MOD1" - }, - "PHY_MATERIAL_PARAM_YOUNG_MOD2": { - "id": "d9cb280f196bfd9bb133b8a03ae1dec6", - "name": "Young modulus Y", - "units": "MPa", - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:megapascals-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_YOUNG_MOD2" - }, - "PHY_MATERIAL_PARAM_YOUNG_MOD3": { - "id": "595e6b4df9f150d65d6433ca74e38819", - "name": "Young modulus Z", - "units": "MPa", - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:megapascals-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_YOUNG_MOD3" - }, - "PHY_MATERIAL_PARAM_UNIT_WEIGHT": { - "id": "0365141133c0bec432d42c52dac5f98c", - "name": "Unit weight", - "units": "kN/m³", - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:kilonewtonsPerCubicMeter-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_UNIT_WEIGHT" - }, - "PHY_MATERIAL_PARAM_POISSON_MOD1": { - "id": "7bbcf88ea51db9d8c93848a33b79f0dc", - "name": "Poisson ratio X", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:general-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_POISSON_MOD1" - }, - "PHY_MATERIAL_PARAM_POISSON_MOD2": { - "id": "0b7efc45684d1d826de70867f32fb215", - "name": "Poisson ratio Y", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:general-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_POISSON_MOD2" - }, - "PHY_MATERIAL_PARAM_POISSON_MOD3": { - "id": "56da38f49a6279c207c33d41a003ca23", - "name": "Poisson ratio Z", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:general-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_POISSON_MOD3" - }, - "1190574f-2634-4c09-9dfc-856fca09d674": { - "id": "9cc7f8905be5567c97374f704c5246ef", - "name": "Malzeme UniFormat", - "units": null, - "value": null, - "isShared": true, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "1190574f-2634-4c09-9dfc-856fca09d674" - }, - "ba4ad29c-d864-4b61-84ba-1b992bebb003": { - "id": "ba170ec00ae31cd20182394c6f55e44a", - "name": "Malzeme Poz No", - "units": null, - "value": null, - "isShared": true, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ba4ad29c-d864-4b61-84ba-1b992bebb003" - } - }, - "smoothness": 50, - "speckle_type": "Objects.Other.Material:Objects.Other.Revit.RevitMaterial", - "transparency": 0, - "applicationId": "e3e052f9-0156-11d5-9301-0000863f27ad-000001a6", - "materialClass": "Generic", - "renderMaterial": null, - "builtInCategory": "OST_Materials", - "materialCategory": "Generic", - "isRevitLinkedModel": false, - "materialQuantities": [], - "totalChildrenCount": 0, - "revitLinkedModelPath": "C:\\Users\\conno\\Downloads\\objects_R23.rvt" - }, - { - "id": "52c485bd16243d285dad61fa712421ce", - "data": [ - 3, - 3, - 6, - 2, - 3, - 6, - 3, - 5, - 3, - 2, - 7, - 4, - 3, - 7, - 2, - 6, - 3, - 5, - 0, - 1, - 3, - 0, - 5, - 3, - 3, - 4, - 1, - 2, - 3, - 1, - 4, - 5, - 3, - 8, - 9, - 10, - 3, - 10, - 11, - 8, - 3, - 12, - 13, - 14, - 3, - 14, - 15, - 12, - 3, - 16, - 17, - 18, - 3, - 18, - 19, - 16, - 3, - 20, - 21, - 22, - 3, - 22, - 23, - 20, - 3, - 24, - 25, - 26, - 3, - 26, - 27, - 24, - 3, - 28, - 29, - 30, - 3, - 30, - 31, - 28, - 3, - 37, - 35, - 36, - 3, - 34, - 35, - 32, - 3, - 37, - 32, - 35, - 3, - 33, - 34, - 32, - 3, - 38, - 39, - 40, - 3, - 40, - 41, - 38, - 3, - 47, - 43, - 46, - 3, - 45, - 46, - 44, - 3, - 47, - 42, - 43, - 3, - 44, - 46, - 43, - 3, - 48, - 49, - 50, - 3, - 50, - 51, - 48 - ], - "speckle_type": "Speckle.Core.Models.DataChunk", - "applicationId": null, - "totalChildrenCount": 0 - }, - { - "id": "9407c61a13fdff3313e5535517d0820e", - "level": { - "id": "13ec7a96382822d854cebf49f12e37f8", - "name": "Level 1", - "units": "cm", - "category": "Levels", - "elementId": "311", - "elevation": 0, - "worksetId": "0", - "createView": true, - "parameters": { - "id": "183527059ef874eecdd4bb74b9ff762f", - "IFC_GUID": { - "id": "253f76685236e94d17544a66f6849bf6", - "name": "IfcGUID", - "units": null, - "value": "3Zu5Bv0LOHrPC10026FoQQ", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_GUID" - }, - "DATUM_TEXT": { - "id": "9dff18eef47fbfcc195b15e01a076e28", - "name": "Name", - "units": null, - "value": "Level 1", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "DATUM_TEXT" - }, - "LEVEL_ELEV": { - "id": "c863e6214242197d74a3a28590f14681", - "name": "Elevation", - "units": "cm", - "value": 0, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:centimeters-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "LEVEL_ELEV" - }, - "speckle_type": "Base", - "applicationId": null, - "ELEM_TYPE_PARAM": { - "id": "b90ad7d6650e182f4b21325bd8e96545", - "name": "Type", - "units": null, - "value": "305", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ELEM_TYPE_PARAM" - }, - "SYMBOL_ID_PARAM": { - "id": "3ca4e3044444c496aff73993b39cee41", - "name": "Type Id", - "units": null, - "value": "305", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "SYMBOL_ID_PARAM" - }, - "DESIGN_OPTION_ID": { - "id": "226f48801e3084d3491eb079e8dd0af8", - "name": "Design Option", - "units": null, - "value": "-1", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "DESIGN_OPTION_ID" - }, - "ELEM_FAMILY_PARAM": { - "id": "775e7c470bc83edf158eb2afd5169271", - "name": "Family", - "units": null, - "value": "305", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ELEM_FAMILY_PARAM" - }, - "LEVEL_UP_TO_LEVEL": { - "id": "2c98ab93a27aba3d81fd68d8848d8813", - "name": "Story Above", - "units": null, - "value": "-1", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "LEVEL_UP_TO_LEVEL" - }, - "IFC_EXPORT_ELEMENT": { - "id": "9433ec034c7dc84e9769c9f9ba332bb5", - "name": "Export to IFC", - "units": null, - "value": 0, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_EXPORT_ELEMENT" - }, - "totalChildrenCount": 0, - "ALL_MODEL_TYPE_NAME": { - "id": "82132d22cc87843fba383206476c52a6", - "name": "Type Name", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_TYPE_NAME" - }, - "ELEM_CATEGORY_PARAM": { - "id": "06675cc605d2289783137e5f409841df", - "name": "Category", - "units": null, - "value": "-2000240", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ELEM_CATEGORY_PARAM" - }, - "LEVEL_IS_STRUCTURAL": { - "id": "c90aaf2fd87636058ab60a26023fab1e", - "name": "Structural", - "units": null, - "value": false, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "LEVEL_IS_STRUCTURAL" - }, - "ALL_MODEL_FAMILY_NAME": { - "id": "7ef14a150734e2bbefb591fab775d23e", - "name": "Family Name", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_FAMILY_NAME" - }, - "ELEM_CATEGORY_PARAM_MT": { - "id": "93b264ba0193732c54d676799cae6c09", - "name": "Category", - "units": null, - "value": "-2000240", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ELEM_CATEGORY_PARAM_MT" - }, - "LEVEL_IS_BUILDING_STORY": { - "id": "1a16badbf32cc26bac1f63565f8c7727", - "name": "Building Story", - "units": null, - "value": true, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "LEVEL_IS_BUILDING_STORY" - }, - "DATUM_VOLUME_OF_INTEREST": { - "id": "5ef80c5931aed0c387b423cce5334d30", - "name": "Scope Box", - "units": null, - "value": "400175", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "DATUM_VOLUME_OF_INTEREST" - }, - "ELEM_FAMILY_AND_TYPE_PARAM": { - "id": "3050db1515d6afaf12d8e50f101eccc7", - "name": "Family and Type", - "units": null, - "value": "305", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ELEM_FAMILY_AND_TYPE_PARAM" - }, - "LEVEL_ROOM_COMPUTATION_HEIGHT": { - "id": "4bf2025d03a4d5cba6962772405fefd3", - "name": "Computation Height", - "units": "cm", - "value": 0, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:centimeters-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "LEVEL_ROOM_COMPUTATION_HEIGHT" - } - }, - "speckle_type": "Objects.BuiltElements.Level:Objects.BuiltElements.Revit.RevitLevel", - "applicationId": "e3e052f9-0156-11d5-9301-0000863f27ad-00000137", - "referenceOnly": false, - "builtInCategory": "OST_Levels", - "isRevitLinkedModel": false, - "materialQuantities": [], - "totalChildrenCount": 0, - "revitLinkedModelPath": "C:\\Users\\conno\\Downloads\\objects_R23.rvt" - }, - "units": "cm", - "category": "Generic Models", - "mirrored": false, - "__closure": { - "a724becac2af2d936a7c79349ce9b709": 1 - }, - "elementId": "350445", - "transform": { - "id": "0657a4853443434a5bc1914a16b3197e", - "units": "cm", - "matrix": [ - 1, - 0, - 0, - 6074.404549122284, - 0, - 1, - 0, - -1463.3102950304494, - 0, - 0, - 1, - 55.050217281585766, - 0, - 0, - 0, - 1 - ], - "speckle_type": "Objects.Other.Transform", - "applicationId": null, - "totalChildrenCount": 0 - }, - "worksetId": "0", - "definition": { - "referencedId": "a724becac2af2d936a7c79349ce9b709", - "speckle_type": "reference" - }, - "parameters": { - "id": "a4913f245c89d285f7502848afd1bbbd", - "IFC_GUID": { - "id": "1826209d32db70d03346475a0b706a3b", - "name": "IfcGUID", - "units": null, - "value": "1cghwxWkP5hebx$jnaWsDQ", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_GUID" - }, - "DOOR_COST": { - "id": "eb6b241b8a9d30a8d060ed3045af62ee", - "name": "Cost", - "units": "฿", - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:currency-1.0.0", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "DOOR_COST" - }, - "speckle_type": "Base", - "ALL_MODEL_URL": { - "id": "8abe9322516ea72e715da245b4303d09", - "name": "URL", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_URL" - }, - "HOST_ID_PARAM": { - "id": "84e2c3f0bcf985aca1fea591785f5827", - "name": "Host Id", - "units": null, - "value": "350409", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "HOST_ID_PARAM" - }, - "IFC_TYPE_GUID": { - "id": "eaeee9de0d8007e3302d5c2d77c4d2ed", - "name": "Type IfcGUID", - "units": null, - "value": "1cghwxWkP5hebx$jnaWsFz", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_TYPE_GUID" - }, - "KEYNOTE_PARAM": { - "id": "bcf04d1e0a8e59168a66d0b63eeb1ab8", - "name": "Keynote", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "KEYNOTE_PARAM" - }, - "PHASE_CREATED": { - "id": "75ca890af17bf6c300fa8bf49a1faad3", - "name": "Phase Created", - "units": null, - "value": "86961", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHASE_CREATED" - }, - "applicationId": null, - "ALL_MODEL_MARK": { - "id": "ccdd2d167e1e58cb2e82700148409df7", - "name": "Mark", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_MARK" - }, - "OMNICLASS_CODE": { - "id": "b75af5017b17f1a11641d8f261601929", - "name": "OmniClass Number", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "OMNICLASS_CODE" - }, - "UNIFORMAT_CODE": { - "id": "8ed423285d1d5d7118b7a43e40b32883", - "name": "Assembly Code", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "UNIFORMAT_CODE" - }, - "WINDOW_TYPE_ID": { - "id": "6b8a0975069f64a97adb326251fb057a", - "name": "Type Mark", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "WINDOW_TYPE_ID" - }, - "ALL_MODEL_IMAGE": { - "id": "061343fdcc12e983dfae8136fd33ab7e", - "name": "Image", - "units": null, - "value": "-1", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_IMAGE" - }, - "ALL_MODEL_MODEL": { - "id": "934bca477ca5e150978b7babe1483839", - "name": "Model", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_MODEL" - }, - "ELEM_TYPE_PARAM": { - "id": "eac4f285a8990c0d62ef4fc6249c37ca", - "name": "Type", - "units": null, - "value": "350282", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ELEM_TYPE_PARAM" - }, - "SYMBOL_ID_PARAM": { - "id": "32398f6c3af193f439748a2ba2b8c707", - "name": "Type Id", - "units": null, - "value": "350282", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "SYMBOL_ID_PARAM" - }, - "DESIGN_OPTION_ID": { - "id": "226f48801e3084d3491eb079e8dd0af8", - "name": "Design Option", - "units": null, - "value": "-1", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "DESIGN_OPTION_ID" - }, - "PHASE_DEMOLISHED": { - "id": "a6185b7ecb2668c79dcca9a47ca54206", - "name": "Phase Demolished", - "units": null, - "value": "-1", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHASE_DEMOLISHED" - }, - "ELEM_FAMILY_PARAM": { - "id": "65d7ac846dfd5ad8fa18b9ac87d4837f", - "name": "Family", - "units": null, - "value": "350282", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ELEM_FAMILY_PARAM" - }, - "FAMILY_LEVEL_PARAM": { - "id": "63a4211d1b852275f34d9bc743ec5d4b", - "name": "Level", - "units": null, - "value": "311", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "FAMILY_LEVEL_PARAM" - }, - "HOST_AREA_COMPUTED": { - "id": "90e0ff0a6668d04de3b1154a7933a02c", - "name": "Area", - "units": "m²", - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:squareMeters-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "HOST_AREA_COMPUTED" - }, - "IFC_EXPORT_ELEMENT": { - "id": "9433ec034c7dc84e9769c9f9ba332bb5", - "name": "Export to IFC", - "units": null, - "value": 0, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_EXPORT_ELEMENT" - }, - "totalChildrenCount": 0, - "ALL_MODEL_TYPE_NAME": { - "id": "82132d22cc87843fba383206476c52a6", - "name": "Type Name", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_TYPE_NAME" - }, - "ELEM_CATEGORY_PARAM": { - "id": "b1907dd0236c7091c80d6da690100dc9", - "name": "Category", - "units": null, - "value": "-2000151", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ELEM_CATEGORY_PARAM" - }, - "ALL_MODEL_TYPE_IMAGE": { - "id": "e6417f0a96a3c38d9a622ab5494e8612", - "name": "Type Image", - "units": null, - "value": "-1", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_TYPE_IMAGE" - }, - "HOST_VOLUME_COMPUTED": { - "id": "f5c4773773dfddba76bbb03d3f33a68f", - "name": "Volume", - "units": "m³", - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:cubicMeters-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "HOST_VOLUME_COMPUTED" - }, - "SCHEDULE_LEVEL_PARAM": { - "id": "cf9f641ea0d9a98fee310f8df288601d", - "name": "Level", - "units": null, - "value": "311", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "SCHEDULE_LEVEL_PARAM" - }, - "ALL_MODEL_DESCRIPTION": { - "id": "dc455c0fef6a720aff6c536385b6c433", - "name": "Description", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_DESCRIPTION" - }, - "ALL_MODEL_FAMILY_NAME": { - "id": "7ef14a150734e2bbefb591fab775d23e", - "name": "Family Name", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_FAMILY_NAME" - }, - "IFC_EXPORT_ELEMENT_AS": { - "id": "32da53c27657cacc0bd4d9bf2340705e", - "name": "Export to IFC As", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_EXPORT_ELEMENT_AS" - }, - "OMNICLASS_DESCRIPTION": { - "id": "1ff48b1d6e07459451e87d61fb809f18", - "name": "OmniClass Title", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "OMNICLASS_DESCRIPTION" - }, - "UNIFORMAT_DESCRIPTION": { - "id": "026207f8c83f70e3dccb1c9d4f0a195c", - "name": "Assembly Description", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "UNIFORMAT_DESCRIPTION" - }, - "ALL_MODEL_MANUFACTURER": { - "id": "03e476c9ecb3a8c39c42a4a99cd15782", - "name": "Manufacturer", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_MANUFACTURER" - }, - "ELEM_CATEGORY_PARAM_MT": { - "id": "a9a91cfa6bc04ca8a0f097d69beb2dda", - "name": "Category", - "units": null, - "value": "-2000151", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ELEM_CATEGORY_PARAM_MT" - }, - "ALL_MODEL_TYPE_COMMENTS": { - "id": "389eba7a64948e921f41b0f13cab0ff8", - "name": "Type Comments", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_TYPE_COMMENTS" - }, - "IFC_EXPORT_ELEMENT_TYPE": { - "id": "0c72bf98d9abf2f9db170f5045bf26e9", - "name": "Export Type to IFC", - "units": null, - "value": 0, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_EXPORT_ELEMENT_TYPE" - }, - "IFC_EXPORT_PREDEFINEDTYPE": { - "id": "1bbe93dc48596577683307830377663e", - "name": "IFC Predefined Type", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_EXPORT_PREDEFINEDTYPE" - }, - "INSTANCE_OFFSET_POS_PARAM": { - "id": "0a2158c7e56fda885b5b4a5a5cebe12c", - "name": "Moves With Nearby Elements", - "units": null, - "value": false, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "INSTANCE_OFFSET_POS_PARAM" - }, - "ELEM_FAMILY_AND_TYPE_PARAM": { - "id": "4608d1553a2ae24a4a02b0e0a592253a", - "name": "Family and Type", - "units": null, - "value": "350282", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ELEM_FAMILY_AND_TYPE_PARAM" - }, - "IFC_EXPORT_ELEMENT_TYPE_AS": { - "id": "cf49cd58a60d03878cf4c7475e053f7a", - "name": "Export Type to IFC As", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_EXPORT_ELEMENT_TYPE_AS" - }, - "ALL_MODEL_INSTANCE_COMMENTS": { - "id": "4025b5ee5e68c7b65fdfc179bef5eab1", - "name": "Comments", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_INSTANCE_COMMENTS" - }, - "STRUCTURAL_FAMILY_CODE_NAME": { - "id": "d9e23cc21311d6878acf9df17a045f8c", - "name": "Code Name", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "STRUCTURAL_FAMILY_CODE_NAME" - }, - "IFC_EXPORT_PREDEFINEDTYPE_TYPE": { - "id": "b7398ab9ec911545bcf3ff348222774a", - "name": "Type IFC Predefined Type", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_EXPORT_PREDEFINEDTYPE_TYPE" - }, - "FAMILY_FREEINST_DEFAULT_ELEVATION": { - "id": "43095255b110c23ab675e11b971bdb0c", - "name": "Default Elevation", - "units": "cm", - "value": 0, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:centimeters-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "FAMILY_FREEINST_DEFAULT_ELEVATION" - } - }, - "handFlipped": false, - "phaseCreated": "New Construction", - "speckle_type": "Objects.Other.Revit.RevitInstance", - "applicationId": "66aabebb-82e6-45ae-897b-fedc64863bb7-000558ed", - "facingFlipped": false, - "renderMaterial": null, - "builtInCategory": "OST_GenericModel", - "isRevitLinkedModel": false, - "materialQuantities": [], - "totalChildrenCount": 0, - "revitLinkedModelPath": "C:\\Users\\conno\\Downloads\\objects_R23.rvt" - }, - { - "id": "9f9f7958ee36b2b582970b2153b96d05", - "area": 0, - "bbox": null, - "faces": [ - { - "referencedId": "52c485bd16243d285dad61fa712421ce", - "speckle_type": "reference" - } - ], - "units": "cm", - "colors": [], - "volume": 0, - "vertices": [ - { - "referencedId": "e85bd8c0246c15998785930e21b9f291", - "speckle_type": "reference" - } - ], - "__closure": { - "52c485bd16243d285dad61fa712421ce": 1, - "e85bd8c0246c15998785930e21b9f291": 1 - }, - "speckle_type": "Objects.Geometry.Mesh", - "applicationId": null, - "renderMaterial": { - "id": "19471384302737d6ce6b2f9309007ead", - "name": "Site - Hardcore", - "diffuse": -5952982, - "opacity": 1, - "emissive": -16777216, - "metalness": 0, - "roughness": 1, - "speckle_type": "Objects.Other.RenderMaterial", - "applicationId": null, - "totalChildrenCount": 0 - }, - "textureCoordinates": [], - "totalChildrenCount": 0 - }, - { - "id": "a27fe83ee1f177b67f4f3b08885d7ec3", - "name": "Sand", - "units": "cm", - "category": "Materials", - "elementId": "522", - "shininess": 128, - "worksetId": "0", - "parameters": { - "id": "13624337daeafddd1821f531ab868076", - "IFC_GUID": { - "id": "2869dc2e70992bc90aa63e08e8cf9712", - "name": "IfcGUID", - "units": null, - "value": "3Zu5C40LOHrPC10026FoMd", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_GUID" - }, - "DOOR_COST": { - "id": "70a90fcdc412ffb329242075e203a4d2", - "name": "Cost", - "units": "฿", - "value": 0, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:currency-1.0.0", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "DOOR_COST" - }, - "speckle_type": "Base", - "ALL_MODEL_URL": { - "id": "6bc4103dfaced7e000e0cf70a83947f9", - "name": "URL", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_URL" - }, - "KEYNOTE_PARAM": { - "id": "464e3bdcb7e701e6268d75b04aa3e4c8", - "name": "Keynote", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "KEYNOTE_PARAM" - }, - "MATERIAL_NAME": { - "id": "b9ae457215355d0e56b62f00da373962", - "name": "Name", - "units": null, - "value": "Sand", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "MATERIAL_NAME" - }, - "applicationId": null, - "ALL_MODEL_MARK": { - "id": "bbee379d7cce40dbf051ca9ff1c082d4", - "name": "Mark", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_MARK" - }, - "ALL_MODEL_IMAGE": { - "id": "061343fdcc12e983dfae8136fd33ab7e", - "name": "Image", - "units": null, - "value": "-1", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_IMAGE" - }, - "ALL_MODEL_MODEL": { - "id": "71e1f3c0a017f967bfa10cb9bbba8339", - "name": "Model", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_MODEL" - }, - "DESIGN_OPTION_ID": { - "id": "226f48801e3084d3491eb079e8dd0af8", - "name": "Design Option", - "units": null, - "value": "-1", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "DESIGN_OPTION_ID" - }, - "IFC_EXPORT_ELEMENT": { - "id": "9433ec034c7dc84e9769c9f9ba332bb5", - "name": "Export to IFC", - "units": null, - "value": 0, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_EXPORT_ELEMENT" - }, - "totalChildrenCount": 0, - "ALL_MODEL_TYPE_NAME": { - "id": "82132d22cc87843fba383206476c52a6", - "name": "Type Name", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_TYPE_NAME" - }, - "ELEM_CATEGORY_PARAM": { - "id": "5c19163426b2c299f7163af68a41a3c9", - "name": "Category", - "units": null, - "value": "-2000700", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ELEM_CATEGORY_PARAM" - }, - "MATERIAL_PARAM_GLOW": { - "id": "f7948f94bb4cd4874fcfac385b99710a", - "name": "Glow", - "units": null, - "value": false, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "MATERIAL_PARAM_GLOW" - }, - "MATERIAL_PARAM_COLOR": { - "id": "5e92b98190fcae6b3011bedef7aa2c58", - "name": "Color", - "units": null, - "value": 11712447, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "MATERIAL_PARAM_COLOR" - }, - "ALL_MODEL_DESCRIPTION": { - "id": "b78c9f4ccde34e07c38fd97c9c33d1f2", - "name": "Description", - "units": null, - "value": "Sand", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_DESCRIPTION" - }, - "ALL_MODEL_FAMILY_NAME": { - "id": "7ef14a150734e2bbefb591fab775d23e", - "name": "Family Name", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_FAMILY_NAME" - }, - "IFC_EXPORT_ELEMENT_AS": { - "id": "32da53c27657cacc0bd4d9bf2340705e", - "name": "Export to IFC As", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_EXPORT_ELEMENT_AS" - }, - "ALL_MODEL_MANUFACTURER": { - "id": "4a3f5e11988e1dc8bc3a460da541f759", - "name": "Manufacturer", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_MANUFACTURER" - }, - "ELEM_CATEGORY_PARAM_MT": { - "id": "f19ce7e9ee16a8b4b1af534dc221364d", - "name": "Category", - "units": null, - "value": "-2000700", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ELEM_CATEGORY_PARAM_MT" - }, - "PHY_MATERIAL_PARAM_TYPE": { - "id": "4f8f1461e5fec1b303a4eb2c0dd80f61", - "name": "Material Type", - "units": null, - "value": 4, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_TYPE" - }, - "MATERIAL_PARAM_SHININESS": { - "id": "a9cec9768cfc309623f977f1f9893a47", - "name": "Shininess", - "units": null, - "value": 128, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "MATERIAL_PARAM_SHININESS" - }, - "PHY_MATERIAL_PARAM_CLASS": { - "id": "12d13621d8d2575a5250474d54f99597", - "name": "Class", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_CLASS" - }, - "IFC_EXPORT_PREDEFINEDTYPE": { - "id": "1bbe93dc48596577683307830377663e", - "name": "IFC Predefined Type", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_EXPORT_PREDEFINEDTYPE" - }, - "MATERIAL_PARAM_SMOOTHNESS": { - "id": "90d91b34c9ac4a57f91891af4807f73f", - "name": "Smoothness", - "units": null, - "value": 50, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "MATERIAL_PARAM_SMOOTHNESS" - }, - "ALL_MODEL_INSTANCE_COMMENTS": { - "id": "77da8d6ddd91fd48c9a5b29afc99c2e5", - "name": "Comments", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_INSTANCE_COMMENTS" - }, - "MATERIAL_PARAM_TRANSPARENCY": { - "id": "dfc016c6c3c3e7fae046c80082deffa7", - "name": "Transparency", - "units": null, - "value": 0, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "MATERIAL_PARAM_TRANSPARENCY" - }, - "PHY_MATERIAL_PARAM_BEHAVIOR": { - "id": "28207a248cf16ca498e3691e5d203efb", - "name": "Behavior", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_BEHAVIOR" - }, - "PHY_MATERIAL_PARAM_SUBCLASS": { - "id": "e47305dca132008d877248bc69f9847c", - "name": "Subclass", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_SUBCLASS" - }, - "PHY_MATERIAL_PARAM_EXP_COEFF1": { - "id": "2dbf6ba098193992b4a52f681ca6639d", - "name": "Thermal expansion coefficient X", - "units": "1/°C", - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:inverseDegreesCelsius-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_EXP_COEFF1" - }, - "PHY_MATERIAL_PARAM_EXP_COEFF2": { - "id": "524095403d54d9182ff4b85f29a1ad5e", - "name": "Thermal expansion coefficient Y", - "units": "1/°C", - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:inverseDegreesCelsius-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_EXP_COEFF2" - }, - "PHY_MATERIAL_PARAM_EXP_COEFF3": { - "id": "b97653d1544a1114ee7ec525af08adbd", - "name": "Thermal expansion coefficient Z", - "units": "1/°C", - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:inverseDegreesCelsius-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_EXP_COEFF3" - }, - "PHY_MATERIAL_PARAM_SHEAR_MOD1": { - "id": "54b7dbaffed47acc7eda2b8dff874714", - "name": "Shear modulus X", - "units": "MPa", - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:megapascals-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_SHEAR_MOD1" - }, - "PHY_MATERIAL_PARAM_SHEAR_MOD2": { - "id": "f9e8f4cc6dc5e91d9c9cd978638292cf", - "name": "Shear modulus Y", - "units": "MPa", - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:megapascals-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_SHEAR_MOD2" - }, - "PHY_MATERIAL_PARAM_SHEAR_MOD3": { - "id": "33b52e54420e7a2bb4be967df6de4937", - "name": "Shear modulus Z", - "units": "MPa", - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:megapascals-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_SHEAR_MOD3" - }, - "PHY_MATERIAL_PARAM_YOUNG_MOD1": { - "id": "8c7b2277766a934bd220ecccb66ad87b", - "name": "Young modulus X", - "units": "MPa", - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:megapascals-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_YOUNG_MOD1" - }, - "PHY_MATERIAL_PARAM_YOUNG_MOD2": { - "id": "d9cb280f196bfd9bb133b8a03ae1dec6", - "name": "Young modulus Y", - "units": "MPa", - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:megapascals-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_YOUNG_MOD2" - }, - "PHY_MATERIAL_PARAM_YOUNG_MOD3": { - "id": "595e6b4df9f150d65d6433ca74e38819", - "name": "Young modulus Z", - "units": "MPa", - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:megapascals-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_YOUNG_MOD3" - }, - "PHY_MATERIAL_PARAM_UNIT_WEIGHT": { - "id": "0365141133c0bec432d42c52dac5f98c", - "name": "Unit weight", - "units": "kN/m³", - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:kilonewtonsPerCubicMeter-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_UNIT_WEIGHT" - }, - "PHY_MATERIAL_PARAM_POISSON_MOD1": { - "id": "7bbcf88ea51db9d8c93848a33b79f0dc", - "name": "Poisson ratio X", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:general-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_POISSON_MOD1" - }, - "PHY_MATERIAL_PARAM_POISSON_MOD2": { - "id": "0b7efc45684d1d826de70867f32fb215", - "name": "Poisson ratio Y", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:general-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_POISSON_MOD2" - }, - "PHY_MATERIAL_PARAM_POISSON_MOD3": { - "id": "56da38f49a6279c207c33d41a003ca23", - "name": "Poisson ratio Z", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:general-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "PHY_MATERIAL_PARAM_POISSON_MOD3" - }, - "1190574f-2634-4c09-9dfc-856fca09d674": { - "id": "9cc7f8905be5567c97374f704c5246ef", - "name": "Malzeme UniFormat", - "units": null, - "value": null, - "isShared": true, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "1190574f-2634-4c09-9dfc-856fca09d674" - }, - "ba4ad29c-d864-4b61-84ba-1b992bebb003": { - "id": "ba170ec00ae31cd20182394c6f55e44a", - "name": "Malzeme Poz No", - "units": null, - "value": null, - "isShared": true, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ba4ad29c-d864-4b61-84ba-1b992bebb003" - } - }, - "smoothness": 50, - "speckle_type": "Objects.Other.Material:Objects.Other.Revit.RevitMaterial", - "transparency": 0, - "applicationId": "e3e05304-0156-11d5-9301-0000863f27ad-0000020a", - "materialClass": "Generic", - "renderMaterial": null, - "builtInCategory": "OST_Materials", - "materialCategory": "Generic", - "isRevitLinkedModel": false, - "materialQuantities": [], - "totalChildrenCount": 0, - "revitLinkedModelPath": "C:\\Users\\conno\\Downloads\\objects_R23.rvt" - }, - { - "id": "a724becac2af2d936a7c79349ce9b709", - "type": "Floor Opening", - "units": "cm", - "family": "Floor Opening", - "category": "Generic Models", - "elements": null, - "elementId": "350282", - "worksetId": "0", - "parameters": { - "id": "5b0a3c9b4b0c07d1fcddfe3f8d3e6cd7", - "DOOR_COST": { - "id": "eb6b241b8a9d30a8d060ed3045af62ee", - "name": "Cost", - "units": "฿", - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:currency-1.0.0", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "DOOR_COST" - }, - "speckle_type": "Base", - "ALL_MODEL_URL": { - "id": "8abe9322516ea72e715da245b4303d09", - "name": "URL", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_URL" - }, - "IFC_TYPE_GUID": { - "id": "eaeee9de0d8007e3302d5c2d77c4d2ed", - "name": "Type IfcGUID", - "units": null, - "value": "1cghwxWkP5hebx$jnaWsFz", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_TYPE_GUID" - }, - "KEYNOTE_PARAM": { - "id": "bcf04d1e0a8e59168a66d0b63eeb1ab8", - "name": "Keynote", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "KEYNOTE_PARAM" - }, - "applicationId": null, - "OMNICLASS_CODE": { - "id": "b75af5017b17f1a11641d8f261601929", - "name": "OmniClass Number", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "OMNICLASS_CODE" - }, - "UNIFORMAT_CODE": { - "id": "8ed423285d1d5d7118b7a43e40b32883", - "name": "Assembly Code", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "UNIFORMAT_CODE" - }, - "WINDOW_TYPE_ID": { - "id": "6b8a0975069f64a97adb326251fb057a", - "name": "Type Mark", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "WINDOW_TYPE_ID" - }, - "ALL_MODEL_MODEL": { - "id": "934bca477ca5e150978b7babe1483839", - "name": "Model", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_MODEL" - }, - "DESIGN_OPTION_ID": { - "id": "226f48801e3084d3491eb079e8dd0af8", - "name": "Design Option", - "units": null, - "value": "-1", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "DESIGN_OPTION_ID" - }, - "totalChildrenCount": 0, - "ALL_MODEL_TYPE_NAME": { - "id": "3ce74d6037cfbfc5450f8d3ea2fb10c1", - "name": "Type Name", - "units": null, - "value": "Floor Opening", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_TYPE_NAME" - }, - "ELEM_CATEGORY_PARAM": { - "id": "b1907dd0236c7091c80d6da690100dc9", - "name": "Category", - "units": null, - "value": "-2000151", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ELEM_CATEGORY_PARAM" - }, - "ALL_MODEL_TYPE_IMAGE": { - "id": "e6417f0a96a3c38d9a622ab5494e8612", - "name": "Type Image", - "units": null, - "value": "-1", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_TYPE_IMAGE" - }, - "ALL_MODEL_DESCRIPTION": { - "id": "dc455c0fef6a720aff6c536385b6c433", - "name": "Description", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_DESCRIPTION" - }, - "ALL_MODEL_FAMILY_NAME": { - "id": "74e464c15502e62ccd3e6dd90cc221e2", - "name": "Family Name", - "units": null, - "value": "Floor Opening", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_FAMILY_NAME" - }, - "OMNICLASS_DESCRIPTION": { - "id": "1ff48b1d6e07459451e87d61fb809f18", - "name": "OmniClass Title", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "OMNICLASS_DESCRIPTION" - }, - "UNIFORMAT_DESCRIPTION": { - "id": "026207f8c83f70e3dccb1c9d4f0a195c", - "name": "Assembly Description", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "UNIFORMAT_DESCRIPTION" - }, - "ALL_MODEL_MANUFACTURER": { - "id": "03e476c9ecb3a8c39c42a4a99cd15782", - "name": "Manufacturer", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_MANUFACTURER" - }, - "ELEM_CATEGORY_PARAM_MT": { - "id": "a9a91cfa6bc04ca8a0f097d69beb2dda", - "name": "Category", - "units": null, - "value": "-2000151", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ELEM_CATEGORY_PARAM_MT" - }, - "ALL_MODEL_TYPE_COMMENTS": { - "id": "389eba7a64948e921f41b0f13cab0ff8", - "name": "Type Comments", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_TYPE_COMMENTS" - }, - "IFC_EXPORT_ELEMENT_TYPE": { - "id": "0c72bf98d9abf2f9db170f5045bf26e9", - "name": "Export Type to IFC", - "units": null, - "value": 0, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_EXPORT_ELEMENT_TYPE" - }, - "IFC_EXPORT_ELEMENT_TYPE_AS": { - "id": "cf49cd58a60d03878cf4c7475e053f7a", - "name": "Export Type to IFC As", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_EXPORT_ELEMENT_TYPE_AS" - }, - "STRUCTURAL_FAMILY_CODE_NAME": { - "id": "d9e23cc21311d6878acf9df17a045f8c", - "name": "Code Name", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "STRUCTURAL_FAMILY_CODE_NAME" - }, - "IFC_EXPORT_PREDEFINEDTYPE_TYPE": { - "id": "b7398ab9ec911545bcf3ff348222774a", - "name": "Type IFC Predefined Type", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": true, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_EXPORT_PREDEFINEDTYPE_TYPE" - }, - "FAMILY_FREEINST_DEFAULT_ELEVATION": { - "id": "43095255b110c23ab675e11b971bdb0c", - "name": "Default Elevation", - "units": "cm", - "value": 0, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:centimeters-1.0.1", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "FAMILY_FREEINST_DEFAULT_ELEVATION" - } - }, - "displayValue": [], - "speckle_type": "Objects.BuiltElements.Revit.RevitElementType:Objects.BuiltElements.Revit.RevitSymbolElementType", - "applicationId": "66aabebb-82e6-45ae-897b-fedc64863bb7-0005584a", - "placementType": "OneLevelBasedHosted", - "builtInCategory": "OST_GenericModel", - "isRevitLinkedModel": false, - "materialQuantities": [], - "totalChildrenCount": 0, - "revitLinkedModelPath": "C:\\Users\\conno\\Downloads\\objects_R23.rvt" - }, - { - "id": "e34b9581f041267f18092ba75dde35fb", - "data": [ - 6349.65567993164, - -1699.9149600219728, - -0.00000000000017596590851098882, - 6349.65567993164, - -1199.9148980712891, - 0.0000000000001353583911622991, - 5849.655501708984, - -1199.9148980712891, - 100.00000076293945, - 5849.655501708984, - -1699.9149600219728, - 100.00000076293945, - 6074.40457397461, - -1537.387787475586, - 55.050219011306766, - 6074.40457397461, - -1387.3877572631836, - 55.050219011306766, - 6157.563218994141, - -1387.3877572631836, - 38.41847620010376, - 6157.563218994141, - -1537.387787475586, - 38.41847620010376 - ], - "speckle_type": "Speckle.Core.Models.DataChunk", - "applicationId": null, - "totalChildrenCount": 0 - }, - { - "id": "e56082f6410025dec8f3cc658a77e4d4", - "data": [ - 3, - 1, - 6, - 0, - 3, - 6, - 1, - 5, - 3, - 0, - 7, - 4, - 3, - 7, - 0, - 6, - 3, - 5, - 2, - 3, - 3, - 2, - 5, - 1, - 3, - 4, - 3, - 0, - 3, - 3, - 4, - 5 - ], - "speckle_type": "Speckle.Core.Models.DataChunk", - "applicationId": null, - "totalChildrenCount": 0 - }, - { - "id": "e85bd8c0246c15998785930e21b9f291", - "data": [ - 6349.65567993164, - -1199.9148980712891, - -36.91689997673035, - 6349.65567993164, - -1699.9149600219728, - -36.91689997673035, - 5849.655501708984, - -1699.9149600219728, - 63.08309715270996, - 5849.655501708984, - -1199.9148980712891, - 63.08309715270996, - 6070.820141601563, - -1537.387787475586, - 18.85021749973297, - 6070.820141601563, - -1387.3877572631836, - 18.85021749973297, - 6007.563072509765, - -1387.3877572631836, - 31.50157645225525, - 6007.563072509765, - -1537.387787475586, - 31.50157645225525, - 6349.65567993164, - -1199.9148980712891, - -36.91689997673035, - 6349.65567993164, - -1199.9148980712891, - 0.0000000000001353583911622991, - 6349.65567993164, - -1699.9149600219728, - -0.00000000000017596590851098882, - 6349.65567993164, - -1699.9149600219728, - -36.91689997673035, - 6349.65567993164, - -1699.9149600219728, - -36.91689997673035, - 6349.65567993164, - -1699.9149600219728, - -0.00000000000017596590851098882, - 5849.655501708984, - -1699.9149600219728, - 100.00000076293945, - 5849.655501708984, - -1699.9149600219728, - 63.08309715270996, - 5849.655501708984, - -1699.9149600219728, - 63.08309715270996, - 5849.655501708984, - -1699.9149600219728, - 100.00000076293945, - 5849.655501708984, - -1199.9148980712891, - 100.00000076293945, - 5849.655501708984, - -1199.9148980712891, - 63.08309715270996, - 5849.655501708984, - -1199.9148980712891, - 63.08309715270996, - 5849.655501708984, - -1199.9148980712891, - 100.00000076293945, - 6349.65567993164, - -1199.9148980712891, - 0.0000000000001353583911622991, - 6349.65567993164, - -1199.9148980712891, - -36.91689997673035, - 6157.563218994141, - -1387.3877572631836, - 18.85021749973297, - 6070.820141601563, - -1387.3877572631836, - 18.85021749973297, - 6070.820141601563, - -1537.387787475586, - 18.85021749973297, - 6157.563218994141, - -1537.387787475586, - 18.85021749973297, - 6007.563072509765, - -1387.3877572631836, - 55.050219011306766, - 6074.40457397461, - -1387.3877572631836, - 55.050219011306766, - 6074.40457397461, - -1537.387787475586, - 55.050219011306766, - 6007.563072509765, - -1537.387787475586, - 55.050219011306766, - 6070.820141601563, - -1387.3877572631836, - 18.85021749973297, - 6157.563218994141, - -1387.3877572631836, - 18.85021749973297, - 6157.563218994141, - -1387.3877572631836, - 38.41847620010376, - 6074.40457397461, - -1387.3877572631836, - 55.050219011306766, - 6007.563072509765, - -1387.3877572631836, - 55.050219011306766, - 6007.563072509765, - -1387.3877572631836, - 31.50157645225525, - 6007.563072509765, - -1537.387787475586, - 55.050219011306766, - 6007.563072509765, - -1537.387787475586, - 31.50157645225525, - 6007.563072509765, - -1387.3877572631836, - 31.50157645225525, - 6007.563072509765, - -1387.3877572631836, - 55.050219011306766, - 6157.563218994141, - -1537.387787475586, - 18.85021749973297, - 6070.820141601563, - -1537.387787475586, - 18.85021749973297, - 6007.563072509765, - -1537.387787475586, - 31.50157645225525, - 6007.563072509765, - -1537.387787475586, - 55.050219011306766, - 6074.40457397461, - -1537.387787475586, - 55.050219011306766, - 6157.563218994141, - -1537.387787475586, - 38.41847620010376, - 6157.563218994141, - -1537.387787475586, - 18.85021749973297, - 6157.563218994141, - -1537.387787475586, - 38.41847620010376, - 6157.563218994141, - -1387.3877572631836, - 38.41847620010376, - 6157.563218994141, - -1387.3877572631836, - 18.85021749973297 - ], - "speckle_type": "Speckle.Core.Models.DataChunk", - "applicationId": null, - "totalChildrenCount": 0 - }, - { - "id": "f4ff3dd4a33c3711164fcae43a2e1376", - "name": "Site - Hardcore", - "units": "cm", - "category": "Materials", - "elementId": "5121", - "shininess": 0, - "worksetId": "0", - "parameters": { - "id": "091981a976a6d8fadb5590cc663c37b6", - "IFC_GUID": { - "id": "2f7d81a271371a035b5831450f8df07a", - "name": "IfcGUID", - "units": null, - "value": "3O98Cq8xiHrPCo0026FpEi", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_GUID" - }, - "DOOR_COST": { - "id": "70a90fcdc412ffb329242075e203a4d2", - "name": "Cost", - "units": "฿", - "value": 0, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": "autodesk.unit.unit:currency-1.0.0", - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "DOOR_COST" - }, - "speckle_type": "Base", - "ALL_MODEL_URL": { - "id": "6bc4103dfaced7e000e0cf70a83947f9", - "name": "URL", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_URL" - }, - "KEYNOTE_PARAM": { - "id": "464e3bdcb7e701e6268d75b04aa3e4c8", - "name": "Keynote", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "KEYNOTE_PARAM" - }, - "MATERIAL_NAME": { - "id": "39bb393e4e62b9550033932a3a1ca949", - "name": "Name", - "units": null, - "value": "Site - Hardcore", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "MATERIAL_NAME" - }, - "applicationId": null, - "ALL_MODEL_MARK": { - "id": "bbee379d7cce40dbf051ca9ff1c082d4", - "name": "Mark", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_MARK" - }, - "ALL_MODEL_IMAGE": { - "id": "061343fdcc12e983dfae8136fd33ab7e", - "name": "Image", - "units": null, - "value": "-1", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_IMAGE" - }, - "ALL_MODEL_MODEL": { - "id": "71e1f3c0a017f967bfa10cb9bbba8339", - "name": "Model", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_MODEL" - }, - "DESIGN_OPTION_ID": { - "id": "226f48801e3084d3491eb079e8dd0af8", - "name": "Design Option", - "units": null, - "value": "-1", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "DESIGN_OPTION_ID" - }, - "IFC_EXPORT_ELEMENT": { - "id": "9433ec034c7dc84e9769c9f9ba332bb5", - "name": "Export to IFC", - "units": null, - "value": 0, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_EXPORT_ELEMENT" - }, - "totalChildrenCount": 0, - "ALL_MODEL_TYPE_NAME": { - "id": "82132d22cc87843fba383206476c52a6", - "name": "Type Name", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_TYPE_NAME" - }, - "ELEM_CATEGORY_PARAM": { - "id": "5c19163426b2c299f7163af68a41a3c9", - "name": "Category", - "units": null, - "value": "-2000700", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ELEM_CATEGORY_PARAM" - }, - "MATERIAL_PARAM_GLOW": { - "id": "f7948f94bb4cd4874fcfac385b99710a", - "name": "Glow", - "units": null, - "value": false, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "MATERIAL_PARAM_GLOW" - }, - "MATERIAL_PARAM_COLOR": { - "id": "f49b83da3a43b7d4b6e076cee58a1dc4", - "name": "Color", - "units": null, - "value": 2763429, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "MATERIAL_PARAM_COLOR" - }, - "ALL_MODEL_DESCRIPTION": { - "id": "668571a7858e6ce18616937000a787d4", - "name": "Description", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_DESCRIPTION" - }, - "ALL_MODEL_FAMILY_NAME": { - "id": "7ef14a150734e2bbefb591fab775d23e", - "name": "Family Name", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_FAMILY_NAME" - }, - "IFC_EXPORT_ELEMENT_AS": { - "id": "32da53c27657cacc0bd4d9bf2340705e", - "name": "Export to IFC As", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_EXPORT_ELEMENT_AS" - }, - "ALL_MODEL_MANUFACTURER": { - "id": "4a3f5e11988e1dc8bc3a460da541f759", - "name": "Manufacturer", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_MANUFACTURER" - }, - "ELEM_CATEGORY_PARAM_MT": { - "id": "f19ce7e9ee16a8b4b1af534dc221364d", - "name": "Category", - "units": null, - "value": "-2000700", - "isShared": false, - "isReadOnly": true, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ELEM_CATEGORY_PARAM_MT" - }, - "MATERIAL_PARAM_SHININESS": { - "id": "88d27eb9a813394c1550ec14075d7d2d", - "name": "Shininess", - "units": null, - "value": 0, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "MATERIAL_PARAM_SHININESS" - }, - "IFC_EXPORT_PREDEFINEDTYPE": { - "id": "1bbe93dc48596577683307830377663e", - "name": "IFC Predefined Type", - "units": null, - "value": null, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "IFC_EXPORT_PREDEFINEDTYPE" - }, - "MATERIAL_PARAM_SMOOTHNESS": { - "id": "0f7233f2095dee428f15e249c08cd15b", - "name": "Smoothness", - "units": null, - "value": 0, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "MATERIAL_PARAM_SMOOTHNESS" - }, - "ALL_MODEL_INSTANCE_COMMENTS": { - "id": "77da8d6ddd91fd48c9a5b29afc99c2e5", - "name": "Comments", - "units": null, - "value": "", - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ALL_MODEL_INSTANCE_COMMENTS" - }, - "MATERIAL_PARAM_TRANSPARENCY": { - "id": "dfc016c6c3c3e7fae046c80082deffa7", - "name": "Transparency", - "units": null, - "value": 0, - "isShared": false, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "MATERIAL_PARAM_TRANSPARENCY" - }, - "1190574f-2634-4c09-9dfc-856fca09d674": { - "id": "9cc7f8905be5567c97374f704c5246ef", - "name": "Malzeme UniFormat", - "units": null, - "value": null, - "isShared": true, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "1190574f-2634-4c09-9dfc-856fca09d674" - }, - "ba4ad29c-d864-4b61-84ba-1b992bebb003": { - "id": "ba170ec00ae31cd20182394c6f55e44a", - "name": "Malzeme Poz No", - "units": null, - "value": null, - "isShared": true, - "isReadOnly": false, - "speckle_type": "Objects.BuiltElements.Revit.Parameter", - "applicationId": null, - "applicationUnit": null, - "isTypeParameter": false, - "totalChildrenCount": 0, - "applicationUnitType": null, - "applicationInternalName": "ba4ad29c-d864-4b61-84ba-1b992bebb003" - } - }, - "smoothness": 0, - "speckle_type": "Objects.Other.Material:Objects.Other.Revit.RevitMaterial", - "transparency": 0, - "applicationId": "d8248334-23bb-11d5-9332-0000863f27ad-00001401", - "materialClass": "Miscellaneous", - "renderMaterial": null, - "builtInCategory": "OST_Materials", - "materialCategory": "Miscellaneous", - "isRevitLinkedModel": false, - "materialQuantities": [], - "totalChildrenCount": 0, - "revitLinkedModelPath": "C:\\Users\\conno\\Downloads\\objects_R23.rvt" - } -] \ No newline at end of file diff --git a/tests/Speckle.Sdk.Serialization.Tests/RevitObject.json.gz b/tests/Speckle.Sdk.Serialization.Tests/RevitObject.json.gz new file mode 100644 index 00000000..1f6789ba Binary files /dev/null and b/tests/Speckle.Sdk.Serialization.Tests/RevitObject.json.gz differ diff --git a/tests/Speckle.Sdk.Serialization.Tests/SerializationTests.cs b/tests/Speckle.Sdk.Serialization.Tests/SerializationTests.cs index 202607c5..64db5cb9 100644 --- a/tests/Speckle.Sdk.Serialization.Tests/SerializationTests.cs +++ b/tests/Speckle.Sdk.Serialization.Tests/SerializationTests.cs @@ -1,79 +1,255 @@ -using System.Reflection; -using NUnit.Framework; -using Shouldly; +using System.Collections.Concurrent; +using FluentAssertions; +using Microsoft.Extensions.Logging.Abstractions; +using Speckle.Newtonsoft.Json; using Speckle.Newtonsoft.Json.Linq; -using Speckle.Objects.BuiltElements; using Speckle.Sdk.Common; using Speckle.Sdk.Host; using Speckle.Sdk.Models; using Speckle.Sdk.Serialisation; +using Speckle.Sdk.Serialisation.Utilities; +using Speckle.Sdk.Serialisation.V2.Receive; +using Speckle.Sdk.Serialisation.V2.Send; +using Speckle.Sdk.Serialization.Tests.Framework; +using Speckle.Sdk.Testing.Framework; namespace Speckle.Sdk.Serialization.Tests; -[TestFixture] -[Description("For certain types, changing property from one type to another should be implicitly backwards compatible")] public class SerializationTests { - private readonly Assembly _assembly = Assembly.GetExecutingAssembly(); - - [SetUp] - public void Setup() + private class TestLoader(string json) : IObjectLoader { - TypeLoader.Reset(); - TypeLoader.Initialize(typeof(Base).Assembly, typeof(Wall).Assembly, _assembly); - } - - private async Task ReadJson(string fullName) - { - await using var stream = _assembly.GetManifestResourceStream(fullName).NotNull(); - using var reader = new StreamReader(stream); - return await reader.ReadToEndAsync(); - } - - private async Task> ReadAsObjects(string fullName) - { - var jsonObjects = new Dictionary(); - var json = await ReadJson(fullName); - var array = JArray.Parse(json); - foreach (var obj in array) + public Task<(Json, IReadOnlyCollection)> GetAndCache(string rootId, DeserializeProcessOptions? options) { - if (obj is JObject jobj) - { - jsonObjects.Add(jobj["id"].NotNull().Value().NotNull(), jobj.ToString()); - } + var childrenIds = ClosureParser.GetChildrenIds(new(json), default).Select(x => new Id(x)).ToList(); + return Task.FromResult<(Json, IReadOnlyCollection)>((new(json), childrenIds)); } - return jsonObjects; + + public string? LoadId(string id) => null; + + public void Dispose() { } } - [Test] - [TestCase("RevitObject.json")] + /* + [Test] + [TestCase("RevitObject.json")] + public async Task RunTest2(string fileName) + { + var fullName = _assembly.GetManifestResourceNames().Single(x => x.EndsWith(fileName)); + var json = await ReadJson(fullName); + var closure = await ReadAsObjects(json); + using DeserializeProcess sut = new(null, new TestLoader(json), new TestTransport(closure)); + var @base = await sut.Deserialize("551513ff4f3596024547fc818f1f3f70"); + @base.Should().NotBeNull(); + }*/ + + public class TestObjectLoader(IReadOnlyDictionary idToObject) : IObjectLoader + { + public Task<(Json, IReadOnlyCollection)> GetAndCache(string rootId, DeserializeProcessOptions? options) + { + var json = idToObject.GetValueOrDefault(rootId); + if (json == null) + { + throw new KeyNotFoundException("Root not found"); + } + + var allChildren = ClosureParser.GetChildrenIds(json, default).Select(x => new Id(x)).ToList(); + return Task.FromResult<(Json, IReadOnlyCollection)>((new(json), allChildren)); + } + + public string? LoadId(string id) => idToObject.GetValueOrDefault(id); + + public void Dispose() { } + } + + [Theory] + [InlineData("RevitObject.json.gz")] public async Task Basic_Namespace_Validation(string fileName) { - var fullName = _assembly.GetManifestResourceNames().Single(x => x.EndsWith(fileName)); - var closure = await ReadAsObjects(fullName); - var deserializer = new BaseObjectDeserializerV2 + var closures = await TestFileManager.GetFileAsClosures(fileName); + var deserializer = new SpeckleObjectDeserializer { - ReadTransport = new TestTransport(closure), - CancellationToken = default + ReadTransport = new TestTransport(closures), + CancellationToken = default, }; - foreach (var (id, objJson) in closure) + + foreach (var (id, objJson) in closures) { var jObject = JObject.Parse(objJson); var oldSpeckleType = jObject["speckle_type"].NotNull().Value().NotNull(); var starts = oldSpeckleType.StartsWith("Speckle.Core.") || oldSpeckleType.StartsWith("Objects."); - starts.ShouldBeTrue($"{oldSpeckleType} isn't expected"); + starts.Should().BeTrue($"{oldSpeckleType} isn't expected"); - var baseType = deserializer.Deserialize(objJson); - id.ShouldBe(baseType.id); + var baseType = await deserializer.DeserializeAsync(objJson); + baseType.id.Should().Be(id); - starts = baseType.speckle_type.StartsWith("Speckle.Core.") || baseType.speckle_type.StartsWith("Objects."); - starts.ShouldBeTrue($"{baseType.speckle_type} isn't expected"); + var oldType = TypeLoader.GetAtomicType(oldSpeckleType); + if (oldType == typeof(Base)) + { + oldSpeckleType.Should().NotContain("Base"); + } + else + { + starts = baseType.speckle_type.StartsWith("Speckle.Core.") || baseType.speckle_type.StartsWith("Objects."); + starts.Should().BeTrue($"{baseType.speckle_type} isn't expected"); - var type = TypeLoader.GetAtomicType(baseType.speckle_type); - type.ShouldNotBeNull(); - var name = TypeLoader.GetTypeString(type) ?? throw new ArgumentNullException(); - starts = name.StartsWith("Speckle.Core") || name.StartsWith("Objects"); - starts.ShouldBeTrue($"{name} isn't expected"); + var type = TypeLoader.GetAtomicType(baseType.speckle_type); + type.Should().NotBeNull(); + var name = TypeLoader.GetTypeString(type) ?? throw new ArgumentNullException($"Could not find: {type}"); + starts = name.StartsWith("Speckle.Core") || name.StartsWith("Objects"); + starts.Should().BeTrue($"{name} isn't expected"); + } + } + } + + [Theory] + [InlineData("RevitObject.json.gz")] + public async Task Basic_Namespace_Validation_New(string fileName) + { + var closures = await TestFileManager.GetFileAsClosures(fileName); + await using var process = new DeserializeProcess( + new TestObjectLoader(closures), + null, + new BaseDeserializer(new ObjectDeserializerFactory()), + new NullLoggerFactory(), + default + ); + await process.Deserialize("3416d3fe01c9196115514c4a2f41617b"); + foreach (var (id, objJson) in closures) + { + var jObject = JObject.Parse(objJson); + var oldSpeckleType = jObject["speckle_type"].NotNull().Value().NotNull(); + var starts = oldSpeckleType.StartsWith("Speckle.Core.") || oldSpeckleType.StartsWith("Objects."); + starts.Should().BeTrue($"{oldSpeckleType} isn't expected"); + + var oldType = TypeLoader.GetAtomicType(oldSpeckleType); + if (oldType == typeof(Base)) + { + oldSpeckleType.Should().NotContain("Base"); + } + else + { + var baseType = process.BaseCache[new Id(id)]; + + starts = baseType.speckle_type.StartsWith("Speckle.Core.") || baseType.speckle_type.StartsWith("Objects."); + starts.Should().BeTrue($"{baseType.speckle_type} isn't expected"); + + var type = TypeLoader.GetAtomicType(baseType.speckle_type); + type.Should().NotBeNull(); + var name = TypeLoader.GetTypeString(type) ?? throw new ArgumentNullException(); + starts = name.StartsWith("Speckle.Core") || name.StartsWith("Objects"); + starts.Should().BeTrue($"{name} isn't expected"); + } + } + } + + [Theory] + [InlineData( + "{\"applicationId\":null,\"speckle_type\":\"Base\",\"IFC_GUID\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcGUID\",\"units\":null,\"value\":\"18HX_ys0P5uu77f1wwA7bn\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"IFC_GUID\",\"id\":\"1f4e29b7198e25221300c684876ec187\"},\"DOOR_COST\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Cost\",\"units\":\"\u0e3f\",\"value\":null,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:currency-1.0.0\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"DOOR_COST\",\"id\":\"80ff4c5df5170b75916a873a394cfbdf\"},\"ALL_MODEL_URL\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"URL\",\"units\":null,\"value\":null,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ALL_MODEL_URL\",\"id\":\"140c53fcea5deaa35115b23cd2ba48c6\"},\"IFC_TYPE_GUID\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type IfcGUID\",\"units\":null,\"value\":\"0w69BRwHvBsBXN3bEBjQin\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"IFC_TYPE_GUID\",\"id\":\"99d5d914df5c50c879e73c50246a9249\"},\"KEYNOTE_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Keynote\",\"units\":null,\"value\":\"S0905\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"KEYNOTE_PARAM\",\"id\":\"c2272311800b04ab4d2b0052df68ecdc\"},\"PHASE_CREATED\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Phase Created\",\"units\":null,\"value\":\"0\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"PHASE_CREATED\",\"id\":\"72ecbbd5d29ea1b48df89d8f88b29120\"},\"ALL_MODEL_MARK\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Mark\",\"units\":null,\"value\":null,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ALL_MODEL_MARK\",\"id\":\"f2e0ed6ebfbab4d4780c5143b774558e\"},\"FUNCTION_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Function\",\"units\":null,\"value\":0,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"FUNCTION_PARAM\",\"id\":\"a43000484f3fa3c5cf60a2ccd79a573c\"},\"UNIFORMAT_CODE\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Assembly Code\",\"units\":null,\"value\":\"\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"UNIFORMAT_CODE\",\"id\":\"b797bb20d49af57eecbe718df4ebd411\"},\"WINDOW_TYPE_ID\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type Mark\",\"units\":null,\"value\":null,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"WINDOW_TYPE_ID\",\"id\":\"d74f026a13a539bd24369ea78b34aa6b\"},\"ALL_MODEL_IMAGE\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Image\",\"units\":null,\"value\":\"-1\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ALL_MODEL_IMAGE\",\"id\":\"4ef25c5fcd2ee32d9b3d6ce9b1047904\"},\"ALL_MODEL_MODEL\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Model\",\"units\":null,\"value\":null,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ALL_MODEL_MODEL\",\"id\":\"13597261389f532c0778e134623eff85\"},\"CLEAR_COVER_TOP\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Rebar Cover - Top Face\",\"units\":null,\"value\":\"95743\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"CLEAR_COVER_TOP\",\"id\":\"ed5f5e056314ee8435a1658a54261e94\"},\"ELEM_TYPE_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type\",\"units\":null,\"value\":\"5432827\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ELEM_TYPE_PARAM\",\"id\":\"e502cb5aed1fda357926c7ca9927c42c\"},\"RELATED_TO_MASS\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Related to Mass\",\"units\":null,\"value\":false,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"RELATED_TO_MASS\",\"id\":\"a43b8424564b8f14738f4dbaa78be150\"},\"SYMBOL_ID_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type Id\",\"units\":null,\"value\":\"5432827\",\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"SYMBOL_ID_PARAM\",\"id\":\"1947cb98e61f79da57f573a3a785b436\"},\"DESIGN_OPTION_ID\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Design Option\",\"units\":null,\"value\":\"-1\",\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"DESIGN_OPTION_ID\",\"id\":\"cf30f731c41543dd134a4877fbdab105\"},\"PHASE_DEMOLISHED\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Phase Demolished\",\"units\":null,\"value\":\"-1\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"PHASE_DEMOLISHED\",\"id\":\"47066bac7728f9b93f4acdb697284a59\"},\"CLEAR_COVER_OTHER\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Rebar Cover - Other Faces\",\"units\":null,\"value\":\"95743\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"CLEAR_COVER_OTHER\",\"id\":\"1afe7d22a897aff809bd92aea1acafd2\"},\"ELEM_FAMILY_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Family\",\"units\":null,\"value\":\"5432827\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ELEM_FAMILY_PARAM\",\"id\":\"ec3a159572a58b85b3a3650e5cc23e90\"},\"CLEAR_COVER_BOTTOM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Rebar Cover - Bottom Face\",\"units\":null,\"value\":\"95743\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"CLEAR_COVER_BOTTOM\",\"id\":\"476161776fc4c6ceb3c544c792a08120\"},\"HOST_AREA_COMPUTED\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Area\",\"units\":\"m\u00b2\",\"value\":7.128858225722908,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":\"autodesk.unit.unit:squareMeters-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"HOST_AREA_COMPUTED\",\"id\":\"5e7567fea07a98c0cdd4903cabd897a3\"},\"IFC_EXPORT_ELEMENT\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Export to IFC\",\"units\":null,\"value\":0,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"IFC_EXPORT_ELEMENT\",\"id\":\"7de623e201f3fcfb16dbcacefe7f8403\"},\"totalChildrenCount\":0,\"ALL_MODEL_TYPE_NAME\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type Name\",\"units\":null,\"value\":null,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ALL_MODEL_TYPE_NAME\",\"id\":\"4699f3fc2fd4e84cc3b6296ded7225b5\"},\"DESIGN_OPTION_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Design Option\",\"units\":null,\"value\":\"Main Model\",\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"DESIGN_OPTION_PARAM\",\"id\":\"771449eae7f2fb96345b165954b2c797\"},\"ELEM_CATEGORY_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Category\",\"units\":null,\"value\":\"-2000032\",\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ELEM_CATEGORY_PARAM\",\"id\":\"fb0b948f7360b9415ea9ede20fb3cdd2\"},\"ALL_MODEL_TYPE_IMAGE\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type Image\",\"units\":null,\"value\":\"-1\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ALL_MODEL_TYPE_IMAGE\",\"id\":\"4c95be61c11f5609f1fa649804bf9814\"},\"ANALYTICAL_ROUGHNESS\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Roughness\",\"units\":null,\"value\":1,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ANALYTICAL_ROUGHNESS\",\"id\":\"eab64895ad089cf272ae6a7431f4cdac\"},\"HOST_VOLUME_COMPUTED\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Volume\",\"units\":\"m\u00b3\",\"value\":1.413211687704679,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":\"autodesk.unit.unit:cubicMeters-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"HOST_VOLUME_COMPUTED\",\"id\":\"4cac83d2757bc70d7e1f299de124d028\"},\"SCHEDULE_LEVEL_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Level\",\"units\":null,\"value\":\"1100600\",\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"SCHEDULE_LEVEL_PARAM\",\"id\":\"d0ab715757ddbaedf5dc2df0726ed38c\"},\"ALL_MODEL_DESCRIPTION\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Description\",\"units\":null,\"value\":null,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ALL_MODEL_DESCRIPTION\",\"id\":\"4abdaadbe23c12c349c65abcd5979f56\"},\"IFC_EXPORT_ELEMENT_AS\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Export to IFC As\",\"units\":null,\"value\":null,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"IFC_EXPORT_ELEMENT_AS\",\"id\":\"ccacac43b32ffd10c88a870492f98f96\"},\"UNIFORMAT_DESCRIPTION\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Assembly Description\",\"units\":null,\"value\":\"\",\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"UNIFORMAT_DESCRIPTION\",\"id\":\"abfe7173561e8b86cae8aa8dc34743d1\"},\"ALL_MODEL_MANUFACTURER\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Manufacturer\",\"units\":null,\"value\":null,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ALL_MODEL_MANUFACTURER\",\"id\":\"e280ae740be9133f1001f218a137bb2f\"},\"ANALYTICAL_ABSORPTANCE\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Absorptance\",\"units\":null,\"value\":0.1,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ANALYTICAL_ABSORPTANCE\",\"id\":\"e1618d04224fb3e11e650f8854e5eddb\"},\"ELEM_CATEGORY_PARAM_MT\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Category\",\"units\":null,\"value\":\"-2000032\",\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ELEM_CATEGORY_PARAM_MT\",\"id\":\"d4119e43880a3cc8632a137d4f3372ae\"},\"ALL_MODEL_TYPE_COMMENTS\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type Comments\",\"units\":null,\"value\":null,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ALL_MODEL_TYPE_COMMENTS\",\"id\":\"8ea15d6198e1f5c632df36270be5433e\"},\"ANALYTICAL_THERMAL_MASS\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Thermal Mass\",\"units\":\"kJ/(m\u00b2·K)\",\"value\":null,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":\"autodesk.unit.unit:kilojoulesPerSquareMeterKelvin-1.0.0\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ANALYTICAL_THERMAL_MASS\",\"id\":\"d8b711b81d9e0ad7f072b60b69bd0239\"},\"HOST_PERIMETER_COMPUTED\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Perimeter\",\"units\":\"mm\",\"value\":11098.801755409942,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":\"autodesk.unit.unit:millimeters-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"HOST_PERIMETER_COMPUTED\",\"id\":\"eb73365794668bf73b3ffd2c80162ee1\"},\"IFC_EXPORT_ELEMENT_TYPE\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Export Type to IFC\",\"units\":null,\"value\":0,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"IFC_EXPORT_ELEMENT_TYPE\",\"id\":\"0a96867bd313e951c229fb92b346b516\"},\"WALL_ATTR_ROOM_BOUNDING\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Room Bounding\",\"units\":null,\"value\":true,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"WALL_ATTR_ROOM_BOUNDING\",\"id\":\"fdf5bd19ac0a9f2878323c71e4ae80ea\"},\"FLOOR_STRUCTURE_ID_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Structure\",\"units\":null,\"value\":null,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"FLOOR_STRUCTURE_ID_PARAM\",\"id\":\"43b858d8cfaf2bd27cb0b466dc6d425b\"},\"SYMBOL_FAMILY_NAME_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Family Name\",\"units\":null,\"value\":null,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"SYMBOL_FAMILY_NAME_PARAM\",\"id\":\"d96f492f43f2b0e11ce86d66c23caf0f\"},\"IFC_EXPORT_PREDEFINEDTYPE\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IFC Predefined Type\",\"units\":null,\"value\":null,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"IFC_EXPORT_PREDEFINEDTYPE\",\"id\":\"b927074616bc0e6e323b52a99867b907\"},\"STRUCTURAL_MATERIAL_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Structural Material\",\"units\":null,\"value\":\"215194\",\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"STRUCTURAL_MATERIAL_PARAM\",\"id\":\"1f7ffc00602d1944892885d68dff8867\"},\"ELEM_FAMILY_AND_TYPE_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Family and Type\",\"units\":null,\"value\":\"5432827\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ELEM_FAMILY_AND_TYPE_PARAM\",\"id\":\"9d58f36db73c0c248a6db682a6c6a6a0\"},\"FLOOR_ATTR_THICKNESS_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Thickness\",\"units\":\"mm\",\"value\":200,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":\"autodesk.unit.unit:millimeters-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"FLOOR_ATTR_THICKNESS_PARAM\",\"id\":\"22c96d409372e700936805b825b574e6\"},\"IFC_EXPORT_ELEMENT_TYPE_AS\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Export Type to IFC As\",\"units\":null,\"value\":null,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"IFC_EXPORT_ELEMENT_TYPE_AS\",\"id\":\"41a53fec385581b6af53942aff3cd2d3\"},\"ALL_MODEL_INSTANCE_COMMENTS\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Comments\",\"units\":null,\"value\":\"\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ALL_MODEL_INSTANCE_COMMENTS\",\"id\":\"ca8cdcc0b3fd824a34dcb42749151cd1\"},\"STRUCTURAL_ELEVATION_AT_TOP\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Elevation at Top\",\"units\":\"mm\",\"value\":21099.999999999898,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":\"autodesk.unit.unit:millimeters-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"STRUCTURAL_ELEVATION_AT_TOP\",\"id\":\"b1f293a63ff03c0c7456f8ba7b703f4f\"},\"FLOOR_HEIGHTABOVELEVEL_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Height Offset From Level\",\"units\":\"mm\",\"value\":-100,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:millimeters-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"FLOOR_HEIGHTABOVELEVEL_PARAM\",\"id\":\"312773813c84648fc5ff2d78a8d8d8bc\"},\"ANALYTICAL_THERMAL_RESISTANCE\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Thermal Resistance (R)\",\"units\":\"(m\u00b2·K)/W\",\"value\":null,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":\"autodesk.unit.unit:squareMeterKelvinsPerWatt-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ANALYTICAL_THERMAL_RESISTANCE\",\"id\":\"fcfa5d36d656d4f8ca2b883a17c310b8\"},\"IFC_EXPORT_PREDEFINEDTYPE_TYPE\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type IFC Predefined Type\",\"units\":null,\"value\":null,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"IFC_EXPORT_PREDEFINEDTYPE_TYPE\",\"id\":\"ac166cbccbcd8335272956f09d8d5d42\"},\"STRUCTURAL_ELEVATION_AT_BOTTOM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Elevation at Bottom\",\"units\":\"mm\",\"value\":20899.9999999999,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":\"autodesk.unit.unit:millimeters-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"STRUCTURAL_ELEVATION_AT_BOTTOM\",\"id\":\"d9e8f0e4b57b00ca99d13df99ea6ac26\"},\"COARSE_SCALE_FILL_PATTERN_COLOR\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Coarse Scale Fill Color\",\"units\":null,\"value\":0,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"COARSE_SCALE_FILL_PATTERN_COLOR\",\"id\":\"854d889fd71071f3b81d0e06f7f1095c\"},\"STRUCTURAL_FLOOR_CORE_THICKNESS\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Core Thickness\",\"units\":\"mm\",\"value\":199.99999999999784,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":\"autodesk.unit.unit:millimeters-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"STRUCTURAL_FLOOR_CORE_THICKNESS\",\"id\":\"6fb6fb65a394c5c68d5a760289c1129d\"},\"STRUCTURAL_ELEVATION_AT_TOP_CORE\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Elevation at Top Core\",\"units\":\"mm\",\"value\":21099.999999999898,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":\"autodesk.unit.unit:millimeters-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"STRUCTURAL_ELEVATION_AT_TOP_CORE\",\"id\":\"aed8eedfb2527594e14ae4e5f74fb5c1\"},\"ANALYTICAL_ELEMENT_HAS_ASSOCIATION\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Has Association\",\"units\":null,\"value\":true,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ANALYTICAL_ELEMENT_HAS_ASSOCIATION\",\"id\":\"c9a6f771f05ef6072100c59c672dfb77\"},\"COARSE_SCALE_FILL_PATTERN_ID_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Coarse Scale Fill Pattern\",\"units\":null,\"value\":\"-1\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"COARSE_SCALE_FILL_PATTERN_ID_PARAM\",\"id\":\"1d271f4d80ffe772f9f8896971050ccc\"},\"FLOOR_ATTR_DEFAULT_THICKNESS_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Default Thickness\",\"units\":\"mm\",\"value\":200,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":\"autodesk.unit.unit:millimeters-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"FLOOR_ATTR_DEFAULT_THICKNESS_PARAM\",\"id\":\"271b8b7f7e29c45065c1ccaa1095b32e\"},\"STRUCTURAL_ELEVATION_AT_TOP_SURVEY\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Elevation at Top Survey\",\"units\":\"mm\",\"value\":30899.999999999894,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":\"autodesk.unit.unit:millimeters-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"STRUCTURAL_ELEVATION_AT_TOP_SURVEY\",\"id\":\"8f2b9f55e373736263d14002838194b4\"},\"STRUCTURAL_ELEVATION_AT_BOTTOM_CORE\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Elevation at Bottom Core\",\"units\":\"mm\",\"value\":20899.9999999999,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":\"autodesk.unit.unit:millimeters-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"STRUCTURAL_ELEVATION_AT_BOTTOM_CORE\",\"id\":\"7a0b7e496383d08605ccb8c776cedbbf\"},\"02b58af4-afcd-404b-9011-3a25d6816e1b\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"ClassificationCode\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"02b58af4-afcd-404b-9011-3a25d6816e1b\",\"id\":\"141f6b021c701e5b4b4ee430652f7f91\"},\"042673e7-8ac4-413d-a393-e0785fbf8889\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Data 3\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"042673e7-8ac4-413d-a393-e0785fbf8889\",\"id\":\"f415ff5c972f45d7e0090b0849c54677\"},\"07afa150-f11f-40a1-a173-7a77ea32cf96\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Data 6\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"07afa150-f11f-40a1-a173-7a77ea32cf96\",\"id\":\"0e3acdf0b385d32d68caa8753c710849\"},\"07b6cf99-a3d2-4d7a-9ea4-246058cfae1a\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Classification.OmniClass.22.Description\",\"units\":null,\"value\":\"High-Tolerance Concrete Floor Finishing\",\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"07b6cf99-a3d2-4d7a-9ea4-246058cfae1a\",\"id\":\"8957914412a138b6255452dd485a25bd\"},\"082d16bb-7cf9-4968-ac22-b6f6ae068028\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcName\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"082d16bb-7cf9-4968-ac22-b6f6ae068028\",\"id\":\"f313bddfb2c5cf9f825ee6653021b04e\"},\"087f96a5-2dd2-42bb-a170-c22485216c09\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Element Condition\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"087f96a5-2dd2-42bb-a170-c22485216c09\",\"id\":\"8665b9cec7a39bffa030e6b415f78fa9\"},\"098e8d4f-1431-49e8-8ef6-69516cf72354\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"3D Model Element GUID\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"098e8d4f-1431-49e8-8ef6-69516cf72354\",\"id\":\"b81c608ec3bd9aa08ea1a2c5a2cea206\"},\"0a004b99-d4e6-4db6-8c88-9b77da33f012\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcDescription\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"0a004b99-d4e6-4db6-8c88-9b77da33f012\",\"id\":\"79b720b93988fd7840213380399408dd\"},\"0bf3a5d2-06c0-4b6c-9ba1-6985ef40c2b0\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type Data 6\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"0bf3a5d2-06c0-4b6c-9ba1-6985ef40c2b0\",\"id\":\"622eab526502ce2a848c4aa932554f96\"},\"0c273fd8-260b-4f34-996e-921fa14a47fc\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Data 4\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"0c273fd8-260b-4f34-996e-921fa14a47fc\",\"id\":\"f70edec21839dfc410d94659d18d52c2\"},\"11f34dfe-4592-4c86-a455-2f020d9376e8\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"ClassificationCode\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"11f34dfe-4592-4c86-a455-2f020d9376e8\",\"id\":\"cd817060f1920a0194139bf2cdddecd4\"},\"12e4c976-0b76-4735-8664-e882b410ac7e\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon A1\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"12e4c976-0b76-4735-8664-e882b410ac7e\",\"id\":\"0d2df557e73500e0c3d72c527d4c36fe\"},\"1336888e-1fed-4e9e-b74b-794bff5b6046\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"GrossArea(BaseQuantities)\",\"units\":\"m\u00b2\",\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:squareMeters-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"1336888e-1fed-4e9e-b74b-794bff5b6046\",\"id\":\"7e0f5932a6c5ad37872436b3ed0cf07b\"},\"15212817-1c39-4c7f-bae4-436acd0e4598\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcDescription\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"15212817-1c39-4c7f-bae4-436acd0e4598\",\"id\":\"f2bba2dcccf5786df17c279367baab39\"},\"18a3daed-8579-45e2-97a0-412159986104\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Data 8\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"18a3daed-8579-45e2-97a0-412159986104\",\"id\":\"173b9745cf4e7cc4b67748c5a03acf04\"},\"18ab825f-ba4c-4a8f-b509-5ddd7c378267\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Item\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"18ab825f-ba4c-4a8f-b509-5ddd7c378267\",\"id\":\"2c130ba41bf9797a0e7e64315695dd7b\"},\"1948bc31-5a23-482a-b337-4bd1fce08aec\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Renovation Status(AC_Pset_RenovationAndPhasing)\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"1948bc31-5a23-482a-b337-4bd1fce08aec\",\"id\":\"59b7e7b981f77ff4e7b01e7d794234f9\"},\"1d9a0983-608b-4aed-b03f-f27e8e0e677a\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon B3\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"1d9a0983-608b-4aed-b03f-f27e8e0e677a\",\"id\":\"ec604873c2b3c4d53f5ea9c2e203fc70\"},\"219c8c15-4722-4b40-9e19-7fbbddeee30f\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Data 1\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"219c8c15-4722-4b40-9e19-7fbbddeee30f\",\"id\":\"abc83e3214698cefe7bbd9326b536b1d\"},\"226b84c2-b3b5-4a04-93f7-9523a21ef4e0\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Discipline\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"226b84c2-b3b5-4a04-93f7-9523a21ef4e0\",\"id\":\"f0e8a4a841062c6b5517b30002fd2325\"},\"244a8c27-edd6-4b09-8905-4cf403c61235\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Schedule Type Code/Number\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"244a8c27-edd6-4b09-8905-4cf403c61235\",\"id\":\"1ff35615eaa5e568fdb3c7c1bb21b72a\"},\"2cbf5041-2b36-4c7f-b65e-439af251d9f7\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Unique Asset ID - Equipment Type\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"2cbf5041-2b36-4c7f-b65e-439af251d9f7\",\"id\":\"1339498cfd39967f51d7d5a31feba974\"},\"2eac0fd8-0c8a-4c5a-9d54-62415d708f37\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Elemental Code\",\"units\":null,\"value\":\"UFSB\",\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"2eac0fd8-0c8a-4c5a-9d54-62415d708f37\",\"id\":\"e20483f71786c3e27291a3eeaa049b48\"},\"2f1ef0a4-09a2-4e80-ba30-57984a475e1d\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Unique Asset ID\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"2f1ef0a4-09a2-4e80-ba30-57984a475e1d\",\"id\":\"d083dfba92681370be68f19d761a9628\"},\"2fb9b7d9-d0b0-4ce2-bbc0-02464fda354c\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon C1\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"2fb9b7d9-d0b0-4ce2-bbc0-02464fda354c\",\"id\":\"71196d8fbc135c7386841bb439f8aaee\"},\"3490690f-a8be-46d9-9607-47c255e9ee89\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcExportAs [Type]\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"3490690f-a8be-46d9-9607-47c255e9ee89\",\"id\":\"cf2c87ffe9b6babcd2659d619fe3a4b7\"},\"35064971-5814-4b17-b572-49ea1320c516\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Data 7\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"35064971-5814-4b17-b572-49ea1320c516\",\"id\":\"ecf761dcb34a3e098f355f401eec5738\"},\"36d9a077-9301-47a0-b049-4f29e17d51dd\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcExportAs [Type]\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"36d9a077-9301-47a0-b049-4f29e17d51dd\",\"id\":\"f547ba1d204747353092fccb1970b8ee\"},\"392cae56-cd6b-4946-817f-242686e12441\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Zone\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"392cae56-cd6b-4946-817f-242686e12441\",\"id\":\"37152ed7e04c0ea23c442aad0be9a611\"},\"3c3d55ea-8a2f-41c1-97fd-d222d586b0b1\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Omniclass Classification Type Code\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"3c3d55ea-8a2f-41c1-97fd-d222d586b0b1\",\"id\":\"861b129e42aac2de8166ad76ded0781c\"},\"3d134cec-2e95-4d43-bc4b-e552d382f73c\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcPresentationLayer\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"3d134cec-2e95-4d43-bc4b-e552d382f73c\",\"id\":\"030b7e2842cde1ce8a1d8e545d0e068a\"},\"3f146225-bd3e-448b-b180-034b880bd662\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon A5\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"3f146225-bd3e-448b-b180-034b880bd662\",\"id\":\"3d573a80fc3bd747d292d75c7751c523\"},\"3f9a284a-7485-460c-b827-9df8cd50720e\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Classification.OmniClass.21.Description\",\"units\":null,\"value\":\"Insitu Concrete\",\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"3f9a284a-7485-460c-b827-9df8cd50720e\",\"id\":\"632da2ce6a52e51df22a05b776421b49\"},\"40e844db-ba22-4ddc-bc15-1fc47f5b12e7\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcSpatialContainer\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"40e844db-ba22-4ddc-bc15-1fc47f5b12e7\",\"id\":\"a8e9d80b93cd5f965d00aaffad6786e9\"},\"444d97fc-d9b6-4424-943d-37ac498a46c4\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Unique Asset ID - Item Number\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"444d97fc-d9b6-4424-943d-37ac498a46c4\",\"id\":\"d7cf7e867db30b45ac62757a6cc11b1b\"},\"46baf2f0-9232-4c37-aa7c-57e37fd5db17\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Product Type\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"46baf2f0-9232-4c37-aa7c-57e37fd5db17\",\"id\":\"b87714b8187aabde851b900a3755655a\"},\"4803c7b6-ded1-46b1-b5eb-ffe9ddcdc20b\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcSpatialContainer\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"4803c7b6-ded1-46b1-b5eb-ffe9ddcdc20b\",\"id\":\"9d6e8c6691db06081c2332c589ed2a31\"},\"48e76a50-9a4f-47a9-8074-79cc7fce9f14\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcPropertySetList\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"48e76a50-9a4f-47a9-8074-79cc7fce9f14\",\"id\":\"38621e3cf898c0fa404d29b88cf6ea5a\"},\"4ac5fa74-7864-45a3-9d89-1ab998b7731a\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon A3\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"4ac5fa74-7864-45a3-9d89-1ab998b7731a\",\"id\":\"34e84e5eed664e1ca94385e405141ef1\"},\"4c575161-247d-46a5-8ae2-72829f37725f\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"NetArea(BaseQuantities)\",\"units\":\"m\u00b2\",\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:squareMeters-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"4c575161-247d-46a5-8ae2-72829f37725f\",\"id\":\"c5741774b669ce685c5e53a704cc0320\"},\"4d293b70-da1a-4830-80a0-4f63b356ff61\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon A2\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"4d293b70-da1a-4830-80a0-4f63b356ff61\",\"id\":\"e629dd3ed399e1b8328aeaa2e90afae9\"},\"4dabccff-7cc0-42ff-a6db-29a28162d3f3\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type IfcPropertySetList\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"4dabccff-7cc0-42ff-a6db-29a28162d3f3\",\"id\":\"3aa01feade7f65bbd785f7083c04bacf\"},\"4fc2bd83-f0b1-41ba-8663-89e3d7f3e660\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcDescription [Type]\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"4fc2bd83-f0b1-41ba-8663-89e3d7f3e660\",\"id\":\"e7639f4355cebda699431a60345609c4\"},\"50a015d9-917f-4ac5-884e-42f7f36b47b1\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon B5\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"50a015d9-917f-4ac5-884e-42f7f36b47b1\",\"id\":\"9a71039a2e2272f527084f2096f9429b\"},\"51778754-e984-42cf-8a6d-a2226baf316f\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcPresentationLayer\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"51778754-e984-42cf-8a6d-a2226baf316f\",\"id\":\"392005ecea2408b03939ef80fbb34a8f\"},\"5188c780-2bf1-460c-8bbf-043dcb4649eb\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcExportAs\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"5188c780-2bf1-460c-8bbf-043dcb4649eb\",\"id\":\"f38369c4acdfa96b7d45ac6187f8a183\"},\"5402c013-1b09-474f-b399-344a0e55a182\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type Material\",\"units\":null,\"value\":\"PT\",\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"5402c013-1b09-474f-b399-344a0e55a182\",\"id\":\"e8252b753c969222390cf77fc56237ef\"},\"579138d4-c882-45f1-bfd6-5ec6f8189161\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Rate Reo Area\",\"units\":null,\"value\":12,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"579138d4-c882-45f1-bfd6-5ec6f8189161\",\"id\":\"09ca602feb656474737d2cf84e89e26f\"},\"5d8a425f-4cff-44fe-9896-932e8e5639ef\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type Data 5\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"5d8a425f-4cff-44fe-9896-932e8e5639ef\",\"id\":\"805c08afb49a184af8649f63531be0e3\"},\"6248687a-e43d-4380-9f28-b98a14157187\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcExportAs [Type]\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"6248687a-e43d-4380-9f28-b98a14157187\",\"id\":\"c300499e47eea08ec5bfd25acc9abae3\"},\"6cbcfae1-3598-4ddd-a606-41f3788c0362\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Managed Asset YesNo\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"6cbcfae1-3598-4ddd-a606-41f3788c0362\",\"id\":\"b9473955cae7232a6d5ba4a2c7669e7c\"},\"76daee20-cdee-48b9-bf5f-1dc46079927e\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type Data 1\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"76daee20-cdee-48b9-bf5f-1dc46079927e\",\"id\":\"90563b7e8ed0acad2128d08383966907\"},\"7949a6c6-d3e4-45bf-bad0-208f3ba33483\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Unique Asset ID - Discipline Abbreviation\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"7949a6c6-d3e4-45bf-bad0-208f3ba33483\",\"id\":\"e427ecb62bb5a5f69dc56b12dfd4583a\"},\"79dbaeea-7a11-4cb4-a521-bc04d1b7a25b\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcName [Type]\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"79dbaeea-7a11-4cb4-a521-bc04d1b7a25b\",\"id\":\"d8a8a1f5d6315d9748e29bdd293d77a5\"},\"7a4a2609-0307-4c38-9a8c-4ffcd19a2d00\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Location\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"7a4a2609-0307-4c38-9a8c-4ffcd19a2d00\",\"id\":\"a4c9b4d5ce413090459c99efad3f1832\"},\"7d013fce-228f-4f3c-aa01-db40e458cc6e\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type Data 8\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"7d013fce-228f-4f3c-aa01-db40e458cc6e\",\"id\":\"07281437b9e050cd5ade4cca8a96227b\"},\"834ba6cf-7f91-49e5-ad0c-717d52a2507a\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcExportAs\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"834ba6cf-7f91-49e5-ad0c-717d52a2507a\",\"id\":\"7783908b6376a626de1c39d735b6cbfc\"},\"86d20f83-240f-44b9-8b27-6311eab2abcd\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon B6\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"86d20f83-240f-44b9-8b27-6311eab2abcd\",\"id\":\"006791a1d0d5566c3b892202b08943fd\"},\"8a91c179-c4cb-471a-b108-ad540b8267e3\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Global Inherited Properties.Level Number\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"8a91c179-c4cb-471a-b108-ad540b8267e3\",\"id\":\"3d3d901b52bacdd137ac67f702680f3d\"},\"8c59bf6c-99ff-455b-bdfa-aeb7861e522e\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcTag\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"8c59bf6c-99ff-455b-bdfa-aeb7861e522e\",\"id\":\"363ec752016b710969c8b4454fd2d35f\"},\"8f645e8b-7523-4462-a0af-858ffeaf44dc\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon D\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"8f645e8b-7523-4462-a0af-858ffeaf44dc\",\"id\":\"0e36907ad605957b17ba8556547d8ceb\"},\"91b7eb2f-0caf-45b0-a65d-83ce1eaca70e\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcExportAs\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"91b7eb2f-0caf-45b0-a65d-83ce1eaca70e\",\"id\":\"04140c6ff8418bd2c731affd5691de8e\"},\"93b76d01-67c3-4799-a5f3-296f97489bd3\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Calc Room Number\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"93b76d01-67c3-4799-a5f3-296f97489bd3\",\"id\":\"4e270753bda04b81fd1c11bf1da5d852\"},\"9898cedf-179a-42e7-8cdc-c6c4212ec3e8\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcPresentationLayer\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"9898cedf-179a-42e7-8cdc-c6c4212ec3e8\",\"id\":\"7fd3f00069f6f1fcdab2ea0307661061\"},\"9a208060-4948-49b2-a1bf-1ab383705469\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcDescription\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"9a208060-4948-49b2-a1bf-1ab383705469\",\"id\":\"75340685a08f1a15df5c7aebbc41a85a\"},\"9a49a9a9-21c9-4f52-aeab-ae4727be6e1d\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type IfcPropertySetList\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"9a49a9a9-21c9-4f52-aeab-ae4727be6e1d\",\"id\":\"93f8195337085871545af0176f0ba282\"},\"9c2f84e0-2489-4a03-b4c8-eb44bb26fb0a\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Data 2\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"9c2f84e0-2489-4a03-b4c8-eb44bb26fb0a\",\"id\":\"fa24d55d23008ef6be1dbf3e8636c67e\"},\"9dd225d2-722b-4cb1-b972-babca7520f7e\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"System Name\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"9dd225d2-722b-4cb1-b972-babca7520f7e\",\"id\":\"056eec36214c733f8ecc448c27785434\"},\"9f484ce3-e8ae-4c20-b21c-0210b770935c\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Unique Asset ID - Tenancy Identifier\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"9f484ce3-e8ae-4c20-b21c-0210b770935c\",\"id\":\"416b24b324db272078942a4420775ec0\"},\"ANALYTICAL_HEAT_TRANSFER_COEFFICIENT\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Heat Transfer Coefficient (U)\",\"units\":\"W/(m\u00b2·K)\",\"value\":null,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":\"autodesk.unit.unit:wattsPerSquareMeterKelvin-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ANALYTICAL_HEAT_TRANSFER_COEFFICIENT\",\"id\":\"8d290ae8d6ec3896b8dda96a83bb2d12\"},\"a5cb3364-d1f0-4ea5-a2d2-44114efbcf65\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcName\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"a5cb3364-d1f0-4ea5-a2d2-44114efbcf65\",\"id\":\"23ac4c2000599c233e5c390330e9108e\"},\"a77ddcdc-4c89-42c3-9d28-bc9e476c0fbe\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcName [Type]\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"a77ddcdc-4c89-42c3-9d28-bc9e476c0fbe\",\"id\":\"c60915bcbf4e2e070ccabc17694ee836\"},\"aa967357-e0b5-49f3-95a0-085e5d7d8951\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcMaterial\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"aa967357-e0b5-49f3-95a0-085e5d7d8951\",\"id\":\"08650ab84cd1be1235a60fccfcb1f39e\"},\"ac9b78b9-e138-483b-9796-6214cf7a5bd8\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Documentation.Home Story Name\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ac9b78b9-e138-483b-9796-6214cf7a5bd8\",\"id\":\"759d341196e21570a2f2d6199b5a9f2f\"},\"aeb679c1-1b82-4476-9099-7d13fd8ae3b8\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type IfcPropertySetList\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"aeb679c1-1b82-4476-9099-7d13fd8ae3b8\",\"id\":\"0a235c1836cd2ce0c5fd3869b938946e\"},\"af8efc07-fec4-4419-8513-4a268c4141c8\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon C3\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"af8efc07-fec4-4419-8513-4a268c4141c8\",\"id\":\"181c0245de21c9820a83396914565762\"},\"afb0a36f-1fa3-4f07-9b05-f86f48b3c3f0\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon C2\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"afb0a36f-1fa3-4f07-9b05-f86f48b3c3f0\",\"id\":\"cd929dfb8d143639a136ff3716800dfc\"},\"b13fb213-450c-4f92-859a-05cd5779daf1\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type Data 7\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"b13fb213-450c-4f92-859a-05cd5779daf1\",\"id\":\"1972d1f9e161892f2ba47ccb04f5e784\"},\"b41f116c-c6f7-418e-bf4b-61cc815f8d99\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Level Number(Global Inherited Properties)\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"b41f116c-c6f7-418e-bf4b-61cc815f8d99\",\"id\":\"09caeb5d7565c779852e7802437af4a2\"},\"b594497d-7b5e-4221-aa1d-063f073aa326\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type Data 2\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"b594497d-7b5e-4221-aa1d-063f073aa326\",\"id\":\"ee718148bffebbcfb7e50f5f2be7f91f\"},\"b753aced-e142-45f9-9bb6-d7edce1df108\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Calc Location\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"b753aced-e142-45f9-9bb6-d7edce1df108\",\"id\":\"ab32fc38096b2f538301b596be3ab123\"},\"b90ec63a-c51f-400a-bff1-66b8d0765f47\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Rate Reo Volume\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"b90ec63a-c51f-400a-bff1-66b8d0765f47\",\"id\":\"5642967c62f928019c93b1bbc0e81c26\"},\"b958fd3c-5ea1-43a2-bc5c-df212ed8cf33\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcDescription [Type]\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"b958fd3c-5ea1-43a2-bc5c-df212ed8cf33\",\"id\":\"ea40993e42d6379ab80b5b7d526347c2\"},\"b9d05d9c-a5f5-4a92-8811-9c5e2eefabd8\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"ClassificationCode\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"b9d05d9c-a5f5-4a92-8811-9c5e2eefabd8\",\"id\":\"c93f07ca1b6c3ac1c114474dc9aeeaca\"},\"bf3519b8-7b28-497e-97d8-afd4bf76203b\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcDescription [Type]\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"bf3519b8-7b28-497e-97d8-afd4bf76203b\",\"id\":\"3c46724925e301e2f88d4ecf4b6150f9\"},\"bfb5b2c0-aa1f-47ae-9cc9-70f7feaef0ea\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcName [Type]\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"bfb5b2c0-aa1f-47ae-9cc9-70f7feaef0ea\",\"id\":\"e8a6f622ac2589ad9fadd82e76c4c10c\"},\"bfd7311c-35b9-447d-9683-8ce244f8c1ad\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type Remarks\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"bfd7311c-35b9-447d-9683-8ce244f8c1ad\",\"id\":\"05d34f2e81b12d25b60b1cd6a3788468\"},\"c4520aa3-cdf4-46b7-9539-180edc16d223\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon A4\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"c4520aa3-cdf4-46b7-9539-180edc16d223\",\"id\":\"54c0d9d278b045cbcb57fe4b2d477b86\"},\"c5b0f410-b4ee-4552-ac04-06fa0c13ec3b\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Unique Asset ID - Level/Floor\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"c5b0f410-b4ee-4552-ac04-06fa0c13ec3b\",\"id\":\"7365bc4a64abd37f8dbed69316983d60\"},\"c67d4cb4-b2d6-426f-8d05-ac6fbe0bd267\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Data 5\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"c67d4cb4-b2d6-426f-8d05-ac6fbe0bd267\",\"id\":\"7caf73cac38e203374836cef4827f0ae\"},\"c7ce9441-9aba-45ab-acbb-74e687481466\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Classification.OmniClass.22.Number\",\"units\":null,\"value\":\"22-03 35 13\",\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"c7ce9441-9aba-45ab-acbb-74e687481466\",\"id\":\"8b96e2130eddfb81c6a35635c5654079\"},\"cab1938b-5da8-4b53-9f0c-bb473c1966a4\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Level Number(Global Inherited Properties)\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"cab1938b-5da8-4b53-9f0c-bb473c1966a4\",\"id\":\"baed41d276e750b374a5ca62366d0e56\"},\"cda17719-cef4-4c69-92f1-e111e85353b1\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcTag\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"cda17719-cef4-4c69-92f1-e111e85353b1\",\"id\":\"a00fb37b573181a2bb6070b416ac2c87\"},\"ce24f3b1-369d-42bb-987e-ac0b45c4f8da\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Classification.OmniClass.23.Description\",\"units\":null,\"value\":\"Concrete Structural Floor Decks\",\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ce24f3b1-369d-42bb-987e-ac0b45c4f8da\",\"id\":\"57445c90784f528c2b0f414f9233a42d\"},\"ce25a030-e3d0-4856-bbc0-a1cdd8f4d4ff\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon B4\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ce25a030-e3d0-4856-bbc0-a1cdd8f4d4ff\",\"id\":\"deb71e96bcf8157d78c59266aaaa86d1\"},\"d05b3c99-0643-409d-ad3b-2704d324bbcd\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon B1\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"d05b3c99-0643-409d-ad3b-2704d324bbcd\",\"id\":\"34d4a2c23bc26a1545523ae8597223ca\"},\"d608342a-e8f5-4ec9-9626-771914eb3da2\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type Data 4\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"d608342a-e8f5-4ec9-9626-771914eb3da2\",\"id\":\"b640302338235776966b78b0735abcca\"},\"d8b20410-414f-4777-8614-a7564519c6cd\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Classification.OmniClass.21.Number\",\"units\":null,\"value\":\"21-02 10 10 20 04\",\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"d8b20410-414f-4777-8614-a7564519c6cd\",\"id\":\"60e194032a186046e1a6db66376b97fb\"},\"db575143-7118-4f29-813e-2ced4535a170\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon B2\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"db575143-7118-4f29-813e-2ced4535a170\",\"id\":\"29e7f431d56d49569acb66b3f0621eb5\"},\"dd0cf380-59d8-4d9f-82da-3e2be59e23a1\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon B7\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"dd0cf380-59d8-4d9f-82da-3e2be59e23a1\",\"id\":\"b2887224f02b48130a75948e3f924e61\"},\"dedec34c-f507-4242-a85f-07a816ff1128\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcName\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"dedec34c-f507-4242-a85f-07a816ff1128\",\"id\":\"eef57991b50ee252c88dcee0dc06fddd\"},\"e4af54de-6137-43b0-97d4-c2260a1a68c3\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcTag\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"e4af54de-6137-43b0-97d4-c2260a1a68c3\",\"id\":\"f00202e050fa8a6ea223906e11e870bd\"},\"ea3ba87c-ae3c-47ed-886d-754f2359389c\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcMaterial\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ea3ba87c-ae3c-47ed-886d-754f2359389c\",\"id\":\"0cd769e3fd9905a59cae6cea37f77b46\"},\"ed6b5c87-b77c-45ab-9d90-8f26e365bca1\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcSpatialContainer\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ed6b5c87-b77c-45ab-9d90-8f26e365bca1\",\"id\":\"76cd1f61df7f11197ead15a92693abae\"},\"ee8153af-4866-45f2-a9a2-b6342ccb1dd6\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcMaterial\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ee8153af-4866-45f2-a9a2-b6342ccb1dd6\",\"id\":\"c32a465b6682dbc0908107858c6b9b6b\"},\"ef2d5da9-0b71-4617-a78c-cf4395808169\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type Data 3\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ef2d5da9-0b71-4617-a78c-cf4395808169\",\"id\":\"28aa3437bdf40659590c753a3e842193\"},\"f3b20cd0-059c-48a6-b744-7a9babf1cb29\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon C4\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"f3b20cd0-059c-48a6-b744-7a9babf1cb29\",\"id\":\"094df4c6e1d89bda1c3d32d19b859e57\"},\"f69d55e2-e23c-4a77-999d-45bae64d5856\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcPropertySetList\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"f69d55e2-e23c-4a77-999d-45bae64d5856\",\"id\":\"3fffe0348a0f398a26003c9552c4dbc9\"},\"f6a1fceb-536f-4261-ad25-4f1fb4dcda76\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Concrete Grade\",\"units\":null,\"value\":\"40 MPa\",\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"f6a1fceb-536f-4261-ad25-4f1fb4dcda76\",\"id\":\"acd05a5a6632bf49f56fe8d9b32ca1ff\"},\"f7c3a959-7884-46fd-97a6-cca3cea07fe7\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcPropertySetList\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"f7c3a959-7884-46fd-97a6-cca3cea07fe7\",\"id\":\"131c5cc690b61877eac2f73f73cd69ad\"},\"fac5d675-3756-485d-9500-4f2aa3096a38\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Product Name\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"fac5d675-3756-485d-9500-4f2aa3096a38\",\"id\":\"b5ba47412a41ee8f0e0b6309435f08e7\"},\"fb16e643-73bd-4c8d-a506-99506b010546\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Rate PT Area\",\"units\":null,\"value\":4.5,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"fb16e643-73bd-4c8d-a506-99506b010546\",\"id\":\"8607ae9e4c1350ec9eee23830a12c77e\"},\"fb272f85-666a-45a4-ae16-fa4d620d81b7\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Classification.OmniClass.23.Number\",\"units\":null,\"value\":\"23-13 35 23 11 11\",\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"fb272f85-666a-45a4-ae16-fa4d620d81b7\",\"id\":\"5bcbae5ccbbdc5366970db0d9dd64eca\"},\"STRUCTURAL_ELEVATION_AT_BOTTOM_SURVEY\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Elevation at Bottom Survey\",\"units\":\"mm\",\"value\":30699.999999999898,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":\"autodesk.unit.unit:millimeters-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"STRUCTURAL_ELEVATION_AT_BOTTOM_SURVEY\",\"id\":\"830d5e76c5b5b84bbab7f9f52e80dfef\"},\"id\":\"e24896645d6932e8d2edc7b56bcd65b2\"}" + )] + [InlineData( + "{\n \"applicationId\": null,\n \"speckle_type\": \"Base\",\n \"name\": \"Physically Based (1)\",\n \"diffuse\": -11810867,\n \"opacity\": 1,\n \"emissive\": -16777216,\n \"metalness\": 0,\n \"roughness\": 0.2,\n \"totalChildrenCount\": 0,\n \"id\": \"3ef9f1e3deb7e8057f9eceb29ff2ea88\"\n}" + )] + public void Serialize_Id_Stable(string json) + { + var jObject = JObject.Parse(json); + var id = jObject["id"].NotNull().Value(); + jObject.Remove("id"); + jObject.Remove("__closure"); + var jsonWithoutId = jObject.ToString(Formatting.None); + var newId = IdGenerator.ComputeId(new Json(jsonWithoutId)); + id.Should().Be(newId.Value); + } + + [Theory] + [InlineData("RevitObject.json.gz", "3416d3fe01c9196115514c4a2f41617b", 7818)] + public async Task Roundtrip_Test_Old(string fileName, string _, int count) + { + var closures = await TestFileManager.GetFileAsClosures(fileName); + var deserializer = new SpeckleObjectDeserializer + { + ReadTransport = new TestTransport(closures), + CancellationToken = default, + }; + + var writtenObjects = new Dictionary(); + var writeTransport = new TestTransport2(writtenObjects); + var serializer = new SpeckleObjectSerializer([writeTransport]); + var newIds = new Dictionary(); + var oldIds = new Dictionary(); + var idToBase = new Dictionary(); + closures.Count.Should().Be(count); + foreach (var (id, objJson) in closures) + { + var base1 = await deserializer.DeserializeAsync(objJson); + base1.id.Should().Be(id); + var j = serializer.Serialize(base1); + //j.Should().Be(objJson); + JToken.DeepEquals(JObject.Parse(j), JObject.Parse(objJson)); + newIds.Add(base1.id.NotNull(), j); + oldIds.Add(id, j); + idToBase.Add(id, base1); + } + newIds.Count.Should().Be(count); + oldIds.Count.Should().Be(count); + idToBase.Count.Should().Be(count); + } + + [Theory] + [InlineData("RevitObject.json.gz", "3416d3fe01c9196115514c4a2f41617b", 7818, 4674)] + public async Task Roundtrip_Test_New(string fileName, string rootId, int oldCount, int newCount) + { + var closures = await TestFileManager.GetFileAsClosures(fileName); + closures.Count.Should().Be(oldCount); + + Base root; + using ( + var o = new ObjectLoader( + new DummySqLiteReceiveManager(closures), + new DummyReceiveServerObjectManager(closures), + null, + default + ) + ) + { + await using var process = new DeserializeProcess( + o, + null, + new BaseDeserializer(new ObjectDeserializerFactory()), + new NullLoggerFactory(), + default, + new(true) + ); + root = await process.Deserialize(rootId); + process.BaseCache.Count.Should().Be(oldCount); + process.Total.Should().Be(oldCount); + } + + var newIdToJson = new ConcurrentDictionary(); + await using ( + var serializeProcess = new SerializeProcess( + null, + new DummySqLiteSendManager(), + new DummySendServerObjectManager(newIdToJson), + new BaseChildFinder(new BasePropertyGatherer()), + new BaseSerializer(new DummySqLiteSendManager(), new ObjectSerializerFactory(new BasePropertyGatherer())), + new NullLoggerFactory(), + default, + new SerializeProcessOptions(true, true, false, true) + ) + ) + { + var (rootId2, _) = await serializeProcess.Serialize(root); + rootId2.Should().Be(root.id); + } + newIdToJson.Count.Should().Be(newCount); + + foreach (var newKvp in newIdToJson) + { + if (closures.TryGetValue(newKvp.Key, out var newValue)) + { + JToken.DeepEquals(JObject.Parse(newValue), JObject.Parse(newKvp.Value)); + } } } } diff --git a/tests/Speckle.Sdk.Serialization.Tests/Speckle.Sdk.Serialization.Tests.csproj b/tests/Speckle.Sdk.Serialization.Tests/Speckle.Sdk.Serialization.Tests.csproj index 2950a290..47352da9 100644 --- a/tests/Speckle.Sdk.Serialization.Tests/Speckle.Sdk.Serialization.Tests.csproj +++ b/tests/Speckle.Sdk.Serialization.Tests/Speckle.Sdk.Serialization.Tests.csproj @@ -4,24 +4,23 @@ net8.0 true System.Runtime.CompilerServices.IsExternalInit;System.Runtime.CompilerServices.RequiresLocationAttribute - Speckle.Sdk.Serialization.Tests + - - - + + - + - + diff --git a/tests/Speckle.Sdk.Serialization.Tests/packages.lock.json b/tests/Speckle.Sdk.Serialization.Tests/packages.lock.json index 6b3de7b8..3eb30310 100644 --- a/tests/Speckle.Sdk.Serialization.Tests/packages.lock.json +++ b/tests/Speckle.Sdk.Serialization.Tests/packages.lock.json @@ -4,24 +4,24 @@ "net8.0": { "altcover": { "type": "Direct", - "requested": "[8.8.74, )", - "resolved": "8.8.74", - "contentHash": "e8RZNE0vZnuBk/gOAWu9K5wm3S7dOrOlZje3PHI9PJUHqvP1cxVJD1eXAAmddFVlixowB7C7/zvC16GnunC2LQ==" + "requested": "[9.0.1, )", + "resolved": "9.0.1", + "contentHash": "aadciFNDT5bnylaYUkKal+s5hF7yU/lmZxImQWAlk1438iPqK1Uf79H5ylELpyLIU49HL5ql+tnWBihp3WVLCA==" }, - "GitVersion.MsBuild": { + "AwesomeAssertions": { "type": "Direct", - "requested": "[5.12.0, )", - "resolved": "5.12.0", - "contentHash": "dJuigXycpJNOiLT9or7mkHSkGFHgGW3/p6cNNYEKZBa7Hhp1FdX/cvqYWWYhRLpfoZOedeA7aRbYiOB3vW/dvA==" + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "6fWiV7mGZUzZXzeiW3hWF0nJokuuNm4hnzuqbM3IXHqGYkWnHl65+wNpuQ73xfJXClX0fmfKcTdQ2Ula719IDg==" }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.10.0, )", - "resolved": "17.10.0", - "contentHash": "0/2HeACkaHEYU3wc83YlcD2Fi4LMtECJjqrtvw0lPi9DCEa35zSPt1j4fuvM8NagjDqJuh1Ja35WcRtn1Um6/A==", + "requested": "[17.13.0, )", + "resolved": "17.13.0", + "contentHash": "W19wCPizaIC9Zh47w8wWI/yxuqR7/dtABwOrc8r2jX/8mUNxM2vw4fXDh+DJTeogxV+KzKwg5jNNGQVwf3LXyA==", "dependencies": { - "Microsoft.CodeCoverage": "17.10.0", - "Microsoft.TestPlatform.TestHost": "17.10.0" + "Microsoft.CodeCoverage": "17.13.0", + "Microsoft.TestPlatform.TestHost": "17.13.0" } }, "Microsoft.SourceLink.GitHub": { @@ -34,33 +34,11 @@ "Microsoft.SourceLink.Common": "8.0.0" } }, - "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" - } + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g==" }, "Speckle.InterfaceGenerator": { "type": "Direct", @@ -68,19 +46,41 @@ "resolved": "0.9.6", "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" }, + "xunit.assert": { + "type": "Direct", + "requested": "[2.9.3, )", + "resolved": "2.9.3", + "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA==" + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[3.0.2, )", + "resolved": "3.0.2", + "contentHash": "oXbusR6iPq0xlqoikjdLvzh+wQDkMv9If58myz9MEzldS4nIcp442Btgs2sWbYWV+caEluMe2pQCZ0hUZgPiow==" + }, + "Argon": { + "type": "Transitive", + "resolved": "0.26.0", + "contentHash": "n7btGXdtRyprGnpLMpBs6rLScxlvPtVWwmTR8h7CtJvpZXBGhGvibEdZxRjeTZNrwf403jJ0ZPpt35Pz/NaNsw==" + }, "DiffEngine": { "type": "Transitive", - "resolved": "11.3.0", - "contentHash": "k0ZgZqd09jLZQjR8FyQbSQE86Q7QZnjEzq1LPHtj1R2AoWO8sjV5x+jlSisL7NZAbUOI4y+7Bog8gkr9WIRBGw==", + "resolved": "15.9.0", + "contentHash": "shE6+tO4w5BmQTX0z+WnUV4UfmPNn6oTqBINbkts6OP0Icyx5WROSDzjjb95EwVYC4IAS+PxxS4Vbapxz4hkdw==", "dependencies": { - "EmptyFiles": "4.4.0", - "System.Management": "6.0.1" + "EmptyFiles": "8.7.1", + "System.Management": "9.0.1" } }, "EmptyFiles": { "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "gwJEfIGS7FhykvtZoscwXj/XwW+mJY6UbAZk+qtLKFUGWC95kfKXnj8VkxsZQnWBxJemM/q664rGLN5nf+OHZw==" + "resolved": "8.7.1", + "contentHash": "C8pvg0TvG2Mkn5LGNFGkFgFu8SUgYFwiu8U3y34qGQnnwKmGnlQTfTIUrtzfSjPxA4q7L/kRu09U5p32otZ2Aw==" + }, + "FSharp.Core": { + "type": "Transitive", + "resolved": "7.0.300", + "contentHash": "8vvItREJ1l5lcp3vBCSJ1mFevVAhR48I34DuF/EoUa7o1KlFpQpagyuZkVYMAsHPIjdp47ZxM9sI4eqeXaeWkA==" }, "GraphQL.Client.Abstractions": { "type": "Transitive", @@ -110,17 +110,65 @@ }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "17.10.0", - "contentHash": "yC7oSlnR54XO5kOuHlVOKtxomNNN1BWXX8lK1G2jaPXT9sUok7kCOoA4Pgs0qyFaCtMrNsprztYMeoEGqCm4uA==" + "resolved": "17.13.0", + "contentHash": "9LIUy0y+DvUmEPtbRDw6Bay3rzwqFV8P4efTrK4CZhQle3M/QwLPjISghfcolmEGAPWxuJi6m98ZEfk4VR4Lfg==" }, "Microsoft.Data.Sqlite.Core": { "type": "Transitive", - "resolved": "7.0.7", - "contentHash": "21FRzcJhaTrlv7kTrqr/ltFcSQM2TyuTTPhUcjO8H73od7Bb3QraNW90c7lUucNI/245XPkKZG4fp7/7OsKCSg==", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", "dependencies": { "SQLitePCLRaw.core": "2.1.4" } }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "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.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -128,18 +176,18 @@ }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.10.0", - "contentHash": "KkwhjQevuDj0aBRoPLY6OLAhGqbPUEBuKLbaCs0kUVw29qiOYncdORd4mLVJbn9vGZ7/iFGQ/+AoJl0Tu5Umdg==", + "resolved": "17.13.0", + "contentHash": "bt0E0Dx+iqW97o4A59RCmUmz/5NarJ7LRL+jXbSHod72ibL5XdNm1Ke+UO5tFhBG4VwHLcSjqq9BUSblGNWamw==", "dependencies": { "System.Reflection.Metadata": "1.6.0" } }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.10.0", - "contentHash": "LWpMdfqhHvcUkeMCvNYJO8QlPLlYz9XPPb+ZbaXIKhdmjAV0wqTSrTiW5FLaf7RRZT50AQADDOYMOe0HxDxNgA==", + "resolved": "17.13.0", + "contentHash": "9GGw08Dc3AXspjekdyTdZ/wYWFlxbgcF0s7BKxzVX+hzAwpifDOdxM+ceVaaJSQOwqt3jtuNlHn3XTpKUS9x9Q==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.10.0", + "Microsoft.TestPlatform.ObjectModel": "17.13.0", "Newtonsoft.Json": "13.0.1" } }, @@ -148,6 +196,20 @@ "resolved": "13.0.1", "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" }, + "Quibble": { + "type": "Transitive", + "resolved": "0.3.1", + "contentHash": "LD6bz2p+4O/BQnmD4mqFZrmdN/IjsPo1wUvfmcH46Q05ng+dyMLl3d2ylj0x412F4fpJEtm0Z3EaCAx4FqgNuQ==", + "dependencies": { + "FSharp.Core": "7.0.300", + "System.Text.Json": "7.0.3" + } + }, + "SimpleInfoName": { + "type": "Transitive", + "resolved": "3.1.0", + "contentHash": "j+ENh86NhxrgDc6T1ueqIR2QOdDkSJY2dbTFyPN/JvIXifB4GHAunlMw/x7P6m7XaXEHr3s+SMZfKBlmnmkO6g==" + }, "SQLitePCLRaw.bundle_e_sqlite3": { "type": "Transitive", "resolved": "2.1.4", @@ -180,15 +242,25 @@ }, "System.CodeDom": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA==" + "resolved": "9.0.1", + "contentHash": "2J5uq+2smnj+u1jlyVJ6BGGqaK9fHcK/EwN7mbsuPqTI6dZr86br8Cg6o/5B+icQ9ANTvTDpJjnhDNtYYZijHQ==" + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.IO.Hashing": { + "type": "Transitive", + "resolved": "9.0.1", + "contentHash": "jY+E/PElNWQiazN0YHqZGvcSedcZ4Wt0Os1nnJ2SzR3gWZlhNRDkSXOhuHJcLuImD8SrJQQ8TfU0W4mVcit2hg==" }, "System.Management": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "10J1D0h/lioojphfJ4Fuh5ZUThT/xOVHdV9roGBittKKNP2PMjrvibEdbVTGZcPra1399Ja3tqIJLyQrc5Wmhg==", + "resolved": "9.0.1", + "contentHash": "CLEo9O6FuO4GQ3ZQkGssg9CJ2w2TN7GMFf3wHTc7YVWJV4xoyJRPw+XIDQnCcSUJCrHhrAWOO60cAX29EV5LFQ==", "dependencies": { - "System.CodeDom": "6.0.0" + "System.CodeDom": "9.0.1" } }, "System.Memory": { @@ -206,6 +278,56 @@ "resolved": "1.6.0", "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "8.0.4", + "contentHash": "bAkhgDJ88XTsqczoxEMliSrpijKZHhbJQldhAmObj/RbrN3sU5dcokuXmWJWsdQAhiMJ9bTayWsL1C9fbbCRhw==", + "dependencies": { + "System.Text.Encodings.Web": "8.0.0" + } + }, + "Verify": { + "type": "Transitive", + "resolved": "28.10.1", + "contentHash": "2B/VtFN5jtF5g28kaM4GdJZTwb3pisd4+wL2NEPi9ZYe2lghWsCzS30V6LF1ILApLBfAorAstkU/Vw3sDWRqrg==", + "dependencies": { + "Argon": "0.26.0", + "DiffEngine": "15.9.0", + "SimpleInfoName": "3.1.0", + "System.IO.Hashing": "9.0.1" + } + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.extensibility.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==", + "dependencies": { + "xunit.abstractions": "2.0.3" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]" + } + }, "speckle.objects": { "type": "Project", "dependencies": { @@ -217,19 +339,27 @@ "dependencies": { "GraphQL.Client": "[6.0.0, )", "Microsoft.CSharp": "[4.7.0, )", - "Microsoft.Data.Sqlite": "[7.0.7, )", - "Polly": "[7.2.3, )", - "Polly.Contrib.WaitAndRetry": "[1.1.1, )", - "Polly.Extensions.Http": "[3.0.0, )", - "Speckle.DoubleNumerics": "[4.0.1, )", + "Microsoft.Data.Sqlite": "[7.0.5, )", + "Microsoft.Extensions.DependencyInjection.Abstractions": "[2.2.0, )", + "Microsoft.Extensions.Logging": "[2.2.0, )", + "Speckle.DoubleNumerics": "[4.1.0, )", "Speckle.Newtonsoft.Json": "[13.0.2, )", - "Speckle.Sdk.Logging": "[1.0.0, )", - "System.Text.Json": "[5.0.2, )" + "Speckle.Sdk.Dependencies": "[1.0.0, )" } }, - "speckle.sdk.logging": { + "speckle.sdk.dependencies": { "type": "Project" }, + "speckle.sdk.testing": { + "type": "Project", + "dependencies": { + "Microsoft.NET.Test.Sdk": "[17.13.0, )", + "Speckle.Sdk": "[1.0.0, )", + "Verify.Quibble": "[2.1.1, )", + "Verify.Xunit": "[28.10.1, )", + "xunit.runner.visualstudio": "[3.0.2, )" + } + }, "GraphQL.Client": { "type": "CentralTransitive", "requested": "[6.0.0, )", @@ -249,40 +379,37 @@ }, "Microsoft.Data.Sqlite": { "type": "CentralTransitive", - "requested": "[7.0.7, )", - "resolved": "7.0.7", - "contentHash": "tiNmV1oPy+Z2R7Wd0bPB/FxCr8B+/5q11OpDMG751GA/YuOL7MZrBFfzv5oFRlFe08K6sjrnbrauzzGIeNrzLQ==", + "requested": "[7.0.5, )", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", "dependencies": { - "Microsoft.Data.Sqlite.Core": "7.0.7", + "Microsoft.Data.Sqlite.Core": "7.0.5", "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" } }, - "Polly": { + "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "CentralTransitive", - "requested": "[7.2.3, )", - "resolved": "7.2.3", - "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" }, - "Polly.Contrib.WaitAndRetry": { + "Microsoft.Extensions.Logging": { "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==", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", "dependencies": { - "Polly": "7.1.0" + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" } }, "Speckle.DoubleNumerics": { "type": "CentralTransitive", - "requested": "[4.0.1, )", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" }, "Speckle.Newtonsoft.Json": { "type": "CentralTransitive", @@ -290,11 +417,31 @@ "resolved": "13.0.2", "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" }, - "System.Text.Json": { + "Verify.Quibble": { "type": "CentralTransitive", - "requested": "[5.0.2, )", - "resolved": "5.0.2", - "contentHash": "I47dVIGiV6SfAyppphxqupertT/5oZkYLDCX6vC3HpOI4ZLjyoKAreUoem2ie6G0RbRuFrlqz/PcTQjfb2DOfQ==" + "requested": "[2.1.1, )", + "resolved": "2.1.1", + "contentHash": "Z8bVwFICa3Dog6Mcnx0wlrn4Y+CFpQXx1f+ijfLn6/v4q00q+jLm9Gu/nVyUFuc75cjn6ieI08UrqXKcR9fTYw==", + "dependencies": { + "Quibble": "0.3.1", + "System.Text.Json": "8.0.4", + "Verify": "26.1.1" + } + }, + "Verify.Xunit": { + "type": "CentralTransitive", + "requested": "[28.10.1, )", + "resolved": "28.10.1", + "contentHash": "mkG7agMlx8oAEGcHoRY72hyDyNTdLIrzbfmniXFQgQ3yKulAHSYvYc9quzhpg0Sy+jb3svbdLqnRSg0VRhet3A==", + "dependencies": { + "Argon": "0.26.0", + "DiffEngine": "15.9.0", + "SimpleInfoName": "3.1.0", + "System.IO.Hashing": "9.0.1", + "Verify": "28.10.1", + "xunit.abstractions": "2.0.3", + "xunit.extensibility.execution": "2.9.3" + } } } } diff --git a/tests/Speckle.Sdk.Testing/Framework/AggregationExceptionScrubber.cs b/tests/Speckle.Sdk.Testing/Framework/AggregationExceptionScrubber.cs new file mode 100644 index 00000000..2c96cfce --- /dev/null +++ b/tests/Speckle.Sdk.Testing/Framework/AggregationExceptionScrubber.cs @@ -0,0 +1,32 @@ +using Speckle.Sdk.Common; + +namespace Speckle.Sdk.Testing.Framework; + +public class AggregationExceptionScrubber : WriteOnlyJsonConverter +{ + private static readonly ExceptionScrubber _innerScrubber = new(); + + public override void Write(VerifyJsonWriter writer, AggregateException exception) + { + writer.WriteStartObject(); + + writer.WriteMember(exception, exception.GetType().FullName, "Type"); + if (exception.InnerExceptions.Count == 1) + { + writer.WritePropertyName("InnerException"); + _innerScrubber.Write(writer, exception.InnerException.NotNull()); + } + else + { + writer.WritePropertyName("InnerExceptions"); + writer.WriteStartArray(); + foreach (var innerException in exception.InnerExceptions) + { + _innerScrubber.Write(writer, innerException); + } + writer.WriteEndArray(); + } + + writer.WriteEndObject(); + } +} diff --git a/tests/Speckle.Sdk.Testing/Framework/DummyReceiveServerObjectManager.cs b/tests/Speckle.Sdk.Testing/Framework/DummyReceiveServerObjectManager.cs new file mode 100644 index 00000000..75787dc7 --- /dev/null +++ b/tests/Speckle.Sdk.Testing/Framework/DummyReceiveServerObjectManager.cs @@ -0,0 +1,55 @@ +using System.Runtime.CompilerServices; +using System.Text; +using Speckle.Sdk.Serialisation.V2; +using Speckle.Sdk.Serialisation.V2.Send; +using Speckle.Sdk.Transports; + +namespace Speckle.Sdk.Testing.Framework; + +public class DummyReceiveServerObjectManager(IReadOnlyDictionary objects) : IServerObjectManager +{ + public async IAsyncEnumerable<(string, string)> DownloadObjects( + IReadOnlyCollection objectIds, + IProgress? progress, + [EnumeratorCancellation] CancellationToken cancellationToken + ) + { + await Task.CompletedTask; + foreach (var id in objectIds) + { + yield return (id, objects[id]); + } + } + + public async Task DownloadSingleObject( + string objectId, + IProgress? progress, + CancellationToken cancellationToken + ) + { + await Task.CompletedTask; + return objects[objectId]; + } + + public Task> HasObjects( + IReadOnlyCollection objectIds, + CancellationToken cancellationToken + ) => throw new NotImplementedException(); + + public Task UploadObjects( + IReadOnlyList objects, + bool compressPayloads, + IProgress? progress, + CancellationToken cancellationToken + ) + { + long totalBytes = 0; + foreach (var item in objects) + { + totalBytes += Encoding.Default.GetByteCount(item.Json.Value); + } + + progress?.Report(new(ProgressEvent.UploadBytes, totalBytes, totalBytes)); + return Task.CompletedTask; + } +} diff --git a/tests/Speckle.Sdk.Testing/Framework/DummySendServerObjectManager.cs b/tests/Speckle.Sdk.Testing/Framework/DummySendServerObjectManager.cs new file mode 100644 index 00000000..0ae163c4 --- /dev/null +++ b/tests/Speckle.Sdk.Testing/Framework/DummySendServerObjectManager.cs @@ -0,0 +1,43 @@ +using System.Collections.Concurrent; +using Speckle.Sdk.Serialisation.V2; +using Speckle.Sdk.Serialisation.V2.Send; +using Speckle.Sdk.Transports; + +namespace Speckle.Sdk.Testing.Framework; + +public class DummySendServerObjectManager(ConcurrentDictionary savedObjects) : IServerObjectManager +{ + public IAsyncEnumerable<(string, string)> DownloadObjects( + IReadOnlyCollection objectIds, + IProgress? progress, + CancellationToken cancellationToken + ) => throw new NotImplementedException(); + + public Task DownloadSingleObject( + string objectId, + IProgress? progress, + CancellationToken cancellationToken + ) => throw new NotImplementedException(); + + public Task> HasObjects( + IReadOnlyCollection objectIds, + CancellationToken cancellationToken + ) + { + return Task.FromResult(objectIds.Distinct().ToDictionary(x => x, savedObjects.ContainsKey)); + } + + public Task UploadObjects( + IReadOnlyList objects, + bool compressPayloads, + IProgress? progress, + CancellationToken cancellationToken + ) + { + foreach (var obj in objects) + { + savedObjects.TryAdd(obj.Id.Value, obj.Json.Value); + } + return Task.CompletedTask; + } +} diff --git a/tests/Speckle.Sdk.Testing/Framework/DummySqLiteReceiveManager.cs b/tests/Speckle.Sdk.Testing/Framework/DummySqLiteReceiveManager.cs new file mode 100644 index 00000000..fcfe6832 --- /dev/null +++ b/tests/Speckle.Sdk.Testing/Framework/DummySqLiteReceiveManager.cs @@ -0,0 +1,23 @@ +using Speckle.Sdk.SQLite; + +namespace Speckle.Sdk.Testing.Framework; + +public sealed class DummySqLiteReceiveManager(IReadOnlyDictionary savedObjects) + : ISqLiteJsonCacheManager +{ + public void Dispose() { } + + public IReadOnlyCollection<(string, string)> GetAllObjects() => throw new NotImplementedException(); + + public void DeleteObject(string id) => throw new NotImplementedException(); + + public string? GetObject(string id) => savedObjects.GetValueOrDefault(id); + + public void SaveObject(string id, string json) => throw new NotImplementedException(); + + public void UpdateObject(string id, string json) => throw new NotImplementedException(); + + public void SaveObjects(IEnumerable<(string id, string json)> items) => throw new NotImplementedException(); + + public bool HasObject(string objectId) => throw new NotImplementedException(); +} diff --git a/tests/Speckle.Sdk.Testing/Framework/DummySqLiteSendManager.cs b/tests/Speckle.Sdk.Testing/Framework/DummySqLiteSendManager.cs new file mode 100644 index 00000000..b1425dd6 --- /dev/null +++ b/tests/Speckle.Sdk.Testing/Framework/DummySqLiteSendManager.cs @@ -0,0 +1,22 @@ +using Speckle.Sdk.SQLite; + +namespace Speckle.Sdk.Testing.Framework; + +public class DummySqLiteSendManager : ISqLiteJsonCacheManager +{ + public string? GetObject(string id) => throw new NotImplementedException(); + + public void SaveObject(string id, string json) => throw new NotImplementedException(); + + public void UpdateObject(string id, string json) => throw new NotImplementedException(); + + public virtual void SaveObjects(IEnumerable<(string id, string json)> items) => throw new NotImplementedException(); + + public bool HasObject(string objectId) => throw new NotImplementedException(); + + public IReadOnlyCollection<(string, string)> GetAllObjects() => throw new NotImplementedException(); + + public void DeleteObject(string id) => throw new NotImplementedException(); + + public void Dispose() { } +} diff --git a/tests/Speckle.Sdk.Testing/Framework/ExceptionScrubber.cs b/tests/Speckle.Sdk.Testing/Framework/ExceptionScrubber.cs new file mode 100644 index 00000000..3209a6bb --- /dev/null +++ b/tests/Speckle.Sdk.Testing/Framework/ExceptionScrubber.cs @@ -0,0 +1,18 @@ +using Argon; + +namespace Speckle.Sdk.Testing.Framework; + +public class ExceptionScrubber : WriteOnlyJsonConverter +{ + public override void Write(VerifyJsonWriter writer, Exception value) + { + var ex = new JObject + { + ["Type"] = value.GetType().FullName, + ["Message"] = value.Message, + ["Source"] = value.Source?.Trim(), + }; + //intentionally removed stacktrace to avoid errors on different machines and line numbers + writer.WriteRawValue(ex.ToString(Formatting.Indented)); + } +} diff --git a/tests/Speckle.Sdk.Testing/Framework/IdStringSerializer.cs b/tests/Speckle.Sdk.Testing/Framework/IdStringSerializer.cs new file mode 100644 index 00000000..5a7e7258 --- /dev/null +++ b/tests/Speckle.Sdk.Testing/Framework/IdStringSerializer.cs @@ -0,0 +1,24 @@ +using System.Diagnostics.CodeAnalysis; +using Argon; +using Speckle.Sdk.Common; +using Speckle.Sdk.Serialisation; + +namespace Speckle.Sdk.Testing.Framework; + +[SuppressMessage("Design", "CA1062:Validate arguments of public methods")] +public class IdStringSerializer : JsonConverter +{ + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + var id = (Id)value; + writer.WriteValue(id.Value); + } + + public override object? ReadJson(JsonReader reader, Type type, object? existingValue, JsonSerializer serializer) + { + var json = reader.ReadAsString(); + return new Id(json.NotNull()); + } + + public override bool CanConvert(Type type) => typeof(Id) == type; +} diff --git a/tests/Speckle.Sdk.Testing/Framework/JsonStringSerializer.cs b/tests/Speckle.Sdk.Testing/Framework/JsonStringSerializer.cs new file mode 100644 index 00000000..11feb571 --- /dev/null +++ b/tests/Speckle.Sdk.Testing/Framework/JsonStringSerializer.cs @@ -0,0 +1,24 @@ +using System.Diagnostics.CodeAnalysis; +using Argon; +using Speckle.Sdk.Common; +using Speckle.Sdk.Serialisation; + +namespace Speckle.Sdk.Testing.Framework; + +[SuppressMessage("Design", "CA1062:Validate arguments of public methods")] +public class JsonStringSerializer : JsonConverter +{ + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + var json = (Json)value; + writer.WriteRawValue(JObject.Parse(json.Value).ToString(Formatting.Indented)); + } + + public override object? ReadJson(JsonReader reader, Type type, object? existingValue, JsonSerializer serializer) + { + var json = reader.ReadAsString(); + return new Json(json.NotNull()); + } + + public override bool CanConvert(Type type) => typeof(Json) == type; +} diff --git a/tests/Speckle.Sdk.Testing/Global.cs b/tests/Speckle.Sdk.Testing/Global.cs new file mode 100644 index 00000000..c7fc3b1f --- /dev/null +++ b/tests/Speckle.Sdk.Testing/Global.cs @@ -0,0 +1 @@ +[assembly: CollectionBehavior(DisableTestParallelization = true)] diff --git a/tests/Speckle.Sdk.Testing/Speckle.Sdk.Testing.csproj b/tests/Speckle.Sdk.Testing/Speckle.Sdk.Testing.csproj new file mode 100644 index 00000000..b5ecb4ed --- /dev/null +++ b/tests/Speckle.Sdk.Testing/Speckle.Sdk.Testing.csproj @@ -0,0 +1,19 @@ + + + + net8.0 + true + + + + + + + + + + + + + + diff --git a/tests/Speckle.Sdk.Testing/SpeckleVerify.cs b/tests/Speckle.Sdk.Testing/SpeckleVerify.cs new file mode 100644 index 00000000..4cb3946f --- /dev/null +++ b/tests/Speckle.Sdk.Testing/SpeckleVerify.cs @@ -0,0 +1,57 @@ +using System.Runtime.CompilerServices; +using Argon; +using Speckle.Sdk.Serialisation; +using Speckle.Sdk.Testing.Framework; + +namespace Speckle.Sdk.Testing; + +public static class SpeckleVerify +{ + private static bool _initialized; + + [ModuleInitializer] + public static void Initialize() + { + if (_initialized) + { + return; + } + + _initialized = true; + VerifierSettings.DontScrubGuids(); + VerifierSettings.DontScrubDateTimes(); + + VerifierSettings.UseStrictJson(); + VerifierSettings.DontIgnoreEmptyCollections(); + VerifierSettings.SortPropertiesAlphabetically(); + VerifierSettings.SortJsonObjects(); + VerifierSettings.AddExtraSettings(x => + { + var existing = x.Converters.OfType>().First(); + x.Converters.Remove(existing); + x.Converters.Add(new AggregationExceptionScrubber()); + x.Converters.Add(new ExceptionScrubber()); + }); + VerifyQuibble.Initialize(); + } + + private static readonly JsonSerializer _jsonSerializer = new() + { + NullValueHandling = NullValueHandling.Include, + Formatting = Formatting.Indented, + Converters = { new JsonStringSerializer() }, + }; + + private static async Task VerifyJsonObjects(IDictionary objects, string sourceFile) => + await VerifyJson(JObject.FromObject(objects, _jsonSerializer).ToString(), sourceFile: sourceFile); + + public static async Task VerifyJsonDictionary( + IDictionary objects, + [CallerFilePath] string sourceFile = "" + ) => await VerifyJsonObjects(objects.ToDictionary(x => x.Key, x => new Json(x.Value)), sourceFile); + + public static async Task VerifyJsonDictionary( + IDictionary objects, + [CallerFilePath] string sourceFile = "" + ) => await VerifyJsonObjects(objects.ToDictionary(x => x.Key.Value, x => x.Value), sourceFile); +} diff --git a/tests/Speckle.Sdk.Testing/VerifyTests.cs b/tests/Speckle.Sdk.Testing/VerifyTests.cs new file mode 100644 index 00000000..a303625d --- /dev/null +++ b/tests/Speckle.Sdk.Testing/VerifyTests.cs @@ -0,0 +1,7 @@ +namespace Speckle.Sdk.Testing; + +public class VerifyTests +{ + [Fact] + public Task TestVerify() => VerifyChecks.Run(); +} diff --git a/tests/Speckle.Sdk.Testing/packages.lock.json b/tests/Speckle.Sdk.Testing/packages.lock.json new file mode 100644 index 00000000..64fa5df7 --- /dev/null +++ b/tests/Speckle.Sdk.Testing/packages.lock.json @@ -0,0 +1,414 @@ +{ + "version": 2, + "dependencies": { + "net8.0": { + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.13.0, )", + "resolved": "17.13.0", + "contentHash": "W19wCPizaIC9Zh47w8wWI/yxuqR7/dtABwOrc8r2jX/8mUNxM2vw4fXDh+DJTeogxV+KzKwg5jNNGQVwf3LXyA==", + "dependencies": { + "Microsoft.CodeCoverage": "17.13.0", + "Microsoft.TestPlatform.TestHost": "17.13.0" + } + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "Verify.Quibble": { + "type": "Direct", + "requested": "[2.1.1, )", + "resolved": "2.1.1", + "contentHash": "Z8bVwFICa3Dog6Mcnx0wlrn4Y+CFpQXx1f+ijfLn6/v4q00q+jLm9Gu/nVyUFuc75cjn6ieI08UrqXKcR9fTYw==", + "dependencies": { + "Quibble": "0.3.1", + "System.Text.Json": "8.0.4", + "Verify": "26.1.1" + } + }, + "Verify.Xunit": { + "type": "Direct", + "requested": "[28.10.1, )", + "resolved": "28.10.1", + "contentHash": "mkG7agMlx8oAEGcHoRY72hyDyNTdLIrzbfmniXFQgQ3yKulAHSYvYc9quzhpg0Sy+jb3svbdLqnRSg0VRhet3A==", + "dependencies": { + "Argon": "0.26.0", + "DiffEngine": "15.9.0", + "SimpleInfoName": "3.1.0", + "System.IO.Hashing": "9.0.1", + "Verify": "28.10.1", + "xunit.abstractions": "2.0.3", + "xunit.extensibility.execution": "2.9.3" + } + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[3.0.2, )", + "resolved": "3.0.2", + "contentHash": "oXbusR6iPq0xlqoikjdLvzh+wQDkMv9If58myz9MEzldS4nIcp442Btgs2sWbYWV+caEluMe2pQCZ0hUZgPiow==" + }, + "Argon": { + "type": "Transitive", + "resolved": "0.26.0", + "contentHash": "n7btGXdtRyprGnpLMpBs6rLScxlvPtVWwmTR8h7CtJvpZXBGhGvibEdZxRjeTZNrwf403jJ0ZPpt35Pz/NaNsw==" + }, + "DiffEngine": { + "type": "Transitive", + "resolved": "15.9.0", + "contentHash": "shE6+tO4w5BmQTX0z+WnUV4UfmPNn6oTqBINbkts6OP0Icyx5WROSDzjjb95EwVYC4IAS+PxxS4Vbapxz4hkdw==", + "dependencies": { + "EmptyFiles": "8.7.1", + "System.Management": "9.0.1" + } + }, + "EmptyFiles": { + "type": "Transitive", + "resolved": "8.7.1", + "contentHash": "C8pvg0TvG2Mkn5LGNFGkFgFu8SUgYFwiu8U3y34qGQnnwKmGnlQTfTIUrtzfSjPxA4q7L/kRu09U5p32otZ2Aw==" + }, + "FSharp.Core": { + "type": "Transitive", + "resolved": "7.0.300", + "contentHash": "8vvItREJ1l5lcp3vBCSJ1mFevVAhR48I34DuF/EoUa7o1KlFpQpagyuZkVYMAsHPIjdp47ZxM9sI4eqeXaeWkA==" + }, + "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.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.13.0", + "contentHash": "9LIUy0y+DvUmEPtbRDw6Bay3rzwqFV8P4efTrK4CZhQle3M/QwLPjISghfcolmEGAPWxuJi6m98ZEfk4VR4Lfg==" + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "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.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.13.0", + "contentHash": "bt0E0Dx+iqW97o4A59RCmUmz/5NarJ7LRL+jXbSHod72ibL5XdNm1Ke+UO5tFhBG4VwHLcSjqq9BUSblGNWamw==", + "dependencies": { + "System.Reflection.Metadata": "1.6.0" + } + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.13.0", + "contentHash": "9GGw08Dc3AXspjekdyTdZ/wYWFlxbgcF0s7BKxzVX+hzAwpifDOdxM+ceVaaJSQOwqt3jtuNlHn3XTpKUS9x9Q==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.13.0", + "Newtonsoft.Json": "13.0.1" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.1", + "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + }, + "Quibble": { + "type": "Transitive", + "resolved": "0.3.1", + "contentHash": "LD6bz2p+4O/BQnmD4mqFZrmdN/IjsPo1wUvfmcH46Q05ng+dyMLl3d2ylj0x412F4fpJEtm0Z3EaCAx4FqgNuQ==", + "dependencies": { + "FSharp.Core": "7.0.300", + "System.Text.Json": "7.0.3" + } + }, + "SimpleInfoName": { + "type": "Transitive", + "resolved": "3.1.0", + "contentHash": "j+ENh86NhxrgDc6T1ueqIR2QOdDkSJY2dbTFyPN/JvIXifB4GHAunlMw/x7P6m7XaXEHr3s+SMZfKBlmnmkO6g==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "9.0.1", + "contentHash": "2J5uq+2smnj+u1jlyVJ6BGGqaK9fHcK/EwN7mbsuPqTI6dZr86br8Cg6o/5B+icQ9ANTvTDpJjnhDNtYYZijHQ==" + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.IO.Hashing": { + "type": "Transitive", + "resolved": "9.0.1", + "contentHash": "jY+E/PElNWQiazN0YHqZGvcSedcZ4Wt0Os1nnJ2SzR3gWZlhNRDkSXOhuHJcLuImD8SrJQQ8TfU0W4mVcit2hg==" + }, + "System.Management": { + "type": "Transitive", + "resolved": "9.0.1", + "contentHash": "CLEo9O6FuO4GQ3ZQkGssg9CJ2w2TN7GMFf3wHTc7YVWJV4xoyJRPw+XIDQnCcSUJCrHhrAWOO60cAX29EV5LFQ==", + "dependencies": { + "System.CodeDom": "9.0.1" + } + }, + "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.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "8.0.4", + "contentHash": "bAkhgDJ88XTsqczoxEMliSrpijKZHhbJQldhAmObj/RbrN3sU5dcokuXmWJWsdQAhiMJ9bTayWsL1C9fbbCRhw==", + "dependencies": { + "System.Text.Encodings.Web": "8.0.0" + } + }, + "Verify": { + "type": "Transitive", + "resolved": "28.10.1", + "contentHash": "2B/VtFN5jtF5g28kaM4GdJZTwb3pisd4+wL2NEPi9ZYe2lghWsCzS30V6LF1ILApLBfAorAstkU/Vw3sDWRqrg==", + "dependencies": { + "Argon": "0.26.0", + "DiffEngine": "15.9.0", + "SimpleInfoName": "3.1.0", + "System.IO.Hashing": "9.0.1" + } + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.extensibility.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==", + "dependencies": { + "xunit.abstractions": "2.0.3" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]" + } + }, + "speckle.sdk": { + "type": "Project", + "dependencies": { + "GraphQL.Client": "[6.0.0, )", + "Microsoft.CSharp": "[4.7.0, )", + "Microsoft.Data.Sqlite": "[7.0.5, )", + "Microsoft.Extensions.DependencyInjection.Abstractions": "[2.2.0, )", + "Microsoft.Extensions.Logging": "[2.2.0, )", + "Speckle.DoubleNumerics": "[4.1.0, )", + "Speckle.Newtonsoft.Json": "[13.0.2, )", + "Speckle.Sdk.Dependencies": "[1.0.0, )" + } + }, + "speckle.sdk.dependencies": { + "type": "Project" + }, + "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": "[7.0.5, )", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "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.Sdk.Tests.Integration/Api/GraphQL/GraphQLClientExceptionHandling.cs b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/GraphQLClientExceptionHandling.cs new file mode 100644 index 00000000..4f81fd93 --- /dev/null +++ b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/GraphQLClientExceptionHandling.cs @@ -0,0 +1,129 @@ +using System.ComponentModel; +using FluentAssertions; +using GraphQL; +using GraphQL.Client.Http; +using Speckle.Newtonsoft.Json; +using Speckle.Sdk.Api; +using Speckle.Sdk.Api.GraphQL.Inputs; +using Xunit; + +namespace Speckle.Sdk.Tests.Integration.Api.GraphQL; + +public class GraphQLClientExceptionHandling : IAsyncLifetime +{ + private Client _sut; + + public Task DisposeAsync() => Task.CompletedTask; + + public async Task InitializeAsync() + { + _sut = await Fixtures.SeedUserWithClient(); + } + + [Fact] + [Description($"Attempts to execute a query on a non-existent server, expect a {nameof(GraphQLHttpRequestException)}")] + public async Task TestHttpLayer() + { + _sut.GQLClient.Options.EndPoint = new Uri("http://127.0.0.1:1234"); //There is no server on this port... + + await Assert.ThrowsAsync(async () => await _sut.ActiveUser.Get().ConfigureAwait(false)); + } + + [Fact] + [Description( + $"Attempts to execute a admin only command from a regular user, expect an inner {nameof(SpeckleGraphQLForbiddenException)}" + )] + public async Task TestGraphQLLayer_Forbidden() + { + //language=graphql + const string QUERY = """ + query Query { + admin { + userList { + items { + id + } + } + } + } + + """; + GraphQLRequest request = new(query: QUERY); + var ex = await Assert.ThrowsAsync( + async () => await _sut.ExecuteGraphQLRequest(request).ConfigureAwait(false) + ); + ex.InnerExceptions.OfType().Count().Should().Be(1); + } + + [Fact, Description($"Attempts to execute a bad query, expect an inner {nameof(SpeckleGraphQLInvalidQueryException)}")] + public async Task TestGraphQLLayer_BadQuery() + { + //language=graphql + const string QUERY = """ + query User { + data:NonExistentQuery { + id + } + } + """; + GraphQLRequest request = new(query: QUERY); + var ex = await Assert.ThrowsAsync( + async () => await _sut.ExecuteGraphQLRequest(request).ConfigureAwait(false) + ); + ex.InnerExceptions.OfType().Count().Should().Be(1); + } + + [Fact] + [Description( + $"Attempts to execute a query with an invalid input, expect an inner {nameof(SpeckleGraphQLBadInputException)}" + )] + public async Task TestGraphQLLayer_BadInput() + { + ProjectUpdateRoleInput input = new(null!, null!, null); + var ex = await Assert.ThrowsAsync( + async () => await _sut.Project.UpdateRole(input).ConfigureAwait(false) + ); + ex.InnerExceptions.OfType().Count().Should().Be(2); + } + + [Fact] + public async Task TestCancel() + { + using CancellationTokenSource cts = new(); + await cts.CancelAsync(); + + var ex = await Assert.ThrowsAsync( + async () => await _sut.ActiveUser.Get(cts.Token).ConfigureAwait(false) + ); + + ex.CancellationToken.Should().BeEquivalentTo(cts.Token); + } + + [Fact] + public void TestDisposal() + { + _sut.Dispose(); + + Assert.Throws(() => _ = _sut.Subscription.CreateUserProjectsUpdatedSubscription()); + } + + [ + Fact, + Description($"Attempts to execute a query with a mismatched type, expect an {nameof(JsonSerializationException)}") + ] + public async Task TestDeserialization() + { + //language=graphql + const string QUERY = """ + query User { + data:activeUser { + id + } + } + """; + GraphQLRequest request = new(query: QUERY); + await Assert.ThrowsAsync( + async () => await _sut.ExecuteGraphQLRequest(request).ConfigureAwait(false) + ); + } +} diff --git a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Legacy/LegacyAPITests.cs b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Legacy/LegacyAPITests.cs deleted file mode 100644 index 08590e1d..00000000 --- a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Legacy/LegacyAPITests.cs +++ /dev/null @@ -1,511 +0,0 @@ -using Shouldly; -using Speckle.Sdk.Api; -using Speckle.Sdk.Api.GraphQL; -using Speckle.Sdk.Credentials; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; -using Speckle.Sdk.Tests.Unit.Host; -using Speckle.Sdk.Transports; - -namespace Speckle.Sdk.Tests.Integration.Api.GraphQL.Legacy; - -public class LegacyAPITests : IDisposable -{ - [SetUp] - public void Setup() - { - TypeLoader.Reset(); - TypeLoader.Initialize(typeof(Base).Assembly, typeof(Point).Assembly); - } - - private string _branchId = ""; - private string _branchName = ""; - private string _commitId = ""; - - private Account _firstUserAccount, - _secondUserAccount; - - private Client _myClient, - _secondClient; - - private ServerTransport _myServerTransport, - _otherServerTransport; - - private string _objectId = ""; - - private string _streamId = ""; - - [OneTimeSetUp] - public async Task OneTimeSetup() - { - TypeLoader.Reset(); - TypeLoader.Initialize(typeof(Base).Assembly); - _firstUserAccount = await Fixtures.SeedUser(); - _secondUserAccount = await Fixtures.SeedUser(); - - _myClient = new Client(_firstUserAccount); - _secondClient = new Client(_secondUserAccount); - } - - private void InitServerTransport() - { - _myServerTransport = new ServerTransport(_firstUserAccount, _streamId); - _myServerTransport.Api.CompressPayloads = false; - _otherServerTransport = new ServerTransport(_firstUserAccount, _streamId); - _otherServerTransport.Api.CompressPayloads = false; - } - - [Test] - public async Task ActiveUserGet() - { - var res = await _myClient.ActiveUserGet(); - Assert.That(res!.id, Is.EqualTo(_myClient.Account.userInfo.id)); - } - - [Test] - public async Task OtherUserGet() - { - var res = await _myClient.OtherUserGet(_secondUserAccount.userInfo.id); - res.ShouldNotBeNull(); - Assert.That(res.name, Is.EqualTo(_secondUserAccount.userInfo.name)); - } - - [Test] - public async Task UserSearch() - { - var res = await _myClient.UserSearch(_firstUserAccount.userInfo.email); - Assert.That(res, Has.Count.EqualTo(1)); - Assert.That(res[0].id, Is.EqualTo(_firstUserAccount.userInfo.id)); - } - - [Test] - public async Task ServerVersion() - { - var res = await _myClient.GetServerVersion(); - - Assert.That(res, Is.Not.Null); - } - - [Test, Order(0)] - public async Task StreamCreate() - { - var res = await _myClient.StreamCreate( - new StreamCreateInput { description = "Hello World", name = "Super Stream 01" } - ); - - Assert.That(res, Is.Not.Null); - _streamId = res; - InitServerTransport(); - } - - [Test, Order(10)] - public async Task StreamsGet() - { - var res = await _myClient.StreamsGet(); - - Assert.That(res, Is.Not.Null); - } - - [Test, Order(11)] - public async Task StreamGet() - { - var res = await _myClient.StreamGet(_streamId); - - Assert.That(res, Is.Not.Null); - Assert.That(res.branches.items[0].name, Is.EqualTo("main")); - Assert.That(res.collaborators, Is.Not.Empty); - } - - [Test, Order(12)] - public async Task IsStreamAccessible() - { - var res = await _myClient.IsStreamAccessible(_streamId); - - Assert.That(res, Is.True); - } - - [Test, Order(13)] - public async Task StreamSearch() - { - var res = await _myClient.StreamSearch(_streamId); - - Assert.That(res, Is.Not.Null); - } - - [Test, Order(20)] - public async Task StreamUpdate() - { - var res = await _myClient.StreamUpdate( - new StreamUpdateInput - { - id = _streamId, - description = "Hello World", - name = "Super Stream 01 EDITED" - } - ); - - Assert.That(res, Is.True); - } - - [Test, Order(31)] - public async Task StreamInviteCreate() - { - var res = await _myClient.StreamInviteCreate( - new StreamInviteCreateInput - { - streamId = _streamId, - email = _secondUserAccount.userInfo.email, - message = "Whasssup!" - } - ); - - Assert.That(res, Is.True); - - Assert.ThrowsAsync( - async () => await _myClient.StreamInviteCreate(new StreamInviteCreateInput { streamId = _streamId }) - ); - } - - [Test, Order(32)] - public async Task StreamInviteGet() - { - var invites = await _secondClient.GetAllPendingInvites(); - - Assert.That(invites, Is.Not.Null); - } - - [Test, Order(33)] - public async Task StreamInviteUse() - { - var invites = await _secondClient.GetAllPendingInvites(); - - var res = await _secondClient.StreamInviteUse(invites[0].streamId, invites[0].token); - - Assert.That(res, Is.True); - } - - [Test, Order(34)] - public async Task StreamUpdatePermission() - { - var res = await _myClient.StreamUpdatePermission( - new StreamPermissionInput - { - role = StreamRoles.STREAM_REVIEWER, - streamId = _streamId, - userId = _secondUserAccount.userInfo.id - } - ); - - Assert.That(res, Is.True); - } - - [Test, Order(40)] - public async Task StreamRevokePermission() - { - var res = await _myClient.StreamRevokePermission( - new StreamRevokePermissionInput { streamId = _streamId, userId = _secondUserAccount.userInfo.id } - ); - - Assert.That(res, Is.True); - } - - #region activity - - [Test, Order(51)] - public async Task StreamGetActivity() - { - var res = await _myClient.StreamGetActivity(_streamId); - - Assert.That(res, Is.Not.Null); - //Assert.AreEqual(commitId, res[0].); - } - - #endregion - - #region comments - - [Test, Order(52)] - public async Task StreamGetComments() - { - var res = await _myClient.StreamGetActivity(_streamId); - - Assert.That(res, Is.Not.Null); - //Assert.AreEqual(commitId, res[0].); - } - - #endregion - - [Test, Order(60)] - public async Task StreamDelete() - { - var res = await _myClient.StreamDelete(_streamId); - Assert.That(res, Is.True); - } - - #region branches - - [Test, Order(41)] - public async Task BranchCreate() - { - var res = await _myClient.BranchCreate( - new BranchCreateInput - { - streamId = _streamId, - description = "this is a sample branch", - name = "sample-branch" - } - ); - Assert.That(res, Is.Not.Null); - _branchId = res; - _branchName = "sample-branch"; - } - - [Test, Order(42)] - public async Task BranchGet() - { - var res = await _myClient.BranchGet(_streamId, _branchName); - - Assert.That(res, Is.Not.Null); - Assert.That(res.description, Is.EqualTo("this is a sample branch")); - } - - [Test, Order(43)] - public async Task StreamGetBranches() - { - var res = await _myClient.StreamGetBranches(_streamId); - - Assert.That(res, Is.Not.Null); - // Branches are now returned in order of creation so 'main' should always go first. - Assert.That(res[0].name, Is.EqualTo("main")); - } - - [Test, Order(51)] - public async Task StreamGetBranches_Throws_WhenRequestingOverLimit() - { - Assert.ThrowsAsync>( - async () => await _myClient.StreamGetBranches(_streamId, ServerLimits.BRANCH_GET_LIMIT + 1) - ); - var res = await _myClient.StreamGetBranches(_streamId, ServerLimits.BRANCH_GET_LIMIT); - - Assert.That(res, Is.Not.Null); - } - - [Test, Order(52)] - public async Task StreamGetBranches_WithManyBranches() - { - var newStreamId = await _myClient.StreamCreate(new StreamCreateInput { name = "Many branches stream" }); - - await CreateEmptyBranches(_myClient, newStreamId, ServerLimits.BRANCH_GET_LIMIT); - - var res = await _myClient.StreamGetBranches(newStreamId, ServerLimits.BRANCH_GET_LIMIT); - - Assert.That(res, Is.Not.Null); - Assert.That(res, Has.Count.EqualTo(ServerLimits.BRANCH_GET_LIMIT)); - } - - private async Task CreateEmptyBranches( - Client client, - string streamId, - int branchCount, - string branchPrefix = "Test branch" - ) - { - // now let's send HTTP requests to each of these URLs in parallel - var options = new ParallelOptions { MaxDegreeOfParallelism = 2 }; - - // now let's send HTTP requests to each of these URLs in parallel - await Parallel.ForEachAsync( - Enumerable.Range(0, branchCount), - options, - async (i, cancellationToken) => - { - await client.BranchCreate( - new BranchCreateInput { name = $"{branchPrefix} {i}", streamId = streamId }, - cancellationToken - ); - } - ); - } - - #region commit - - [Test, Order(43)] - public async Task CommitCreate() - { - var myObject = new Base(); - var ptsList = new List(); - for (int i = 0; i < 100; i++) - { - ptsList.Add(new Point(i, i, i)); - } - - myObject["@Points"] = ptsList; - - (_objectId, _) = await Operations.Send(myObject, new List { _myServerTransport }); - - Assert.That(_objectId, Is.Not.Null); - - var res = await _myClient.CommitCreate( - new CommitCreateInput - { - streamId = _streamId, - branchName = _branchName, - objectId = _objectId, - message = "Fibber Fibbo", - sourceApplication = "Tests", - totalChildrenCount = 100 - } - ); - - Assert.That(res, Is.Not.Null); - _commitId = res; - - var res2 = await _myClient.CommitCreate( - new CommitCreateInput - { - streamId = _streamId, - branchName = _branchName, - objectId = _objectId, - message = "Fabber Fabbo", - sourceApplication = "Tests", - totalChildrenCount = 100, - parents = new List { _commitId } - } - ); - - Assert.That(res2, Is.Not.Null); - _commitId = res2; - } - - [Test, Order(44)] - public async Task CommitGet() - { - var res = await _myClient.CommitGet(_streamId, _commitId); - - Assert.That(res, Is.Not.Null); - Assert.That(res.message, Is.EqualTo("Fabber Fabbo")); - } - - [Test, Order(45)] - public async Task StreamGetCommits() - { - var res = await _myClient.StreamGetCommits(_streamId); - - Assert.That(res, Is.Not.Null); - Assert.That(res[0].id, Is.EqualTo(_commitId)); - } - - #region object - - [Test, Order(45)] - public async Task ObjectGet() - { - var res = await _myClient.ObjectGet(_streamId, _objectId); - - Assert.That(res, Is.Not.Null); - Assert.That(res.totalChildrenCount, Is.EqualTo(100)); - } - - #endregion - - [Test, Order(46)] - public async Task CommitUpdate() - { - var res = await _myClient.CommitUpdate( - new CommitUpdateInput - { - streamId = _streamId, - id = _commitId, - message = "DIM IS DA BEST" - } - ); - - Assert.That(res, Is.True); - } - - [Test, Order(47)] - public async Task CommitReceived() - { - var res = await _myClient.CommitReceived( - new CommitReceivedInput - { - commitId = _commitId, - streamId = _streamId, - sourceApplication = "sharp-tests", - message = "The test message" - } - ); - - Assert.That(res, Is.True); - } - - [Test, Order(48)] - public async Task CommitDelete() - { - var res = await _myClient.CommitDelete(new CommitDeleteInput { id = _commitId, streamId = _streamId }); - Assert.That(res, Is.True); - } - - #endregion - - - [Test, Order(49)] - public async Task BranchUpdate() - { - var res = await _myClient.BranchUpdate( - new BranchUpdateInput - { - streamId = _streamId, - id = _branchId, - name = "sample-branch EDITED" - } - ); - - Assert.That(res, Is.True); - } - - [Test, Order(50)] - public async Task BranchDelete() - { - var res = await _myClient.BranchDelete(new BranchDeleteInput { id = _branchId, streamId = _streamId }); - Assert.That(res, Is.True); - } - - #endregion - - #region send/receive bare - - //[Test, Order(60)] - //public async Task SendDetached() - //{ - // var myObject = new Base(); - // var ptsList = new List(); - // for (int i = 0; i < 100; i++) - // ptsList.Add(new Point(i, i, i)); - - // myObject["@Points"] = ptsList; - - // var otherTransport = new ServerTransport(firstUserAccount, null); - // otherTransport.StreamId = - - // objectId = await Operations.Send(myObject, new List() { myServerTransport }, disposeTransports: true); - //} - - //[Test, Order(61)] - //public async Task ReceiveAndCompose() - //{ - // var myObject = await Operations.Receive(objectId, myServerTransport); - // Assert.NotNull(myObject); - // Assert.AreEqual(100, ((List)myObject["@Points"]).Count); - //} - - #endregion - - public void Dispose() - { - _myClient?.Dispose(); - _secondClient?.Dispose(); - _myServerTransport?.Dispose(); - _otherServerTransport?.Dispose(); - } -} diff --git a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Branches.cs b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Branches.cs deleted file mode 100644 index 00fd0c68..00000000 --- a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Branches.cs +++ /dev/null @@ -1,131 +0,0 @@ -using Speckle.Sdk.Api; -using Speckle.Sdk.Credentials; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; -using Speckle.Sdk.Tests.Unit.Host; - -namespace Speckle.Sdk.Tests.Integration.Api.GraphQL.Legacy.Subscriptions; - -public class Branches : IDisposable -{ - private BranchInfo _branchCreatedInfo; - private BranchInfo _branchDeletedInfo; - private string _branchId; - private BranchInfo _branchUpdatedInfo; - private Client _client; - private string _streamId; - private Account _testUserAccount; - - [SetUp] - public void Setup() - { - TypeLoader.Reset(); - TypeLoader.Initialize(typeof(Base).Assembly, typeof(Point).Assembly); - } - - [OneTimeSetUp] - public async Task OneTimeSetUp() - { - _testUserAccount = await Fixtures.SeedUser(); - _client = new Client(_testUserAccount); - } - - [Test, Order(0)] - public async Task SubscribeBranchCreated() - { - var streamInput = new StreamCreateInput { description = "Hello World", name = "Super Stream 01" }; - - _streamId = await _client.StreamCreate(streamInput); - Assert.That(_streamId, Is.Not.Null); - - _client.SubscribeBranchCreated(_streamId); - _client.OnBranchCreated += Client_OnBranchCreated; - - Thread.Sleep(5000); //let server catch-up - - var branchInput = new BranchCreateInput - { - description = "Just testing branch create...", - name = "awesome-features", - streamId = _streamId - }; - - _branchId = await _client.BranchCreate(branchInput); - Assert.That(_branchId, Is.Not.Null); - - await Task.Run(() => - { - Thread.Sleep(1000); //let client catch-up - Assert.That(_branchCreatedInfo, Is.Not.Null); - Assert.That(_branchCreatedInfo.name, Is.EqualTo(branchInput.name)); - }); - } - - private void Client_OnBranchCreated(object sender, BranchInfo e) - { - _branchCreatedInfo = e; - } - - [Test, Order(1)] - public async Task SubscribeBranchUpdated() - { - _client.SubscribeBranchUpdated(_streamId); - _client.OnBranchUpdated += Client_OnBranchUpdated; - - Thread.Sleep(1000); //let server catch-up - - var branchInput = new BranchUpdateInput - { - description = "Just testing branch bpdate...", - name = "cool-features", - streamId = _streamId, - id = _branchId - }; - - var res = await _client.BranchUpdate(branchInput); - Assert.That(res, Is.True); - - await Task.Run(() => - { - Thread.Sleep(1000); //let client catch-up - Assert.That(_branchUpdatedInfo, Is.Not.Null); - Assert.That(_branchUpdatedInfo.name, Is.EqualTo(branchInput.name)); - }); - } - - private void Client_OnBranchUpdated(object sender, BranchInfo e) - { - _branchUpdatedInfo = e; - } - - [Test, Order(3)] - public async Task SubscribeBranchDeleted() - { - _client.SubscribeBranchDeleted(_streamId); - _client.OnBranchDeleted += Client_OnBranchDeleted; - - Thread.Sleep(1000); //let server catch-up - - var branchInput = new BranchDeleteInput { streamId = _streamId, id = _branchId }; - - var res = await _client.BranchDelete(branchInput); - Assert.That(res, Is.True); - - await Task.Run(() => - { - Thread.Sleep(1000); //let client catch-up - Assert.That(_branchDeletedInfo, Is.Not.Null); - Assert.That(_branchDeletedInfo.id, Is.EqualTo(_branchId)); - }); - } - - private void Client_OnBranchDeleted(object sender, BranchInfo e) - { - _branchDeletedInfo = e; - } - - public void Dispose() - { - _client?.Dispose(); - } -} diff --git a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Commits.cs b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Commits.cs deleted file mode 100644 index f0981829..00000000 --- a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Commits.cs +++ /dev/null @@ -1,168 +0,0 @@ -using Speckle.Sdk.Api; -using Speckle.Sdk.Credentials; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; -using Speckle.Sdk.Tests.Unit.Host; -using Speckle.Sdk.Transports; - -namespace Speckle.Sdk.Tests.Integration.Api.GraphQL.Legacy.Subscriptions; - -public class Commits : IDisposable -{ - private Client _client; - private CommitInfo _commitCreatedInfo; - private CommitInfo _commitDeletedInfo; - private string _commitId; - private CommitInfo _commitUpdatedInfo; - private ServerTransport _myServerTransport; - private string _streamId; - private Account _testUserAccount; - - [SetUp] - public void Setup() - { - TypeLoader.Reset(); - TypeLoader.Initialize(typeof(Base).Assembly, typeof(Point).Assembly); - } - - [OneTimeSetUp] - public async Task OneTimeSetUp() - { - _testUserAccount = await Fixtures.SeedUser(); - _client = new Client(_testUserAccount); - } - - private void InitServerTransport() - { - _myServerTransport = new ServerTransport(_testUserAccount, _streamId); - _myServerTransport.Api.CompressPayloads = false; - } - - [Test, Order(0)] - //[Ignore("Ironically, it fails.")] - public async Task SubscribeCommitCreated() - { - var streamInput = new StreamCreateInput { description = "Hello World", name = "Super Stream 01" }; - - _streamId = await _client.StreamCreate(streamInput); - Assert.That(_streamId, Is.Not.Null); - - InitServerTransport(); - - var branchInput = new BranchCreateInput - { - description = "Just testing branch create...", - name = "awesome-features", - streamId = _streamId - }; - - var branchId = await _client.BranchCreate(branchInput); - Assert.That(branchId, Is.Not.Null); - - _client.SubscribeCommitCreated(_streamId); - _client.OnCommitCreated += Client_OnCommitCreated; - - Thread.Sleep(1000); //let server catch-up - - var myObject = new Base(); - var ptsList = new List(); - for (int i = 0; i < 100; i++) - { - ptsList.Add(new Point(i, i, i)); - } - - myObject["Points"] = ptsList; - - var sendResult = await Operations.Send(myObject, _myServerTransport, false); - - var commitInput = new CommitCreateInput - { - streamId = _streamId, - branchName = "awesome-features", - objectId = sendResult.rootObjId, - message = "sending some test points", - sourceApplication = "Tests", - totalChildrenCount = 20 - }; - - _commitId = await _client.CommitCreate(commitInput); - Assert.That(_commitId, Is.Not.Null); - - await Task.Run(() => - { - Thread.Sleep(2000); //let client catch-up - Assert.That(_commitCreatedInfo, Is.Not.Null); - Assert.That(_commitCreatedInfo.message, Is.EqualTo(commitInput.message)); - }); - } - - private void Client_OnCommitCreated(object sender, CommitInfo e) - { - _commitCreatedInfo = e; - } - - [Test, Order(1)] - //[Ignore("Ironically, it fails.")] - public async Task SubscribeCommitUpdated() - { - _client.SubscribeCommitUpdated(_streamId); - _client.OnCommitUpdated += Client_OnCommitUpdated; - - Thread.Sleep(1000); //let server catch-up - - var commitInput = new CommitUpdateInput - { - message = "Just testing commit update...", - streamId = _streamId, - id = _commitId - }; - - var res = await _client.CommitUpdate(commitInput); - Assert.That(res, Is.True); - - await Task.Run(() => - { - Thread.Sleep(2000); //let client catch-up - Assert.That(_commitUpdatedInfo, Is.Not.Null); - Assert.That(_commitUpdatedInfo.message, Is.EqualTo(commitInput.message)); - }); - } - - private void Client_OnCommitUpdated(object sender, CommitInfo e) - { - _commitUpdatedInfo = e; - } - - [Test, Order(3)] - //[Ignore("Ironically, it fails.")] - public async Task SubscribeCommitDeleted() - { - _client.SubscribeCommitDeleted(_streamId); - _client.OnCommitDeleted += Client_OnCommitDeleted; - - Thread.Sleep(1000); //let server catch-up - - var commitInput = new CommitDeleteInput { streamId = _streamId, id = _commitId }; - - var res = await _client.CommitDelete(commitInput); - Assert.That(res, Is.True); - - await Task.Run(() => - { - Thread.Sleep(2000); //let client catch-up - Assert.That(_commitDeletedInfo, Is.Not.Null); - Assert.That(_commitDeletedInfo.id, Is.EqualTo(_commitId)); - }); - } - - private void Client_OnCommitDeleted(object sender, CommitInfo e) - { - _commitDeletedInfo = e; - } - - public void Dispose() - { - _client?.Dispose(); - _myServerTransport?.Dispose(); - } -} diff --git a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Streams.cs b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Streams.cs deleted file mode 100644 index bec4bbee..00000000 --- a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Legacy/Subscriptions/Streams.cs +++ /dev/null @@ -1,122 +0,0 @@ -using Speckle.Sdk.Api; -using Speckle.Sdk.Credentials; -using Speckle.Sdk.Host; -using Speckle.Sdk.Models; -using Speckle.Sdk.Tests.Unit.Host; - -namespace Speckle.Sdk.Tests.Integration.Api.GraphQL.Legacy.Subscriptions; - -public class Streams : IDisposable -{ - private Client _client; - - private StreamInfo _streamAddedInfo; - private string _streamId; - private StreamInfo _streamRemovedInfo; - private StreamInfo _streamUpdatedInfo; - private Account _testUserAccount; - - [SetUp] - public void Setup() - { - TypeLoader.Reset(); - TypeLoader.Initialize(typeof(Base).Assembly, typeof(Point).Assembly); - } - - [OneTimeSetUp] - public async Task OneTimeSetUp() - { - _testUserAccount = await Fixtures.SeedUser(); - _client = new Client(_testUserAccount); - } - - [Test, Order(0)] - public async Task SubscribeStreamAdded() - { - _client.SubscribeUserStreamAdded(); - _client.OnUserStreamAdded += Client_OnUserStreamAdded; - - Thread.Sleep(1000); //let server catch-up - - var streamInput = new StreamCreateInput { description = "Hello World", name = "Super Stream 01" }; - - var res = await _client.StreamCreate(streamInput); - _streamId = res; - Assert.That(res, Is.Not.Null); - - await Task.Run(() => - { - Thread.Sleep(1000); //let client catch-up - Assert.That(_streamAddedInfo, Is.Not.Null); - Assert.That(_streamAddedInfo.name, Is.EqualTo(streamInput.name)); - }); - } - - private void Client_OnUserStreamAdded(object sender, StreamInfo e) - { - _streamAddedInfo = e; - } - - [Test, Order(1)] - public async Task SubscribeStreamUpdated() - { - _client.SubscribeStreamUpdated(_streamId); - _client.OnStreamUpdated += Client_OnStreamUpdated; - - Thread.Sleep(100); //let server catch-up - - var streamInput = new StreamUpdateInput - { - id = _streamId, - description = "Hello World", - name = "Super Stream 01 EDITED" - }; - - var res = await _client.StreamUpdate(streamInput); - - Assert.That(res, Is.True); - - await Task.Run(() => - { - Thread.Sleep(100); //let client catch-up - Assert.That(_streamUpdatedInfo, Is.Not.Null); - Assert.That(_streamUpdatedInfo.name, Is.EqualTo(streamInput.name)); - }); - } - - private void Client_OnStreamUpdated(object sender, StreamInfo e) - { - _streamUpdatedInfo = e; - } - - [Test, Order(2)] - public async Task SubscribeUserStreamRemoved() - { - _client.SubscribeUserStreamRemoved(); - _client.OnUserStreamRemoved += Client_OnStreamRemoved; - ; - - Thread.Sleep(100); //let server catch-up - - var res = await _client.StreamDelete(_streamId); - - Assert.That(res, Is.True); - - await Task.Run(() => - { - Thread.Sleep(100); //let client catch-up - Assert.That(_streamRemovedInfo, Is.Not.Null); - Assert.That(_streamRemovedInfo.id, Is.EqualTo(_streamId)); - }); - } - - private void Client_OnStreamRemoved(object sender, StreamInfo e) - { - _streamRemovedInfo = e; - } - - public void Dispose() - { - _client?.Dispose(); - } -} diff --git a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ActiveUserResourceTests.cs b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ActiveUserResourceTests.cs index 4ae7c597..f4f99370 100644 --- a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ActiveUserResourceTests.cs +++ b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ActiveUserResourceTests.cs @@ -1,37 +1,60 @@ -using Speckle.Sdk.Api; -using Speckle.Sdk.Api.GraphQL.Models; +using FluentAssertions; +using Speckle.Sdk.Api; +using Speckle.Sdk.Api.GraphQL.Inputs; using Speckle.Sdk.Api.GraphQL.Resources; +using Xunit; namespace Speckle.Sdk.Tests.Integration.API.GraphQL.Resources; -[TestOf(typeof(ActiveUserResource))] -public class ActiveUserResourceTests +public class ActiveUserResourceTests : IAsyncLifetime { private Client _testUser; private ActiveUserResource Sut => _testUser.ActiveUser; - [OneTimeSetUp] - public async Task Setup() + // Setup method for xUnit using IAsyncLifetime + public async Task InitializeAsync() { _testUser = await Fixtures.SeedUserWithClient(); } - [Test] + public Task DisposeAsync() + { + // No resources to dispose + return Task.CompletedTask; + } + + [Fact] 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)); + res.Should().NotBeNull(); + res!.id.Should().Be(_testUser.Account.userInfo.id); } - [Test] + [Fact] public async Task ActiveUserGet_NonAuthed() { var result = await Fixtures.Unauthed.ActiveUser.Get(); - Assert.That(result, Is.EqualTo(null)); + result.Should().BeNull(); } - [Test] + [Fact] + public async Task ActiveUserUpdate() + { + const string NEW_NAME = "Ron"; + const string NEW_BIO = "Now I have a bio, isn't that nice!"; + const string NEW_COMPANY = "Limited Cooperation Organization Inc"; + + var res = await Sut.Update(new UserUpdateInput(name: NEW_NAME, bio: NEW_BIO, company: NEW_COMPANY)); + + res.Should().NotBeNull(); + res.id.Should().Be(_testUser.Account.userInfo.id); + res.name.Should().Be(NEW_NAME); + res.company.Should().Be(NEW_COMPANY); + res.bio.Should().Be(NEW_BIO); + } + + [Fact] public async Task ActiveUserGetProjects() { var p1 = await _testUser.Project.Create(new("Project 1", null, null)); @@ -39,14 +62,17 @@ public class ActiveUserResourceTests 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)); + res.items.Should().Contain(x => x.id == p1.id); + res.items.Should().Contain(x => x.id == p2.id); + res.items.Count.Should().Be(2); } - [Test] - public void ActiveUserGetProjects_NoAuth() + [Fact] + public async Task ActiveUserGetProjects_NoAuth() { - Assert.ThrowsAsync(async () => await Fixtures.Unauthed.ActiveUser.GetProjects()); + await FluentActions + .Invoking(async () => await Fixtures.Unauthed.ActiveUser.GetProjects()) + .Should() + .ThrowAsync(); } } diff --git a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/CommentResourceTests.cs b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/CommentResourceTests.cs index 8399fb99..80611b01 100644 --- a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/CommentResourceTests.cs +++ b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/CommentResourceTests.cs @@ -1,97 +1,122 @@ -using Speckle.Sdk.Api; +using FluentAssertions; +using Speckle.Sdk.Api; using Speckle.Sdk.Api.GraphQL.Inputs; using Speckle.Sdk.Api.GraphQL.Models; using Speckle.Sdk.Api.GraphQL.Resources; +using Speckle.Sdk.Common; +using Xunit; +using Version = Speckle.Sdk.Api.GraphQL.Models.Version; namespace Speckle.Sdk.Tests.Integration.API.GraphQL.Resources; -[TestOf(typeof(CommentResource))] -public class CommentResourceTests +public class CommentResourceTests : IAsyncLifetime { private Client _testUser; - private CommentResource Sut => _testUser.Comment; + private CommentResource Sut; private Project _project; private Model _model; - private string _versionId; + private Version _version; private Comment _comment; - [SetUp] - public async Task Setup() + // Constructor for setup + public async Task InitializeAsync() { + // Synchronous operations converted to async Task.Run for constructor _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.id); + _version = await Fixtures.CreateVersion(_testUser, _project.id, _model.id); _comment = await CreateComment(); + Sut = _testUser.Comment; } - [Test] + public Task DisposeAsync() + { + // No resources to dispose + return Task.CompletedTask; + } + + [Fact] + public async Task Get() + { + var comment = await Sut.Get(_comment.id, _project.id); + + comment.Should().NotBeNull(); + comment.id.Should().Be(_comment.id); + comment.authorId.Should().Be(_testUser.Account.userInfo.id); + } + + [Fact] 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)); + + comments.Should().NotBeNull(); + comments.items.Count.Should().Be(1); + comments.totalCount.Should().Be(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)); + comment.Should().NotBeNull(); + comment.authorId.Should().Be(_testUser.Account.userInfo.id); + comment.id.Should().Be(_comment.id); + comment.authorId.Should().Be(_comment.authorId); + comment.archived.Should().Be(false); + comment.createdAt.Should().Be(_comment.createdAt); } - [Test] + [Fact] 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); + await Sut.MarkViewed(new(_comment.id, _project.id)); + + var res = await Sut.Get(_comment.id, _project.id); + res.viewedAt.Should().NotBeNull(); } - [Test] + [Fact] public async Task Archive() { - var archived = await Sut.Archive(_comment.id); - Assert.That(archived, Is.True); + await Sut.Archive(new(_comment.id, _project.id, true)); + var archived = await Sut.Get(_comment.id, _project.id); - archived = await Sut.Archive(_comment.id); - Assert.That(archived, Is.True); + archived.archived.Should().BeTrue(); + + await Sut.Archive(new(_comment.id, _project.id, false)); + var unarchived = await Sut.Get(_comment.id, _project.id); + + unarchived.archived.Should().BeFalse(); } - [Test] + [Fact] 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 blobIds = blobs.Select(b => b.id.NotNull()).ToList(); + var input = new EditCommentInput(new(blobIds, null), _comment.id, _project.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)); + editedComment.Should().NotBeNull(); + editedComment.id.Should().Be(_comment.id); + editedComment.authorId.Should().Be(_comment.authorId); + editedComment.createdAt.Should().Be(_comment.createdAt); + editedComment.updatedAt.Should().BeOnOrAfter(_comment.updatedAt); } - [Test] + [Fact] 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 blobIds = blobs.Select(b => b.id.NotNull()).ToList(); + var input = new CreateCommentReplyInput(new(blobIds, null), _comment.id, _project.id); var editedComment = await Sut.Reply(input); - Assert.That(editedComment, Is.Not.Null); + editedComment.Should().NotBeNull(); } private async Task CreateComment() { - return await Fixtures.CreateComment(_testUser, _project.id, _model.id, _versionId); + return await Fixtures.CreateComment(_testUser, _project.id, _model.id, _version.id); } } diff --git a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ModelResourceExceptionalTests.cs b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ModelResourceExceptionalTests.cs index 4025845e..91c995fd 100644 --- a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ModelResourceExceptionalTests.cs +++ b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ModelResourceExceptionalTests.cs @@ -1,88 +1,137 @@ -using Speckle.Sdk.Api; +using FluentAssertions; +using Speckle.Sdk.Api; using Speckle.Sdk.Api.GraphQL.Enums; using Speckle.Sdk.Api.GraphQL.Inputs; using Speckle.Sdk.Api.GraphQL.Models; using Speckle.Sdk.Api.GraphQL.Resources; +using Xunit; namespace Speckle.Sdk.Tests.Integration.API.GraphQL.Resources; -[TestOf(typeof(ModelResource))] -public class ModelResourceExceptionalTests +public class ModelResourceExceptionalTests : IAsyncLifetime { private Client _testUser; private ModelResource Sut => _testUser.Model; private Project _project; private Model _model; - [OneTimeSetUp] - public async Task Setup() + // Replaces NUnit's OneTimeSetUp with an async constructor logic or initializer pattern + public async Task InitializeAsync() { _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) + public Task DisposeAsync() => Task.CompletedTask; + + [Theory] + [InlineData("")] + [InlineData(" ")] + public async Task ModelCreate_Throws_InvalidInput(string name) { + // Arrange CreateModelInput input = new(name, null, _project.id); - Assert.CatchAsync(async () => await Sut.Create(input)); + + // Act & Assert + var ex = await FluentActions + .Invoking(async () => await Sut.Create(input)) + .Should() + .ThrowAsync(); + + ex.WithInnerExceptionExactly(); } - [Test] - public void ModelGet_Throws_NoAuth() + [Fact] + public async Task ModelGet_Throws_NoAuth() { - Assert.CatchAsync(async () => await Fixtures.Unauthed.Model.Get(_model.id, _project.id)); + // Act & Assert + var ex = await FluentActions + .Invoking(async () => await Fixtures.Unauthed.Model.Get(_model.id, _project.id)) + .Should() + .ThrowAsync(); + + ex.WithInnerExceptionExactly(); } - [Test] - public void ModelGet_Throws_NonExistentModel() + [Fact] + public async Task ModelGet_Throws_NonExistentModel() { - Assert.CatchAsync(async () => await Sut.Get("non existent model", _project.id)); + // Act & Assert + var ex = await FluentActions + .Invoking(async () => await Sut.Get("non existent model", _project.id)) + .Should() + .ThrowAsync(); + ex.WithInnerExceptionExactly(); } - [Test] - public void ModelGet_Throws_NonExistentProject() + [Fact] + public async Task ModelGet_Throws_NonExistentProject() { - Assert.ThrowsAsync( - async () => await Sut.Get(_model.id, "non existent project") - ); + // Act & Assert + var ex = await FluentActions + .Invoking(async () => await Sut.Get(_model.id, "non existent project")) + .Should() + .ThrowAsync(); + ex.WithInnerExceptionExactly(); } - [Test] - public void ModelUpdate_Throws_NonExistentModel() + [Fact] + public async Task ModelUpdate_Throws_NonExistentModel() { + // Arrange UpdateModelInput input = new("non-existent model", "MY new name", "MY new desc", _project.id); - Assert.CatchAsync(async () => await Sut.Update(input)); + // Act & Assert + var ex = await FluentActions + .Invoking(async () => await Sut.Update(input)) + .Should() + .ThrowAsync(); + ex.WithInnerExceptionExactly(); } - [Test] - public void ModelUpdate_Throws_NonExistentProject() + [Fact] + public async Task ModelUpdate_Throws_NonExistentProject() { + // Arrange UpdateModelInput input = new(_model.id, "MY new name", "MY new desc", "non-existent project"); - Assert.ThrowsAsync(async () => await Sut.Update(input)); + // Act & Assert + var ex = await FluentActions + .Invoking(async () => await Sut.Update(input)) + .Should() + .ThrowAsync(); + ex.WithInnerExceptionExactly(); } - [Test] - public void ModelUpdate_Throws_NonAuthProject() + [Fact] + public async Task ModelUpdate_Throws_NonAuthProject() { + // Arrange UpdateModelInput input = new(_model.id, "MY new name", "MY new desc", _project.id); - Assert.CatchAsync(async () => await Fixtures.Unauthed.Model.Update(input)); + // Act & Assert + var ex = await FluentActions + .Invoking(async () => await Fixtures.Unauthed.Model.Update(input)) + .Should() + .ThrowAsync(); + ex.WithInnerExceptionExactly(); } - [Test] + [Fact] public async Task ModelDelete_Throws_NoAuth() { + // Arrange 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)); + await Sut.Delete(input); + + // Act & Assert + var ex = await FluentActions + .Invoking(async () => await Sut.Delete(input)) + .Should() + .ThrowAsync(); + ex.WithInnerExceptionExactly(); } } diff --git a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ModelResourceTests.cs b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ModelResourceTests.cs index 814e4bd8..19051efb 100644 --- a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ModelResourceTests.cs +++ b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ModelResourceTests.cs @@ -1,96 +1,127 @@ -using Speckle.Sdk.Api; +using FluentAssertions; +using Speckle.Sdk.Api; using Speckle.Sdk.Api.GraphQL.Inputs; using Speckle.Sdk.Api.GraphQL.Models; using Speckle.Sdk.Api.GraphQL.Resources; +using Xunit; namespace Speckle.Sdk.Tests.Integration.API.GraphQL.Resources; -[TestOf(typeof(ModelResource))] -public class ModelResourceTests +public class ModelResourceTests : IAsyncLifetime { private Client _testUser; private ModelResource Sut => _testUser.Model; private Project _project; private Model _model; - [SetUp] - public async Task Setup() + public async Task InitializeAsync() { + // Runs instead of [SetUp] in NUnit _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) + public Task DisposeAsync() { + // Perform any cleanup, if needed + return Task.CompletedTask; + } + + [Theory] + [InlineData("My Model", "My model description")] + [InlineData("my/nested/model", null)] + public async Task ModelCreate(string name, string? description) + { + // Arrange CreateModelInput input = new(name, description, _project.id); + + // Act 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)); + // Assert + result.Should().NotBeNull(); + result.id.Should().NotBeNull(); + result.name.Should().ContainEquivalentOf(input.name); + result.description.Should().Be(input.description); } - [Test] + [Fact] public async Task ModelGet() { + // Act 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)); + // Assert + result.id.Should().Be(_model.id); + result.name.Should().Be(_model.name); + result.description.Should().Be(_model.description); + result.createdAt.Should().Be(_model.createdAt); + result.updatedAt.Should().Be(_model.updatedAt); } - [Test] + [Fact] public async Task GetModels() { + // Act 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)); + // Assert + result.items.Count.Should().Be(1); + result.totalCount.Should().Be(1); + result.items[0].id.Should().Be(_model.id); } - [Test] + [Fact] public async Task Project_GetModels() { + // Act 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)); + // Assert + result.id.Should().Be(_project.id); + result.models.items.Count.Should().Be(1); + result.models.totalCount.Should().Be(1); + result.models.items[0].id.Should().Be(_model.id); } - [Test] + [Fact] public async Task ModelUpdate() { + // Arrange 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); + var input = new UpdateModelInput(_model.id, NEW_NAME, NEW_DESCRIPTION, _project.id); + + // Act 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)); + // Assert + updatedModel.id.Should().Be(_model.id); + updatedModel.name.Should().ContainEquivalentOf(NEW_NAME); + updatedModel.description.Should().Be(NEW_DESCRIPTION); + updatedModel.updatedAt.Should().BeOnOrAfter(_model.updatedAt); } - [Test] + [Fact] public async Task ModelDelete() { - DeleteModelInput input = new(_model.id, _project.id); + // Arrange + var input = new DeleteModelInput(_model.id, _project.id); - bool response = await Sut.Delete(input); - Assert.That(response, Is.True); + // Act + await Sut.Delete(input); - Assert.CatchAsync(async () => _ = await Sut.Get(_model.id, _project.id)); - Assert.CatchAsync(async () => _ = await Sut.Delete(input)); + // Assert: Ensure fetching the deleted model throws an exception + var getEx = await FluentActions + .Invoking(() => Sut.Get(_model.id, _project.id)) + .Should() + .ThrowAsync(); + getEx.WithInnerExceptionExactly(); + + // Assert: Ensure deleting the non-existing model again throws an exception + var delEx = await FluentActions.Invoking(() => Sut.Delete(input)).Should().ThrowAsync(); + getEx.WithInnerExceptionExactly(); } } diff --git a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/OtherUserResourceTests.cs b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/OtherUserResourceTests.cs index 6ac9818e..86e41c71 100644 --- a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/OtherUserResourceTests.cs +++ b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/OtherUserResourceTests.cs @@ -1,50 +1,54 @@ -using Speckle.Sdk.Api; +using FluentAssertions; +using Speckle.Sdk.Api; using Speckle.Sdk.Api.GraphQL.Resources; using Speckle.Sdk.Credentials; +using Xunit; namespace Speckle.Sdk.Tests.Integration.API.GraphQL.Resources; -[TestOf(typeof(OtherUserResource))] public class OtherUserResourceTests { - private Client _testUser; - private Account _testData; + private readonly Client _testUser; + private readonly Account _testData; private OtherUserResource Sut => _testUser.OtherUser; - [OneTimeSetUp] - public async Task Setup() + public OtherUserResourceTests() { - _testUser = await Fixtures.SeedUserWithClient(); - _testData = await Fixtures.SeedUser(); + _testUser = Fixtures.SeedUserWithClient().GetAwaiter().GetResult(); + _testData = Fixtures.SeedUser().GetAwaiter().GetResult(); } - [Test] - public async Task OtherUserGet() + [Fact] + public async Task OtherUserGet_Should_ReturnCorrectUser() { var res = await Sut.Get(_testData.userInfo.id); - Assert.That(res, Is.Not.Null); - Assert.That(res!.name, Is.EqualTo(_testData.userInfo.name)); + + res.Should().NotBeNull(); + res!.name.Should().Be(_testData.userInfo.name); } - [Test] - public async Task OtherUserGet_NonExistentUser() + [Fact] + public async Task OtherUserGet_NonExistentUser_Should_ReturnNull() { var result = await Sut.Get("AnIdThatDoesntExist"); - Assert.That(result, Is.Null); + + result.Should().BeNull(); } - [Test] - public async Task UserSearch() + [Fact] + public async Task UserSearch_Should_ReturnMatchingUser() { 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)); + + res.items.Should().ContainSingle(); + res.items[0].id.Should().Be(_testData.userInfo.id); } - [Test] - public async Task UserSearch_NonExistentUser() + [Fact] + public async Task UserSearch_NonExistentUser_Should_ReturnEmptyList() { var res = await Sut.UserSearch("idontexist@example.com", 25); - Assert.That(res.items, Has.Count.EqualTo(0)); + + res.items.Should().BeEmpty(); } } diff --git a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ProjectInviteResourceExceptionalTests.cs b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ProjectInviteResourceExceptionalTests.cs index b74b0d45..23137093 100644 --- a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ProjectInviteResourceExceptionalTests.cs +++ b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ProjectInviteResourceExceptionalTests.cs @@ -1,32 +1,48 @@ -using Speckle.Sdk.Api; +using FluentAssertions; +using Speckle.Sdk.Api; using Speckle.Sdk.Api.GraphQL.Inputs; using Speckle.Sdk.Api.GraphQL.Models; using Speckle.Sdk.Api.GraphQL.Resources; +using Xunit; namespace Speckle.Sdk.Tests.Integration.API.GraphQL.Resources; -[TestOf(typeof(ProjectInviteResource))] -public class ProjectInviteResourceExceptionalTests +public class ProjectInviteResourceExceptionalTests : IAsyncLifetime { private Client _testUser; private Project _project; private ProjectInviteResource Sut => _testUser.ProjectInvite; - [OneTimeSetUp] - public async Task Setup() + // Replacing OneTimeSetUp with IAsyncLifetime's InitializeAsync + public async Task InitializeAsync() { _testUser = await Fixtures.SeedUserWithClient(); - _project = await _testUser.Project.Create(new("test", null, null)); + _project = await _testUser.Project.Create(new ProjectCreateInput("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) + // Implementing IAsyncLifetime's DisposeAsync (optional if no cleanup is needed) + public Task DisposeAsync() => Task.CompletedTask; + + [Theory] + [InlineData(null, null, null, null)] + [InlineData(null, "something", "something", null)] + public async Task ProjectInviteCreate_InvalidInput_ShouldThrowSpeckleGraphQLException( + 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); - }); + var input = new ProjectInviteCreateInput(email, role, serverRole, userId); + + var exception = await FluentActions + .Invoking(async () => + { + await Sut.Create(_project.id, input); + }) + .Should() + .ThrowAsync(); + + exception.WithInnerExceptionExactly(); } } diff --git a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ProjectInviteResourceTests.cs b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ProjectInviteResourceTests.cs index 0a618d91..0a4efd57 100644 --- a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ProjectInviteResourceTests.cs +++ b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ProjectInviteResourceTests.cs @@ -1,21 +1,21 @@ -using Speckle.Sdk.Api; +using FluentAssertions; +using Speckle.Sdk.Api; using Speckle.Sdk.Api.GraphQL; using Speckle.Sdk.Api.GraphQL.Inputs; using Speckle.Sdk.Api.GraphQL.Models; -using Speckle.Sdk.Api.GraphQL.Resources; +using Speckle.Sdk.Common; +using Xunit; namespace Speckle.Sdk.Tests.Integration.API.GraphQL.Resources; -[TestOf(typeof(ProjectInviteResource))] -public class ProjectInviteResourceTests +public class ProjectInviteResourceTests : IAsyncLifetime { private Client _inviter, _invitee; private Project _project; private PendingStreamCollaborator _createdInvite; - [SetUp] - public async Task Setup() + public async Task InitializeAsync() { _inviter = await Fixtures.SeedUserWithClient(); _invitee = await Fixtures.SeedUserWithClient(); @@ -23,85 +23,91 @@ public class ProjectInviteResourceTests _createdInvite = await SeedInvite(); } + public Task DisposeAsync() => Task.CompletedTask; + 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(); + var invites = await _invitee.ActiveUser.GetProjectInvites(); return invites.First(i => i.projectId == res.id); } - [Test] + [Fact] 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 invites = await _invitee.ActiveUser.GetProjectInvites(); 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); + res.id.Should().Be(_project.id); + res.invitedTeam.Should().ContainSingle(); + invite.user!.id.Should().Be(_invitee.Account.userInfo.id); + invite.token.Should().NotBeNull(); } - [Test] + [Fact] 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)); + res.id.Should().Be(_project.id); + res.invitedTeam.Should().ContainSingle(); + res.invitedTeam[0].user!.id.Should().Be(_invitee.Account.userInfo.id); } - [Test] + [Fact] public async Task ProjectInviteGet() { - var collaborator = await _invitee.ProjectInvite.Get(_project.id, _createdInvite.token); + var collaborator = await _invitee.ProjectInvite.Get(_project.id, _createdInvite.token).NotNull(); - Assert.That( - collaborator, - Has.Property(nameof(PendingStreamCollaborator.inviteId)).EqualTo(_createdInvite.inviteId) - ); - Assert.That(collaborator!.user.id, Is.EqualTo(_createdInvite.user.id)); + collaborator.inviteId.Should().Be(_createdInvite.inviteId); + collaborator.user!.id.Should().Be(_createdInvite.user!.id); } - [Test] + [Fact] + public async Task ProjectInviteGet_NonExisting() + { + var collaborator = await _invitee.ProjectInvite.Get(_project.id, "this is not a real token"); + collaborator.Should().BeNull(); + } + + [Fact] 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); + ProjectInviteUseInput input = new(true, _createdInvite.projectId, _createdInvite.token.NotNull()); + await _invitee.ProjectInvite.Use(input); var project = await _inviter.Project.GetWithTeam(_project.id); - var teamMembers = project.team.Select(c => c.user.id); + var teamMembers = project.team.Select(c => c.user.id).ToArray(); var expectedTeamMembers = new[] { _inviter.Account.userInfo.id, _invitee.Account.userInfo.id }; - Assert.That(teamMembers, Is.EquivalentTo(expectedTeamMembers)); + + teamMembers.Should().BeEquivalentTo(expectedTeamMembers); } - [Test] + [Fact] public async Task ProjectInviteCancel_MemberNotAdded() { var res = await _inviter.ProjectInvite.Cancel(_createdInvite.projectId, _createdInvite.inviteId); - - Assert.That(res.invitedTeam, Is.Empty); + res.invitedTeam.Should().BeEmpty(); } - [Test] - [TestCase(StreamRoles.STREAM_OWNER)] - [TestCase(StreamRoles.STREAM_REVIEWER)] - [TestCase(StreamRoles.STREAM_CONTRIBUTOR)] - [TestCase(StreamRoles.REVOKE)] - public async Task ProjectUpdateRole(string newRole) + [Theory] + [InlineData(StreamRoles.STREAM_OWNER)] + [InlineData(StreamRoles.STREAM_CONTRIBUTOR)] + [InlineData(StreamRoles.STREAM_REVIEWER)] + [InlineData(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)); + ProjectUpdateRoleInput input = new(_invitee.Account.userInfo.id, _project.id, newRole); + await _inviter.Project.UpdateRole(input); + + var finalProject = await _invitee.Project.Get(_project.id); + finalProject.role.Should().Be(newRole); } } diff --git a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ProjectResourceExceptionalTests.cs b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ProjectResourceExceptionalTests.cs index c616626c..2fd429b3 100644 --- a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ProjectResourceExceptionalTests.cs +++ b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ProjectResourceExceptionalTests.cs @@ -1,14 +1,16 @@ -using Speckle.Sdk.Api; +using FluentAssertions; +using Speckle.Sdk.Api; using Speckle.Sdk.Api.GraphQL; using Speckle.Sdk.Api.GraphQL.Enums; using Speckle.Sdk.Api.GraphQL.Inputs; using Speckle.Sdk.Api.GraphQL.Models; using Speckle.Sdk.Api.GraphQL.Resources; +using Speckle.Sdk.Common; +using Xunit; namespace Speckle.Sdk.Tests.Integration.API.GraphQL.Resources; -[TestOf(typeof(ProjectResource))] -public class ProjectResourceExceptionalTests +public class ProjectResourceExceptionalTests : IAsyncLifetime { private Client _testUser, _secondUser, @@ -16,8 +18,9 @@ public class ProjectResourceExceptionalTests private Project _testProject; private ProjectResource Sut => _testUser.Project; - [OneTimeSetUp] - public async Task Setup() + public Task DisposeAsync() => Task.CompletedTask; + + public async Task InitializeAsync() { _testUser = await Fixtures.SeedUserWithClient(); _secondUser = await Fixtures.SeedUserWithClient(); @@ -32,82 +35,97 @@ public class ProjectResourceExceptionalTests // 4. Server doesn't exist (is down) //There's got to be a smarter way to parametrise these... - [Test] - public void ProjectCreate_WithoutAuth() + [Fact] + public async Task ProjectCreate_WithoutAuth() { - ProjectCreateInput input = - new("The best project", "The best description for the best project", ProjectVisibility.Private); + ProjectCreateInput input = new( + "The best project", + "The best description for the best project", + ProjectVisibility.Private + ); - Assert.ThrowsAsync(async () => await _unauthedUser.Project.Create(input)); + var ex = await Assert.ThrowsAsync(async () => _ = await _unauthedUser.Project.Create(input)); + ex.InnerExceptions.Single().Should().BeOfType(); } - [Test] + [Fact] 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)); + var ex = await Assert.ThrowsAsync( + async () => _ = await _unauthedUser.Project.Get(privateStream.id) + ); + ex.InnerExceptions.Single().Should().BeOfType(); } - [Test] - public void ProjectGet_NonExistentProject() + [Fact] + public async Task ProjectGet_NonExistentProject() { - Assert.ThrowsAsync(async () => await Sut.Get("NonExistentProject")); + var ex = await Assert.ThrowsAsync(async () => await Sut.Get("NonExistentProject")); + ex.InnerExceptions.Single().Should().BeOfType(); } - [Test] - public void ProjectUpdate_NonExistentProject() + [Fact] + public async Task ProjectUpdate_NonExistentProject() { - Assert.ThrowsAsync( + var ex = await Assert.ThrowsAsync( async () => _ = await Sut.Update(new("NonExistentProject", "My new name")) ); + ex.InnerExceptions.Single().Should().BeOfType(); } - [Test] - public void ProjectUpdate_NoAuth() + [Fact] + public async Task ProjectUpdate_NoAuth() { - Assert.ThrowsAsync( + var ex = await Assert.ThrowsAsync( async () => _ = await _unauthedUser.Project.Update(new(_testProject.id, "My new name")) ); + ex.InnerExceptions.Single().Should().BeOfType(); } - [Test] - [TestCase(StreamRoles.STREAM_OWNER)] - [TestCase(StreamRoles.STREAM_CONTRIBUTOR)] - [TestCase(StreamRoles.STREAM_REVIEWER)] - [TestCase(StreamRoles.REVOKE)] - public void ProjectUpdateRole_NonExistentProject(string newRole) + [Theory] + [InlineData(StreamRoles.STREAM_OWNER)] + [InlineData(StreamRoles.STREAM_CONTRIBUTOR)] + [InlineData(StreamRoles.STREAM_REVIEWER)] + [InlineData(StreamRoles.REVOKE)] + public async Task ProjectUpdateRole_NonExistentProject(string? newRole) { - ProjectUpdateRoleInput input = new(_secondUser.Account.id, "NonExistentProject", newRole); + ProjectUpdateRoleInput input = new(_secondUser.Account.id.NotNull(), "NonExistentProject", newRole); - Assert.ThrowsAsync(async () => await Sut.UpdateRole(input)); + var ex = await Assert.ThrowsAsync(async () => _ = await Sut.UpdateRole(input)); + ex.InnerExceptions.Single().Should().BeOfType(); } - [Test] - [TestCase(StreamRoles.STREAM_OWNER)] - [TestCase(StreamRoles.STREAM_CONTRIBUTOR)] - [TestCase(StreamRoles.STREAM_REVIEWER)] - [TestCase(StreamRoles.REVOKE)] - public void ProjectUpdateRole_NonAuth(string newRole) + [Theory] + [InlineData(StreamRoles.STREAM_OWNER)] + [InlineData(StreamRoles.STREAM_CONTRIBUTOR)] + [InlineData(StreamRoles.STREAM_REVIEWER)] + [InlineData(StreamRoles.REVOKE)] + public async Task ProjectUpdateRole_NonAuth(string? newRole) { - ProjectUpdateRoleInput input = new(_secondUser.Account.id, "NonExistentProject", newRole); - Assert.ThrowsAsync(async () => await _unauthedUser.Project.UpdateRole(input)); + ProjectUpdateRoleInput input = new(_secondUser.Account.id.NotNull(), "NonExistentProject", newRole); + + var ex = await Assert.ThrowsAsync( + async () => _ = await _unauthedUser.Project.UpdateRole(input) + ); + ex.InnerExceptions.Single().Should().BeOfType(); } - [Test] + [Fact] public async Task ProjectDelete_NonExistentProject() { - bool response = await Sut.Delete(_testProject.id); - Assert.That(response, Is.True); + await Sut.Delete(_testProject.id); - Assert.ThrowsAsync(async () => _ = await Sut.Get(_testProject.id)); //TODO: Exception types + var ex = await Assert.ThrowsAsync(async () => _ = await Sut.Get(_testProject.id)); + ex.InnerExceptions.Single().Should().BeOfType(); } - [Test] - public void ProjectInvites_NoAuth() + [Fact] + public async Task ProjectInvites_NoAuth() { - Assert.ThrowsAsync(async () => await Fixtures.Unauthed.ActiveUser.ProjectInvites()); + await Assert.ThrowsAsync(async () => await Fixtures.Unauthed.ActiveUser.ProjectInvites()); } } diff --git a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ProjectResourceTests.cs b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ProjectResourceTests.cs index 17755aa6..98cee1fe 100644 --- a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ProjectResourceTests.cs +++ b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/ProjectResourceTests.cs @@ -1,72 +1,103 @@ -using Speckle.Sdk.Api; +using FluentAssertions; +using Speckle.Sdk.Api; using Speckle.Sdk.Api.GraphQL.Enums; using Speckle.Sdk.Api.GraphQL.Inputs; using Speckle.Sdk.Api.GraphQL.Models; using Speckle.Sdk.Api.GraphQL.Resources; +using Xunit; namespace Speckle.Sdk.Tests.Integration.API.GraphQL.Resources; -[TestOf(typeof(ProjectResource))] public class ProjectResourceTests { - private Client _testUser; - private Project _testProject; + private readonly Client _testUser; + private readonly Project _testProject; private ProjectResource Sut => _testUser.Project; - [OneTimeSetUp] - public async Task Setup() + public ProjectResourceTests() { - _testUser = await Fixtures.SeedUserWithClient(); - _testProject = await _testUser.Project.Create(new("test project123", "desc", null)); + var setupTask = Setup(); + setupTask.Wait(); // Ensure setup runs synchronously for the constructor + (_testUser, _testProject) = setupTask.Result; } - [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) + private async Task<(Client TestUser, Project TestProject)> Setup() { - 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)); + var testUser = await Fixtures.SeedUserWithClient(); + var testProject = await testUser.Project.Create(new ProjectCreateInput("test project123", "desc", null)); + return (testUser, testProject); } - [Test] - public async Task ProjectGet() + [Theory] + [InlineData("Very private project", "My secret project", ProjectVisibility.Private)] + [InlineData("Very public project", null, ProjectVisibility.Public)] + public async Task ProjectCreate_Should_CreateProjectSuccessfully( + string name, + string? description, + ProjectVisibility visibility + ) { - Project result = await Sut.Get(_testProject.id); + // Arrange + var input = new ProjectCreateInput(name, description, visibility); - 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)); + // Act + var result = await Sut.Create(input); + + // Assert + result.Should().NotBeNull(); + result.id.Should().NotBeNullOrWhiteSpace(); + result.name.Should().Be(input.name); + result.description.Should().Be(input.description ?? string.Empty); + input.visibility.Should().NotBeNull(); } - [Test] - public async Task ProjectUpdate() + [Fact] + public async Task ProjectGet_Should_ReturnCorrectProject() { + // Act + var result = await Sut.Get(_testProject.id); + + // Assert + result.id.Should().Be(_testProject.id); + result.name.Should().Be(_testProject.name); + result.description.Should().Be(_testProject.description); + result.visibility.Should().Be(_testProject.visibility); + result.createdAt.Should().Be(_testProject.createdAt); + } + + [Fact] + public async Task ProjectUpdate_Should_UpdateProjectSuccessfully() + { + // Arrange 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)); + // Act + var newProject = await Sut.Update( + new ProjectUpdateInput(_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)); + // Assert + newProject.id.Should().Be(_testProject.id); + newProject.name.Should().Be(NEW_NAME); + newProject.description.Should().Be(NEW_DESCRIPTION); + newProject.visibility.Should().Be(NEW_VISIBILITY); } - [Test] - public async Task ProjectDelete() + [Fact] + public async Task ProjectDelete_Should_DeleteProjectSuccessfully() { - Project toDelete = await Sut.Create(new("Delete me", null, null)); - bool response = await Sut.Delete(toDelete.id); - Assert.That(response, Is.True); + // Arrange + var toDelete = await Sut.Create(new ProjectCreateInput("Delete me", null, null)); - Assert.ThrowsAsync(async () => _ = await Sut.Get(toDelete.id)); + // Act + await Sut.Delete(toDelete.id); + + // Assert + await FluentActions + .Invoking(async () => await Sut.Get(toDelete.id)) + .Should() + .ThrowAsync(); } } diff --git a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/SubscriptionResourceTests.cs b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/SubscriptionResourceTests.cs index 784f9ee6..9abe9cce 100644 --- a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/SubscriptionResourceTests.cs +++ b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/SubscriptionResourceTests.cs @@ -1,23 +1,31 @@ -using Speckle.Sdk.Api; +using FluentAssertions; +using Speckle.Sdk.Api; +using Speckle.Sdk.Api.GraphQL.Enums; using Speckle.Sdk.Api.GraphQL.Inputs; using Speckle.Sdk.Api.GraphQL.Models; using Speckle.Sdk.Api.GraphQL.Resources; +using Xunit; +using Version = Speckle.Sdk.Api.GraphQL.Models.Version; namespace Speckle.Sdk.Tests.Integration.API.GraphQL.Resources; -[TestOf(typeof(SubscriptionResource))] -public class SubscriptionResourceTests +public class SubscriptionResourceTests : IAsyncLifetime { private const int WAIT_PERIOD = 300; private Client _testUser; private Project _testProject; private Model _testModel; - private string _testVersion; + private Version _testVersion; private SubscriptionResource Sut => _testUser.Subscription; - [OneTimeSetUp] - public async Task Setup() + public Task DisposeAsync() + { + _testUser.Dispose(); + return Task.CompletedTask; + } + + public async Task InitializeAsync() { _testUser = await Fixtures.SeedUserWithClient(); _testProject = await _testUser.Project.Create(new("test project123", "desc", null)); @@ -25,7 +33,7 @@ public class SubscriptionResourceTests _testVersion = await Fixtures.CreateVersion(_testUser, _testProject.id, _testModel.id); } - [Test] + [Fact] public async Task UserProjectsUpdated_SubscriptionIsCalled() { UserProjectsUpdatedMessage? subscriptionMessage = null; @@ -39,11 +47,13 @@ public class SubscriptionResourceTests 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)); + subscriptionMessage.Should().NotBeNull(); + subscriptionMessage!.id.Should().Be(created.id); + subscriptionMessage.type.Should().Be(UserProjectsUpdatedMessageType.ADDED); + subscriptionMessage.project.Should().NotBeNull(); } - [Test] + [Fact] public async Task ProjectModelsUpdated_SubscriptionIsCalled() { ProjectModelsUpdatedMessage? subscriptionMessage = null; @@ -58,11 +68,13 @@ public class SubscriptionResourceTests 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)); + subscriptionMessage.Should().NotBeNull(); + subscriptionMessage!.id.Should().Be(created.id); + subscriptionMessage.type.Should().Be(ProjectModelsUpdatedMessageType.CREATED); + subscriptionMessage.model.Should().NotBeNull(); } - [Test] + [Fact] public async Task ProjectUpdated_SubscriptionIsCalled() { ProjectUpdatedMessage? subscriptionMessage = null; @@ -77,11 +89,13 @@ public class SubscriptionResourceTests 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)); + subscriptionMessage.Should().NotBeNull(); + subscriptionMessage!.id.Should().Be(created.id); + subscriptionMessage.type.Should().Be(ProjectUpdatedMessageType.UPDATED); + subscriptionMessage.project.Should().NotBeNull(); } - [Test] + [Fact] public async Task ProjectVersionsUpdated_SubscriptionIsCalled() { ProjectVersionsUpdatedMessage? subscriptionMessage = null; @@ -95,11 +109,13 @@ public class SubscriptionResourceTests 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)); + subscriptionMessage.Should().NotBeNull(); + subscriptionMessage!.id.Should().Be(created.id); + subscriptionMessage.type.Should().Be(ProjectVersionsUpdatedMessageType.CREATED); + subscriptionMessage.version.Should().NotBeNull(); } - [Test] + [Fact] public async Task ProjectCommentsUpdated_SubscriptionIsCalled() { string resourceIdString = $"{_testProject.id},{_testModel.id},{_testVersion}"; @@ -110,11 +126,13 @@ public class SubscriptionResourceTests await Task.Delay(WAIT_PERIOD); // Give time to subscription to be setup - var created = await Fixtures.CreateComment(_testUser, _testProject.id, _testModel.id, _testVersion); + var created = await Fixtures.CreateComment(_testUser, _testProject.id, _testModel.id, _testVersion.id); 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)); + subscriptionMessage.Should().NotBeNull(); + subscriptionMessage!.id.Should().Be(created.id); + subscriptionMessage.type.Should().Be(ProjectCommentsUpdatedMessageType.CREATED); + subscriptionMessage.comment.Should().NotBeNull(); } } diff --git a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/VersionResourceTests.cs b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/VersionResourceTests.cs index 34c2b530..321fdd2a 100644 --- a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/VersionResourceTests.cs +++ b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/VersionResourceTests.cs @@ -1,13 +1,14 @@ -using Speckle.Sdk.Api; +using FluentAssertions; +using Speckle.Sdk.Api; using Speckle.Sdk.Api.GraphQL.Inputs; using Speckle.Sdk.Api.GraphQL.Models; using Speckle.Sdk.Api.GraphQL.Resources; +using Xunit; using Version = Speckle.Sdk.Api.GraphQL.Models.Version; namespace Speckle.Sdk.Tests.Integration.API.GraphQL.Resources; -[TestOf(typeof(VersionResource))] -public class VersionResourceTests +public class VersionResourceTests : IAsyncLifetime { private Client _testUser; private VersionResource Sut => _testUser.Version; @@ -16,95 +17,100 @@ public class VersionResourceTests private Model _model2; private Version _version; - [SetUp] - public async Task Setup() + public Task DisposeAsync() => Task.CompletedTask; + + public async Task InitializeAsync() { _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, _model1.id); - - _version = await Sut.Get(versionId, _model1.id, _project.id); + _version = await Fixtures.CreateVersion(_testUser, _project.id, _model1.id); } - [Test] + [Fact] public async Task VersionGet() { - Version result = await Sut.Get(_version.id, _model1.id, _project.id); + Version result = await Sut.Get(_version.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)); + result.id.Should().Be(_version.id); + result.message.Should().Be(_version.message); } - [Test] + [Fact] 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)); + result.items.Count.Should().Be(1); + result.totalCount.Should().Be(1); + result.items[0].id.Should().Be(_version.id); } - [Test] + [Fact] public async Task VersionReceived() { MarkReceivedVersionInput input = new(_version.id, _project.id, "Integration test"); - var result = await Sut.Received(input); - - Assert.That(result, Is.True); + await Sut.Received(input); } - [Test] + [Fact] public async Task ModelGetWithVersions() { - Model result = await _testUser.Model.GetWithVersions(_model1.id, _project.id); + var 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)); + result.id.Should().Be(_model1.id); + result.versions.items.Count.Should().Be(1); + result.versions.totalCount.Should().Be(1); + result.versions.items[0].id.Should().Be(_version.id); } - [Test] + [Fact] public async Task VersionUpdate() { const string NEW_MESSAGE = "MY new version message"; - UpdateVersionInput input = new(_version.id, NEW_MESSAGE); + UpdateVersionInput input = new(_version.id, _project.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)); + updatedVersion.id.Should().Be(_version.id); + updatedVersion.message.Should().Be(NEW_MESSAGE); + updatedVersion.previewUrl.Should().Be(_version.previewUrl); } - [Test] + [Fact] public async Task VersionMoveToModel() { - MoveVersionsInput input = new(_model2.name, new[] { _version.id }); + MoveVersionsInput input = new(_project.id, _model2.name, [_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)); + id.Should().Be(_model2.id); - Assert.CatchAsync(async () => await Sut.Get(id, _model1.id, _project.id)); + Version movedVersion = await Sut.Get(_version.id, _project.id); + + movedVersion.id.Should().Be(_version.id); + movedVersion.message.Should().Be(_version.message); + movedVersion.previewUrl.Should().Be(_version.previewUrl); } - [Test] + [Fact] public async Task VersionDelete() { - DeleteVersionsInput input = new(new[] { _version.id }); + DeleteVersionsInput input = new([_version.id], _project.id); - bool response = await Sut.Delete(input); - Assert.That(response, Is.True); + await Sut.Delete(input); - Assert.CatchAsync(async () => _ = await Sut.Get(_version.id, _model1.id, _project.id)); - Assert.CatchAsync(async () => _ = await Sut.Delete(input)); + var getEx = await FluentActions + .Invoking(async () => await Sut.Get(_version.id, _project.id)) + .Should() + .ThrowAsync(); + getEx.WithInnerExceptionExactly(); + + var delEx = await FluentActions + .Invoking(async () => await Sut.Delete(input)) + .Should() + .ThrowAsync(); + delEx.WithInnerExceptionExactly(); } } diff --git a/tests/Speckle.Sdk.Tests.Integration/Credentials/UserServerInfoTests.cs b/tests/Speckle.Sdk.Tests.Integration/Credentials/UserServerInfoTests.cs index 7dc9b1a8..43a93be8 100644 --- a/tests/Speckle.Sdk.Tests.Integration/Credentials/UserServerInfoTests.cs +++ b/tests/Speckle.Sdk.Tests.Integration/Credentials/UserServerInfoTests.cs @@ -1,35 +1,32 @@ -using GraphQL.Client.Http; +using FluentAssertions; +using GraphQL.Client.Http; +using Microsoft.Extensions.DependencyInjection; using Speckle.Sdk.Api.GraphQL.Models; using Speckle.Sdk.Credentials; +using Xunit; namespace Speckle.Sdk.Tests.Integration.Credentials; -public class UserServerInfoTests +public class UserServerInfoTests : IAsyncLifetime { private Account _acc; - [OneTimeSetUp] - public async Task Setup() + public Task DisposeAsync() => Task.CompletedTask; + + public async Task InitializeAsync() { _acc = await Fixtures.SeedUser(); } - [Test] + [Fact] public async Task IsFrontEnd2True() { - ServerInfo? result = await AccountManager.GetServerInfo(new("https://app.speckle.systems/")); + ServerInfo? result = await Fixtures + .ServiceProvider.GetRequiredService() + .GetServerInfo(new("https://app.speckle.systems/")); - Assert.That(result, Is.Not.Null); - Assert.That(result!.frontend2, Is.True); - } - - [Test] - public async Task IsFrontEnd2False() - { - ServerInfo? result = await AccountManager.GetServerInfo(new("https://speckle.xyz/")); - - Assert.That(result, Is.Not.Null); - Assert.That(result!.frontend2, Is.False); + result.Should().NotBeNull(); + result.frontend2.Should().BeTrue(); } /// @@ -38,50 +35,72 @@ public class UserServerInfoTests /// This is not doable in local server because there is no end-point on this to ping. /// This is a bad sign for mutation. /// - [Test] - public void GetServerInfo_ExpectFail_CantPing() + [Fact] + public async Task GetServerInfo_ExpectFail_CantPing() { Uri serverUrl = new(_acc.serverInfo.url); - Assert.ThrowsAsync(async () => await AccountManager.GetServerInfo(serverUrl)); + await FluentActions + .Invoking( + async () => await Fixtures.ServiceProvider.GetRequiredService().GetServerInfo(serverUrl) + ) + .Should() + .ThrowAsync(); } - [Test] - public void GetServerInfo_ExpectFail_NoServer() + [Fact] + public async Task GetServerInfo_ExpectFail_NoServer() { Uri serverUrl = new("http://invalidserver.local"); - Assert.ThrowsAsync(async () => await AccountManager.GetServerInfo(serverUrl)); + await FluentActions + .Invoking( + async () => await Fixtures.ServiceProvider.GetRequiredService().GetServerInfo(serverUrl) + ) + .Should() + .ThrowAsync(); } - [Test] + [Fact] public async Task GetUserInfo() { Uri serverUrl = new(_acc.serverInfo.url); - UserInfo result = await AccountManager.GetUserInfo(_acc.token, serverUrl); + UserInfo result = await Fixtures + .ServiceProvider.GetRequiredService() + .GetUserInfo(_acc.token, serverUrl); - Assert.That(result.id, Is.EqualTo(_acc.userInfo.id)); - Assert.That(result.name, Is.EqualTo(_acc.userInfo.name)); - Assert.That(result.email, Is.EqualTo(_acc.userInfo.email)); - Assert.That(result.company, Is.EqualTo(_acc.userInfo.company)); - Assert.That(result.avatar, Is.EqualTo(_acc.userInfo.avatar)); + result.id.Should().Be(_acc.userInfo.id); + result.name.Should().Be(_acc.userInfo.name); + result.email.Should().Be(_acc.userInfo.email); + result.company.Should().Be(_acc.userInfo.company); + result.avatar.Should().Be(_acc.userInfo.avatar); } - [Test] - public void GetUserInfo_ExpectFail_NoServer() + [Fact] + public async Task GetUserInfo_ExpectFail_NoServer() { Uri serverUrl = new("http://invalidserver.local"); - Assert.ThrowsAsync(async () => await AccountManager.GetUserInfo("", serverUrl)); + await FluentActions + .Invoking( + async () => await Fixtures.ServiceProvider.GetRequiredService().GetUserInfo("", serverUrl) + ) + .Should() + .ThrowAsync(); } - [Test] - public void GetUserInfo_ExpectFail_NoUser() + [Fact] + public async Task GetUserInfo_ExpectFail_NoUser() { Uri serverUrl = new(_acc.serverInfo.url); - - Assert.ThrowsAsync( - async () => await AccountManager.GetUserInfo("Bearer 08913c3c1e7ac65d779d1e1f11b942a44ad9672ca9", serverUrl) - ); + await FluentActions + .Invoking( + async () => + await Fixtures + .ServiceProvider.GetRequiredService() + .GetUserInfo("Bearer 08913c3c1e7ac65d779d1e1f11b942a44ad9672ca9", serverUrl) + ) + .Should() + .ThrowAsync(); } } diff --git a/tests/Speckle.Sdk.Tests.Integration/Fixtures.cs b/tests/Speckle.Sdk.Tests.Integration/Fixtures.cs index 45dacf31..bef9259f 100644 --- a/tests/Speckle.Sdk.Tests.Integration/Fixtures.cs +++ b/tests/Speckle.Sdk.Tests.Integration/Fixtures.cs @@ -2,14 +2,18 @@ using System.Globalization; using System.Net.Mime; using System.Text; using System.Web; +using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; using Speckle.Sdk.Api; using Speckle.Sdk.Api.GraphQL.Inputs; using Speckle.Sdk.Api.GraphQL.Models; using Speckle.Sdk.Common; using Speckle.Sdk.Credentials; +using Speckle.Sdk.Host; using Speckle.Sdk.Models; +using Speckle.Sdk.Tests.Unit.Serialisation; using Speckle.Sdk.Transports; +using Version = Speckle.Sdk.Api.GraphQL.Models.Version; namespace Speckle.Sdk.Tests.Integration; @@ -17,17 +21,31 @@ public static class Fixtures { 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 IServiceProvider ServiceProvider { get; set; } + + static Fixtures() + { + TypeLoader.Reset(); + TypeLoader.Initialize(typeof(Base).Assembly, typeof(IgnoreTest).Assembly); + ServiceProvider = TestServiceSetup.GetServiceProvider(); + } + + public static Client Unauthed => + ServiceProvider + .GetRequiredService() + .Create(new Account { serverInfo = Server, userInfo = new UserInfo() }); public static async Task SeedUserWithClient() { - return new Client(await SeedUser()); + return ServiceProvider.GetRequiredService().Create(await SeedUser()); } - public static async Task CreateVersion(Client client, string projectId, string modelId) + public static async Task CreateVersion(Client client, string projectId, string modelId) { - using ServerTransport remote = new(client.Account, projectId); - var (objectId, _) = await Operations.Send(new() { applicationId = "ASDF" }, remote, false); + using var remote = ServiceProvider.GetRequiredService().Create(client.Account, projectId); + var (objectId, _) = await ServiceProvider + .GetRequiredService() + .Send(new() { applicationId = "ASDF" }, remote, false); CreateVersionInput input = new(objectId, modelId, projectId); return await client.Version.Create(input); } @@ -35,13 +53,12 @@ public static class Fixtures public static async Task SeedUser() { var seed = Guid.NewGuid().ToString().ToLower(); - Dictionary user = - new() - { - ["email"] = $"{seed.Substring(0, 7)}@example.com", - ["password"] = "12ABC3456789DEF0GHO", - ["name"] = $"{seed.Substring(0, 5)} Name" - }; + Dictionary user = new() + { + ["email"] = $"{seed[..7]}@example.com", + ["password"] = "12ABC3456789DEF0GHO", + ["name"] = $"{seed[..5]} Name", + }; using var httpClient = new HttpClient( new HttpClientHandler { AllowAutoRedirect = false, CheckCertificateRevocationList = true } @@ -68,14 +85,13 @@ public static class Fixtures var query = HttpUtility.ParseQueryString(uri.Query); string accessCode = query["access_code"] ?? throw new Exception("Redirect Uri has no 'access_code'."); - Dictionary tokenBody = - new() - { - ["accessCode"] = accessCode, - ["appId"] = "spklwebapp", - ["appSecret"] = "spklwebapp", - ["challenge"] = "challengingchallenge" - }; + Dictionary tokenBody = new() + { + ["accessCode"] = accessCode, + ["appId"] = "spklwebapp", + ["appSecret"] = "spklwebapp", + ["challenge"] = "challengingchallenge", + }; var tokenResponse = await httpClient.PostAsync( "/auth/token", @@ -92,12 +108,14 @@ public static class Fixtures { id = user["name"], email = user["email"], - name = user["name"] + name = user["name"], }, - serverInfo = Server + serverInfo = Server, }; - var user1 = await AccountManager.GetUserInfo(acc.token, new(acc.serverInfo.url)); + var user1 = await ServiceProvider + .GetRequiredService() + .GetUserInfo(acc.token, new(acc.serverInfo.url)); acc.userInfo = user1; return acc; } @@ -109,7 +127,7 @@ public static class Fixtures ["foo"] = "foo", ["bar"] = "bar", ["baz"] = "baz", - ["now"] = DateTime.Now.ToString(CultureInfo.InvariantCulture) + ["now"] = DateTime.Now.ToString(CultureInfo.InvariantCulture), }; return @base; @@ -121,7 +139,7 @@ public static class Fixtures { ["foo"] = "foo", ["bar"] = "bar", - ["@baz"] = new Base() { ["mux"] = "mux", ["qux"] = "qux" } + ["@baz"] = new Base() { ["mux"] = "mux", ["qux"] = "qux" }, }; return @base; @@ -142,17 +160,17 @@ public static class Fixtures 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(); + var blobIds = blobs.Select(b => b.id.NotNull()).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); + using var remote = ServiceProvider.GetRequiredService().Create(account, projectId); var blobs = Fixtures.GenerateThreeBlobs(); Base myObject = new() { ["blobs"] = blobs }; - await Operations.Send(myObject, remote, false); + await ServiceProvider.GetRequiredService().Send(myObject, remote, false); return blobs; } } diff --git a/tests/Speckle.Sdk.Tests.Integration/GraphQLCLient.cs b/tests/Speckle.Sdk.Tests.Integration/GraphQLCLient.cs deleted file mode 100644 index 1503c129..00000000 --- a/tests/Speckle.Sdk.Tests.Integration/GraphQLCLient.cs +++ /dev/null @@ -1,64 +0,0 @@ -using GraphQL; -using Speckle.Sdk.Api; -using Speckle.Sdk.Credentials; - -namespace Speckle.Sdk.Tests.Integration; - -public class GraphQLClientTests : IDisposable -{ - private Account _account; - private Client _client; - - [OneTimeSetUp] - public async Task Setup() - { - _account = await Fixtures.SeedUser(); - _client = new Client(_account); - } - - [Test] - public void ThrowsForbiddenException() - { - Assert.ThrowsAsync( - async () => - await _client.ExecuteGraphQLRequest>( - new GraphQLRequest - { - Query = - @"query { - adminStreams{ - totalCount - } - }" - } - ) - ); - } - - [Test] - public void Cancellation() - { - using CancellationTokenSource tokenSource = new(); - tokenSource.Cancel(); - Assert.CatchAsync( - async () => - await _client.ExecuteGraphQLRequest>( - new GraphQLRequest - { - Query = - @"query { - adminStreams{ - totalCount - } - }" - }, - tokenSource.Token - ) - ); - } - - public void Dispose() - { - _client?.Dispose(); - } -} diff --git a/tests/Speckle.Sdk.Tests.Integration/MemoryTransportTests.cs b/tests/Speckle.Sdk.Tests.Integration/MemoryTransportTests.cs new file mode 100644 index 00000000..99ccf856 --- /dev/null +++ b/tests/Speckle.Sdk.Tests.Integration/MemoryTransportTests.cs @@ -0,0 +1,75 @@ +using System.Reflection; +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Speckle.Sdk.Api; +using Speckle.Sdk.Host; +using Speckle.Sdk.Models; +using Speckle.Sdk.Transports; +using Xunit; + +namespace Speckle.Sdk.Tests.Integration; + +public class MemoryTransportTests : IDisposable +{ + private readonly MemoryTransport _memoryTransport = new(blobStorageEnabled: true); + private IOperations _operations; + + public MemoryTransportTests() + { + CleanData(); + TypeLoader.Reset(); + TypeLoader.Initialize(typeof(Base).Assembly, Assembly.GetExecutingAssembly()); + var serviceProvider = TestServiceSetup.GetServiceProvider(); + _operations = serviceProvider.GetRequiredService(); + } + + public void Dispose() => CleanData(); + + private void CleanData() + { + if (Directory.Exists(_memoryTransport.BlobStorageFolder)) + { + Directory.Delete(_memoryTransport.BlobStorageFolder, true); + } + + Directory.CreateDirectory(_memoryTransport.BlobStorageFolder); + } + + [Fact] + public async Task SendAndReceiveObjectWithBlobs() + { + var myObject = Fixtures.GenerateSimpleObject(); + myObject["blobs"] = Fixtures.GenerateThreeBlobs(); + + var sendResult = await _operations.Send(myObject, _memoryTransport, false); + + // NOTE: used to debug diffing + // await Operations.Send(myObject, new List { transport }); + + var receivedObject = await _operations.Receive(sendResult.rootObjId, _memoryTransport, new MemoryTransport()); + + var allFiles = Directory + .GetFiles(_memoryTransport.BlobStorageFolder) + .Select(fp => fp.Split(Path.DirectorySeparatorChar).Last()) + .ToList(); + + var blobPaths = allFiles + .Where(fp => fp.Length > Blob.LocalHashPrefixLength) // excludes things like .DS_store + .ToList(); + + // Check that there are three downloaded blobs! + blobPaths.Count.Should().Be(3); + + var objectBlobs = receivedObject["blobs"] as IList; + objectBlobs.Should().NotBeNull(); + + var blobs = objectBlobs!.Cast().ToList(); + // Check that we have three blobs + blobs.Count.Should().Be(3); + + // Check that received blobs point to local path (where they were received) + blobs[0].filePath.Should().Contain(_memoryTransport.BlobStorageFolder); + blobs[1].filePath.Should().Contain(_memoryTransport.BlobStorageFolder); + blobs[2].filePath.Should().Contain(_memoryTransport.BlobStorageFolder); + } +} diff --git a/tests/Speckle.Sdk.Tests.Integration/ServerTransportTests.cs b/tests/Speckle.Sdk.Tests.Integration/ServerTransportTests.cs deleted file mode 100644 index f105dcfb..00000000 --- a/tests/Speckle.Sdk.Tests.Integration/ServerTransportTests.cs +++ /dev/null @@ -1,177 +0,0 @@ -using System.Reflection; -using Shouldly; -using Speckle.Sdk.Api; -using Speckle.Sdk.Credentials; -using Speckle.Sdk.Host; -using Speckle.Sdk.Logging; -using Speckle.Sdk.Models; -using Speckle.Sdk.Transports; - -namespace Speckle.Sdk.Tests.Integration; - -public class ServerTransportTests : IDisposable -{ - private string _basePath; - private Account _account; - private Client _client; - private string _streamId; - private ServerTransport _transport; - - [OneTimeSetUp] - public async Task InitialSetup() - { - _basePath = Path.Join(Path.GetTempPath(), "speckleTest"); - - CleanData(); - Directory.CreateDirectory(_basePath); - SpecklePathProvider.OverrideApplicationDataPath(_basePath); - - _account = await Fixtures.SeedUser(); - _client = new Client(_account); - _streamId = await _client.StreamCreate(new StreamCreateInput { description = "Flobber", name = "Blobber" }); - } - - [SetUp] - public void Setup() - { - CleanData(); - // need to recreate the server transport object for each test - // to make sure all folders are properly initialized - _transport = new ServerTransport(_account, _streamId); - TypeLoader.Reset(); - TypeLoader.Initialize(typeof(Base).Assembly, Assembly.GetExecutingAssembly()); - } - - [TearDown] - public void TearDown() - { - CleanData(); - } - - private void CleanData() - { - _transport?.Dispose(); - if (Directory.Exists(_basePath)) - { - Directory.Delete(_basePath, true); - } - } - - [Test] - public async Task SendObject() - { - var myObject = Fixtures.GenerateNestedObject(); - - var objectId = await Operations.Send(myObject, _transport, false); - - Assert.That(objectId, Is.Not.Null); - } - - [Test] - public async Task SendAndReceiveObjectWithBlobs() - { - var myObject = Fixtures.GenerateSimpleObject(); - myObject["blobs"] = Fixtures.GenerateThreeBlobs(); - - var sendResult = await Operations.Send(myObject, _transport, false); - - // NOTE: used to debug diffing - // await Operations.Send(myObject, new List { transport }); - - var receivedObject = await Operations.Receive(sendResult.rootObjId, _transport, new MemoryTransport()); - - var allFiles = Directory - .GetFiles(_transport.BlobStorageFolder) - .Select(fp => fp.Split(Path.DirectorySeparatorChar).Last()) - .ToList(); - var blobPaths = allFiles - .Where(fp => fp.Length > Blob.LocalHashPrefixLength) // excludes things like .DS_store - .ToList(); - - // Check that there are three downloaded blobs! - Assert.That(blobPaths, Has.Count.EqualTo(3)); - 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) - Assert.That(blobs[0].filePath, Contains.Substring(_transport.BlobStorageFolder)); - Assert.That(blobs[1].filePath, Contains.Substring(_transport.BlobStorageFolder)); - Assert.That(blobs[2].filePath, Contains.Substring(_transport.BlobStorageFolder)); - } - - [Test] - public async Task SendWithBlobsWithoutSQLiteSendCache() - { - var myObject = Fixtures.GenerateSimpleObject(); - myObject["blobs"] = Fixtures.GenerateThreeBlobs(); - - var memTransport = new MemoryTransport(); - var sendResult = await Operations.Send(myObject, new List { _transport, memTransport }); - - var receivedObject = await Operations.Receive(sendResult.rootObjId, _transport, new MemoryTransport()); - - var allFiles = Directory - .GetFiles(_transport.BlobStorageFolder) - .Select(fp => fp.Split(Path.DirectorySeparatorChar).Last()) - .ToList(); - var blobPaths = allFiles - .Where(fp => fp.Length > Blob.LocalHashPrefixLength) // excludes things like .DS_store - .ToList(); - - // Check that there are three downloaded blobs! - Assert.That(blobPaths, Has.Count.EqualTo(3)); - - 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) - Assert.That(blobs[0].filePath, Contains.Substring(_transport.BlobStorageFolder)); - Assert.That(blobs[1].filePath, Contains.Substring(_transport.BlobStorageFolder)); - Assert.That(blobs[2].filePath, Contains.Substring(_transport.BlobStorageFolder)); - } - - [Test] - public async Task SendReceiveWithCleanedMemoryCache() - { - var myObject = Fixtures.GenerateSimpleObject(); - myObject["blobs"] = Fixtures.GenerateThreeBlobs(); - - var memTransport = new MemoryTransport(); - var sendResult = await Operations.Send(myObject, new ITransport[] { _transport, memTransport }); - - memTransport = new MemoryTransport(); - Base receivedObject = await Operations.Receive(sendResult.rootObjId, _transport, memTransport); - Assert.That(receivedObject, Is.Not.Null); - - var allFiles = Directory - .GetFiles(_transport.BlobStorageFolder) - .Select(fp => fp.Split(Path.DirectorySeparatorChar).Last()) - .ToList(); - var blobPaths = allFiles - .Where(fp => fp.Length > Blob.LocalHashPrefixLength) // excludes things like .DS_store - .ToList(); - - // Check that there are three downloaded blobs! - Assert.That(blobPaths.Count, Is.EqualTo(3)); - - 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) - Assert.That(blobs[0].filePath, Contains.Substring(_transport.BlobStorageFolder)); - Assert.That(blobs[1].filePath, Contains.Substring(_transport.BlobStorageFolder)); - Assert.That(blobs[2].filePath, Contains.Substring(_transport.BlobStorageFolder)); - } - - public void Dispose() - { - _client?.Dispose(); - _transport?.Dispose(); - } -} diff --git a/tests/Speckle.Sdk.Tests.Integration/Speckle.Sdk.Tests.Integration.csproj b/tests/Speckle.Sdk.Tests.Integration/Speckle.Sdk.Tests.Integration.csproj index 5eb43529..e5204dc2 100644 --- a/tests/Speckle.Sdk.Tests.Integration/Speckle.Sdk.Tests.Integration.csproj +++ b/tests/Speckle.Sdk.Tests.Integration/Speckle.Sdk.Tests.Integration.csproj @@ -9,9 +9,8 @@ - - - + + diff --git a/tests/Speckle.Sdk.Tests.Integration/TestServiceSetup.cs b/tests/Speckle.Sdk.Tests.Integration/TestServiceSetup.cs new file mode 100644 index 00000000..30d44dff --- /dev/null +++ b/tests/Speckle.Sdk.Tests.Integration/TestServiceSetup.cs @@ -0,0 +1,14 @@ +using Microsoft.Extensions.DependencyInjection; +using Speckle.Sdk.Host; + +namespace Speckle.Sdk.Tests.Integration; + +public static class TestServiceSetup +{ + public static IServiceProvider GetServiceProvider() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSpeckleSdk(HostApplications.Navisworks, HostAppVersion.v2023, "Test"); + return serviceCollection.BuildServiceProvider(); + } +} diff --git a/tests/Speckle.Sdk.Tests.Integration/Usings.cs b/tests/Speckle.Sdk.Tests.Integration/Usings.cs deleted file mode 100644 index 32445676..00000000 --- a/tests/Speckle.Sdk.Tests.Integration/Usings.cs +++ /dev/null @@ -1 +0,0 @@ -global using NUnit.Framework; diff --git a/tests/Speckle.Sdk.Tests.Integration/packages.lock.json b/tests/Speckle.Sdk.Tests.Integration/packages.lock.json index ce249d81..32c8bf35 100644 --- a/tests/Speckle.Sdk.Tests.Integration/packages.lock.json +++ b/tests/Speckle.Sdk.Tests.Integration/packages.lock.json @@ -4,24 +4,18 @@ "net8.0": { "altcover": { "type": "Direct", - "requested": "[8.8.74, )", - "resolved": "8.8.74", - "contentHash": "e8RZNE0vZnuBk/gOAWu9K5wm3S7dOrOlZje3PHI9PJUHqvP1cxVJD1eXAAmddFVlixowB7C7/zvC16GnunC2LQ==" - }, - "GitVersion.MsBuild": { - "type": "Direct", - "requested": "[5.12.0, )", - "resolved": "5.12.0", - "contentHash": "dJuigXycpJNOiLT9or7mkHSkGFHgGW3/p6cNNYEKZBa7Hhp1FdX/cvqYWWYhRLpfoZOedeA7aRbYiOB3vW/dvA==" + "requested": "[9.0.1, )", + "resolved": "9.0.1", + "contentHash": "aadciFNDT5bnylaYUkKal+s5hF7yU/lmZxImQWAlk1438iPqK1Uf79H5ylELpyLIU49HL5ql+tnWBihp3WVLCA==" }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.10.0, )", - "resolved": "17.10.0", - "contentHash": "0/2HeACkaHEYU3wc83YlcD2Fi4LMtECJjqrtvw0lPi9DCEa35zSPt1j4fuvM8NagjDqJuh1Ja35WcRtn1Um6/A==", + "requested": "[17.13.0, )", + "resolved": "17.13.0", + "contentHash": "W19wCPizaIC9Zh47w8wWI/yxuqR7/dtABwOrc8r2jX/8mUNxM2vw4fXDh+DJTeogxV+KzKwg5jNNGQVwf3LXyA==", "dependencies": { - "Microsoft.CodeCoverage": "17.10.0", - "Microsoft.TestPlatform.TestHost": "17.10.0" + "Microsoft.CodeCoverage": "17.13.0", + "Microsoft.TestPlatform.TestHost": "17.13.0" } }, "Microsoft.SourceLink.GitHub": { @@ -34,33 +28,11 @@ "Microsoft.SourceLink.Common": "8.0.0" } }, - "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" - } + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g==" }, "Speckle.InterfaceGenerator": { "type": "Direct", @@ -68,19 +40,22 @@ "resolved": "0.9.6", "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" }, - "DiffEngine": { - "type": "Transitive", - "resolved": "11.3.0", - "contentHash": "k0ZgZqd09jLZQjR8FyQbSQE86Q7QZnjEzq1LPHtj1R2AoWO8sjV5x+jlSisL7NZAbUOI4y+7Bog8gkr9WIRBGw==", + "xunit": { + "type": "Direct", + "requested": "[2.9.3, )", + "resolved": "2.9.3", + "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==", "dependencies": { - "EmptyFiles": "4.4.0", - "System.Management": "6.0.1" + "xunit.analyzers": "1.18.0", + "xunit.assert": "2.9.3", + "xunit.core": "[2.9.3]" } }, - "EmptyFiles": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "gwJEfIGS7FhykvtZoscwXj/XwW+mJY6UbAZk+qtLKFUGWC95kfKXnj8VkxsZQnWBxJemM/q664rGLN5nf+OHZw==" + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[3.0.2, )", + "resolved": "3.0.2", + "contentHash": "oXbusR6iPq0xlqoikjdLvzh+wQDkMv9If58myz9MEzldS4nIcp442Btgs2sWbYWV+caEluMe2pQCZ0hUZgPiow==" }, "GraphQL.Client.Abstractions": { "type": "Transitive", @@ -110,17 +85,65 @@ }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "17.10.0", - "contentHash": "yC7oSlnR54XO5kOuHlVOKtxomNNN1BWXX8lK1G2jaPXT9sUok7kCOoA4Pgs0qyFaCtMrNsprztYMeoEGqCm4uA==" + "resolved": "17.13.0", + "contentHash": "9LIUy0y+DvUmEPtbRDw6Bay3rzwqFV8P4efTrK4CZhQle3M/QwLPjISghfcolmEGAPWxuJi6m98ZEfk4VR4Lfg==" }, "Microsoft.Data.Sqlite.Core": { "type": "Transitive", - "resolved": "7.0.7", - "contentHash": "21FRzcJhaTrlv7kTrqr/ltFcSQM2TyuTTPhUcjO8H73od7Bb3QraNW90c7lUucNI/245XPkKZG4fp7/7OsKCSg==", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", "dependencies": { "SQLitePCLRaw.core": "2.1.4" } }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "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.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -128,18 +151,18 @@ }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.10.0", - "contentHash": "KkwhjQevuDj0aBRoPLY6OLAhGqbPUEBuKLbaCs0kUVw29qiOYncdORd4mLVJbn9vGZ7/iFGQ/+AoJl0Tu5Umdg==", + "resolved": "17.13.0", + "contentHash": "bt0E0Dx+iqW97o4A59RCmUmz/5NarJ7LRL+jXbSHod72ibL5XdNm1Ke+UO5tFhBG4VwHLcSjqq9BUSblGNWamw==", "dependencies": { "System.Reflection.Metadata": "1.6.0" } }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.10.0", - "contentHash": "LWpMdfqhHvcUkeMCvNYJO8QlPLlYz9XPPb+ZbaXIKhdmjAV0wqTSrTiW5FLaf7RRZT50AQADDOYMOe0HxDxNgA==", + "resolved": "17.13.0", + "contentHash": "9GGw08Dc3AXspjekdyTdZ/wYWFlxbgcF0s7BKxzVX+hzAwpifDOdxM+ceVaaJSQOwqt3jtuNlHn3XTpKUS9x9Q==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.10.0", + "Microsoft.TestPlatform.ObjectModel": "17.13.0", "Newtonsoft.Json": "13.0.1" } }, @@ -178,18 +201,10 @@ "SQLitePCLRaw.core": "2.1.4" } }, - "System.CodeDom": { + "System.ComponentModel.Annotations": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA==" - }, - "System.Management": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "10J1D0h/lioojphfJ4Fuh5ZUThT/xOVHdV9roGBittKKNP2PMjrvibEdbVTGZcPra1399Ja3tqIJLyQrc5Wmhg==", - "dependencies": { - "System.CodeDom": "6.0.0" - } + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" }, "System.Memory": { "type": "Transitive", @@ -206,36 +221,81 @@ "resolved": "1.6.0", "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw==" + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "1.18.0", + "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ==" + }, + "xunit.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]", + "xunit.extensibility.execution": "[2.9.3]" + } + }, + "xunit.extensibility.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==", + "dependencies": { + "xunit.abstractions": "2.0.3" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]" + } + }, "speckle.sdk": { "type": "Project", "dependencies": { "GraphQL.Client": "[6.0.0, )", "Microsoft.CSharp": "[4.7.0, )", - "Microsoft.Data.Sqlite": "[7.0.7, )", - "Polly": "[7.2.3, )", - "Polly.Contrib.WaitAndRetry": "[1.1.1, )", - "Polly.Extensions.Http": "[3.0.0, )", - "Speckle.DoubleNumerics": "[4.0.1, )", + "Microsoft.Data.Sqlite": "[7.0.5, )", + "Microsoft.Extensions.DependencyInjection.Abstractions": "[2.2.0, )", + "Microsoft.Extensions.Logging": "[2.2.0, )", + "Speckle.DoubleNumerics": "[4.1.0, )", "Speckle.Newtonsoft.Json": "[13.0.2, )", - "Speckle.Sdk.Logging": "[1.0.0, )", - "System.Text.Json": "[5.0.2, )" + "Speckle.Sdk.Dependencies": "[1.0.0, )" } }, - "speckle.sdk.logging": { + "speckle.sdk.dependencies": { "type": "Project" }, "speckle.sdk.tests.unit": { "type": "Project", "dependencies": { - "Microsoft.NET.Test.Sdk": "[17.10.0, )", - "NUnit": "[4.1.0, )", - "NUnit3TestAdapter": "[4.5.0, )", - "Shouldly": "[4.2.1, )", - "Speckle.DoubleNumerics": "[4.0.1, )", + "AwesomeAssertions": "[8.0.0, )", + "Microsoft.Extensions.DependencyInjection": "[2.2.0, )", + "Microsoft.NET.Test.Sdk": "[17.13.0, )", + "Speckle.DoubleNumerics": "[4.1.0, )", "Speckle.Sdk": "[1.0.0, )", - "altcover": "[8.8.74, )" + "altcover": "[9.0.1, )", + "xunit": "[2.9.3, )", + "xunit.runner.visualstudio": "[3.0.2, )" } }, + "AwesomeAssertions": { + "type": "CentralTransitive", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "6fWiV7mGZUzZXzeiW3hWF0nJokuuNm4hnzuqbM3IXHqGYkWnHl65+wNpuQ73xfJXClX0fmfKcTdQ2Ula719IDg==" + }, "GraphQL.Client": { "type": "CentralTransitive", "requested": "[6.0.0, )", @@ -255,40 +315,46 @@ }, "Microsoft.Data.Sqlite": { "type": "CentralTransitive", - "requested": "[7.0.7, )", - "resolved": "7.0.7", - "contentHash": "tiNmV1oPy+Z2R7Wd0bPB/FxCr8B+/5q11OpDMG751GA/YuOL7MZrBFfzv5oFRlFe08K6sjrnbrauzzGIeNrzLQ==", + "requested": "[7.0.5, )", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", "dependencies": { - "Microsoft.Data.Sqlite.Core": "7.0.7", + "Microsoft.Data.Sqlite.Core": "7.0.5", "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" } }, - "Polly": { + "Microsoft.Extensions.DependencyInjection": { "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==", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==", "dependencies": { - "Polly": "7.1.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" } }, "Speckle.DoubleNumerics": { "type": "CentralTransitive", - "requested": "[4.0.1, )", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" }, "Speckle.Newtonsoft.Json": { "type": "CentralTransitive", @@ -296,11 +362,11 @@ "resolved": "13.0.2", "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" }, - "System.Text.Json": { + "xunit.assert": { "type": "CentralTransitive", - "requested": "[5.0.2, )", - "resolved": "5.0.2", - "contentHash": "I47dVIGiV6SfAyppphxqupertT/5oZkYLDCX6vC3HpOI4ZLjyoKAreUoem2ie6G0RbRuFrlqz/PcTQjfb2DOfQ==" + "requested": "[2.9.3, )", + "resolved": "2.9.3", + "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA==" } } } diff --git a/tests/Speckle.Sdk.Tests.Performance/Benchmarks/CryptSha256Hash.cs b/tests/Speckle.Sdk.Tests.Performance/Benchmarks/CryptSha256Hash.cs new file mode 100644 index 00000000..39889f50 --- /dev/null +++ b/tests/Speckle.Sdk.Tests.Performance/Benchmarks/CryptSha256Hash.cs @@ -0,0 +1,31 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Engines; +using Speckle.Sdk.Helpers; + +namespace Speckle.Sdk.Tests.Performance.Benchmarks; + +[MemoryDiagnoser] +[SimpleJob(RunStrategy.Throughput)] +public class CryptSha256Hash +{ + private string testData; + + [GlobalSetup] + public void Setup() + { + var random = new Random(420); + testData = new string(Enumerable.Range(0, 10_000_000).Select(_ => (char)random.Next(32, 127)).ToArray()); + } + + [Benchmark] + public string Sha256() + { + return Crypt.Sha256(testData); + } + + [Benchmark] + public string Sha256_Span() + { + return Crypt.Sha256(testData.AsSpan()); + } +} diff --git a/tests/Speckle.Sdk.Tests.Performance/Benchmarks/GeneralDeserializerTest.cs b/tests/Speckle.Sdk.Tests.Performance/Benchmarks/GeneralDeserializerTest.cs new file mode 100644 index 00000000..e6cc814a --- /dev/null +++ b/tests/Speckle.Sdk.Tests.Performance/Benchmarks/GeneralDeserializerTest.cs @@ -0,0 +1,83 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Engines; +using Microsoft.Extensions.DependencyInjection; +using Speckle.Objects.Geometry; +using Speckle.Sdk.Credentials; +using Speckle.Sdk.Helpers; +using Speckle.Sdk.Host; +using Speckle.Sdk.Logging; +using Speckle.Sdk.Models; +using Speckle.Sdk.Serialisation.V2; +using Speckle.Sdk.Serialisation.V2.Receive; +using Speckle.Sdk.SQLite; + +namespace Speckle.Sdk.Tests.Performance.Benchmarks; + +/// +/// How many threads on our Deserializer is optimal +/// +[MemoryDiagnoser] +[SimpleJob(RunStrategy.Monitoring, 0, 0, 2)] +public class GeneralDeserializer : IDisposable +{ + private const bool skipCache = true; + + /* + private const string url = "https://latest.speckle.systems/projects/a3ac1b2706/models/59d3b0f3c6"; //small? + private const string streamId = "a3ac1b2706"; + private const string rootId = "7d53bcf28c6696ecac8781684a0aa006";*/ + + + private const string url = "https://latest.speckle.systems/projects/2099ac4b5f/models/da511c4d1e"; //perf? + private const string streamId = "2099ac4b5f"; + private const string rootId = "30fb4cbe6eb2202b9e7b4a4fcc3dd2b6"; + private TestDataHelper _dataSource; + + [GlobalSetup] + public async Task Setup() + { + TypeLoader.Initialize(typeof(Base).Assembly, typeof(Point).Assembly); + _dataSource = new TestDataHelper(); + await _dataSource + .SeedTransport(new Account() { serverInfo = new() { url = url } }, streamId, rootId, skipCache) + .ConfigureAwait(false); + } + + [Benchmark] + public async Task RunTest_New() + { + var sqlite = TestDataHelper + .ServiceProvider.GetRequiredService() + .CreateFromStream(streamId); + var serverObjects = new ServerObjectManager( + TestDataHelper.ServiceProvider.GetRequiredService(), + TestDataHelper.ServiceProvider.GetRequiredService(), + new Uri(url), + streamId, + null + ); + await using var process = new DeserializeProcess( + sqlite, + serverObjects, + null, + new BaseDeserializer(new ObjectDeserializerFactory()), + default, + new(skipCache) + ); + return await process.Deserialize(rootId).ConfigureAwait(false); + } + + /* + [Benchmark] + public async Task RunTest_Old() + { + SpeckleObjectDeserializer sut = new() { ReadTransport = _dataSource.Transport }; + string data = await _dataSource.Transport.GetObject(_dataSource.ObjectId)!; + return await sut.DeserializeAsync(data); + } + */ + [GlobalCleanup] + public void Cleanup() => Dispose(); + + public void Dispose() => _dataSource.Dispose(); +} diff --git a/tests/Speckle.Sdk.Tests.Performance/Benchmarks/GeneralReceiveTest.cs b/tests/Speckle.Sdk.Tests.Performance/Benchmarks/GeneralReceiveTest.cs new file mode 100644 index 00000000..cb62d9dc --- /dev/null +++ b/tests/Speckle.Sdk.Tests.Performance/Benchmarks/GeneralReceiveTest.cs @@ -0,0 +1,65 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Engines; +using Microsoft.Extensions.DependencyInjection; +using Speckle.Objects.Geometry; +using Speckle.Sdk.Api; +using Speckle.Sdk.Credentials; +using Speckle.Sdk.Host; +using Speckle.Sdk.Models; +using Speckle.Sdk.Transports; + +namespace Speckle.Sdk.Tests.Performance.Benchmarks; + +/// +/// How many threads on our Deserializer is optimal +/// +[MemoryDiagnoser] +[SimpleJob(RunStrategy.Monitoring, 0, 0, 1)] +public class GeneralReceiveTest : IDisposable +{ + /* + private const string url = "https://latest.speckle.systems/projects/a3ac1b2706/models/59d3b0f3c6"; //small? + private const string streamId = "a3ac1b2706";S + private const string rootId = "7d53bcf28c6696ecac8781684a0aa006";*/ + + + private const string url = "https://latest.speckle.systems/projects/2099ac4b5f/models/da511c4d1e"; //perf? + private readonly Uri _baseUrl = new("https://latest.speckle.systems"); + private const string streamId = "2099ac4b5f"; + private const string rootId = "30fb4cbe6eb2202b9e7b4a4fcc3dd2b6"; + private TestDataHelper _dataSource; + private IOperations _operations; + private ITransport remoteTransport; + + [GlobalSetup] + public async Task Setup() + { + TypeLoader.Initialize(typeof(Base).Assembly, typeof(Point).Assembly); + _dataSource = new TestDataHelper(); + var acc = new Account() { serverInfo = new() { url = url } }; + await _dataSource.SeedTransport(acc, streamId, rootId, true).ConfigureAwait(false); + _operations = TestDataHelper.ServiceProvider.GetRequiredService(); + // await _operations.Receive2(_baseUrl, streamId, rootId, null); + + remoteTransport = TestDataHelper + .ServiceProvider.GetRequiredService() + .Create(acc, streamId); + } + + [Benchmark] + public async Task RunTest_Receive() + { + return await _operations.Receive(rootId, remoteTransport, _dataSource.Transport); + } + + [Benchmark] + public async Task RunTest_Receive2() + { + return await _operations.Receive2(_baseUrl, streamId, rootId, null, null, default); + } + + [GlobalCleanup] + public void Cleanup() => Dispose(); + + public void Dispose() => _dataSource.Dispose(); +} diff --git a/tests/Speckle.Sdk.Tests.Performance/Benchmarks/GeneralSendTest.cs b/tests/Speckle.Sdk.Tests.Performance/Benchmarks/GeneralSendTest.cs new file mode 100644 index 00000000..994ea723 --- /dev/null +++ b/tests/Speckle.Sdk.Tests.Performance/Benchmarks/GeneralSendTest.cs @@ -0,0 +1,85 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Engines; +using Microsoft.Extensions.DependencyInjection; +using Speckle.Objects.Geometry; +using Speckle.Sdk.Api; +using Speckle.Sdk.Api.GraphQL.Enums; +using Speckle.Sdk.Api.GraphQL.Models; +using Speckle.Sdk.Common; +using Speckle.Sdk.Credentials; +using Speckle.Sdk.Host; +using Speckle.Sdk.Models; +using Speckle.Sdk.Serialisation; +using Speckle.Sdk.Transports; +using Version = Speckle.Sdk.Api.GraphQL.Models.Version; + +namespace Speckle.Sdk.Tests.Performance.Benchmarks; + +/// +/// How many threads on our Deserializer is optimal +/// +[MemoryDiagnoser] +[SimpleJob(RunStrategy.Monitoring, iterationCount: 1)] +public class GeneralSendTest +{ + private Base _testData; + private IOperations _operations; + private ServerTransport _remote; + private Account acc; + private Client client; + + private Project _project; + + [GlobalSetup] + public async Task Setup() + { + TypeLoader.Initialize(typeof(Base).Assembly, typeof(Point).Assembly); + using var dataSource = new TestDataHelper(); + await dataSource + .SeedTransport( + new Account() { serverInfo = new() { url = "https://latest.speckle.systems/" } }, + "2099ac4b5f", + "30fb4cbe6eb2202b9e7b4a4fcc3dd2b6", + false + ) + .ConfigureAwait(false); + + SpeckleObjectDeserializer deserializer = new() { ReadTransport = dataSource.Transport }; + string data = await dataSource.Transport.GetObject(dataSource.ObjectId).NotNull(); + _testData = await deserializer.DeserializeAsync(data).NotNull(); + _operations = TestDataHelper.ServiceProvider.GetRequiredService(); + + acc = TestDataHelper + .ServiceProvider.GetRequiredService() + .GetAccounts("https://latest.speckle.systems") + .First(); + + client = TestDataHelper.ServiceProvider.GetRequiredService().Create(acc); + + _project = await client.Project.Create( + new($"General Send Test run {Guid.NewGuid()}", null, ProjectVisibility.Public) + ); + _remote = TestDataHelper.ServiceProvider.GetRequiredService().Create(acc, _project.id); + } + + [Benchmark(Baseline = true)] + public async Task Send_old() + { + using SQLiteTransport local = new(); + var res = await _operations.Send(_testData, [_remote, local]); + return await TagVersion($"Send_old {Guid.NewGuid()}", res.rootObjId); + } + + [Benchmark] + public async Task Send_new() + { + var res = await _operations.Send2(new(acc.serverInfo.url), _project.id, acc.token, _testData, null, default); + return await TagVersion($"Send_new {Guid.NewGuid()}", res.RootId); + } + + private async Task TagVersion(string name, string objectId) + { + var model = await client.Model.Create(new(name, null, _project.id)); + return await client.Version.Create(new(objectId, model.id, _project.id)); + } +} diff --git a/tests/Speckle.Sdk.Tests.Performance/Benchmarks/GeneralSerializerTest.cs b/tests/Speckle.Sdk.Tests.Performance/Benchmarks/GeneralSerializerTest.cs new file mode 100644 index 00000000..48b544dd --- /dev/null +++ b/tests/Speckle.Sdk.Tests.Performance/Benchmarks/GeneralSerializerTest.cs @@ -0,0 +1,80 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Engines; +using Speckle.Objects.Geometry; +using Speckle.Sdk.Common; +using Speckle.Sdk.Credentials; +using Speckle.Sdk.Host; +using Speckle.Sdk.Models; +using Speckle.Sdk.Serialisation; +using Speckle.Sdk.Transports; + +namespace Speckle.Sdk.Tests.Performance.Benchmarks; + +/// +/// How many threads on our Deserializer is optimal +/// +[MemoryDiagnoser] +[SimpleJob(RunStrategy.Monitoring)] +public class GeneralSerializerTest +{ + private Base _testData; + + [GlobalSetup] + public async Task Setup() + { + TypeLoader.Initialize(typeof(Base).Assembly, typeof(Point).Assembly); + using var dataSource = new TestDataHelper(); + await dataSource + .SeedTransport( + new Account() + { + serverInfo = new() { url = "https://latest.speckle.systems/projects/2099ac4b5f/models/da511c4d1e" }, + }, + "2099ac4b5f", + "30fb4cbe6eb2202b9e7b4a4fcc3dd2b6", + false + ) + .ConfigureAwait(false); + + SpeckleObjectDeserializer deserializer = new() { ReadTransport = dataSource.Transport }; + string data = await dataSource.Transport.GetObject(dataSource.ObjectId).NotNull(); + _testData = await deserializer.DeserializeAsync(data).NotNull(); + } + + [Benchmark] + public string RunTest() + { + var remote = new NullTransport(); + SpeckleObjectSerializer sut = new([remote]); + var x = sut.Serialize(_testData); + return x; + } +} + +public class NullTransport : ITransport +{ + public string TransportName { get; set; } = ""; + public Dictionary TransportContext { get; } = new(); + public TimeSpan Elapsed { get; } = TimeSpan.Zero; + public CancellationToken CancellationToken { get; set; } + public IProgress OnProgressAction { get; set; } + + public void BeginWrite() { } + + public void EndWrite() { } + + public void SaveObject(string id, string serializedObject) { } + + public Task WriteComplete() + { + return Task.CompletedTask; + } + + public Task GetObject(string id) => throw new NotImplementedException(); + + public Task CopyObjectAndChildren(string id, ITransport targetTransport) => + throw new NotImplementedException(); + + public Task> HasObjects(IReadOnlyList objectIds) => + throw new NotImplementedException(); +} diff --git a/tests/Speckle.Sdk.Tests.Performance/Program.cs b/tests/Speckle.Sdk.Tests.Performance/Program.cs new file mode 100644 index 00000000..75694359 --- /dev/null +++ b/tests/Speckle.Sdk.Tests.Performance/Program.cs @@ -0,0 +1,8 @@ +// See https://aka.ms/new-console-template for more information + +using BenchmarkDotNet.Running; + +BenchmarkSwitcher.FromAssemblies([typeof(Program).Assembly]).Run(args); +// var sut = new GeneralSendTest(); +// await sut.Setup(); +// await sut.Send2(); diff --git a/tests/Speckle.Sdk.Tests.Performance/RegressionTestConfig.cs b/tests/Speckle.Sdk.Tests.Performance/RegressionTestConfig.cs new file mode 100644 index 00000000..54c53006 --- /dev/null +++ b/tests/Speckle.Sdk.Tests.Performance/RegressionTestConfig.cs @@ -0,0 +1,52 @@ +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Engines; +using BenchmarkDotNet.Jobs; + +namespace Speckle.Sdk.Tests.Performance; + +[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class, AllowMultiple = true)] +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") + .WithStrategy(strategy) + .WithLaunchCount(launchCount) + .WithWarmupCount(warmupCount) + .WithIterationCount(iterationCount) + ); + } + + bool isBaseline = true; + foreach (var version in nugetVersions) + { + jobs.Add( + new Job(version) + .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.Sdk.Tests.Performance/Speckle.Sdk.Tests.Performance.csproj b/tests/Speckle.Sdk.Tests.Performance/Speckle.Sdk.Tests.Performance.csproj new file mode 100644 index 00000000..5dcc0434 --- /dev/null +++ b/tests/Speckle.Sdk.Tests.Performance/Speckle.Sdk.Tests.Performance.csproj @@ -0,0 +1,22 @@ + + + + Exe + net8.0 + enable + disable + true + + + + + + + + + + + + + + diff --git a/tests/Speckle.Sdk.Tests.Performance/TestDataHelper.cs b/tests/Speckle.Sdk.Tests.Performance/TestDataHelper.cs new file mode 100644 index 00000000..9ca4a397 --- /dev/null +++ b/tests/Speckle.Sdk.Tests.Performance/TestDataHelper.cs @@ -0,0 +1,74 @@ +using Microsoft.Data.Sqlite; +using Microsoft.Extensions.DependencyInjection; +using Speckle.Sdk.Api; +using Speckle.Sdk.Credentials; +using Speckle.Sdk.Host; +using Speckle.Sdk.Models; +using Speckle.Sdk.Transports; + +namespace Speckle.Sdk.Tests.Performance; + +public sealed class TestDataHelper : IDisposable +{ + private static readonly string s_basePath = $"./temp {Guid.NewGuid()}"; + public SQLiteTransport Transport { get; private set; } + + public static IServiceProvider ServiceProvider { get; set; } + public string ObjectId { get; private set; } + + public TestDataHelper() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSpeckleSdk(HostApplications.Navisworks, HostAppVersion.v2023, "Test"); + ServiceProvider = serviceCollection.BuildServiceProvider(); + } + + public async Task SeedTransport(Account account, string streamId, string objectId, bool skipCache) + { + // Transport = new SQLiteTransport(s_basePath, APPLICATION_NAME); + Transport = new SQLiteTransport(); + + //seed SQLite transport with test data + ObjectId = await SeedTransport(account, streamId, objectId, Transport, skipCache).ConfigureAwait(false); + } + + public async Task SeedTransport( + Account account, + string streamId, + string objectId, + ITransport transport, + bool skipCache + ) + { + if (!skipCache) + { + using ServerTransport remoteTransport = ServiceProvider + .GetRequiredService() + .Create(account, 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 ServiceProvider + .GetRequiredService() + .Receive(ObjectId, null, Transport) + .ConfigureAwait(false); + } + + public void Dispose() + { + Transport.Dispose(); + SqliteConnection.ClearAllPools(); + if (Directory.Exists(s_basePath)) + { + Directory.Delete(s_basePath, true); + } + } +} diff --git a/tests/Speckle.Sdk.Tests.Performance/packages.lock.json b/tests/Speckle.Sdk.Tests.Performance/packages.lock.json new file mode 100644 index 00000000..7a4a7024 --- /dev/null +++ b/tests/Speckle.Sdk.Tests.Performance/packages.lock.json @@ -0,0 +1,430 @@ +{ + "version": 2, + "dependencies": { + "net8.0": { + "BenchmarkDotNet": { + "type": "Direct", + "requested": "[0.14.0, )", + "resolved": "0.14.0", + "contentHash": "eIPSDKi3oni734M1rt/XJAwGQQOIf9gLjRRKKJ0HuVy3vYd7gnmAIX1bTjzI9ZbAY/nPddgqqgM/TeBYitMCIg==", + "dependencies": { + "BenchmarkDotNet.Annotations": "0.14.0", + "CommandLineParser": "2.9.1", + "Gee.External.Capstone": "2.3.0", + "Iced": "1.17.0", + "Microsoft.CodeAnalysis.CSharp": "4.1.0", + "Microsoft.Diagnostics.Runtime": "2.2.332302", + "Microsoft.Diagnostics.Tracing.TraceEvent": "3.1.8", + "Microsoft.DotNet.PlatformAbstractions": "3.1.6", + "Perfolizer": "[0.3.17]", + "System.Management": "5.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Direct", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.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" + } + }, + "PolySharp": { + "type": "Direct", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g==" + }, + "Speckle.InterfaceGenerator": { + "type": "Direct", + "requested": "[0.9.6, )", + "resolved": "0.9.6", + "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" + }, + "BenchmarkDotNet.Annotations": { + "type": "Transitive", + "resolved": "0.14.0", + "contentHash": "CUDCg6bgHrDzhjnA+IOBl5gAo8Y5hZ2YSs7MBXrYMlMKpBZqrD5ez0537uDveOkcf+YWAoK+S4sMcuWPbIz8bw==" + }, + "CommandLineParser": { + "type": "Transitive", + "resolved": "2.9.1", + "contentHash": "OE0sl1/sQ37bjVsPKKtwQlWDgqaxWgtme3xZz7JssWUzg5JpMIyHgCTY9MVMxOg48fJ1AgGT3tgdH5m/kQ5xhA==" + }, + "Gee.External.Capstone": { + "type": "Transitive", + "resolved": "2.3.0", + "contentHash": "2ap/rYmjtzCOT8hxrnEW/QeiOt+paD8iRrIcdKX0cxVwWLFa1e+JDBNeECakmccXrSFeBQuu5AV8SNkipFMMMw==" + }, + "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==" + }, + "Iced": { + "type": "Transitive", + "resolved": "1.17.0", + "contentHash": "8x+HCVTl/HHTGpscH3vMBhV8sknN/muZFw9s3TsI8SA6+c43cOTCi2+jE4KsU8pNLbJ++iF2ZFcpcXHXtDglnw==" + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.CodeAnalysis.Analyzers": { + "type": "Transitive", + "resolved": "3.3.3", + "contentHash": "j/rOZtLMVJjrfLRlAMckJLPW/1rze9MT1yfWqSIbUPGRu1m1P0fuo9PmqapwsmePfGB5PJrudQLvmUOAMF0DqQ==" + }, + "Microsoft.CodeAnalysis.Common": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "bNzTyxP3iD5FPFHfVDl15Y6/wSoI7e3MeV0lOaj9igbIKTjgrmuw6LoVJ06jUNFA7+KaDC/OIsStWl/FQJz6sQ==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.3.3", + "System.Collections.Immutable": "5.0.0", + "System.Memory": "4.5.4", + "System.Reflection.Metadata": "5.0.0", + "System.Runtime.CompilerServices.Unsafe": "5.0.0", + "System.Text.Encoding.CodePages": "4.5.1", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.CodeAnalysis.CSharp": { + "type": "Transitive", + "resolved": "4.1.0", + "contentHash": "sbu6kDGzo9bfQxuqWpeEE7I9P30bSuZEnpDz9/qz20OU6pm79Z63+/BsAzO2e/R/Q97kBrpj647wokZnEVr97w==", + "dependencies": { + "Microsoft.CodeAnalysis.Common": "[4.1.0]" + } + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "Microsoft.Diagnostics.NETCore.Client": { + "type": "Transitive", + "resolved": "0.2.251802", + "contentHash": "bqnYl6AdSeboeN4v25hSukK6Odm6/54E3Y2B8rBvgqvAW0mF8fo7XNRVE2DMOG7Rk0fiuA079QIH28+V+W1Zdg==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "1.1.0", + "Microsoft.Extensions.Logging": "2.1.1" + } + }, + "Microsoft.Diagnostics.Runtime": { + "type": "Transitive", + "resolved": "2.2.332302", + "contentHash": "Hp84ivxSKIMTBzYSATxmUsm3YSXHWivcwiRRbsydGmqujMUK8BAueLN0ssAVEOkOBmh0vjUBhrq7YcroT7VCug==", + "dependencies": { + "Microsoft.Diagnostics.NETCore.Client": "0.2.251802", + "System.Collections.Immutable": "5.0.0", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, + "Microsoft.Diagnostics.Tracing.TraceEvent": { + "type": "Transitive", + "resolved": "3.1.8", + "contentHash": "kl3UMrZKSeSEYZ8rt/GjLUQToREjgQABqfg6PzQBmSlYHTZOKE9ePEOS2xptROQ9SVvngg3QGX51TIT11iZ0wA==", + "dependencies": { + "Microsoft.Win32.Registry": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, + "Microsoft.DotNet.PlatformAbstractions": { + "type": "Transitive", + "resolved": "3.1.6", + "contentHash": "jek4XYaQ/PGUwDKKhwR8K47Uh1189PFzMeLqO83mXrXQVIpARZCcfuDedH50YDTepBkfijCZN5U/vZi++erxtg==" + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "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.NETCore.Platforms": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "Perfolizer": { + "type": "Transitive", + "resolved": "0.3.17", + "contentHash": "FQgtCoF2HFwvzKWulAwBS5BGLlh8pgbrJtOp47jyBwh2CW16juVtacN1azOA2BqdrJXkXTNLNRMo7ZlHHiuAnA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.4", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.4" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg==" + }, + "SQLitePCLRaw.provider.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.4", + "contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.4" + } + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "JPJArwA1kdj8qDAkY2XGjSWoYnqiM7q/3yRNkt6n28Mnn95MuEGkZXUbPBf7qc3IjwrGY5ttQon7yqHZyQJmOQ==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g==" + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" + }, + "System.Management": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "MF1CHaRcC+MLFdnDthv4/bKWBZnlnSpkGqa87pKukQefgEdwtb9zFW6zs0GjPp73qtpYYg4q6PEKbzJbxCpKfw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "Microsoft.Win32.Registry": "5.0.0", + "System.CodeDom": "5.0.0" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==" + }, + "System.Reactive": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==" + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "System.Text.Encoding.CodePages": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "4J2JQXbftjPMppIHJ7IC+VXQ9XfEagN92vZZNoG12i+zReYlim5dMoXFC1Zzg7tsnKDM7JPo5bYfFK4Jheq44w==", + "dependencies": { + "Microsoft.NETCore.Platforms": "2.1.2", + "System.Runtime.CompilerServices.Unsafe": "4.5.2" + } + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==" + }, + "speckle.objects": { + "type": "Project", + "dependencies": { + "Speckle.Sdk": "[1.0.0, )" + } + }, + "speckle.sdk": { + "type": "Project", + "dependencies": { + "GraphQL.Client": "[6.0.0, )", + "Microsoft.CSharp": "[4.7.0, )", + "Microsoft.Data.Sqlite": "[7.0.5, )", + "Microsoft.Extensions.DependencyInjection.Abstractions": "[2.2.0, )", + "Microsoft.Extensions.Logging": "[2.2.0, )", + "Speckle.DoubleNumerics": "[4.1.0, )", + "Speckle.Newtonsoft.Json": "[13.0.2, )", + "Speckle.Sdk.Dependencies": "[1.0.0, )" + } + }, + "speckle.sdk.dependencies": { + "type": "Project" + }, + "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.Bcl.AsyncInterfaces": { + "type": "CentralTransitive", + "requested": "[5.0.0, )", + "resolved": "1.1.0", + "contentHash": "1Am6l4Vpn3/K32daEqZI+FFr96OlZkgwK2LcT3pZ2zWubR5zTPW3/FkO1Rat9kb7oQOa4rxgl9LJHc5tspCWfg==" + }, + "Microsoft.CSharp": { + "type": "CentralTransitive", + "requested": "[4.7.0, )", + "resolved": "4.7.0", + "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + }, + "Microsoft.Data.Sqlite": { + "type": "CentralTransitive", + "requested": "[7.0.5, )", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "7.0.5", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" + } + }, + "Speckle.DoubleNumerics": { + "type": "CentralTransitive", + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" + }, + "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.Sdk.Tests.Unit/Api/ClientResiliencyPolicyTest.cs b/tests/Speckle.Sdk.Tests.Unit/Api/ClientResiliencyPolicyTest.cs new file mode 100644 index 00000000..af764b5d --- /dev/null +++ b/tests/Speckle.Sdk.Tests.Unit/Api/ClientResiliencyPolicyTest.cs @@ -0,0 +1,84 @@ +using System.Diagnostics; +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Speckle.Sdk.Api; +using Speckle.Sdk.Api.GraphQL.Models; +using Speckle.Sdk.Credentials; +using Xunit; + +namespace Speckle.Sdk.Tests.Unit.Api; + +public sealed class GraphQLClientTests : IDisposable +{ + private readonly Client _client; + + public GraphQLClientTests() + { + var serviceProvider = TestServiceSetup.GetServiceProvider(); + _client = serviceProvider + .GetRequiredService() + .Create( + new Account + { + token = "this is a scam", + serverInfo = new ServerInfo { url = "http://goto.testing" }, + } + ); + } + + public void Dispose() => _client?.Dispose(); + + [Fact] + public async Task TestExecuteWithResiliencePoliciesDoesntRetryTaskCancellation() + { + var timer = new Stopwatch(); + timer.Start(); + await Assert.ThrowsAsync(async () => + { + var tokenSource = new CancellationTokenSource(); + tokenSource.Cancel(); + await _client.ExecuteWithResiliencePolicies( + async () => + await Task.Run( + async () => + { + await Task.Delay(1000); + return "foo"; + }, + tokenSource.Token + ) + ); + }); + timer.Stop(); + timer.ElapsedMilliseconds.Should().BeLessThan(1000); + + // the default retry policy would retry 5 times with 1 second jitter backoff each + // if the elapsed is less than a second, this was def not retried + } + + [Fact] + public async Task TestExecuteWithResiliencePoliciesRetry() + { + var counter = 0; + var maxRetryCount = 5; + var expectedResult = "finally it finishes"; + var timer = new Stopwatch(); + timer.Start(); + var result = await _client.ExecuteWithResiliencePolicies(() => + { + counter++; + if (counter < maxRetryCount) + { + throw new SpeckleGraphQLInternalErrorException(); + } + + return Task.FromResult(expectedResult); + }); + timer.Stop(); + // The baseline for wait is 1 seconds between the jittered retry + timer.ElapsedMilliseconds.Should().BeGreaterThanOrEqualTo(5000); + counter.Should().Be(maxRetryCount); + } + + public class FakeGqlResponseModel { } +} diff --git a/tests/Speckle.Sdk.Tests.Unit/Api/GraphQLClient.cs b/tests/Speckle.Sdk.Tests.Unit/Api/GraphQLClient.cs deleted file mode 100644 index 535608a8..00000000 --- a/tests/Speckle.Sdk.Tests.Unit/Api/GraphQLClient.cs +++ /dev/null @@ -1,120 +0,0 @@ -using System.Diagnostics; -using GraphQL; -using NUnit.Framework; -using Speckle.Sdk.Api; -using Speckle.Sdk.Api.GraphQL; -using Speckle.Sdk.Api.GraphQL.Models; -using Speckle.Sdk.Credentials; - -namespace Speckle.Sdk.Tests.Unit.Api; - -[TestOf(typeof(Client))] -public sealed class GraphQLClientTests : IDisposable -{ - private Client _client; - - [OneTimeSetUp] - public void Setup() - { - _client = new Client( - new Account - { - token = "this is a scam", - serverInfo = new ServerInfo { url = "http://goto.testing" } - } - ); - } - - public void Dispose() - { - _client?.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(SpeckleGraphQLInternalErrorException), - new Map { { "code", "INTERNAL_SERVER_ERROR" } } - ); - yield return new TestCaseData(typeof(SpeckleGraphQLException), new Map { { "foo", "bar" } }); - } - - [Test, TestCaseSource(nameof(ErrorCases))] - public void TestExceptionThrowingFromGraphQLErrors(Type exType, Map extensions) - { - Assert.Throws( - exType, - () => - _client.MaybeThrowFromGraphQLErrors( - new GraphQLRequest(), - new GraphQLResponse - { - Errors = new GraphQLError[] { new() { Extensions = extensions } } - } - ) - ); - } - - [Test] - public void TestMaybeThrowsDoesntThrowForNoErrors() - { - Assert.DoesNotThrow(() => _client.MaybeThrowFromGraphQLErrors(new GraphQLRequest(), new GraphQLResponse())); - } - - [Test] - public void TestExecuteWithResiliencePoliciesDoesntRetryTaskCancellation() - { - var timer = new Stopwatch(); - timer.Start(); - Assert.ThrowsAsync(async () => - { - var tokenSource = new CancellationTokenSource(); - tokenSource.Cancel(); - await _client.ExecuteWithResiliencePolicies( - async () => - await Task.Run( - async () => - { - await Task.Delay(1000); - return "foo"; - }, - tokenSource.Token - ) - ); - }); - timer.Stop(); - var elapsed = timer.ElapsedMilliseconds; - - // the default retry policy would retry 5 times with 1 second jitter backoff each - // if the elapsed is less than a second, this was def not retried - Assert.That(elapsed, Is.LessThan(1000)); - } - - [Test] - public async Task TestExecuteWithResiliencePoliciesRetry() - { - var counter = 0; - var maxRetryCount = 5; - var expectedResult = "finally it finishes"; - var timer = new Stopwatch(); - timer.Start(); - var result = await _client.ExecuteWithResiliencePolicies(() => - { - counter++; - if (counter < maxRetryCount) - { - throw new SpeckleGraphQLInternalErrorException(new GraphQLRequest(), new GraphQLResponse()); - } - - return Task.FromResult(expectedResult); - }); - timer.Stop(); - // The baseline for wait is 1 seconds between the jittered retry - Assert.That(timer.ElapsedMilliseconds, Is.GreaterThanOrEqualTo(5000)); - Assert.That(counter, Is.EqualTo(maxRetryCount)); - } - - public class FakeGqlResponseModel { } -} diff --git a/tests/Speckle.Sdk.Tests.Unit/Api/GraphQLErrorHandler.cs b/tests/Speckle.Sdk.Tests.Unit/Api/GraphQLErrorHandler.cs new file mode 100644 index 00000000..3a06614f --- /dev/null +++ b/tests/Speckle.Sdk.Tests.Unit/Api/GraphQLErrorHandler.cs @@ -0,0 +1,41 @@ +using FluentAssertions; +using GraphQL; +using Speckle.Sdk.Api; +using Speckle.Sdk.Api.GraphQL; +using Xunit; + +namespace Speckle.Sdk.Tests.Unit.Api; + +public class GraphQLErrorHandlerTests +{ + public static IEnumerable ErrorCases() + { + yield return [typeof(SpeckleGraphQLForbiddenException), new Map { { "code", "FORBIDDEN" } }]; + yield return [typeof(SpeckleGraphQLForbiddenException), new Map { { "code", "UNAUTHENTICATED" } }]; + yield return [typeof(SpeckleGraphQLInternalErrorException), new Map { { "code", "INTERNAL_SERVER_ERROR" } }]; + yield return [typeof(SpeckleGraphQLStreamNotFoundException), new Map { { "code", "STREAM_NOT_FOUND" } }]; + yield return [typeof(SpeckleGraphQLBadInputException), new Map { { "code", "BAD_USER_INPUT" } }]; + yield return [typeof(SpeckleGraphQLInvalidQueryException), new Map { { "code", "GRAPHQL_PARSE_FAILED" } }]; + yield return [typeof(SpeckleGraphQLInvalidQueryException), new Map { { "code", "GRAPHQL_VALIDATION_FAILED" } }]; + yield return [typeof(SpeckleGraphQLException), new Map { { "foo", "bar" } }]; + yield return [typeof(SpeckleGraphQLException), new Map { { "code", "CUSTOM_THING" } }]; + } + + [Theory] + [MemberData(nameof(ErrorCases))] + public void TestExceptionThrowingFromGraphQLErrors(Type exType, Map extensions) + { + var ex = Assert.Throws( + () => + new GraphQLResponse + { + Errors = [new() { Extensions = extensions }], + }.EnsureGraphQLSuccess() + ); + ex.InnerExceptions.Count.Should().Be(1); + ex.InnerExceptions[0].Should().BeOfType(exType); + } + + [Fact] + public void TestMaybeThrowsDoesntThrowForNoErrors() => new GraphQLResponse().EnsureGraphQLSuccess(); +} diff --git a/tests/Speckle.Sdk.Tests.Unit/Api/HelpersTests.cs b/tests/Speckle.Sdk.Tests.Unit/Api/HelpersTests.cs deleted file mode 100644 index 2ae8fa95..00000000 --- a/tests/Speckle.Sdk.Tests.Unit/Api/HelpersTests.cs +++ /dev/null @@ -1,35 +0,0 @@ -using NUnit.Framework; - -namespace Speckle.Sdk.Tests.Unit.Api; - -[TestFixture] -[TestOf(typeof(Sdk.Api.Helpers))] -public class HelpersTests -{ - [Test] - [TestCase(30, "just now")] - [TestCase(60, "1 minute ago")] - [TestCase(60 * 2, "2 minutes ago")] - [TestCase(60 * 60 * 1, "1 hour ago")] - [TestCase(60 * 60 * 2, "2 hours ago")] - [TestCase(60 * 60 * 24 * 1, "1 day ago")] - [TestCase(60 * 60 * 24 * 2, "2 days ago")] - [TestCase(60 * 60 * 24 * 7 * 1, "1 week ago")] - [TestCase(60 * 60 * 24 * 7 * 2, "2 weeks ago")] - [TestCase(60 * 60 * 24 * 31 * 1, "1 month ago")] - [TestCase(60 * 60 * 24 * 31 * 2, "2 months ago")] - [TestCase(60 * 60 * 24 * 365 * 1, "1 year ago")] - [TestCase(60 * 60 * 24 * 365 * 2, "2 years ago")] - public void TimeAgo_DisplaysTextCorrectly(int secondsAgo, string expectedText) - { - // Get current time and subtract the input amount - var dateTime = DateTime.UtcNow; - - dateTime = dateTime.Subtract(new TimeSpan(0, 0, secondsAgo)); - - // Get the timeAgo text representation - var actual = Sdk.Api.Helpers.TimeAgo(dateTime); - - Assert.That(actual, Is.EqualTo(expectedText)); - } -} diff --git a/tests/Speckle.Sdk.Tests.Unit/Api/Operations/ClosureTests.cs b/tests/Speckle.Sdk.Tests.Unit/Api/Operations/ClosureTests.cs index a8a3e98b..1240254e 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Api/Operations/ClosureTests.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Api/Operations/ClosureTests.cs @@ -1,25 +1,29 @@ +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; -using NUnit.Framework; +using Speckle.Sdk.Api; using Speckle.Sdk.Common; using Speckle.Sdk.Host; using Speckle.Sdk.Models; using Speckle.Sdk.Tests.Unit.Host; using Speckle.Sdk.Transports; +using Xunit; namespace Speckle.Sdk.Tests.Unit.Api.Operations; -[TestFixture] -[TestOf(typeof(Sdk.Api.Operations))] public class Closures { - [SetUp] - public void Setup() + private readonly IOperations _operations; + + public Closures() { TypeLoader.Reset(); TypeLoader.Initialize(typeof(Base).Assembly, typeof(TableLegFixture).Assembly); + var serviceProvider = TestServiceSetup.GetServiceProvider(); + _operations = serviceProvider.GetRequiredService(); } - [Test(Description = "Checks whether closures are generated correctly by the serialiser.")] + [Fact(DisplayName = "Checks whether closures are generated correctly by the serialiser.")] public async Task CorrectDecompositionTracking() { var d5 = new Base(); @@ -45,36 +49,36 @@ public class Closures var transport = new MemoryTransport(); - var sendResult = await Sdk.Api.Operations.Send(d1, transport, false); + var sendResult = await _operations.Send(d1, transport, false); - var test = await Sdk.Api.Operations.Receive(sendResult.rootObjId, localTransport: transport); + var test = await _operations.Receive(sendResult.rootObjId, localTransport: transport); test.id.NotNull(); - Assert.That(d1.GetId(true), Is.EqualTo(test.id)); + d1.GetId(true).Should().BeEquivalentTo((test.id)); var d1_ = NotNullExtensions.NotNull(JsonConvert.DeserializeObject(transport.Objects[d1.GetId(true)])); var d2_ = NotNullExtensions.NotNull(JsonConvert.DeserializeObject(transport.Objects[d2.GetId(true)])); var d3_ = NotNullExtensions.NotNull(JsonConvert.DeserializeObject(transport.Objects[d3.GetId(true)])); - var d4_ = JsonConvert.DeserializeObject(transport.Objects[d4.GetId(true)]); - var d5_ = JsonConvert.DeserializeObject(transport.Objects[d5.GetId(true)]); + JsonConvert.DeserializeObject(transport.Objects[d4.GetId(true)]); + JsonConvert.DeserializeObject(transport.Objects[d5.GetId(true)]); var depthOf_d5_in_d1 = int.Parse((string)d1_.__closure[d5.GetId(true)]); - Assert.That(depthOf_d5_in_d1, Is.EqualTo(1)); + depthOf_d5_in_d1.Should().Be(1); var depthOf_d4_in_d1 = int.Parse((string)d1_.__closure[d4.GetId(true)]); - Assert.That(depthOf_d4_in_d1, Is.EqualTo(3)); + depthOf_d4_in_d1.Should().Be(3); var depthOf_d5_in_d3 = int.Parse((string)d3_.__closure[d5.GetId(true)]); - Assert.That(depthOf_d5_in_d3, Is.EqualTo(2)); + depthOf_d5_in_d3.Should().Be(2); var depthOf_d4_in_d3 = int.Parse((string)d3_.__closure[d4.GetId(true)]); - Assert.That(depthOf_d4_in_d3, Is.EqualTo(1)); + depthOf_d4_in_d3.Should().Be(1); var depthOf_d5_in_d2 = int.Parse((string)d2_.__closure[d5.GetId(true)]); - Assert.That(depthOf_d5_in_d2, Is.EqualTo(1)); + depthOf_d5_in_d2.Should().Be(1); } - [Test] + [Fact] public void DescendantsCounting() { Base myBase = new(); @@ -112,18 +116,15 @@ public class Closures myBase["@detachTheDictionary"] = dictionary; - var count = myBase.GetTotalChildrenCount(); - Assert.That(count, Is.EqualTo(112)); + myBase.GetTotalChildrenCount().Should().Be(112); var tableTest = new DiningTable(); - var tableKidsCount = tableTest.GetTotalChildrenCount(); - Assert.That(tableKidsCount, Is.EqualTo(10)); + tableTest.GetTotalChildrenCount().Should().Be(10); // Explicitely test for recurisve references! var recursiveRef = new Base { applicationId = "random" }; recursiveRef["@recursive"] = recursiveRef; - var supriseCount = recursiveRef.GetTotalChildrenCount(); - Assert.That(supriseCount, Is.EqualTo(2)); + recursiveRef.GetTotalChildrenCount().Should().Be(2); } } diff --git a/tests/Speckle.Sdk.Tests.Unit/Api/Operations/OperationsReceiveTests.Exceptional.cs b/tests/Speckle.Sdk.Tests.Unit/Api/Operations/OperationsReceiveTests.Exceptional.cs index 3acafdd7..1079ef52 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Api/Operations/OperationsReceiveTests.Exceptional.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Api/Operations/OperationsReceiveTests.Exceptional.cs @@ -1,41 +1,41 @@ -using NUnit.Framework; -using Speckle.Sdk.Transports; +using Speckle.Sdk.Transports; +using Xunit; namespace Speckle.Sdk.Tests.Unit.Api.Operations; public partial class OperationsReceiveTests { - [Test, TestCaseSource(nameof(TestCases))] - public void Receive_ObjectsDontExist_ExceptionThrown(string id) + [Theory, MemberData(nameof(TestCases))] + public async Task Receive_ObjectsDontExist_ExceptionThrown(string id) { MemoryTransport emptyTransport1 = new(); MemoryTransport emptyTransport2 = new(); - Assert.ThrowsAsync(async () => + await Assert.ThrowsAsync(async () => { - await Sdk.Api.Operations.Receive(id, emptyTransport1, emptyTransport2); + await _operations.Receive(id, emptyTransport1, emptyTransport2); }); } - [Test, TestCaseSource(nameof(TestCases))] - public void Receive_ObjectsDontExistNullRemote_ExceptionThrown(string id) + [Theory, MemberData(nameof(TestCases))] + public async Task Receive_ObjectsDontExistNullRemote_ExceptionThrown(string id) { MemoryTransport emptyTransport = new(); - Assert.ThrowsAsync(async () => + await Assert.ThrowsAsync(async () => { - await Sdk.Api.Operations.Receive(id, null, emptyTransport); + await _operations.Receive(id, null, emptyTransport); }); } - [Test, TestCaseSource(nameof(TestCases))] - public void Receive_OperationCanceled_ExceptionThrown(string id) + [Theory, MemberData(nameof(TestCases))] + public async Task Receive_OperationCanceled_ExceptionThrown(string id) { using CancellationTokenSource ctc = new(); ctc.Cancel(); MemoryTransport emptyTransport2 = new(); - Assert.CatchAsync(async () => + await Assert.ThrowsAsync(async () => { - await Sdk.Api.Operations.Receive(id, _testCaseTransport, emptyTransport2, cancellationToken: ctc.Token); + await _operations.Receive(id, _testCaseTransport, emptyTransport2, cancellationToken: ctc.Token); }); } } diff --git a/tests/Speckle.Sdk.Tests.Unit/Api/Operations/OperationsReceiveTests.cs b/tests/Speckle.Sdk.Tests.Unit/Api/Operations/OperationsReceiveTests.cs index eb70a09b..8df3a466 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Api/Operations/OperationsReceiveTests.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Api/Operations/OperationsReceiveTests.cs @@ -1,34 +1,47 @@ using System.Reflection; -using NUnit.Framework; +using Microsoft.Extensions.DependencyInjection; +using Speckle.Sdk.Api; using Speckle.Sdk.Host; using Speckle.Sdk.Models; using Speckle.Sdk.Transports; +using Xunit; namespace Speckle.Sdk.Tests.Unit.Api.Operations; -[TestFixture, TestOf(nameof(Sdk.Api.Operations.Receive))] -public sealed partial class OperationsReceiveTests +public sealed partial class OperationsReceiveTests : IDisposable { private static readonly Base[] s_testObjects; + private readonly IOperations _operations; + private readonly MemoryTransport _testCaseTransport; static OperationsReceiveTests() { Reset(); s_testObjects = [ - new() { ["string prop"] = "simple test case", ["numerical prop"] = 123, }, + new() { ["string prop"] = "simple test case", ["numerical prop"] = 123 }, new() { ["@detachedProp"] = new Base() { ["the best prop"] = "1234!" } }, new() { ["@detachedList"] = new List { new() { ["the worst prop"] = null } }, ["dictionaryProp"] = new Dictionary { ["dict"] = new() { ["the best prop"] = "" } }, - } + }, ]; } - public static IEnumerable TestCases => s_testObjects.Select(x => x.GetId(true)); + public OperationsReceiveTests() + { + Reset(); + var serviceProvider = TestServiceSetup.GetServiceProvider(); + _operations = serviceProvider.GetRequiredService(); + _testCaseTransport = new MemoryTransport(); - private MemoryTransport _testCaseTransport; + // Simulate a one-time setup action + foreach (var b in s_testObjects) + { + _ = _operations.Send(b, _testCaseTransport, false).GetAwaiter().GetResult(); + } + } private static void Reset() { @@ -36,67 +49,52 @@ public sealed partial class OperationsReceiveTests TypeLoader.Initialize(typeof(Base).Assembly, Assembly.GetExecutingAssembly()); } - [OneTimeSetUp] - public async Task GlobalSetup() + public static IEnumerable TestCases() { - Reset(); - _testCaseTransport = new MemoryTransport(); - foreach (var b in s_testObjects) + foreach (var s in s_testObjects) { - await Sdk.Api.Operations.Send(b, _testCaseTransport, false); + yield return [s.GetId(true)]; } } - [SetUp] - public void Setup() => Reset(); - - [Test, TestCaseSource(nameof(TestCases))] + [Theory] + [MemberData(nameof(TestCases))] public async Task Receive_FromLocal_ExistingObjects(string id) { - Base result = await Sdk.Api.Operations.Receive(id, null, _testCaseTransport); + Base result = await _operations.Receive(id, null, _testCaseTransport); - Assert.That(result.id, Is.EqualTo(id)); + Assert.NotNull(result); + Assert.Equal(id, result.id); } - [Test, TestCaseSource(nameof(TestCases))] + [Theory] + [MemberData(nameof(TestCases))] public async Task Receive_FromRemote_ExistingObjects(string id) { MemoryTransport localTransport = new(); - Base result = await Sdk.Api.Operations.Receive(id, _testCaseTransport, localTransport); + Base result = await _operations.Receive(id, _testCaseTransport, localTransport); - Assert.That(result.id, Is.EqualTo(id)); + Assert.NotNull(result); + Assert.Equal(id, result.id); } - [Test, TestCaseSource(nameof(TestCases))] + [Theory] + [MemberData(nameof(TestCases))] public async Task Receive_FromLocal_OnProgressActionCalled(string id) { bool wasCalled = false; - _ = await Sdk.Api.Operations.Receive(id, null, _testCaseTransport, onProgressAction: _ => wasCalled = true); - - Assert.That(wasCalled, Is.True); - } - - [Test, TestCaseSource(nameof(TestCases))] - public async Task Receive_FromLocal_OnTotalChildrenCountKnownCalled(string id) - { - bool wasCalled = false; - int children = 0; - var result = await Sdk.Api.Operations.Receive( + _ = await _operations.Receive( id, null, _testCaseTransport, - onTotalChildrenCountKnown: c => - { - wasCalled = true; - children = c; - } + onProgressAction: new UnitTestProgress(_ => wasCalled = true) ); - Assert.That(result.id, Is.EqualTo(id)); + Assert.True(wasCalled); + } - var expectedChildren = result.GetTotalChildrenCount() - 1; - - Assert.That(wasCalled, Is.True); - Assert.That(children, Is.EqualTo(expectedChildren)); + public void Dispose() + { + // Cleanup resources if necessary } } diff --git a/tests/Speckle.Sdk.Tests.Unit/Api/Operations/SendObjectReferences.cs b/tests/Speckle.Sdk.Tests.Unit/Api/Operations/SendObjectReferences.cs index 77e0b63e..3e402fc8 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Api/Operations/SendObjectReferences.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Api/Operations/SendObjectReferences.cs @@ -1,51 +1,69 @@ -using NUnit.Framework; +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Speckle.Sdk.Api; +using Speckle.Sdk.Host; using Speckle.Sdk.Models; using Speckle.Sdk.Transports; +using Xunit; namespace Speckle.Sdk.Tests.Unit.Api.Operations; public class SendObjectReferences { - [TestCase(0)] - [TestCase(1)] - [TestCase(10)] + private readonly IOperations _operations; + + public SendObjectReferences() + { + TypeLoader.Reset(); + TypeLoader.Initialize(typeof(Base).Assembly, typeof(DataChunk).Assembly); + var serviceProvider = TestServiceSetup.GetServiceProvider(); + _operations = serviceProvider.GetRequiredService(); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(10)] public async Task SendObjectsWithApplicationIds(int testDepth) { Base testData = GenerateTestCase(testDepth, true); MemoryTransport transport = new(); - var result = await Speckle.Sdk.Api.Operations.Send(testData, [transport]); + var result = await _operations.Send(testData, [transport]); - Assert.That(result.rootObjId, Is.Not.Null); - Assert.That(result.rootObjId, Has.Length.EqualTo(32)); + result.rootObjId.Should().NotBeNull(); - Assert.That(result.convertedReferences, Has.Count.EqualTo(Math.Pow(2, testDepth + 1) - 2)); + result.rootObjId.Length.Should().Be(32); + + result.convertedReferences.Count.Should().Be((int)(Math.Pow(2, testDepth + 1) - 2)); } - [TestCase(0)] - [TestCase(1)] - [TestCase(10)] + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(10)] public async Task SendObjectsWithoutApplicationIds(int testDepth) { Base testData = GenerateTestCase(testDepth, false); MemoryTransport transport = new(); - var result = await Speckle.Sdk.Api.Operations.Send(testData, [transport]); + var result = await _operations.Send(testData, [transport]); - Assert.That(result.rootObjId, Is.Not.Null); - Assert.That(result.rootObjId, Has.Length.EqualTo(32)); + result.rootObjId.Should().NotBeNull(); - Assert.That(result.convertedReferences, Is.Empty); + result.rootObjId.Length.Should().Be(32); + + result.convertedReferences.Should().BeEmpty(); } private Base GenerateTestCase(int depth, bool withAppId) { var appId = withAppId ? $"{Guid.NewGuid()}" : null; - var ret = new Base() { applicationId = appId, }; + var ret = new Base() { applicationId = appId }; if (depth > 0) { ret["@elements"] = new List { GenerateTestCase(depth - 1, withAppId), - GenerateTestCase(depth - 1, withAppId) + GenerateTestCase(depth - 1, withAppId), }; } diff --git a/tests/Speckle.Sdk.Tests.Unit/Api/Operations/SendReceiveLocal.cs b/tests/Speckle.Sdk.Tests.Unit/Api/Operations/SendReceiveLocal.cs index ba2f3c95..813d9307 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Api/Operations/SendReceiveLocal.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Api/Operations/SendReceiveLocal.cs @@ -1,32 +1,35 @@ -using System.Collections.Concurrent; -using NUnit.Framework; +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Speckle.Sdk.Api; using Speckle.Sdk.Common; using Speckle.Sdk.Host; using Speckle.Sdk.Models; using Speckle.Sdk.Tests.Unit.Host; using Speckle.Sdk.Transports; +using Xunit; namespace Speckle.Sdk.Tests.Unit.Api.Operations; -[TestFixture] public sealed class SendReceiveLocal : IDisposable { - [SetUp] - public void Setup() + private readonly IOperations _operations; + + public SendReceiveLocal() { TypeLoader.Reset(); TypeLoader.Initialize(typeof(Base).Assembly, typeof(Point).Assembly); + var serviceProvider = TestServiceSetup.GetServiceProvider(); + _operations = serviceProvider.GetRequiredService(); } - private string? _objId01; - private string? _commitId02; - private const int NUM_OBJECTS = 3001; private readonly SQLiteTransport _sut = new(); - [Test(Description = "Pushing a commit locally"), Order(1)] - public async Task LocalUpload() + public void Dispose() => _sut.Dispose(); + + [Fact(DisplayName = "Pushing a commit locally")] + public async Task LocalUploadAndDownload() { var myObject = new Base(); var rand = new Random(); @@ -41,24 +44,18 @@ public sealed class SendReceiveLocal : IDisposable } using SQLiteTransport localTransport = new(); - (_objId01, var references) = await Sdk.Api.Operations.Send(myObject, localTransport, false); + (var objId01, var references) = await _operations.Send(myObject, localTransport, false); - Assert.That(_objId01, Is.Not.Null); - Assert.That(references, Has.Count.EqualTo(NUM_OBJECTS)); + objId01.Should().NotBeNull(); + references.Count.Should().Be(NUM_OBJECTS); - TestContext.Out.WriteLine($"Written {NUM_OBJECTS + 1} objects. Commit id is {_objId01}"); + var commitPulled = await _operations.Receive(objId01.NotNull()); + + ((List)commitPulled["@items"].NotNull())[0].Should().BeOfType(); + ((List)commitPulled["@items"].NotNull()).Count.Should().Be(NUM_OBJECTS); } - [Test(Description = "Pulling a commit locally"), Order(2)] - public async Task LocalDownload() - { - var commitPulled = await Sdk.Api.Operations.Receive(_objId01.NotNull()); - - Assert.That(((List)commitPulled["@items"].NotNull())[0], Is.TypeOf()); - Assert.That(((List)commitPulled["@items"].NotNull()), Has.Count.EqualTo(NUM_OBJECTS)); - } - - [Test(Description = "Pushing and Pulling a commit locally")] + [Fact(DisplayName = "Pushing and Pulling a commit locally")] public async Task LocalUploadDownload() { var myObject = new Base(); @@ -73,16 +70,15 @@ public sealed class SendReceiveLocal : IDisposable ); } - (_objId01, _) = await Sdk.Api.Operations.Send(myObject, _sut, false); + (var objId01, _) = await _operations.Send(myObject, _sut, false); - var commitPulled = await Sdk.Api.Operations.Receive(_objId01); + var commitPulled = await _operations.Receive(objId01); List items = (List)commitPulled["@items"].NotNull(); - - Assert.That(items, Has.All.TypeOf()); - Assert.That(items, Has.Count.EqualTo(NUM_OBJECTS)); + items.Should().AllSatisfy(x => x.Should().BeOfType()); + items.Count.Should().Be(NUM_OBJECTS); } - [Test(Description = "Pushing and pulling a commit locally"), Order(3)] + [Fact(DisplayName = "Pushing and pulling a commit locally")] public async Task LocalUploadDownloadSmall() { var myObject = new Base(); @@ -97,16 +93,15 @@ public sealed class SendReceiveLocal : IDisposable ); } - (_objId01, _) = await Sdk.Api.Operations.Send(myObject, _sut, false); + (var objId01, _) = await _operations.Send(myObject, _sut, false); - Assert.That(_objId01, Is.Not.Null); - TestContext.Out.WriteLine($"Written {NUM_OBJECTS + 1} objects. Commit id is {_objId01}"); + objId01.Should().NotBeNull(); - var objsPulled = await Sdk.Api.Operations.Receive(_objId01); - Assert.That(((List)objsPulled["@items"].NotNull()), Has.Count.EqualTo(30)); + var objsPulled = await _operations.Receive(objId01); + ((List)objsPulled["@items"].NotNull()).Count.Should().Be(30); } - [Test(Description = "Pushing and pulling a commit locally"), Order(3)] + [Fact(DisplayName = "Pushing and pulling a commit locally")] public async Task LocalUploadDownloadListDic() { var myList = new List { 1, 2, 3, "ciao" }; @@ -114,26 +109,23 @@ public sealed class SendReceiveLocal : IDisposable { { "a", myList }, { "b", 2 }, - { "c", "ciao" } + { "c", "ciao" }, }; var myObject = new Base(); myObject["@dictionary"] = myDic; myObject["@list"] = myList; - (_objId01, _) = await Sdk.Api.Operations.Send(myObject, _sut, false); + (var _objId01, _) = await _operations.Send(myObject, _sut, false); - Assert.That(_objId01, Is.Not.Null); + _objId01.Should().NotBeNull(); - var objsPulled = await Sdk.Api.Operations.Receive(_objId01); - Assert.That( - ((List)((Dictionary)objsPulled["@dictionary"].NotNull())["a"]).First(), - Is.EqualTo(1) - ); - Assert.That(((List)objsPulled["@list"].NotNull()).Last(), Is.EqualTo("ciao")); + var objsPulled = await _operations.Receive(_objId01); + ((List)((Dictionary)objsPulled["@dictionary"].NotNull())["a"]).First().Should().Be(1); + ((List)objsPulled["@list"].NotNull()).Last().Should().Be("ciao"); } - [Test(Description = "Pushing and pulling a random object, with our without detachment"), Order(3)] + [Fact(DisplayName = "Pushing and pulling a random object, with or without detachment")] public async Task UploadDownloadNonCommitObject() { var obj = new Base(); @@ -159,32 +151,31 @@ public sealed class SendReceiveLocal : IDisposable ((List)((dynamic)obj)["@LayerC"]).Add(new Point(i, i, i + rand.NextDouble()) { applicationId = i + "baz" }); } - (_objId01, _) = await Sdk.Api.Operations.Send(obj, _sut, false); + (var objId01, _) = await _operations.Send(obj, _sut, false); - Assert.That(_objId01, Is.Not.Null); - TestContext.Out.WriteLine($"Written {NUM_OBJECTS + 1} objects. Commit id is {_objId01}"); + objId01.Should().NotBeNull(); - var objPulled = await Sdk.Api.Operations.Receive(_objId01); + var objPulled = await _operations.Receive(objId01); - Assert.That(objPulled, Is.TypeOf()); + objPulled.Should().BeOfType(); // Note: even if the layers were originally declared as lists of "Base" objects, on deserialisation we cannot know that, // as it's a dynamic property. Dynamic properties, if their content value is ambigous, will default to a common-sense standard. // This specifically manifests in the case of lists and dictionaries: List will become List, and // Dictionary will deserialize to Dictionary. var layerA = ((dynamic)objPulled)["LayerA"] as List; - Assert.That(layerA, Has.Count.EqualTo(30)); + layerA?.Count.Should().Be(30); var layerC = (List)((dynamic)objPulled)["@LayerC"]; - Assert.That(layerC, Has.Count.EqualTo(30)); - Assert.That(layerC[0], Is.TypeOf()); + layerC.Count.Should().Be(30); + layerC[0].Should().BeOfType(); var layerD = ((dynamic)objPulled)["@LayerD"] as List; - Assert.That(layerD, Has.Count.EqualTo(2)); + layerD?.Count.Should().Be(2); } - [Test(Description = "Should show progress!"), Order(4)] - public async Task UploadProgressReports() + [Fact(DisplayName = "Should show progress!")] + public async Task UploadAndDownloadProgressReports() { Base myObject = new() { ["items"] = new List() }; var rand = new Random(); @@ -196,51 +187,30 @@ public sealed class SendReceiveLocal : IDisposable ); } - ConcurrentBag? progress = null; - (_commitId02, _) = await Sdk.Api.Operations.Send( - myObject, - _sut, - false, - onProgressAction: dict => + (var commitId02, _) = await _operations.Send(myObject, _sut, false); + + ProgressArgs? progress = null; + await _operations.Receive( + commitId02.NotNull(), + onProgressAction: new UnitTestProgress(x => { - progress = dict; - } + progress = x; + }) ); - progress.NotNull(); - Assert.That(progress, Has.Count.GreaterThanOrEqualTo(1)); + progress.Should().NotBeNull(); } - [Test(Description = "Should show progress!"), Order(5)] - public async Task DownloadProgressReports() - { - ConcurrentBag? progress = null; - await Sdk.Api.Operations.Receive( - _commitId02.NotNull(), - onProgressAction: dict => - { - progress = dict; - } - ); - progress.NotNull(); - Assert.That(progress, Has.Count.GreaterThanOrEqualTo(1)); - } - - [Test(Description = "Should not dispose of transports if so specified.")] + [Fact(DisplayName = "Should not dispose of transports if so specified.")] public async Task ShouldNotDisposeTransports() { var @base = new Base(); @base["test"] = "the best"; SQLiteTransport myLocalTransport = new(); - var sendResult = await Sdk.Api.Operations.Send(@base, myLocalTransport, false); - await Sdk.Api.Operations.Send(@base, myLocalTransport, false); + var sendResult = await _operations.Send(@base, myLocalTransport, false); + await _operations.Send(@base, myLocalTransport, false); - _ = await Sdk.Api.Operations.Receive(sendResult.rootObjId, null, myLocalTransport); - await Sdk.Api.Operations.Receive(sendResult.rootObjId, null, myLocalTransport); - } - - public void Dispose() - { - _sut.Dispose(); + _ = await _operations.Receive(sendResult.rootObjId, null, myLocalTransport); + await _operations.Receive(sendResult.rootObjId, null, myLocalTransport); } } diff --git a/tests/Speckle.Sdk.Tests.Unit/Api/Operations/SerializationTests.cs b/tests/Speckle.Sdk.Tests.Unit/Api/Operations/SerializationTests.cs index 9a11c099..b5dc30fe 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Api/Operations/SerializationTests.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Api/Operations/SerializationTests.cs @@ -1,38 +1,42 @@ using System.Drawing; -using NUnit.Framework; +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Speckle.Sdk.Api; using Speckle.Sdk.Host; using Speckle.Sdk.Models; using Speckle.Sdk.Tests.Unit.Host; +using Xunit; using Point = Speckle.Sdk.Tests.Unit.Host.Point; namespace Speckle.Sdk.Tests.Unit.Api.Operations; -[TestFixture] -[TestOf(typeof(Sdk.Api.Operations))] public class ObjectSerialization { - [SetUp] - public void Setup() + private readonly IOperations _operations; + + public ObjectSerialization() { TypeLoader.Reset(); TypeLoader.Initialize(typeof(Base).Assembly, typeof(DataChunk).Assembly, typeof(ColorMock).Assembly); + var serviceProvider = TestServiceSetup.GetServiceProvider(); + _operations = serviceProvider.GetRequiredService(); } - [Test] - public void IgnoreCircularReferences() + [Fact] + public async Task IgnoreCircularReferences() { var pt = new Point(1, 2, 3); pt["circle"] = pt; - var test = Sdk.Api.Operations.Serialize(pt); + var test = _operations.Serialize(pt); - var result = Sdk.Api.Operations.Deserialize(test); + var result = await _operations.DeserializeAsync(test); var circle = result["circle"]; - Assert.That(circle, Is.Null); + circle.Should().BeNull(); } - [Test] - public void InterfacePropHandling() + [Fact] + public async Task InterfacePropHandling() { Line tail = new() { Start = new Point(0, 0, 0), End = new Point(42, 42, 42) }; PolygonalFeline cat = new() { Tail = tail }; @@ -42,7 +46,7 @@ public class ObjectSerialization cat.Claws[$"Claw number {i}"] = new Line { Start = new Point(i, i, i), - End = new Point(i + 3.14, i + 3.14, i + 3.14) + End = new Point(i + 3.14, i + 3.14, i + 3.14), }; if (i % 2 == 0) @@ -63,32 +67,32 @@ public class ObjectSerialization cat.Fur[i] = new Line { Start = new Point(i, i, i), End = new Point(i + 3.14, i + 3.14, i + 3.14) }; } - var result = Sdk.Api.Operations.Serialize(cat); + var result = _operations.Serialize(cat); - var deserialisedFeline = Sdk.Api.Operations.Deserialize(result); + var deserialisedFeline = await _operations.DeserializeAsync(result); - Assert.That(deserialisedFeline.GetId(), Is.EqualTo(cat.GetId())); // If we're getting the same hash... we're probably fine! + deserialisedFeline.GetId().Should().Be(cat.GetId()); } - [Test] - public void InheritanceTests() + [Fact] + public async Task InheritanceTests() { var superPoint = new SuperPoint { X = 10, Y = 10, Z = 10, - W = 42 + W = 42, }; - var str = Sdk.Api.Operations.Serialize(superPoint); - var sstr = Sdk.Api.Operations.Deserialize(str); + var str = _operations.Serialize(superPoint); + var sstr = await _operations.DeserializeAsync(str); - Assert.That(sstr.speckle_type, Is.EqualTo(superPoint.speckle_type)); + sstr.speckle_type.Should().Be(superPoint.speckle_type); } - [Test] - public void ListDynamicProp() + [Fact] + public async Task ListDynamicProp() { var point = new Point(); var test = new List(); @@ -100,49 +104,50 @@ public class ObjectSerialization point["test"] = test; - var str = Sdk.Api.Operations.Serialize(point); - var dsrls = Sdk.Api.Operations.Deserialize(str); + var str = _operations.Serialize(point); + var dsrls = await _operations.DeserializeAsync(str); - var list = dsrls["test"] as List; // NOTE: on dynamically added lists, we cannot infer the inner type and we always fall back to a generic list. - Assert.That(list, Has.Count.EqualTo(100)); + var list = dsrls["test"] as List; + list.Should().NotBeNull(); // Ensure the list isn't null in first place + list!.Count.Should().Be(100); } - [Test] - public void ChunkSerialisation() + [Fact] + public async Task ChunkSerialisation() { - var baseBasedChunk = new DataChunk(); + var baseBasedChunk = new DataChunk() { data = new() }; for (var i = 0; i < 200; i++) { baseBasedChunk.data.Add(new SuperPoint { W = i }); } - var stringBasedChunk = new DataChunk(); + var stringBasedChunk = new DataChunk() { data = new() }; for (var i = 0; i < 200; i++) { stringBasedChunk.data.Add(i + "_hai"); } - var doubleBasedChunk = new DataChunk(); + var doubleBasedChunk = new DataChunk() { data = new() }; for (var i = 0; i < 200; i++) { doubleBasedChunk.data.Add(i + 0.33); } - var baseChunkString = Sdk.Api.Operations.Serialize(baseBasedChunk); - var stringChunkString = Sdk.Api.Operations.Serialize(stringBasedChunk); - var doubleChunkString = Sdk.Api.Operations.Serialize(doubleBasedChunk); + var baseChunkString = _operations.Serialize(baseBasedChunk); + var stringChunkString = _operations.Serialize(stringBasedChunk); + var doubleChunkString = _operations.Serialize(doubleBasedChunk); - var baseChunkDeserialised = (DataChunk)Sdk.Api.Operations.Deserialize(baseChunkString); - var stringChunkDeserialised = (DataChunk)Sdk.Api.Operations.Deserialize(stringChunkString); - var doubleChunkDeserialised = (DataChunk)Sdk.Api.Operations.Deserialize(doubleChunkString); + var baseChunkDeserialised = (DataChunk)await _operations.DeserializeAsync(baseChunkString); + var stringChunkDeserialised = (DataChunk)await _operations.DeserializeAsync(stringChunkString); + var doubleChunkDeserialised = (DataChunk)await _operations.DeserializeAsync(doubleChunkString); - Assert.That(baseChunkDeserialised.data, Has.Count.EqualTo(baseBasedChunk.data.Count)); - Assert.That(stringChunkDeserialised.data, Has.Count.EqualTo(stringBasedChunk.data.Count)); - Assert.That(doubleChunkDeserialised.data, Has.Count.EqualTo(doubleBasedChunk.data.Count)); + baseChunkDeserialised.data.Count.Should().Be(baseBasedChunk.data.Count); + stringChunkDeserialised.data.Count.Should().Be(stringBasedChunk.data.Count); + doubleChunkDeserialised.data.Count.Should().Be(doubleBasedChunk.data.Count); } - [Test] - public void ObjectWithChunksSerialisation() + [Fact] + public async Task ObjectWithChunksSerialisation() { const int MAX_NUM = 2020; var mesh = new FakeMesh { ArrayOfDoubles = new double[MAX_NUM], ArrayOfLegs = new TableLeg[MAX_NUM] }; @@ -163,13 +168,13 @@ public class ObjectSerialization mesh["@(800)CustomChunk"] = customChunk; mesh["@()DefaultChunk"] = defaultChunk; - var serialised = Sdk.Api.Operations.Serialize(mesh); - var deserialised = Sdk.Api.Operations.Deserialize(serialised); + var serialised = _operations.Serialize(mesh); + var deserialised = await _operations.DeserializeAsync(serialised); - Assert.That(mesh.GetId(), Is.EqualTo(deserialised.GetId())); + mesh.GetId().Should().Be(deserialised.GetId()); } - [Test] + [Fact] public void EmptyListSerialisationTests() { // NOTE: expected behaviour is that empty lists should serialize as empty lists. Don't ask why, it's complicated. @@ -184,7 +189,7 @@ public class ObjectSerialization test["nestedList"] = new List { new List { new List() } }; test["@nestedDetachableList"] = new List { new List { new List() } }; - var serialised = Sdk.Api.Operations.Serialize(test); + var serialised = _operations.Serialize(test); var isCorrect = serialised.Contains("\"@(5)emptyChunks\":[]") && serialised.Contains("\"emptyList\":[]") @@ -192,7 +197,7 @@ public class ObjectSerialization && serialised.Contains("\"nestedList\":[[[]]]") && serialised.Contains("\"@nestedDetachableList\":[[[]]]"); - Assert.That(isCorrect, Is.EqualTo(true)); + isCorrect.Should().BeTrue(); } [SpeckleType("Speckle.Core.Tests.Unit.Api.Operations.ObjectSerialization+DateMock")] @@ -201,16 +206,16 @@ public class ObjectSerialization public DateTime TestField { get; set; } } - [Test] - public void DateSerialisation() + [Fact] + public async Task DateSerialisation() { var date = new DateTime(2020, 1, 14); var mockBase = new DateMock { TestField = date }; - var result = Sdk.Api.Operations.Serialize(mockBase); - var test = (DateMock)Sdk.Api.Operations.Deserialize(result); + var result = _operations.Serialize(mockBase); + var test = (DateMock)await _operations.DeserializeAsync(result); - Assert.That(test.TestField, Is.EqualTo(date)); + test.TestField.Should().Be(date); } [SpeckleType("Speckle.Core.Tests.Unit.Api.Operations.ObjectSerialization+GUIDMock")] @@ -219,16 +224,16 @@ public class ObjectSerialization public Guid TestField { get; set; } } - [Test] - public void GuidSerialisation() + [Fact] + public async Task GuidSerialisation() { var guid = Guid.NewGuid(); var mockBase = new GUIDMock { TestField = guid }; - var result = Sdk.Api.Operations.Serialize(mockBase); - var test = (GUIDMock)Sdk.Api.Operations.Deserialize(result); + var result = _operations.Serialize(mockBase); + var test = (GUIDMock)await _operations.DeserializeAsync(result); - Assert.That(test.TestField, Is.EqualTo(guid)); + test.TestField.Should().Be(guid); } [SpeckleType("Speckle.Core.Tests.Unit.Api.Operations.ObjectSerialization+ColorMock")] @@ -237,16 +242,16 @@ public class ObjectSerialization public Color TestField { get; set; } } - [Test] - public void ColorSerialisation() + [Fact] + public async Task ColorSerialisation() { var color = Color.FromArgb(255, 4, 126, 251); var mockBase = new ColorMock { TestField = color }; - var result = Sdk.Api.Operations.Serialize(mockBase); - var test = (ColorMock)Sdk.Api.Operations.Deserialize(result); + var result = _operations.Serialize(mockBase); + var test = (ColorMock)await _operations.DeserializeAsync(result); - Assert.That(test.TestField, Is.EqualTo(color)); + test.TestField.Should().Be(color); } [SpeckleType("Speckle.Core.Tests.Unit.Api.Operations.ObjectSerialization+StringDateTimeRegressionMock")] @@ -255,14 +260,14 @@ public class ObjectSerialization public string TestField { get; set; } } - [Test] - public void StringDateTimeRegression() + [Fact] + public async Task StringDateTimeRegression() { var mockBase = new StringDateTimeRegressionMock { TestField = "2021-11-12T11:32:01" }; - var result = Sdk.Api.Operations.Serialize(mockBase); - var test = (StringDateTimeRegressionMock)Sdk.Api.Operations.Deserialize(result); + var result = _operations.Serialize(mockBase); + var test = (StringDateTimeRegressionMock)await _operations.DeserializeAsync(result); - Assert.That(test.TestField, Is.EqualTo(mockBase.TestField)); + test.TestField.Should().Be(mockBase.TestField); } } diff --git a/tests/Speckle.Sdk.Tests.Unit/Assembly.cs b/tests/Speckle.Sdk.Tests.Unit/Assembly.cs new file mode 100644 index 00000000..a4bcec54 --- /dev/null +++ b/tests/Speckle.Sdk.Tests.Unit/Assembly.cs @@ -0,0 +1,3 @@ +using Xunit; + +[assembly: CollectionBehavior(DisableTestParallelization = true)] diff --git a/tests/Speckle.Sdk.Tests.Unit/Common/NotNullTests.cs b/tests/Speckle.Sdk.Tests.Unit/Common/NotNullTests.cs index 680cf666..a9ac02d5 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Common/NotNullTests.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Common/NotNullTests.cs @@ -1,61 +1,94 @@ -using NUnit.Framework; -using Shouldly; +using FluentAssertions; using Speckle.Sdk.Common; +using Xunit; namespace Speckle.Sdk.Tests.Unit.Common; public class NotNullTests { - [TestCase(null, 0)] - [TestCase(new string[0], 0)] - [TestCase(new[] { "yay" }, 1)] + [Theory] + [InlineData(null, 0)] + [InlineData(new string[0], 0)] + [InlineData(new[] { "yay" }, 1)] public void Empty(string[]? test, int length) { - var list = NotNullExtensions.Empty(test).ToList(); - list.Count.ShouldBe(length); + var list = test.Empty().ToList(); + list.Count.Should().Be(length); } - [Test] + [Fact] public void NotNullClass() { - var t = NotNullExtensions.NotNull("test"); - t.ShouldNotBeNull().ShouldBe("test"); + var t = "test".NotNull(); + t.Should().Be("test"); } - [Test] + [Fact] public void NotNullStruct() { var t = NotNullExtensions.NotNull(2); - t.ShouldBe(2); + t.Should().Be(2); } - [Test] + [Fact] public async Task NotNullClass_Task() { - var t = await NotNullExtensions.NotNull(Task.FromResult("test")); - t.ShouldNotBeNull().ShouldBe("test"); + var t = await Task.FromResult("test").NotNull(); + t.Should().Be("test"); } - [Test] + [Fact] public async Task NotNullStruct_Task() { - var t = await NotNullExtensions.NotNull(Task.FromResult(2)); - t.ShouldBe(2); + var t = await Task.FromResult(2).NotNull(); + t.Should().Be(2); } - [Test] - public void NotNullClass_Exception() => - Assert.Throws(() => NotNullExtensions.NotNull((string?)null)); + [Fact] + public async Task NotNullClass_ValueTask() + { + var t = await ValueTask.FromResult("test").NotNull(); + t.Should().Be("test"); + } - [Test] - public void NotNullStruct_Exception() => - Assert.Throws(() => NotNullExtensions.NotNull((int?)null)); + [Fact] + public async Task NotNullStruct_ValueTask() + { + var t = await ValueTask.FromResult(2).NotNull(); + t.Should().Be(2); + } - [Test] - public void NotNullClass_Task_Exception() => - Assert.ThrowsAsync(() => NotNullExtensions.NotNull(Task.FromResult((string?)null))); + [Fact] + public void NotNullClass_Exception() => FluentActions.Invoking(() => ((string?)null).NotNull()); - [Test] - public void NotNullStruct_Task_Exception() => - Assert.ThrowsAsync(() => NotNullExtensions.NotNull(Task.FromResult((int?)null))); + [Fact] + public void NotNullStruct_Exception() => FluentActions.Invoking(() => ((int?)null).NotNull()); + + [Fact] + public async Task NotNullClass_Task_Exception() => + await FluentActions + .Invoking(async () => await Task.FromResult((string?)null).NotNull()) + .Should() + .ThrowAsync(); + + [Fact] + public async Task NotNullStruct_Task_Exception() => + await FluentActions + .Invoking(async () => await Task.FromResult((int?)null).NotNull()) + .Should() + .ThrowAsync(); + + [Fact] + public async Task NotNullClass_ValueTask_Exception() => + await FluentActions + .Invoking(async () => await ValueTask.FromResult((string?)null).NotNull()) + .Should() + .ThrowAsync(); + + [Fact] + public async Task NotNullStruct_ValueTask_Exception() => + await FluentActions + .Invoking(async () => await ValueTask.FromResult((int?)null).NotNull()) + .Should() + .ThrowAsync(); } diff --git a/tests/Speckle.Sdk.Tests.Unit/Common/RangeFromTests.cs b/tests/Speckle.Sdk.Tests.Unit/Common/RangeFromTests.cs new file mode 100644 index 00000000..575277bd --- /dev/null +++ b/tests/Speckle.Sdk.Tests.Unit/Common/RangeFromTests.cs @@ -0,0 +1,14 @@ +using Speckle.Sdk.Dependencies; +using Xunit; + +namespace Speckle.Sdk.Tests.Unit.Common; + +public class RangeFromTests +{ + [Fact] + public void EnsureRange() + { + var list = EnumerableExtensions.RangeFrom(1, 4).ToArray(); + Assert.Equal([1, 2, 3, 4], list); + } +} diff --git a/tests/Speckle.Sdk.Tests.Unit/Common/UnitsTest.cs b/tests/Speckle.Sdk.Tests.Unit/Common/UnitsTest.cs index 054c5601..3f8caa7c 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Common/UnitsTest.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Common/UnitsTest.cs @@ -1,27 +1,96 @@ -using NUnit.Framework; +using FluentAssertions; using Speckle.Sdk.Common; +using Xunit; namespace Speckle.Sdk.Tests.Unit.Common; -[TestOf(typeof(Units))] public class UnitsTest { private const double EPS = 0.00022; - [Test, Combinatorial] - [DefaultFloatingPointTolerance(EPS)] - public void TestUnitConversion( - [ValueSource(typeof(Units), nameof(Units.SupportedUnits))] string from, - [ValueSource(typeof(Units), nameof(Units.SupportedUnits))] string to - ) + public static IReadOnlyCollection OfficiallySupportedUnits => Units.SupportedUnits; + public static IReadOnlyCollection NotSupportedUnits => ["feeters", "liters", "us_ft"]; + public static IReadOnlyCollection ConversionSupport => [.. Units.SupportedUnits, null]; + + [Theory] + [MemberData(nameof(ConversionSupportGenerator))] + public void TestUnitConversion(string? from, string? to) { var forwards = Units.GetConversionFactor(from, to); var backwards = Units.GetConversionFactor(to, from); - Assert.That( - backwards * forwards, - Is.EqualTo(1d), - $"Behaviour says that 1{from} == {forwards}{to}, and 1{to} == {backwards}{from}" - ); + (backwards * forwards) + .Should() + .BeApproximately(1d, EPS, $"Behaviour says that 1{from} == {forwards}{to}, and 1{to} == {backwards}{from}"); + } + + [Theory] + [MemberData(nameof(OfficiallySupportedUnitsGenerator))] + public void IsUnitSupported_ReturnsTrue_AllSupportedUnits(string unit) + { + bool res = Units.IsUnitSupported(unit); + res.Should().BeTrue(); + } + + [Theory] + [MemberData(nameof(NotSupportedUnitsGenerator))] + public void IsUnitSupported_ReturnsFalse_NotSupportedUnits(string unit) + { + bool res = Units.IsUnitSupported(unit); + res.Should().BeFalse(); + } + + [Theory] + [MemberData(nameof(OfficiallySupportedUnitsGenerator))] + public void GetUnitsFromString_ReturnsSupported(string unit) + { + string? lower = Units.GetUnitsFromString(unit); + string? upper = Units.GetUnitsFromString(unit.ToUpperInvariant()); + + lower.Should().Be(unit); + upper.Should().Be(unit); + } + + [Theory] + [MemberData(nameof(NotSupportedUnitsGenerator))] + public void GetUnitsFromString_ThrowsUnSupported(string unit) => + FluentActions.Invoking(() => Units.GetUnitsFromString(unit)).Should().Throw(); + + [Theory] + [MemberData(nameof(OfficiallySupportedUnitsGenerator))] + public void UnitEncoding_RoundTrip(string unit) + { + var encoded = Units.GetEncodingFromUnit(unit); + var res = Units.GetUnitFromEncoding(encoded); + + res.Should().Be(unit); + } + + // Generators for MemberData + public static IEnumerable OfficiallySupportedUnitsGenerator() + { + foreach (var unit in OfficiallySupportedUnits) + { + yield return [unit]; + } + } + + public static IEnumerable NotSupportedUnitsGenerator() + { + foreach (var unit in NotSupportedUnits) + { + yield return [unit]; + } + } + + public static IEnumerable ConversionSupportGenerator() + { + foreach (var from in ConversionSupport) + { + foreach (var to in ConversionSupport) + { + yield return [from, to]; + } + } } } diff --git a/tests/Speckle.Sdk.Tests.Unit/Credentials/AccountServerMigrationTests.cs b/tests/Speckle.Sdk.Tests.Unit/Credentials/AccountServerMigrationTests.cs index 6f153240..b61eae75 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Credentials/AccountServerMigrationTests.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Credentials/AccountServerMigrationTests.cs @@ -1,65 +1,66 @@ -using NUnit.Framework; +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; using Speckle.Sdk.Api.GraphQL.Models; using Speckle.Sdk.Credentials; +using Xunit; namespace Speckle.Sdk.Tests.Unit.Credentials; -public class AccountServerMigrationTests +public class AccountServerMigrationTests : IDisposable { - private readonly List _accountsToCleanUp = new(); + private readonly List _accountsToCleanUp = []; - public static IEnumerable MigrationTestCase() + public static IEnumerable MigrationTestCases() { const string OLD_URL = "https://old.example.com"; 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)); 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); - List givenAccounts = new() { oldAccount, newAccount, otherAccount }; + List givenAccounts = [oldAccount, newAccount, otherAccount]; - yield return new TestCaseData(givenAccounts, NEW_URL, new[] { newAccount }) - .SetName("Get New") - .SetDescription("When requesting for new account, ensure only this account is returned"); + yield return [givenAccounts, NEW_URL, new[] { newAccount }]; - yield return new TestCaseData(givenAccounts, OLD_URL, new[] { newAccount }) - .SetName("Get New via Old") - .SetDescription("When requesting for old account, ensure migrated account is returned first"); + yield return [givenAccounts, OLD_URL, new[] { newAccount }]; - var reversed = Enumerable.Reverse(givenAccounts).ToList(); + var reversed = givenAccounts.AsEnumerable().Reverse().ToList(); - yield return new TestCaseData(reversed, OLD_URL, new[] { newAccount }) - .SetName("Get New via Old (Reversed order)") - .SetDescription("Account order shouldn't matter"); + yield return [reversed, OLD_URL, new[] { newAccount }]; } - [Test] - [TestCaseSource(nameof(MigrationTestCase))] + [Theory] + [MemberData(nameof(MigrationTestCases))] public void TestServerMigration(IList accounts, string requestedUrl, IList expectedSequence) { + // Add accounts to the local setup AddAccounts(accounts); - var result = AccountManager.GetAccounts(requestedUrl).ToList(); + var serviceProvider = TestServiceSetup.GetServiceProvider(); + var result = serviceProvider.GetRequiredService().GetAccounts(requestedUrl).ToList(); - Assert.That(result, Is.EquivalentTo(expectedSequence)); + // Assert the result using Shouldly + result.Should().BeEquivalentTo(expectedSequence); } - [TearDown] - public void TearDown() + public void Dispose() { - //Clean up any of the test accounts we made + // Clean up accounts after each test foreach (var acc in _accountsToCleanUp) { Fixtures.DeleteLocalAccount(acc.id); } + _accountsToCleanUp.Clear(); } private static Account CreateTestAccount(string url, Uri? movedFrom, Uri? movedTo, string? id = null) { id ??= Guid.NewGuid().ToString(); + return new Account { token = "myToken", @@ -67,20 +68,20 @@ public class AccountServerMigrationTests { url = url, name = "myServer", - migration = new ServerMigration { movedTo = movedTo, movedFrom = movedFrom } + migration = new ServerMigration { movedTo = movedTo, movedFrom = movedFrom }, }, userInfo = new UserInfo { id = id, email = "user@example.com", - name = "user" - } + name = "user", + }, }; } private void AddAccounts(IEnumerable accounts) { - foreach (Account account in accounts) + foreach (var account in accounts) { _accountsToCleanUp.Add(account); Fixtures.UpdateOrSaveAccount(account); diff --git a/tests/Speckle.Sdk.Tests.Unit/Credentials/Accounts.cs b/tests/Speckle.Sdk.Tests.Unit/Credentials/Accounts.cs index fafa8808..ac9df5f0 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Credentials/Accounts.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Credentials/Accounts.cs @@ -1,21 +1,26 @@ -using NUnit.Framework; +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; using Speckle.Sdk.Api.GraphQL.Models; using Speckle.Sdk.Credentials; +using Xunit; namespace Speckle.Sdk.Tests.Unit.Credentials; -[TestFixture] -public class CredentialInfrastructure +public class CredentialInfrastructure : IDisposable { - [OneTimeSetUp] - public static void SetUp() + private readonly IAccountManager _accountManager; + private static readonly Account s_testAccount1; + private static readonly Account s_testAccount2; + private static readonly Account s_testAccount3; + + static CredentialInfrastructure() { s_testAccount1 = new Account { refreshToken = "bla", token = "bla", serverInfo = new ServerInfo { url = "https://bla.example.com", company = "bla" }, - userInfo = new UserInfo { email = "one@two.com" } + userInfo = new UserInfo { email = "one@two.com" }, }; s_testAccount2 = new Account @@ -23,7 +28,7 @@ public class CredentialInfrastructure refreshToken = "foo", token = "bar", serverInfo = new ServerInfo { url = "https://baz.example.com", company = "qux" }, - userInfo = new UserInfo { email = "three@four.com" } + userInfo = new UserInfo { email = "three@four.com" }, }; s_testAccount3 = new Account @@ -34,72 +39,72 @@ public class CredentialInfrastructure { email = "six@five.com", id = "123345", - name = "Test Account 3" - } + name = "Test Account 3", + }, }; + } + public CredentialInfrastructure() + { Fixtures.UpdateOrSaveAccount(s_testAccount1); Fixtures.UpdateOrSaveAccount(s_testAccount2); Fixtures.SaveLocalAccount(s_testAccount3); + + var serviceProvider = TestServiceSetup.GetServiceProvider(); + _accountManager = serviceProvider.GetRequiredService(); } - [OneTimeTearDown] - public static void TearDown() + public void Dispose() { + _accountManager.Dispose(); Fixtures.DeleteLocalAccount(s_testAccount1.id); Fixtures.DeleteLocalAccount(s_testAccount2.id); + Fixtures.DeleteLocalAccount(s_testAccount3.id); Fixtures.DeleteLocalAccountFile(); } - private static Account s_testAccount1, - s_testAccount2, - s_testAccount3; - - [Test] + [Fact] public void GetAllAccounts() { - var accs = AccountManager.GetAccounts().ToList(); - Assert.That(accs, Has.Count.GreaterThanOrEqualTo(3)); // Tests are adding three accounts, you might have extra accounts on your machine when testing :D + var accs = _accountManager.GetAccounts().ToList(); + accs.Count.Should().BeGreaterThanOrEqualTo(3); // Tests are adding three accounts, there might be extra accounts locally } - [Test] + [Fact] public void GetAccount_ById() { - var result = AccountManager.GetAccount(s_testAccount1.id); + var result = _accountManager.GetAccount(s_testAccount1.id); - Assert.That(result, Is.EqualTo(s_testAccount1)); + result.Should().Be(s_testAccount1); // Uses `Shouldly` for a clean assertion } - [Test] - public void GetAccount_ById_ThrowsWhenNotFound() - { - Assert.Throws(() => AccountManager.GetAccount("Non_existent_id")); - } + [Fact] + public void GetAccount_ById_ThrowsWhenNotFound() => + FluentActions + .Invoking(() => _accountManager.GetAccount("Non_existent_id")) + .Should() + .Throw(); - public static IEnumerable TestCases() - { - SetUp(); - return new[] { s_testAccount1, s_testAccount2, s_testAccount3 }; - } + public static TheoryData TestCases() => new() { s_testAccount1, s_testAccount2, s_testAccount3 }; - [Test] - [TestCaseSource(nameof(TestCases))] + [Theory] + [MemberData(nameof(TestCases))] public void GetAccountsForServer(Account target) { - var accs = AccountManager.GetAccounts(target.serverInfo.url).ToList(); + var accs = _accountManager.GetAccounts(target.serverInfo.url).ToList(); - Assert.That(accs, Has.Count.EqualTo(1)); + accs.Count.Should().Be(1); var acc = accs[0]; - Assert.That(acc, Is.Not.SameAs(target), "We expect new objects (no reference equality)"); - Assert.That(acc.serverInfo.company, Is.EqualTo(target.serverInfo.company)); - Assert.That(acc.serverInfo.url, Is.EqualTo(target.serverInfo.url)); - Assert.That(acc.refreshToken, Is.EqualTo(target.refreshToken)); - Assert.That(acc.token, Is.EqualTo(target.token)); + acc.Should().NotBeSameAs(target); // We expect new objects (no reference equality) + acc.serverInfo.company.Should().Be(target.serverInfo.company); + acc.serverInfo.url.Should().Be(target.serverInfo.url); + acc.refreshToken.Should().Be(target.refreshToken); + acc.token.Should().Be(target.token); } - [Test] + [Fact] public void EnsureLocalIdentifiers_AreUniqueAcrossServers() { // Accounts with the same user ID in different servers should always result in different local identifiers. @@ -107,15 +112,15 @@ public class CredentialInfrastructure var acc1 = new Account { serverInfo = new ServerInfo { url = "https://speckle.xyz" }, - userInfo = new UserInfo { id = id } + userInfo = new UserInfo { id = id }, }.GetLocalIdentifier(); var acc2 = new Account { serverInfo = new ServerInfo { url = "https://app.speckle.systems" }, - userInfo = new UserInfo { id = id } + userInfo = new UserInfo { id = id }, }.GetLocalIdentifier(); - Assert.That(acc1, Is.Not.EqualTo(acc2)); + acc1.Should().NotBe(acc2); } } diff --git a/tests/Speckle.Sdk.Tests.Unit/Credentials/FE2WrapperTests.cs b/tests/Speckle.Sdk.Tests.Unit/Credentials/FE2WrapperTests.cs deleted file mode 100644 index cd7bba65..00000000 --- a/tests/Speckle.Sdk.Tests.Unit/Credentials/FE2WrapperTests.cs +++ /dev/null @@ -1,68 +0,0 @@ -using NUnit.Framework; -using Speckle.Sdk.Credentials; -using Speckle.Sdk.Logging; - -namespace Speckle.Sdk.Tests.Unit.Credentials; - -[TestFixture] -[TestOf(typeof(StreamWrapper))] -public class Fe2WrapperTests -{ - [TestCase( - "https://latest.speckle.systems/projects/92b620fb17/models/76fd8a01c8", - StreamWrapperType.Branch, - "92b620fb17", - "76fd8a01c8" - )] - [TestCase( - "https://latest.speckle.systems/projects/92b620fb17/models/76fd8a01c8@7dc324e4bb", - StreamWrapperType.Commit, - "92b620fb17", - "76fd8a01c8", - "7dc324e4bb" - )] - [TestCase( - "https://latest.speckle.systems/projects/92b620fb17/models/bdd52d7fd174328a080770e2a7fef98a", - StreamWrapperType.Object, - "92b620fb17", - null, - null, - "bdd52d7fd174328a080770e2a7fef98a" - )] - public void ParseFe2Links( - string url, - StreamWrapperType expectedType, - string expectedProjectId, - string? expectedBranchId = null, - string? expectedCommitId = null, - string? expectedObjectId = null - ) - { - var streamWrapper = new StreamWrapper(url); - Assert.That(streamWrapper, Is.Not.Null); - Assert.That(streamWrapper.Type, Is.EqualTo(expectedType)); - Assert.That(streamWrapper.StreamId, Is.EqualTo(expectedProjectId)); - Assert.That(streamWrapper.BranchName, Is.EqualTo(expectedBranchId)); - Assert.That(streamWrapper.CommitId, Is.EqualTo(expectedCommitId)); - Assert.That(streamWrapper.ObjectId, Is.EqualTo(expectedObjectId)); - } - - [TestCase("https://latest.speckle.systems/projects/92b620fb17/models/all")] - [TestCase("https://latest.speckle.systems/projects/92b620fb17/models/0fe8ca21c0,76fd8a01c8")] - [TestCase("https://latest.speckle.systems/projects/92b620fb17/models/A,76fd8a01c8@7dc324e4bb,B@C,D@E,F")] - public void ParseFe2NotSupportedLinks(string url) - { - Assert.Throws(() => new StreamWrapper(url)); - } - - [TestCase("https://latest.speckle.systems/")] - [TestCase("https://latest.speckle.systems/projects")] - [TestCase("https://latest.speckle.systems/projects/")] - [TestCase("https://latest.speckle.systems/projects/92b620fb17")] - [TestCase("https://latest.speckle.systems/projects/92b620fb17/")] - [TestCase("https://latest.speckle.systems/projects/92b620fb17/models/")] - public void ParseFe2InvalidLinks(string url) - { - Assert.Throws(() => new StreamWrapper(url)); - } -} diff --git a/tests/Speckle.Sdk.Tests.Unit/Credentials/StreamWrapperTests.cs b/tests/Speckle.Sdk.Tests.Unit/Credentials/StreamWrapperTests.cs deleted file mode 100644 index c16784c7..00000000 --- a/tests/Speckle.Sdk.Tests.Unit/Credentials/StreamWrapperTests.cs +++ /dev/null @@ -1,82 +0,0 @@ -using NUnit.Framework; -using Speckle.Sdk.Credentials; - -namespace Speckle.Sdk.Tests.Unit.Credentials; - -[TestFixture] -[TestOf(typeof(StreamWrapper))] -public class StreamWrapperTests -{ - [Test] - public void ParseStream() - { - var wrapper = new StreamWrapper("https://testing.speckle.dev/streams/a75ab4f10f"); - Assert.That(wrapper.Type, Is.EqualTo(StreamWrapperType.Stream)); - } - - [Test] - public void ParseBranch() - { - var wrapperCrazy = new StreamWrapper( - "https://testing.speckle.dev/streams/4c3ce1459c/branches/%F0%9F%8D%95%E2%AC%85%F0%9F%8C%9F%20you%20wat%3F" - ); - Assert.That(wrapperCrazy.BranchName, Is.EqualTo("🍕⬅🌟 you wat?")); - Assert.That(wrapperCrazy.Type, Is.EqualTo(StreamWrapperType.Branch)); - - wrapperCrazy = new StreamWrapper("https://testing.speckle.dev/streams/4c3ce1459c/branches/next%20level"); - Assert.That(wrapperCrazy.BranchName, Is.EqualTo("next level")); - Assert.That(wrapperCrazy.Type, Is.EqualTo(StreamWrapperType.Branch)); - } - - [Test] - public void ParseObject() - { - var wrapper = new StreamWrapper( - "https://testing.speckle.dev/streams/a75ab4f10f/objects/5530363e6d51c904903dafc3ea1d2ec6" - ); - Assert.That(wrapper.Type, Is.EqualTo(StreamWrapperType.Object)); - } - - [Test] - public void ParseCommit() - { - var wrapper = new StreamWrapper("https://testing.speckle.dev/streams/4c3ce1459c/commits/8b9b831792"); - Assert.That(wrapper.Type, Is.EqualTo(StreamWrapperType.Commit)); - } - - [Test] - public void ParseGlobalAsBranch() - { - var wrapper = new StreamWrapper("https://testing.speckle.dev/streams/0c6ad366c4/globals/"); - Assert.That(wrapper.Type, Is.EqualTo(StreamWrapperType.Branch)); - } - - [Test] - public void ParseGlobalAsCommit() - { - var wrapper = new StreamWrapper("https://testing.speckle.dev/streams/0c6ad366c4/globals/abd3787893"); - Assert.That(wrapper.Type, Is.EqualTo(StreamWrapperType.Commit)); - } - - [TestCase("https://testing.speckle.dev/projects/0c6ad366c4/models/abd3787893", StreamWrapperType.Branch)] - [TestCase("https://testing.speckle.dev/projects/28dd9ad7ba/models/117eb16f2c@b1b8579d93", StreamWrapperType.Commit)] - [TestCase( - "https://testing.speckle.dev/projects/28dd9ad7ba/models/6ae9712d6a8bad80a3efd4a29a21c31a", - StreamWrapperType.Object - )] - public void ParseFe2Urls(string speckleUrl, StreamWrapperType expectedType) - { - var wrapper = new StreamWrapper(speckleUrl); - Assert.That(wrapper.Type, Is.EqualTo(expectedType)); - Assert.That(wrapper.ToString(), Is.EqualTo(speckleUrl)); - } - - [TestCase( - "https://testing.speckle.dev/projects/28dd9ad7ba/models/117eb16f2c@b1b8579d93,abd3787893,6ae9712d6a8bad80a3efd4a29a21c31a", - StreamWrapperType.Object - )] - public void ParseFe2MultiModelUrls_IsNotSupported(string speckleUrl, StreamWrapperType expectedType) - { - Assert.Throws(() => new StreamWrapper(speckleUrl)); - } -} diff --git a/tests/Speckle.Sdk.Tests.Unit/Fixtures.cs b/tests/Speckle.Sdk.Tests.Unit/Fixtures.cs index d5a018fe..65f49f12 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Fixtures.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Fixtures.cs @@ -1,4 +1,5 @@ using Newtonsoft.Json; +using Speckle.Sdk.Common; using Speckle.Sdk.Credentials; using Speckle.Sdk.Logging; using Speckle.Sdk.Transports; @@ -16,7 +17,7 @@ public abstract class Fixtures public static void UpdateOrSaveAccount(Account account) { - DeleteLocalAccount(account.id); + DeleteLocalAccount(account.id.NotNull()); string serializedObject = JsonConvert.SerializeObject(account); s_accountStorage.SaveObjectSync(account.id, serializedObject); } @@ -27,13 +28,7 @@ public abstract class Fixtures File.WriteAllText(s_accountPath, json); } - public static void DeleteLocalAccount(string id) - { - s_accountStorage.DeleteObject(id); - } + public static void DeleteLocalAccount(string id) => s_accountStorage.DeleteObject(id); - public static void DeleteLocalAccountFile() - { - File.Delete(s_accountPath); - } + public static void DeleteLocalAccountFile() => File.Delete(s_accountPath); } diff --git a/tests/Speckle.Sdk.Tests.Unit/Helpers/Path.cs b/tests/Speckle.Sdk.Tests.Unit/Helpers/Path.cs index f3e9bd2b..422dcbbe 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Helpers/Path.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Helpers/Path.cs @@ -1,14 +1,13 @@ using System.Runtime.InteropServices; -using NUnit.Framework; +using FluentAssertions; using Speckle.Sdk.Logging; +using Xunit; namespace Speckle.Sdk.Tests.Unit.Helpers; -[TestFixture] -[TestOf(nameof(SpecklePathProvider))] public class SpecklePathTests { - [Test] + [Fact] public void TestUserApplicationDataPath() { var userPath = SpecklePathProvider.UserApplicationDataPath(); @@ -39,19 +38,10 @@ public class SpecklePathTests throw new NotImplementedException("Your OS platform is not supported"); } - Assert.That(userPath, Does.Match(pattern)); + userPath.Should().MatchRegex(pattern); } - [Test] - public void TestUserApplicationDataPathOverride() - { - var newPath = Path.GetTempPath(); - SpecklePathProvider.OverrideApplicationDataPath(newPath); - Assert.That(SpecklePathProvider.UserApplicationDataPath(), Is.EqualTo(newPath)); - SpecklePathProvider.OverrideApplicationDataPath(null); - } - - [Test] + [Fact] public void TestInstallApplicationDataPath() { var installPath = SpecklePathProvider.InstallApplicationDataPath; @@ -87,6 +77,6 @@ public class SpecklePathTests throw new NotImplementedException("Your OS platform is not supported"); } - Assert.That(installPath, Does.Match(pattern)); + installPath.Should().MatchRegex(pattern); } } diff --git a/tests/Speckle.Sdk.Tests.Unit/Host/HostApplicationTests.cs b/tests/Speckle.Sdk.Tests.Unit/Host/HostApplicationTests.cs index 18379b05..87d5aa3c 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Host/HostApplicationTests.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Host/HostApplicationTests.cs @@ -1,19 +1,22 @@ -using NUnit.Framework; -using Shouldly; +using FluentAssertions; using Speckle.Sdk.Host; +using Xunit; namespace Speckle.Sdk.Tests.Unit.Host; public class HostApplicationTests { - private static List _hostAppVersion = Enum.GetValues().ToList(); + public static TheoryData HostAppVersionData => new(Enum.GetValues().ToList()); - [Test] - [TestCaseSource("_hostAppVersion")] + [Theory] + [MemberData(nameof(HostAppVersionData))] public void HostAppVersionParsingTests(HostAppVersion appVersion) { - appVersion.ToString().StartsWith("v").ShouldBeTrue(); + // Assert that the string representation starts with 'v' + appVersion.ToString().StartsWith('v').Should().BeTrue(); + + // Assert that the parsed version is a positive integer var version = HostApplications.GetVersion(appVersion); - int.Parse(version).ShouldBePositive(); + int.Parse(version).Should().BePositive(); } } diff --git a/tests/Speckle.Sdk.Tests.Unit/Host/TestKit.cs b/tests/Speckle.Sdk.Tests.Unit/Host/TestKit.cs index 1db050b2..c3cd1b7b 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Host/TestKit.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Host/TestKit.cs @@ -34,7 +34,7 @@ public class DiningTable : Base { length = 200, width = 12, - thickness = 3 + thickness = 3, }; } diff --git a/tests/Speckle.Sdk.Tests.Unit/Models/BaseTests.cs b/tests/Speckle.Sdk.Tests.Unit/Models/BaseTests.cs index 5eed8698..c658b48a 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Models/BaseTests.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Models/BaseTests.cs @@ -1,41 +1,38 @@ -using NUnit.Framework; +using FluentAssertions; using Speckle.Sdk.Common; using Speckle.Sdk.Host; using Speckle.Sdk.Models; +using Xunit; namespace Speckle.Sdk.Tests.Unit.Models; -[TestFixture] -[TestOf(typeof(Base))] -[TestOf(typeof(DynamicBase))] public class BaseTests { - [SetUp] - public void Setup() + public BaseTests() { TypeLoader.Reset(); TypeLoader.Initialize(typeof(Base).Assembly, typeof(BaseTests).Assembly); } - [Test] + [Fact] public void CanGetSetDynamicItemProp() { var @base = new Base(); @base["Item"] = "Item"; - Assert.That(@base["Item"], Is.EqualTo("Item")); + @base["Item"].Should().Be("Item"); } - [Test] + [Fact] public void CanGetSetTypedItemProp() { var @base = new ObjectWithItemProp { Item = "baz" }; - Assert.That(@base["Item"], Is.EqualTo("baz")); - Assert.That(@base.Item, Is.EqualTo("baz")); + @base["Item"].Should().Be("baz"); + @base.Item.Should().Be("baz"); } - [Test(Description = "Checks if validation is performed in property names")] + [Fact(DisplayName = "Checks if validation is performed in property names")] public void CanValidatePropNames() { dynamic @base = new Base(); @@ -45,27 +42,35 @@ public class BaseTests // Only single leading @ allowed @base["@something"] = "A"; - Assert.Throws(() => - { - @base["@@@something"] = "Testing"; - }); + FluentActions + .Invoking(() => + { + @base["@@@something"] = "Testing"; + }) + .Should() + .Throw(); // Invalid chars: ./ - Assert.Throws(() => - { - @base["some.thing"] = "Testing"; - }); - Assert.Throws(() => - { - @base["some/thing"] = "Testing"; - }); - + FluentActions + .Invoking(() => + { + @base["some.thing"] = "Testing"; + }) + .Should() + .Throw(); + FluentActions + .Invoking(() => + { + @base["some/thing"] = "Testing"; + }) + .Should() + .Throw(); // Trying to change a class member value will throw exceptions. //Assert.Throws(() => { @base["speckle_type"] = "Testing"; }); //Assert.Throws(() => { @base["id"] = "Testing"; }); } - [Test] + [Fact] public void CountDynamicChunkables() { const int MAX_NUM = 3000; @@ -83,10 +88,10 @@ public class BaseTests @base["@(1000)cc2"] = customChunkArr; var num = @base.GetTotalChildrenCount(); - Assert.That(num, Is.EqualTo(MAX_NUM / 1000 * 2 + 1)); + num.Should().Be(MAX_NUM / 1000 * 2 + 1); } - [Test] + [Fact] public void CountTypedChunkables() { const int MAX_NUM = 3000; @@ -105,33 +110,33 @@ public class BaseTests var num = @base.GetTotalChildrenCount(); var actualNum = 1 + MAX_NUM / 300 + MAX_NUM / 1000; - Assert.That(num, Is.EqualTo(actualNum)); + num.Should().Be(actualNum); } - [Test(Description = "Checks that no ignored or obsolete properties are returned")] + [Fact(DisplayName = "Checks that no ignored or obsolete properties are returned")] public void CanGetMemberNames() { var @base = new SampleObject(); var dynamicProp = "dynamicProp"; @base[dynamicProp] = 123; var names = @base.GetMembers().Keys; - Assert.That(names, Has.No.Member(nameof(@base.IgnoredSchemaProp))); - Assert.That(names, Has.No.Member(nameof(@base.ObsoleteSchemaProp))); - Assert.That(names, Has.Member(dynamicProp)); - Assert.That(names, Has.Member(nameof(@base.attachedProp))); + names.Should().NotContain(nameof(@base.IgnoredSchemaProp)); + names.Should().NotContain(nameof(@base.ObsoleteSchemaProp)); + names.Should().Contain(dynamicProp); + names.Should().Contain(nameof(@base.attachedProp)); } - [Test(Description = "Checks that only instance properties are returned, excluding obsolete and ignored.")] + [Fact(DisplayName = "Checks that only instance properties are returned, excluding obsolete and ignored.")] public void CanGetMembers_OnlyInstance() { var @base = new SampleObject(); @base["dynamicProp"] = 123; var names = @base.GetMembers(DynamicBaseMemberType.Instance).Keys; - Assert.That(names, Has.Member(nameof(@base.attachedProp))); + names.Should().Contain(nameof(@base.attachedProp)); } - [Test(Description = "Checks that only dynamic properties are returned")] + [Fact(DisplayName = "Checks that only dynamic properties are returned")] public void CanGetMembers_OnlyDynamic() { var @base = new SampleObject(); @@ -139,33 +144,33 @@ public class BaseTests @base[dynamicProp] = 123; var names = @base.GetMembers(DynamicBaseMemberType.Dynamic).Keys; - Assert.That(names, Has.Member(dynamicProp)); - Assert.That(names, Has.Count.EqualTo(1)); + names.Should().Contain(dynamicProp); + names.Count.Should().Be(1); } - [Test(Description = "Checks that all typed properties (including ignored ones) are returned")] + [Fact(DisplayName = "Checks that all typed properties (including ignored ones) are returned")] public void CanGetMembers_OnlyInstance_IncludeIgnored() { var @base = new SampleObject(); @base["dynamicProp"] = 123; var names = @base.GetMembers(DynamicBaseMemberType.Instance | DynamicBaseMemberType.SchemaIgnored).Keys; - Assert.That(names, Has.Member(nameof(@base.IgnoredSchemaProp))); - Assert.That(names, Has.Member(nameof(@base.attachedProp))); + names.Should().Contain(nameof(@base.IgnoredSchemaProp)); + names.Should().Contain(nameof(@base.attachedProp)); } - [Test(Description = "Checks that all typed properties (including obsolete ones) are returned")] + [Fact(DisplayName = "Checks that all typed properties (including obsolete ones) are returned")] public void CanGetMembers_OnlyInstance_IncludeObsolete() { var @base = new SampleObject(); @base["dynamicProp"] = 123; var names = @base.GetMembers(DynamicBaseMemberType.Instance | DynamicBaseMemberType.Obsolete).Keys; - Assert.That(names, Has.Member(nameof(@base.ObsoleteSchemaProp))); - Assert.That(names, Has.Member(nameof(@base.attachedProp))); + names.Should().Contain(nameof(@base.ObsoleteSchemaProp)); + names.Should().Contain(nameof(@base.attachedProp)); } - [Test] + [Fact] public void CanGetDynamicMembers() { var @base = new SampleObject(); @@ -173,11 +178,11 @@ public class BaseTests @base[dynamicProp] = null; var names = @base.GetDynamicMemberNames(); - Assert.That(names, Has.Member(dynamicProp)); - Assert.That(@base[dynamicProp], Is.Null); + names.Should().Contain(dynamicProp); + @base[dynamicProp].Should().BeNull(); } - [Test] + [Fact] public void CanSetDynamicMembers() { var @base = new SampleObject(); @@ -185,19 +190,19 @@ public class BaseTests var value = "something"; // Can create a new dynamic member @base[key] = value; - Assert.That(value, Is.EqualTo((string)@base[key].NotNull())); + value.Should().Be((string)@base[key].NotNull()); // Can overwrite existing value = "some other value"; @base[key] = value; - Assert.That(value, Is.EqualTo((string)@base[key].NotNull())); + value.Should().Be((string)@base[key].NotNull()); // Accepts null values @base[key] = null; - Assert.That(@base[key], Is.Null); + @base[key].Should().BeNull(); } - [Test] + [Fact] public void CanShallowCopy() { var sample = new SampleObject(); @@ -208,8 +213,8 @@ public class BaseTests var sampleMembers = sample.GetMembers(selectedMembers); var copyMembers = copy.GetMembers(selectedMembers); - Assert.That(copyMembers.Keys, Is.EquivalentTo(sampleMembers.Keys)); - Assert.That(copyMembers.Values, Is.EquivalentTo(sampleMembers.Values)); + copyMembers.Keys.Should().BeEquivalentTo(sampleMembers.Keys); + copyMembers.Values.Should().BeEquivalentTo(sampleMembers.Values); } [SpeckleType("Speckle.Core.Tests.Unit.Models.BaseTests+SampleObject")] diff --git a/tests/Speckle.Sdk.Tests.Unit/Models/Extensions/BaseExtensionsTests.cs b/tests/Speckle.Sdk.Tests.Unit/Models/Extensions/BaseExtensionsTests.cs index 5af6a011..f0a24de5 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Models/Extensions/BaseExtensionsTests.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Models/Extensions/BaseExtensionsTests.cs @@ -1,46 +1,44 @@ -using NUnit.Framework; +using FluentAssertions; using Speckle.Sdk.Host; using Speckle.Sdk.Models; using Speckle.Sdk.Models.Collections; using Speckle.Sdk.Models.Extensions; +using Xunit; namespace Speckle.Sdk.Tests.Unit.Models.Extensions; -[TestFixture] -[TestOf(nameof(BaseExtensions))] public class BaseExtensionsTests { - [SetUp] - public void Setup() + public BaseExtensionsTests() { TypeLoader.Reset(); TypeLoader.Initialize(typeof(Base).Assembly); } - [Test] - [TestCase("myDynamicProp")] - [TestCase("elements")] + [Theory] + [InlineData("myDynamicProp")] + [InlineData("elements")] public void GetDetachedPropName_Dynamic(string propertyName) { var data = new TestBase(); var result = data.GetDetachedPropName(propertyName); var expected = $"@{propertyName}"; - Assert.That(result, Is.EqualTo(expected)); + result.Should().Be(expected); } - [Test] - [TestCase(nameof(TestBase.myProperty))] - [TestCase(nameof(TestBase.myOtherProperty))] + [Theory] + [InlineData(nameof(TestBase.myProperty))] + [InlineData(nameof(TestBase.myOtherProperty))] public void GetDetachedPropName_Instance(string propertyName) { var data = new TestBase(); var result = data.GetDetachedPropName(propertyName); - Assert.That(result, Is.EqualTo(propertyName)); + result.Should().Be(propertyName); } - [Test] + [Fact] public void TraverseWithPath() { var collection = new Collection() { name = "collection" }; @@ -51,17 +49,18 @@ public class BaseExtensionsTests var basePaths = collection.TraverseWithPath((obj => obj is not Collection)).ToList(); - Assert.That(basePaths.Count, Is.EqualTo(3)); - Assert.That(basePaths[0].Item2.speckle_type, Is.EqualTo("Speckle.Core.Models.Collections.Collection")); - Assert.That(basePaths[0].Item2["name"], Is.EqualTo("collection")); - Assert.That(basePaths[0].Item1, Is.EqualTo(new List())); + basePaths.Count.Should().Be(3); - Assert.That(basePaths[1].Item2.speckle_type, Is.EqualTo("Speckle.Core.Models.Collections.Collection")); - Assert.That(basePaths[1].Item2["name"], Is.EqualTo("subCollection")); - Assert.That(basePaths[1].Item1, Is.EqualTo(new List() { "collection" })); + basePaths[0].Item2.speckle_type.Should().Be("Speckle.Core.Models.Collections.Collection"); + basePaths[0].Item2["name"].Should().Be("collection"); + basePaths[0].Item1.Should().BeEquivalentTo(new List()); - Assert.That(basePaths[2].Item2.speckle_type, Is.EqualTo("Base")); - Assert.That(basePaths[2].Item1, Is.EqualTo(new List() { "collection", "subCollection" })); + basePaths[1].Item2.speckle_type.Should().Be("Speckle.Core.Models.Collections.Collection"); + basePaths[1].Item2["name"].Should().Be("subCollection"); + basePaths[1].Item1.Should().BeEquivalentTo(new List() { "collection" }); + + basePaths[2].Item2.speckle_type.Should().Be("Base"); + basePaths[2].Item1.Should().BeEquivalentTo(new List() { "collection", "subCollection" }); } [SpeckleType("Speckle.Core.Tests.Unit.Models.Extensions.BaseExtensionsTests+TestBase")] diff --git a/tests/Speckle.Sdk.Tests.Unit/Models/Extensions/DisplayValueTests.cs b/tests/Speckle.Sdk.Tests.Unit/Models/Extensions/DisplayValueTests.cs index 07b00adf..b000afcc 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Models/Extensions/DisplayValueTests.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Models/Extensions/DisplayValueTests.cs @@ -1,11 +1,11 @@ -using NUnit.Framework; +using FluentAssertions; using Speckle.Sdk.Host; using Speckle.Sdk.Models; using Speckle.Sdk.Models.Extensions; +using Xunit; namespace Speckle.Sdk.Tests.Unit.Models.Extensions; -[TestOf(typeof(BaseExtensions))] public class DisplayValueTests { private const string PAYLOAD = "This is my payload"; @@ -22,28 +22,35 @@ public class DisplayValueTests TypeLoader.Initialize(typeof(Base).Assembly); } - [SetUp] + [Fact] public void Setup() => Reset(); - [TestCaseSource(nameof(TestCases))] + [Theory] + [MemberData(nameof(TestCases))] public void TestTryGetDisplayValue_WithValue(Base testCase) { var res = testCase.TryGetDisplayValue(); - Assert.That(res, Has.Count.EqualTo(1)); - Assert.That(res, Has.One.Items.TypeOf().With.Property(nameof(Base.applicationId)).EqualTo(PAYLOAD)); + // Assert collection count + res?.Count.Should().Be(1); + + // Assert the single item matches the expected type and property + var displayValue = res?[0]; + displayValue.Should().NotBeNull(); + displayValue!.applicationId.Should().Be(PAYLOAD); } - public static IEnumerable TestCases() + public static IEnumerable TestCases() { - var listOfBase = new List { s_displayValue }; //This is what our deserializer will output + var listOfBase = new List { s_displayValue }; // This is what our deserializer will output var listOfMesh = new List { s_displayValue }; - yield return new Base { ["@displayValue"] = s_displayValue }; - yield return new Base { ["displayValue"] = s_displayValue }; - yield return new Base { ["@displayValue"] = listOfBase }; - yield return new Base { ["displayValue"] = listOfBase }; - yield return new TypedDisplayValue { displayValue = s_displayValue }; - yield return new TypedDisplayValueList { displayValue = listOfMesh }; + + yield return [new Base { ["@displayValue"] = s_displayValue }]; + yield return [new Base { ["displayValue"] = s_displayValue }]; + yield return [new Base { ["@displayValue"] = listOfBase }]; + yield return [new Base { ["displayValue"] = listOfBase }]; + yield return [new TypedDisplayValue { displayValue = s_displayValue }]; + yield return [new TypedDisplayValueList { displayValue = listOfMesh }]; } [SpeckleType("Speckle.Core.Tests.Unit.Models.Extensions.DisplayValueTests+TypedDisplayValue")] diff --git a/tests/Speckle.Sdk.Tests.Unit/Models/Extensions/ExceptionTests.cs b/tests/Speckle.Sdk.Tests.Unit/Models/Extensions/ExceptionTests.cs index bf6640bd..4b5c3750 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Models/Extensions/ExceptionTests.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Models/Extensions/ExceptionTests.cs @@ -1,26 +1,27 @@ -using NUnit.Framework; +using FluentAssertions; using Speckle.Sdk.Models.Extensions; +using Xunit; namespace Speckle.Sdk.Tests.Unit.Models.Extensions; -[TestFixture] -[TestOf(typeof(BaseExtensions))] public class ExceptionTests { - [Test] + [Fact] public void CanPrintAllInnerExceptions() { + // Test with a single exception var ex = new Exception("Some error"); var exMsg = ex.ToFormattedString(); - Assert.That(exMsg, Is.Not.Null); + exMsg.Should().NotBeNull(); + // Test with an inner exception var ex2 = new Exception("One or more errors occurred", ex); var ex2Msg = ex2.ToFormattedString(); - Assert.That(ex2Msg, Is.Not.Null); + ex2Msg.Should().NotBeNull(); + // Test with an aggregate exception var ex3 = new AggregateException("One or more errors occurred", ex2); var ex3Msg = ex3.ToFormattedString(); - - Assert.That(ex3Msg, Is.Not.Null); + ex3Msg.Should().NotBeNull(); } } diff --git a/tests/Speckle.Sdk.Tests.Unit/Models/GraphTraversal/GraphTraversalTests.cs b/tests/Speckle.Sdk.Tests.Unit/Models/GraphTraversal/GraphTraversalTests.cs index a31680f4..945102a4 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Models/GraphTraversal/GraphTraversalTests.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Models/GraphTraversal/GraphTraversalTests.cs @@ -1,17 +1,15 @@ using System.Collections; -using NUnit.Framework; +using FluentAssertions; using Speckle.Sdk.Host; using Speckle.Sdk.Models; using Speckle.Sdk.Models.GraphTraversal; -using Speckle.Sdk.Tests.Unit.Host; +using Xunit; namespace Speckle.Sdk.Tests.Unit.Models.GraphTraversal; -[TestFixture, TestOf(typeof(Sdk.Models.GraphTraversal.GraphTraversal))] public class GraphTraversalTests { - [SetUp] - public void Setup() + public GraphTraversalTests() { TypeLoader.Reset(); TypeLoader.Initialize(typeof(Base).Assembly, typeof(TraversalMock).Assembly); @@ -23,7 +21,7 @@ public class GraphTraversalTests return sut.Traverse(testCase); } - [Test] + [Fact] public void Traverse_TraversesListMembers() { var traverseListsRule = TraversalRule @@ -36,26 +34,25 @@ public class GraphTraversalTests var expectTraverse = new Base { id = "List Member" }; var expectIgnored = new Base { id = "Not List Member" }; - TraversalMock testCase = - new() - { - ListChildren = new List { expectTraverse }, - DictChildren = new Dictionary { ["myprop"] = expectIgnored }, - Child = expectIgnored - }; + TraversalMock testCase = new() + { + ListChildren = [expectTraverse], + DictChildren = new Dictionary { ["myprop"] = expectIgnored }, + Child = expectIgnored, + }; var ret = Traverse(testCase, traverseListsRule).Select(b => b.Current).ToList(); - //Assert expected members present - Assert.That(ret, Has.Exactly(1).Items.EqualTo(testCase)); - Assert.That(ret, Has.Exactly(1).Items.EqualTo(expectTraverse)); + // Assert expected members present + ret.Should().Contain(testCase); + ret.Should().Contain(expectTraverse); - //Assert unexpected members not present - Assert.That(ret, Has.No.Member(expectIgnored)); - Assert.That(ret, Has.Count.EqualTo(2)); + // Assert unexpected members not present + ret.Should().NotContain(expectIgnored); + ret.Count.Should().Be(2); } - [Test] + [Fact] public void Traverse_TraversesDictMembers() { var traverseListsRule = TraversalRule @@ -68,26 +65,25 @@ public class GraphTraversalTests var expectTraverse = new Base { id = "Dict Member" }; var expectIgnored = new Base { id = "Not Dict Member" }; - TraversalMock testCase = - new() - { - ListChildren = new List { expectIgnored }, - DictChildren = new Dictionary { ["myprop"] = expectTraverse }, - Child = expectIgnored - }; + TraversalMock testCase = new() + { + ListChildren = [expectIgnored], + DictChildren = new Dictionary { ["myprop"] = expectTraverse }, + Child = expectIgnored, + }; var ret = Traverse(testCase, traverseListsRule).Select(b => b.Current).ToList(); - //Assert expected members present - Assert.That(ret, Has.Exactly(1).Items.EqualTo(testCase)); - Assert.That(ret, Has.Exactly(1).Items.EqualTo(expectTraverse)); + // Assert expected members present + ret.Should().Contain(testCase); + ret.Should().Contain(expectTraverse); - //Assert unexpected members not present - Assert.That(ret, Has.No.Member(expectIgnored)); - Assert.That(ret, Has.Count.EqualTo(2)); + // Assert unexpected members not present + ret.Should().NotContain(expectIgnored); + ret.Count.Should().Be(2); } - [Test] + [Fact] public void Traverse_TraversesDynamic() { var traverseListsRule = TraversalRule @@ -98,26 +94,25 @@ public class GraphTraversalTests var expectTraverse = new Base { id = "List Member" }; var expectIgnored = new Base { id = "Not List Member" }; - TraversalMock testCase = - new() - { - Child = expectIgnored, - ["dynamicChild"] = expectTraverse, - ["dynamicListChild"] = new List { expectTraverse } - }; + TraversalMock testCase = new() + { + Child = expectIgnored, + ["dynamicChild"] = expectTraverse, + ["dynamicListChild"] = new List { expectTraverse }, + }; var ret = Traverse(testCase, traverseListsRule).Select(b => b.Current).ToList(); - //Assert expected members present - Assert.That(ret, Has.Exactly(1).Items.EqualTo(testCase)); - Assert.That(ret, Has.Exactly(2).Items.EqualTo(expectTraverse)); + // Assert expected members present + ret.Should().Contain(testCase); + ret.Count(x => x == expectTraverse).Should().Be(2); - //Assert unexpected members not present - Assert.That(ret, Has.No.Member(expectIgnored)); - Assert.That(ret, Has.Count.EqualTo(3)); + // Assert unexpected members not present + ret.Should().NotContain(expectIgnored); + ret.Count.Should().Be(3); } - [Test] + [Fact] public void Traverse_ExclusiveRule() { var expectTraverse = new Base { id = "List Member" }; @@ -128,22 +123,21 @@ public class GraphTraversalTests .When(_ => true) .ContinueTraversing(x => x.GetMembers(DynamicBaseMemberType.Dynamic).Select(kvp => kvp.Key)); - TraversalMock testCase = - new() - { - Child = expectIgnored, - ["dynamicChild"] = expectTraverse, - ["dynamicListChild"] = new List { expectTraverse } - }; + TraversalMock testCase = new() + { + Child = expectIgnored, + ["dynamicChild"] = expectTraverse, + ["dynamicListChild"] = new List { expectTraverse }, + }; var ret = Traverse(testCase, traverseListsRule).Select(b => b.Current).ToList(); - //Assert expected members present - Assert.That(ret, Has.Exactly(1).Items.EqualTo(testCase)); - Assert.That(ret, Has.Exactly(2).Items.EqualTo(expectTraverse)); + // Assert expected members present + ret.Should().Contain(testCase); + ret.Count(x => x == expectTraverse).Should().Be(2); - //Assert unexpected members not present - Assert.That(ret, Has.No.Member(expectIgnored)); - Assert.That(ret, Has.Count.EqualTo(3)); + // Assert unexpected members not present + ret.Should().NotContain(expectIgnored); + ret.Count.Should().Be(3); } } diff --git a/tests/Speckle.Sdk.Tests.Unit/Models/GraphTraversal/TraversalContextExtensionsTests.cs b/tests/Speckle.Sdk.Tests.Unit/Models/GraphTraversal/TraversalContextExtensionsTests.cs index a386ca14..d8c6f072 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Models/GraphTraversal/TraversalContextExtensionsTests.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Models/GraphTraversal/TraversalContextExtensionsTests.cs @@ -1,62 +1,71 @@ -using NUnit.Framework; +using FluentAssertions; using Speckle.Sdk.Common; using Speckle.Sdk.Models; using Speckle.Sdk.Models.Collections; using Speckle.Sdk.Models.GraphTraversal; +using Xunit; namespace Speckle.Sdk.Tests.Unit.Models.GraphTraversal; -[TestOf(typeof(TraversalContextExtensions))] +// Mark test class for xUnit public class TraversalContextExtensionsTests { - public static int[] TestDepths => new[] { 1, 2, 10 }; - private TraversalContext? CreateLinkedList(int depth, Func createBaseFunc) { if (depth <= 0) + { return null; + } + return new TraversalContext(createBaseFunc(depth), $"{depth}", CreateLinkedList(depth - 1, createBaseFunc)); } - [TestCaseSource(nameof(TestDepths))] + [Theory] // replaces [TestCaseSource] + [MemberData(nameof(GetTestDepths))] public void GetPropertyPath_ReturnsSequentialPath(int depth) { - var testData = CreateLinkedList(depth, i => new()).NotNull(); + var testData = CreateLinkedList(depth, i => new Base()).NotNull(); - var path = TraversalContextExtensions.GetPropertyPath(testData); + var path = testData.GetPropertyPath(); var expected = Enumerable.Range(1, depth).Select(i => i.ToString()); - Assert.That(path, Is.EquivalentTo(expected)); + path.Should().BeEquivalentTo(expected); } - [TestCaseSource(nameof(TestDepths))] + [Theory] + [MemberData(nameof(GetTestDepths))] public void GetAscendant(int depth) { - var testData = CreateLinkedList(depth, i => new()).NotNull(); + var testData = CreateLinkedList(depth, i => new Base()).NotNull(); - var all = TraversalContextExtensions.GetAscendants(testData).ToArray(); + var all = testData.GetAscendants().ToArray(); - Assert.That(all, Has.Length.EqualTo(depth)); + all.Length.Should().Be(depth); } - [TestCaseSource(nameof(TestDepths))] + [Theory] + [MemberData(nameof(GetTestDepths))] public void GetAscendantOfType_AllBase(int depth) { - var testData = CreateLinkedList(depth, i => new()).NotNull(); + var testData = CreateLinkedList(depth, i => new Base()).NotNull(); - var all = TraversalContextExtensions.GetAscendantOfType(testData).ToArray(); + var all = testData.GetAscendantOfType().ToArray(); - Assert.That(all, Has.Length.EqualTo(depth)); + all.Length.Should().Be(depth); } - [TestCaseSource(nameof(TestDepths))] + [Theory] + [MemberData(nameof(GetTestDepths))] public void GetAscendantOfType_EveryOtherIsCollection(int depth) { var testData = CreateLinkedList(depth, i => i % 2 == 0 ? new Base() : new Collection()).NotNull(); - var all = TraversalContextExtensions.GetAscendantOfType(testData).ToArray(); + var all = testData.GetAscendantOfType().ToArray(); - Assert.That(all, Has.Length.EqualTo(Math.Ceiling(depth / 2.0))); + all.Length.Should().Be((int)Math.Ceiling(depth / 2.0)); } + + // Providing the test depths to [MemberData] for xUnit + public static IEnumerable GetTestDepths() => new[] { 1, 2, 10 }.Select(depth => new object[] { depth }); } diff --git a/tests/Speckle.Sdk.Tests.Unit/Models/Hashing.cs b/tests/Speckle.Sdk.Tests.Unit/Models/Hashing.cs index e57b3bf5..decd2b33 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Models/Hashing.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Models/Hashing.cs @@ -1,50 +1,50 @@ using System.Diagnostics; -using NUnit.Framework; +using FluentAssertions; using Speckle.Sdk.Host; using Speckle.Sdk.Models; using Speckle.Sdk.Tests.Unit.Host; +using Xunit; namespace Speckle.Sdk.Tests.Unit.Models; -[TestFixture] -[TestOf(typeof(Base))] +// Removed [TestFixture] and [TestOf] annotations as they are NUnit specific public class Hashing { - [SetUp] - public void Setup() + public Hashing() { TypeLoader.Reset(); TypeLoader.Initialize(typeof(Base).Assembly, typeof(DiningTable).Assembly); } - [Test(Description = "Checks that hashing (as represented by object ids) actually works.")] - public void HashChangeCheck() + [Fact(DisplayName = "Checks that hashing (as represented by object IDs) actually works.")] + public void HashChangeCheck_Test() { var table = new DiningTable(); var secondTable = new DiningTable(); - Assert.That(secondTable.GetId(), Is.EqualTo(table.GetId())); + secondTable.GetId().Should().Be(table.GetId(), "Object IDs of identical objects should match."); ((dynamic)secondTable).testProp = "wonderful"; - Assert.That(secondTable.GetId(), Is.Not.EqualTo(table.GetId())); + secondTable.GetId().Should().NotBe(table.GetId(), "Changing a property should alter the object ID."); } - [Test( - Description = "Tests the convention that dynamic properties that have key names prepended with '__' are ignored." - )] - public void IgnoredDynamicPropertiesCheck() + [Fact(DisplayName = "Verifies that dynamic properties with '__' prefix are ignored during hashing.")] + public void IgnoredDynamicPropertiesCheck_Test() { var table = new DiningTable(); var originalHash = table.GetId(); ((dynamic)table).__testProp = "wonderful"; - Assert.That(table.GetId(), Is.EqualTo(originalHash)); + table + .GetId() + .Should() + .Be(originalHash, "Hashing of table should not change when '__' prefixed properties are added."); } - [Test(Description = "Rather stupid test as results vary wildly even on one machine.")] - public void HashingPerformance() + [Fact(DisplayName = "Performance test: Hash computation time for large and small objects.")] + public void HashingPerformance_Test() { var polyline = new Polyline(); @@ -62,25 +62,25 @@ public class Hashing _ = polyline.GetId(); var diff1 = stopWatch.ElapsedMilliseconds - stopWatchStep; - Assert.That(diff1, Is.LessThan(300), $"Hashing shouldn't take that long ({diff1} ms) for the test object used."); + diff1.Should().BeLessThan(300, $"Hashing shouldn't take that long ({diff1} ms) for the test object used."); Console.WriteLine($"Big obj hash duration: {diff1} ms"); var pt = new Point { X = 10, Y = 12, - Z = 30 + Z = 30, }; stopWatchStep = stopWatch.ElapsedMilliseconds; _ = pt.GetId(); var diff2 = stopWatch.ElapsedMilliseconds - stopWatchStep; - Assert.That(diff2, Is.LessThan(10), $"Hashing shouldn't take that long ({diff2} ms)for the point object used."); + diff2.Should().BeLessThan(10, $"Hashing shouldn't take that long ({diff2} ms) for the point object used."); Console.WriteLine($"Small obj hash duration: {diff2} ms"); } - [Test(Description = "The hash of a decomposed object is different that that of a non-decomposed object.")] - public void DecompositionHashes() + [Fact(DisplayName = "Verifies that decomposed and non-decomposed objects have different hashes.")] + public void DecompositionHashes_Test() { var table = new DiningTable(); ((dynamic)table)["@decomposeMePlease"] = new Point(); @@ -88,6 +88,6 @@ public class Hashing var hash1 = table.GetId(); var hash2 = table.GetId(true); - Assert.That(hash2, Is.Not.EqualTo(hash1)); + hash2.Should().NotBe(hash1, "Hash values should differ for decomposed and non-decomposed objects."); } } diff --git a/tests/Speckle.Sdk.Tests.Unit/Models/SpeckleType.cs b/tests/Speckle.Sdk.Tests.Unit/Models/SpeckleType.cs index 8d404779..63500570 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Models/SpeckleType.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Models/SpeckleType.cs @@ -1,45 +1,43 @@ -using NUnit.Framework; +using FluentAssertions; using Speckle.Sdk.Host; using Speckle.Sdk.Models; -using TestModels; +using Speckle.Sdk.Tests.Unit.Models.TestModels; +using Xunit; namespace Speckle.Sdk.Tests.Unit.Models { - [TestFixture] - [TestOf(typeof(Base))] public class SpeckleTypeTests { - [SetUp] - public void Setup() + public SpeckleTypeTests() { + // Setup logic during test class initialization TypeLoader.Reset(); TypeLoader.Initialize(typeof(Base).Assembly, typeof(Foo).Assembly); } - [Test, TestCaseSource(nameof(s_cases))] - public void SpeckleTypeIsProperlyBuilt(Base foo, string expectedType) - { - Assert.That(foo.speckle_type, Is.EqualTo(expectedType)); - } + [Theory] + [MemberData(nameof(Cases))] + public void SpeckleTypeIsProperlyBuilt(Base foo, string expectedType) => foo.speckle_type.Should().Be(expectedType); - private static readonly object[] s_cases = - { - new object[] { new Base(), "Base" }, - new object[] { new Foo(), "TestModels.Foo" }, - new object[] { new Bar(), "TestModels.Foo:TestModels.Bar" }, - new object[] { new Baz(), "TestModels.Foo:TestModels.Bar:TestModels.Baz" } - }; + public static IEnumerable Cases => + new List + { + new object[] { new Base(), "Base" }, + new object[] { new Foo(), "TestModels.Foo" }, + new object[] { new Bar(), "TestModels.Foo:TestModels.Bar" }, + new object[] { new Baz(), "TestModels.Foo:TestModels.Bar:TestModels.Baz" }, + }; + } + + namespace TestModels + { + [SpeckleType("TestModels.Foo")] + public class Foo : Base { } + + [SpeckleType("TestModels.Bar")] + public class Bar : Foo { } + + [SpeckleType("TestModels.Baz")] + public class Baz : Bar { } } } - -namespace TestModels -{ - [SpeckleType("TestModels.Foo")] - public class Foo : Base { } - - [SpeckleType("TestModels.Bar")] - public class Bar : Foo { } - - [SpeckleType("TestModels.Baz")] - public class Baz : Bar { } -} diff --git a/tests/Speckle.Sdk.Tests.Unit/Models/TraversalTests.cs b/tests/Speckle.Sdk.Tests.Unit/Models/TraversalTests.cs index 3e4aaea2..da0c804b 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Models/TraversalTests.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Models/TraversalTests.cs @@ -1,44 +1,50 @@ -using NUnit.Framework; +using FluentAssertions; +using Speckle.Sdk.Common; using Speckle.Sdk.Models; using Speckle.Sdk.Models.Extensions; +using Xunit; namespace Speckle.Sdk.Tests.Unit.Models; -[TestFixture, TestOf(typeof(BaseExtensions))] public class TraversalTests { - [Test, Description("Tests that provided breaker rules are respected")] + [Fact(DisplayName = "Tests that provided breaker rules are respected")] public void TestFlattenWithBreaker() { //Setup - Base root = - new() + Base root = new() + { + id = "root", + ["child"] = new Base { - id = "root", + id = "traverse through me", ["child"] = new Base { - id = "traverse through me", - ["child"] = new Base - { - id = "break on me, go no further", - ["child"] = new Base { id = "should have ignored me" } - } - } - }; + id = "break on me, go no further", + ["child"] = new Base { id = "should have ignored me" }, + }, + }, + }; - static bool BreakRule(Base b) => b.id.Contains("break on me"); + static bool BreakRule(Base b) => b.id.NotNull().Contains("break on me"); //Flatten var ret = root.Flatten(BreakRule).ToList(); //Test - Assert.That(ret, Has.Count.EqualTo(3)); - Assert.That(ret, Is.Unique); - Assert.That(ret.Where(BreakRule), Is.Not.Empty); - Assert.That(ret, Has.No.Member(Contains.Substring("should have ignored me"))); + ret.Count.Should().Be(3); + + ret.Should().OnlyHaveUniqueItems(); + + ret.Where(BreakRule).Should().NotBeEmpty(); + + ret.Should().NotContain(x => x.id == "should have ignored me"); } - [Test, TestCase(5, 5), TestCase(5, 10), TestCase(10, 5), Description("Tests breaking after a fixed number of items")] + [Theory(DisplayName = "Tests breaking after a fixed number of items")] + [InlineData(5, 5)] + [InlineData(5, 10)] + [InlineData(10, 5)] public void TestBreakerFixed(int nestDepth, int flattenDepth) { //Setup @@ -56,11 +62,12 @@ public class TraversalTests var ret = rootObject.Flatten(_ => ++counter >= flattenDepth).ToList(); //Test - Assert.That(ret, Has.Count.EqualTo(Math.Min(flattenDepth, nestDepth))); - Assert.That(ret, Is.Unique); + ret.Count.Should().Be(Math.Min(flattenDepth, nestDepth)); + + ret.Should().OnlyHaveUniqueItems(); } - [Test, Timeout(2000), Description("Tests that the flatten function does not get stuck on circular references")] + [Fact(DisplayName = "Tests that the flatten function does not get stuck on circular references")] public void TestCircularReference() { //Setup @@ -76,12 +83,14 @@ public class TraversalTests var ret = objectA.Flatten().ToList(); //Test - Assert.That(ret, Is.Unique); - Assert.That(ret, Is.EquivalentTo(new[] { objectA, objectB, objectC })); - Assert.That(ret, Has.Count.EqualTo(3)); + ret.Should().OnlyHaveUniqueItems(); + + ret.Should().BeEquivalentTo([objectA, objectB, objectC]); + + ret.Count.Should().Be(3); } - [Test, Description("Tests that the flatten function correctly handles (non circular) duplicates")] + [Fact(DisplayName = "Tests that the flatten function correctly handles (non circular) duplicates")] public void TestDuplicates() { //Setup @@ -95,8 +104,10 @@ public class TraversalTests var ret = objectA.Flatten().ToList(); //Test - Assert.That(ret, Is.Unique); - Assert.That(ret, Is.EquivalentTo(new[] { objectA, objectB })); - Assert.That(ret, Has.Count.EqualTo(2)); + ret.Should().OnlyHaveUniqueItems(); + + ret.Should().BeEquivalentTo([objectA, objectB]); + + ret.Count.Should().Be(2); } } diff --git a/tests/Speckle.Sdk.Tests.Unit/Models/UtilitiesTests.cs b/tests/Speckle.Sdk.Tests.Unit/Models/UtilitiesTests.cs index 9ff1fffd..72f71821 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Models/UtilitiesTests.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Models/UtilitiesTests.cs @@ -1,32 +1,121 @@ -using NUnit.Framework; +using FluentAssertions; +using Speckle.Sdk.Dependencies; using Speckle.Sdk.Helpers; -using Speckle.Sdk.Models; +using Xunit; namespace Speckle.Sdk.Tests.Unit.Models; -[TestFixture(TestOf = typeof(Crypt))] public sealed class HashUtilityTests { - [Test] - [TestOf(nameof(Crypt.Md5))] - [TestCase("WnAbz1hCznVmDh1", "ad48ff1e60ea2369de178aaab2fa99af")] - [TestCase("wQKrSUzBB7FI1o6", "2424cff4a88055b149e5ff2aaf0b3131")] - public void Md5(string input, string expected) + public static IEnumerable SmallTestCases() { - var lower = Crypt.Md5(input, "x2"); - var upper = Crypt.Md5(input, "X2"); - Assert.That(lower, Is.EqualTo(expected.ToLower())); - Assert.That(upper, Is.EqualTo(expected.ToUpper())); + yield return + [ + "fxFB14cBcXvoENN", + "491267c87e343c2a4f9070034f4f8966e8ee4c14e5baf6f49289833142e5b509", + "d38572fdb20fe90c4871178df3f9570d", + ]; + yield return + [ + "tgWsOH8frdAwJT7", + "dd62d2028d8243f07cbdbb0cd4c3460a96c88dd6322dd9fceba4e4912ad88fa7", + "a7eecf20d68f836f462963928cd0f1a1", + ]; + yield return + [ + "wQKrSUzBB7FI1o6", + "70be5055f737e05d287c8898c7fcd3342733a337b67fe64f91fd34dcdf92fc88", + "2424cff4a88055b149e5ff2aaf0b3131", + ]; + yield return + [ + "WnAbz1hCznVmDh1", + "511433f4bb8d24d4ef7d4478984fd36f17ab6c58676f40ad0f4bcb615de0e313", + "ad48ff1e60ea2369de178aaab2fa99af", + ]; } - [TestCase("fxFB14cBcXvoENN", "887db9349afa455f957a95f9dbacbb3c10697749cf4d4afc5c6398932a596fbc")] - [TestCase("tgWsOH8frdAwJT7", "e486224ded0dcb1452d69d0d005a6dcbc52087f6e8c66e04803e1337a192abb4")] - [TestOf(nameof(Crypt.Sha256))] - public void Sha256(string input, string expected) + public static IEnumerable SmallTestCases(IEnumerable cases, IEnumerable range) { - var lower = Crypt.Sha256(input, "x2"); - var upper = Crypt.Sha256(input, "X2"); - Assert.That(lower, Is.EqualTo(expected.ToLower())); - Assert.That(upper, Is.EqualTo(expected.ToUpper())); + foreach (var length in range) + { + foreach (var testCase in cases) + { + yield return [.. testCase, length]; + } + } + } + + public static IEnumerable SmallTestCasesMd5() => + SmallTestCases(SmallTestCases(), EnumerableExtensions.RangeFrom(0, 32)); + + public static IEnumerable SmallTestCasesSha256() => + SmallTestCases(SmallTestCases(), EnumerableExtensions.RangeFrom(2, 64)); + + public static IEnumerable SmallTestCasesSha256Span() => + SmallTestCases(SmallTestCases(), EnumerableExtensions.RangeFrom(2, 64).Where(x => x % 2 == 0)); + + public static IEnumerable LargeTestCases() + { + Random random = new(420); + yield return + [ + new string(Enumerable.Range(0, 1_000_000).Select(_ => (char)random.Next(32, 127)).ToArray()), + "b919b9e60cd6bb86ab395ee1408e12efd4d3e4e7b58f02b4cda6b4120086959a", + ]; + yield return + [ + new string(Enumerable.Range(0, 10_000_000).Select(_ => (char)random.Next(32, 127)).ToArray()), + "f2e83101c3066c8a2983acdb92df53504ec00ac1e5afb71b7c3798cb4daf6162", + ]; + } + + [Theory] + [MemberData(nameof(SmallTestCasesMd5))] + public void Md5(string input, string _, string expected, int length) + { + var resultLower = Crypt.Md5(input, "x2", length); + var resultUpper = Crypt.Md5(input, "X2", length); + + resultLower.Should().Be(new string(expected.ToLower()[..length])); + + resultUpper.Should().Be(new string(expected.ToUpper()[..length])); + } + + [Theory] + [MemberData(nameof(SmallTestCasesSha256))] + public void Sha256(string input, string expected, string _, int length) + { + var resultLower = Crypt.Sha256(input, "x2", length); + var resultUpper = Crypt.Sha256(input, "X2", length); + + resultLower.Should().Be(new string(expected.ToLower()[..length])); + + resultUpper.Should().Be(new string(expected.ToUpper()[..length])); + } + + [Theory] + [MemberData(nameof(SmallTestCasesSha256Span))] + public void Sha256_Span( + string input, + string expected, + string _, + int length //Span version of the function must have multiple of 2 + ) + { + var resultLowerSpan = Crypt.Sha256(input.AsSpan(), "x2", length); + var resultUpperSpan = Crypt.Sha256(input.AsSpan(), "X2", length); + + resultLowerSpan.Should().Be(new string(expected.ToLower()[..length])); + + resultUpperSpan.Should().Be(new string(expected.ToUpper()[..length])); + } + + [Theory] + [MemberData(nameof(LargeTestCases))] + public void Sha256_LargeDataTests(string input, string expected) + { + var computedHash = Crypt.Sha256(input.AsSpan()); + computedHash.Should().Be(expected); } } diff --git a/tests/Speckle.Sdk.Tests.Unit/SQLite/SQLiteJsonCacheManagerTests.cs b/tests/Speckle.Sdk.Tests.Unit/SQLite/SQLiteJsonCacheManagerTests.cs new file mode 100644 index 00000000..49c2a549 --- /dev/null +++ b/tests/Speckle.Sdk.Tests.Unit/SQLite/SQLiteJsonCacheManagerTests.cs @@ -0,0 +1,83 @@ +using FluentAssertions; +using Speckle.Sdk.SQLite; +using Xunit; + +namespace Speckle.Sdk.Tests.Unit.SQLite; + +public class SQLiteJsonCacheManagerTests : IDisposable +{ + private readonly string _basePath = $"{Guid.NewGuid()}.db"; + + public void Dispose() + { + if (File.Exists(_basePath)) + { + //don't disable the pool because we should be disabling it in the manager + GC.Collect(); + GC.WaitForPendingFinalizers(); + File.Delete(_basePath); + } + } + + [Fact] + public void TestGetAll() + { + var data = new List<(string id, string json)>() { ("id1", "1"), ("id2", "2") }; + using var manager = new SqLiteJsonCacheManager(_basePath, 2); + manager.SaveObjects(data); + var items = manager.GetAllObjects(); + items.Count.Should().Be(data.Count); + var i = items.ToDictionary(); + foreach (var (id, json) in data) + { + i.TryGetValue(id, out var j).Should().BeTrue(); + j.Should().Be(json); + } + } + + [Fact] + public void TestGet() + { + var data = new List<(string id, string json)>() { ("id1", "1"), ("id2", "2") }; + using var manager = new SqLiteJsonCacheManager(_basePath, 2); + foreach (var d in data) + { + manager.SaveObject(d.id, d.json); + } + foreach (var d in data) + { + manager.SaveObject(d.id, d.json); + } + var items = manager.GetAllObjects(); + items.Count.Should().Be(data.Count); + + var id1 = data[0].id; + var json1 = manager.GetObject(id1); + json1.Should().Be(data[0].json); + manager.HasObject(id1).Should().BeTrue(); + + manager.UpdateObject(id1, "3"); + json1 = manager.GetObject(id1); + json1.Should().Be("3"); + manager.HasObject(id1).Should().BeTrue(); + + manager.DeleteObject(id1); + json1 = manager.GetObject(id1); + json1.Should().BeNull(); + manager.HasObject(id1).Should().BeFalse(); + + manager.UpdateObject(id1, "3"); + json1 = manager.GetObject(id1); + json1.Should().Be("3"); + manager.HasObject(id1).Should().BeTrue(); + + var id2 = data[1].id; + var json2 = manager.GetObject(id2); + json2.Should().Be(data[1].json); + manager.HasObject(id2).Should().BeTrue(); + manager.DeleteObject(id2); + json2 = manager.GetObject(id2); + json2.Should().BeNull(); + manager.HasObject(id2).Should().BeFalse(); + } +} diff --git a/tests/Speckle.Sdk.Tests.Unit/SQLite/SQLiteJsonExceptionTests.cs b/tests/Speckle.Sdk.Tests.Unit/SQLite/SQLiteJsonExceptionTests.cs new file mode 100644 index 00000000..91721a95 --- /dev/null +++ b/tests/Speckle.Sdk.Tests.Unit/SQLite/SQLiteJsonExceptionTests.cs @@ -0,0 +1,26 @@ +using Microsoft.Data.Sqlite; +using Speckle.Sdk.SQLite; +using Xunit; + +namespace Speckle.Sdk.Tests.Unit.SQLite; + +public class SqLiteJsonCacheExceptionTests +{ + [Fact] + public void ExpectedExceptionFires_Void() + { + using var pool = new CacheDbCommandPool("DataSource=:memory:", 1); + Assert.Throws( + () => pool.Use(CacheOperation.Get, new Action(_ => throw new SqliteException("test", 1, 1))) + ); + } + + [Fact] + public void ExpectedExceptionFires_Return() + { + using var pool = new CacheDbCommandPool("DataSource=:memory:", 1); + Assert.Throws( + () => pool.Use(CacheOperation.Get, new Func(_ => throw new SqliteException("test", 1, 1))) + ); + } +} diff --git a/tests/Speckle.Sdk.Tests.Unit/Serialisation/BatchTests.cs b/tests/Speckle.Sdk.Tests.Unit/Serialisation/BatchTests.cs new file mode 100644 index 00000000..de6f38a0 --- /dev/null +++ b/tests/Speckle.Sdk.Tests.Unit/Serialisation/BatchTests.cs @@ -0,0 +1,113 @@ +using FluentAssertions; +using Speckle.Sdk.Dependencies; +using Speckle.Sdk.Serialisation.V2.Send; +using Xunit; + +namespace Speckle.Sdk.Tests.Unit.Serialisation; + +public class BatchTests +{ + private class BatchItem(int size) : IHasByteSize + { + public int ByteSize { get; } = size; + } + + private static readonly Action EMPTY_LOGGER = _ => { }; + + [Fact] + public void TestBatchSize_Calc() + { + using var batch = new Batch(); + batch.Add(new BatchItem(1)); + batch.BatchByteSize.Should().Be(1); + batch.Add(new BatchItem(2)); + batch.BatchByteSize.Should().Be(3); + } + + [Fact] + public void Ensure_logging() + { + using var batch = new Batch(); + batch.AddBatchItem(new BatchItem(2)); + bool called = false; + batch.GetBatchSize(x => called = true, 1); + called.Should().BeTrue(); + } + + [Fact] + public void TestBatchSize_Trim() + { + using var batch = new Batch(); + batch.Add(new BatchItem(1)); + batch.Add(new BatchItem(2)); + batch.BatchByteSize.Should().Be(3); + + batch.Items.Capacity.Should().Be(Pools.DefaultCapacity); + batch.TrimExcess(); + + batch.Items.Capacity.Should().Be(2); + batch.BatchByteSize.Should().Be(3); + } + + [Fact] + public void Basics() + { + using var batch = BatchExtensions.CreateBatch(); + batch.AddBatchItem(new BatchItem(2)); + batch.BatchByteSize.Should().Be(2); + batch.AddBatchItem(new BatchItem(2)); + batch.BatchByteSize.Should().Be(4); + batch.AddBatchItem(new BatchItem(2)); + batch.BatchByteSize.Should().Be(6); + + batch.TrimExcess(); + batch.BatchByteSize.Should().Be(6); + batch.Items.Count.Should().Be(3); + + batch.AddBatchItem(new BatchItem(2)); + batch.BatchByteSize.Should().Be(8); + batch.Items.Count.Should().Be(4); + } + + [Fact] + public void Large_Message_Problem_1() + { + const int MAX_BATCH_SIZE = 5; + + using var batch = BatchExtensions.CreateBatch(); + batch.AddBatchItem(new BatchItem(2)); + bool full = batch.GetBatchSize(EMPTY_LOGGER, MAX_BATCH_SIZE) == MAX_BATCH_SIZE; + full.Should().BeFalse(); + batch.AddBatchItem(new BatchItem(2)); + full = batch.GetBatchSize(EMPTY_LOGGER, MAX_BATCH_SIZE) == MAX_BATCH_SIZE; + full.Should().BeFalse(); + batch.AddBatchItem(new BatchItem(2)); + full = batch.GetBatchSize(EMPTY_LOGGER, MAX_BATCH_SIZE) == MAX_BATCH_SIZE; + full.Should().BeTrue(); + } + + [Fact] + public void Large_Message_Problem_2() + { + const int MAX_BATCH_SIZE = 5; + + using var batch = BatchExtensions.CreateBatch(); + batch.AddBatchItem(new BatchItem(63)); + bool full = batch.GetBatchSize(EMPTY_LOGGER, MAX_BATCH_SIZE) == MAX_BATCH_SIZE; + full.Should().BeTrue(); + } + + [Fact] + public void Large_Message_Problem_3() + { + const int MAX_BATCH_SIZE = 5; + + using var batch = BatchExtensions.CreateBatch(); + batch.AddBatchItem(new BatchItem(2)); + bool full = batch.GetBatchSize(EMPTY_LOGGER, MAX_BATCH_SIZE) == MAX_BATCH_SIZE; + full.Should().BeFalse(); + batch.AddBatchItem(new BatchItem(63)); + full = batch.GetBatchSize(EMPTY_LOGGER, MAX_BATCH_SIZE) == MAX_BATCH_SIZE; + full.Should().BeTrue(); + } +} diff --git a/tests/Speckle.Sdk.Tests.Unit/Serialisation/ChunkingTests.cs b/tests/Speckle.Sdk.Tests.Unit/Serialisation/ChunkingTests.cs new file mode 100644 index 00000000..7eb55588 --- /dev/null +++ b/tests/Speckle.Sdk.Tests.Unit/Serialisation/ChunkingTests.cs @@ -0,0 +1,56 @@ +using FluentAssertions; +using Speckle.Newtonsoft.Json; +using Speckle.Sdk.Host; +using Speckle.Sdk.Models; +using Speckle.Sdk.Serialisation; +using Speckle.Sdk.Transports; +using Xunit; + +namespace Speckle.Sdk.Tests.Unit.Serialisation; + +public class ChunkingTests +{ + public static IEnumerable TestCases() + { + // Initialize type loader + TypeLoader.Reset(); + TypeLoader.Initialize(typeof(Base).Assembly, typeof(IgnoreTest).Assembly); + + // Return test data as a collection of objects for xUnit + yield return [CreateDynamicTestCase(10, 100), 10]; + yield return [CreateDynamicTestCase(0.5, 100), 1]; + yield return [CreateDynamicTestCase(20.5, 100), 21]; + + yield return [CreateDynamicTestCase(10, 1000), 10]; + yield return [CreateDynamicTestCase(0.5, 1000), 1]; + yield return [CreateDynamicTestCase(20.5, 1000), 21]; + } + + [Theory] + [MemberData(nameof(TestCases))] + public void ChunkSerializationTest(Base testCase, int expectedChunkCount) + { + // Arrange + var transport = new MemoryTransport(); + var sut = new SpeckleObjectSerializer([transport]); + + // Act + _ = sut.Serialize(testCase); + var serializedObjects = transport + .Objects.Values.Select(json => JsonConvert.DeserializeObject>(json)) + .ToList(); + + var numberOfChunks = serializedObjects.Count(x => + x!.TryGetValue("speckle_type", out var speckleType) && ((string)speckleType!) == "Speckle.Core.Models.DataChunk" + ); + + numberOfChunks.Should().Be(expectedChunkCount); + } + + private static Base CreateDynamicTestCase(double numberOfChunks, int chunkSize) + { + // Helper method to create the dynamic test case + var value = Enumerable.Range(0, (int)Math.Floor(chunkSize * numberOfChunks)).ToList(); + return new Base { [$"@({chunkSize})chunkedProperty"] = value }; + } +} diff --git a/tests/Speckle.Sdk.Tests.Unit/Serialisation/JsonIgnoreAttributeTests.cs b/tests/Speckle.Sdk.Tests.Unit/Serialisation/JsonIgnoreAttributeTests.cs index b934404f..2b03a901 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Serialisation/JsonIgnoreAttributeTests.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Serialisation/JsonIgnoreAttributeTests.cs @@ -1,35 +1,130 @@ -using NUnit.Framework; +using FluentAssertions; using Speckle.Newtonsoft.Json; +using Speckle.Sdk.Common; +using Speckle.Sdk.Host; using Speckle.Sdk.Models; using Speckle.Sdk.Serialisation; +using Speckle.Sdk.Transports; +using Xunit; namespace Speckle.Sdk.Tests.Unit.Serialisation; -[TestOf(typeof(BaseObjectSerializerV2))] +/// +/// Tests that the leads to properties being ignored both from the final JSON output, +/// But also from the id calculation +/// public sealed class JsonIgnoreRespected { - [Test] - public void IgnoredProperties_NotIncludedInJson() + public JsonIgnoreRespected() { - IgnoreTest testData = new(); + TypeLoader.Reset(); + TypeLoader.Initialize(typeof(Base).Assembly, typeof(IgnoreTest).Assembly); + } - BaseObjectSerializerV2 sut = new(); + public static IEnumerable IgnoredTestCases() + { + const string EXPECTED_PAYLOAD = "this should have been included"; + const string EXPECTED_HASH = "e1d9f0685266465c9bfe4e71f2eee6e9"; + yield return ["this should have been ignored", EXPECTED_PAYLOAD, EXPECTED_HASH]; + yield return ["again, ignored!", EXPECTED_PAYLOAD, EXPECTED_HASH]; + yield return ["this one is not", EXPECTED_PAYLOAD, EXPECTED_HASH]; + } - var res = sut.Serialize(testData); + public static IEnumerable IgnoredCompoundTestCases() + { + const string EXPECTED_PAYLOAD = "this should have been included"; + const string EXPECTED_HASH = "eeaeee4e61b04b313dd840cd63341eee"; + yield return ["this should have been ignored", EXPECTED_PAYLOAD, EXPECTED_HASH]; + yield return ["again, ignored!", EXPECTED_PAYLOAD, EXPECTED_HASH]; + yield return ["this one is not", EXPECTED_PAYLOAD, EXPECTED_HASH]; + } - Assert.That(res, Does.Contain(nameof(testData.ShouldBeIncluded))); - Assert.That(res, Does.Contain(testData.ShouldBeIncluded)); + [Theory] + [MemberData(nameof(IgnoredTestCases))] + public void IgnoredProperties_NotIncludedInJson(string ignoredPayload, string expectedPayload, string expectedHash) + { + IgnoreTest testData = new(ignoredPayload, expectedPayload); - Assert.That(res, Does.Not.Contain(nameof(testData.ShouldBeIgnored))); - Assert.That(res, Does.Not.Contain(testData.ShouldBeIgnored)); + SpeckleObjectSerializer sut = new(); + + var result = sut.SerializeBase(testData); + result.Should().NotBeNull(); + result!.Value.Id.Should().NotBeNull(); + + var jsonString = result.Value.Json.ToString(); + jsonString.Should().NotContain(nameof(testData.ShouldBeIgnored)); + jsonString.Should().NotContain(ignoredPayload); + + jsonString.Should().Contain(nameof(testData.ShouldBeIncluded)); + jsonString.Should().Contain(expectedPayload); + + result.Value.Id!.Value.Value.Should().Be(expectedHash); + } + + [Theory] + [MemberData(nameof(IgnoredCompoundTestCases))] + public void IgnoredProperties_Compound_NotIncludedInJson( + string ignoredPayload, + string expectedPayload, + string expectedHash + ) + { + IgnoredCompoundTest testData = new(ignoredPayload, expectedPayload); + + MemoryTransport savedObjects = new(); + SpeckleObjectSerializer sut = new(writeTransports: [savedObjects]); + + var result = sut.SerializeBase(testData); + var (json, id) = result.NotNull(); + json.Value.Should().NotBeNull(); + id.Should().NotBeNull(); + + savedObjects.SaveObject(id!.Value.Value.NotNull(), json.Value); + + foreach ((_, string childJson) in savedObjects.Objects) + { + childJson.Should().NotContain(nameof(testData.ShouldBeIgnored)); + childJson.Should().NotContain(ignoredPayload); + + childJson.Should().Contain(nameof(testData.ShouldBeIncluded)); + childJson.Should().Contain(expectedPayload); + } + + id.Value.Value.Should().Be(expectedHash); } } -[SpeckleType("Speckle.Sdk.Tests.Unit.Serialisation.IgnoreTest")] -public sealed class IgnoreTest : Base +[SpeckleType("Speckle.Sdk.Test.Unit.Serialisation.IgnoredCompoundTest")] +public sealed class IgnoredCompoundTest(string ignoredPayload, string expectedPayload) : Base { [JsonIgnore] - public string ShouldBeIgnored => "this should have been ignored"; + public Base ShouldBeIgnored => new IgnoreTest(ignoredPayload, expectedPayload) { ["override"] = ignoredPayload }; - public string ShouldBeIncluded => "this should have been included"; + public Base ShouldBeIncluded => new IgnoreTest(ignoredPayload, expectedPayload); + + [JsonIgnore, DetachProperty] + public Base ShouldBeIgnoredDetached => ShouldBeIgnored; + + [DetachProperty] + public Base ShouldBeIncludedDetached => ShouldBeIncluded; + + [JsonIgnore] + public List ShouldBeIgnoredList => [ShouldBeIgnored]; + + [JsonIgnore, DetachProperty] + public List ShouldBeIgnoredDetachedList => ShouldBeIgnoredList; + + public List ShouldBeIncludedList => [ShouldBeIncluded]; + + [DetachProperty] + public List ShouldBeIncludedDetachedList => ShouldBeIncludedList; +} + +[SpeckleType("Speckle.Sdk.Tests.Unit.Serialisation.IgnoreTest")] +public sealed class IgnoreTest(string shouldBeIgnoredPayload, string shouldBeIncludedPayload) : Base +{ + [JsonIgnore] + public string ShouldBeIgnored => shouldBeIgnoredPayload; + + public string ShouldBeIncluded => shouldBeIncludedPayload; } diff --git a/tests/Speckle.Sdk.Tests.Unit/Serialisation/ObjectModelDeprecationTests.cs b/tests/Speckle.Sdk.Tests.Unit/Serialisation/ObjectModelDeprecationTests.cs index 7c1437bc..eae0825c 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Serialisation/ObjectModelDeprecationTests.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Serialisation/ObjectModelDeprecationTests.cs @@ -1,41 +1,42 @@ -using NUnit.Framework; -using Shouldly; +using FluentAssertions; using Speckle.Sdk.Host; using Speckle.Sdk.Models; using Speckle.Sdk.Serialisation.Deprecated; +using Xunit; namespace Speckle.Sdk.Tests.Unit.Serialisation { - [TestFixture] - [TestOf(typeof(TypeLoader))] public class TypeLoaderTests { - [SetUp] - public void Setup() + // Constructor replaces the [SetUp] functionality in NUnit + public TypeLoaderTests() { TypeLoader.Reset(); TypeLoader.Initialize(typeof(Base).Assembly, typeof(MySpeckleBase).Assembly); } - [Test] + [Fact] // Replaces [Test] public void TestThatTypeWithoutAttributeFails() { - var e = Assert.Throws(() => TypeLoader.ParseType(typeof(string))); - e.ShouldNotBeNull(); + // Record.Exception is the xUnit alternative of Assert.Throws + var exception = Record.Exception(() => TypeLoader.ParseType(typeof(string))); + + exception.Should().NotBeNull(); // Shouldly assertion + exception.Should().BeOfType(); // Ensure it's the correct exception type } - [Test] + [Fact] // Replaces [Test] public void TestThatTypeWithoutMultipleAttributes() { string destinationType = $"Speckle.Core.Serialisation.{nameof(MySpeckleBase)}"; var result = TypeLoader.GetAtomicType(destinationType); - Assert.That(result, Is.EqualTo(typeof(MySpeckleBase))); + result.Should().Be(typeof(MySpeckleBase)); // Shouldly assertion replaces Assert.That destinationType = $"Speckle.Core.Serialisation.Deprecated.{nameof(MySpeckleBase)}"; result = TypeLoader.GetAtomicType(destinationType); - Assert.That(result, Is.EqualTo(typeof(MySpeckleBase))); + result.Should().Be(typeof(MySpeckleBase)); // Shouldly assertion replaces Assert.That } } } diff --git a/tests/Speckle.Sdk.Tests.Unit/Serialisation/PrimitiveTestFixture.cs b/tests/Speckle.Sdk.Tests.Unit/Serialisation/PrimitiveTestFixture.cs new file mode 100644 index 00000000..75f63184 --- /dev/null +++ b/tests/Speckle.Sdk.Tests.Unit/Serialisation/PrimitiveTestFixture.cs @@ -0,0 +1,48 @@ +namespace Speckle.Sdk.Tests.Unit.Serialisation; + +public abstract class PrimitiveTestFixture +{ + public static IEnumerable Int8TestCases => + new sbyte[] { 0, sbyte.MaxValue, sbyte.MinValue }.Select(x => new object[] { x }); + public static readonly short[] Int16TestCases = [short.MaxValue, short.MinValue]; + public static IEnumerable Int32TestCases => + new int[] { int.MinValue, int.MaxValue }.Select(x => new object[] { x }); + + public static IEnumerable Int64TestCases => + new long[] { long.MinValue, long.MaxValue }.Select(x => new object[] { x }); + + public static IEnumerable UInt64TestCases => + new ulong[] { ulong.MinValue, ulong.MaxValue }.Select(x => new object[] { x }); + + public static IEnumerable Float64TestCases => + new[] + { + 0, + double.Epsilon, + double.MaxValue, + double.MinValue, + double.PositiveInfinity, + double.NegativeInfinity, + double.NaN, + }.Select(x => new object[] { x }); + + public static IEnumerable Float32TestCases => + new[] + { + default, + float.Epsilon, + float.MaxValue, + float.MinValue, + float.PositiveInfinity, + float.NegativeInfinity, + float.NaN, + }.Select(x => new object[] { x }); + + public static Half[] Float16TestCases { get; } = + [default, Half.Epsilon, Half.MaxValue, Half.MinValue, Half.PositiveInfinity, Half.NegativeInfinity, Half.NaN]; + + public static float[] FloatIntegralTestCases { get; } = [0, 1, int.MaxValue, int.MinValue]; + + public static IEnumerable MyEnums { get; } = + Enum.GetValues(typeof(MyEnum)).Cast().Select(x => new[] { x }); +} diff --git a/tests/Speckle.Sdk.Tests.Unit/Serialisation/SerializerBreakingChanges.cs b/tests/Speckle.Sdk.Tests.Unit/Serialisation/SerializerBreakingChanges.cs index 934b2cd5..a2510abe 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Serialisation/SerializerBreakingChanges.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Serialisation/SerializerBreakingChanges.cs @@ -1,47 +1,66 @@ -using NUnit.Framework; +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Speckle.Sdk.Api; +using Speckle.Sdk.Host; +using Speckle.Sdk.Models; using Speckle.Sdk.Serialisation; +using Speckle.Sdk.Tests.Unit.Host; +using Xunit; namespace Speckle.Sdk.Tests.Unit.Serialisation; /// -/// Test fixture that documents what property typing changes break backwards/cross/forwards compatibility, and are "breaking" changes. +/// Test class that documents what property typing changes break backwards/cross/forwards compatibility, +/// and are "breaking" changes. /// This doesn't guarantee things work this way for SpecklePy /// Nor does it encompass other tricks (like deserialize callback, or computed json ignored properties) /// -[TestFixture] -[Description( - "For certain types, changing property from one type to another is a breaking change, and not backwards/forwards compatible" -)] public class SerializerBreakingChanges : PrimitiveTestFixture { - [Test] - public void StringToInt_ShouldThrow() + private readonly IOperations _operations; + + // xUnit does not support a Setup method; instead, you can use the constructor for initialization. + public SerializerBreakingChanges() + { + TypeLoader.Reset(); + TypeLoader.Initialize(typeof(Base).Assembly, typeof(Point).Assembly); + var serviceProvider = TestServiceSetup.GetServiceProvider(); + _operations = serviceProvider.GetRequiredService(); + } + + [Fact] + public async Task StringToInt_ShouldThrow() { var from = new StringValueMock { value = "testValue" }; - Assert.Throws(() => from.SerializeAsTAndDeserialize()); + await FluentActions + .Invoking(async () => await from.SerializeAsTAndDeserialize(_operations)) + .Should() + .ThrowAsync(); } - [Test, TestCaseSource(nameof(MyEnums))] - public void StringToEnum_ShouldThrow(MyEnum testCase) + [Theory] + [MemberData(nameof(MyEnums))] // Replaces [TestCaseSource(nameof(MyEnums))] + public async Task StringToEnum_ShouldThrow(MyEnum testCase) { var from = new StringValueMock { value = testCase.ToString() }; - Assert.Throws(() => - { - var res = from.SerializeAsTAndDeserialize(); - }); + await FluentActions + .Invoking(async () => await from.SerializeAsTAndDeserialize(_operations)) + .Should() + .ThrowAsync(); } - [ - Test, - Description("Deserialization of a JTokenType.Float to a .NET short/int/long should throw exception"), - TestCaseSource(nameof(Float64TestCases)), - TestCase(1e+30) - ] - public void DoubleToInt_ShouldThrow(double testCase) + [Theory(DisplayName = "Deserialization of a JTokenType.Float to a .NET short/int/long should throw exception")] + [MemberData(nameof(Float64TestCases))] + [InlineData(1e+30)] // Inline test case replaces [TestCase(1e+30)] + public async Task DoubleToInt_ShouldThrow(double testCase) { var from = new DoubleValueMock { value = testCase }; - Assert.Throws(() => from.SerializeAsTAndDeserialize()); + + await FluentActions + .Invoking(async () => await from.SerializeAsTAndDeserialize(_operations)) + .Should() + .ThrowAsync(); } } diff --git a/tests/Speckle.Sdk.Tests.Unit/Serialisation/SerializerNonBreakingChanges.cs b/tests/Speckle.Sdk.Tests.Unit/Serialisation/SerializerNonBreakingChanges.cs index a0247f45..42397f93 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Serialisation/SerializerNonBreakingChanges.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Serialisation/SerializerNonBreakingChanges.cs @@ -1,225 +1,214 @@ using System.Drawing; -using NUnit.Framework; -using Speckle.DoubleNumerics; +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; using Speckle.Sdk.Api; -using Speckle.Sdk.Helpers; using Speckle.Sdk.Host; using Speckle.Sdk.Models; +using Speckle.Sdk.Serialisation; +using Xunit; +using Matrix4x4 = Speckle.DoubleNumerics.Matrix4x4; namespace Speckle.Sdk.Tests.Unit.Serialisation; -/// -/// Test fixture that documents what property typing changes maintain backwards/cross/forwards compatibility, and are "non-breaking" changes. -/// This doesn't guarantee things work this way for SpecklePy -/// Nor does it encompass other tricks (like deserialize callback, or computed json ignored properties) -/// -[TestFixture] -[Description("For certain types, changing property from one type to another should be implicitly backwards compatible")] public class SerializerNonBreakingChanges : PrimitiveTestFixture { - [SetUp] - public void Setup() + private readonly IOperations _operations; + + public SerializerNonBreakingChanges() { TypeLoader.Reset(); TypeLoader.Initialize(typeof(StringValueMock).Assembly); + var serviceProvider = TestServiceSetup.GetServiceProvider(); + _operations = serviceProvider.GetRequiredService(); } - [Test, TestCaseSource(nameof(Int8TestCases)), TestCaseSource(nameof(Int32TestCases))] - public void IntToColor(int argb) + [Theory, MemberData(nameof(Int8TestCases)), MemberData(nameof(Int32TestCases))] + public async Task IntToColor(int argb) { var from = new IntValueMock { value = argb }; - var res = from.SerializeAsTAndDeserialize(); - Assert.That(res.value.ToArgb(), Is.EqualTo(argb)); + var res = await from.SerializeAsTAndDeserialize(_operations); + res.value.ToArgb().Should().Be(argb); } - [Test, TestCaseSource(nameof(Int8TestCases)), TestCaseSource(nameof(Int32TestCases))] - public void ColorToInt(int argb) + [Theory, MemberData(nameof(Int8TestCases)), MemberData(nameof(Int32TestCases))] + public async Task ColorToInt(int argb) { var from = new ColorValueMock { value = Color.FromArgb(argb) }; - var res = from.SerializeAsTAndDeserialize(); - Assert.That(res.value, Is.EqualTo(argb)); + var res = await from.SerializeAsTAndDeserialize(_operations); + res.value.Should().Be(argb); } - [ - Test, - TestCaseSource(nameof(Int8TestCases)), - TestCaseSource(nameof(Int32TestCases)), - TestCaseSource(nameof(Int64TestCases)) - ] - public void IntToDouble(long testCase) + [Theory, MemberData(nameof(Int8TestCases)), MemberData(nameof(Int32TestCases)), MemberData(nameof(Int64TestCases))] + public async Task IntToDouble(long testCase) { var from = new IntValueMock { value = testCase }; - var res = from.SerializeAsTAndDeserialize(); - Assert.That(res.value, Is.EqualTo(testCase)); + var res = await from.SerializeAsTAndDeserialize(_operations); + res.value.Should().Be(testCase); } - [Test] - public void NullToInt() + [Fact] + public async Task NullToInt() { var from = new ObjectValueMock { value = null }; - var res = from.SerializeAsTAndDeserialize(); - Assert.That(res.value, Is.EqualTo(default(int))); + var res = await from.SerializeAsTAndDeserialize(_operations); + res.value.Should().Be(default(int)); } - [Test] - public void NullToDouble() + [Fact] + public async Task NullToDouble() { var from = new ObjectValueMock { value = null }; - var res = from.SerializeAsTAndDeserialize(); - Assert.That(res.value, Is.EqualTo(default(double))); + var res = await from.SerializeAsTAndDeserialize(_operations); + res.value.Should().Be(0); } - [ - Test, - TestCaseSource(nameof(Int8TestCases)), - TestCaseSource(nameof(Int32TestCases)), - TestCaseSource(nameof(Int64TestCases)) - ] - public void IntToString(long testCase) + [Theory] + [MemberData(nameof(UInt64TestCases))] + public async Task UIntToDouble(ulong testCase) + { + var from = new UIntValueMock { value = testCase }; + + var res = await from.SerializeAsTAndDeserialize(_operations); + res.value.Should().BeApproximately(testCase, 2048); + } + + [Theory, MemberData(nameof(Int8TestCases)), MemberData(nameof(Int32TestCases)), MemberData(nameof(Int64TestCases))] + public async Task IntToString(long testCase) { var from = new IntValueMock { value = testCase }; - var res = from.SerializeAsTAndDeserialize(); - Assert.That(res.value, Is.EqualTo(testCase.ToString())); + var res = await from.SerializeAsTAndDeserialize(_operations); + res.value.Should().Be(testCase.ToString()); } - private static readonly double[][] s_arrayTestCases = - { - Array.Empty(), - new double[] { 0, 1, int.MaxValue, int.MinValue }, - new[] { default, double.Epsilon, double.MaxValue, double.MinValue } - }; + public static IEnumerable s_arrayTestCases => + new object[] + { + Array.Empty(), + new double[] { 0, 1, int.MaxValue, int.MinValue }, + new[] { default, double.Epsilon, double.MaxValue, double.MinValue }, + }.Select(x => new[] { x }); - [Test, TestCaseSource(nameof(s_arrayTestCases))] - public void ArrayToList(double[] testCase) + [Theory, MemberData(nameof(s_arrayTestCases))] + public async Task ArrayToList(double[] testCase) { var from = new ArrayDoubleValueMock { value = testCase }; - var res = from.SerializeAsTAndDeserialize(); - Assert.That(res.value, Is.EquivalentTo(testCase)); + var res = await from.SerializeAsTAndDeserialize(_operations); + res.value.Should().BeEquivalentTo(testCase); } - [Test, TestCaseSource(nameof(s_arrayTestCases))] - public void ListToArray(double[] testCase) + [Theory, MemberData(nameof(s_arrayTestCases))] + public async Task ListToArray(double[] testCase) { var from = new ListDoubleValueMock { value = testCase.ToList() }; - var res = from.SerializeAsTAndDeserialize(); - Assert.That(res.value, Is.EquivalentTo(testCase)); + var res = await from.SerializeAsTAndDeserialize(_operations); + res.value.Should().BeEquivalentTo(testCase); } - [Test, TestCaseSource(nameof(s_arrayTestCases))] - public void ListToIList(double[] testCase) + [Theory, MemberData(nameof(s_arrayTestCases))] + public async Task ListToIList(double[] testCase) { var from = new ListDoubleValueMock { value = testCase.ToList() }; - var res = from.SerializeAsTAndDeserialize(); - Assert.That(res.value, Is.EquivalentTo(testCase)); + var res = await from.SerializeAsTAndDeserialize(_operations); + res.value.Should().BeEquivalentTo(testCase); } - [Test, TestCaseSource(nameof(s_arrayTestCases))] - public void ListToIReadOnlyList(double[] testCase) + [Theory, MemberData(nameof(s_arrayTestCases))] + public async Task ListToIReadOnlyList(double[] testCase) { var from = new ListDoubleValueMock { value = testCase.ToList() }; - var res = from.SerializeAsTAndDeserialize(); - Assert.That(res.value, Is.EquivalentTo(testCase)); + var res = await from.SerializeAsTAndDeserialize(_operations); + res.value.Should().BeEquivalentTo(testCase); } - [Test, TestCaseSource(nameof(s_arrayTestCases))] - public void IListToList(double[] testCase) + [Theory, MemberData(nameof(s_arrayTestCases))] + public async Task IListToList(double[] testCase) { var from = new IListDoubleValueMock { value = testCase.ToList() }; - var res = from.SerializeAsTAndDeserialize(); - Assert.That(res.value, Is.EquivalentTo(testCase)); + var res = await from.SerializeAsTAndDeserialize(_operations); + res.value.Should().BeEquivalentTo(testCase); } - [Test, TestCaseSource(nameof(s_arrayTestCases))] - public void IReadOnlyListToList(double[] testCase) + [Theory, MemberData(nameof(s_arrayTestCases))] + public async Task IReadOnlyListToList(double[] testCase) { var from = new IReadOnlyListDoubleValueMock { value = testCase.ToList() }; - var res = from.SerializeAsTAndDeserialize(); - Assert.That(res.value, Is.EquivalentTo(testCase)); + var res = await from.SerializeAsTAndDeserialize(_operations); + res.value.Should().BeEquivalentTo(testCase); } - [Test, TestCaseSource(nameof(MyEnums))] - public void EnumToInt(MyEnum testCase) + [Theory, MemberData(nameof(MyEnums))] + public async Task EnumToInt(MyEnum testCase) { var from = new EnumValueMock { value = testCase }; - var res = from.SerializeAsTAndDeserialize(); - Assert.That(res.value, Is.EqualTo((int)testCase)); + var res = await from.SerializeAsTAndDeserialize(_operations); + res.value.Should().Be((int)testCase); } - [Test, TestCaseSource(nameof(MyEnums))] - public void IntToEnum(MyEnum testCase) + [Theory, MemberData(nameof(MyEnums))] + public async Task IntToEnum(MyEnum testCase) { var from = new IntValueMock { value = (int)testCase }; - var res = from.SerializeAsTAndDeserialize(); - Assert.That(res.value, Is.EqualTo(testCase)); + var res = await from.SerializeAsTAndDeserialize(_operations); + res.value.Should().Be(testCase); } - [Test] - [TestCaseSource(nameof(Float64TestCases))] - [TestCaseSource(nameof(Float32TestCases))] - public void DoubleToDouble(double testCase) + [Theory, MemberData(nameof(Float32TestCases)), MemberData(nameof(Float64TestCases))] + public async Task DoubleToDouble(double testCase) { var from = new DoubleValueMock { value = testCase }; - var res = from.SerializeAsTAndDeserialize(); - Assert.That(res.value, Is.EqualTo(testCase)); + var res = await from.SerializeAsTAndDeserialize(_operations); + res.value.Should().Be(testCase); } - [Test] - [TestCase(123, 255)] - [TestCase(256, 1)] - [TestCase(256, float.MinValue)] - public void ListToMatrix64(int seed, double scalar) + [Theory] + [InlineData(123, 255)] + [InlineData(256, 1)] + [InlineData(256, float.MinValue)] + public async Task ListToMatrix64(int seed, double scalar) { Random rand = new(seed); List testCase = Enumerable.Range(0, 16).Select(_ => rand.NextDouble() * scalar).ToList(); - ListDoubleValueMock from = new() { value = testCase, }; + ListDoubleValueMock from = new() { value = testCase }; - //Test List -> Matrix - var res = from.SerializeAsTAndDeserialize(); - Assert.That(res.value.M11, Is.EqualTo(testCase[0])); - Assert.That(res.value.M44, Is.EqualTo(testCase[testCase.Count - 1])); + var res = await from.SerializeAsTAndDeserialize(_operations); + res.value.M11.Should().Be(testCase[0]); + res.value.M44.Should().Be(testCase[^1]); - //Test Matrix -> List - var backAgain = res.SerializeAsTAndDeserialize(); - Assert.That(backAgain.value, Is.Not.Null); - Assert.That(backAgain.value, Is.EquivalentTo(testCase)); + var backAgain = await res.SerializeAsTAndDeserialize(_operations); + backAgain.value.Should().NotBeNull(); + backAgain.value.Should().BeEquivalentTo(testCase); } - [Test] - [TestCase(123, 255)] - [TestCase(256, 1)] - [DefaultFloatingPointTolerance(Constants.EPS)] - public void Matrix32ToMatrix64(int seed, float scalar) + [Theory] + [InlineData(123, 255)] + [InlineData(256, 1)] + public async Task Matrix32ToMatrix64(int seed, float scalar) { Random rand = new(seed); List testCase = Enumerable.Range(0, 16).Select(_ => rand.NextDouble() * scalar).ToList(); - ListDoubleValueMock from = new() { value = testCase, }; + ListDoubleValueMock from = new() { value = testCase }; - //Test List -> Matrix - var res = from.SerializeAsTAndDeserialize(); - Assert.That(res.value.M11, Is.EqualTo(testCase[0])); - Assert.That(res.value.M44, Is.EqualTo(testCase[testCase.Count - 1])); - - //Test Matrix -> List - var backAgain = res.SerializeAsTAndDeserialize(); - Assert.That(backAgain.value, Is.Not.Null); - Assert.That(backAgain.value, Is.EquivalentTo(testCase)); + await FluentActions + .Invoking(async () => await from.SerializeAsTAndDeserialize(_operations)) + .Should() + .ThrowAsync(); } } @@ -259,6 +248,12 @@ public class IntValueMock : SerializerMock public long value { get; set; } } +[SpeckleType("Speckle.Core.Tests.Unit.Serialisation.IntValueMock")] +public class UIntValueMock : SerializerMock +{ + public ulong value { get; set; } +} + [SpeckleType("Speckle.Core.Tests.Unit.Serialisation.StringValueMock")] public class StringValueMock : SerializerMock { @@ -309,7 +304,7 @@ public enum MyEnum Three, Neg = -1, Min = int.MinValue, - Max = int.MaxValue + Max = int.MaxValue, } public abstract class SerializerMock : Base @@ -330,53 +325,16 @@ public abstract class SerializerMock : Base _speckle_type = target.speckle_type; } - internal TTo SerializeAsTAndDeserialize() + internal async Task SerializeAsTAndDeserialize(IOperations operations) where TTo : Base, new() { SerializeAs(); - var json = Operations.Serialize(this); + var json = operations.Serialize(this); - Base result = Operations.Deserialize(json); - Assert.That(result, Is.Not.Null); - Assert.That(result, Is.TypeOf()); + Base result = await operations.DeserializeAsync(json); + result.Should().NotBeNull(); + result.Should().BeOfType(); return (TTo)result; } } - -public abstract class PrimitiveTestFixture -{ - public static readonly sbyte[] Int8TestCases = { default, sbyte.MaxValue, sbyte.MinValue }; - public static readonly short[] Int16TestCases = { short.MaxValue, short.MinValue }; - public static readonly int[] Int32TestCases = { int.MinValue, int.MaxValue }; - public static readonly long[] Int64TestCases = { long.MaxValue, long.MinValue }; - - public static double[] Float64TestCases { get; } = - { - default, - double.Epsilon, - double.MaxValue, - double.MinValue, - double.PositiveInfinity, - double.NegativeInfinity, - double.NaN - }; - - public static float[] Float32TestCases { get; } = - { - default, - float.Epsilon, - float.MaxValue, - float.MinValue, - float.PositiveInfinity, - float.NegativeInfinity, - float.NaN - }; - - public static Half[] Float16TestCases { get; } = - { default, Half.Epsilon, Half.MaxValue, Half.MinValue, Half.PositiveInfinity, Half.NegativeInfinity, Half.NaN }; - - public static float[] FloatIntegralTestCases { get; } = { 0, 1, int.MaxValue, int.MinValue }; - - public static MyEnum[] MyEnums { get; } = Enum.GetValues(typeof(MyEnum)).Cast().ToArray(); -} diff --git a/tests/Speckle.Sdk.Tests.Unit/Serialisation/SimpleRoundTripTests.cs b/tests/Speckle.Sdk.Tests.Unit/Serialisation/SimpleRoundTripTests.cs index faf2082f..dc6e8cb2 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Serialisation/SimpleRoundTripTests.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Serialisation/SimpleRoundTripTests.cs @@ -1,25 +1,29 @@ using System.Reflection; -using NUnit.Framework; +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Speckle.Sdk.Api; using Speckle.Sdk.Host; using Speckle.Sdk.Models; using Speckle.Sdk.Tests.Unit.Host; +using Xunit; namespace Speckle.Sdk.Tests.Unit.Serialisation; public class SimpleRoundTripTests { - static SimpleRoundTripTests() - { - Reset(); - } + private readonly IOperations _operations; - private static void Reset() + public SimpleRoundTripTests() { TypeLoader.Reset(); TypeLoader.Initialize(typeof(Base).Assembly, Assembly.GetExecutingAssembly()); + var serviceProvider = TestServiceSetup.GetServiceProvider(); + _operations = serviceProvider.GetRequiredService(); } - public static IEnumerable TestData() + public static IEnumerable TestData() => TestDataInternal().Select(x => new object[] { x }); + + public static IEnumerable TestDataInternal() { yield return new DiningTable { ["@strangeVariable_NAme3"] = new TableLegFixture() }; @@ -31,15 +35,13 @@ public class SimpleRoundTripTests yield return polyline; } - [SetUp] - public void Setup() => Reset(); - - [TestCaseSource(nameof(TestData))] - public void SimpleSerialization(Base testData) + [Theory] + [MemberData(nameof(TestData))] + public async Task SimpleSerialization(Base testData) { - var result = Sdk.Api.Operations.Serialize(testData); - var test = Sdk.Api.Operations.Deserialize(result); + var result = _operations.Serialize(testData); + var test = await _operations.DeserializeAsync(result); - Assert.That(testData.GetId(), Is.EqualTo(test.GetId())); + testData.GetId().Should().Be(test.GetId()); } } diff --git a/tests/Speckle.Sdk.Tests.Unit/Speckle.Sdk.Tests.Unit.csproj b/tests/Speckle.Sdk.Tests.Unit/Speckle.Sdk.Tests.Unit.csproj index 39d114b2..5449b7dc 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Speckle.Sdk.Tests.Unit.csproj +++ b/tests/Speckle.Sdk.Tests.Unit/Speckle.Sdk.Tests.Unit.csproj @@ -8,11 +8,12 @@ + - - - + + + diff --git a/tests/Speckle.Sdk.Tests.Unit/TestServiceSetup.cs b/tests/Speckle.Sdk.Tests.Unit/TestServiceSetup.cs new file mode 100644 index 00000000..28493be0 --- /dev/null +++ b/tests/Speckle.Sdk.Tests.Unit/TestServiceSetup.cs @@ -0,0 +1,14 @@ +using Microsoft.Extensions.DependencyInjection; +using Speckle.Sdk.Host; + +namespace Speckle.Sdk.Tests.Unit; + +public static class TestServiceSetup +{ + public static IServiceProvider GetServiceProvider() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSpeckleSdk(HostApplications.Navisworks, HostAppVersion.v2023, "Test"); + return serviceCollection.BuildServiceProvider(); + } +} diff --git a/tests/Speckle.Sdk.Tests.Unit/Transports/DiskTransportTests.cs b/tests/Speckle.Sdk.Tests.Unit/Transports/DiskTransportTests.cs index 36aaf999..d903a902 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Transports/DiskTransportTests.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Transports/DiskTransportTests.cs @@ -1,37 +1,40 @@ -using NUnit.Framework; +using FluentAssertions; using Speckle.Sdk.Common; using Speckle.Sdk.Transports; +using Xunit; namespace Speckle.Sdk.Tests.Unit.Transports; -[TestFixture] -[TestOf(nameof(DiskTransport))] -public sealed class DiskTransportTests : TransportTests +public sealed class DiskTransportTests : TransportTests, IDisposable { + private readonly DiskTransport _diskTransport; + private readonly string _basePath = $"./temp_{Guid.NewGuid()}"; + private const string ApplicationName = "Speckle Integration Tests"; + private readonly string _fullPath; + protected override ITransport Sut => _diskTransport.NotNull(); - private DiskTransport _diskTransport; - - private static readonly string s_basePath = $"./temp {Guid.NewGuid()}"; - private const string APPLICATION_NAME = "Speckle Integration Tests"; - private static readonly string s_fullPath = Path.Combine(s_basePath, APPLICATION_NAME); - - [SetUp] - public void Setup() + public DiskTransportTests() { - _diskTransport = new DiskTransport(s_fullPath); + _fullPath = Path.Combine(_basePath, ApplicationName); + _diskTransport = new DiskTransport(_fullPath); } - [TearDown] - public void TearDown() - { - Directory.Delete(s_basePath, true); - } - - [Test] + [Fact] public void DirectoryCreated_AfterInitialization() { - bool fileExists = Directory.Exists(s_fullPath); - Assert.That(fileExists, Is.True); + // Act + var directoryExists = Directory.Exists(_fullPath); + + // Assert + directoryExists.Should().BeTrue(); + } + + public void Dispose() + { + if (Directory.Exists(_basePath)) + { + Directory.Delete(_basePath, true); + } } } diff --git a/tests/Speckle.Sdk.Tests.Unit/Transports/MemoryTransportTests.cs b/tests/Speckle.Sdk.Tests.Unit/Transports/MemoryTransportTests.cs index 55182760..e457c1a7 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Transports/MemoryTransportTests.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Transports/MemoryTransportTests.cs @@ -1,21 +1,23 @@ -using System.Collections.Concurrent; -using NUnit.Framework; +// MemoryTransportTests.cs + +using FluentAssertions; using Speckle.Sdk.Common; using Speckle.Sdk.Transports; +using Xunit; namespace Speckle.Sdk.Tests.Unit.Transports; -[TestFixture] -[TestOf(nameof(MemoryTransport))] public sealed class MemoryTransportTests : TransportTests { protected override ITransport Sut => _memoryTransport.NotNull(); + private readonly MemoryTransport _memoryTransport; - private MemoryTransport _memoryTransport; - - [SetUp] - public void Setup() + // Constructor used for setup in xUnit + public MemoryTransportTests() { - _memoryTransport = new MemoryTransport(new ConcurrentDictionary()); + _memoryTransport = new MemoryTransport(); } + + [Fact] + public void TransportName_ShouldSetProperly() => _memoryTransport.TransportName.Should().Be("Memory"); } diff --git a/tests/Speckle.Sdk.Tests.Unit/Transports/SQLiteTransport2Tests.cs b/tests/Speckle.Sdk.Tests.Unit/Transports/SQLiteTransport2Tests.cs new file mode 100644 index 00000000..e4d20cf4 --- /dev/null +++ b/tests/Speckle.Sdk.Tests.Unit/Transports/SQLiteTransport2Tests.cs @@ -0,0 +1,149 @@ +using FluentAssertions; +using Microsoft.Data.Sqlite; +using Speckle.Sdk.Common; +using Speckle.Sdk.Serialisation.Utilities; +using Speckle.Sdk.Transports; +using Xunit; + +namespace Speckle.Sdk.Tests.Unit.Transports; + +public sealed class SQLiteTransport2Tests : TransportTests, IDisposable +{ + protected override ITransport? Sut => _sqlite; + + private SQLiteTransport2? _sqlite; + + private static readonly string s_name = $"test-{Guid.NewGuid()}"; + private static readonly string s_basePath = SqlitePaths.GetDBPath(s_name); + + public SQLiteTransport2Tests() + { + _sqlite = new SQLiteTransport2(s_name); + } + + public void Dispose() + { + _sqlite?.Dispose(); + SqliteConnection.ClearAllPools(); + if (File.Exists(s_basePath)) + { + File.Delete(s_basePath); + } + + _sqlite = null; + } + + [Fact] + public void DbCreated_AfterInitialization() + { + bool fileExists = File.Exists(s_basePath); + fileExists.Should().BeTrue(); + } + + [Fact(DisplayName = "Tests that an object can be updated")] + public async Task UpdateObject_AfterAdd() + { + const string PAYLOAD_ID = "MyTestObjectId"; + const string PAYLOAD_DATA = "MyTestObjectData"; + + _sqlite.NotNull().SaveObject(PAYLOAD_ID, PAYLOAD_DATA); + await _sqlite.WriteComplete(); + + const string NEW_PAYLOAD = "MyEvenBetterObjectData"; + _sqlite.UpdateObject(PAYLOAD_ID, NEW_PAYLOAD); + await _sqlite.WriteComplete(); + + var result = await _sqlite.GetObject(PAYLOAD_ID); + result.Should().Be(NEW_PAYLOAD); + } + + [Fact(DisplayName = "Tests that updating an object that hasn't been saved previously adds the object to the DB")] + public async Task UpdateObject_WhenMissing() + { + const string PAYLOAD_ID = "MyTestObjectId"; + const string PAYLOAD_DATA = "MyTestObjectData"; + + var preUpdate = await _sqlite.NotNull().GetObject(PAYLOAD_ID); + preUpdate.Should().BeNull(); + + _sqlite.UpdateObject(PAYLOAD_ID, PAYLOAD_DATA); + await _sqlite.WriteComplete(); + + var postUpdate = await _sqlite.GetObject(PAYLOAD_ID); + postUpdate.Should().Be(PAYLOAD_DATA); + } + + [Fact] + public async Task SaveAndRetrieveObject_Sync() + { + const string PAYLOAD_ID = "MyTestObjectId"; + const string PAYLOAD_DATA = "MyTestObjectData"; + + var preAdd = await Sut.NotNull().GetObject(PAYLOAD_ID); + preAdd.Should().BeNull(); + + _sqlite.NotNull().SaveObjectSync(PAYLOAD_ID, PAYLOAD_DATA); + + { + var postAdd = await Sut.GetObject(PAYLOAD_ID); + postAdd.Should().Be(PAYLOAD_DATA); + } + } + + [Fact(DisplayName = "Tests enumerating through all objects while updating them without infinite loop")] + public void UpdateObject_WhileEnumerating() + { + const string UPDATE_STRING = "_new"; + Dictionary testData = new() + { + { "a", "This is object a" }, + { "b", "This is object b" }, + { "c", "This is object c" }, + { "d", "This is object d" }, + }; + int length = testData.Values.First().Length; + + foreach (var (key, data) in testData) + { + _sqlite.NotNull().SaveObjectSync(key, data); + } + + foreach (var o in _sqlite.NotNull().GetAllObjects()) + { + string newData = o + UPDATE_STRING; + string key = $"{o[length - 1]}"; + + _sqlite.UpdateObject(key, newData); + } + + // Assert that objects were updated + _sqlite.GetAllObjects().ToList().Should().AllSatisfy(o => o.Should().Contain(UPDATE_STRING)); + // Assert that objects were only updated once + _sqlite.GetAllObjects().ToList().Should().AllSatisfy(o => o.Should().HaveLength(length + UPDATE_STRING.Length)); + } + + [Theory(DisplayName = "Tests that GetAllObjects can be called concurrently from multiple threads")] + [InlineData(6, 32)] + public void GetAllObjects_IsThreadSafe(int dataSize, int parallelism) + { + foreach (int i in Enumerable.Range(0, dataSize)) + { + _sqlite.NotNull().SaveObjectSync(i.ToString(), Guid.NewGuid().ToString()); + } + + List[] results = new List[parallelism]; + Parallel.ForEach( + Enumerable.Range(0, parallelism), + i => + { + results[i] = _sqlite.NotNull().GetAllObjects().ToList(); + } + ); + + foreach (var result in results) + { + result.Should().BeEquivalentTo(results[0]); + result.Count.Should().Be(dataSize); + } + } +} diff --git a/tests/Speckle.Sdk.Tests.Unit/Transports/SQLiteTransportTests.cs b/tests/Speckle.Sdk.Tests.Unit/Transports/SQLiteTransportTests.cs index 2ce88c8d..1a385466 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Transports/SQLiteTransportTests.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Transports/SQLiteTransportTests.cs @@ -1,119 +1,109 @@ +using FluentAssertions; using Microsoft.Data.Sqlite; -using NUnit.Framework; -using Speckle.Sdk.Common; using Speckle.Sdk.Transports; +using Xunit; namespace Speckle.Sdk.Tests.Unit.Transports; -[TestFixture] -[TestOf(nameof(SQLiteTransport))] public sealed class SQLiteTransportTests : TransportTests, IDisposable { protected override ITransport? Sut => _sqlite; - private SQLiteTransport? _sqlite; + private readonly SQLiteTransport _sqlite; private static readonly string s_basePath = $"./temp {Guid.NewGuid()}"; private const string APPLICATION_NAME = "Speckle Integration Tests"; - [SetUp] - public void Setup() + // Constructor replaces [SetUp] + public SQLiteTransportTests() { _sqlite = new SQLiteTransport(s_basePath, APPLICATION_NAME); } - [TearDown] - public void TearDown() + // Disposal replaces [TearDown] for cleanup + public void Dispose() { _sqlite?.Dispose(); SqliteConnection.ClearAllPools(); Directory.Delete(s_basePath, true); - _sqlite = null; } - [Test] + [Fact] public void DbCreated_AfterInitialization() { bool fileExists = File.Exists($"{s_basePath}/{APPLICATION_NAME}/Data.db"); - Assert.That(fileExists, Is.True); + fileExists.Should().BeTrue(); } - [Test] - [Description("Tests that an object can be updated")] + [Fact] public async Task UpdateObject_AfterAdd() { const string PAYLOAD_ID = "MyTestObjectId"; const string PAYLOAD_DATA = "MyTestObjectData"; - _sqlite.NotNull().SaveObject(PAYLOAD_ID, PAYLOAD_DATA); + _sqlite!.SaveObject(PAYLOAD_ID, PAYLOAD_DATA); await _sqlite.WriteComplete(); const string NEW_PAYLOAD = "MyEvenBetterObjectData"; _sqlite.UpdateObject(PAYLOAD_ID, NEW_PAYLOAD); await _sqlite.WriteComplete(); - var result = _sqlite.GetObject(PAYLOAD_ID); - Assert.That(result, Is.EqualTo(NEW_PAYLOAD)); + var result = await _sqlite.GetObject(PAYLOAD_ID); + result.Should().Be(NEW_PAYLOAD); } - [Test] - [Description("Tests that updating an object that hasn't been saved previously adds the object to the DB")] + [Fact] public async Task UpdateObject_WhenMissing() { const string PAYLOAD_ID = "MyTestObjectId"; const string PAYLOAD_DATA = "MyTestObjectData"; - var preUpdate = _sqlite.NotNull().GetObject(PAYLOAD_ID); - Assert.That(preUpdate, Is.Null); + var preUpdate = await _sqlite!.GetObject(PAYLOAD_ID); + preUpdate.Should().BeNull(); _sqlite.UpdateObject(PAYLOAD_ID, PAYLOAD_DATA); await _sqlite.WriteComplete(); - var postUpdate = _sqlite.GetObject(PAYLOAD_ID); - Assert.That(postUpdate, Is.EqualTo(PAYLOAD_DATA)); + var postUpdate = await _sqlite.GetObject(PAYLOAD_ID); + postUpdate.Should().Be(PAYLOAD_DATA); } - [Test] - public void SaveAndRetrieveObject_Sync() + [Fact] + public async Task SaveAndRetrieveObject_Sync() { const string PAYLOAD_ID = "MyTestObjectId"; const string PAYLOAD_DATA = "MyTestObjectData"; - var preAdd = Sut.NotNull().GetObject(PAYLOAD_ID); - Assert.That(preAdd, Is.Null); + var preAdd = await Sut!.GetObject(PAYLOAD_ID); + preAdd.Should().BeNull(); - _sqlite.NotNull().SaveObjectSync(PAYLOAD_ID, PAYLOAD_DATA); + _sqlite!.SaveObjectSync(PAYLOAD_ID, PAYLOAD_DATA); { - var postAdd = Sut.GetObject(PAYLOAD_ID); - Assert.That(postAdd, Is.EqualTo(PAYLOAD_DATA)); + var postAdd = await Sut.GetObject(PAYLOAD_ID); + postAdd.Should().Be(PAYLOAD_DATA); } } - [Test( - Description = "Tests that it is possible to enumerate through all objects of the transport while updating them, without getting stuck in an infinite loop" - )] - [Timeout(1000)] + [Fact] // No xUnit [Timeout], so this is purely indicative public void UpdateObject_WhileEnumerating() { - //I question if this is the behaviour we want, but AccountManager.GetObjects is relying on being able to update objects while enumerating over them const string UPDATE_STRING = "_new"; - Dictionary testData = - new() - { - { "a", "This is object a" }, - { "b", "This is object b" }, - { "c", "This is object c" }, - { "d", "This is object d" } - }; + Dictionary testData = new() + { + { "a", "This is object a" }, + { "b", "This is object b" }, + { "c", "This is object c" }, + { "d", "This is object d" }, + }; int length = testData.Values.First().Length; foreach (var (key, data) in testData) { - _sqlite.NotNull().SaveObjectSync(key, data); + _sqlite!.SaveObjectSync(key, data); } - foreach (var o in _sqlite.NotNull().GetAllObjects()) + foreach (var o in _sqlite.GetAllObjects()) { string newData = o + UPDATE_STRING; string key = $"{o[length - 1]}"; @@ -121,23 +111,19 @@ public sealed class SQLiteTransportTests : TransportTests, IDisposable _sqlite.UpdateObject(key, newData); } - //Assert that objects were updated - Assert.That(_sqlite.GetAllObjects().ToList(), Has.All.Contains(UPDATE_STRING)); - //Assert that objects were only updated once - Assert.That(_sqlite.GetAllObjects().ToList(), Has.All.Length.EqualTo(length + UPDATE_STRING.Length)); + // Assert that objects were updated + _sqlite.GetAllObjects().ToList().Should().AllSatisfy(o => o.Should().Contain(UPDATE_STRING)); + // Assert that objects were only updated once + _sqlite.GetAllObjects().ToList().Should().AllSatisfy(o => o.Should().HaveLength(length + UPDATE_STRING.Length)); } - [Test] - [Repeat(10)] - [TestCase(6, 32)] - [Description( - $"Tests that the {nameof(SQLiteTransport.GetAllObjects)} function can be called concurrently from multiple threads" - )] + [Theory] + [InlineData(6, 32)] public void GetAllObjects_IsThreadSafe(int dataSize, int parallelism) { foreach (int i in Enumerable.Range(0, dataSize)) { - _sqlite.NotNull().SaveObjectSync(i.ToString(), Guid.NewGuid().ToString()); + _sqlite!.SaveObjectSync(i.ToString(), Guid.NewGuid().ToString()); } List[] results = new List[parallelism]; @@ -145,19 +131,14 @@ public sealed class SQLiteTransportTests : TransportTests, IDisposable Enumerable.Range(0, parallelism), i => { - results[i] = _sqlite.NotNull().GetAllObjects().ToList(); + results[i] = _sqlite.GetAllObjects().ToList(); } ); foreach (var result in results) { - Assert.That(result, Is.EquivalentTo(results[0])); - Assert.That(result, Has.Count.EqualTo(dataSize)); + result.Should().BeEquivalentTo(results[0]); + result.Count.Should().Be(dataSize); } } - - public void Dispose() - { - _sqlite?.Dispose(); - } } diff --git a/tests/Speckle.Sdk.Tests.Unit/Transports/TransportTests.cs b/tests/Speckle.Sdk.Tests.Unit/Transports/TransportTests.cs index 1574bc9d..2aa32fdf 100644 --- a/tests/Speckle.Sdk.Tests.Unit/Transports/TransportTests.cs +++ b/tests/Speckle.Sdk.Tests.Unit/Transports/TransportTests.cs @@ -1,62 +1,60 @@ -using NUnit.Framework; +using FluentAssertions; using Speckle.Newtonsoft.Json; using Speckle.Sdk.Common; using Speckle.Sdk.Transports; +using Xunit; namespace Speckle.Sdk.Tests.Unit.Transports; -[TestFixture] public abstract class TransportTests { protected abstract ITransport? Sut { get; } - [Test] + [Fact] public async Task SaveAndRetrieveObject() { const string PAYLOAD_ID = "MyTestObjectId"; const string PAYLOAD_DATA = "MyTestObjectData"; { - var preAdd = Sut.NotNull().GetObject(PAYLOAD_ID); - Assert.That(preAdd, Is.Null); + var preAdd = await Sut.NotNull().GetObject(PAYLOAD_ID); + preAdd.Should().BeNull(); } Sut.SaveObject(PAYLOAD_ID, PAYLOAD_DATA); await Sut.WriteComplete(); { - var postAdd = Sut.GetObject(PAYLOAD_ID); - Assert.That(postAdd, Is.EqualTo(PAYLOAD_DATA)); + var postAdd = await Sut.GetObject(PAYLOAD_ID); + postAdd.Should().Be(PAYLOAD_DATA); } } - [Test] + [Fact] public async Task HasObject() { const string PAYLOAD_ID = "MyTestObjectId"; const string PAYLOAD_DATA = "MyTestObjectData"; { - var preAdd = await Sut.NotNull().HasObjects(new[] { PAYLOAD_ID }); - Assert.That(preAdd, Has.Exactly(1).Items); - Assert.That(preAdd, Has.No.ContainValue(true)); - Assert.That(preAdd, Contains.Key(PAYLOAD_ID)); + var preAdd = await Sut.NotNull().HasObjects([PAYLOAD_ID]); + preAdd.Count.Should().Be(1); + preAdd.Values.Should().NotContain(true); + preAdd.Keys.Should().Contain(PAYLOAD_ID); } Sut.SaveObject(PAYLOAD_ID, PAYLOAD_DATA); await Sut.WriteComplete(); { - var postAdd = await Sut.HasObjects(new[] { PAYLOAD_ID }); - - Assert.That(postAdd, Has.Exactly(1).Items); - Assert.That(postAdd, Has.No.ContainValue(false)); - Assert.That(postAdd, Contains.Key(PAYLOAD_ID)); + var postAdd = await Sut.HasObjects([PAYLOAD_ID]); + postAdd.Count.Should().Be(1); + postAdd.Values.Should().NotContain(false); + postAdd.Keys.Should().Contain(PAYLOAD_ID); } } - [Test] - [Description("Test that transports save objects when many threads are concurrently saving data")] + [Fact] public async Task SaveObject_ConcurrentWrites() { const int TEST_DATA_COUNT = 100; @@ -75,71 +73,54 @@ public abstract class TransportTests await Sut.NotNull().WriteComplete(); - //Test 1. SavedObjectCount //WARN: FAIL!!! seems this is not implemented for SQLite Transport - //Assert.That(transport.SavedObjectCount, Is.EqualTo(testDataCount)); - - //Test 2. HasObjects + //Test: HasObjects var ids = testData.Select(x => x.id).ToList(); var hasObjectsResult = await Sut.HasObjects(ids); - Assert.That(hasObjectsResult, Does.Not.ContainValue(false)); - Assert.That(hasObjectsResult.Keys, Is.EquivalentTo(ids)); + hasObjectsResult.Values.Should().NotContain(false); + hasObjectsResult.Keys.Should().BeEquivalentTo(ids); - //Test 3. GetObjects + //Test: GetObjects foreach (var x in testData) { - var res = Sut.GetObject(x.id); - Assert.That(res, Is.EqualTo(x.data)); + var res = await Sut.GetObject(x.id); + res.Should().Be(x.data); } } - [Test] - public async Task ProgressAction_Called_OnSaveObject() - { - bool wasCalled = false; - Sut.NotNull().OnProgressAction = (_) => wasCalled = true; - - Sut.SaveObject("12345", "fake payload data"); - - await Sut.WriteComplete(); - - Assert.That(wasCalled, Is.True); - } - - [Test] + [Fact] public void ToString_IsNotEmpty() { var toString = Sut.NotNull().ToString(); - - Assert.That(toString, Is.Not.Null); - Assert.That(toString, Is.Not.Empty); + toString.Should().NotBeNullOrEmpty(); } - [Test] + [Fact] public void TransportName_IsNotEmpty() { var toString = Sut.NotNull().TransportName; - - Assert.That(toString, Is.Not.Null); - Assert.That(toString, Is.Not.Empty); + toString.Should().NotBeNullOrEmpty(); } - [Test] - public void SaveObject_ExceptionThrown_TaskIsCanceled() + [Fact] + public async Task SaveObject_ExceptionThrown_TaskIsCanceled() { using CancellationTokenSource tokenSource = new(); Sut.NotNull().CancellationToken = tokenSource.Token; - tokenSource.Cancel(); + await tokenSource.CancelAsync(); - Assert.CatchAsync(async () => - { - Sut.SaveObject("abcdef", "fake payload data"); - await Sut.WriteComplete(); - }); + await FluentActions + .Invoking(async () => + { + Sut.SaveObject("abcdef", "fake payload data"); + await Sut.WriteComplete(); + }) + .Should() + .ThrowAsync(); } - [Test] + [Fact] public async Task CopyObjectAndChildren() { //Assemble @@ -168,8 +149,8 @@ public abstract class TransportTests //Assert foreach (var (expectedId, expectedData) in testData) { - var actual = destination.GetObject(expectedId); - Assert.That(actual, Is.EqualTo(expectedData)); + var actual = await destination.GetObject(expectedId); + actual.Should().Be(expectedData); } } } diff --git a/tests/Speckle.Sdk.Tests.Unit/UnitTestProgress.cs b/tests/Speckle.Sdk.Tests.Unit/UnitTestProgress.cs new file mode 100644 index 00000000..ad74c0ec --- /dev/null +++ b/tests/Speckle.Sdk.Tests.Unit/UnitTestProgress.cs @@ -0,0 +1,6 @@ +namespace Speckle.Sdk.Tests.Unit; + +public class UnitTestProgress(Action handler) : IProgress +{ + public void Report(T value) => handler(value); +} diff --git a/tests/Speckle.Sdk.Tests.Unit/packages.lock.json b/tests/Speckle.Sdk.Tests.Unit/packages.lock.json index 2dd713eb..07972a13 100644 --- a/tests/Speckle.Sdk.Tests.Unit/packages.lock.json +++ b/tests/Speckle.Sdk.Tests.Unit/packages.lock.json @@ -4,24 +4,33 @@ "net8.0": { "altcover": { "type": "Direct", - "requested": "[8.8.74, )", - "resolved": "8.8.74", - "contentHash": "e8RZNE0vZnuBk/gOAWu9K5wm3S7dOrOlZje3PHI9PJUHqvP1cxVJD1eXAAmddFVlixowB7C7/zvC16GnunC2LQ==" + "requested": "[9.0.1, )", + "resolved": "9.0.1", + "contentHash": "aadciFNDT5bnylaYUkKal+s5hF7yU/lmZxImQWAlk1438iPqK1Uf79H5ylELpyLIU49HL5ql+tnWBihp3WVLCA==" }, - "GitVersion.MsBuild": { + "AwesomeAssertions": { "type": "Direct", - "requested": "[5.12.0, )", - "resolved": "5.12.0", - "contentHash": "dJuigXycpJNOiLT9or7mkHSkGFHgGW3/p6cNNYEKZBa7Hhp1FdX/cvqYWWYhRLpfoZOedeA7aRbYiOB3vW/dvA==" + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "6fWiV7mGZUzZXzeiW3hWF0nJokuuNm4hnzuqbM3IXHqGYkWnHl65+wNpuQ73xfJXClX0fmfKcTdQ2Ula719IDg==" + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Direct", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0" + } }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.10.0, )", - "resolved": "17.10.0", - "contentHash": "0/2HeACkaHEYU3wc83YlcD2Fi4LMtECJjqrtvw0lPi9DCEa35zSPt1j4fuvM8NagjDqJuh1Ja35WcRtn1Um6/A==", + "requested": "[17.13.0, )", + "resolved": "17.13.0", + "contentHash": "W19wCPizaIC9Zh47w8wWI/yxuqR7/dtABwOrc8r2jX/8mUNxM2vw4fXDh+DJTeogxV+KzKwg5jNNGQVwf3LXyA==", "dependencies": { - "Microsoft.CodeCoverage": "17.10.0", - "Microsoft.TestPlatform.TestHost": "17.10.0" + "Microsoft.CodeCoverage": "17.13.0", + "Microsoft.TestPlatform.TestHost": "17.13.0" } }, "Microsoft.SourceLink.GitHub": { @@ -34,39 +43,17 @@ "Microsoft.SourceLink.Common": "8.0.0" } }, - "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" - } + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g==" }, "Speckle.DoubleNumerics": { "type": "Direct", - "requested": "[4.0.1, )", - "resolved": "4.0.1", - "contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w==" + "requested": "[4.1.0, )", + "resolved": "4.1.0", + "contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A==" }, "Speckle.InterfaceGenerator": { "type": "Direct", @@ -74,19 +61,22 @@ "resolved": "0.9.6", "contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w==" }, - "DiffEngine": { - "type": "Transitive", - "resolved": "11.3.0", - "contentHash": "k0ZgZqd09jLZQjR8FyQbSQE86Q7QZnjEzq1LPHtj1R2AoWO8sjV5x+jlSisL7NZAbUOI4y+7Bog8gkr9WIRBGw==", + "xunit": { + "type": "Direct", + "requested": "[2.9.3, )", + "resolved": "2.9.3", + "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==", "dependencies": { - "EmptyFiles": "4.4.0", - "System.Management": "6.0.1" + "xunit.analyzers": "1.18.0", + "xunit.assert": "2.9.3", + "xunit.core": "[2.9.3]" } }, - "EmptyFiles": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "gwJEfIGS7FhykvtZoscwXj/XwW+mJY6UbAZk+qtLKFUGWC95kfKXnj8VkxsZQnWBxJemM/q664rGLN5nf+OHZw==" + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[3.0.2, )", + "resolved": "3.0.2", + "contentHash": "oXbusR6iPq0xlqoikjdLvzh+wQDkMv9If58myz9MEzldS4nIcp442Btgs2sWbYWV+caEluMe2pQCZ0hUZgPiow==" }, "GraphQL.Client.Abstractions": { "type": "Transitive", @@ -116,17 +106,65 @@ }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "17.10.0", - "contentHash": "yC7oSlnR54XO5kOuHlVOKtxomNNN1BWXX8lK1G2jaPXT9sUok7kCOoA4Pgs0qyFaCtMrNsprztYMeoEGqCm4uA==" + "resolved": "17.13.0", + "contentHash": "9LIUy0y+DvUmEPtbRDw6Bay3rzwqFV8P4efTrK4CZhQle3M/QwLPjISghfcolmEGAPWxuJi6m98ZEfk4VR4Lfg==" }, "Microsoft.Data.Sqlite.Core": { "type": "Transitive", - "resolved": "7.0.7", - "contentHash": "21FRzcJhaTrlv7kTrqr/ltFcSQM2TyuTTPhUcjO8H73od7Bb3QraNW90c7lUucNI/245XPkKZG4fp7/7OsKCSg==", + "resolved": "7.0.5", + "contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==", "dependencies": { "SQLitePCLRaw.core": "2.1.4" } }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==" + }, + "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.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", @@ -134,18 +172,18 @@ }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.10.0", - "contentHash": "KkwhjQevuDj0aBRoPLY6OLAhGqbPUEBuKLbaCs0kUVw29qiOYncdORd4mLVJbn9vGZ7/iFGQ/+AoJl0Tu5Umdg==", + "resolved": "17.13.0", + "contentHash": "bt0E0Dx+iqW97o4A59RCmUmz/5NarJ7LRL+jXbSHod72ibL5XdNm1Ke+UO5tFhBG4VwHLcSjqq9BUSblGNWamw==", "dependencies": { "System.Reflection.Metadata": "1.6.0" } }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.10.0", - "contentHash": "LWpMdfqhHvcUkeMCvNYJO8QlPLlYz9XPPb+ZbaXIKhdmjAV0wqTSrTiW5FLaf7RRZT50AQADDOYMOe0HxDxNgA==", + "resolved": "17.13.0", + "contentHash": "9GGw08Dc3AXspjekdyTdZ/wYWFlxbgcF0s7BKxzVX+hzAwpifDOdxM+ceVaaJSQOwqt3jtuNlHn3XTpKUS9x9Q==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.10.0", + "Microsoft.TestPlatform.ObjectModel": "17.13.0", "Newtonsoft.Json": "13.0.1" } }, @@ -184,18 +222,10 @@ "SQLitePCLRaw.core": "2.1.4" } }, - "System.CodeDom": { + "System.ComponentModel.Annotations": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA==" - }, - "System.Management": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "10J1D0h/lioojphfJ4Fuh5ZUThT/xOVHdV9roGBittKKNP2PMjrvibEdbVTGZcPra1399Ja3tqIJLyQrc5Wmhg==", - "dependencies": { - "System.CodeDom": "6.0.0" - } + "resolved": "4.5.0", + "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" }, "System.Memory": { "type": "Transitive", @@ -212,22 +242,60 @@ "resolved": "1.6.0", "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw==" + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "1.18.0", + "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ==" + }, + "xunit.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]", + "xunit.extensibility.execution": "[2.9.3]" + } + }, + "xunit.extensibility.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==", + "dependencies": { + "xunit.abstractions": "2.0.3" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]" + } + }, "speckle.sdk": { "type": "Project", "dependencies": { "GraphQL.Client": "[6.0.0, )", "Microsoft.CSharp": "[4.7.0, )", - "Microsoft.Data.Sqlite": "[7.0.7, )", - "Polly": "[7.2.3, )", - "Polly.Contrib.WaitAndRetry": "[1.1.1, )", - "Polly.Extensions.Http": "[3.0.0, )", - "Speckle.DoubleNumerics": "[4.0.1, )", + "Microsoft.Data.Sqlite": "[7.0.5, )", + "Microsoft.Extensions.DependencyInjection.Abstractions": "[2.2.0, )", + "Microsoft.Extensions.Logging": "[2.2.0, )", + "Speckle.DoubleNumerics": "[4.1.0, )", "Speckle.Newtonsoft.Json": "[13.0.2, )", - "Speckle.Sdk.Logging": "[1.0.0, )", - "System.Text.Json": "[5.0.2, )" + "Speckle.Sdk.Dependencies": "[1.0.0, )" } }, - "speckle.sdk.logging": { + "speckle.sdk.dependencies": { "type": "Project" }, "GraphQL.Client": { @@ -249,33 +317,30 @@ }, "Microsoft.Data.Sqlite": { "type": "CentralTransitive", - "requested": "[7.0.7, )", - "resolved": "7.0.7", - "contentHash": "tiNmV1oPy+Z2R7Wd0bPB/FxCr8B+/5q11OpDMG751GA/YuOL7MZrBFfzv5oFRlFe08K6sjrnbrauzzGIeNrzLQ==", + "requested": "[7.0.5, )", + "resolved": "7.0.5", + "contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==", "dependencies": { - "Microsoft.Data.Sqlite.Core": "7.0.7", + "Microsoft.Data.Sqlite.Core": "7.0.5", "SQLitePCLRaw.bundle_e_sqlite3": "2.1.4" } }, - "Polly": { + "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "CentralTransitive", - "requested": "[7.2.3, )", - "resolved": "7.2.3", - "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" }, - "Polly.Contrib.WaitAndRetry": { + "Microsoft.Extensions.Logging": { "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==", + "requested": "[2.2.0, )", + "resolved": "2.2.0", + "contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==", "dependencies": { - "Polly": "7.1.0" + "Microsoft.Extensions.Configuration.Binder": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0" } }, "Speckle.Newtonsoft.Json": { @@ -284,11 +349,11 @@ "resolved": "13.0.2", "contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA==" }, - "System.Text.Json": { + "xunit.assert": { "type": "CentralTransitive", - "requested": "[5.0.2, )", - "resolved": "5.0.2", - "contentHash": "I47dVIGiV6SfAyppphxqupertT/5oZkYLDCX6vC3HpOI4ZLjyoKAreUoem2ie6G0RbRuFrlqz/PcTQjfb2DOfQ==" + "requested": "[2.9.3, )", + "resolved": "2.9.3", + "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA==" } } }