Compare commits
120 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9794195e9c | |||
| a61c442930 | |||
| 68a407905d | |||
| 0f2abaf532 | |||
| 07634b6f6a | |||
| e938725d35 | |||
| d3369e3ce5 | |||
| d75a61d775 | |||
| 2ae4003afb | |||
| 24db4c4ae4 | |||
| edf63d4a1b | |||
| b5b0922e7f | |||
| ff390f772d | |||
| d69f0bba2a | |||
| 33c14fc14c | |||
| 536e58aacc | |||
| 88188aace6 | |||
| ad44a7cdbc | |||
| 38449dca9a | |||
| 764eb43838 | |||
| a84e6d89ca | |||
| 377829adae | |||
| a479440b66 | |||
| cc9639b179 | |||
| d44b4fa52b | |||
| ea6ca8c555 | |||
| 113f0fd551 | |||
| bcc4e25970 | |||
| b733ce5f29 | |||
| 1c8b2b82d7 | |||
| 11cd2dc1cb | |||
| 3789898ea2 | |||
| 0b3318f9e1 | |||
| 7589ad5f05 | |||
| 6df32262bd | |||
| 001ca1c287 | |||
| 59e459559b | |||
| d305fe59cb | |||
| f163b2822e | |||
| 630bb38b8b | |||
| bacff2da34 | |||
| 129d5285ed | |||
| 5d07fe0ea0 | |||
| 062e3c2838 | |||
| f4cc4bc77e | |||
| d395f03219 | |||
| 56ed399d70 | |||
| 7e0766fc7f | |||
| 0df343ebe1 | |||
| 3cad57ceb3 | |||
| 11937ad7c9 | |||
| 8210fde69a | |||
| fa6f90621e | |||
| f6f1852664 | |||
| b3f4190614 | |||
| 2fc0024cd2 | |||
| 300a5627fd | |||
| 22f029fe33 | |||
| c728266c88 | |||
| 7f2d57cdad | |||
| 81a22bd4cc | |||
| f64da099ab | |||
| f4ba200640 | |||
| 4e84766be9 | |||
| 73a95aded0 | |||
| 404600a839 | |||
| 93d517eab7 | |||
| 4110d90107 | |||
| 2d134bf7e1 | |||
| 686d0fd31c | |||
| b06878bbe1 | |||
| 64114167a8 | |||
| 0c7cd75353 | |||
| 3c5679ad2a | |||
| b043623021 | |||
| bd0997913a | |||
| ac0ab3904c | |||
| 5c9d672a2b | |||
| a79f0fd035 | |||
| 18d6653282 | |||
| d58a656c8a | |||
| 720d049145 | |||
| 129688a646 | |||
| fa66258b23 | |||
| 9f0c572993 | |||
| 158baff5b0 | |||
| 7a29d27e46 | |||
| 82e3d37dd1 | |||
| 9695ec8c51 | |||
| 15fa319433 | |||
| 793bbb9cd3 | |||
| 3291010d43 | |||
| 14732ce174 | |||
| ba655988b0 | |||
| 96822c4e66 | |||
| 08356de1ad | |||
| 375f5071ae | |||
| 5900a3c178 | |||
| d7bf324029 | |||
| c784fbf462 | |||
| 26f1802787 | |||
| a96e0f8c8e | |||
| 9f36c9cfe5 | |||
| 4959f277e8 | |||
| ea08b83f7a | |||
| b24dc685fa | |||
| 7cad14fe25 | |||
| 4d552b6834 | |||
| 378a91995e | |||
| 53b66dd26b | |||
| 9dd04c0881 | |||
| 474c18c29f | |||
| f20064c9f0 | |||
| 6f1b22d13a | |||
| ef19bfa69d | |||
| 45601f1a2f | |||
| adf3298baf | |||
| 8f5e5f675b | |||
| 9217e67a9d | |||
| ebccf6ff79 |
@@ -3,14 +3,14 @@
|
|||||||
"isRoot": true,
|
"isRoot": true,
|
||||||
"tools": {
|
"tools": {
|
||||||
"csharpier": {
|
"csharpier": {
|
||||||
"version": "0.30.6",
|
"version": "1.0.1",
|
||||||
"commands": [
|
"commands": [
|
||||||
"dotnet-csharpier"
|
"csharpier"
|
||||||
],
|
],
|
||||||
"rollForward": false
|
"rollForward": false
|
||||||
},
|
},
|
||||||
"gitversion.tool": {
|
"gitversion.tool": {
|
||||||
"version": "5.12.0",
|
"version": "6.1.0",
|
||||||
"commands": [
|
"commands": [
|
||||||
"dotnet-gitversion"
|
"dotnet-gitversion"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
Directory.Build.targets
|
||||||
|
Directory.Build.props
|
||||||
|
|
||||||
|
**/bin/*
|
||||||
|
**/obj/*
|
||||||
|
_ReSharper.SharpCompress/
|
||||||
|
bin/
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
TestArchives/Scratch/
|
||||||
|
TestArchives/Scratch2/
|
||||||
|
TestResults/
|
||||||
|
*.nupkg
|
||||||
|
packages/*/
|
||||||
|
project.lock.json
|
||||||
|
tests/TestArchives/Scratch
|
||||||
|
.vs
|
||||||
|
tools
|
||||||
|
.vscode
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
*.snupkg
|
||||||
|
coverage.xml
|
||||||
|
|
||||||
|
*.received.*
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
printWidth: 120
|
printWidth: 120
|
||||||
useTabs: false
|
useTabs: false
|
||||||
tabWidth: 2
|
indentSize: 2
|
||||||
preprocessorSymbolSets:
|
preprocessorSymbolSets:
|
||||||
- ""
|
- ""
|
||||||
- "DEBUG"
|
- "DEBUG"
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ name: .NET Build and Publish
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ["main", "dev"]
|
branches: ["main", "dev"]
|
||||||
|
tags: ["3.*"]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|||||||
+7
-20
@@ -1,5 +1,4 @@
|
|||||||
<Project>
|
<Project>
|
||||||
|
|
||||||
<PropertyGroup Label="Compiler Properties">
|
<PropertyGroup Label="Compiler Properties">
|
||||||
<LangVersion>12</LangVersion>
|
<LangVersion>12</LangVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
@@ -7,7 +6,6 @@
|
|||||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Label="Nugetspec Package Properties">
|
<PropertyGroup Label="Nugetspec Package Properties">
|
||||||
<!-- Defines common Nugetspec properties -->
|
<!-- Defines common Nugetspec properties -->
|
||||||
<!-- Inheriting packable projects should define the rest of the nugetspec properties (PackageId, Description) -->
|
<!-- Inheriting packable projects should define the rest of the nugetspec properties (PackageId, Description) -->
|
||||||
@@ -22,18 +20,16 @@
|
|||||||
<PackageTags>speckle</PackageTags>
|
<PackageTags>speckle</PackageTags>
|
||||||
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Label="Nuget Package Properties">
|
<PropertyGroup Label="Nuget Package Properties">
|
||||||
<IsPackable>false</IsPackable> <!--Can be set to true in inheriting .props/.csproj files for projects that should be packed-->
|
<IsPackable>false</IsPackable>
|
||||||
|
<!--Can be set to true in inheriting .props/.csproj files for projects that should be packed-->
|
||||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
||||||
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
|
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(GITHUB_ACTIONS)' == 'true'">
|
<PropertyGroup Condition="'$(GITHUB_ACTIONS)' == 'true'">
|
||||||
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
|
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Label="Analyers">
|
<PropertyGroup Label="Analyers">
|
||||||
<EnableNetAnalyzers>true</EnableNetAnalyzers>
|
<EnableNetAnalyzers>true</EnableNetAnalyzers>
|
||||||
<AnalysisLevel>latest-AllEnabledByDefault</AnalysisLevel>
|
<AnalysisLevel>latest-AllEnabledByDefault</AnalysisLevel>
|
||||||
@@ -41,7 +37,6 @@
|
|||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
|
||||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
|
|
||||||
<!-- Ingored warnings, some aspirational but too noisy for now, some by design. -->
|
<!-- Ingored warnings, some aspirational but too noisy for now, some by design. -->
|
||||||
<NoWarn>
|
<NoWarn>
|
||||||
<!--Disabled by design-->
|
<!--Disabled by design-->
|
||||||
@@ -59,28 +54,20 @@
|
|||||||
<!-- Aspirational -->
|
<!-- Aspirational -->
|
||||||
CA1502;CA1716;NETSDK1206;
|
CA1502;CA1716;NETSDK1206;
|
||||||
$(NoWarn)
|
$(NoWarn)
|
||||||
</NoWarn>
|
</NoWarn
|
||||||
|
>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<!-- Expose the repository root to all projects -->
|
<!-- Expose the repository root to all projects -->
|
||||||
<RepositoryRoot>$(MSBuildThisFileDirectory)</RepositoryRoot>
|
<RepositoryRoot>$(MSBuildThisFileDirectory)</RepositoryRoot>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="..\..\README.md" Pack="true" PackagePath="\"/>
|
<None Include="..\..\README.md" Pack="true" PackagePath="\" />
|
||||||
<None
|
<None Condition="'$(IsPackable)' == 'true'" Include="..\..\logo.png" Pack="true" PackagePath="\" Visible="false" />
|
||||||
Condition="'$(IsPackable)' == 'true'"
|
|
||||||
Include="..\..\logo.png"
|
|
||||||
Pack="true"
|
|
||||||
PackagePath="\"
|
|
||||||
Visible="false"/>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<!-- This file contains the configuration for some analyzer warnings, such as cyclomatic
|
<!-- This file contains the configuration for some analyzer warnings, such as cyclomatic
|
||||||
complexity threshold -->
|
complexity threshold -->
|
||||||
<AdditionalFiles Include="$(RepositoryRoot)CodeMetricsConfig.txt"/>
|
<AdditionalFiles Include="$(RepositoryRoot)CodeMetricsConfig.txt" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
+13
-14
@@ -1,18 +1,17 @@
|
|||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup Condition="'$(IsTestProject)' == 'true'">
|
<PropertyGroup Condition="'$(IsTestProject)' == 'true'">
|
||||||
<NoWarn>
|
<NoWarn>
|
||||||
$(NoWarn);
|
<!-- Things we need to test -->
|
||||||
<!-- Things we need to test -->
|
CS0618;CA1034;CA2201;CA1051;CA1040;CA1724;
|
||||||
CS0618;CA1034;CA2201;CA1051;CA1040;CA1724;
|
IDE0044;IDE0130;CA1508;
|
||||||
IDE0044;IDE0130;CA1508;
|
<!-- Analysers that provide no tangeable value to a test project -->
|
||||||
<!-- Analysers that provide no tangeable value to a test project -->
|
CA5394;CA2007;CA1852;CA1819;CA1711;CA1063;CA1816;CA2234;CS8618;CA1054;CA1810;CA2208;CA1019;CA1831;
|
||||||
CA5394;CA2007;CA1852;CA1819;CA1711;CA1063;CA1816;CA2234;CS8618;CA1054;CA1810;CA2208;CA1019;CA1831;
|
$(NoWarn);
|
||||||
</NoWarn>
|
</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<Target Name="DeepClean">
|
<Target Name="DeepClean">
|
||||||
<Message Text="Deep clean of $(MSBuildProjectName).csproj" Importance="high"/>
|
<Message Text="Deep clean of $(MSBuildProjectName).csproj" Importance="high" />
|
||||||
<RemoveDir Directories="$(BaseIntermediateOutputPath)"/>
|
<RemoveDir Directories="$(BaseIntermediateOutputPath)" />
|
||||||
<RemoveDir Directories="$(BaseOutputPath)"/>
|
<RemoveDir Directories="$(BaseOutputPath)" />
|
||||||
</Target>
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,38 +1,39 @@
|
|||||||
<Project>
|
<Project>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageVersion Include="altcover" Version="9.0.1" />
|
<PackageVersion Include="altcover" Version="9.0.1" />
|
||||||
<PackageVersion Include="AwesomeAssertions" Version="8.0.0" />
|
<PackageVersion Include="AwesomeAssertions" Version="8.1.0" />
|
||||||
<PackageVersion Include="BenchmarkDotNet" Version="0.14.0" />
|
<PackageVersion Include="BenchmarkDotNet" Version="0.14.0" />
|
||||||
<PackageVersion Include="Bullseye" Version="5.0.0" />
|
<PackageVersion Include="Bullseye" Version="6.0.0" />
|
||||||
<PackageVersion Include="GraphQL.Client" Version="6.0.0" />
|
<PackageVersion Include="GraphQL.Client" Version="6.0.0" />
|
||||||
<PackageVersion Include="Glob" Version="1.1.9" />
|
<PackageVersion Include="Glob" Version="1.1.9" />
|
||||||
|
<PackageVersion Include="HttpMultipartParser" Version="9.0.0" />
|
||||||
<PackageVersion Include="ILRepack.FullAuto" Version="1.6.0" />
|
<PackageVersion Include="ILRepack.FullAuto" Version="1.6.0" />
|
||||||
<PackageVersion Include="Microsoft.CSharp" Version="4.7.0" />
|
<PackageVersion Include="Microsoft.CSharp" Version="4.7.0" />
|
||||||
<!-- Keep at exactly 7.0.5 for side by side with V2 -->
|
<!-- Keep at exactly 7.0.5 for side by side with V2 -->
|
||||||
<PackageVersion Include="Microsoft.Data.Sqlite" Version="[7.0.5,)" />
|
<PackageVersion Include="Microsoft.Data.Sqlite" Version="[7.0.5,)" />
|
||||||
<PackageVersion Include="Microsoft.Extensions.ObjectPool" Version="9.0.1" />
|
<PackageVersion Include="Microsoft.Extensions.ObjectPool" Version="9.0.4" />
|
||||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
|
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
|
||||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="[2.2.0,)" />
|
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="[2.2.0,)" />
|
||||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="[2.2.0,)" />
|
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="[2.2.0,)" />
|
||||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="[2.2.0,)" />
|
<PackageVersion Include="Microsoft.Extensions.Logging" Version="[2.2.0,)" />
|
||||||
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="[5.0.0,)" />
|
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="[5.0.0,)" />
|
||||||
<PackageVersion Include="Moq" Version="4.20.70" />
|
<PackageVersion Include="Moq" Version="4.20.72" />
|
||||||
<PackageVersion Include="Open.ChannelExtensions" Version="9.0.0" />
|
<PackageVersion Include="Open.ChannelExtensions" Version="9.1.0" />
|
||||||
<PackageVersion Include="Polly" Version="7.2.3" />
|
<PackageVersion Include="Polly" Version="7.2.3" />
|
||||||
<PackageVersion Include="Polly.Contrib.WaitAndRetry" Version="1.1.1" />
|
<PackageVersion Include="Polly.Contrib.WaitAndRetry" Version="1.1.1" />
|
||||||
<PackageVersion Include="Polly.Extensions.Http" Version="3.0.0" />
|
<PackageVersion Include="Polly.Extensions.Http" Version="3.0.0" />
|
||||||
|
<PackageVersion Include="RichardSzalay.MockHttp" Version="7.0.0" />
|
||||||
<PackageVersion Include="Speckle.Newtonsoft.Json" Version="13.0.2" />
|
<PackageVersion Include="Speckle.Newtonsoft.Json" Version="13.0.2" />
|
||||||
<PackageVersion Include="Speckle.DoubleNumerics" Version="4.1.0" />
|
<PackageVersion Include="Speckle.DoubleNumerics" Version="4.1.0" />
|
||||||
<PackageVersion Include="SimpleExec" Version="12.0.0" />
|
<PackageVersion Include="SimpleExec" Version="12.0.0" />
|
||||||
<PackageVersion Include="System.Threading.Channels" Version="9.0.2" />
|
<PackageVersion Include="System.Threading.Channels" Version="9.0.4" />
|
||||||
<PackageVersion Include="Verify.Quibble" Version="2.1.1" />
|
<PackageVersion Include="Verify.Quibble" Version="2.1.1" />
|
||||||
<PackageVersion Include="Verify.Xunit" Version="28.10.1" />
|
<PackageVersion Include="Verify.Xunit" Version="29.4.0" />
|
||||||
<PackageVersion Include="xunit" Version="2.9.3" />
|
<PackageVersion Include="xunit" Version="2.9.3" />
|
||||||
<PackageVersion Include="xunit.assert" Version="2.9.3" />
|
<PackageVersion Include="xunit.assert" Version="2.9.3" />
|
||||||
<PackageVersion Include="xunit.runner.visualstudio" Version="3.0.2" />
|
<PackageVersion Include="xunit.runner.visualstudio" Version="3.0.2" />
|
||||||
<GlobalPackageReference Include="PolySharp" Version="1.15.0" />
|
<GlobalPackageReference Include="PolySharp" Version="1.15.0" />
|
||||||
<GlobalPackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
|
<GlobalPackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
|
||||||
<GlobalPackageReference Include="GitVersion.MsBuild" Version="5.12.0" />
|
|
||||||
<GlobalPackageReference Include="Speckle.InterfaceGenerator" Version="0.9.6" />
|
<GlobalPackageReference Include="Speckle.InterfaceGenerator" Version="0.9.6" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
+3
-8
@@ -1,11 +1,6 @@
|
|||||||
|
workflow: GitFlow/v1
|
||||||
next-version: 3.0.0
|
next-version: 3.0.0
|
||||||
mode: ContinuousDeployment
|
|
||||||
assembly-informational-format: "{Major}.{Minor}.{Patch}-{PreReleaseTag}"
|
|
||||||
branches:
|
branches:
|
||||||
main:
|
main:
|
||||||
regex: ^main$
|
prevent-increment:
|
||||||
tag: rc
|
when-current-commit-tagged: true
|
||||||
develop:
|
|
||||||
tag: dev
|
|
||||||
pull-request:
|
|
||||||
tag: pr
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ Speckle | Sharp | SDK
|
|||||||
|
|
||||||
### .NET SDK, Tests, and Objects
|
### .NET SDK, Tests, and Objects
|
||||||
|
|
||||||
[](https://codecov.io/gh/specklesystems/speckle-sharp-sdk)
|
[](https://codecov.io/gh/specklesystems/speckle-sharp-sdk)
|
||||||
|
|
||||||
> [!WARNING]
|
> [!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!
|
> 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!
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{DA2AED
|
|||||||
docker-compose.yml = docker-compose.yml
|
docker-compose.yml = docker-compose.yml
|
||||||
CodeMetricsConfig.txt = CodeMetricsConfig.txt
|
CodeMetricsConfig.txt = CodeMetricsConfig.txt
|
||||||
Directory.Build.Targets = Directory.Build.Targets
|
Directory.Build.Targets = Directory.Build.Targets
|
||||||
|
.config\dotnet-tools.json = .config\dotnet-tools.json
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{58D37DA9-F948-48CA-9A73-F5BBBD533DBF}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{58D37DA9-F948-48CA-9A73-F5BBBD533DBF}"
|
||||||
|
|||||||
+38
-13
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Text.Json;
|
||||||
using GlobExpressions;
|
using GlobExpressions;
|
||||||
using static Bullseye.Targets;
|
using static Bullseye.Targets;
|
||||||
using static SimpleExec.Command;
|
using static SimpleExec.Command;
|
||||||
@@ -11,11 +12,20 @@ const string BUILD = "build";
|
|||||||
const string TEST = "test";
|
const string TEST = "test";
|
||||||
const string INTEGRATION = "integration";
|
const string INTEGRATION = "integration";
|
||||||
const string PACK = "pack";
|
const string PACK = "pack";
|
||||||
const string PACK_LOCAL = "pack-local";
|
|
||||||
const string CLEAN_LOCKS = "clean-locks";
|
const string CLEAN_LOCKS = "clean-locks";
|
||||||
const string PERF = "perf";
|
const string PERF = "perf";
|
||||||
const string DEEP_CLEAN = "deep-clean";
|
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(
|
Target(
|
||||||
CLEAN_LOCKS,
|
CLEAN_LOCKS,
|
||||||
() =>
|
() =>
|
||||||
@@ -32,7 +42,7 @@ Target(
|
|||||||
|
|
||||||
Target(
|
Target(
|
||||||
CLEAN,
|
CLEAN,
|
||||||
ForEach("**/output"),
|
forEach: ["**/output"],
|
||||||
dir =>
|
dir =>
|
||||||
{
|
{
|
||||||
IEnumerable<string> GetDirectories(string d)
|
IEnumerable<string> GetDirectories(string d)
|
||||||
@@ -58,22 +68,28 @@ Target(
|
|||||||
|
|
||||||
Target(RESTORE_TOOLS, () => RunAsync("dotnet", "tool restore"));
|
Target(RESTORE_TOOLS, () => RunAsync("dotnet", "tool restore"));
|
||||||
|
|
||||||
Target(FORMAT, DependsOn(RESTORE_TOOLS), () => RunAsync("dotnet", "csharpier --check ."));
|
Target(FORMAT, dependsOn: [RESTORE_TOOLS], () => RunAsync("dotnet", "csharpier check ."));
|
||||||
|
|
||||||
Target(RESTORE, () => RunAsync("dotnet", "restore Speckle.Sdk.sln --locked-mode"));
|
Target(RESTORE, dependsOn: [FORMAT], () => RunAsync("dotnet", "restore Speckle.Sdk.sln --locked-mode"));
|
||||||
|
|
||||||
Target(
|
Target(
|
||||||
BUILD,
|
BUILD,
|
||||||
DependsOn(RESTORE),
|
dependsOn: [RESTORE],
|
||||||
async () =>
|
async () =>
|
||||||
{
|
{
|
||||||
await RunAsync("dotnet", $"build Speckle.Sdk.sln -c Release --no-restore -warnaserror").ConfigureAwait(false);
|
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);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
Target(
|
Target(
|
||||||
TEST,
|
TEST,
|
||||||
DependsOn(BUILD),
|
dependsOn: [BUILD],
|
||||||
Glob.Files(".", "**/*.Tests.Unit.csproj").Concat(Glob.Files(".", "**/*.Tests.csproj")),
|
Glob.Files(".", "**/*.Tests.Unit.csproj").Concat(Glob.Files(".", "**/*.Tests.csproj")),
|
||||||
async file =>
|
async file =>
|
||||||
{
|
{
|
||||||
@@ -87,7 +103,7 @@ Target(
|
|||||||
|
|
||||||
Target(
|
Target(
|
||||||
INTEGRATION,
|
INTEGRATION,
|
||||||
DependsOn(BUILD),
|
dependsOn: [BUILD],
|
||||||
async () =>
|
async () =>
|
||||||
{
|
{
|
||||||
await RunAsync("docker", "compose -f docker-compose.yml up --wait").ConfigureAwait(false);
|
await RunAsync("docker", "compose -f docker-compose.yml up --wait").ConfigureAwait(false);
|
||||||
@@ -152,11 +168,20 @@ Target(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
static Task RunPack() => RunAsync("dotnet", "pack Speckle.Sdk.sln -c Release -o output --no-build");
|
Target(
|
||||||
|
PACK,
|
||||||
|
dependsOn: [TEST],
|
||||||
|
async () =>
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var (version, fileVersion) = await GetVersions().ConfigureAwait(false);
|
||||||
|
Console.WriteLine($"Version: {version} & {fileVersion}");
|
||||||
|
await RunAsync("dotnet", $"pack Speckle.Sdk.sln -c Release -o output --no-build -p:Version={version}")
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
Target(PACK, DependsOn(TEST), RunPack);
|
Target("default", dependsOn: [FORMAT, TEST, INTEGRATION], () => Console.WriteLine("Done!"));
|
||||||
Target(PACK_LOCAL, DependsOn(BUILD), RunPack);
|
|
||||||
|
|
||||||
Target("default", DependsOn(FORMAT, TEST, INTEGRATION), () => Console.WriteLine("Done!"));
|
|
||||||
|
|
||||||
await RunTargetsAndExitAsync(args).ConfigureAwait(true);
|
await RunTargetsAndExitAsync(args).ConfigureAwait(true);
|
||||||
|
|||||||
+1
-2
@@ -1,10 +1,9 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Bullseye" />
|
<PackageReference Include="Bullseye" />
|
||||||
<PackageReference Include="Glob" />
|
<PackageReference Include="Glob" />
|
||||||
<PackageReference Include="SimpleExec" />
|
<PackageReference Include="SimpleExec" />
|
||||||
|
|||||||
@@ -4,15 +4,9 @@
|
|||||||
"net8.0": {
|
"net8.0": {
|
||||||
"Bullseye": {
|
"Bullseye": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[5.0.0, )",
|
"requested": "[6.0.0, )",
|
||||||
"resolved": "5.0.0",
|
"resolved": "6.0.0",
|
||||||
"contentHash": "bqyt+m17ym+5aN45C5oZRAjuLDt8jKiCm/ys1XfymIXSkrTFwvI/QsbY3ucPSHDz7SF7uON7B57kXFv5H2k1ew=="
|
"contentHash": "vgwwXfzs7jJrskWH7saHRMgPzziq/e86QZNWY1MnMxd7e+De7E7EX4K3C7yrvaK9y02SJoLxNxcLG/q5qUAghw=="
|
||||||
},
|
|
||||||
"GitVersion.MsBuild": {
|
|
||||||
"type": "Direct",
|
|
||||||
"requested": "[5.12.0, )",
|
|
||||||
"resolved": "5.12.0",
|
|
||||||
"contentHash": "dJuigXycpJNOiLT9or7mkHSkGFHgGW3/p6cNNYEKZBa7Hhp1FdX/cvqYWWYhRLpfoZOedeA7aRbYiOB3vW/dvA=="
|
|
||||||
},
|
},
|
||||||
"Glob": {
|
"Glob": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
using Speckle.Objects.Geometry;
|
||||||
|
using Speckle.Sdk.Models;
|
||||||
|
using Point = Speckle.Objects.Geometry.Point;
|
||||||
|
|
||||||
|
namespace Speckle.Objects.Annotation;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Text class for representation in the viewer
|
||||||
|
/// </summary>
|
||||||
|
[SpeckleType("Objects.Annotation.Text")]
|
||||||
|
public class Text : Base
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Plain text, without formatting
|
||||||
|
/// </summary>
|
||||||
|
public required string value { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Origin point, relation to the text is defined by AlignmentHorizontal and AlignmentVertical
|
||||||
|
/// </summary>
|
||||||
|
public required Point origin { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Height in linear units or pixels (if Units.None)
|
||||||
|
/// </summary>
|
||||||
|
public required double height { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Units will be 'Units.None' if the text size is defined in pixels (stays the same size
|
||||||
|
/// independently of zooming the model). Default height in pixels is 17px (used for Viewer measurements)
|
||||||
|
/// </summary>
|
||||||
|
public required string units { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Horizontal alignment: Left, Center or Right
|
||||||
|
/// </summary>
|
||||||
|
public AlignmentHorizontal alignmentH { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Vertical alignment: Top, Center or Bottom
|
||||||
|
/// </summary>
|
||||||
|
public AlignmentVertical alignmentV { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Plane will be null if the text object orientation follows camera view
|
||||||
|
/// </summary>
|
||||||
|
public Plane? plane { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maximum width of the text field (in 'units').
|
||||||
|
/// Text will be split into lines (wrapped) to fit into the width.
|
||||||
|
/// null, if text should not be wrapped.
|
||||||
|
/// </summary>
|
||||||
|
public double? maxWidth { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum AlignmentHorizontal
|
||||||
|
{
|
||||||
|
Left,
|
||||||
|
Center,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum AlignmentVertical
|
||||||
|
{
|
||||||
|
Top,
|
||||||
|
Center,
|
||||||
|
Bottom,
|
||||||
|
}
|
||||||
@@ -12,6 +12,13 @@ public class RevitObject : DataObject, IRevitObject
|
|||||||
public required string family { get; set; }
|
public required string family { get; set; }
|
||||||
public required string category { get; set; }
|
public required string category { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The level constraint of the object.
|
||||||
|
/// For objects constrained by multiple levels, this represents the base constraint.
|
||||||
|
/// For objects with no level constraint, this should be null.
|
||||||
|
/// </summary>
|
||||||
|
public required string? level { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A Curve or Point object representing the location of a Revit element.
|
/// A Curve or Point object representing the location of a Revit element.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ using Speckle.Newtonsoft.Json;
|
|||||||
using Speckle.Objects.Other;
|
using Speckle.Objects.Other;
|
||||||
using Speckle.Objects.Primitive;
|
using Speckle.Objects.Primitive;
|
||||||
using Speckle.Sdk.Common;
|
using Speckle.Sdk.Common;
|
||||||
using Speckle.Sdk.Host;
|
|
||||||
using Speckle.Sdk.Models;
|
using Speckle.Sdk.Models;
|
||||||
|
|
||||||
namespace Speckle.Objects.Geometry;
|
namespace Speckle.Objects.Geometry;
|
||||||
@@ -31,7 +30,7 @@ public class Brep : Base, IHasArea, IHasVolume, IHasBoundingBox, ITransformable<
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the flat list of numbers representing the <see cref="Brep"/>'s surfaces.
|
/// Gets or sets the flat list of numbers representing the <see cref="Brep"/>'s surfaces.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DetachProperty, SchemaIgnore, Chunkable(31250)]
|
[DetachProperty, Chunkable(31250)]
|
||||||
public List<double> SurfacesValue
|
public List<double> SurfacesValue
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -77,7 +76,7 @@ public class Brep : Base, IHasArea, IHasVolume, IHasBoundingBox, ITransformable<
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This is only used for the <see cref="Brep"/> class serialisation/deserialisation. You should use <see cref="Brep.Curve3D"/> instead.
|
/// This is only used for the <see cref="Brep"/> class serialisation/deserialisation. You should use <see cref="Brep.Curve3D"/> instead.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[DetachProperty, SchemaIgnore, Chunkable(31250)]
|
[DetachProperty, Chunkable(31250)]
|
||||||
public List<double> Curve3DValues
|
public List<double> Curve3DValues
|
||||||
{
|
{
|
||||||
get => CurveArrayEncodingExtensions.ToArray(Curve3D);
|
get => CurveArrayEncodingExtensions.ToArray(Curve3D);
|
||||||
@@ -102,7 +101,7 @@ public class Brep : Base, IHasArea, IHasVolume, IHasBoundingBox, ITransformable<
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This is only used for the <see cref="Brep"/> class serialisation/deserialisation. You should use <see cref="Brep.Curve2D"/> instead.
|
/// This is only used for the <see cref="Brep"/> class serialisation/deserialisation. You should use <see cref="Brep.Curve2D"/> instead.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[DetachProperty, SchemaIgnore, Chunkable(31250)]
|
[DetachProperty, Chunkable(31250)]
|
||||||
public List<double> Curve2DValues
|
public List<double> Curve2DValues
|
||||||
{
|
{
|
||||||
get => CurveArrayEncodingExtensions.ToArray(Curve2D);
|
get => CurveArrayEncodingExtensions.ToArray(Curve2D);
|
||||||
@@ -127,7 +126,7 @@ public class Brep : Base, IHasArea, IHasVolume, IHasBoundingBox, ITransformable<
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This is only used for the <see cref="Brep"/> class serialisation/deserialisation. You should use <see cref="Brep.Vertices"/> instead.
|
/// This is only used for the <see cref="Brep"/> class serialisation/deserialisation. You should use <see cref="Brep.Vertices"/> instead.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[DetachProperty, SchemaIgnore, Chunkable(31250)]
|
[DetachProperty, Chunkable(31250)]
|
||||||
public List<double> VerticesValue
|
public List<double> VerticesValue
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -167,7 +166,7 @@ public class Brep : Base, IHasArea, IHasVolume, IHasBoundingBox, ITransformable<
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This is only used for the <see cref="Brep"/> class serialisation/deserialisation. You should use <see cref="Brep.Edges"/> instead.
|
/// This is only used for the <see cref="Brep"/> class serialisation/deserialisation. You should use <see cref="Brep.Edges"/> instead.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[DetachProperty, SchemaIgnore, Chunkable(62500)]
|
[DetachProperty, Chunkable(62500)]
|
||||||
public List<double?> EdgesValue
|
public List<double?> EdgesValue
|
||||||
{
|
{
|
||||||
get =>
|
get =>
|
||||||
@@ -241,7 +240,7 @@ public class Brep : Base, IHasArea, IHasVolume, IHasBoundingBox, ITransformable<
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This is only used for the <see cref="Brep"/> class serialisation/deserialisation. You should use <see cref="Brep.Loops"/> instead.
|
/// This is only used for the <see cref="Brep"/> class serialisation/deserialisation. You should use <see cref="Brep.Loops"/> instead.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[DetachProperty, SchemaIgnore, Chunkable(62500)]
|
[DetachProperty, Chunkable(62500)]
|
||||||
public List<int> LoopsValue
|
public List<int> LoopsValue
|
||||||
{
|
{
|
||||||
get =>
|
get =>
|
||||||
@@ -297,7 +296,7 @@ public class Brep : Base, IHasArea, IHasVolume, IHasBoundingBox, ITransformable<
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This is only used for the <see cref="Brep"/> class serialisation/deserialisation. You should use <see cref="Brep.Trims"/> instead.
|
/// This is only used for the <see cref="Brep"/> class serialisation/deserialisation. You should use <see cref="Brep.Trims"/> instead.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[DetachProperty, SchemaIgnore, Chunkable(62500)]
|
[DetachProperty, Chunkable(62500)]
|
||||||
public List<int> TrimsValue
|
public List<int> TrimsValue
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -363,7 +362,7 @@ public class Brep : Base, IHasArea, IHasVolume, IHasBoundingBox, ITransformable<
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This is only used for the <see cref="Brep"/> class serialisation/deserialisation. You should use <see cref="Brep.Faces"/> instead.
|
/// This is only used for the <see cref="Brep"/> class serialisation/deserialisation. You should use <see cref="Brep.Faces"/> instead.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[DetachProperty, SchemaIgnore, Chunkable(62500)]
|
[DetachProperty, Chunkable(62500)]
|
||||||
public List<int> FacesValue
|
public List<int> FacesValue
|
||||||
{
|
{
|
||||||
get =>
|
get =>
|
||||||
|
|||||||
@@ -68,7 +68,6 @@ public class Plane : Base, ITransformable<Plane>
|
|||||||
/// Returns the values of this <see cref="Plane"/> as a list of numbers
|
/// Returns the values of this <see cref="Plane"/> as a list of numbers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A list of values representing the Plane.</returns>
|
/// <returns>A list of values representing the Plane.</returns>
|
||||||
|
|
||||||
public List<double> ToList()
|
public List<double> ToList()
|
||||||
{
|
{
|
||||||
var list = new List<double>();
|
var list = new List<double>();
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
using Speckle.Objects.Other;
|
||||||
|
using Speckle.Sdk.Common;
|
||||||
|
using Speckle.Sdk.Models;
|
||||||
|
|
||||||
|
namespace Speckle.Objects.Geometry;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Flat polygon, defined by an outer boundary and inner loops.
|
||||||
|
/// </summary>
|
||||||
|
[SpeckleType("Objects.Geometry.Region")]
|
||||||
|
public class Region : Base, IHasArea, IHasBoundingBox, ITransformable, IDisplayValue<List<Mesh>>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Boundary of a region.
|
||||||
|
/// Should be a planar, closed, non-self-intersecting ICurve.
|
||||||
|
/// </summary>
|
||||||
|
public required ICurve boundary { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loops (voids) in the region.
|
||||||
|
/// Each loop should be planar, closed, non-self-intersecting ICurve, located inside the boundary.
|
||||||
|
/// The loops should not intersect or touch each other.
|
||||||
|
/// </summary>
|
||||||
|
public required List<ICurve> innerLoops { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The units of object's coordinates.
|
||||||
|
/// This should be one of <see cref="Units"/>
|
||||||
|
/// </summary>
|
||||||
|
public required string units { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indication whether the region is just a geometry (false) or has a hatch pattern (true).
|
||||||
|
/// It's a distinction for receiving in apps that support both Region and Hatch (aka region with hatch pattern)
|
||||||
|
/// </summary>
|
||||||
|
public required bool hasHatchPattern { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public double area { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public Box? bbox { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
[DetachProperty]
|
||||||
|
public List<Mesh> displayValue { get; set; } = new();
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool TransformTo(Transform transform, out ITransformable transformed)
|
||||||
|
{
|
||||||
|
// assign self to the returned object, in case transformation fails
|
||||||
|
transformed = this;
|
||||||
|
|
||||||
|
// transform boundary
|
||||||
|
if (boundary is ITransformable boundaryTransformable)
|
||||||
|
{
|
||||||
|
boundaryTransformable.TransformTo(transform, out ITransformable transformedBoundaryResult);
|
||||||
|
var transformedBoundary = (ICurve)transformedBoundaryResult;
|
||||||
|
|
||||||
|
// transform inner loops
|
||||||
|
var transformedLoops = new List<ICurve>();
|
||||||
|
foreach (var loop in innerLoops)
|
||||||
|
{
|
||||||
|
if (loop is ITransformable loopTransformable)
|
||||||
|
{
|
||||||
|
loopTransformable.TransformTo(transform, out ITransformable transformedLoop);
|
||||||
|
transformedLoops.Add((ICurve)transformedLoop);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// transform display meshes
|
||||||
|
var transformedMeshes = new List<Mesh>();
|
||||||
|
foreach (var mesh in displayValue)
|
||||||
|
{
|
||||||
|
mesh.TransformTo(transform, out ITransformable transformedMesh);
|
||||||
|
transformedMeshes.Add((Mesh)transformedMesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if boundary and loops transformations succeeded
|
||||||
|
transformed = new Region
|
||||||
|
{
|
||||||
|
boundary = transformedBoundary,
|
||||||
|
innerLoops = transformedLoops,
|
||||||
|
hasHatchPattern = hasHatchPattern,
|
||||||
|
displayValue = transformedMeshes,
|
||||||
|
units = units,
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -147,7 +147,6 @@ public class Surface : Base, IHasBoundingBox, IHasArea, ITransformable<Surface>
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A 2-dimensional array representing this <see cref="Surface"/>s control points.</returns>
|
/// <returns>A 2-dimensional array representing this <see cref="Surface"/>s control points.</returns>
|
||||||
/// <remarks>The ControlPoints will be ordered following directions "[u][v]"</remarks>
|
/// <remarks>The ControlPoints will be ordered following directions "[u][v]"</remarks>
|
||||||
|
|
||||||
public List<List<ControlPoint>> GetControlPoints()
|
public List<List<ControlPoint>> GetControlPoints()
|
||||||
{
|
{
|
||||||
var matrix = new List<List<ControlPoint>>();
|
var matrix = new List<List<ControlPoint>>();
|
||||||
|
|||||||
@@ -1,37 +1,30 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup Label="Compiler Properties">
|
<PropertyGroup Label="Compiler Properties">
|
||||||
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
|
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
|
||||||
<PolySharpExcludeGeneratedTypes>System.Runtime.CompilerServices.RequiresLocationAttribute</PolySharpExcludeGeneratedTypes>
|
<PolySharpExcludeGeneratedTypes>System.Runtime.CompilerServices.RequiresLocationAttribute</PolySharpExcludeGeneratedTypes>
|
||||||
<Configurations>Debug;Release;Local</Configurations>
|
<Configurations>Debug;Release;Local</Configurations>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Label="Nugetspec Package Properties">
|
<PropertyGroup Label="Nugetspec Package Properties">
|
||||||
<PackageId>Speckle.Objects</PackageId>
|
<PackageId>Speckle.Objects</PackageId>
|
||||||
<Description>Objects is the default object model for Speckle</Description>
|
<Description>Objects is the default object model for Speckle</Description>
|
||||||
<PackageTags>$(PackageTags) objects</PackageTags>
|
<PackageTags>$(PackageTags) objects</PackageTags>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Label="Nuget Package Properties">
|
<PropertyGroup Label="Nuget Package Properties">
|
||||||
<IsPackable>true</IsPackable>
|
<IsPackable>true</IsPackable>
|
||||||
<IncludeSymbols>true</IncludeSymbols>
|
<IncludeSymbols>true</IncludeSymbols>
|
||||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Label="Analyers">
|
<PropertyGroup Label="Analyers">
|
||||||
<NoWarn>
|
<NoWarn>
|
||||||
$(NoWarn);
|
$(NoWarn);
|
||||||
CA1819;CA1008;CA2225;
|
CA1819;CA1008;CA2225;
|
||||||
</NoWarn>
|
</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup Label="Expose internals to test projects">
|
<ItemGroup Label="Expose internals to test projects">
|
||||||
<InternalsVisibleTo Include="Speckle.Objects.Tests.Unit" />
|
<InternalsVisibleTo Include="Speckle.Objects.Tests.Unit" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Label="Project References">
|
<ItemGroup Label="Project References">
|
||||||
<ProjectReference Include="..\Speckle.Sdk\Speckle.Sdk.csproj" />
|
<ProjectReference Include="..\Speckle.Sdk\Speckle.Sdk.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -2,12 +2,6 @@
|
|||||||
"version": 2,
|
"version": 2,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
".NETStandard,Version=v2.0": {
|
".NETStandard,Version=v2.0": {
|
||||||
"GitVersion.MsBuild": {
|
|
||||||
"type": "Direct",
|
|
||||||
"requested": "[5.12.0, )",
|
|
||||||
"resolved": "5.12.0",
|
|
||||||
"contentHash": "dJuigXycpJNOiLT9or7mkHSkGFHgGW3/p6cNNYEKZBa7Hhp1FdX/cvqYWWYhRLpfoZOedeA7aRbYiOB3vW/dvA=="
|
|
||||||
},
|
|
||||||
"Microsoft.SourceLink.GitHub": {
|
"Microsoft.SourceLink.GitHub": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[8.0.0, )",
|
"requested": "[8.0.0, )",
|
||||||
@@ -315,12 +309,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"net8.0": {
|
"net8.0": {
|
||||||
"GitVersion.MsBuild": {
|
|
||||||
"type": "Direct",
|
|
||||||
"requested": "[5.12.0, )",
|
|
||||||
"resolved": "5.12.0",
|
|
||||||
"contentHash": "dJuigXycpJNOiLT9or7mkHSkGFHgGW3/p6cNNYEKZBa7Hhp1FdX/cvqYWWYhRLpfoZOedeA7aRbYiOB3vW/dvA=="
|
|
||||||
},
|
|
||||||
"Microsoft.SourceLink.GitHub": {
|
"Microsoft.SourceLink.GitHub": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[8.0.0, )",
|
"requested": "[8.0.0, )",
|
||||||
|
|||||||
@@ -22,4 +22,23 @@ public static class Collections
|
|||||||
public static class EnumerableExtensions
|
public static class EnumerableExtensions
|
||||||
{
|
{
|
||||||
public static IEnumerable<int> RangeFrom(int from, int to) => Enumerable.Range(from, to - from + 1);
|
public static IEnumerable<int> RangeFrom(int from, int to) => Enumerable.Range(from, to - from + 1);
|
||||||
|
|
||||||
|
#if NETSTANDARD2_0
|
||||||
|
public static IEnumerable<TSource> DistinctBy<TSource, TKey>(
|
||||||
|
this IEnumerable<TSource> source,
|
||||||
|
Func<TSource, TKey> keySelector
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var keys = new HashSet<TKey>();
|
||||||
|
foreach (var element in source)
|
||||||
|
{
|
||||||
|
if (keys.Contains(keySelector(element)))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
keys.Add(keySelector(element));
|
||||||
|
yield return element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,13 +19,13 @@ public static class BatchExtensions
|
|||||||
public static void AddBatchItem<T>(this IMemoryOwner<T> batch, T item)
|
public static void AddBatchItem<T>(this IMemoryOwner<T> batch, T item)
|
||||||
where T : IHasByteSize => ((Batch<T>)batch).Add(item);
|
where T : IHasByteSize => ((Batch<T>)batch).Add(item);
|
||||||
|
|
||||||
public static int GetBatchSize<T>(this IMemoryOwner<T> batch, Action<string> logAsWarning, int maxBatchSize)
|
public static int GetBatchSize<T>(this IMemoryOwner<T> batch, int maxBatchSize)
|
||||||
where T : IHasByteSize
|
where T : IHasByteSize
|
||||||
{
|
{
|
||||||
var currentSize = ((Batch<T>)batch).BatchByteSize;
|
var currentSize = ((Batch<T>)batch).BatchByteSize;
|
||||||
if (currentSize > maxBatchSize)
|
if (currentSize > maxBatchSize)
|
||||||
{
|
{
|
||||||
logAsWarning($"Batch size exceeded. Current size: {currentSize} bytes. Max size: {maxBatchSize} bytes.");
|
//doing this to say it's full since the channel reader only does full being equivalent
|
||||||
return maxBatchSize;
|
return maxBatchSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ public static class ChannelExtensions
|
|||||||
{
|
{
|
||||||
public static BatchingChannelReader<T, IMemoryOwner<T>> BatchByByteSize<T>(
|
public static BatchingChannelReader<T, IMemoryOwner<T>> BatchByByteSize<T>(
|
||||||
this ChannelReader<T> source,
|
this ChannelReader<T> source,
|
||||||
Action<string> logAsWarning,
|
|
||||||
int batchSize,
|
int batchSize,
|
||||||
bool singleReader = false,
|
bool singleReader = false,
|
||||||
bool allowSynchronousContinuations = false
|
bool allowSynchronousContinuations = false
|
||||||
@@ -16,7 +15,6 @@ public static class ChannelExtensions
|
|||||||
where T : IHasByteSize =>
|
where T : IHasByteSize =>
|
||||||
new SizeBatchingChannelReader<T>(
|
new SizeBatchingChannelReader<T>(
|
||||||
source ?? throw new ArgumentNullException(nameof(source)),
|
source ?? throw new ArgumentNullException(nameof(source)),
|
||||||
logAsWarning,
|
|
||||||
batchSize,
|
batchSize,
|
||||||
singleReader,
|
singleReader,
|
||||||
allowSynchronousContinuations
|
allowSynchronousContinuations
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ using Speckle.Sdk.Serialisation.V2.Send;
|
|||||||
|
|
||||||
namespace Speckle.Sdk.Dependencies.Serialization;
|
namespace Speckle.Sdk.Dependencies.Serialization;
|
||||||
|
|
||||||
public abstract class ChannelSaver<T>(Action<string> logAsWarning, CancellationToken cancellationToken)
|
public abstract class ChannelSaver<T>
|
||||||
where T : IHasByteSize
|
where T : IHasByteSize
|
||||||
{
|
{
|
||||||
private const int SEND_CAPACITY = 500;
|
private const int SEND_CAPACITY = 1000;
|
||||||
private const int HTTP_SEND_CHUNK_SIZE = 25_000_000; //bytes
|
private const int HTTP_SEND_CHUNK_SIZE = 25_000_000; //bytes
|
||||||
private static readonly TimeSpan HTTP_BATCH_TIMEOUT = TimeSpan.FromSeconds(2);
|
private static readonly TimeSpan HTTP_BATCH_TIMEOUT = TimeSpan.FromSeconds(2);
|
||||||
private const int MAX_PARALLELISM_HTTP = 4;
|
private const int MAX_PARALLELISM_HTTP = 4;
|
||||||
@@ -16,8 +16,6 @@ public abstract class ChannelSaver<T>(Action<string> logAsWarning, CancellationT
|
|||||||
private const int MAX_CACHE_WRITE_PARALLELISM = 4;
|
private const int MAX_CACHE_WRITE_PARALLELISM = 4;
|
||||||
private const int MAX_CACHE_BATCH = 500;
|
private const int MAX_CACHE_BATCH = 500;
|
||||||
|
|
||||||
private readonly CancellationTokenSource _cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
|
||||||
|
|
||||||
private readonly Channel<T> _checkCacheChannel = Channel.CreateBounded<T>(
|
private readonly Channel<T> _checkCacheChannel = Channel.CreateBounded<T>(
|
||||||
new BoundedChannelOptions(SEND_CAPACITY)
|
new BoundedChannelOptions(SEND_CAPACITY)
|
||||||
{
|
{
|
||||||
@@ -30,26 +28,31 @@ public abstract class ChannelSaver<T>(Action<string> logAsWarning, CancellationT
|
|||||||
_ => throw new NotImplementedException("Dropping items not supported.")
|
_ => throw new NotImplementedException("Dropping items not supported.")
|
||||||
);
|
);
|
||||||
|
|
||||||
public Task Start() =>
|
public Task Start(
|
||||||
|
int? maxParallelism,
|
||||||
|
int? httpBatchSize,
|
||||||
|
int? cacheBatchSize,
|
||||||
|
CancellationToken cancellationToken
|
||||||
|
) =>
|
||||||
_checkCacheChannel
|
_checkCacheChannel
|
||||||
.Reader.BatchByByteSize(logAsWarning, HTTP_SEND_CHUNK_SIZE)
|
.Reader.BatchByByteSize(httpBatchSize ?? HTTP_SEND_CHUNK_SIZE)
|
||||||
.WithTimeout(HTTP_BATCH_TIMEOUT)
|
.WithTimeout(HTTP_BATCH_TIMEOUT)
|
||||||
.PipeAsync(
|
.PipeAsync(
|
||||||
MAX_PARALLELISM_HTTP,
|
maxParallelism ?? MAX_PARALLELISM_HTTP,
|
||||||
async x => await SendToServer(x).ConfigureAwait(false),
|
async x => await SendToServer(x).ConfigureAwait(false),
|
||||||
HTTP_CAPACITY,
|
HTTP_CAPACITY,
|
||||||
false,
|
false,
|
||||||
_cts.Token
|
cancellationToken
|
||||||
)
|
)
|
||||||
.Join()
|
.Join()
|
||||||
.Batch(MAX_CACHE_BATCH)
|
.Batch(cacheBatchSize ?? MAX_CACHE_BATCH)
|
||||||
.WithTimeout(HTTP_BATCH_TIMEOUT)
|
.WithTimeout(HTTP_BATCH_TIMEOUT)
|
||||||
.ReadAllConcurrently(MAX_CACHE_WRITE_PARALLELISM, SaveToCache, _cts.Token)
|
.ReadAllConcurrently(maxParallelism ?? MAX_CACHE_WRITE_PARALLELISM, SaveToCache, cancellationToken)
|
||||||
.ContinueWith(
|
.ContinueWith(
|
||||||
t =>
|
t =>
|
||||||
{
|
{
|
||||||
Exception? ex = t.Exception;
|
Exception? ex = t.Exception;
|
||||||
if (ex is null && t.Status is TaskStatus.Canceled && !_cts.Token.IsCancellationRequested)
|
if (ex is null && t.Status is TaskStatus.Canceled && !cancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
ex = new OperationCanceledException();
|
ex = new OperationCanceledException();
|
||||||
}
|
}
|
||||||
@@ -60,25 +63,27 @@ public abstract class ChannelSaver<T>(Action<string> logAsWarning, CancellationT
|
|||||||
}
|
}
|
||||||
_checkCacheChannel.Writer.TryComplete(ex);
|
_checkCacheChannel.Writer.TryComplete(ex);
|
||||||
},
|
},
|
||||||
_cts.Token,
|
cancellationToken,
|
||||||
TaskContinuationOptions.ExecuteSynchronously,
|
TaskContinuationOptions.ExecuteSynchronously,
|
||||||
TaskScheduler.Current
|
TaskScheduler.Current
|
||||||
);
|
);
|
||||||
|
|
||||||
public async ValueTask Save(T item)
|
public async Task SaveAsync(T item, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (Exception is not null || _cts.IsCancellationRequested)
|
if (Exception is not null)
|
||||||
{
|
{
|
||||||
return; //don't save if we're already done through an error
|
return; //don't save if we're already done through an error
|
||||||
}
|
}
|
||||||
await _checkCacheChannel.Writer.WriteAsync(item).ConfigureAwait(false);
|
//can switch to check then try pattern when back pressure is needed or exceptions are too much
|
||||||
|
//the trees don't need to respond to back pressure
|
||||||
|
await _checkCacheChannel.Writer.WriteAsync(item, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<IMemoryOwner<T>> SendToServer(IMemoryOwner<T> batch)
|
private async Task<IMemoryOwner<T>> SendToServer(IMemoryOwner<T> batch)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await SendToServer((Batch<T>)batch).ConfigureAwait(false);
|
await SendToServerInternal((Batch<T>)batch).ConfigureAwait(false);
|
||||||
return batch;
|
return batch;
|
||||||
}
|
}
|
||||||
#pragma warning disable CA1031
|
#pragma warning disable CA1031
|
||||||
@@ -90,20 +95,6 @@ public abstract class ChannelSaver<T>(Action<string> logAsWarning, CancellationT
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SendToServer(Batch<T> 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<T> batch);
|
protected abstract Task SendToServerInternal(Batch<T> batch);
|
||||||
|
|
||||||
public abstract void SaveToCache(List<T> item);
|
public abstract void SaveToCache(List<T> item);
|
||||||
@@ -118,13 +109,11 @@ public abstract class ChannelSaver<T>(Action<string> logAsWarning, CancellationT
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Exception? Exception { get; set; }
|
public Exception? Exception { get; set; }
|
||||||
|
|
||||||
private void RecordException(Exception ex)
|
private void RecordException(Exception ex)
|
||||||
{
|
{
|
||||||
Exception = ex;
|
Exception = ex;
|
||||||
_checkCacheChannel.Writer.TryComplete(ex);
|
_checkCacheChannel.Writer.TryComplete(ex);
|
||||||
//cancel everything!
|
|
||||||
_cts.Cancel();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ public interface IHasByteSize
|
|||||||
|
|
||||||
public sealed class SizeBatchingChannelReader<T>(
|
public sealed class SizeBatchingChannelReader<T>(
|
||||||
ChannelReader<T> source,
|
ChannelReader<T> source,
|
||||||
Action<string> logAsWarning,
|
|
||||||
int batchSize,
|
int batchSize,
|
||||||
bool singleReader,
|
bool singleReader,
|
||||||
bool syncCont = false
|
bool syncCont = false
|
||||||
@@ -34,5 +33,5 @@ public sealed class SizeBatchingChannelReader<T>(
|
|||||||
|
|
||||||
protected override void AddBatchItem(IMemoryOwner<T> batch, T item) => batch.AddBatchItem(item);
|
protected override void AddBatchItem(IMemoryOwner<T> batch, T item) => batch.AddBatchItem(item);
|
||||||
|
|
||||||
protected override int GetBatchSize(IMemoryOwner<T> batch) => batch.GetBatchSize(logAsWarning, _batchSize);
|
protected override int GetBatchSize(IMemoryOwner<T> batch) => batch.GetBatchSize(_batchSize);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup Label="Compiler Properties">
|
<PropertyGroup Label="Compiler Properties">
|
||||||
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
|
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
|
||||||
<Configurations>Debug;Release;Local</Configurations>
|
<Configurations>Debug;Release;Local</Configurations>
|
||||||
@@ -7,21 +6,18 @@
|
|||||||
<ILRepackRenameInternalized>true</ILRepackRenameInternalized>
|
<ILRepackRenameInternalized>true</ILRepackRenameInternalized>
|
||||||
<ILRepackMergeDebugSymbols>true</ILRepackMergeDebugSymbols>
|
<ILRepackMergeDebugSymbols>true</ILRepackMergeDebugSymbols>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Label="Nugetspec Package Properties">
|
<PropertyGroup Label="Nugetspec Package Properties">
|
||||||
<PackageId>Speckle.Sdk.Dependencies</PackageId>
|
<PackageId>Speckle.Sdk.Dependencies</PackageId>
|
||||||
<Description>The .NET SDK for Speckle</Description>
|
<Description>The .NET SDK for Speckle</Description>
|
||||||
<PackageTags>$(PackageTags) core sdk</PackageTags>
|
<PackageTags>$(PackageTags) core sdk</PackageTags>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Label="Nuget Package Properties">
|
<PropertyGroup Label="Nuget Package Properties">
|
||||||
<IsPackable>true</IsPackable>
|
<IsPackable>true</IsPackable>
|
||||||
<IncludeSymbols>true</IncludeSymbols>
|
<IncludeSymbols>true</IncludeSymbols>
|
||||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="ILRepack.FullAuto">
|
<PackageReference Include="ILRepack.FullAuto">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
@@ -31,6 +27,5 @@
|
|||||||
<PackageReference Include="Polly.Extensions.Http" PrivateAssets="all" />
|
<PackageReference Include="Polly.Extensions.Http" PrivateAssets="all" />
|
||||||
<PackageReference Include="Open.ChannelExtensions" PrivateAssets="all" />
|
<PackageReference Include="Open.ChannelExtensions" PrivateAssets="all" />
|
||||||
<PackageReference Include="System.Threading.Channels" PrivateAssets="all" />
|
<PackageReference Include="System.Threading.Channels" PrivateAssets="all" />
|
||||||
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using Speckle.Sdk.Logging;
|
|||||||
|
|
||||||
namespace Speckle.Sdk.Helpers;
|
namespace Speckle.Sdk.Helpers;
|
||||||
|
|
||||||
public sealed class SpeckleHttpClientHandler : DelegatingHandler
|
internal sealed class SpeckleHttpClientHandler : DelegatingHandler
|
||||||
{
|
{
|
||||||
private readonly IAsyncPolicy<HttpResponseMessage> _resiliencePolicy;
|
private readonly IAsyncPolicy<HttpResponseMessage> _resiliencePolicy;
|
||||||
private readonly ISdkActivityFactory _activityFactory;
|
private readonly ISdkActivityFactory _activityFactory;
|
||||||
|
|||||||
@@ -40,8 +40,13 @@ public sealed class SpeckleHttpClientHandlerFactory(ISdkActivityFactory activity
|
|||||||
return Policy.WrapAsync(retryPolicy, timeoutPolicy);
|
return Policy.WrapAsync(retryPolicy, timeoutPolicy);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SpeckleHttpClientHandler Create(
|
public DelegatingHandler Create(
|
||||||
HttpMessageHandler? innerHandler = null,
|
HttpMessageHandler? innerHandler = null,
|
||||||
int timeoutSeconds = DEFAULT_TIMEOUT_SECONDS
|
int timeoutSeconds = DEFAULT_TIMEOUT_SECONDS
|
||||||
) => new(innerHandler ?? new HttpClientHandler(), activityFactory, HttpAsyncPolicy(timeoutSeconds: timeoutSeconds));
|
) =>
|
||||||
|
new SpeckleHttpClientHandler(
|
||||||
|
innerHandler ?? new HttpClientHandler(),
|
||||||
|
activityFactory,
|
||||||
|
HttpAsyncPolicy(timeoutSeconds: timeoutSeconds)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,6 @@
|
|||||||
"version": 2,
|
"version": 2,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
".NETStandard,Version=v2.0": {
|
".NETStandard,Version=v2.0": {
|
||||||
"GitVersion.MsBuild": {
|
|
||||||
"type": "Direct",
|
|
||||||
"requested": "[5.12.0, )",
|
|
||||||
"resolved": "5.12.0",
|
|
||||||
"contentHash": "dJuigXycpJNOiLT9or7mkHSkGFHgGW3/p6cNNYEKZBa7Hhp1FdX/cvqYWWYhRLpfoZOedeA7aRbYiOB3vW/dvA=="
|
|
||||||
},
|
|
||||||
"ILRepack.FullAuto": {
|
"ILRepack.FullAuto": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[1.6.0, )",
|
"requested": "[1.6.0, )",
|
||||||
@@ -19,9 +13,9 @@
|
|||||||
},
|
},
|
||||||
"Microsoft.Extensions.ObjectPool": {
|
"Microsoft.Extensions.ObjectPool": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[9.0.1, )",
|
"requested": "[9.0.4, )",
|
||||||
"resolved": "9.0.1",
|
"resolved": "9.0.4",
|
||||||
"contentHash": "r64veU9uYILp6pYqfo3qzRab8zLMALvXZgT4VRY79tXMLu8X79uTlJ6nqPLtPIVhfCPXycRh8ILyFz/gGBDQdQ=="
|
"contentHash": "G7p1k2xVZ+2aVANz0JdSiafr+AHDHeS1kF8+Y0ABbIsByd0erOL59IDXBs9vcdJf3pPV/murO0mbtr4k40QxWw=="
|
||||||
},
|
},
|
||||||
"Microsoft.SourceLink.GitHub": {
|
"Microsoft.SourceLink.GitHub": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
@@ -44,13 +38,13 @@
|
|||||||
},
|
},
|
||||||
"Open.ChannelExtensions": {
|
"Open.ChannelExtensions": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[9.0.0, )",
|
"requested": "[9.1.0, )",
|
||||||
"resolved": "9.0.0",
|
"resolved": "9.1.0",
|
||||||
"contentHash": "DP+l5S6G46wcuY4I4kNXE+RDOmJr0DKuMienOdt0mMBN9z7vmLSC8YQbqCyb9i9LNjXj1tgCx5LyitJiRr/v7g==",
|
"contentHash": "D6c24vMGy1oZ06vmkD2/FNzWHK7ZIihuv2spDgYEeaUp+eobrILQnrNQKRoASFXD4JGfZ7nfvTM0e+AX79dt8Q==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.Bcl.AsyncInterfaces": "9.0.0",
|
"Microsoft.Bcl.AsyncInterfaces": "9.0.4",
|
||||||
"System.Collections.Immutable": "9.0.0",
|
"System.Collections.Immutable": "9.0.4",
|
||||||
"System.Threading.Channels": "9.0.0"
|
"System.Threading.Channels": "9.0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Polly": {
|
"Polly": {
|
||||||
@@ -88,11 +82,11 @@
|
|||||||
},
|
},
|
||||||
"System.Threading.Channels": {
|
"System.Threading.Channels": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[9.0.2, )",
|
"requested": "[9.0.4, )",
|
||||||
"resolved": "9.0.2",
|
"resolved": "9.0.4",
|
||||||
"contentHash": "pUmqkuBS9OxWHOlfNad09Oxc8gRbxgN9UQtsqHPst4jfcgZRxQetNcsT2oe+VnUpEFAtBy1FZcJZiOscrBmA7g==",
|
"contentHash": "4qBn2H6/aXBpE/Pm3wY5yusY/pEvQz99NlWHrTUji0qCmOdbhhjaALcpmbfW2ksxlPM6i6S+QFLkpOQdyfeKYQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.Bcl.AsyncInterfaces": "9.0.2",
|
"Microsoft.Bcl.AsyncInterfaces": "9.0.4",
|
||||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -123,8 +117,8 @@
|
|||||||
},
|
},
|
||||||
"System.Collections.Immutable": {
|
"System.Collections.Immutable": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "9.0.0",
|
"resolved": "9.0.4",
|
||||||
"contentHash": "QhkXUl2gNrQtvPmtBTQHb0YsUrDiDQ2QS09YbtTTiSjGcf7NBqtYbrG/BE06zcBPCKEwQGzIv13IVdXNOSub2w==",
|
"contentHash": "wfm2NgK22MmBe5qJjp52qzpkeDZKb4l9LbdubhZSehY1z4LS+lld6R+B+UQNb2AZRHu/QJlHxEUcRst5hIEejg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"System.Memory": "4.5.5",
|
"System.Memory": "4.5.5",
|
||||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
|
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
|
||||||
@@ -161,20 +155,14 @@
|
|||||||
"Microsoft.Bcl.AsyncInterfaces": {
|
"Microsoft.Bcl.AsyncInterfaces": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[5.0.0, )",
|
"requested": "[5.0.0, )",
|
||||||
"resolved": "9.0.2",
|
"resolved": "9.0.4",
|
||||||
"contentHash": "1CED0BGD7dCKsbe7tDhzpPB2Qdi9x35QChu6zkBEI4s0T5bDkkttGReqQnOeOfRNSxtP2WvpX6Ik/0O93XDuMw==",
|
"contentHash": "9VGI5kxIvrNG2mqLQZnUR6y/3fcnygD8eNpHR+CqfbnIXvea6nehnYknDKQTxZVPMpzpNca+7DxLBmpdB3q0Bw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"net8.0": {
|
"net8.0": {
|
||||||
"GitVersion.MsBuild": {
|
|
||||||
"type": "Direct",
|
|
||||||
"requested": "[5.12.0, )",
|
|
||||||
"resolved": "5.12.0",
|
|
||||||
"contentHash": "dJuigXycpJNOiLT9or7mkHSkGFHgGW3/p6cNNYEKZBa7Hhp1FdX/cvqYWWYhRLpfoZOedeA7aRbYiOB3vW/dvA=="
|
|
||||||
},
|
|
||||||
"ILRepack.FullAuto": {
|
"ILRepack.FullAuto": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[1.6.0, )",
|
"requested": "[1.6.0, )",
|
||||||
@@ -186,9 +174,9 @@
|
|||||||
},
|
},
|
||||||
"Microsoft.Extensions.ObjectPool": {
|
"Microsoft.Extensions.ObjectPool": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[9.0.1, )",
|
"requested": "[9.0.4, )",
|
||||||
"resolved": "9.0.1",
|
"resolved": "9.0.4",
|
||||||
"contentHash": "r64veU9uYILp6pYqfo3qzRab8zLMALvXZgT4VRY79tXMLu8X79uTlJ6nqPLtPIVhfCPXycRh8ILyFz/gGBDQdQ=="
|
"contentHash": "G7p1k2xVZ+2aVANz0JdSiafr+AHDHeS1kF8+Y0ABbIsByd0erOL59IDXBs9vcdJf3pPV/murO0mbtr4k40QxWw=="
|
||||||
},
|
},
|
||||||
"Microsoft.SourceLink.GitHub": {
|
"Microsoft.SourceLink.GitHub": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
@@ -202,9 +190,9 @@
|
|||||||
},
|
},
|
||||||
"Open.ChannelExtensions": {
|
"Open.ChannelExtensions": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[9.0.0, )",
|
"requested": "[9.1.0, )",
|
||||||
"resolved": "9.0.0",
|
"resolved": "9.1.0",
|
||||||
"contentHash": "DP+l5S6G46wcuY4I4kNXE+RDOmJr0DKuMienOdt0mMBN9z7vmLSC8YQbqCyb9i9LNjXj1tgCx5LyitJiRr/v7g=="
|
"contentHash": "D6c24vMGy1oZ06vmkD2/FNzWHK7ZIihuv2spDgYEeaUp+eobrILQnrNQKRoASFXD4JGfZ7nfvTM0e+AX79dt8Q=="
|
||||||
},
|
},
|
||||||
"Polly": {
|
"Polly": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
@@ -241,9 +229,9 @@
|
|||||||
},
|
},
|
||||||
"System.Threading.Channels": {
|
"System.Threading.Channels": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[9.0.2, )",
|
"requested": "[9.0.4, )",
|
||||||
"resolved": "9.0.2",
|
"resolved": "9.0.4",
|
||||||
"contentHash": "pUmqkuBS9OxWHOlfNad09Oxc8gRbxgN9UQtsqHPst4jfcgZRxQetNcsT2oe+VnUpEFAtBy1FZcJZiOscrBmA7g=="
|
"contentHash": "4qBn2H6/aXBpE/Pm3wY5yusY/pEvQz99NlWHrTUji0qCmOdbhhjaALcpmbfW2ksxlPM6i6S+QFLkpOQdyfeKYQ=="
|
||||||
},
|
},
|
||||||
"ILRepack": {
|
"ILRepack": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Speckle.Sdk.Api.GraphQL;
|
using Speckle.Sdk.Api.GraphQL;
|
||||||
|
using Speckle.Sdk.Api.GraphQL.Models;
|
||||||
|
|
||||||
namespace Speckle.Sdk.Api;
|
namespace Speckle.Sdk.Api;
|
||||||
|
|
||||||
@@ -96,3 +97,15 @@ public sealed class SpeckleGraphQLInvalidQueryException : SpeckleGraphQLExceptio
|
|||||||
public SpeckleGraphQLInvalidQueryException(string? message, Exception? innerException)
|
public SpeckleGraphQLInvalidQueryException(string? message, Exception? innerException)
|
||||||
: base(message, innerException) { }
|
: base(message, innerException) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <seealso cref="PermissionCheckResult"/>
|
||||||
|
public sealed class WorkspacePermissionException : SpeckleGraphQLException
|
||||||
|
{
|
||||||
|
public WorkspacePermissionException() { }
|
||||||
|
|
||||||
|
public WorkspacePermissionException(string? message)
|
||||||
|
: base(message) { }
|
||||||
|
|
||||||
|
public WorkspacePermissionException(string? message, Exception? innerException)
|
||||||
|
: base(message, innerException) { }
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using System.Reflection;
|
|||||||
using GraphQL;
|
using GraphQL;
|
||||||
using GraphQL.Client.Http;
|
using GraphQL.Client.Http;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Speckle.InterfaceGenerator;
|
||||||
using Speckle.Newtonsoft.Json;
|
using Speckle.Newtonsoft.Json;
|
||||||
using Speckle.Newtonsoft.Json.Serialization;
|
using Speckle.Newtonsoft.Json.Serialization;
|
||||||
using Speckle.Sdk.Api.GraphQL;
|
using Speckle.Sdk.Api.GraphQL;
|
||||||
@@ -16,8 +17,14 @@ using Speckle.Sdk.Logging;
|
|||||||
|
|
||||||
namespace Speckle.Sdk.Api;
|
namespace Speckle.Sdk.Api;
|
||||||
|
|
||||||
|
public partial interface IClient : IDisposable
|
||||||
|
{
|
||||||
|
GraphQLHttpClient GQLClient { get; }
|
||||||
|
}
|
||||||
|
|
||||||
[SuppressMessage("Maintainability", "CA1506:Avoid excessive class coupling", Justification = "Class needs refactor")]
|
[SuppressMessage("Maintainability", "CA1506:Avoid excessive class coupling", Justification = "Class needs refactor")]
|
||||||
public sealed class Client : ISpeckleGraphQLClient, IDisposable
|
[GenerateAutoInterface]
|
||||||
|
public sealed class Client : ISpeckleGraphQLClient, IClient
|
||||||
{
|
{
|
||||||
private readonly ILogger<Client> _logger;
|
private readonly ILogger<Client> _logger;
|
||||||
private readonly ISdkActivityFactory _activityFactory;
|
private readonly ISdkActivityFactory _activityFactory;
|
||||||
@@ -29,6 +36,7 @@ public sealed class Client : ISpeckleGraphQLClient, IDisposable
|
|||||||
public ProjectInviteResource ProjectInvite { get; }
|
public ProjectInviteResource ProjectInvite { get; }
|
||||||
public CommentResource Comment { get; }
|
public CommentResource Comment { get; }
|
||||||
public SubscriptionResource Subscription { get; }
|
public SubscriptionResource Subscription { get; }
|
||||||
|
public WorkspaceResource Workspace { get; }
|
||||||
|
|
||||||
public Uri ServerUrl => new(Account.serverInfo.url);
|
public Uri ServerUrl => new(Account.serverInfo.url);
|
||||||
|
|
||||||
@@ -37,6 +45,7 @@ public sealed class Client : ISpeckleGraphQLClient, IDisposable
|
|||||||
|
|
||||||
private HttpClient HttpClient { get; }
|
private HttpClient HttpClient { get; }
|
||||||
|
|
||||||
|
[AutoInterfaceIgnore]
|
||||||
public GraphQLHttpClient GQLClient { get; }
|
public GraphQLHttpClient GQLClient { get; }
|
||||||
|
|
||||||
/// <param name="account"></param>
|
/// <param name="account"></param>
|
||||||
@@ -61,12 +70,14 @@ public sealed class Client : ISpeckleGraphQLClient, IDisposable
|
|||||||
ProjectInvite = new(this);
|
ProjectInvite = new(this);
|
||||||
Comment = new(this);
|
Comment = new(this);
|
||||||
Subscription = new(this);
|
Subscription = new(this);
|
||||||
|
Workspace = new(this);
|
||||||
|
|
||||||
HttpClient = CreateHttpClient(application, speckleHttp, account);
|
HttpClient = CreateHttpClient(application, speckleHttp, account);
|
||||||
|
|
||||||
GQLClient = CreateGraphQLClient(account, HttpClient);
|
GQLClient = CreateGraphQLClient(account, HttpClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[AutoInterfaceIgnore]
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -132,6 +143,7 @@ public sealed class Client : ISpeckleGraphQLClient, IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[AutoInterfaceIgnore]
|
||||||
IDisposable ISpeckleGraphQLClient.SubscribeTo<T>(GraphQLRequest request, Action<object, T> callback) =>
|
IDisposable ISpeckleGraphQLClient.SubscribeTo<T>(GraphQLRequest request, Action<object, T> callback) =>
|
||||||
SubscribeTo(request, callback);
|
SubscribeTo(request, callback);
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,6 @@ public class ClientFactory(
|
|||||||
ISpeckleHttp speckleHttp
|
ISpeckleHttp speckleHttp
|
||||||
) : IClientFactory
|
) : IClientFactory
|
||||||
{
|
{
|
||||||
public Client Create(Account account) =>
|
public IClient Create(Account account) =>
|
||||||
new(loggerFactory.CreateLogger<Client>(), activityFactory, application, speckleHttp, account);
|
new Client(loggerFactory.CreateLogger<Client>(), activityFactory, application, speckleHttp, account);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
public enum ProjectVisibility
|
public enum ProjectVisibility
|
||||||
{
|
{
|
||||||
Private,
|
Private = 0,
|
||||||
Public,
|
|
||||||
Unlisted,
|
[Obsolete("Use Unlisted instead", true)]
|
||||||
|
Public = 1,
|
||||||
|
Unlisted = 2,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,13 @@ public sealed record ProjectCommentsFilter(bool? includeArchived, bool? loadedVe
|
|||||||
|
|
||||||
public sealed record ProjectCreateInput(string? name, string? description, ProjectVisibility? visibility);
|
public sealed record ProjectCreateInput(string? name, string? description, ProjectVisibility? visibility);
|
||||||
|
|
||||||
|
public sealed record WorkspaceProjectCreateInput(
|
||||||
|
string? name,
|
||||||
|
string? description,
|
||||||
|
ProjectVisibility? visibility,
|
||||||
|
string workspaceId
|
||||||
|
);
|
||||||
|
|
||||||
public sealed record ProjectInviteCreateInput(string? email, string? role, string? serverRole, string? userId);
|
public sealed record ProjectInviteCreateInput(string? email, string? role, string? serverRole, string? userId);
|
||||||
|
|
||||||
public sealed record ProjectInviteUseInput(bool accept, string projectId, string token);
|
public sealed record ProjectInviteUseInput(bool accept, string projectId, string token);
|
||||||
@@ -29,4 +36,4 @@ public sealed record ProjectUpdateInput(
|
|||||||
|
|
||||||
public sealed record ProjectUpdateRoleInput(string userId, string projectId, string? role);
|
public sealed record ProjectUpdateRoleInput(string userId, string projectId, string? role);
|
||||||
|
|
||||||
public sealed record UserProjectsFilter(string search, IReadOnlyList<string>? onlyWithRoles = null);
|
public sealed record WorkspaceProjectsFilter(string? search, bool? withProjectRoleOnly);
|
||||||
|
|||||||
@@ -6,3 +6,13 @@ public sealed record UserUpdateInput(
|
|||||||
string? company = null,
|
string? company = null,
|
||||||
string? name = null
|
string? name = null
|
||||||
);
|
);
|
||||||
|
|
||||||
|
public sealed record UserProjectsFilter(
|
||||||
|
string? search = null,
|
||||||
|
IReadOnlyList<string>? onlyWithRoles = null,
|
||||||
|
string? workspaceId = null,
|
||||||
|
bool? personalOnly = null,
|
||||||
|
bool? includeImplicitAccess = null
|
||||||
|
);
|
||||||
|
|
||||||
|
public sealed record UserWorkspacesFilter(string? search);
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
namespace Speckle.Sdk.Api.GraphQL.Models;
|
||||||
|
|
||||||
|
public sealed class PermissionCheckResult
|
||||||
|
{
|
||||||
|
public bool authorized { get; init; }
|
||||||
|
public string code { get; init; }
|
||||||
|
public string message { get; init; }
|
||||||
|
|
||||||
|
/// <exception cref="SpeckleException">Throws when <see cref="PermissionCheckResult.authorized"/> is <see langword="false"/></exception>
|
||||||
|
public void EnsureAuthorised()
|
||||||
|
{
|
||||||
|
if (!authorized)
|
||||||
|
{
|
||||||
|
throw new WorkspacePermissionException(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Speckle.Sdk.Api.GraphQL.Models;
|
||||||
|
|
||||||
|
public sealed class ProjectPermissionChecks
|
||||||
|
{
|
||||||
|
public PermissionCheckResult canCreateModel { get; init; }
|
||||||
|
public PermissionCheckResult canDelete { get; init; }
|
||||||
|
}
|
||||||
@@ -7,6 +7,8 @@ public sealed class Version
|
|||||||
public string id { get; init; }
|
public string id { get; init; }
|
||||||
public string? message { get; init; }
|
public string? message { get; init; }
|
||||||
public Uri previewUrl { get; init; }
|
public Uri previewUrl { get; init; }
|
||||||
public string referencedObject { get; init; }
|
|
||||||
|
/// <remarks>May be <see langword="null"/> if workspaces version history limit has been exceeded</remarks>
|
||||||
|
public string? referencedObject { get; init; }
|
||||||
public string? sourceApplication { get; init; }
|
public string? sourceApplication { get; init; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
namespace Speckle.Sdk.Api.GraphQL.Models;
|
||||||
|
|
||||||
|
public sealed class Workspace
|
||||||
|
{
|
||||||
|
public string id { get; init; }
|
||||||
|
public string name { get; init; }
|
||||||
|
public string role { get; init; }
|
||||||
|
public string slug { get; init; }
|
||||||
|
public string? description { get; init; }
|
||||||
|
public WorkspacePermissionChecks permissions { get; init; }
|
||||||
|
public WorkspaceCreationState? creationState { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class WorkspaceCreationState
|
||||||
|
{
|
||||||
|
public bool completed { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class WorkspacePermissionChecks
|
||||||
|
{
|
||||||
|
public PermissionCheckResult canCreateProject { get; init; }
|
||||||
|
}
|
||||||
@@ -85,6 +85,7 @@ public sealed class ActiveUserResource
|
|||||||
/// <param name="cancellationToken"></param>
|
/// <param name="cancellationToken"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
/// <inheritdoc cref="ISpeckleGraphQLClient.ExecuteGraphQLRequest{T}"/>
|
/// <inheritdoc cref="ISpeckleGraphQLClient.ExecuteGraphQLRequest{T}"/>
|
||||||
|
/// <exception cref="SpeckleException">The ActiveUser could not be found (e.g. the client is not authenticated)</exception>
|
||||||
public async Task<ResourceCollection<Project>> GetProjects(
|
public async Task<ResourceCollection<Project>> GetProjects(
|
||||||
int limit = ServerLimits.DEFAULT_PAGINATION_REQUEST,
|
int limit = ServerLimits.DEFAULT_PAGINATION_REQUEST,
|
||||||
string? cursor = null,
|
string? cursor = null,
|
||||||
@@ -135,7 +136,7 @@ public sealed class ActiveUserResource
|
|||||||
|
|
||||||
if (response.data 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.data.data;
|
return response.data.data;
|
||||||
@@ -199,8 +200,165 @@ public sealed class ActiveUserResource
|
|||||||
return response.data.data;
|
return response.data.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="GetProjectInvites"/>
|
/// <param name="cancellationToken"></param>
|
||||||
[Obsolete($"Renamed to {nameof(GetProjectInvites)}")]
|
/// <returns></returns>
|
||||||
public async Task<List<PendingStreamCollaborator>> ProjectInvites(CancellationToken cancellationToken = default) =>
|
/// <inheritdoc cref="ISpeckleGraphQLClient.ExecuteGraphQLRequest{T}"/>
|
||||||
await GetProjectInvites(cancellationToken).ConfigureAwait(false);
|
/// <exception cref="SpeckleException">The ActiveUser could not be found (e.g. the client is not authenticated)</exception>
|
||||||
|
public async Task<PermissionCheckResult> CanCreatePersonalProjects(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
//language=graphql
|
||||||
|
const string QUERY = """
|
||||||
|
query CanCreatePersonalProject {
|
||||||
|
data:activeUser {
|
||||||
|
data:permissions {
|
||||||
|
data:canCreatePersonalProject {
|
||||||
|
authorized
|
||||||
|
code
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
var request = new GraphQLRequest { Query = QUERY };
|
||||||
|
|
||||||
|
var response = await _client
|
||||||
|
.ExecuteGraphQLRequest<NullableResponse<RequiredResponse<RequiredResponse<PermissionCheckResult>>?>>(
|
||||||
|
request,
|
||||||
|
cancellationToken
|
||||||
|
)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (response.data is null)
|
||||||
|
{
|
||||||
|
throw new SpeckleException("GraphQL response indicated that the ActiveUser could not be found");
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.data.data.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <remarks>This feature is only available on Workspace enabled servers (e.g. app.speckle.systems)</remarks>
|
||||||
|
/// <param name="limit"></param>
|
||||||
|
/// <param name="cursor"></param>
|
||||||
|
/// <param name="filter"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <inheritdoc cref="ISpeckleGraphQLClient.ExecuteGraphQLRequest{T}"/>
|
||||||
|
/// <exception cref="SpeckleException">The ActiveUser could not be found (e.g. the client is not authenticated)</exception>
|
||||||
|
public async Task<ResourceCollection<Workspace>> GetWorkspaces(
|
||||||
|
int limit = ServerLimits.DEFAULT_PAGINATION_REQUEST,
|
||||||
|
string? cursor = null,
|
||||||
|
UserWorkspacesFilter? filter = null,
|
||||||
|
CancellationToken cancellationToken = default
|
||||||
|
)
|
||||||
|
{
|
||||||
|
//language=graphql
|
||||||
|
const string QUERY = """
|
||||||
|
query ActiveUser($limit: Int!, $cursor: String, $filter: UserWorkspacesFilter) {
|
||||||
|
data:activeUser {
|
||||||
|
data:workspaces(limit: $limit, cursor: $cursor, filter: $filter) {
|
||||||
|
cursor
|
||||||
|
totalCount
|
||||||
|
items {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
role
|
||||||
|
slug
|
||||||
|
logo
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
readOnly
|
||||||
|
description
|
||||||
|
creationState
|
||||||
|
{
|
||||||
|
completed
|
||||||
|
}
|
||||||
|
permissions {
|
||||||
|
canCreateProject {
|
||||||
|
authorized
|
||||||
|
code
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
var request = new GraphQLRequest
|
||||||
|
{
|
||||||
|
Query = QUERY,
|
||||||
|
Variables = new
|
||||||
|
{
|
||||||
|
limit,
|
||||||
|
cursor,
|
||||||
|
filter,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var response = await _client
|
||||||
|
.ExecuteGraphQLRequest<NullableResponse<RequiredResponse<ResourceCollection<Workspace>>?>>(
|
||||||
|
request,
|
||||||
|
cancellationToken
|
||||||
|
)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (response.data is null)
|
||||||
|
{
|
||||||
|
throw new SpeckleException("GraphQL response indicated that the ActiveUser could not be found");
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.data.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <inheritdoc cref="ISpeckleGraphQLClient.ExecuteGraphQLRequest{T}"/>
|
||||||
|
/// <exception cref="SpeckleException">The ActiveUser could not be found (e.g. the client is not authenticated)</exception>
|
||||||
|
public async Task<Workspace?> GetActiveWorkspace(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
//language=graphql
|
||||||
|
const string QUERY = """
|
||||||
|
query ActiveUser {
|
||||||
|
data:activeUser {
|
||||||
|
data:activeWorkspace {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
role
|
||||||
|
slug
|
||||||
|
logo
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
readOnly
|
||||||
|
description
|
||||||
|
creationState
|
||||||
|
{
|
||||||
|
completed
|
||||||
|
}
|
||||||
|
permissions {
|
||||||
|
canCreateProject {
|
||||||
|
authorized
|
||||||
|
code
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
var request = new GraphQLRequest { Query = QUERY };
|
||||||
|
|
||||||
|
var response = await _client
|
||||||
|
.ExecuteGraphQLRequest<NullableResponse<NullableResponse<Workspace?>?>>(request, cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (response.data is null)
|
||||||
|
{
|
||||||
|
throw new SpeckleException("GraphQL response indicated that the ActiveUser could not be found");
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.data.data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,42 @@ public sealed class ProjectResource
|
|||||||
return response.data;
|
return response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <param name="projectId"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <inheritdoc cref="ISpeckleGraphQLClient.ExecuteGraphQLRequest{T}"/>
|
||||||
|
public async Task<ProjectPermissionChecks> GetPermissions(
|
||||||
|
string projectId,
|
||||||
|
CancellationToken cancellationToken = default
|
||||||
|
)
|
||||||
|
{
|
||||||
|
//language=graphql
|
||||||
|
const string QUERY = """
|
||||||
|
query Project($projectId: String!) {
|
||||||
|
data:project(id: $projectId) {
|
||||||
|
data:permissions {
|
||||||
|
canCreateModel {
|
||||||
|
authorized
|
||||||
|
code
|
||||||
|
message
|
||||||
|
}
|
||||||
|
canDelete {
|
||||||
|
authorized
|
||||||
|
code
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
GraphQLRequest request = new() { Query = QUERY, Variables = new { projectId } };
|
||||||
|
|
||||||
|
var response = await _client
|
||||||
|
.ExecuteGraphQLRequest<RequiredResponse<RequiredResponse<ProjectPermissionChecks>>>(request, cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
return response.data.data;
|
||||||
|
}
|
||||||
|
|
||||||
/// <param name="projectId"></param>
|
/// <param name="projectId"></param>
|
||||||
/// <param name="modelsLimit">Max number of models to fetch</param>
|
/// <param name="modelsLimit">Max number of models to fetch</param>
|
||||||
/// <param name="modelsCursor">Optional cursor for pagination</param>
|
/// <param name="modelsCursor">Optional cursor for pagination</param>
|
||||||
@@ -186,6 +222,10 @@ public sealed class ProjectResource
|
|||||||
return response.data;
|
return response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a non-workspace project (aka Personal Project)<br/>
|
||||||
|
/// See <see cref="ActiveUserResource.CanCreatePersonalProjects"/> to see if the user has permission
|
||||||
|
/// </summary>
|
||||||
/// <param name="input"></param>
|
/// <param name="input"></param>
|
||||||
/// <param name="cancellationToken"></param>
|
/// <param name="cancellationToken"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
@@ -219,6 +259,49 @@ public sealed class ProjectResource
|
|||||||
return response.data.data;
|
return response.data.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a workspace project.<br/>
|
||||||
|
/// This feature is only supported on Workspace Enabled Servers (e.g. app.speckle.systems)
|
||||||
|
/// See <see cref="ActiveUserResource.CanCreatePersonalProjects"/> to see if the user has permission
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <inheritdoc cref="ISpeckleGraphQLClient.ExecuteGraphQLRequest{T}"/>
|
||||||
|
public async Task<Project> CreateInWorkspace(
|
||||||
|
WorkspaceProjectCreateInput input,
|
||||||
|
CancellationToken cancellationToken = default
|
||||||
|
)
|
||||||
|
{
|
||||||
|
//language=graphql
|
||||||
|
const string QUERY = """
|
||||||
|
mutation WorkspaceProjectCreate($input: WorkspaceProjectCreateInput!) {
|
||||||
|
data:workspaceMutations {
|
||||||
|
data:projects {
|
||||||
|
data:create(input: $input) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
description
|
||||||
|
visibility
|
||||||
|
allowPublicComments
|
||||||
|
role
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
sourceApps
|
||||||
|
workspaceId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
GraphQLRequest request = new() { Query = QUERY, Variables = new { input } };
|
||||||
|
|
||||||
|
var response = await _client
|
||||||
|
.ExecuteGraphQLRequest<RequiredResponse<RequiredResponse<RequiredResponse<Project>>>>(request, cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
return response.data.data.data;
|
||||||
|
}
|
||||||
|
|
||||||
/// <param name="input"></param>
|
/// <param name="input"></param>
|
||||||
/// <param name="cancellationToken"></param>
|
/// <param name="cancellationToken"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
|
|||||||
@@ -0,0 +1,120 @@
|
|||||||
|
using GraphQL;
|
||||||
|
using Speckle.Sdk.Api.GraphQL.Inputs;
|
||||||
|
using Speckle.Sdk.Api.GraphQL.Models;
|
||||||
|
using Speckle.Sdk.Api.GraphQL.Models.Responses;
|
||||||
|
|
||||||
|
namespace Speckle.Sdk.Api.GraphQL.Resources;
|
||||||
|
|
||||||
|
public sealed class WorkspaceResource
|
||||||
|
{
|
||||||
|
private readonly ISpeckleGraphQLClient _client;
|
||||||
|
|
||||||
|
internal WorkspaceResource(ISpeckleGraphQLClient client)
|
||||||
|
{
|
||||||
|
_client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <param name="workspaceId"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <inheritdoc cref="ISpeckleGraphQLClient.ExecuteGraphQLRequest{T}"/>
|
||||||
|
public async Task<Workspace> Get(string workspaceId, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
//language=graphql
|
||||||
|
const string QUERY = """
|
||||||
|
query WorkspaceGet($workspaceId: String!) {
|
||||||
|
data:workspace(id: $workspaceId) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
role
|
||||||
|
slug
|
||||||
|
logo
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
readOnly
|
||||||
|
description
|
||||||
|
creationState
|
||||||
|
{
|
||||||
|
completed
|
||||||
|
}
|
||||||
|
permissions {
|
||||||
|
canCreateProject {
|
||||||
|
authorized
|
||||||
|
code
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
var request = new GraphQLRequest { Query = QUERY, Variables = new { workspaceId } };
|
||||||
|
|
||||||
|
var response = await _client
|
||||||
|
.ExecuteGraphQLRequest<RequiredResponse<RequiredResponse<Workspace>>>(request, cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
return response.data.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <param name="workspaceId"></param>
|
||||||
|
/// <param name="limit">Max number of projects to fetch</param>
|
||||||
|
/// <param name="cursor">Optional cursor for pagination</param>
|
||||||
|
/// <param name="filter"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <inheritdoc cref="ISpeckleGraphQLClient.ExecuteGraphQLRequest{T}"/>
|
||||||
|
/// <see cref="Get"/>
|
||||||
|
public async Task<ResourceCollection<Project>> GetProjects(
|
||||||
|
string workspaceId,
|
||||||
|
int limit = ServerLimits.DEFAULT_PAGINATION_REQUEST,
|
||||||
|
string? cursor = null,
|
||||||
|
WorkspaceProjectsFilter? filter = null,
|
||||||
|
CancellationToken cancellationToken = default
|
||||||
|
)
|
||||||
|
{
|
||||||
|
//language=graphql
|
||||||
|
const string QUERY = """
|
||||||
|
query Workspace($workspaceId: String!, $limit: Int!, $cursor: String, $filter: WorkspaceProjectsFilter) {
|
||||||
|
data:workspace(id: $workspaceId) {
|
||||||
|
data:projects(limit: $limit, cursor: $cursor, filter: $filter) {
|
||||||
|
cursor
|
||||||
|
items {
|
||||||
|
allowPublicComments
|
||||||
|
createdAt
|
||||||
|
description
|
||||||
|
id
|
||||||
|
name
|
||||||
|
role
|
||||||
|
sourceApps
|
||||||
|
updatedAt
|
||||||
|
visibility
|
||||||
|
workspaceId
|
||||||
|
}
|
||||||
|
totalCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
var request = new GraphQLRequest
|
||||||
|
{
|
||||||
|
Query = QUERY,
|
||||||
|
Variables = new
|
||||||
|
{
|
||||||
|
workspaceId,
|
||||||
|
limit,
|
||||||
|
cursor,
|
||||||
|
filter,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var response = await _client
|
||||||
|
.ExecuteGraphQLRequest<RequiredResponse<RequiredResponse<ResourceCollection<Project>>>>(
|
||||||
|
request,
|
||||||
|
cancellationToken
|
||||||
|
)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
return response.data.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,7 +29,7 @@ public partial class Operations
|
|||||||
metricsFactory.CreateCounter<long>("Receive").Add(1);
|
metricsFactory.CreateCounter<long>("Receive").Add(1);
|
||||||
|
|
||||||
receiveActivity?.SetTag("objectId", objectId);
|
receiveActivity?.SetTag("objectId", objectId);
|
||||||
var process = serializeProcessFactory.CreateDeserializeProcess(
|
var process = deserializeProcessFactory.CreateDeserializeProcess(
|
||||||
url,
|
url,
|
||||||
streamId,
|
streamId,
|
||||||
authorizationToken,
|
authorizationToken,
|
||||||
|
|||||||
@@ -15,5 +15,6 @@ public partial class Operations(
|
|||||||
ILogger<Operations> logger,
|
ILogger<Operations> logger,
|
||||||
ISdkActivityFactory activityFactory,
|
ISdkActivityFactory activityFactory,
|
||||||
ISdkMetricsFactory metricsFactory,
|
ISdkMetricsFactory metricsFactory,
|
||||||
ISerializeProcessFactory serializeProcessFactory
|
ISerializeProcessFactory serializeProcessFactory,
|
||||||
|
IDeserializeProcessFactory deserializeProcessFactory
|
||||||
) : IOperations;
|
) : IOperations;
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
[assembly: InternalsVisibleTo("Speckle.Objects.Tests.Unit")]
|
||||||
|
[assembly: InternalsVisibleTo("Speckle.Sdk.Tests.Performance")]
|
||||||
@@ -399,8 +399,9 @@ public sealed class AccountManager(
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Refetches user and server info for each account
|
/// Refetches user and server info for each account
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="app"> It is defaultAppId in the server. By default it is "sca" to not break existing parts that this function involves.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task UpdateAccounts(CancellationToken ct = default)
|
public async Task UpdateAccounts(CancellationToken ct = default, string app = "sca")
|
||||||
{
|
{
|
||||||
// need to ToList() the GetAccounts call or the UpdateObject call at the end of this method
|
// 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
|
// will not work because sqlite does not support concurrent db calls
|
||||||
@@ -415,16 +416,9 @@ public sealed class AccountManager(
|
|||||||
//TODO: once we get a token expired exception from the server use that instead
|
//TODO: once we get a token expired exception from the server use that instead
|
||||||
if (userServerInfo?.activeUser == null || userServerInfo.serverInfo == null)
|
if (userServerInfo?.activeUser == null || userServerInfo.serverInfo == null)
|
||||||
{
|
{
|
||||||
var tokenResponse = await GetRefreshedToken(account.refreshToken, url).ConfigureAwait(false);
|
// We were initially was handling refresh token here bc quite a while ago server was returning null
|
||||||
userServerInfo = await GetUserServerInfo(tokenResponse.token, url, ct).ConfigureAwait(false);
|
// for activeUser and serverInfo instead of throwing exception. In short, our logic moved into catch block to cover both.
|
||||||
|
throw new SpeckleException("Token is expired");
|
||||||
if (userServerInfo?.activeUser == null || userServerInfo.serverInfo == null)
|
|
||||||
{
|
|
||||||
throw new SpeckleException("Could not refresh token");
|
|
||||||
}
|
|
||||||
|
|
||||||
account.token = tokenResponse.token;
|
|
||||||
account.refreshToken = tokenResponse.refreshToken;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
account.isOnline = true;
|
account.isOnline = true;
|
||||||
@@ -437,11 +431,32 @@ public sealed class AccountManager(
|
|||||||
}
|
}
|
||||||
catch (Exception ex) when (!ex.IsFatal())
|
catch (Exception ex) when (!ex.IsFatal())
|
||||||
{
|
{
|
||||||
account.isOnline = false;
|
await RefreshAndSetAccountToken(account, app).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
ct.ThrowIfCancellationRequested();
|
ct.ThrowIfCancellationRequested();
|
||||||
_accountStorage.SaveObject(account.id, JsonConvert.SerializeObject(account));
|
_accountStorage.UpdateObject(account.id, JsonConvert.SerializeObject(account));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Mutates the account with new tokens.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="account"></param>
|
||||||
|
/// <param name="app"></param>
|
||||||
|
private async Task RefreshAndSetAccountToken(Account account, string app)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Uri url = new(account.serverInfo.url);
|
||||||
|
var tokenResponse = await GetRefreshedToken(account.refreshToken, url, app).ConfigureAwait(false);
|
||||||
|
account.token = tokenResponse.token;
|
||||||
|
account.refreshToken = tokenResponse.refreshToken;
|
||||||
|
account.isOnline = true;
|
||||||
|
}
|
||||||
|
catch (Exception ex) when (!ex.IsFatal())
|
||||||
|
{
|
||||||
|
account.isOnline = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -766,7 +781,7 @@ public sealed class AccountManager(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<TokenExchangeResponse> GetRefreshedToken(string refreshToken, Uri server)
|
private async Task<TokenExchangeResponse> GetRefreshedToken(string refreshToken, Uri server, string app = "sca")
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -774,8 +789,8 @@ public sealed class AccountManager(
|
|||||||
|
|
||||||
var body = new
|
var body = new
|
||||||
{
|
{
|
||||||
appId = "sca",
|
appId = app,
|
||||||
appSecret = "sca",
|
appSecret = app,
|
||||||
refreshToken,
|
refreshToken,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Speckle.Newtonsoft.Json;
|
using Speckle.Newtonsoft.Json;
|
||||||
using Speckle.Sdk.Dependencies;
|
using Speckle.Sdk.Dependencies;
|
||||||
@@ -5,6 +6,8 @@ using Speckle.Sdk.Serialisation;
|
|||||||
|
|
||||||
namespace Speckle.Sdk.Helpers;
|
namespace Speckle.Sdk.Helpers;
|
||||||
|
|
||||||
|
//just a wrapper around a lot of newtonsoft overloads
|
||||||
|
[ExcludeFromCodeCoverage]
|
||||||
public sealed class SerializerIdWriter : JsonWriter
|
public sealed class SerializerIdWriter : JsonWriter
|
||||||
{
|
{
|
||||||
private readonly JsonWriter _jsonWriter;
|
private readonly JsonWriter _jsonWriter;
|
||||||
|
|||||||
@@ -15,10 +15,15 @@ public class SpeckleHttp(ILogger<SpeckleHttp> logger, ISpeckleHttpClientHandlerF
|
|||||||
/// <param name="uri">The URI that should be pinged</param>
|
/// <param name="uri">The URI that should be pinged</param>
|
||||||
/// <exception cref="System.Net.Http.HttpRequestException">Request to <paramref name="uri"/> failed</exception>
|
/// <exception cref="System.Net.Http.HttpRequestException">Request to <paramref name="uri"/> failed</exception>
|
||||||
public async Task<HttpResponseMessage> HttpPing(Uri uri)
|
public async Task<HttpResponseMessage> HttpPing(Uri uri)
|
||||||
|
{
|
||||||
|
using var httpClient = CreateHttpClient();
|
||||||
|
return await HttpPing(uri, httpClient).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<HttpResponseMessage> HttpPing(Uri uri, HttpClient httpClient)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var httpClient = CreateHttpClient();
|
|
||||||
HttpResponseMessage response = await httpClient.GetAsync(uri).ConfigureAwait(false);
|
HttpResponseMessage response = await httpClient.GetAsync(uri).ConfigureAwait(false);
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
logger.LogInformation("Successfully pinged {uri}", uri);
|
logger.LogInformation("Successfully pinged {uri}", uri);
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
namespace Speckle.Sdk.Host;
|
|
||||||
|
|
||||||
[AttributeUsage(AttributeTargets.Constructor)]
|
|
||||||
public sealed class SchemaInfoAttribute : Attribute
|
|
||||||
{
|
|
||||||
public SchemaInfoAttribute(string name, string description)
|
|
||||||
: this(name, description, null, null) { }
|
|
||||||
|
|
||||||
public SchemaInfoAttribute(string name, string description, string? category, string? subcategory)
|
|
||||||
{
|
|
||||||
Name = name;
|
|
||||||
Description = description;
|
|
||||||
Category = category;
|
|
||||||
Subcategory = subcategory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string? Subcategory { get; }
|
|
||||||
|
|
||||||
public string? Category { get; }
|
|
||||||
|
|
||||||
public string Description { get; }
|
|
||||||
|
|
||||||
public string Name { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
[AttributeUsage(AttributeTargets.Constructor)]
|
|
||||||
public sealed class SchemaDeprecatedAttribute : Attribute { }
|
|
||||||
|
|
||||||
[AttributeUsage(AttributeTargets.Parameter)]
|
|
||||||
public sealed class SchemaParamInfoAttribute : Attribute
|
|
||||||
{
|
|
||||||
public SchemaParamInfoAttribute(string description)
|
|
||||||
{
|
|
||||||
Description = description;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Description { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Used to indicate which is the main input parameter of the schema builder component. Schema info will be attached to this object.
|
|
||||||
/// </summary>
|
|
||||||
[AttributeUsage(AttributeTargets.Parameter)]
|
|
||||||
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...
|
|
||||||
/// <summary>
|
|
||||||
/// Used to ignore properties from expand objects etc
|
|
||||||
/// </summary>
|
|
||||||
[AttributeUsage(AttributeTargets.Property)]
|
|
||||||
public sealed class SchemaIgnoreAttribute : Attribute { }
|
|
||||||
|
|
||||||
[AttributeUsage(AttributeTargets.Method)]
|
|
||||||
public sealed class SchemaComputedAttribute : Attribute
|
|
||||||
{
|
|
||||||
public SchemaComputedAttribute(string name)
|
|
||||||
{
|
|
||||||
Name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Name { get; }
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
namespace Speckle.Sdk.Host;
|
|
||||||
|
|
||||||
public enum HostAppVersion
|
|
||||||
{
|
|
||||||
v3,
|
|
||||||
v6,
|
|
||||||
v7,
|
|
||||||
v8,
|
|
||||||
v2019,
|
|
||||||
v2020,
|
|
||||||
v2021,
|
|
||||||
v2022,
|
|
||||||
v2023,
|
|
||||||
v2024,
|
|
||||||
v2025,
|
|
||||||
v21,
|
|
||||||
v22,
|
|
||||||
v25,
|
|
||||||
v26,
|
|
||||||
v715,
|
|
||||||
v716,
|
|
||||||
v717,
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
namespace Speckle.Sdk.Host;
|
|
||||||
|
|
||||||
public readonly struct HostApplication
|
|
||||||
{
|
|
||||||
public string Name { get; }
|
|
||||||
public string Slug { get; }
|
|
||||||
|
|
||||||
public HostApplication(string name, string slug)
|
|
||||||
{
|
|
||||||
Name = name;
|
|
||||||
Slug = slug;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,220 +0,0 @@
|
|||||||
using System.Diagnostics.Contracts;
|
|
||||||
|
|
||||||
namespace Speckle.Sdk.Host;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// List of Host Applications - their slugs should match our ghost tags and ci/cd slugs
|
|
||||||
/// </summary>
|
|
||||||
public static class HostApplications
|
|
||||||
{
|
|
||||||
public static string GetVersion(HostAppVersion version) => version.ToString().TrimStart('v');
|
|
||||||
|
|
||||||
public static readonly HostApplication Rhino = new("Rhino", "rhino"),
|
|
||||||
Grasshopper = new("Grasshopper", "grasshopper"),
|
|
||||||
Revit = new("Revit", "revit"),
|
|
||||||
Dynamo = new("Dynamo", "dynamo"),
|
|
||||||
Unity = new("Unity", "unity"),
|
|
||||||
GSA = new("GSA", "gsa"),
|
|
||||||
Civil = new("Civil 3D", "civil3d"),
|
|
||||||
Civil3D = new("Civil 3D", "civil3d"),
|
|
||||||
AutoCAD = new("AutoCAD", "autocad"),
|
|
||||||
MicroStation = new("MicroStation", "microstation"),
|
|
||||||
OpenRoads = new("OpenRoads", "openroads"),
|
|
||||||
OpenRail = new("OpenRail", "openrail"),
|
|
||||||
OpenBuildings = new("OpenBuildings", "openbuildings"),
|
|
||||||
ETABS = new("ETABS", "etabs"),
|
|
||||||
SAP2000 = new("SAP2000", "sap2000"),
|
|
||||||
CSiBridge = new("CSiBridge", "csibridge"),
|
|
||||||
SAFE = new("SAFE", "safe"),
|
|
||||||
TeklaStructures = new("Tekla Structures", "teklastructures"),
|
|
||||||
Dxf = new("DXF Converter", "dxf"),
|
|
||||||
Excel = new("Excel", "excel"),
|
|
||||||
Unreal = new("Unreal", "unreal"),
|
|
||||||
PowerBI = new("Power BI", "powerbi"),
|
|
||||||
Blender = new("Blender", "blender"),
|
|
||||||
QGIS = new("QGIS", "qgis"),
|
|
||||||
ArcGIS = new("ArcGIS", "arcgis"),
|
|
||||||
SketchUp = new("SketchUp", "sketchup"),
|
|
||||||
Archicad = new("Archicad", "archicad"),
|
|
||||||
TopSolid = new("TopSolid", "topsolid"),
|
|
||||||
Python = new("Python", "python"),
|
|
||||||
NET = new(".NET", "net"),
|
|
||||||
Navisworks = new("Navisworks", "navisworks"),
|
|
||||||
AdvanceSteel = new("Advance Steel", "advancesteel"),
|
|
||||||
Other = new("Other", "other");
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a HostApplication form a string. It could be the versioned name or a string coming from a process running.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="appname">String with the name of the app</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[Pure]
|
|
||||||
public static HostApplication GetHostAppFromString(string? appname)
|
|
||||||
{
|
|
||||||
if (appname == null)
|
|
||||||
{
|
|
||||||
return Other;
|
|
||||||
}
|
|
||||||
|
|
||||||
appname = appname.ToLowerInvariant().Replace(" ", "");
|
|
||||||
if (appname.Contains("dynamo"))
|
|
||||||
{
|
|
||||||
return Dynamo;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appname.Contains("revit"))
|
|
||||||
{
|
|
||||||
return Revit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appname.Contains("autocad"))
|
|
||||||
{
|
|
||||||
return AutoCAD;
|
|
||||||
}
|
|
||||||
if (appname.Contains("civil3d"))
|
|
||||||
{
|
|
||||||
return Civil3D;
|
|
||||||
}
|
|
||||||
if (appname.Contains("civil"))
|
|
||||||
{
|
|
||||||
return Civil;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appname.Contains("rhino"))
|
|
||||||
{
|
|
||||||
return Rhino;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appname.Contains("grasshopper"))
|
|
||||||
{
|
|
||||||
return Grasshopper;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appname.Contains("unity"))
|
|
||||||
{
|
|
||||||
return Unity;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appname.Contains("gsa"))
|
|
||||||
{
|
|
||||||
return GSA;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appname.Contains("microstation"))
|
|
||||||
{
|
|
||||||
return MicroStation;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appname.Contains("openroads"))
|
|
||||||
{
|
|
||||||
return OpenRoads;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appname.Contains("openrail"))
|
|
||||||
{
|
|
||||||
return OpenRail;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appname.Contains("openbuildings"))
|
|
||||||
{
|
|
||||||
return OpenBuildings;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appname.Contains("etabs"))
|
|
||||||
{
|
|
||||||
return ETABS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appname.Contains("sap"))
|
|
||||||
{
|
|
||||||
return SAP2000;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appname.Contains("csibridge"))
|
|
||||||
{
|
|
||||||
return CSiBridge;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appname.Contains("safe"))
|
|
||||||
{
|
|
||||||
return SAFE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appname.Contains("teklastructures"))
|
|
||||||
{
|
|
||||||
return TeklaStructures;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appname.Contains("dxf"))
|
|
||||||
{
|
|
||||||
return Dxf;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appname.Contains("excel"))
|
|
||||||
{
|
|
||||||
return Excel;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appname.Contains("unreal"))
|
|
||||||
{
|
|
||||||
return Unreal;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appname.Contains("powerbi"))
|
|
||||||
{
|
|
||||||
return PowerBI;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appname.Contains("blender"))
|
|
||||||
{
|
|
||||||
return Blender;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appname.Contains("qgis"))
|
|
||||||
{
|
|
||||||
return QGIS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appname.Contains("arcgis"))
|
|
||||||
{
|
|
||||||
return ArcGIS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appname.Contains("sketchup"))
|
|
||||||
{
|
|
||||||
return SketchUp;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appname.Contains("archicad"))
|
|
||||||
{
|
|
||||||
return Archicad;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appname.Contains("topsolid"))
|
|
||||||
{
|
|
||||||
return TopSolid;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appname.Contains("python"))
|
|
||||||
{
|
|
||||||
return Python;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appname.Contains("net"))
|
|
||||||
{
|
|
||||||
return NET;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appname.Contains("navisworks"))
|
|
||||||
{
|
|
||||||
return Navisworks;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appname.Contains("advancesteel"))
|
|
||||||
{
|
|
||||||
return AdvanceSteel;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new HostApplication(appname, appname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,9 +5,9 @@ using Speckle.Sdk.Models;
|
|||||||
|
|
||||||
namespace Speckle.Sdk.Host;
|
namespace Speckle.Sdk.Host;
|
||||||
|
|
||||||
public record LoadedType(string Name, Type Type, List<string> DeprecatedNames);
|
internal record LoadedType(string Name, Type Type, List<string> DeprecatedNames);
|
||||||
|
|
||||||
public static class TypeLoader
|
internal static class TypeLoader
|
||||||
{
|
{
|
||||||
private static bool s_initialized;
|
private static bool s_initialized;
|
||||||
private static List<LoadedType> s_availableTypes = new();
|
private static List<LoadedType> s_availableTypes = new();
|
||||||
|
|||||||
@@ -28,20 +28,17 @@ public class Base : DynamicBase, ISpeckleObject
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A speckle object's id is an unique hash based on its properties. <b>NOTE: this field will be null unless the object was deserialised from a source. Use the <see cref="GetId"/> function to get it.</b>
|
/// A speckle object's id is an unique hash based on its properties. <b>NOTE: this field will be null unless the object was deserialised from a source. Use the <see cref="GetId"/> function to get it.</b>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SchemaIgnore]
|
|
||||||
public virtual string? id { get; set; }
|
public virtual string? id { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Secondary, ideally host application driven, object identifier.
|
/// Secondary, ideally host application driven, object identifier.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SchemaIgnore]
|
|
||||||
public string? applicationId { get; set; }
|
public string? applicationId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Holds the type information of this speckle object, derived automatically
|
/// Holds the type information of this speckle object, derived automatically
|
||||||
/// from its assembly name and inheritance.
|
/// from its assembly name and inheritance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SchemaIgnore]
|
|
||||||
public virtual string speckle_type
|
public virtual string speckle_type
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -210,11 +207,7 @@ public class Base : DynamicBase, ISpeckleObject
|
|||||||
myDuplicate.id = id;
|
myDuplicate.id = id;
|
||||||
myDuplicate.applicationId = applicationId;
|
myDuplicate.applicationId = applicationId;
|
||||||
|
|
||||||
foreach (
|
foreach (var kvp in GetMembers())
|
||||||
var kvp in GetMembers(
|
|
||||||
DynamicBaseMemberType.Instance | DynamicBaseMemberType.Dynamic | DynamicBaseMemberType.SchemaIgnored
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
var propertyInfo = type.GetProperty(kvp.Key);
|
var propertyInfo = type.GetProperty(kvp.Key);
|
||||||
if (propertyInfo is not null && !propertyInfo.CanWrite)
|
if (propertyInfo is not null && !propertyInfo.CanWrite)
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using System.Dynamic;
|
using System.Dynamic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Speckle.Newtonsoft.Json;
|
using Speckle.Newtonsoft.Json;
|
||||||
using Speckle.Sdk.Common;
|
|
||||||
using Speckle.Sdk.Host;
|
using Speckle.Sdk.Host;
|
||||||
|
|
||||||
namespace Speckle.Sdk.Models;
|
namespace Speckle.Sdk.Models;
|
||||||
@@ -233,16 +232,12 @@ public class DynamicBase : DynamicObject, IDynamicMetaObjectProvider
|
|||||||
.GetBaseProperties(GetType())
|
.GetBaseProperties(GetType())
|
||||||
.Where(x =>
|
.Where(x =>
|
||||||
{
|
{
|
||||||
var hasIgnored = x.IsDefined(typeof(SchemaIgnoreAttribute), true);
|
|
||||||
var hasObsolete = x.IsDefined(typeof(ObsoleteAttribute), true);
|
var hasObsolete = x.IsDefined(typeof(ObsoleteAttribute), true);
|
||||||
|
|
||||||
// If obsolete is false and prop has obsolete attr
|
// If obsolete is false and prop has obsolete attr
|
||||||
// OR
|
// OR
|
||||||
// If schemaIgnored is true and prop has schemaIgnore attr
|
// If schemaIgnored is true and prop has schemaIgnore attr
|
||||||
return !(
|
return !(!includeMembers.HasFlag(DynamicBaseMemberType.Obsolete) && hasObsolete);
|
||||||
!includeMembers.HasFlag(DynamicBaseMemberType.SchemaIgnored) && hasIgnored
|
|
||||||
|| !includeMembers.HasFlag(DynamicBaseMemberType.Obsolete) && hasObsolete
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
foreach (var pi in pinfos)
|
foreach (var pi in pinfos)
|
||||||
{
|
{
|
||||||
@@ -252,20 +247,6 @@ public class DynamicBase : DynamicObject, IDynamicMetaObjectProvider
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (includeMembers.HasFlag(DynamicBaseMemberType.SchemaComputed))
|
|
||||||
{
|
|
||||||
GetType()
|
|
||||||
.GetMethods()
|
|
||||||
.Where(e => e.IsDefined(typeof(SchemaComputedAttribute)) && !e.IsDefined(typeof(ObsoleteAttribute)))
|
|
||||||
.ToList()
|
|
||||||
.ForEach(e =>
|
|
||||||
{
|
|
||||||
var attr = e.GetCustomAttribute<SchemaComputedAttribute>().NotNull();
|
|
||||||
dic[attr.Name] = e.Invoke(this, null);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return dic;
|
return dic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using Speckle.Sdk.Host;
|
|
||||||
|
|
||||||
namespace Speckle.Sdk.Models;
|
namespace Speckle.Sdk.Models;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -23,23 +21,18 @@ public enum DynamicBaseMemberType
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
Obsolete = 4,
|
Obsolete = 4,
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The typed members flagged with <see cref="SchemaIgnoreAttribute"/> attribute.
|
|
||||||
/// </summary>
|
|
||||||
SchemaIgnored = 8,
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The typed methods flagged with TODO:
|
/// The typed methods flagged with TODO:
|
||||||
/// </summary>
|
/// </summary>
|
||||||
SchemaComputed = 16,
|
SchemaComputed = 16,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// All the typed members, including ones with <see cref="ObsoleteAttribute"/> or <see cref="SchemaIgnoreAttribute"/> attributes.
|
/// All the typed members, including ones with <see cref="ObsoleteAttribute"/> attributes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
InstanceAll = Instance + Obsolete + SchemaIgnored,
|
InstanceAll = Instance + Obsolete,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// All the members, including dynamic and instance members flagged with <see cref="ObsoleteAttribute"/> or <see cref="SchemaIgnoreAttribute"/> attributes
|
/// All the members, including dynamic and instance members flagged with <see cref="ObsoleteAttribute"/> attributes
|
||||||
/// </summary>
|
/// </summary>
|
||||||
All = InstanceAll + Dynamic,
|
All = InstanceAll + Dynamic,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,9 +72,14 @@ public sealed class SqLiteJsonCacheManager : ISqLiteJsonCacheManager
|
|||||||
cmd4.ExecuteNonQuery();
|
cmd4.ExecuteNonQuery();
|
||||||
}
|
}
|
||||||
|
|
||||||
using (SqliteCommand cmd0 = new("PRAGMA journal_mode='wal';", c))
|
using (SqliteCommand cmd5 = new("PRAGMA journal_mode='wal';", c))
|
||||||
{
|
{
|
||||||
cmd0.ExecuteNonQuery();
|
cmd5.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
//do this to wait 5 seconds to avoid db lock exceptions, this is 0 by default
|
||||||
|
using (SqliteCommand cmd6 = new("PRAGMA busy_timeout=5000;", c))
|
||||||
|
{
|
||||||
|
cmd6.ExecuteNonQuery();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Speckle.InterfaceGenerator;
|
||||||
|
using Speckle.Sdk.Serialisation.V2.Receive;
|
||||||
|
using Speckle.Sdk.SQLite;
|
||||||
|
using Speckle.Sdk.Transports;
|
||||||
|
|
||||||
|
namespace Speckle.Sdk.Serialisation.V2;
|
||||||
|
|
||||||
|
[GenerateAutoInterface]
|
||||||
|
public class DeserializeProcessFactory(
|
||||||
|
IBaseDeserializer baseDeserializer,
|
||||||
|
ISqLiteJsonCacheManagerFactory sqLiteJsonCacheManagerFactory,
|
||||||
|
IServerObjectManagerFactory serverObjectManagerFactory,
|
||||||
|
ILoggerFactory loggerFactory
|
||||||
|
) : IDeserializeProcessFactory
|
||||||
|
{
|
||||||
|
public IDeserializeProcess CreateDeserializeProcess(
|
||||||
|
Uri url,
|
||||||
|
string streamId,
|
||||||
|
string? authorizationToken,
|
||||||
|
IProgress<ProgressArgs>? 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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IDeserializeProcess CreateDeserializeProcess(
|
||||||
|
ConcurrentDictionary<Id, Json> jsonCache,
|
||||||
|
ConcurrentDictionary<string, string> objects,
|
||||||
|
IProgress<ProgressArgs>? progress,
|
||||||
|
CancellationToken cancellationToken,
|
||||||
|
DeserializeProcessOptions? options = null
|
||||||
|
) =>
|
||||||
|
new DeserializeProcess(
|
||||||
|
new MemoryJsonCacheManager(jsonCache),
|
||||||
|
new MemoryServerObjectManager(objects),
|
||||||
|
progress,
|
||||||
|
baseDeserializer,
|
||||||
|
loggerFactory,
|
||||||
|
cancellationToken,
|
||||||
|
options
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
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<string> objectIds,
|
|
||||||
IProgress<ProgressArgs>? progress,
|
|
||||||
CancellationToken cancellationToken
|
|
||||||
) => throw new NotImplementedException();
|
|
||||||
|
|
||||||
public Task<string?> DownloadSingleObject(
|
|
||||||
string objectId,
|
|
||||||
IProgress<ProgressArgs>? progress,
|
|
||||||
CancellationToken cancellationToken
|
|
||||||
) => throw new NotImplementedException();
|
|
||||||
|
|
||||||
public Task<Dictionary<string, bool>> HasObjects(
|
|
||||||
IReadOnlyCollection<string> objectIds,
|
|
||||||
CancellationToken cancellationToken
|
|
||||||
) => throw new NotImplementedException();
|
|
||||||
|
|
||||||
public Task UploadObjects(
|
|
||||||
IReadOnlyList<BaseItem> objects,
|
|
||||||
bool compressPayloads,
|
|
||||||
IProgress<ProgressArgs>? 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using Speckle.Sdk.SQLite;
|
||||||
|
|
||||||
|
namespace Speckle.Sdk.Serialisation.V2;
|
||||||
|
|
||||||
|
#pragma warning disable CA1063
|
||||||
|
public class MemoryJsonCacheManager(ConcurrentDictionary<Id, Json> jsonCache) : ISqLiteJsonCacheManager
|
||||||
|
#pragma warning restore CA1063
|
||||||
|
{
|
||||||
|
public IReadOnlyCollection<(string Id, string Json)> GetAllObjects() =>
|
||||||
|
jsonCache.Select(x => (x.Key.Value, x.Value.Value)).ToList();
|
||||||
|
|
||||||
|
public void DeleteObject(string id) => jsonCache.TryRemove(new Id(id), out _);
|
||||||
|
|
||||||
|
public string? GetObject(string id) => jsonCache.TryGetValue(new Id(id), out var json) ? json.Value : null;
|
||||||
|
|
||||||
|
public void SaveObject(string id, string json) => jsonCache.TryAdd(new Id(id), new Json(json));
|
||||||
|
|
||||||
|
public void UpdateObject(string id, string json) => jsonCache[new Id(id)] = new Json(json);
|
||||||
|
|
||||||
|
public virtual void SaveObjects(IEnumerable<(string id, string json)> items)
|
||||||
|
{
|
||||||
|
foreach (var (id, json) in items)
|
||||||
|
{
|
||||||
|
SaveObject(id, json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasObject(string objectId) => jsonCache.ContainsKey(new Id(objectId));
|
||||||
|
|
||||||
|
#pragma warning disable CA1063
|
||||||
|
#pragma warning disable CA1816
|
||||||
|
public void Dispose()
|
||||||
|
#pragma warning restore CA1816
|
||||||
|
#pragma warning restore CA1063
|
||||||
|
{ }
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Speckle.Sdk.Serialisation.V2.Send;
|
||||||
|
using Speckle.Sdk.Transports;
|
||||||
|
|
||||||
|
namespace Speckle.Sdk.Serialisation.V2;
|
||||||
|
|
||||||
|
public class MemoryServerObjectManager(ConcurrentDictionary<string, string> objects) : IServerObjectManager
|
||||||
|
{
|
||||||
|
public virtual async IAsyncEnumerable<(string, string)> DownloadObjects(
|
||||||
|
IReadOnlyCollection<string> objectIds,
|
||||||
|
IProgress<ProgressArgs>? progress,
|
||||||
|
[EnumeratorCancellation] CancellationToken cancellationToken
|
||||||
|
)
|
||||||
|
{
|
||||||
|
foreach (var item in objects.Where(x => objectIds.Contains(x.Key)))
|
||||||
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
yield return (item.Key, item.Value);
|
||||||
|
}
|
||||||
|
await Task.CompletedTask.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual Task<string?> DownloadSingleObject(
|
||||||
|
string objectId,
|
||||||
|
IProgress<ProgressArgs>? progress,
|
||||||
|
CancellationToken cancellationToken
|
||||||
|
) => Task.FromResult(objects.TryGetValue(objectId, out var json) ? json : null);
|
||||||
|
|
||||||
|
public virtual Task<Dictionary<string, bool>> HasObjects(
|
||||||
|
IReadOnlyCollection<string> objectIds,
|
||||||
|
CancellationToken cancellationToken
|
||||||
|
) => Task.FromResult(objectIds.ToDictionary(x => x, objects.ContainsKey));
|
||||||
|
|
||||||
|
public virtual Task UploadObjects(
|
||||||
|
IReadOnlyList<BaseItem> objectToUpload,
|
||||||
|
bool compressPayloads,
|
||||||
|
IProgress<ProgressArgs>? progress,
|
||||||
|
CancellationToken cancellationToken
|
||||||
|
)
|
||||||
|
{
|
||||||
|
foreach (BaseItem baseItem in objectToUpload)
|
||||||
|
{
|
||||||
|
objects.TryAdd(baseItem.Id.Value, baseItem.Json.Value);
|
||||||
|
}
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,8 +6,7 @@ namespace Speckle.Sdk.Serialisation.V2;
|
|||||||
public sealed class PriorityScheduler(
|
public sealed class PriorityScheduler(
|
||||||
ILogger<PriorityScheduler> logger,
|
ILogger<PriorityScheduler> logger,
|
||||||
ThreadPriority priority,
|
ThreadPriority priority,
|
||||||
int maximumConcurrencyLevel,
|
int maximumConcurrencyLevel
|
||||||
CancellationToken cancellationToken
|
|
||||||
) : TaskScheduler, IAsyncDisposable
|
) : TaskScheduler, IAsyncDisposable
|
||||||
{
|
{
|
||||||
private readonly BlockingCollection<Task> _tasks = new();
|
private readonly BlockingCollection<Task> _tasks = new();
|
||||||
@@ -56,7 +55,7 @@ public sealed class PriorityScheduler(
|
|||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
//we're done so leave
|
//we're done so leave
|
||||||
if (_tasks.IsCompleted || cancellationToken.IsCancellationRequested)
|
if (_tasks.IsCompleted)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -66,11 +65,6 @@ public sealed class PriorityScheduler(
|
|||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
//cancelled just leave
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
//didn't get a task but just timed out so continue
|
//didn't get a task but just timed out so continue
|
||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -54,8 +54,7 @@ public sealed class DeserializeProcess(
|
|||||||
private readonly PriorityScheduler _belowNormal = new(
|
private readonly PriorityScheduler _belowNormal = new(
|
||||||
loggerFactory.CreateLogger<PriorityScheduler>(),
|
loggerFactory.CreateLogger<PriorityScheduler>(),
|
||||||
ThreadPriority.BelowNormal,
|
ThreadPriority.BelowNormal,
|
||||||
Environment.ProcessorCount * 2,
|
Environment.ProcessorCount * 2
|
||||||
cancellationToken
|
|
||||||
);
|
);
|
||||||
|
|
||||||
private readonly DeserializeProcessOptions _options = options ?? new();
|
private readonly DeserializeProcessOptions _options = options ?? new();
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ public sealed class ObjectLoader(
|
|||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
if (Exception is not null)
|
if (Exception is not null)
|
||||||
{
|
{
|
||||||
throw new SpeckleException("Error while sending", Exception);
|
throw new SpeckleException("Error while loading", Exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ using System.Text;
|
|||||||
|
|
||||||
namespace Speckle.Sdk.Serialisation.V2.Send;
|
namespace Speckle.Sdk.Serialisation.V2.Send;
|
||||||
|
|
||||||
public readonly record struct BaseItem(Id Id, Json Json, bool NeedsStorage, Dictionary<Id, int>? Closures)
|
public sealed record BaseItem(Id Id, Json Json, bool NeedsStorage, Dictionary<Id, int>? Closures) : IHasByteSize
|
||||||
: IHasByteSize
|
|
||||||
{
|
{
|
||||||
public int ByteSize { get; } = Encoding.UTF8.GetByteCount(Json.Value);
|
public int ByteSize { get; } = Encoding.UTF8.GetByteCount(Json.Value);
|
||||||
|
|
||||||
@@ -13,7 +12,7 @@ public readonly record struct BaseItem(Id Id, Json Json, bool NeedsStorage, Dict
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return string.Equals(Id.Value, other.Value.Id.Value, StringComparison.OrdinalIgnoreCase);
|
return string.Equals(Id.Value, other.Id.Value, StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int GetHashCode() => Id.GetHashCode();
|
public override int GetHashCode() => Id.GetHashCode();
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
using System.Collections;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
|
|
||||||
namespace Speckle.Sdk.Serialisation.V2.Send;
|
|
||||||
|
|
||||||
public class EmptyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
|
|
||||||
{
|
|
||||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => throw new NotImplementedException();
|
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
|
||||||
|
|
||||||
public void Add(KeyValuePair<TKey, TValue> item) { }
|
|
||||||
|
|
||||||
public void Clear() => throw new NotImplementedException();
|
|
||||||
|
|
||||||
public bool Contains(KeyValuePair<TKey, TValue> item) => false;
|
|
||||||
|
|
||||||
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) => throw new NotImplementedException();
|
|
||||||
|
|
||||||
public bool Remove(KeyValuePair<TKey, TValue> 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<TKey> Keys { get; }
|
|
||||||
public ICollection<TValue> Values { get; }
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Speckle.Sdk.Dependencies;
|
||||||
|
using Speckle.Sdk.Dependencies.Serialization;
|
||||||
|
using Speckle.Sdk.SQLite;
|
||||||
|
using Speckle.Sdk.Transports;
|
||||||
|
|
||||||
|
namespace Speckle.Sdk.Serialisation.V2.Send;
|
||||||
|
|
||||||
|
public interface IObjectSaver : IDisposable
|
||||||
|
{
|
||||||
|
Exception? Exception { get; set; }
|
||||||
|
Task Start(int? maxParallelism, int? httpBatchSize, int? cacheBatchSize, CancellationToken cancellationToken);
|
||||||
|
void DoneTraversing();
|
||||||
|
Task DoneSaving();
|
||||||
|
Task SaveAsync(BaseItem item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ObjectSaver(
|
||||||
|
IProgress<ProgressArgs>? progress,
|
||||||
|
ISqLiteJsonCacheManager sqLiteJsonCacheManager,
|
||||||
|
IServerObjectManager serverObjectManager,
|
||||||
|
ILogger<ObjectSaver> logger,
|
||||||
|
CancellationToken cancellationToken,
|
||||||
|
#pragma warning disable CS9107
|
||||||
|
#pragma warning disable CA2254
|
||||||
|
SerializeProcessOptions? options = null
|
||||||
|
) : ChannelSaver<BaseItem>, IObjectSaver
|
||||||
|
#pragma warning restore CA2254
|
||||||
|
#pragma warning restore CS9107
|
||||||
|
{
|
||||||
|
private readonly CancellationTokenSource _cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(
|
||||||
|
cancellationToken
|
||||||
|
);
|
||||||
|
|
||||||
|
private readonly SerializeProcessOptions _options = options ?? new();
|
||||||
|
|
||||||
|
private long _uploaded;
|
||||||
|
private long _cached;
|
||||||
|
|
||||||
|
private long _objectsSerialized;
|
||||||
|
|
||||||
|
protected override async Task SendToServerInternal(Batch<BaseItem> batch)
|
||||||
|
{
|
||||||
|
if (_cancellationTokenSource.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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(), _cancellationTokenSource.Token)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
objectBatch = batch.Items.Where(x => !hasObjects[x.Id.Value]).ToList();
|
||||||
|
if (objectBatch.Count != 0)
|
||||||
|
{
|
||||||
|
await serverObjectManager
|
||||||
|
.UploadObjects(objectBatch, true, progress, _cancellationTokenSource.Token)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
Interlocked.Add(ref _uploaded, batch.Items.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
progress?.Report(new(ProgressEvent.UploadedObjects, _uploaded, null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
_cancellationTokenSource.Cancel();
|
||||||
|
}
|
||||||
|
#pragma warning disable CA1031
|
||||||
|
catch (Exception e)
|
||||||
|
#pragma warning restore CA1031
|
||||||
|
{
|
||||||
|
RecordException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SaveAsync(BaseItem item)
|
||||||
|
{
|
||||||
|
Interlocked.Increment(ref _objectsSerialized);
|
||||||
|
await SaveAsync(item, _cancellationTokenSource.Token).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SaveToCache(List<BaseItem> batch)
|
||||||
|
{
|
||||||
|
if (_cancellationTokenSource.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!_options.SkipCacheWrite && batch.Count != 0)
|
||||||
|
{
|
||||||
|
sqLiteJsonCacheManager.SaveObjects(batch.Select(x => (x.Id.Value, x.Json.Value)));
|
||||||
|
Interlocked.Add(ref _cached, batch.Count);
|
||||||
|
progress?.Report(new(ProgressEvent.CachedToLocal, _cached, _objectsSerialized));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
_cancellationTokenSource.Cancel();
|
||||||
|
}
|
||||||
|
#pragma warning disable CA1031
|
||||||
|
catch (Exception e)
|
||||||
|
#pragma warning restore CA1031
|
||||||
|
{
|
||||||
|
RecordException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RecordException(Exception e)
|
||||||
|
{
|
||||||
|
//order here matters
|
||||||
|
logger.LogError(e, "Error in SDK");
|
||||||
|
Exception = e;
|
||||||
|
_cancellationTokenSource.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_cancellationTokenSource.Dispose();
|
||||||
|
sqLiteJsonCacheManager.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,9 +4,7 @@ using Microsoft.Extensions.Logging;
|
|||||||
using Speckle.InterfaceGenerator;
|
using Speckle.InterfaceGenerator;
|
||||||
using Speckle.Sdk.Common;
|
using Speckle.Sdk.Common;
|
||||||
using Speckle.Sdk.Dependencies;
|
using Speckle.Sdk.Dependencies;
|
||||||
using Speckle.Sdk.Dependencies.Serialization;
|
|
||||||
using Speckle.Sdk.Models;
|
using Speckle.Sdk.Models;
|
||||||
using Speckle.Sdk.SQLite;
|
|
||||||
using Speckle.Sdk.Transports;
|
using Speckle.Sdk.Transports;
|
||||||
|
|
||||||
namespace Speckle.Sdk.Serialisation.V2.Send;
|
namespace Speckle.Sdk.Serialisation.V2.Send;
|
||||||
@@ -16,7 +14,12 @@ public record SerializeProcessOptions(
|
|||||||
bool SkipCacheWrite = false,
|
bool SkipCacheWrite = false,
|
||||||
bool SkipServer = false,
|
bool SkipServer = false,
|
||||||
bool SkipFindTotalObjects = false
|
bool SkipFindTotalObjects = false
|
||||||
);
|
)
|
||||||
|
{
|
||||||
|
public int? MaxHttpSendSize { get; set; }
|
||||||
|
public int? MaxCacheSize { get; set; }
|
||||||
|
public int? MaxParallelism { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public readonly record struct SerializeProcessResults(
|
public readonly record struct SerializeProcessResults(
|
||||||
string RootId,
|
string RootId,
|
||||||
@@ -28,28 +31,27 @@ public partial interface ISerializeProcess : IAsyncDisposable;
|
|||||||
[GenerateAutoInterface]
|
[GenerateAutoInterface]
|
||||||
public sealed class SerializeProcess(
|
public sealed class SerializeProcess(
|
||||||
IProgress<ProgressArgs>? progress,
|
IProgress<ProgressArgs>? progress,
|
||||||
ISqLiteJsonCacheManager sqLiteJsonCacheManager,
|
IObjectSaver objectSaver,
|
||||||
IServerObjectManager serverObjectManager,
|
|
||||||
IBaseChildFinder baseChildFinder,
|
IBaseChildFinder baseChildFinder,
|
||||||
IBaseSerializer baseSerializer,
|
IBaseSerializer baseSerializer,
|
||||||
ILoggerFactory loggerFactory,
|
ILoggerFactory loggerFactory,
|
||||||
CancellationToken cancellationToken,
|
CancellationToken cancellationToken,
|
||||||
SerializeProcessOptions? options = null
|
SerializeProcessOptions? options = null
|
||||||
#pragma warning disable CS9107
|
) : ISerializeProcess
|
||||||
#pragma warning disable CA2254
|
|
||||||
)
|
|
||||||
: ChannelSaver<BaseItem>(x => loggerFactory.CreateLogger<SerializeProcess>().LogWarning(x), cancellationToken),
|
|
||||||
ISerializeProcess
|
|
||||||
#pragma warning restore CA2254
|
|
||||||
#pragma warning restore CS9107
|
|
||||||
{
|
{
|
||||||
|
private static readonly Dictionary<Id, NodeInfo> EMPTY_CLOSURES = new();
|
||||||
|
|
||||||
|
private readonly CancellationTokenSource _processSource = CancellationTokenSource.CreateLinkedTokenSource(
|
||||||
|
cancellationToken
|
||||||
|
);
|
||||||
|
private readonly ILogger<SerializeProcess> _logger = loggerFactory.CreateLogger<SerializeProcess>();
|
||||||
|
|
||||||
//async dispose
|
//async dispose
|
||||||
[SuppressMessage("Usage", "CA2213:Disposable fields should be disposed")]
|
[SuppressMessage("Usage", "CA2213:Disposable fields should be disposed")]
|
||||||
private readonly PriorityScheduler _highest = new(
|
private readonly PriorityScheduler _highest = new(
|
||||||
loggerFactory.CreateLogger<PriorityScheduler>(),
|
loggerFactory.CreateLogger<PriorityScheduler>(),
|
||||||
ThreadPriority.Highest,
|
ThreadPriority.Highest,
|
||||||
2,
|
2
|
||||||
cancellationToken
|
|
||||||
);
|
);
|
||||||
|
|
||||||
//async dispose
|
//async dispose
|
||||||
@@ -57,12 +59,9 @@ public sealed class SerializeProcess(
|
|||||||
private readonly PriorityScheduler _belowNormal = new(
|
private readonly PriorityScheduler _belowNormal = new(
|
||||||
loggerFactory.CreateLogger<PriorityScheduler>(),
|
loggerFactory.CreateLogger<PriorityScheduler>(),
|
||||||
ThreadPriority.BelowNormal,
|
ThreadPriority.BelowNormal,
|
||||||
Environment.ProcessorCount * 2,
|
Environment.ProcessorCount * 2
|
||||||
cancellationToken
|
|
||||||
);
|
);
|
||||||
|
|
||||||
private readonly SerializeProcessOptions _options = options ?? new();
|
private readonly SerializeProcessOptions _options = options ?? new();
|
||||||
private readonly ILogger<SerializeProcess> _logger = loggerFactory.CreateLogger<SerializeProcess>();
|
|
||||||
|
|
||||||
private readonly Pool<Dictionary<Id, NodeInfo>> _currentClosurePool = Pools.CreateDictionaryPool<Id, NodeInfo>();
|
private readonly Pool<Dictionary<Id, NodeInfo>> _currentClosurePool = Pools.CreateDictionaryPool<Id, NodeInfo>();
|
||||||
private readonly Pool<ConcurrentDictionary<Id, NodeInfo>> _childClosurePool = Pools.CreateConcurrentDictionaryPool<
|
private readonly Pool<ConcurrentDictionary<Id, NodeInfo>> _childClosurePool = Pools.CreateConcurrentDictionaryPool<
|
||||||
@@ -75,25 +74,24 @@ public sealed class SerializeProcess(
|
|||||||
|
|
||||||
private long _objectsSerialized;
|
private long _objectsSerialized;
|
||||||
|
|
||||||
private long _uploaded;
|
|
||||||
private long _cached;
|
|
||||||
|
|
||||||
[AutoInterfaceIgnore]
|
[AutoInterfaceIgnore]
|
||||||
public async ValueTask DisposeAsync()
|
public async ValueTask DisposeAsync()
|
||||||
{
|
{
|
||||||
await WaitForSchedulerCompletion().ConfigureAwait(false);
|
await WaitForSchedulerCompletion().ConfigureAwait(false);
|
||||||
await _highest.DisposeAsync().ConfigureAwait(false);
|
await _highest.DisposeAsync().ConfigureAwait(false);
|
||||||
await _belowNormal.DisposeAsync().ConfigureAwait(false);
|
await _belowNormal.DisposeAsync().ConfigureAwait(false);
|
||||||
sqLiteJsonCacheManager.Dispose();
|
objectSaver.Dispose();
|
||||||
|
_processSource.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ThrowIfFailed()
|
private void ThrowIfFailed()
|
||||||
{
|
{
|
||||||
if (Exception is not null)
|
//order here matters...null with cancellation means a user did it, otherwise it's a real Exception
|
||||||
|
if (objectSaver.Exception is not null)
|
||||||
{
|
{
|
||||||
throw new SpeckleException("Error while sending", Exception);
|
throw new SpeckleException("Error while sending", objectSaver.Exception);
|
||||||
}
|
}
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
_processSource.Token.ThrowIfCancellationRequested();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task WaitForSchedulerCompletion()
|
private async Task WaitForSchedulerCompletion()
|
||||||
@@ -106,30 +104,36 @@ public sealed class SerializeProcess(
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var channelTask = Start();
|
var channelTask = objectSaver.Start(
|
||||||
|
options?.MaxParallelism,
|
||||||
|
options?.MaxHttpSendSize,
|
||||||
|
options?.MaxCacheSize,
|
||||||
|
_processSource.Token
|
||||||
|
);
|
||||||
var findTotalObjectsTask = Task.CompletedTask;
|
var findTotalObjectsTask = Task.CompletedTask;
|
||||||
if (!_options.SkipFindTotalObjects)
|
if (!_options.SkipFindTotalObjects)
|
||||||
{
|
{
|
||||||
ThrowIfFailed();
|
ThrowIfFailed();
|
||||||
findTotalObjectsTask = Task.Factory.StartNew(
|
findTotalObjectsTask = Task.Factory.StartNew(
|
||||||
() => TraverseTotal(root),
|
() => TraverseTotal(root),
|
||||||
cancellationToken,
|
_processSource.Token,
|
||||||
TaskCreationOptions.AttachedToParent | TaskCreationOptions.PreferFairness,
|
TaskCreationOptions.AttachedToParent | TaskCreationOptions.PreferFairness,
|
||||||
_highest
|
_highest
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await Traverse(root).ConfigureAwait(false);
|
await Traverse(root).ConfigureAwait(false);
|
||||||
DoneTraversing();
|
ThrowIfFailed();
|
||||||
|
objectSaver.DoneTraversing();
|
||||||
await Task.WhenAll(findTotalObjectsTask, channelTask).ConfigureAwait(false);
|
await Task.WhenAll(findTotalObjectsTask, channelTask).ConfigureAwait(false);
|
||||||
ThrowIfFailed();
|
ThrowIfFailed();
|
||||||
await DoneSaving().ConfigureAwait(false);
|
await objectSaver.DoneSaving().ConfigureAwait(false);
|
||||||
ThrowIfFailed();
|
ThrowIfFailed();
|
||||||
await WaitForSchedulerCompletion().ConfigureAwait(false);
|
await WaitForSchedulerCompletion().ConfigureAwait(false);
|
||||||
ThrowIfFailed();
|
ThrowIfFailed();
|
||||||
return new(root.id.NotNull(), baseSerializer.ObjectReferences.Freeze());
|
return new(root.id.NotNull(), baseSerializer.ObjectReferences.Freeze());
|
||||||
}
|
}
|
||||||
catch (TaskCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
ThrowIfFailed();
|
ThrowIfFailed();
|
||||||
throw;
|
throw;
|
||||||
@@ -138,9 +142,13 @@ public sealed class SerializeProcess(
|
|||||||
|
|
||||||
private void TraverseTotal(Base obj)
|
private void TraverseTotal(Base obj)
|
||||||
{
|
{
|
||||||
|
if (_processSource.Token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
foreach (var child in baseChildFinder.GetChildren(obj))
|
foreach (var child in baseChildFinder.GetChildren(obj))
|
||||||
{
|
{
|
||||||
_objectsFound++;
|
Interlocked.Increment(ref _objectsFound);
|
||||||
progress?.Report(new(ProgressEvent.FindingChildren, _objectsFound, null));
|
progress?.Report(new(ProgressEvent.FindingChildren, _objectsFound, null));
|
||||||
TraverseTotal(child);
|
TraverseTotal(child);
|
||||||
}
|
}
|
||||||
@@ -148,114 +156,142 @@ public sealed class SerializeProcess(
|
|||||||
|
|
||||||
private async Task<Dictionary<Id, NodeInfo>> Traverse(Base obj)
|
private async Task<Dictionary<Id, NodeInfo>> Traverse(Base obj)
|
||||||
{
|
{
|
||||||
var tasks = new List<Task<Dictionary<Id, NodeInfo>>>();
|
if (_processSource.Token.IsCancellationRequested)
|
||||||
foreach (var child in baseChildFinder.GetChildren(obj))
|
|
||||||
{
|
{
|
||||||
// tmp is necessary because of the way closures close over loop variables
|
return EMPTY_CLOSURES;
|
||||||
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<Id, NodeInfo>[] 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<BaseItem> batch)
|
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!_options.SkipServer && batch.Items.Count != 0)
|
var tasks = new List<Task<Dictionary<Id, NodeInfo>>>();
|
||||||
|
foreach (var child in baseChildFinder.GetChildren(obj))
|
||||||
{
|
{
|
||||||
var objectBatch = batch.Items.Distinct().ToList();
|
// tmp is necessary because of the way closures close over loop variables
|
||||||
var hasObjects = await serverObjectManager
|
var tmp = child;
|
||||||
.HasObjects(objectBatch.Select(x => x.Id.Value).Freeze(), cancellationToken)
|
if (_processSource.Token.IsCancellationRequested)
|
||||||
.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);
|
return EMPTY_CLOSURES;
|
||||||
Interlocked.Exchange(ref _uploaded, _uploaded + batch.Items.Count);
|
}
|
||||||
|
var t = Task
|
||||||
|
.Factory.StartNew(
|
||||||
|
// ReSharper disable once AccessToDisposedClosure
|
||||||
|
// don't need to capture here
|
||||||
|
async () => await Traverse(tmp).ConfigureAwait(false),
|
||||||
|
_processSource.Token,
|
||||||
|
TaskCreationOptions.AttachedToParent | TaskCreationOptions.PreferFairness,
|
||||||
|
_belowNormal
|
||||||
|
)
|
||||||
|
.Unwrap();
|
||||||
|
tasks.Add(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_processSource.Token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return EMPTY_CLOSURES;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Dictionary<Id, NodeInfo>> taskClosures = new();
|
||||||
|
if (tasks.Count > 0)
|
||||||
|
{
|
||||||
|
var currentTasks = tasks.ToList();
|
||||||
|
do
|
||||||
|
{
|
||||||
|
//grab when any Task is done and see if we're cancelling
|
||||||
|
var t = await Task.WhenAny(currentTasks).ConfigureAwait(false);
|
||||||
|
if (t.IsCanceled)
|
||||||
|
{
|
||||||
|
return EMPTY_CLOSURES;
|
||||||
|
}
|
||||||
|
if (t.IsFaulted)
|
||||||
|
{
|
||||||
|
if (t.Exception is not null)
|
||||||
|
{
|
||||||
|
RecordException(t.Exception);
|
||||||
|
}
|
||||||
|
return EMPTY_CLOSURES;
|
||||||
|
}
|
||||||
|
taskClosures.Add(t.Result);
|
||||||
|
currentTasks.Remove(t);
|
||||||
|
} while (currentTasks.Count > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_processSource.Token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return EMPTY_CLOSURES;
|
||||||
|
}
|
||||||
|
|
||||||
|
var childClosures = _childClosurePool.Get();
|
||||||
|
foreach (var childClosure in taskClosures)
|
||||||
|
{
|
||||||
|
foreach (var kvp in childClosure)
|
||||||
|
{
|
||||||
|
childClosures[kvp.Key] = kvp.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
progress?.Report(new(ProgressEvent.UploadedObjects, _uploaded, null));
|
_currentClosurePool.Return(childClosure);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_processSource.Token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return EMPTY_CLOSURES;
|
||||||
|
}
|
||||||
|
|
||||||
|
var items = baseSerializer.Serialise(obj, childClosures, _options.SkipCacheRead, _processSource.Token);
|
||||||
|
|
||||||
|
if (_processSource.Token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return EMPTY_CLOSURES;
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentClosures = _currentClosurePool.Get();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Interlocked.Increment(ref _objectCount);
|
||||||
|
progress?.Report(new(ProgressEvent.FromCacheOrSerialized, _objectCount, Math.Max(_objectCount, _objectsFound)));
|
||||||
|
foreach (var item in items)
|
||||||
|
{
|
||||||
|
if (item.NeedsStorage)
|
||||||
|
{
|
||||||
|
if (_processSource.Token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return EMPTY_CLOSURES;
|
||||||
|
}
|
||||||
|
|
||||||
|
Interlocked.Increment(ref _objectsSerialized);
|
||||||
|
await objectSaver.SaveAsync(item).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currentClosures.ContainsKey(item.Id))
|
||||||
|
{
|
||||||
|
currentClosures.Add(item.Id, new NodeInfo(item.Json, item.Closures));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_childClosurePool.Return(childClosures);
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentClosures;
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
throw;
|
return EMPTY_CLOSURES;
|
||||||
}
|
}
|
||||||
#pragma warning disable CA1031
|
#pragma warning disable CA1031
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
#pragma warning restore CA1031
|
#pragma warning restore CA1031
|
||||||
{
|
{
|
||||||
_logger.LogError(e, "Error sending objects to server");
|
RecordException(e);
|
||||||
throw;
|
return EMPTY_CLOSURES;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void SaveToCache(List<BaseItem> batch)
|
private void RecordException(Exception e)
|
||||||
{
|
{
|
||||||
try
|
//order here matters
|
||||||
{
|
_logger.LogError(e, "Error in SDK");
|
||||||
if (!_options.SkipCacheWrite && batch.Count != 0)
|
objectSaver.Exception = e;
|
||||||
{
|
_processSource.Cancel();
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,16 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Speckle.Sdk.Serialisation.V2.Receive;
|
using Speckle.InterfaceGenerator;
|
||||||
using Speckle.Sdk.Serialisation.V2.Send;
|
using Speckle.Sdk.Serialisation.V2.Send;
|
||||||
using Speckle.Sdk.SQLite;
|
using Speckle.Sdk.SQLite;
|
||||||
using Speckle.Sdk.Transports;
|
using Speckle.Sdk.Transports;
|
||||||
|
|
||||||
namespace Speckle.Sdk.Serialisation.V2;
|
namespace Speckle.Sdk.Serialisation.V2;
|
||||||
|
|
||||||
public interface ISerializeProcessFactory
|
[GenerateAutoInterface]
|
||||||
{
|
|
||||||
ISerializeProcess CreateSerializeProcess(
|
|
||||||
Uri url,
|
|
||||||
string streamId,
|
|
||||||
string? authorizationToken,
|
|
||||||
IProgress<ProgressArgs>? progress,
|
|
||||||
CancellationToken cancellationToken,
|
|
||||||
SerializeProcessOptions? options = null
|
|
||||||
);
|
|
||||||
IDeserializeProcess CreateDeserializeProcess(
|
|
||||||
Uri url,
|
|
||||||
string streamId,
|
|
||||||
string? authorizationToken,
|
|
||||||
IProgress<ProgressArgs>? progress,
|
|
||||||
CancellationToken cancellationToken,
|
|
||||||
DeserializeProcessOptions? options = null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SerializeProcessFactory(
|
public class SerializeProcessFactory(
|
||||||
IBaseChildFinder baseChildFinder,
|
IBaseChildFinder baseChildFinder,
|
||||||
IObjectSerializerFactory objectSerializerFactory,
|
IObjectSerializerFactory objectSerializerFactory,
|
||||||
IBaseDeserializer baseDeserializer,
|
|
||||||
ISqLiteJsonCacheManagerFactory sqLiteJsonCacheManagerFactory,
|
ISqLiteJsonCacheManagerFactory sqLiteJsonCacheManagerFactory,
|
||||||
IServerObjectManagerFactory serverObjectManagerFactory,
|
IServerObjectManagerFactory serverObjectManagerFactory,
|
||||||
ILoggerFactory loggerFactory
|
ILoggerFactory loggerFactory
|
||||||
@@ -46,34 +27,54 @@ public class SerializeProcessFactory(
|
|||||||
{
|
{
|
||||||
var sqLiteJsonCacheManager = sqLiteJsonCacheManagerFactory.CreateFromStream(streamId);
|
var sqLiteJsonCacheManager = sqLiteJsonCacheManagerFactory.CreateFromStream(streamId);
|
||||||
var serverObjectManager = serverObjectManagerFactory.Create(url, streamId, authorizationToken);
|
var serverObjectManager = serverObjectManagerFactory.Create(url, streamId, authorizationToken);
|
||||||
return new SerializeProcess(
|
return CreateSerializeProcess(sqLiteJsonCacheManager, serverObjectManager, progress, cancellationToken, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ISerializeProcess CreateSerializeProcess(
|
||||||
|
ISqLiteJsonCacheManager sqLiteJsonCacheManager,
|
||||||
|
IServerObjectManager serverObjectManager,
|
||||||
|
IProgress<ProgressArgs>? progress,
|
||||||
|
CancellationToken cancellationToken,
|
||||||
|
SerializeProcessOptions? options = null
|
||||||
|
) =>
|
||||||
|
new SerializeProcess(
|
||||||
progress,
|
progress,
|
||||||
sqLiteJsonCacheManager,
|
new ObjectSaver(
|
||||||
serverObjectManager,
|
progress,
|
||||||
|
sqLiteJsonCacheManager,
|
||||||
|
serverObjectManager,
|
||||||
|
loggerFactory.CreateLogger<ObjectSaver>(),
|
||||||
|
cancellationToken
|
||||||
|
),
|
||||||
baseChildFinder,
|
baseChildFinder,
|
||||||
new BaseSerializer(sqLiteJsonCacheManager, objectSerializerFactory),
|
new BaseSerializer(sqLiteJsonCacheManager, objectSerializerFactory),
|
||||||
loggerFactory,
|
loggerFactory,
|
||||||
cancellationToken,
|
cancellationToken,
|
||||||
options
|
options
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
public IDeserializeProcess CreateDeserializeProcess(
|
public ISerializeProcess CreateSerializeProcess(
|
||||||
Uri url,
|
ConcurrentDictionary<Id, Json> jsonCache,
|
||||||
string streamId,
|
ConcurrentDictionary<string, string> objects,
|
||||||
string? authorizationToken,
|
|
||||||
IProgress<ProgressArgs>? progress,
|
IProgress<ProgressArgs>? progress,
|
||||||
CancellationToken cancellationToken,
|
CancellationToken cancellationToken,
|
||||||
DeserializeProcessOptions? options = null
|
SerializeProcessOptions? options = null
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var sqLiteJsonCacheManager = sqLiteJsonCacheManagerFactory.CreateFromStream(streamId);
|
#pragma warning disable CA2000
|
||||||
var serverObjectManager = serverObjectManagerFactory.Create(url, streamId, authorizationToken);
|
var memoryJsonCacheManager = new MemoryJsonCacheManager(jsonCache);
|
||||||
return new DeserializeProcess(
|
#pragma warning restore CA2000
|
||||||
sqLiteJsonCacheManager,
|
return new SerializeProcess(
|
||||||
serverObjectManager,
|
|
||||||
progress,
|
progress,
|
||||||
baseDeserializer,
|
new ObjectSaver(
|
||||||
|
progress,
|
||||||
|
memoryJsonCacheManager,
|
||||||
|
new MemoryServerObjectManager(objects),
|
||||||
|
loggerFactory.CreateLogger<ObjectSaver>(),
|
||||||
|
cancellationToken
|
||||||
|
),
|
||||||
|
baseChildFinder,
|
||||||
|
new BaseSerializer(memoryJsonCacheManager, objectSerializerFactory),
|
||||||
loggerFactory,
|
loggerFactory,
|
||||||
cancellationToken,
|
cancellationToken,
|
||||||
options
|
options
|
||||||
|
|||||||
@@ -13,6 +13,12 @@ using Speckle.Sdk.Transports.ServerUtils;
|
|||||||
|
|
||||||
namespace Speckle.Sdk.Serialisation.V2;
|
namespace Speckle.Sdk.Serialisation.V2;
|
||||||
|
|
||||||
|
public class ServerObjectManagerOptions(TimeSpan? timeout = null, string? boundary = null)
|
||||||
|
{
|
||||||
|
public TimeSpan Timeout => timeout ?? TimeSpan.FromSeconds(120);
|
||||||
|
public string Boundary => boundary ??= Guid.NewGuid().ToString();
|
||||||
|
}
|
||||||
|
|
||||||
[GenerateAutoInterface]
|
[GenerateAutoInterface]
|
||||||
public class ServerObjectManager : IServerObjectManager
|
public class ServerObjectManager : IServerObjectManager
|
||||||
{
|
{
|
||||||
@@ -21,6 +27,7 @@ public class ServerObjectManager : IServerObjectManager
|
|||||||
private readonly ISdkActivityFactory _activityFactory;
|
private readonly ISdkActivityFactory _activityFactory;
|
||||||
private readonly HttpClient _client;
|
private readonly HttpClient _client;
|
||||||
private readonly string _streamId;
|
private readonly string _streamId;
|
||||||
|
private readonly ServerObjectManagerOptions _serverObjectManagerOptions;
|
||||||
|
|
||||||
public ServerObjectManager(
|
public ServerObjectManager(
|
||||||
ISpeckleHttp speckleHttp,
|
ISpeckleHttp speckleHttp,
|
||||||
@@ -28,13 +35,14 @@ public class ServerObjectManager : IServerObjectManager
|
|||||||
Uri baseUri,
|
Uri baseUri,
|
||||||
string streamId,
|
string streamId,
|
||||||
string? authorizationToken,
|
string? authorizationToken,
|
||||||
int timeoutSeconds = 120
|
ServerObjectManagerOptions? options = null
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
_serverObjectManagerOptions = options ?? new();
|
||||||
_activityFactory = activityFactory;
|
_activityFactory = activityFactory;
|
||||||
_client = speckleHttp.CreateHttpClient(
|
_client = speckleHttp.CreateHttpClient(
|
||||||
new HttpClientHandler { AutomaticDecompression = DecompressionMethods.GZip },
|
new HttpClientHandler { AutomaticDecompression = DecompressionMethods.GZip },
|
||||||
timeoutSeconds: timeoutSeconds,
|
timeoutSeconds: (int)_serverObjectManagerOptions.Timeout.TotalSeconds,
|
||||||
authorizationToken: authorizationToken
|
authorizationToken: authorizationToken
|
||||||
);
|
);
|
||||||
_client.BaseAddress = baseUri;
|
_client.BaseAddress = baseUri;
|
||||||
@@ -139,10 +147,8 @@ public class ServerObjectManager : IServerObjectManager
|
|||||||
CancellationToken cancellationToken
|
CancellationToken cancellationToken
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
using var _ = _activityFactory.Start();
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
// Stopwatch sw = new Stopwatch(); sw.Start();
|
|
||||||
|
|
||||||
string objectsPostParameter = JsonConvert.SerializeObject(objectIds);
|
string objectsPostParameter = JsonConvert.SerializeObject(objectIds);
|
||||||
var payload = new Dictionary<string, string> { { "objects", objectsPostParameter } };
|
var payload = new Dictionary<string, string> { { "objects", objectsPostParameter } };
|
||||||
string serializedPayload = JsonConvert.SerializeObject(payload);
|
string serializedPayload = JsonConvert.SerializeObject(payload);
|
||||||
@@ -169,6 +175,7 @@ public class ServerObjectManager : IServerObjectManager
|
|||||||
CancellationToken cancellationToken
|
CancellationToken cancellationToken
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
using var _ = _activityFactory.Start();
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
using HttpRequestMessage message = new()
|
using HttpRequestMessage message = new()
|
||||||
@@ -177,7 +184,7 @@ public class ServerObjectManager : IServerObjectManager
|
|||||||
Method = HttpMethod.Post,
|
Method = HttpMethod.Post,
|
||||||
};
|
};
|
||||||
|
|
||||||
MultipartFormDataContent multipart = new();
|
MultipartFormDataContent multipart = new(_serverObjectManagerOptions.Boundary);
|
||||||
|
|
||||||
int mpId = 0;
|
int mpId = 0;
|
||||||
var ctBuilder = new StringBuilder("[");
|
var ctBuilder = new StringBuilder("[");
|
||||||
|
|||||||
@@ -8,6 +8,13 @@ namespace Speckle.Sdk.Serialisation.V2;
|
|||||||
public class ServerObjectManagerFactory(ISpeckleHttp speckleHttp, ISdkActivityFactory activityFactory)
|
public class ServerObjectManagerFactory(ISpeckleHttp speckleHttp, ISdkActivityFactory activityFactory)
|
||||||
: IServerObjectManagerFactory
|
: IServerObjectManagerFactory
|
||||||
{
|
{
|
||||||
public IServerObjectManager Create(Uri url, string streamId, string? authorizationToken, int timeoutSeconds = 120) =>
|
public IServerObjectManager Create(Uri url, string streamId, string? authorizationToken, TimeSpan? timeout = null) =>
|
||||||
new ServerObjectManager(speckleHttp, activityFactory, url, streamId, authorizationToken, timeoutSeconds);
|
new ServerObjectManager(
|
||||||
|
speckleHttp,
|
||||||
|
activityFactory,
|
||||||
|
url,
|
||||||
|
streamId,
|
||||||
|
authorizationToken,
|
||||||
|
new ServerObjectManagerOptions(timeout: timeout)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,47 +1,117 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
using Speckle.Sdk.Api;
|
||||||
|
using Speckle.Sdk.Credentials;
|
||||||
using Speckle.Sdk.Dependencies;
|
using Speckle.Sdk.Dependencies;
|
||||||
using Speckle.Sdk.Host;
|
using Speckle.Sdk.Host;
|
||||||
using Speckle.Sdk.Logging;
|
using Speckle.Sdk.Logging;
|
||||||
|
using Speckle.Sdk.Models.GraphTraversal;
|
||||||
|
using Speckle.Sdk.Serialisation.V2;
|
||||||
|
using Speckle.Sdk.Serialisation.V2.Receive;
|
||||||
|
using Speckle.Sdk.Serialisation.V2.Send;
|
||||||
|
using Speckle.Sdk.SQLite;
|
||||||
|
using Speckle.Sdk.Transports;
|
||||||
|
using Speckle.Sdk.Transports.ServerUtils;
|
||||||
|
|
||||||
namespace Speckle.Sdk;
|
namespace Speckle.Sdk;
|
||||||
|
|
||||||
|
public record Application(string Name, string Slug);
|
||||||
|
|
||||||
|
public record SpeckleSdkOptions(
|
||||||
|
Application Application,
|
||||||
|
string ApplicationVersion,
|
||||||
|
string? SpeckleVersion,
|
||||||
|
IEnumerable<Assembly>? Assemblies
|
||||||
|
);
|
||||||
|
|
||||||
public static class ServiceRegistration
|
public static class ServiceRegistration
|
||||||
{
|
{
|
||||||
|
private static string GetAssemblyVersion() =>
|
||||||
|
Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "Unknown";
|
||||||
|
|
||||||
public static IServiceCollection AddSpeckleSdk(
|
public static IServiceCollection AddSpeckleSdk(
|
||||||
this IServiceCollection serviceCollection,
|
this IServiceCollection serviceCollection,
|
||||||
HostApplication application,
|
Application application,
|
||||||
HostAppVersion version,
|
string applicationVersion,
|
||||||
string speckleVersion
|
string? speckleVersion = null,
|
||||||
|
IEnumerable<Assembly>? assemblies = null
|
||||||
|
) => serviceCollection.AddSpeckleSdk(new(application, applicationVersion, speckleVersion, assemblies));
|
||||||
|
|
||||||
|
public static IServiceCollection AddSpeckleSdk(
|
||||||
|
this IServiceCollection serviceCollection,
|
||||||
|
Application application,
|
||||||
|
string applicationVersion,
|
||||||
|
string? speckleVersion,
|
||||||
|
params Assembly[] assemblies
|
||||||
|
) => serviceCollection.AddSpeckleSdk(new(application, applicationVersion, speckleVersion, assemblies));
|
||||||
|
|
||||||
|
public static IServiceCollection AddSpeckleSdk(
|
||||||
|
this IServiceCollection serviceCollection,
|
||||||
|
Application application,
|
||||||
|
string applicationVersion,
|
||||||
|
params Assembly[] assemblies
|
||||||
|
) => serviceCollection.AddSpeckleSdk(new(application, applicationVersion, null, assemblies));
|
||||||
|
|
||||||
|
public static IServiceCollection AddSpeckleSdk(
|
||||||
|
this IServiceCollection serviceCollection,
|
||||||
|
SpeckleSdkOptions speckleSdkOptions
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
var currentAssembly = Assembly.GetExecutingAssembly();
|
||||||
|
var allAssembles = speckleSdkOptions.Assemblies?.ToList() ?? [];
|
||||||
|
if (!allAssembles.Contains(currentAssembly))
|
||||||
|
{
|
||||||
|
allAssembles.Add(currentAssembly);
|
||||||
|
}
|
||||||
|
TypeLoader.Reset();
|
||||||
|
TypeLoader.Initialize(allAssembles.ToArray());
|
||||||
serviceCollection.AddLogging();
|
serviceCollection.AddLogging();
|
||||||
string name = application.Name;
|
|
||||||
|
|
||||||
serviceCollection.AddSingleton<ISpeckleApplication>(
|
serviceCollection.AddSingleton<ISpeckleApplication>(
|
||||||
new SpeckleApplication
|
new SpeckleApplication
|
||||||
{
|
{
|
||||||
HostApplication = name,
|
HostApplication = speckleSdkOptions.Application.Name,
|
||||||
SpeckleVersion = speckleVersion,
|
HostApplicationVersion = speckleSdkOptions.ApplicationVersion,
|
||||||
HostApplicationVersion = HostApplications.GetVersion(version),
|
Slug = speckleSdkOptions.Application.Slug,
|
||||||
Slug = application.Slug,
|
SpeckleVersion = speckleSdkOptions.SpeckleVersion ?? GetAssemblyVersion(),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
serviceCollection.TryAddSingleton<ISdkActivityFactory, NullActivityFactory>();
|
serviceCollection.TryAddSingleton<ISdkActivityFactory, NullActivityFactory>();
|
||||||
serviceCollection.TryAddSingleton<ISdkMetricsFactory, NullSdkMetricsFactory>();
|
serviceCollection.TryAddSingleton<ISdkMetricsFactory, NullSdkMetricsFactory>();
|
||||||
serviceCollection.AddMatchingInterfacesAsTransient(Assembly.GetExecutingAssembly());
|
serviceCollection.AddMatchingInterfacesAsTransient(
|
||||||
|
Assembly.GetExecutingAssembly(),
|
||||||
|
typeof(ServerTransport),
|
||||||
|
typeof(Account),
|
||||||
|
typeof(ServerApi),
|
||||||
|
typeof(SqLiteJsonCacheManager),
|
||||||
|
typeof(ServerObjectManager),
|
||||||
|
typeof(BaseSerializer),
|
||||||
|
typeof(SerializeProcess),
|
||||||
|
typeof(ObjectSaver),
|
||||||
|
typeof(ObjectSerializer),
|
||||||
|
typeof(ObjectDeserializer),
|
||||||
|
typeof(DeserializeProcess),
|
||||||
|
typeof(ObjectLoader),
|
||||||
|
typeof(TraversalRule),
|
||||||
|
typeof(Client)
|
||||||
|
);
|
||||||
serviceCollection.AddMatchingInterfacesAsTransient(typeof(GraphQLRetry).Assembly);
|
serviceCollection.AddMatchingInterfacesAsTransient(typeof(GraphQLRetry).Assembly);
|
||||||
return serviceCollection;
|
return serviceCollection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IServiceCollection AddMatchingInterfacesAsTransient(
|
public static IServiceCollection AddMatchingInterfacesAsTransient(
|
||||||
this IServiceCollection serviceCollection,
|
this IServiceCollection serviceCollection,
|
||||||
Assembly assembly
|
Assembly assembly,
|
||||||
|
params Type[] classesToIgnore
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
foreach (var type in assembly.ExportedTypes.Where(t => t.IsNonAbstractClass()))
|
foreach (var type in assembly.ExportedTypes.Where(t => t.IsNonAbstractClass()))
|
||||||
{
|
{
|
||||||
|
if (classesToIgnore.Contains(type))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
foreach (var matchingInterface in type.FindMatchingInterface())
|
foreach (var matchingInterface in type.FindMatchingInterface())
|
||||||
{
|
{
|
||||||
serviceCollection.TryAddTransient(matchingInterface, type);
|
serviceCollection.TryAddTransient(matchingInterface, type);
|
||||||
|
|||||||
@@ -1,42 +1,36 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup Label="Compiler Properties">
|
<PropertyGroup Label="Compiler Properties">
|
||||||
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
|
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
|
||||||
<Configurations>Debug;Release;Local</Configurations>
|
<Configurations>Debug;Release;Local</Configurations>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Label="Nugetspec Package Properties">
|
<PropertyGroup Label="Nugetspec Package Properties">
|
||||||
<PackageId>Speckle.Sdk</PackageId>
|
<PackageId>Speckle.Sdk</PackageId>
|
||||||
<Description>The .NET SDK for Speckle</Description>
|
<Description>The .NET SDK for Speckle</Description>
|
||||||
<PackageTags>$(PackageTags) core sdk</PackageTags>
|
<PackageTags>$(PackageTags) core sdk</PackageTags>
|
||||||
<NoWarn>$(NoWarn);CS8618</NoWarn>
|
<NoWarn>$(NoWarn);CS8618</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Label="Nuget Package Properties">
|
<PropertyGroup Label="Nuget Package Properties">
|
||||||
<IsPackable>true</IsPackable>
|
<IsPackable>true</IsPackable>
|
||||||
<IncludeSymbols>true</IncludeSymbols>
|
<IncludeSymbols>true</IncludeSymbols>
|
||||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup Label="Expose internals to test projects">
|
<ItemGroup Label="Expose internals to test projects">
|
||||||
<InternalsVisibleTo Include="Speckle.Sdk.Tests.Unit" />
|
<InternalsVisibleTo Include="Speckle.Sdk.Tests.Unit" />
|
||||||
<InternalsVisibleTo Include="Speckle.Sdk.Tests.Integration" />
|
<InternalsVisibleTo Include="Speckle.Sdk.Tests.Integration" />
|
||||||
<InternalsVisibleTo Include="Speckle.Sdk.Serialization.Tests" />
|
<InternalsVisibleTo Include="Speckle.Sdk.Serialization.Tests" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="GraphQL.Client"/>
|
<PackageReference Include="GraphQL.Client" />
|
||||||
<PackageReference Include="Microsoft.CSharp" />
|
<PackageReference Include="Microsoft.CSharp" />
|
||||||
<PackageReference Include="Microsoft.Data.Sqlite"/>
|
<PackageReference Include="Microsoft.Data.Sqlite" />
|
||||||
<PackageReference Include="Speckle.DoubleNumerics" />
|
<PackageReference Include="Speckle.DoubleNumerics" />
|
||||||
<PackageReference Include="Speckle.Newtonsoft.Json" />
|
<PackageReference Include="Speckle.Newtonsoft.Json" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0'">
|
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0'">
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" OverrideVersion="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" OverrideVersion="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging" OverrideVersion="8.0.0"/>
|
<PackageReference Include="Microsoft.Extensions.Logging" OverrideVersion="8.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0'">
|
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0'">
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
|
||||||
@@ -46,5 +40,4 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Speckle.Sdk.Dependencies\Speckle.Sdk.Dependencies.csproj" />
|
<ProjectReference Include="..\Speckle.Sdk.Dependencies\Speckle.Sdk.Dependencies.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
namespace Speckle.Sdk;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// These are exceptions who's message is not user friendly
|
|
||||||
/// </summary>
|
|
||||||
public class SpeckleNonUserFacingException : SpeckleException
|
|
||||||
{
|
|
||||||
public SpeckleNonUserFacingException() { }
|
|
||||||
|
|
||||||
public SpeckleNonUserFacingException(string? message)
|
|
||||||
: base(message) { }
|
|
||||||
|
|
||||||
public SpeckleNonUserFacingException(string? message, Exception? innerException)
|
|
||||||
: base(message, innerException) { }
|
|
||||||
}
|
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
using System.Diagnostics;
|
|
||||||
using System.Text;
|
|
||||||
using Speckle.Sdk.Logging;
|
|
||||||
|
|
||||||
namespace Speckle.Sdk.Transports;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes speckle objects to disk.
|
|
||||||
/// </summary>
|
|
||||||
public class DiskTransport : ICloneable, ITransport
|
|
||||||
{
|
|
||||||
public DiskTransport(string? basePath = null)
|
|
||||||
{
|
|
||||||
basePath ??= Path.Combine(SpecklePathProvider.UserSpeckleFolderPath, "DiskTransportFiles");
|
|
||||||
|
|
||||||
RootPath = Path.Combine(basePath);
|
|
||||||
|
|
||||||
Directory.CreateDirectory(RootPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string RootPath { get; set; }
|
|
||||||
|
|
||||||
public object Clone()
|
|
||||||
{
|
|
||||||
return new DiskTransport
|
|
||||||
{
|
|
||||||
RootPath = RootPath,
|
|
||||||
CancellationToken = CancellationToken,
|
|
||||||
OnErrorAction = OnErrorAction,
|
|
||||||
OnProgressAction = OnProgressAction,
|
|
||||||
TransportName = TransportName,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public string TransportName { get; set; } = "Disk";
|
|
||||||
|
|
||||||
public Dictionary<string, object> TransportContext =>
|
|
||||||
new()
|
|
||||||
{
|
|
||||||
{ "name", TransportName },
|
|
||||||
{ "type", GetType().Name },
|
|
||||||
{ "basePath", RootPath },
|
|
||||||
};
|
|
||||||
|
|
||||||
public CancellationToken CancellationToken { get; set; }
|
|
||||||
|
|
||||||
public IProgress<ProgressArgs>? OnProgressAction { get; set; }
|
|
||||||
|
|
||||||
public Action<string, Exception>? OnErrorAction { get; set; }
|
|
||||||
|
|
||||||
public int SavedObjectCount { get; private set; }
|
|
||||||
|
|
||||||
public TimeSpan Elapsed { get; set; } = TimeSpan.Zero;
|
|
||||||
|
|
||||||
public void BeginWrite()
|
|
||||||
{
|
|
||||||
SavedObjectCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void EndWrite() { }
|
|
||||||
|
|
||||||
public Task<string?> GetObject(string id)
|
|
||||||
{
|
|
||||||
CancellationToken.ThrowIfCancellationRequested();
|
|
||||||
|
|
||||||
var filePath = Path.Combine(RootPath, id);
|
|
||||||
if (File.Exists(filePath))
|
|
||||||
{
|
|
||||||
return Task.FromResult<string?>(File.ReadAllText(filePath, Encoding.UTF8));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.FromResult<string?>(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SaveObject(string id, string serializedObject)
|
|
||||||
{
|
|
||||||
var stopwatch = Stopwatch.StartNew();
|
|
||||||
CancellationToken.ThrowIfCancellationRequested();
|
|
||||||
|
|
||||||
var filePath = Path.Combine(RootPath, id);
|
|
||||||
if (File.Exists(filePath))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
File.WriteAllText(filePath, serializedObject, Encoding.UTF8);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
throw new TransportException(this, $"Failed to write object {id} to disk", ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
SavedObjectCount++;
|
|
||||||
stopwatch.Stop();
|
|
||||||
Elapsed += stopwatch.Elapsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task WriteComplete()
|
|
||||||
{
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> CopyObjectAndChildren(string id, ITransport targetTransport)
|
|
||||||
{
|
|
||||||
string res = await TransportHelpers
|
|
||||||
.CopyObjectAndChildrenAsync(id, this, targetTransport, CancellationToken)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<Dictionary<string, bool>> HasObjects(IReadOnlyList<string> objectIds)
|
|
||||||
{
|
|
||||||
Dictionary<string, bool> ret = new();
|
|
||||||
foreach (string objectId in objectIds)
|
|
||||||
{
|
|
||||||
var filePath = Path.Combine(RootPath, objectId);
|
|
||||||
ret[objectId] = File.Exists(filePath);
|
|
||||||
}
|
|
||||||
return Task.FromResult(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return $"Disk Transport @{RootPath}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,12 +2,6 @@
|
|||||||
"version": 2,
|
"version": 2,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
".NETStandard,Version=v2.0": {
|
".NETStandard,Version=v2.0": {
|
||||||
"GitVersion.MsBuild": {
|
|
||||||
"type": "Direct",
|
|
||||||
"requested": "[5.12.0, )",
|
|
||||||
"resolved": "5.12.0",
|
|
||||||
"contentHash": "dJuigXycpJNOiLT9or7mkHSkGFHgGW3/p6cNNYEKZBa7Hhp1FdX/cvqYWWYhRLpfoZOedeA7aRbYiOB3vW/dvA=="
|
|
||||||
},
|
|
||||||
"GraphQL.Client": {
|
"GraphQL.Client": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[6.0.0, )",
|
"requested": "[6.0.0, )",
|
||||||
@@ -301,12 +295,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"net8.0": {
|
"net8.0": {
|
||||||
"GitVersion.MsBuild": {
|
|
||||||
"type": "Direct",
|
|
||||||
"requested": "[5.12.0, )",
|
|
||||||
"resolved": "5.12.0",
|
|
||||||
"contentHash": "dJuigXycpJNOiLT9or7mkHSkGFHgGW3/p6cNNYEKZBa7Hhp1FdX/cvqYWWYhRLpfoZOedeA7aRbYiOB3vW/dvA=="
|
|
||||||
},
|
|
||||||
"GraphQL.Client": {
|
"GraphQL.Client": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[6.0.0, )",
|
"requested": "[6.0.0, )",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using Speckle.Objects.Geometry;
|
using Speckle.Objects.Geometry;
|
||||||
using Speckle.Sdk.Host;
|
using Speckle.Sdk.Host;
|
||||||
|
using Speckle.Sdk.Models;
|
||||||
using Speckle.Sdk.Testing;
|
using Speckle.Sdk.Testing;
|
||||||
|
|
||||||
namespace Speckle.Objects.Tests.Unit;
|
namespace Speckle.Objects.Tests.Unit;
|
||||||
@@ -12,6 +13,6 @@ public static class Module
|
|||||||
{
|
{
|
||||||
SpeckleVerify.Initialize();
|
SpeckleVerify.Initialize();
|
||||||
TypeLoader.Reset();
|
TypeLoader.Reset();
|
||||||
TypeLoader.Initialize(typeof(Polyline).Assembly);
|
TypeLoader.Initialize(typeof(Base).Assembly, typeof(Polyline).Assembly);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+76
@@ -0,0 +1,76 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"applicationId": null,
|
||||||
|
"data": [
|
||||||
|
3,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
"id": "13da8e855141b835e68bba721411046e",
|
||||||
|
"speckle_type": "Speckle.Core.Models.DataChunk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"__closure": {
|
||||||
|
"13da8e855141b835e68bba721411046e": 100,
|
||||||
|
"cc9d42395b317ed25947b1285bbd5103": 100,
|
||||||
|
"ede4848fc3abda9275a19fee3447ffbd": 100
|
||||||
|
},
|
||||||
|
"applicationId": "asdfasdf",
|
||||||
|
"area": 42.0,
|
||||||
|
"bbox": null,
|
||||||
|
"colors": [
|
||||||
|
{
|
||||||
|
"__closure": null,
|
||||||
|
"referencedId": "cc9d42395b317ed25947b1285bbd5103",
|
||||||
|
"speckle_type": "reference"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"faces": [
|
||||||
|
{
|
||||||
|
"__closure": null,
|
||||||
|
"referencedId": "13da8e855141b835e68bba721411046e",
|
||||||
|
"speckle_type": "reference"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "85fdad62ca935832449ffb359ebed7b8",
|
||||||
|
"speckle_type": "Objects.Geometry.Mesh",
|
||||||
|
"textureCoordinates": [],
|
||||||
|
"units": "m",
|
||||||
|
"vertexNormals": [],
|
||||||
|
"vertices": [
|
||||||
|
{
|
||||||
|
"__closure": null,
|
||||||
|
"referencedId": "ede4848fc3abda9275a19fee3447ffbd",
|
||||||
|
"speckle_type": "reference"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"volume": 420.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"applicationId": null,
|
||||||
|
"data": [
|
||||||
|
-10185235,
|
||||||
|
-10185235,
|
||||||
|
-10185235
|
||||||
|
],
|
||||||
|
"id": "cc9d42395b317ed25947b1285bbd5103",
|
||||||
|
"speckle_type": "Speckle.Core.Models.DataChunk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"applicationId": null,
|
||||||
|
"data": [
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
1.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"id": "ede4848fc3abda9275a19fee3447ffbd",
|
||||||
|
"speckle_type": "Speckle.Core.Models.DataChunk"
|
||||||
|
}
|
||||||
|
]
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"applicationId": "iosdf;juasdfioj",
|
||||||
|
"id": "035cad79be39e59c765b7946c4ea5f0c",
|
||||||
|
"speckle_type": "Objects.Geometry.Point",
|
||||||
|
"units": "ft",
|
||||||
|
"x": 123.123,
|
||||||
|
"y": 111.222,
|
||||||
|
"z": -0.001
|
||||||
|
}
|
||||||
|
]
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"applicationId": "iosdf;juasdfioj",
|
||||||
|
"bbox": null,
|
||||||
|
"id": "282d78f7ca1a2ec37bf3fcf3f24e7a27",
|
||||||
|
"speckle_type": "Objects.Geometry.Vector",
|
||||||
|
"units": "in",
|
||||||
|
"x": 321.321,
|
||||||
|
"y": 222.111,
|
||||||
|
"z": -1.001
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using Speckle.Sdk.Models;
|
||||||
|
using Speckle.Sdk.Serialisation;
|
||||||
|
using Speckle.Sdk.Serialisation.V2.Send;
|
||||||
|
using Speckle.Sdk.Testing;
|
||||||
|
|
||||||
|
namespace Speckle.Objects.SerializationTests;
|
||||||
|
|
||||||
|
public sealed class ObjectsSerializationTest
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(ObjectsTestData.TheoryData), MemberType = typeof(ObjectsTestData))]
|
||||||
|
public async Task SerializeAndVerify(Base testCase)
|
||||||
|
{
|
||||||
|
var serialized = Serialize(testCase);
|
||||||
|
|
||||||
|
await VerifySerialized(serialized).UseParameters(testCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IReadOnlyList<(Id, Json, Dictionary<Id, int>)> Serialize(Base data)
|
||||||
|
{
|
||||||
|
using var serializer = new ObjectSerializerFactory(new BasePropertyGatherer()).Create(
|
||||||
|
new Dictionary<Id, NodeInfo>(),
|
||||||
|
default
|
||||||
|
);
|
||||||
|
return serializer.Serialize(data).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SettingsTask VerifySerialized(IReadOnlyList<(Id, Json, Dictionary<Id, int>)> serializedResult)
|
||||||
|
{
|
||||||
|
var jsons = serializedResult.OrderBy(x => x.Item1.Value).Select(x => x.Item2).ToArray();
|
||||||
|
return SpeckleVerify.VerifyJsons(jsons);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
using Speckle.Objects.Geometry;
|
||||||
|
using Speckle.Sdk.Common;
|
||||||
|
using Speckle.Sdk.Models;
|
||||||
|
using Point = Speckle.Objects.Geometry.Point;
|
||||||
|
|
||||||
|
namespace Speckle.Objects.SerializationTests;
|
||||||
|
|
||||||
|
public static class ObjectsTestData
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Data set of simple valid Speckle Objects
|
||||||
|
/// Right now, we should have exactly one for each non-abstract object model.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static IEnumerable<Base> Data()
|
||||||
|
{
|
||||||
|
yield return new Mesh()
|
||||||
|
{
|
||||||
|
vertices = [0d, 0d, 0d, 1d, 0d, 0d, 1d, 1d, 0d],
|
||||||
|
faces = [3, 0, 1, 2],
|
||||||
|
applicationId = "asdfasdf",
|
||||||
|
units = Units.Meters,
|
||||||
|
area = 42,
|
||||||
|
volume = 420,
|
||||||
|
colors = [-10185235, -10185235, -10185235],
|
||||||
|
};
|
||||||
|
yield return new Point()
|
||||||
|
{
|
||||||
|
x = 123.123,
|
||||||
|
y = 111.222,
|
||||||
|
z = -0.001,
|
||||||
|
applicationId = "iosdf;juasdfioj",
|
||||||
|
units = Units.Feet,
|
||||||
|
};
|
||||||
|
yield return new Vector()
|
||||||
|
{
|
||||||
|
x = 321.321,
|
||||||
|
y = 222.111,
|
||||||
|
z = -1.001,
|
||||||
|
applicationId = "iosdf;juasdfioj",
|
||||||
|
units = Units.Inches,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TheoryData<Base> TheoryData => new(Data());
|
||||||
|
}
|
||||||
@@ -1,21 +1,18 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<IsTestProject>true</IsTestProject>
|
<IsTestProject>true</IsTestProject>
|
||||||
<PolySharpExcludeGeneratedTypes>System.Runtime.CompilerServices.IsExternalInit</PolySharpExcludeGeneratedTypes>
|
<PolySharpExcludeGeneratedTypes>System.Runtime.CompilerServices.IsExternalInit</PolySharpExcludeGeneratedTypes>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="altcover" />
|
<PackageReference Include="altcover" />
|
||||||
<PackageReference Include="AwesomeAssertions" />
|
<PackageReference Include="AwesomeAssertions" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||||
<PackageReference Include="xunit.assert" />
|
<PackageReference Include="xunit.assert" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio"/>
|
<PackageReference Include="xunit.runner.visualstudio" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\src\Speckle.Objects\Speckle.Objects.csproj" />
|
<ProjectReference Include="..\..\src\Speckle.Objects\Speckle.Objects.csproj" />
|
||||||
<ProjectReference Include="..\Speckle.Sdk.Testing\Speckle.Sdk.Testing.csproj" />
|
<ProjectReference Include="..\Speckle.Sdk.Testing\Speckle.Sdk.Testing.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -10,15 +10,9 @@
|
|||||||
},
|
},
|
||||||
"AwesomeAssertions": {
|
"AwesomeAssertions": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[8.0.0, )",
|
"requested": "[8.1.0, )",
|
||||||
"resolved": "8.0.0",
|
"resolved": "8.1.0",
|
||||||
"contentHash": "6fWiV7mGZUzZXzeiW3hWF0nJokuuNm4hnzuqbM3IXHqGYkWnHl65+wNpuQ73xfJXClX0fmfKcTdQ2Ula719IDg=="
|
"contentHash": "IfNC4cpXPi9tclWvuNO9lfkuIxJsUTLTS1NXto55jDrAUQJYl0zLI9ByISrfkbBE2Xtg+IWaAXQ6jnUx3anDuw=="
|
||||||
},
|
|
||||||
"GitVersion.MsBuild": {
|
|
||||||
"type": "Direct",
|
|
||||||
"requested": "[5.12.0, )",
|
|
||||||
"resolved": "5.12.0",
|
|
||||||
"contentHash": "dJuigXycpJNOiLT9or7mkHSkGFHgGW3/p6cNNYEKZBa7Hhp1FdX/cvqYWWYhRLpfoZOedeA7aRbYiOB3vW/dvA=="
|
|
||||||
},
|
},
|
||||||
"Microsoft.NET.Test.Sdk": {
|
"Microsoft.NET.Test.Sdk": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
@@ -66,22 +60,30 @@
|
|||||||
},
|
},
|
||||||
"Argon": {
|
"Argon": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "0.26.0",
|
"resolved": "0.28.0",
|
||||||
"contentHash": "n7btGXdtRyprGnpLMpBs6rLScxlvPtVWwmTR8h7CtJvpZXBGhGvibEdZxRjeTZNrwf403jJ0ZPpt35Pz/NaNsw=="
|
"contentHash": "78BmoFm8SK733nq4F/SjqNKkXJHdrg/MslvYfNjJX/nM/mEkltHUzPJRjBE9VI/zghsjFPQxMRPEUaqIgg98zg=="
|
||||||
|
},
|
||||||
|
"Castle.Core": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "5.1.1",
|
||||||
|
"contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Diagnostics.EventLog": "6.0.0"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"DiffEngine": {
|
"DiffEngine": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "15.9.0",
|
"resolved": "16.2.1",
|
||||||
"contentHash": "shE6+tO4w5BmQTX0z+WnUV4UfmPNn6oTqBINbkts6OP0Icyx5WROSDzjjb95EwVYC4IAS+PxxS4Vbapxz4hkdw==",
|
"contentHash": "UfMgXClqOGkPNfth210upiTY18LPCgjsfNrh0Olo5qI+QTkkCO6wHSuOwknxJdKtsWoaJ+E132Y2nzD0PiLWRw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"EmptyFiles": "8.7.1",
|
"EmptyFiles": "8.9.1",
|
||||||
"System.Management": "9.0.1"
|
"System.Management": "8.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"EmptyFiles": {
|
"EmptyFiles": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "8.7.1",
|
"resolved": "8.9.1",
|
||||||
"contentHash": "C8pvg0TvG2Mkn5LGNFGkFgFu8SUgYFwiu8U3y34qGQnnwKmGnlQTfTIUrtzfSjPxA4q7L/kRu09U5p32otZ2Aw=="
|
"contentHash": "GbGf+oH/xiI3C5vJ5TnoA4sx7x7LhtOvN00fxihRZJsj40XuXk2TMz/4m26PfNSJj8JMAqo3BUBirjvam+3xkA=="
|
||||||
},
|
},
|
||||||
"FSharp.Core": {
|
"FSharp.Core": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
@@ -248,25 +250,30 @@
|
|||||||
},
|
},
|
||||||
"System.CodeDom": {
|
"System.CodeDom": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "9.0.1",
|
"resolved": "8.0.0",
|
||||||
"contentHash": "2J5uq+2smnj+u1jlyVJ6BGGqaK9fHcK/EwN7mbsuPqTI6dZr86br8Cg6o/5B+icQ9ANTvTDpJjnhDNtYYZijHQ=="
|
"contentHash": "WTlRjL6KWIMr/pAaq3rYqh0TJlzpouaQ/W1eelssHgtlwHAH25jXTkUphTYx9HaIIf7XA6qs/0+YhtLEQRkJ+Q=="
|
||||||
},
|
},
|
||||||
"System.ComponentModel.Annotations": {
|
"System.ComponentModel.Annotations": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "4.5.0",
|
"resolved": "4.5.0",
|
||||||
"contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg=="
|
"contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg=="
|
||||||
},
|
},
|
||||||
|
"System.Diagnostics.EventLog": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.0",
|
||||||
|
"contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw=="
|
||||||
|
},
|
||||||
"System.IO.Hashing": {
|
"System.IO.Hashing": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "9.0.1",
|
"resolved": "9.0.4",
|
||||||
"contentHash": "jY+E/PElNWQiazN0YHqZGvcSedcZ4Wt0Os1nnJ2SzR3gWZlhNRDkSXOhuHJcLuImD8SrJQQ8TfU0W4mVcit2hg=="
|
"contentHash": "WogPvgAFqQORFD8Iyha6RZ+/1QB3dsWRWxbwi8/HHVgiGQ8z0oMWpwe8Kk3Ti+Roe+P6a3sBg+WwBfEsyziZKg=="
|
||||||
},
|
},
|
||||||
"System.Management": {
|
"System.Management": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "9.0.1",
|
"resolved": "8.0.0",
|
||||||
"contentHash": "CLEo9O6FuO4GQ3ZQkGssg9CJ2w2TN7GMFf3wHTc7YVWJV4xoyJRPw+XIDQnCcSUJCrHhrAWOO60cAX29EV5LFQ==",
|
"contentHash": "jrK22i5LRzxZCfGb+tGmke2VH7oE0DvcDlJ1HAKYU8cPmD8XnpUT0bYn2Gy98GEhGjtfbR/sxKTVb+dE770pfA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"System.CodeDom": "9.0.1"
|
"System.CodeDom": "8.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"System.Memory": {
|
"System.Memory": {
|
||||||
@@ -304,13 +311,13 @@
|
|||||||
},
|
},
|
||||||
"Verify": {
|
"Verify": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "28.10.1",
|
"resolved": "29.4.0",
|
||||||
"contentHash": "2B/VtFN5jtF5g28kaM4GdJZTwb3pisd4+wL2NEPi9ZYe2lghWsCzS30V6LF1ILApLBfAorAstkU/Vw3sDWRqrg==",
|
"contentHash": "wlqJ6ygXORa3lrAaErTA4EWkDcK9pBG2k5iC/I5F2UpWfyV7aXw/+SwGiWYe4XSk9d7VObe4xi4+0AV9rLRsBw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Argon": "0.26.0",
|
"Argon": "0.28.0",
|
||||||
"DiffEngine": "15.9.0",
|
"DiffEngine": "16.2.1",
|
||||||
"SimpleInfoName": "3.1.0",
|
"SimpleInfoName": "3.1.0",
|
||||||
"System.IO.Hashing": "9.0.1"
|
"System.IO.Hashing": "9.0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"xunit.abstractions": {
|
"xunit.abstractions": {
|
||||||
@@ -359,11 +366,10 @@
|
|||||||
"speckle.sdk.testing": {
|
"speckle.sdk.testing": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.NET.Test.Sdk": "[17.13.0, )",
|
"Moq": "[4.20.72, )",
|
||||||
"Speckle.Sdk": "[1.0.0, )",
|
"Speckle.Sdk": "[1.0.0, )",
|
||||||
"Verify.Quibble": "[2.1.1, )",
|
"Verify.Quibble": "[2.1.1, )",
|
||||||
"Verify.Xunit": "[28.10.1, )",
|
"Verify.Xunit": "[29.4.0, )"
|
||||||
"xunit.runner.visualstudio": "[3.0.2, )"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"GraphQL.Client": {
|
"GraphQL.Client": {
|
||||||
@@ -411,6 +417,15 @@
|
|||||||
"Microsoft.Extensions.Options": "2.2.0"
|
"Microsoft.Extensions.Options": "2.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Moq": {
|
||||||
|
"type": "CentralTransitive",
|
||||||
|
"requested": "[4.20.72, )",
|
||||||
|
"resolved": "4.20.72",
|
||||||
|
"contentHash": "EA55cjyNn8eTNWrgrdZJH5QLFp2L43oxl1tlkoYUKIE9pRwL784OWiTXeCV5ApS+AMYEAlt7Fo03A2XfouvHmQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"Castle.Core": "5.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Speckle.DoubleNumerics": {
|
"Speckle.DoubleNumerics": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[4.1.0, )",
|
"requested": "[4.1.0, )",
|
||||||
@@ -436,15 +451,15 @@
|
|||||||
},
|
},
|
||||||
"Verify.Xunit": {
|
"Verify.Xunit": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[28.10.1, )",
|
"requested": "[29.4.0, )",
|
||||||
"resolved": "28.10.1",
|
"resolved": "29.4.0",
|
||||||
"contentHash": "mkG7agMlx8oAEGcHoRY72hyDyNTdLIrzbfmniXFQgQ3yKulAHSYvYc9quzhpg0Sy+jb3svbdLqnRSg0VRhet3A==",
|
"contentHash": "P8HYW7aromKGm90Cgx0XKL3qKKmYZHDwHTQfBfDCCPnhNlVWevJzrpuUQ0+3vTVdRAtsts2a1OE/tD+3yjJbHA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Argon": "0.26.0",
|
"Argon": "0.28.0",
|
||||||
"DiffEngine": "15.9.0",
|
"DiffEngine": "16.2.1",
|
||||||
"SimpleInfoName": "3.1.0",
|
"SimpleInfoName": "3.1.0",
|
||||||
"System.IO.Hashing": "9.0.1",
|
"System.IO.Hashing": "9.0.4",
|
||||||
"Verify": "28.10.1",
|
"Verify": "29.4.0",
|
||||||
"xunit.abstractions": "2.0.3",
|
"xunit.abstractions": "2.0.3",
|
||||||
"xunit.extensibility.execution": "2.9.3"
|
"xunit.extensibility.execution": "2.9.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,14 @@
|
|||||||
#pragma warning disable CA1506
|
#pragma warning disable CA1506
|
||||||
using System.Reflection;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging.Abstractions;
|
|
||||||
using Speckle.Sdk;
|
using Speckle.Sdk;
|
||||||
using Speckle.Sdk.Credentials;
|
using Speckle.Sdk.Credentials;
|
||||||
using Speckle.Sdk.Host;
|
|
||||||
using Speckle.Sdk.Models;
|
|
||||||
using Speckle.Sdk.Serialisation.V2;
|
using Speckle.Sdk.Serialisation.V2;
|
||||||
using Speckle.Sdk.Serialisation.V2.Receive;
|
|
||||||
using Speckle.Sdk.Serialisation.V2.Send;
|
using Speckle.Sdk.Serialisation.V2.Send;
|
||||||
using Speckle.Sdk.Serialization.Testing;
|
using Speckle.Sdk.Serialization.Testing;
|
||||||
using Speckle.Sdk.SQLite;
|
|
||||||
|
|
||||||
const bool skipCacheReceive = false;
|
const bool skipCacheReceive = false;
|
||||||
const bool skipCacheSendCheck = true;
|
const bool skipCacheSendCheck = true;
|
||||||
const bool skipCacheSendSave = false;
|
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 url = "https://latest.speckle.systems/projects/a3ac1b2706/models/59d3b0f3c6"; //small?
|
||||||
var streamId = "a3ac1b2706";
|
var streamId = "a3ac1b2706";
|
||||||
@@ -33,7 +25,7 @@ var streamId = "2099ac4b5f";
|
|||||||
var rootId = "30fb4cbe6eb2202b9e7b4a4fcc3dd2b6";*/
|
var rootId = "30fb4cbe6eb2202b9e7b4a4fcc3dd2b6";*/
|
||||||
|
|
||||||
var serviceCollection = new ServiceCollection();
|
var serviceCollection = new ServiceCollection();
|
||||||
serviceCollection.AddSpeckleSdk(HostApplications.Navisworks, HostAppVersion.v2023, "Test");
|
serviceCollection.AddSpeckleSdk(new("Tests", "test"), "v3");
|
||||||
var serviceProvider = serviceCollection.BuildServiceProvider();
|
var serviceProvider = serviceCollection.BuildServiceProvider();
|
||||||
|
|
||||||
Console.WriteLine("Attach");
|
Console.WriteLine("Attach");
|
||||||
@@ -41,21 +33,15 @@ Console.WriteLine("Attach");
|
|||||||
var token = serviceProvider.GetRequiredService<IAccountManager>().GetDefaultAccount()?.token;
|
var token = serviceProvider.GetRequiredService<IAccountManager>().GetDefaultAccount()?.token;
|
||||||
var progress = new Progress(true);
|
var progress = new Progress(true);
|
||||||
|
|
||||||
var factory = new SerializeProcessFactory(
|
var factory = serviceProvider.GetRequiredService<IDeserializeProcessFactory>();
|
||||||
new BaseChildFinder(new BasePropertyGatherer()),
|
|
||||||
new ObjectSerializerFactory(new BasePropertyGatherer()),
|
|
||||||
new BaseDeserializer(new ObjectDeserializerFactory()),
|
|
||||||
serviceProvider.GetRequiredService<ISqLiteJsonCacheManagerFactory>(),
|
|
||||||
serviceProvider.GetRequiredService<IServerObjectManagerFactory>(),
|
|
||||||
new NullLoggerFactory()
|
|
||||||
);
|
|
||||||
var process = factory.CreateDeserializeProcess(new Uri(url), streamId, token, progress, default, new(skipCacheReceive));
|
var process = factory.CreateDeserializeProcess(new Uri(url), streamId, token, progress, default, new(skipCacheReceive));
|
||||||
var @base = await process.Deserialize(rootId).ConfigureAwait(false);
|
var @base = await process.Deserialize(rootId).ConfigureAwait(false);
|
||||||
Console.WriteLine("Deserialized");
|
Console.WriteLine("Deserialized");
|
||||||
Console.ReadLine();
|
Console.ReadLine();
|
||||||
Console.WriteLine("Executing");
|
Console.WriteLine("Executing");
|
||||||
|
|
||||||
var process2 = factory.CreateSerializeProcess(
|
var serializeProcessFactory = serviceProvider.GetRequiredService<ISerializeProcessFactory>();
|
||||||
|
var serializeProcess = serializeProcessFactory.CreateSerializeProcess(
|
||||||
new Uri(url),
|
new Uri(url),
|
||||||
streamId,
|
streamId,
|
||||||
token,
|
token,
|
||||||
@@ -63,8 +49,8 @@ var process2 = factory.CreateSerializeProcess(
|
|||||||
default,
|
default,
|
||||||
new SerializeProcessOptions(skipCacheSendCheck, skipCacheSendSave, true, true)
|
new SerializeProcessOptions(skipCacheSendCheck, skipCacheSendSave, true, true)
|
||||||
);
|
);
|
||||||
await process2.Serialize(@base).ConfigureAwait(false);
|
await serializeProcess.Serialize(@base).ConfigureAwait(false);
|
||||||
Console.WriteLine("Detach");
|
Console.WriteLine("Detach");
|
||||||
Console.ReadLine();
|
Console.ReadLine();
|
||||||
await process2.DisposeAsync().ConfigureAwait(false);
|
await serializeProcess.DisposeAsync().ConfigureAwait(false);
|
||||||
#pragma warning restore CA1506
|
#pragma warning restore CA1506
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
<PropertyGroup>
|
<OutputType>Exe</OutputType>
|
||||||
<OutputType>Exe</OutputType>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<Nullable>enable</Nullable>
|
||||||
<Nullable>enable</Nullable>
|
</PropertyGroup>
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\src\Speckle.Sdk\Speckle.Sdk.csproj" />
|
<ProjectReference Include="..\..\src\Speckle.Sdk\Speckle.Sdk.csproj" />
|
||||||
<ProjectReference Include="..\Speckle.Sdk.Tests.Performance\Speckle.Sdk.Tests.Performance.csproj" />
|
<ProjectReference Include="..\Speckle.Sdk.Tests.Performance\Speckle.Sdk.Tests.Performance.csproj" />
|
||||||
@@ -13,5 +12,4 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -2,12 +2,6 @@
|
|||||||
"version": 2,
|
"version": 2,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"net8.0": {
|
"net8.0": {
|
||||||
"GitVersion.MsBuild": {
|
|
||||||
"type": "Direct",
|
|
||||||
"requested": "[5.12.0, )",
|
|
||||||
"resolved": "5.12.0",
|
|
||||||
"contentHash": "dJuigXycpJNOiLT9or7mkHSkGFHgGW3/p6cNNYEKZBa7Hhp1FdX/cvqYWWYhRLpfoZOedeA7aRbYiOB3vW/dvA=="
|
|
||||||
},
|
|
||||||
"Microsoft.Extensions.DependencyInjection": {
|
"Microsoft.Extensions.DependencyInjection": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[2.2.0, )",
|
"requested": "[2.2.0, )",
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using Speckle.Sdk.Serialisation.V2;
|
using System.Collections.Concurrent;
|
||||||
|
using Speckle.Sdk.Serialisation;
|
||||||
|
using Speckle.Sdk.Serialisation.V2;
|
||||||
using Speckle.Sdk.Serialisation.V2.Send;
|
using Speckle.Sdk.Serialisation.V2.Send;
|
||||||
using Speckle.Sdk.Testing.Framework;
|
using Speckle.Sdk.Testing.Framework;
|
||||||
using Speckle.Sdk.Transports;
|
using Speckle.Sdk.Transports;
|
||||||
@@ -6,7 +8,7 @@ using Speckle.Sdk.Transports;
|
|||||||
namespace Speckle.Sdk.Serialization.Tests;
|
namespace Speckle.Sdk.Serialization.Tests;
|
||||||
|
|
||||||
public sealed class CancellationSqLiteJsonCacheManager(CancellationTokenSource cancellationTokenSource)
|
public sealed class CancellationSqLiteJsonCacheManager(CancellationTokenSource cancellationTokenSource)
|
||||||
: DummySqLiteJsonCacheManager
|
: MemoryJsonCacheManager(new ConcurrentDictionary<Id, Json>())
|
||||||
{
|
{
|
||||||
public override void SaveObjects(IEnumerable<(string id, string json)> items)
|
public override void SaveObjects(IEnumerable<(string id, string json)> items)
|
||||||
{
|
{
|
||||||
@@ -24,7 +26,8 @@ public class CancellationSqLiteSendManager(CancellationTokenSource cancellationT
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CancellationServerObjectManager(CancellationTokenSource cancellationTokenSource) : DummyServerObjectManager
|
public class CancellationServerObjectManager(CancellationTokenSource cancellationTokenSource)
|
||||||
|
: MemoryServerObjectManager(new ConcurrentDictionary<string, string>())
|
||||||
{
|
{
|
||||||
public override Task UploadObjects(
|
public override Task UploadObjects(
|
||||||
IReadOnlyList<BaseItem> objects,
|
IReadOnlyList<BaseItem> objects,
|
||||||
|
|||||||
+12
-2
@@ -1,5 +1,15 @@
|
|||||||
{
|
{
|
||||||
"Type": "System.OperationCanceledException",
|
"CancellationToken": {
|
||||||
|
"IsCancellationRequested": true,
|
||||||
|
"CanBeCanceled": true,
|
||||||
|
"WaitHandle": {
|
||||||
|
"SafeWaitHandle": {
|
||||||
|
"IsClosed": false,
|
||||||
|
"IsInvalid": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Data": {},
|
||||||
"Message": "The operation was canceled.",
|
"Message": "The operation was canceled.",
|
||||||
"Source": "System.Private.CoreLib"
|
"Type": "OperationCanceledException"
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-2
@@ -1,5 +1,15 @@
|
|||||||
{
|
{
|
||||||
"Type": "System.OperationCanceledException",
|
"CancellationToken": {
|
||||||
|
"IsCancellationRequested": true,
|
||||||
|
"CanBeCanceled": true,
|
||||||
|
"WaitHandle": {
|
||||||
|
"SafeWaitHandle": {
|
||||||
|
"IsClosed": false,
|
||||||
|
"IsInvalid": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Data": {},
|
||||||
"Message": "The operation was canceled.",
|
"Message": "The operation was canceled.",
|
||||||
"Source": "System.Private.CoreLib"
|
"Type": "OperationCanceledException"
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-2
@@ -1,5 +1,15 @@
|
|||||||
{
|
{
|
||||||
"Type": "System.OperationCanceledException",
|
"CancellationToken": {
|
||||||
|
"IsCancellationRequested": true,
|
||||||
|
"CanBeCanceled": true,
|
||||||
|
"WaitHandle": {
|
||||||
|
"SafeWaitHandle": {
|
||||||
|
"IsClosed": false,
|
||||||
|
"IsInvalid": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Data": {},
|
||||||
"Message": "The operation was canceled.",
|
"Message": "The operation was canceled.",
|
||||||
"Source": "System.Private.CoreLib"
|
"Type": "OperationCanceledException"
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-2
@@ -1,5 +1,15 @@
|
|||||||
{
|
{
|
||||||
"Type": "System.OperationCanceledException",
|
"CancellationToken": {
|
||||||
|
"IsCancellationRequested": true,
|
||||||
|
"CanBeCanceled": true,
|
||||||
|
"WaitHandle": {
|
||||||
|
"SafeWaitHandle": {
|
||||||
|
"IsClosed": false,
|
||||||
|
"IsInvalid": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Data": {},
|
||||||
"Message": "The operation was canceled.",
|
"Message": "The operation was canceled.",
|
||||||
"Source": "System.Private.CoreLib"
|
"Type": "OperationCanceledException"
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-2
@@ -1,5 +1,15 @@
|
|||||||
{
|
{
|
||||||
"Type": "System.OperationCanceledException",
|
"CancellationToken": {
|
||||||
|
"IsCancellationRequested": true,
|
||||||
|
"CanBeCanceled": true,
|
||||||
|
"WaitHandle": {
|
||||||
|
"SafeWaitHandle": {
|
||||||
|
"IsClosed": false,
|
||||||
|
"IsInvalid": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Data": {},
|
||||||
"Message": "The operation was canceled.",
|
"Message": "The operation was canceled.",
|
||||||
"Source": "System.Private.CoreLib"
|
"Type": "OperationCanceledException"
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-2
@@ -1,5 +1,15 @@
|
|||||||
{
|
{
|
||||||
"Type": "System.OperationCanceledException",
|
"CancellationToken": {
|
||||||
|
"IsCancellationRequested": true,
|
||||||
|
"CanBeCanceled": true,
|
||||||
|
"WaitHandle": {
|
||||||
|
"SafeWaitHandle": {
|
||||||
|
"IsClosed": false,
|
||||||
|
"IsInvalid": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Data": {},
|
||||||
"Message": "The operation was canceled.",
|
"Message": "The operation was canceled.",
|
||||||
"Source": "System.Private.CoreLib"
|
"Type": "OperationCanceledException"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging.Abstractions;
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
using Speckle.Objects.Geometry;
|
using Speckle.Objects.Geometry;
|
||||||
using Speckle.Sdk.Host;
|
|
||||||
using Speckle.Sdk.Models;
|
using Speckle.Sdk.Models;
|
||||||
using Speckle.Sdk.Serialisation;
|
using Speckle.Sdk.Serialisation;
|
||||||
|
using Speckle.Sdk.Serialisation.V2;
|
||||||
using Speckle.Sdk.Serialisation.V2.Receive;
|
using Speckle.Sdk.Serialisation.V2.Receive;
|
||||||
using Speckle.Sdk.Serialisation.V2.Send;
|
using Speckle.Sdk.Serialisation.V2.Send;
|
||||||
using Speckle.Sdk.Serialization.Tests.Framework;
|
using Speckle.Sdk.Serialization.Tests.Framework;
|
||||||
@@ -14,10 +15,15 @@ namespace Speckle.Sdk.Serialization.Tests;
|
|||||||
|
|
||||||
public class CancellationTests
|
public class CancellationTests
|
||||||
{
|
{
|
||||||
|
private readonly ISerializeProcessFactory _factory;
|
||||||
|
|
||||||
public CancellationTests()
|
public CancellationTests()
|
||||||
{
|
{
|
||||||
TypeLoader.Reset();
|
var serviceCollection = new ServiceCollection();
|
||||||
TypeLoader.Initialize(typeof(Base).Assembly, typeof(DetachedTests).Assembly, typeof(Polyline).Assembly);
|
serviceCollection.AddSpeckleSdk(new("Tests", "test"), "v3", typeof(TestClass).Assembly, typeof(Polyline).Assembly);
|
||||||
|
var serviceProvider = serviceCollection.BuildServiceProvider();
|
||||||
|
|
||||||
|
_factory = serviceProvider.GetRequiredService<ISerializeProcessFactory>();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -26,19 +32,17 @@ public class CancellationTests
|
|||||||
var testClass = new TestClass() { RegularProperty = "Hello" };
|
var testClass = new TestClass() { RegularProperty = "Hello" };
|
||||||
|
|
||||||
using var cancellationSource = new CancellationTokenSource();
|
using var cancellationSource = new CancellationTokenSource();
|
||||||
await using var serializeProcess = new SerializeProcess(
|
|
||||||
|
await using var serializeProcess = _factory.CreateSerializeProcess(
|
||||||
|
new ConcurrentDictionary<Id, Json>(),
|
||||||
|
new ConcurrentDictionary<string, string>(),
|
||||||
null,
|
null,
|
||||||
new DummySqLiteSendManager(),
|
|
||||||
new DummyServerObjectManager(),
|
|
||||||
new BaseChildFinder(new BasePropertyGatherer()),
|
|
||||||
new BaseSerializer(new DummySqLiteSendManager(), new ObjectSerializerFactory(new BasePropertyGatherer())),
|
|
||||||
new NullLoggerFactory(),
|
|
||||||
cancellationSource.Token,
|
cancellationSource.Token,
|
||||||
new SerializeProcessOptions(true, true, false, true)
|
new SerializeProcessOptions(true, true, false, true)
|
||||||
);
|
);
|
||||||
await cancellationSource.CancelAsync();
|
await cancellationSource.CancelAsync();
|
||||||
var ex = await Assert.ThrowsAsync<OperationCanceledException>(
|
var ex = await Assert.ThrowsAsync<OperationCanceledException>(async () =>
|
||||||
async () => await serializeProcess.Serialize(testClass)
|
await serializeProcess.Serialize(testClass)
|
||||||
);
|
);
|
||||||
await Verify(ex);
|
await Verify(ex);
|
||||||
cancellationSource.IsCancellationRequested.Should().BeTrue();
|
cancellationSource.IsCancellationRequested.Should().BeTrue();
|
||||||
@@ -50,18 +54,16 @@ public class CancellationTests
|
|||||||
var testClass = new TestClass() { RegularProperty = "Hello" };
|
var testClass = new TestClass() { RegularProperty = "Hello" };
|
||||||
|
|
||||||
using var cancellationSource = new CancellationTokenSource();
|
using var cancellationSource = new CancellationTokenSource();
|
||||||
await using var serializeProcess = new SerializeProcess(
|
|
||||||
null,
|
await using var serializeProcess = _factory.CreateSerializeProcess(
|
||||||
new DummySqLiteSendManager(),
|
new DummySqLiteSendManager(),
|
||||||
new CancellationServerObjectManager(cancellationSource),
|
new CancellationServerObjectManager(cancellationSource),
|
||||||
new BaseChildFinder(new BasePropertyGatherer()),
|
null,
|
||||||
new BaseSerializer(new DummySqLiteSendManager(), new ObjectSerializerFactory(new BasePropertyGatherer())),
|
|
||||||
new NullLoggerFactory(),
|
|
||||||
cancellationSource.Token,
|
cancellationSource.Token,
|
||||||
new SerializeProcessOptions(true, false, false, true)
|
new SerializeProcessOptions(true, true, false, true)
|
||||||
);
|
);
|
||||||
var ex = await Assert.ThrowsAsync<OperationCanceledException>(
|
var ex = await Assert.ThrowsAsync<OperationCanceledException>(async () =>
|
||||||
async () => await serializeProcess.Serialize(testClass)
|
await serializeProcess.Serialize(testClass)
|
||||||
);
|
);
|
||||||
await Verify(ex);
|
await Verify(ex);
|
||||||
cancellationSource.IsCancellationRequested.Should().BeTrue();
|
cancellationSource.IsCancellationRequested.Should().BeTrue();
|
||||||
@@ -73,18 +75,16 @@ public class CancellationTests
|
|||||||
var testClass = new TestClass() { RegularProperty = "Hello" };
|
var testClass = new TestClass() { RegularProperty = "Hello" };
|
||||||
|
|
||||||
using var cancellationSource = new CancellationTokenSource();
|
using var cancellationSource = new CancellationTokenSource();
|
||||||
await using var serializeProcess = new SerializeProcess(
|
await using var serializeProcess = _factory.CreateSerializeProcess(
|
||||||
|
new DummySqLiteSendManager(),
|
||||||
|
new CancellationServerObjectManager(cancellationSource),
|
||||||
null,
|
null,
|
||||||
new CancellationSqLiteSendManager(cancellationSource),
|
|
||||||
new DummyServerObjectManager(),
|
|
||||||
new BaseChildFinder(new BasePropertyGatherer()),
|
|
||||||
new BaseSerializer(new DummySqLiteSendManager(), new ObjectSerializerFactory(new BasePropertyGatherer())),
|
|
||||||
new NullLoggerFactory(),
|
|
||||||
cancellationSource.Token,
|
cancellationSource.Token,
|
||||||
new SerializeProcessOptions(true, false, false, true)
|
new SerializeProcessOptions(true, true, false, true)
|
||||||
);
|
);
|
||||||
var ex = await Assert.ThrowsAsync<OperationCanceledException>(
|
|
||||||
async () => await serializeProcess.Serialize(testClass)
|
var ex = await Assert.ThrowsAsync<OperationCanceledException>(async () =>
|
||||||
|
await serializeProcess.Serialize(testClass)
|
||||||
);
|
);
|
||||||
await Verify(ex);
|
await Verify(ex);
|
||||||
cancellationSource.IsCancellationRequested.Should().BeTrue();
|
cancellationSource.IsCancellationRequested.Should().BeTrue();
|
||||||
@@ -94,7 +94,7 @@ public class CancellationTests
|
|||||||
[InlineData("RevitObject.json.gz", "3416d3fe01c9196115514c4a2f41617b", 7818)]
|
[InlineData("RevitObject.json.gz", "3416d3fe01c9196115514c4a2f41617b", 7818)]
|
||||||
public async Task Cancellation_Receive_Cache(string fileName, string rootId, int oldCount)
|
public async Task Cancellation_Receive_Cache(string fileName, string rootId, int oldCount)
|
||||||
{
|
{
|
||||||
var closures = await TestFileManager.GetFileAsClosures(fileName);
|
var closures = TestFileManager.GetFileAsClosures(fileName);
|
||||||
closures.Count.Should().Be(oldCount);
|
closures.Count.Should().Be(oldCount);
|
||||||
|
|
||||||
using var cancellationSource = new CancellationTokenSource();
|
using var cancellationSource = new CancellationTokenSource();
|
||||||
@@ -121,7 +121,7 @@ public class CancellationTests
|
|||||||
[InlineData("RevitObject.json.gz", "3416d3fe01c9196115514c4a2f41617b", 7818)]
|
[InlineData("RevitObject.json.gz", "3416d3fe01c9196115514c4a2f41617b", 7818)]
|
||||||
public async Task Cancellation_Receive_Server(string fileName, string rootId, int oldCount)
|
public async Task Cancellation_Receive_Server(string fileName, string rootId, int oldCount)
|
||||||
{
|
{
|
||||||
var closures = await TestFileManager.GetFileAsClosures(fileName);
|
var closures = TestFileManager.GetFileAsClosures(fileName);
|
||||||
closures.Count.Should().Be(oldCount);
|
closures.Count.Should().Be(oldCount);
|
||||||
|
|
||||||
using var cancellationSource = new CancellationTokenSource();
|
using var cancellationSource = new CancellationTokenSource();
|
||||||
@@ -148,7 +148,7 @@ public class CancellationTests
|
|||||||
[InlineData("RevitObject.json.gz", "3416d3fe01c9196115514c4a2f41617b", 7818)]
|
[InlineData("RevitObject.json.gz", "3416d3fe01c9196115514c4a2f41617b", 7818)]
|
||||||
public async Task Cancellation_Receive_Deserialize(string fileName, string rootId, int oldCount)
|
public async Task Cancellation_Receive_Deserialize(string fileName, string rootId, int oldCount)
|
||||||
{
|
{
|
||||||
var closures = await TestFileManager.GetFileAsClosures(fileName);
|
var closures = TestFileManager.GetFileAsClosures(fileName);
|
||||||
closures.Count.Should().Be(oldCount);
|
closures.Count.Should().Be(oldCount);
|
||||||
|
|
||||||
using var cancellationSource = new CancellationTokenSource();
|
using var cancellationSource = new CancellationTokenSource();
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Microsoft.Extensions.Logging.Abstractions;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Speckle.Newtonsoft.Json.Linq;
|
using Speckle.Newtonsoft.Json.Linq;
|
||||||
using Speckle.Objects.Geometry;
|
using Speckle.Objects.Geometry;
|
||||||
using Speckle.Sdk.Host;
|
|
||||||
using Speckle.Sdk.Models;
|
using Speckle.Sdk.Models;
|
||||||
using Speckle.Sdk.Serialisation;
|
using Speckle.Sdk.Serialisation;
|
||||||
using Speckle.Sdk.Serialisation.V2;
|
using Speckle.Sdk.Serialisation.V2;
|
||||||
@@ -16,10 +15,15 @@ namespace Speckle.Sdk.Serialization.Tests;
|
|||||||
|
|
||||||
public class DetachedTests
|
public class DetachedTests
|
||||||
{
|
{
|
||||||
|
private readonly ISerializeProcessFactory _factory;
|
||||||
|
|
||||||
public DetachedTests()
|
public DetachedTests()
|
||||||
{
|
{
|
||||||
TypeLoader.Reset();
|
var serviceCollection = new ServiceCollection();
|
||||||
TypeLoader.Initialize(typeof(Base).Assembly, typeof(DetachedTests).Assembly, typeof(Polyline).Assembly);
|
serviceCollection.AddSpeckleSdk(new("Tests", "test"), "v3", typeof(TestClass).Assembly, typeof(Polyline).Assembly);
|
||||||
|
var serviceProvider = serviceCollection.BuildServiceProvider();
|
||||||
|
|
||||||
|
_factory = serviceProvider.GetRequiredService<ISerializeProcessFactory>();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -30,19 +34,16 @@ public class DetachedTests
|
|||||||
@base.detachedProp = new SamplePropBase() { name = "detachedProp" };
|
@base.detachedProp = new SamplePropBase() { name = "detachedProp" };
|
||||||
@base.attachedProp = new SamplePropBase() { name = "attachedProp" };
|
@base.attachedProp = new SamplePropBase() { name = "attachedProp" };
|
||||||
|
|
||||||
var objects = new Dictionary<string, string>();
|
var objects = new ConcurrentDictionary<string, string>();
|
||||||
|
|
||||||
await using var process2 = new SerializeProcess(
|
await using var serializeProcess = _factory.CreateSerializeProcess(
|
||||||
|
new ConcurrentDictionary<Id, Json>(),
|
||||||
|
objects,
|
||||||
null,
|
null,
|
||||||
new DummySendCacheManager(objects),
|
|
||||||
new DummyServerObjectManager(),
|
|
||||||
new BaseChildFinder(new BasePropertyGatherer()),
|
|
||||||
new BaseSerializer(new DummySendCacheManager(objects), new ObjectSerializerFactory(new BasePropertyGatherer())),
|
|
||||||
new NullLoggerFactory(),
|
|
||||||
default,
|
default,
|
||||||
new SerializeProcessOptions(false, false, true, true)
|
new SerializeProcessOptions(false, false, true, true)
|
||||||
);
|
);
|
||||||
await process2.Serialize(@base);
|
await serializeProcess.Serialize(@base);
|
||||||
|
|
||||||
await VerifyJsonDictionary(objects);
|
await VerifyJsonDictionary(objects);
|
||||||
}
|
}
|
||||||
@@ -115,19 +116,16 @@ public class DetachedTests
|
|||||||
line = new Polyline() { units = "test", value = [3.0, 4.0] },
|
line = new Polyline() { units = "test", value = [3.0, 4.0] },
|
||||||
};
|
};
|
||||||
|
|
||||||
var objects = new Dictionary<string, string>();
|
var objects = new ConcurrentDictionary<string, string>();
|
||||||
|
|
||||||
await using var process2 = new SerializeProcess(
|
await using var serializeProcess = _factory.CreateSerializeProcess(
|
||||||
|
new ConcurrentDictionary<Id, Json>(),
|
||||||
|
objects,
|
||||||
null,
|
null,
|
||||||
new DummySendCacheManager(objects),
|
|
||||||
new DummyServerObjectManager(),
|
|
||||||
new BaseChildFinder(new BasePropertyGatherer()),
|
|
||||||
new BaseSerializer(new DummySendCacheManager(objects), new ObjectSerializerFactory(new BasePropertyGatherer())),
|
|
||||||
new NullLoggerFactory(),
|
|
||||||
default,
|
default,
|
||||||
new SerializeProcessOptions(false, false, true, true)
|
new SerializeProcessOptions(false, false, true, true)
|
||||||
);
|
);
|
||||||
var results = await process2.Serialize(@base);
|
var results = await serializeProcess.Serialize(@base);
|
||||||
|
|
||||||
await VerifyJsonDictionary(objects);
|
await VerifyJsonDictionary(objects);
|
||||||
}
|
}
|
||||||
@@ -185,19 +183,17 @@ public class DetachedTests
|
|||||||
@base.list = new List<double>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
|
@base.list = new List<double>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
|
||||||
@base.list2 = new List<double>() { 1, 10 };
|
@base.list2 = new List<double>() { 1, 10 };
|
||||||
|
|
||||||
var objects = new Dictionary<string, string>();
|
var objects = new ConcurrentDictionary<string, string>();
|
||||||
|
|
||||||
await using var process2 = new SerializeProcess(
|
await using var serializeProcess = _factory.CreateSerializeProcess(
|
||||||
|
new ConcurrentDictionary<Id, Json>(),
|
||||||
|
objects,
|
||||||
null,
|
null,
|
||||||
new DummySendCacheManager(objects),
|
|
||||||
new DummyServerObjectManager(),
|
|
||||||
new BaseChildFinder(new BasePropertyGatherer()),
|
|
||||||
new BaseSerializer(new DummySendCacheManager(objects), new ObjectSerializerFactory(new BasePropertyGatherer())),
|
|
||||||
new NullLoggerFactory(),
|
|
||||||
default,
|
default,
|
||||||
new SerializeProcessOptions(false, false, true, true)
|
new SerializeProcessOptions(false, false, true, true)
|
||||||
);
|
);
|
||||||
var results = await process2.Serialize(@base);
|
|
||||||
|
var results = await serializeProcess.Serialize(@base);
|
||||||
|
|
||||||
objects.Count.Should().Be(3);
|
objects.Count.Should().Be(3);
|
||||||
var x = JObject.Parse(objects["efeadaca70a85ae6d3acfc93a8b380db"]);
|
var x = JObject.Parse(objects["efeadaca70a85ae6d3acfc93a8b380db"]);
|
||||||
@@ -220,19 +216,16 @@ public class DetachedTests
|
|||||||
@base.list2 = new List<double>() { 1, 10 };
|
@base.list2 = new List<double>() { 1, 10 };
|
||||||
@base.arr = [1, 10];
|
@base.arr = [1, 10];
|
||||||
|
|
||||||
var objects = new Dictionary<string, string>();
|
var objects = new ConcurrentDictionary<string, string>();
|
||||||
|
|
||||||
await using var process2 = new SerializeProcess(
|
await using var serializeProcess = _factory.CreateSerializeProcess(
|
||||||
|
new ConcurrentDictionary<Id, Json>(),
|
||||||
|
objects,
|
||||||
null,
|
null,
|
||||||
new DummySendCacheManager(objects),
|
|
||||||
new DummyServerObjectManager(),
|
|
||||||
new BaseChildFinder(new BasePropertyGatherer()),
|
|
||||||
new BaseSerializer(new DummySendCacheManager(objects), new ObjectSerializerFactory(new BasePropertyGatherer())),
|
|
||||||
new NullLoggerFactory(),
|
|
||||||
default,
|
default,
|
||||||
new SerializeProcessOptions(false, false, true, true)
|
new SerializeProcessOptions(false, false, true, true)
|
||||||
);
|
);
|
||||||
var results = await process2.Serialize(@base);
|
var results = await serializeProcess.Serialize(@base);
|
||||||
await VerifyJsonDictionary(objects);
|
await VerifyJsonDictionary(objects);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-2
@@ -1,5 +1,11 @@
|
|||||||
{
|
{
|
||||||
"Type": "Speckle.Sdk.SpeckleException",
|
"Data": {},
|
||||||
|
"InnerException": {
|
||||||
|
"$type": "NotImplementedException",
|
||||||
|
"Data": {},
|
||||||
|
"Message": "The method or operation is not implemented.",
|
||||||
|
"Type": "NotImplementedException"
|
||||||
|
},
|
||||||
"Message": "Error while sending",
|
"Message": "Error while sending",
|
||||||
"Source": "Speckle.Sdk"
|
"Type": "SpeckleException"
|
||||||
}
|
}
|
||||||
|
|||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"Data": {},
|
||||||
|
"InnerException": {
|
||||||
|
"$type": "Exception",
|
||||||
|
"Data": {},
|
||||||
|
"Message": "Count exceeded",
|
||||||
|
"Type": "Exception"
|
||||||
|
},
|
||||||
|
"Message": "Error while sending",
|
||||||
|
"Type": "SpeckleException"
|
||||||
|
}
|
||||||
+9
-3
@@ -1,5 +1,11 @@
|
|||||||
{
|
{
|
||||||
"Type": "Speckle.Sdk.SpeckleException",
|
"Data": {},
|
||||||
"Message": "Error while sending",
|
"InnerException": {
|
||||||
"Source": "Speckle.Sdk"
|
"$type": "NotImplementedException",
|
||||||
|
"Data": {},
|
||||||
|
"Message": "The method or operation is not implemented.",
|
||||||
|
"Type": "NotImplementedException"
|
||||||
|
},
|
||||||
|
"Message": "Error while loading",
|
||||||
|
"Type": "SpeckleException"
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user