Dim/flopper experiments (#438)
* Adds object flopper for experimental object uploads Implements a new mechanism for uploading objects to the server. * wip * pipe works * WIP * wip - refactors send pipeline
This commit is contained in:
committed by
GitHub
parent
12df19e431
commit
4980796cd6
@@ -8,6 +8,8 @@
|
||||
<PackageVersion Include="Glob" Version="1.1.9" />
|
||||
<PackageVersion Include="HttpMultipartParser" Version="9.0.0" />
|
||||
<PackageVersion Include="ILRepack.FullAuto" Version="1.6.0" />
|
||||
<PackageVersion Include="Microsoft.AspNet.WebApi.Client" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="10.0.1" />
|
||||
<PackageVersion Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
<!-- Keep at exactly 7.0.5 for side by side with V2 -->
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite" Version="[7.0.5,)" />
|
||||
@@ -16,7 +18,6 @@
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" 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.Bcl.AsyncInterfaces" Version="[5.0.0,)" />
|
||||
<PackageVersion Include="Moq" Version="4.20.72" />
|
||||
<PackageVersion Include="Newtonsoft.Json.Schema" Version="4.0.1" />
|
||||
<PackageVersion Include="Open.ChannelExtensions" Version="9.1.0" />
|
||||
@@ -28,7 +29,9 @@
|
||||
<PackageVersion Include="Speckle.DoubleNumerics" Version="4.1.0" />
|
||||
<PackageVersion Include="SimpleExec" Version="12.0.0" />
|
||||
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
|
||||
<PackageVersion Include="System.Threading.Channels" Version="9.0.4" />
|
||||
<PackageVersion Include="System.IO.Pipelines" Version="10.0.1" />
|
||||
<PackageVersion Include="System.Net.Http.Json" Version="10.0.1" />
|
||||
<PackageVersion Include="System.Threading.Channels" Version="10.0.1" />
|
||||
<PackageVersion Include="Verify.Quibble" Version="2.1.1" />
|
||||
<PackageVersion Include="Verify.Xunit" Version="29.4.0" />
|
||||
<PackageVersion Include="System.Text.Json" Version="8.0.5" />
|
||||
@@ -39,4 +42,4 @@
|
||||
<GlobalPackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
|
||||
<GlobalPackageReference Include="Speckle.InterfaceGenerator" Version="0.9.6" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -130,6 +130,19 @@
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
|
||||
},
|
||||
"Newtonsoft.Json": {
|
||||
"type": "Transitive",
|
||||
"resolved": "13.0.1",
|
||||
"contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
|
||||
},
|
||||
"Newtonsoft.Json.Bson": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.2",
|
||||
"contentHash": "QYFyxhaABwmq3p/21VrZNYvCg3DaEoN/wUuw5nmfAf0X3HLjgupwhkEWdgfb9nvGAUIv3osmZoD3kKl4jxEmYQ==",
|
||||
"dependencies": {
|
||||
"Newtonsoft.Json": "12.0.1"
|
||||
}
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -162,8 +175,8 @@
|
||||
},
|
||||
"System.Buffers": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "AwarXzzoDwX6BgrhjoJsk6tUezZEozOT5Y9QKF94Gl4JK91I4PIIBkBco9068Y9/Dra8Dkbie99kXB8+1BaYKw=="
|
||||
"resolved": "4.6.1",
|
||||
"contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw=="
|
||||
},
|
||||
"System.ComponentModel.Annotations": {
|
||||
"type": "Transitive",
|
||||
@@ -172,18 +185,18 @@
|
||||
},
|
||||
"System.Memory": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.3",
|
||||
"contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==",
|
||||
"resolved": "4.6.3",
|
||||
"contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==",
|
||||
"dependencies": {
|
||||
"System.Buffers": "4.4.0",
|
||||
"System.Numerics.Vectors": "4.4.0",
|
||||
"System.Runtime.CompilerServices.Unsafe": "4.5.2"
|
||||
"System.Buffers": "4.6.1",
|
||||
"System.Numerics.Vectors": "4.6.1",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.1.2"
|
||||
}
|
||||
},
|
||||
"System.Numerics.Vectors": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ=="
|
||||
"resolved": "4.6.1",
|
||||
"contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q=="
|
||||
},
|
||||
"System.Reactive": {
|
||||
"type": "Transitive",
|
||||
@@ -205,8 +218,8 @@
|
||||
},
|
||||
"System.Runtime.CompilerServices.Unsafe": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.3",
|
||||
"contentHash": "3TIsJhD1EiiT0w2CcDMN/iSSwnNnsrnbzeVHSKkaEgV85txMprmuO+Yq2AdSbeVGcg28pdNDTPK87tJhX7VFHw=="
|
||||
"resolved": "6.1.2",
|
||||
"contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw=="
|
||||
},
|
||||
"System.Runtime.InteropServices.WindowsRuntime": {
|
||||
"type": "Transitive",
|
||||
@@ -216,26 +229,40 @@
|
||||
"System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Text.Encodings.Web": {
|
||||
"type": "Transitive",
|
||||
"resolved": "10.0.1",
|
||||
"contentHash": "cVAka0o1rJJ5/De0pjNs7jcaZk5hUGf1HGzUyVmE2MEB1Vf0h/8qsWxImk1zjitCbeD2Avaq2P2+usdvqgbeVQ==",
|
||||
"dependencies": {
|
||||
"System.Buffers": "4.6.1",
|
||||
"System.Memory": "4.6.3",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.1.2"
|
||||
}
|
||||
},
|
||||
"System.Threading.Tasks.Extensions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.4",
|
||||
"contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
|
||||
"resolved": "4.6.3",
|
||||
"contentHash": "7sCiwilJLYbTZELaKnc7RecBBXWXA+xMLQWZKWawBxYjp6DBlSE3v9/UcvKBvr1vv2tTOhipiogM8rRmxlhrVA==",
|
||||
"dependencies": {
|
||||
"System.Runtime.CompilerServices.Unsafe": "4.5.3"
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.1.2"
|
||||
}
|
||||
},
|
||||
"speckle.sdk": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "[6.0.0, )",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "[5.0.0, )",
|
||||
"Microsoft.AspNet.WebApi.Client": "[6.0.0, )",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "[10.0.1, )",
|
||||
"Microsoft.CSharp": "[4.7.0, )",
|
||||
"Microsoft.Data.Sqlite": "[7.0.5, )",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "[2.2.0, )",
|
||||
"Microsoft.Extensions.Logging": "[2.2.0, )",
|
||||
"Speckle.DoubleNumerics": "[4.1.0, )",
|
||||
"Speckle.Newtonsoft.Json": "[13.0.2, )",
|
||||
"Speckle.Sdk.Dependencies": "[1.0.0, )"
|
||||
"Speckle.Sdk.Dependencies": "[1.0.0, )",
|
||||
"System.IO.Pipelines": "[10.0.1, )",
|
||||
"System.Net.Http.Json": "[10.0.1, )",
|
||||
"System.Threading.Channels": "[10.0.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.sdk.dependencies": {
|
||||
@@ -252,13 +279,25 @@
|
||||
"System.Reactive": "5.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.AspNet.WebApi.Client": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[6.0.0, )",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "zXeWP03dTo67AoDHUzR+/urck0KFssdCKOC+dq7Nv1V2YbFh/nIg09L0/3wSvyRONEdwxGB/ssEGmPNIIhAcAw==",
|
||||
"dependencies": {
|
||||
"Newtonsoft.Json": "13.0.1",
|
||||
"Newtonsoft.Json.Bson": "1.0.2",
|
||||
"System.Memory": "4.5.5",
|
||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||
}
|
||||
},
|
||||
"Microsoft.Bcl.AsyncInterfaces": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[5.0.0, )",
|
||||
"resolved": "5.0.0",
|
||||
"contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==",
|
||||
"requested": "[10.0.1, )",
|
||||
"resolved": "10.0.1",
|
||||
"contentHash": "E1HSLkPHXEO30JEij2pWbOuzz1Z5ND4a5l7IP1T2RgQuE0a0NzEIvtO64RNy3Otn6PFezbT80cfm3M/Cgt70PA==",
|
||||
"dependencies": {
|
||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||
"System.Threading.Tasks.Extensions": "4.6.3"
|
||||
}
|
||||
},
|
||||
"Microsoft.CSharp": {
|
||||
@@ -306,6 +345,54 @@
|
||||
"requested": "[13.0.2, )",
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"System.IO.Pipelines": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[10.0.1, )",
|
||||
"resolved": "10.0.1",
|
||||
"contentHash": "26LbFXHKd7PmRnWlkjnYgmjd5B6HYVG+1MpTO25BdxTJnx6D0O16JPAC/S4YBqjtt4YpfGj1QO/Ss6SPMGEGQw==",
|
||||
"dependencies": {
|
||||
"System.Buffers": "4.6.1",
|
||||
"System.Memory": "4.6.3",
|
||||
"System.Threading.Tasks.Extensions": "4.6.3"
|
||||
}
|
||||
},
|
||||
"System.Net.Http.Json": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[10.0.1, )",
|
||||
"resolved": "10.0.1",
|
||||
"contentHash": "XGOWt78ccgO9esyNlCemlS2b8JZPrH85pk/MvdHJxp6KwwUY/GnDaw2fPpJa7lgotiTkWnXnhip+ULNKaP7a8A==",
|
||||
"dependencies": {
|
||||
"System.Buffers": "4.6.1",
|
||||
"System.Memory": "4.6.3",
|
||||
"System.Text.Json": "10.0.1",
|
||||
"System.Threading.Tasks.Extensions": "4.6.3"
|
||||
}
|
||||
},
|
||||
"System.Text.Json": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[8.0.5, )",
|
||||
"resolved": "10.0.1",
|
||||
"contentHash": "EsgwDgU1PFqhrFA9l5n+RBu76wFhNGCEwu8ITrBNhjPP3MxLyklroU5GIF8o6JYpYg6T4KD/VICfMdgPAvNp5g==",
|
||||
"dependencies": {
|
||||
"Microsoft.Bcl.AsyncInterfaces": "10.0.1",
|
||||
"System.Buffers": "4.6.1",
|
||||
"System.IO.Pipelines": "10.0.1",
|
||||
"System.Memory": "4.6.3",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.1.2",
|
||||
"System.Text.Encodings.Web": "10.0.1",
|
||||
"System.Threading.Tasks.Extensions": "4.6.3"
|
||||
}
|
||||
},
|
||||
"System.Threading.Channels": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[10.0.1, )",
|
||||
"resolved": "10.0.1",
|
||||
"contentHash": "YRqU6Y2Cl6C+HrG5h1ftgKZ5VDTSA7j1wMKs5RtlauPeQ2EZ639Jt5aOFHdX3naP01hDDWFOWPApmNDVKwOpmg==",
|
||||
"dependencies": {
|
||||
"Microsoft.Bcl.AsyncInterfaces": "10.0.1",
|
||||
"System.Threading.Tasks.Extensions": "4.6.3"
|
||||
}
|
||||
}
|
||||
},
|
||||
"net8.0": {
|
||||
@@ -418,6 +505,19 @@
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
|
||||
},
|
||||
"Newtonsoft.Json": {
|
||||
"type": "Transitive",
|
||||
"resolved": "13.0.1",
|
||||
"contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
|
||||
},
|
||||
"Newtonsoft.Json.Bson": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.2",
|
||||
"contentHash": "QYFyxhaABwmq3p/21VrZNYvCg3DaEoN/wUuw5nmfAf0X3HLjgupwhkEWdgfb9nvGAUIv3osmZoD3kKl4jxEmYQ==",
|
||||
"dependencies": {
|
||||
"Newtonsoft.Json": "12.0.1"
|
||||
}
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -455,8 +555,8 @@
|
||||
},
|
||||
"System.Memory": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.3",
|
||||
"contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA=="
|
||||
"resolved": "4.5.5",
|
||||
"contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw=="
|
||||
},
|
||||
"System.Reactive": {
|
||||
"type": "Transitive",
|
||||
@@ -468,16 +568,30 @@
|
||||
"resolved": "4.5.1",
|
||||
"contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw=="
|
||||
},
|
||||
"System.Text.Encodings.Web": {
|
||||
"type": "Transitive",
|
||||
"resolved": "10.0.1",
|
||||
"contentHash": "cVAka0o1rJJ5/De0pjNs7jcaZk5hUGf1HGzUyVmE2MEB1Vf0h/8qsWxImk1zjitCbeD2Avaq2P2+usdvqgbeVQ=="
|
||||
},
|
||||
"System.Threading.Tasks.Extensions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.4",
|
||||
"contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg=="
|
||||
},
|
||||
"speckle.sdk": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "[6.0.0, )",
|
||||
"Microsoft.AspNet.WebApi.Client": "[6.0.0, )",
|
||||
"Microsoft.Data.Sqlite": "[7.0.5, )",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "[2.2.0, )",
|
||||
"Microsoft.Extensions.Logging": "[2.2.0, )",
|
||||
"Speckle.DoubleNumerics": "[4.1.0, )",
|
||||
"Speckle.Newtonsoft.Json": "[13.0.2, )",
|
||||
"Speckle.Sdk.Dependencies": "[1.0.0, )"
|
||||
"Speckle.Sdk.Dependencies": "[1.0.0, )",
|
||||
"System.IO.Pipelines": "[10.0.1, )",
|
||||
"System.Net.Http.Json": "[10.0.1, )",
|
||||
"System.Threading.Channels": "[10.0.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.sdk.dependencies": {
|
||||
@@ -494,6 +608,18 @@
|
||||
"System.Reactive": "5.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.AspNet.WebApi.Client": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[6.0.0, )",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "zXeWP03dTo67AoDHUzR+/urck0KFssdCKOC+dq7Nv1V2YbFh/nIg09L0/3wSvyRONEdwxGB/ssEGmPNIIhAcAw==",
|
||||
"dependencies": {
|
||||
"Newtonsoft.Json": "13.0.1",
|
||||
"Newtonsoft.Json.Bson": "1.0.2",
|
||||
"System.Memory": "4.5.5",
|
||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||
}
|
||||
},
|
||||
"Microsoft.Data.Sqlite": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[7.0.5, )",
|
||||
@@ -533,6 +659,37 @@
|
||||
"requested": "[13.0.2, )",
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"System.IO.Pipelines": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[10.0.1, )",
|
||||
"resolved": "10.0.1",
|
||||
"contentHash": "26LbFXHKd7PmRnWlkjnYgmjd5B6HYVG+1MpTO25BdxTJnx6D0O16JPAC/S4YBqjtt4YpfGj1QO/Ss6SPMGEGQw=="
|
||||
},
|
||||
"System.Net.Http.Json": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[10.0.1, )",
|
||||
"resolved": "10.0.1",
|
||||
"contentHash": "XGOWt78ccgO9esyNlCemlS2b8JZPrH85pk/MvdHJxp6KwwUY/GnDaw2fPpJa7lgotiTkWnXnhip+ULNKaP7a8A==",
|
||||
"dependencies": {
|
||||
"System.Text.Json": "10.0.1"
|
||||
}
|
||||
},
|
||||
"System.Text.Json": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[8.0.5, )",
|
||||
"resolved": "10.0.1",
|
||||
"contentHash": "EsgwDgU1PFqhrFA9l5n+RBu76wFhNGCEwu8ITrBNhjPP3MxLyklroU5GIF8o6JYpYg6T4KD/VICfMdgPAvNp5g==",
|
||||
"dependencies": {
|
||||
"System.IO.Pipelines": "10.0.1",
|
||||
"System.Text.Encodings.Web": "10.0.1"
|
||||
}
|
||||
},
|
||||
"System.Threading.Channels": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[10.0.1, )",
|
||||
"resolved": "10.0.1",
|
||||
"contentHash": "YRqU6Y2Cl6C+HrG5h1ftgKZ5VDTSA7j1wMKs5RtlauPeQ2EZ639Jt5aOFHdX3naP01hDDWFOWPApmNDVKwOpmg=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,12 +82,12 @@
|
||||
},
|
||||
"System.Threading.Channels": {
|
||||
"type": "Direct",
|
||||
"requested": "[9.0.4, )",
|
||||
"resolved": "9.0.4",
|
||||
"contentHash": "4qBn2H6/aXBpE/Pm3wY5yusY/pEvQz99NlWHrTUji0qCmOdbhhjaALcpmbfW2ksxlPM6i6S+QFLkpOQdyfeKYQ==",
|
||||
"requested": "[10.0.1, )",
|
||||
"resolved": "10.0.1",
|
||||
"contentHash": "YRqU6Y2Cl6C+HrG5h1ftgKZ5VDTSA7j1wMKs5RtlauPeQ2EZ639Jt5aOFHdX3naP01hDDWFOWPApmNDVKwOpmg==",
|
||||
"dependencies": {
|
||||
"Microsoft.Bcl.AsyncInterfaces": "9.0.4",
|
||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||
"Microsoft.Bcl.AsyncInterfaces": "10.0.1",
|
||||
"System.Threading.Tasks.Extensions": "4.6.3"
|
||||
}
|
||||
},
|
||||
"ILRepack": {
|
||||
@@ -141,24 +141,24 @@
|
||||
},
|
||||
"System.Runtime.CompilerServices.Unsafe": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
|
||||
"resolved": "6.1.2",
|
||||
"contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw=="
|
||||
},
|
||||
"System.Threading.Tasks.Extensions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.4",
|
||||
"contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
|
||||
"resolved": "4.6.3",
|
||||
"contentHash": "7sCiwilJLYbTZELaKnc7RecBBXWXA+xMLQWZKWawBxYjp6DBlSE3v9/UcvKBvr1vv2tTOhipiogM8rRmxlhrVA==",
|
||||
"dependencies": {
|
||||
"System.Runtime.CompilerServices.Unsafe": "4.5.3"
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.1.2"
|
||||
}
|
||||
},
|
||||
"Microsoft.Bcl.AsyncInterfaces": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[5.0.0, )",
|
||||
"resolved": "9.0.4",
|
||||
"contentHash": "9VGI5kxIvrNG2mqLQZnUR6y/3fcnygD8eNpHR+CqfbnIXvea6nehnYknDKQTxZVPMpzpNca+7DxLBmpdB3q0Bw==",
|
||||
"requested": "[10.0.1, )",
|
||||
"resolved": "10.0.1",
|
||||
"contentHash": "E1HSLkPHXEO30JEij2pWbOuzz1Z5ND4a5l7IP1T2RgQuE0a0NzEIvtO64RNy3Otn6PFezbT80cfm3M/Cgt70PA==",
|
||||
"dependencies": {
|
||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||
"System.Threading.Tasks.Extensions": "4.6.3"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -229,9 +229,9 @@
|
||||
},
|
||||
"System.Threading.Channels": {
|
||||
"type": "Direct",
|
||||
"requested": "[9.0.4, )",
|
||||
"resolved": "9.0.4",
|
||||
"contentHash": "4qBn2H6/aXBpE/Pm3wY5yusY/pEvQz99NlWHrTUji0qCmOdbhhjaALcpmbfW2ksxlPM6i6S+QFLkpOQdyfeKYQ=="
|
||||
"requested": "[10.0.1, )",
|
||||
"resolved": "10.0.1",
|
||||
"contentHash": "YRqU6Y2Cl6C+HrG5h1ftgKZ5VDTSA7j1wMKs5RtlauPeQ2EZ639Jt5aOFHdX3naP01hDDWFOWPApmNDVKwOpmg=="
|
||||
},
|
||||
"ILRepack": {
|
||||
"type": "Transitive",
|
||||
|
||||
@@ -17,7 +17,7 @@ public enum DynamicBaseMemberType
|
||||
Dynamic = 2,
|
||||
|
||||
/// <summary>
|
||||
/// The typed members flagged with <see cref="ObsoleteAttribute"/> attribute.
|
||||
/// The typed members flagged with ObsoleteAttribute attribute.
|
||||
/// </summary>
|
||||
Obsolete = 4,
|
||||
|
||||
@@ -27,12 +27,12 @@ public enum DynamicBaseMemberType
|
||||
SchemaComputed = 16,
|
||||
|
||||
/// <summary>
|
||||
/// All the typed members, including ones with <see cref="ObsoleteAttribute"/> attributes.
|
||||
/// All the typed members, including ones with ObsoleteAttribute attributes.
|
||||
/// </summary>
|
||||
InstanceAll = Instance + Obsolete,
|
||||
|
||||
/// <summary>
|
||||
/// All the members, including dynamic and instance members flagged with <see cref="ObsoleteAttribute"/> attributes
|
||||
/// All the members, including dynamic and instance members flagged with ObsoleteAttribute attributes
|
||||
/// </summary>
|
||||
All = InstanceAll + Dynamic,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
namespace Speckle.Sdk.Pipelines;
|
||||
|
||||
/// <summary>
|
||||
/// Wraps a stream to report upload progress as bytes are read.
|
||||
/// </summary>
|
||||
public sealed class ProgressStream : Stream
|
||||
{
|
||||
private readonly Stream _innerStream;
|
||||
private readonly long _totalBytes;
|
||||
private readonly IProgress<(long BytesSent, long TotalBytes)>? _progress;
|
||||
private long _bytesSent;
|
||||
|
||||
public ProgressStream(
|
||||
Stream innerStream,
|
||||
long totalBytes,
|
||||
IProgress<(long BytesSent, long TotalBytes)>? progress = null
|
||||
)
|
||||
{
|
||||
_innerStream = innerStream;
|
||||
_totalBytes = totalBytes;
|
||||
_progress = progress;
|
||||
_bytesSent = 0;
|
||||
}
|
||||
|
||||
public override bool CanRead => _innerStream.CanRead;
|
||||
public override bool CanSeek => _innerStream.CanSeek;
|
||||
public override bool CanWrite => false;
|
||||
public override long Length => _innerStream.Length;
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get => _innerStream.Position;
|
||||
set => _innerStream.Position = value;
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
int bytesRead = _innerStream.Read(buffer, offset, count);
|
||||
ReportProgress(bytesRead);
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
int bytesRead = await _innerStream
|
||||
#if NET8_0_OR_GREATER
|
||||
.ReadAsync(buffer.AsMemory(offset, count), cancellationToken)
|
||||
#else
|
||||
.ReadAsync(buffer, offset, count, cancellationToken)
|
||||
#endif
|
||||
.ConfigureAwait(false);
|
||||
ReportProgress(bytesRead);
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
#if NET8_0_OR_GREATER
|
||||
public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
|
||||
{
|
||||
int bytesRead = await _innerStream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
|
||||
ReportProgress(bytesRead);
|
||||
return bytesRead;
|
||||
}
|
||||
#endif
|
||||
|
||||
private void ReportProgress(int bytesRead)
|
||||
{
|
||||
_bytesSent += bytesRead;
|
||||
_progress?.Report((_bytesSent, _totalBytes));
|
||||
}
|
||||
|
||||
public override void Flush() => _innerStream.Flush();
|
||||
|
||||
public override Task FlushAsync(CancellationToken cancellationToken) => _innerStream.FlushAsync(cancellationToken);
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin) => _innerStream.Seek(offset, origin);
|
||||
|
||||
public override void SetLength(long value) => throw new NotSupportedException();
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_innerStream.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#if NET8_0_OR_GREATER
|
||||
public override async ValueTask DisposeAsync()
|
||||
{
|
||||
await _innerStream.DisposeAsync().ConfigureAwait(false);
|
||||
await base.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using Speckle.Sdk.Credentials;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Serialisation;
|
||||
|
||||
namespace Speckle.Sdk.Pipelines;
|
||||
|
||||
public record UploadItem(string Id, Json Json, string SpeckleType, ObjectReference Reference);
|
||||
|
||||
public sealed class SendPipeline : IDisposable
|
||||
{
|
||||
private readonly CancellationToken _cancellationToken;
|
||||
private readonly Serializer _serializer = new();
|
||||
private readonly Uploader _uploader;
|
||||
|
||||
public SendPipeline(
|
||||
Account account,
|
||||
string projectId,
|
||||
string modelId,
|
||||
string ingestionId,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
_cancellationToken = cancellationToken;
|
||||
_uploader = new Uploader(projectId, modelId, ingestionId, account.serverInfo.url, account.token, cancellationToken);
|
||||
}
|
||||
|
||||
private UploadItem _lastItem;
|
||||
|
||||
public async Task<ObjectReference> Process(Base @base)
|
||||
{
|
||||
var results = _serializer.Serialize(@base).ToArray();
|
||||
var first = results.First();
|
||||
foreach (var item in results)
|
||||
{
|
||||
// we're not doing fire and forget here so that we get the backpressure from the uploader
|
||||
await _uploader.PushAsync(item).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// NOTE: this is important to keep track of. When we serialze an object, we get back a list of objects, with the first one being the original root.
|
||||
// In the case of the commit root object, this means the last object is not necessarily the root; we therefore need to manually track its existance here
|
||||
// and ensure it's the last one through in the uploader's stream. See WaitForUpload down below.
|
||||
_lastItem = first;
|
||||
return first.Reference;
|
||||
}
|
||||
|
||||
public async Task WaitForUpload()
|
||||
{
|
||||
await _uploader.PushAsync(_lastItem).ConfigureAwait(false);
|
||||
await _uploader.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<string> WaitForUploadAndServerProcessing()
|
||||
{
|
||||
// TODO: in some way, wait for the server to process the upload and return the actual new version id
|
||||
return await Task.FromResult("todo").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void Dispose() => _uploader.Dispose();
|
||||
}
|
||||
@@ -0,0 +1,372 @@
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using Speckle.DoubleNumerics;
|
||||
using Speckle.Newtonsoft.Json;
|
||||
using Speckle.Sdk.Dependencies;
|
||||
using Speckle.Sdk.Helpers;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Serialisation;
|
||||
|
||||
namespace Speckle.Sdk.Pipelines;
|
||||
|
||||
/// <summary>
|
||||
/// Another serializer, cleaner and meaner. Provides methods for serializing Speckle objects into a format suitable for upload or storage.
|
||||
/// This class handles the conversion of <see cref="Speckle.Sdk.Models.Base"/> and its derivatives
|
||||
/// into serialized JSON structures along with associated metadata, closures, and references.
|
||||
/// <para>Any reference objects coming through are being "passed through" serialized - they do not get double encoded.</para>
|
||||
/// </summary>
|
||||
public class Serializer
|
||||
{
|
||||
private readonly record struct PropertyInfo(string Name, object? Value, bool IsDetachable);
|
||||
|
||||
public IEnumerable<UploadItem> Serialize(Base root)
|
||||
{
|
||||
// Special case: if root is already an ObjectReference, serialize it verbatim
|
||||
if (root is ObjectReference existingRef)
|
||||
{
|
||||
var uploadItem = ReferenceToUploadItem(existingRef);
|
||||
yield return uploadItem;
|
||||
yield break;
|
||||
}
|
||||
|
||||
var detachedObjects = new List<(Id, Json, Dictionary<string, int>, Base, string)>();
|
||||
var rootClosures = new Dictionary<string, int>();
|
||||
|
||||
var (rootId, rootJson) = SerializeBase(root, false, rootClosures, detachedObjects);
|
||||
|
||||
var rootReference = new ObjectReference
|
||||
{
|
||||
referencedId = rootId.Value,
|
||||
applicationId = root.applicationId,
|
||||
closure = rootClosures.Count > 0 ? rootClosures : null,
|
||||
};
|
||||
|
||||
yield return new UploadItem(rootId.Value, rootJson, root.speckle_type, rootReference);
|
||||
|
||||
foreach (var (id, json, closures, baseObj, speckleType) in detachedObjects)
|
||||
{
|
||||
var reference = new ObjectReference
|
||||
{
|
||||
referencedId = id.Value,
|
||||
applicationId = baseObj.applicationId,
|
||||
closure = closures.Count > 0 ? closures : null,
|
||||
};
|
||||
|
||||
yield return new UploadItem(id.Value, json, speckleType, reference);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<PropertyInfo> ExtractProperties(Base baseObj)
|
||||
{
|
||||
var typedProperties = baseObj.GetInstanceMembers();
|
||||
foreach (var prop in typedProperties)
|
||||
{
|
||||
if (prop.Name == "id" || prop.Name.StartsWith("__"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (prop.IsDefined(typeof(JsonIgnoreAttribute), false))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var value = prop.GetValue(baseObj);
|
||||
var isDetachable = prop.GetCustomAttribute<DetachPropertyAttribute>(true)?.Detachable ?? false;
|
||||
|
||||
yield return new PropertyInfo(prop.Name, value, isDetachable);
|
||||
}
|
||||
|
||||
foreach (var propName in baseObj.DynamicPropertyKeys)
|
||||
{
|
||||
if (propName.StartsWith("__"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var value = baseObj[propName];
|
||||
|
||||
#pragma warning disable CA1866
|
||||
var isDetachable = propName.StartsWith("@");
|
||||
#pragma warning restore CA1866
|
||||
|
||||
yield return new PropertyInfo(propName, value, isDetachable);
|
||||
}
|
||||
}
|
||||
|
||||
private (Id, Json) SerializeBase(
|
||||
Base baseObj,
|
||||
bool forceDetach,
|
||||
Dictionary<string, int> closures,
|
||||
List<(Id, Json, Dictionary<string, int>, Base, string)> detachedObjects
|
||||
)
|
||||
{
|
||||
var childClosures = new Dictionary<string, int>();
|
||||
|
||||
var sb = Pools.StringBuilders.Get();
|
||||
try
|
||||
{
|
||||
using var stringWriter = new StringWriter(sb);
|
||||
using var jsonWriter = new JsonTextWriter(stringWriter);
|
||||
using var idWriter = new SerializerIdWriter(jsonWriter);
|
||||
|
||||
idWriter.WriteStartObject();
|
||||
|
||||
foreach (var prop in ExtractProperties(baseObj))
|
||||
{
|
||||
idWriter.WritePropertyName(prop.Name);
|
||||
SerializeValue(prop.Value, idWriter, prop.IsDetachable, childClosures, detachedObjects);
|
||||
}
|
||||
|
||||
var (jsonForId, finalWriter) = idWriter.FinishIdWriter();
|
||||
var id = IdGenerator.ComputeId(jsonForId);
|
||||
|
||||
finalWriter.WritePropertyName("id");
|
||||
finalWriter.WriteValue(id.Value);
|
||||
|
||||
baseObj.id = id.Value;
|
||||
|
||||
if ((forceDetach || childClosures.Count > 0) && childClosures.Count > 0)
|
||||
{
|
||||
finalWriter.WritePropertyName("__closure");
|
||||
finalWriter.WriteStartObject();
|
||||
foreach (var kvp in childClosures)
|
||||
{
|
||||
finalWriter.WritePropertyName(kvp.Key);
|
||||
finalWriter.WriteValue(kvp.Value);
|
||||
}
|
||||
finalWriter.WriteEndObject();
|
||||
|
||||
foreach (var kvp in childClosures)
|
||||
{
|
||||
closures[kvp.Key] = closures.TryGetValue(kvp.Key, out var existing) ? existing + kvp.Value : kvp.Value;
|
||||
}
|
||||
}
|
||||
|
||||
finalWriter.WriteEndObject();
|
||||
finalWriter.Flush();
|
||||
|
||||
var json = new Json(stringWriter.ToString());
|
||||
return (id, json);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Pools.StringBuilders.Return(sb);
|
||||
}
|
||||
}
|
||||
|
||||
private void SerializeValue(
|
||||
object? value,
|
||||
JsonWriter writer,
|
||||
bool isDetachable,
|
||||
Dictionary<string, int> closures,
|
||||
List<(Id, Json, Dictionary<string, int>, Base, string)> detachedObjects
|
||||
)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
writer.WriteNull();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (value)
|
||||
{
|
||||
case string s:
|
||||
writer.WriteValue(s);
|
||||
return;
|
||||
case int i:
|
||||
writer.WriteValue(i);
|
||||
return;
|
||||
case long l:
|
||||
writer.WriteValue(l);
|
||||
return;
|
||||
case double d:
|
||||
writer.WriteValue(d);
|
||||
return;
|
||||
case float f:
|
||||
writer.WriteValue(f);
|
||||
return;
|
||||
case bool b:
|
||||
writer.WriteValue(b);
|
||||
return;
|
||||
case Enum e:
|
||||
writer.WriteValue((int)(object)e);
|
||||
return;
|
||||
case Guid g:
|
||||
writer.WriteValue(g.ToString());
|
||||
return;
|
||||
case DateTime dt:
|
||||
writer.WriteValue(dt.ToString("o"));
|
||||
return;
|
||||
|
||||
case Matrix4x4 md:
|
||||
writer.WriteStartArray();
|
||||
writer.WriteValue(md.M11);
|
||||
writer.WriteValue(md.M12);
|
||||
writer.WriteValue(md.M13);
|
||||
writer.WriteValue(md.M14);
|
||||
writer.WriteValue(md.M21);
|
||||
writer.WriteValue(md.M22);
|
||||
writer.WriteValue(md.M23);
|
||||
writer.WriteValue(md.M24);
|
||||
writer.WriteValue(md.M31);
|
||||
writer.WriteValue(md.M32);
|
||||
writer.WriteValue(md.M33);
|
||||
writer.WriteValue(md.M34);
|
||||
writer.WriteValue(md.M41);
|
||||
writer.WriteValue(md.M42);
|
||||
writer.WriteValue(md.M43);
|
||||
writer.WriteValue(md.M44);
|
||||
writer.WriteEndArray();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle ObjectReference before Base (since ObjectReference extends Base)
|
||||
// This prevents double-serialization and properly propagates closures
|
||||
if (value is ObjectReference objRef)
|
||||
{
|
||||
writer.WriteStartObject();
|
||||
writer.WritePropertyName("speckle_type");
|
||||
writer.WriteValue("reference");
|
||||
writer.WritePropertyName("referencedId");
|
||||
writer.WriteValue(objRef.referencedId);
|
||||
writer.WriteEndObject();
|
||||
|
||||
// Propagate closure: add the referenced ID
|
||||
closures[objRef.referencedId] = closures.TryGetValue(objRef.referencedId, out var existing) ? existing + 1 : 1;
|
||||
|
||||
// Propagate nested closures from the ObjectReference.closure dictionary
|
||||
if (objRef.closure != null)
|
||||
{
|
||||
foreach (var kvp in objRef.closure)
|
||||
{
|
||||
closures[kvp.Key] = closures.TryGetValue(kvp.Key, out var existingDepth)
|
||||
? existingDepth + kvp.Value
|
||||
: kvp.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (value is Base baseObj)
|
||||
{
|
||||
if (isDetachable)
|
||||
{
|
||||
var childClosures = new Dictionary<string, int>();
|
||||
var (childId, childJson) = SerializeBase(baseObj, true, childClosures, detachedObjects);
|
||||
|
||||
detachedObjects.Add((childId, childJson, childClosures, baseObj, baseObj.speckle_type));
|
||||
|
||||
writer.WriteStartObject();
|
||||
writer.WritePropertyName("speckle_type");
|
||||
writer.WriteValue("reference");
|
||||
writer.WritePropertyName("referencedId");
|
||||
writer.WriteValue(childId.Value);
|
||||
writer.WriteEndObject();
|
||||
|
||||
closures[childId.Value] = closures.TryGetValue(childId.Value, out var existing) ? existing + 1 : 1;
|
||||
|
||||
foreach (var kvp in childClosures)
|
||||
{
|
||||
closures[kvp.Key] = closures.TryGetValue(kvp.Key, out var existingDepth)
|
||||
? existingDepth + kvp.Value
|
||||
: kvp.Value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var inlineClosures = new Dictionary<string, int>();
|
||||
var (_, inlineJson) = SerializeBase(baseObj, false, inlineClosures, detachedObjects);
|
||||
|
||||
writer.WriteRawValue(inlineJson.Value);
|
||||
|
||||
foreach (var kvp in inlineClosures)
|
||||
{
|
||||
closures[kvp.Key] = closures.TryGetValue(kvp.Key, out var existingDepth)
|
||||
? existingDepth + kvp.Value
|
||||
: kvp.Value;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (value is IDictionary dict)
|
||||
{
|
||||
writer.WriteStartObject();
|
||||
foreach (DictionaryEntry kvp in dict)
|
||||
{
|
||||
if (kvp.Key is not string key)
|
||||
{
|
||||
throw new ArgumentException("Dictionary keys must be strings");
|
||||
}
|
||||
|
||||
writer.WritePropertyName(key);
|
||||
SerializeValue(kvp.Value, writer, false, closures, detachedObjects);
|
||||
}
|
||||
writer.WriteEndObject();
|
||||
return;
|
||||
}
|
||||
|
||||
if (value is IEnumerable enumerable and not string)
|
||||
{
|
||||
writer.WriteStartArray();
|
||||
foreach (var item in enumerable)
|
||||
{
|
||||
SerializeValue(item, writer, isDetachable, closures, detachedObjects);
|
||||
}
|
||||
writer.WriteEndArray();
|
||||
return;
|
||||
}
|
||||
|
||||
writer.WriteValue(value.ToString());
|
||||
}
|
||||
|
||||
private UploadItem ReferenceToUploadItem(ObjectReference existingRef)
|
||||
{
|
||||
var sb = Pools.StringBuilders.Get();
|
||||
try
|
||||
{
|
||||
using var stringWriter = new StringWriter(sb);
|
||||
using var jsonWriter = new JsonTextWriter(stringWriter);
|
||||
|
||||
jsonWriter.WriteStartObject();
|
||||
jsonWriter.WritePropertyName("speckle_type");
|
||||
jsonWriter.WriteValue("reference");
|
||||
jsonWriter.WritePropertyName("referencedId");
|
||||
jsonWriter.WriteValue(existingRef.referencedId);
|
||||
jsonWriter.WritePropertyName("__closure");
|
||||
|
||||
if (existingRef.closure != null && existingRef.closure.Count > 0)
|
||||
{
|
||||
jsonWriter.WriteStartObject();
|
||||
foreach (var kvp in existingRef.closure)
|
||||
{
|
||||
jsonWriter.WritePropertyName(kvp.Key);
|
||||
jsonWriter.WriteValue(kvp.Value);
|
||||
}
|
||||
jsonWriter.WriteEndObject();
|
||||
}
|
||||
else
|
||||
{
|
||||
jsonWriter.WriteNull();
|
||||
}
|
||||
|
||||
jsonWriter.WriteEndObject();
|
||||
jsonWriter.Flush();
|
||||
|
||||
var refJson = new Json(stringWriter.ToString());
|
||||
|
||||
return new UploadItem(
|
||||
existingRef.referencedId,
|
||||
refJson,
|
||||
existingRef.speckle_type,
|
||||
existingRef // Pass through the original ObjectReference
|
||||
);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Pools.StringBuilders.Return(sb);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
using System.IO.Compression;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Http.Json;
|
||||
using System.Threading.Channels;
|
||||
|
||||
namespace Speckle.Sdk.Pipelines;
|
||||
|
||||
public sealed class Uploader : IDisposable
|
||||
{
|
||||
private readonly string _projectId;
|
||||
private readonly string _modelId;
|
||||
private readonly string _ingestionId;
|
||||
private readonly CancellationToken _cancellationToken;
|
||||
private readonly HttpClient _client;
|
||||
private readonly Channel<UploadItem> _channel;
|
||||
private readonly Task<UploadResult> _sendTask;
|
||||
|
||||
public Uploader(
|
||||
string projectId,
|
||||
string modelId,
|
||||
string ingestionId,
|
||||
string apiEndpoint,
|
||||
string authToken,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
_projectId = projectId;
|
||||
_modelId = modelId;
|
||||
_ingestionId = ingestionId;
|
||||
_cancellationToken = cancellationToken;
|
||||
|
||||
Uri apiBaseUrl = new(new(apiEndpoint), "/api/v1/");
|
||||
_client = new HttpClient { BaseAddress = apiBaseUrl, Timeout = TimeSpan.FromMinutes(30) };
|
||||
|
||||
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authToken);
|
||||
|
||||
_channel = Channel.CreateBounded<UploadItem>(
|
||||
new BoundedChannelOptions(1000) { FullMode = BoundedChannelFullMode.Wait }
|
||||
);
|
||||
|
||||
_sendTask = SendLoopAsync();
|
||||
}
|
||||
|
||||
public ValueTask PushAsync(UploadItem item, CancellationToken ct = default) => _channel.Writer.WriteAsync(item, ct);
|
||||
|
||||
public async Task<string> CompleteAsync()
|
||||
{
|
||||
_channel.Writer.Complete();
|
||||
var result = await _sendTask.ConfigureAwait(false);
|
||||
return result.IngestionId;
|
||||
}
|
||||
|
||||
private async Task<UploadResult> SendLoopAsync()
|
||||
{
|
||||
// 1. Stream channel to temp file
|
||||
string tempFilePath = Path.GetTempFileName();
|
||||
System.Diagnostics.Debug.WriteLine($"Temp file is at {tempFilePath}");
|
||||
try
|
||||
{
|
||||
long fileSizeBytes;
|
||||
{
|
||||
using var fileStream = new FileStream(tempFilePath, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||
using var gzip = new GZipStream(fileStream, CompressionLevel.Optimal);
|
||||
using var writer = new StreamWriter(gzip);
|
||||
await foreach (var item in _channel.Reader.ReadAllAsync(_cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
await writer.WriteLineAsync($"{item.Id}\t{item.Json}\t{item.SpeckleType}").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await writer.FlushAsync().ConfigureAwait(false);
|
||||
await gzip.FlushAsync(_cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
// fileStream.Flush();
|
||||
// fileStream.Close();
|
||||
fileSizeBytes = new FileInfo(tempFilePath).Length;
|
||||
|
||||
// 2. Request presigned URL
|
||||
var signUri = new Uri($"projects/{_projectId}/models/{_modelId}/uploads/sign", UriKind.Relative);
|
||||
|
||||
var signResponse = await HttpClientExtensions
|
||||
.PostAsJsonAsync(_client, signUri, _cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
signResponse.EnsureSuccessStatusCode();
|
||||
|
||||
var presignedUpload =
|
||||
await signResponse.Content.ReadFromJsonAsync<PresignedUploadResponse>(_cancellationToken).ConfigureAwait(false)
|
||||
?? throw new InvalidOperationException("Failed to get presigned upload URL");
|
||||
|
||||
// 3. Upload to S3
|
||||
using var fileStreamUpload = new FileStream(tempFilePath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
|
||||
Stream progressStream = fileStreamUpload; // TODO: wrap with progress stream
|
||||
|
||||
using var streamContent = new StreamContent(progressStream);
|
||||
streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
|
||||
streamContent.Headers.ContentLength = fileSizeBytes;
|
||||
|
||||
using var uploadRequest = new HttpRequestMessage(HttpMethod.Put, new Uri(presignedUpload.Url, UriKind.Absolute));
|
||||
uploadRequest.Content = streamContent;
|
||||
|
||||
using var s3Client = new HttpClient(); // NOTE: using a separate client for S3 as we DO NOT NEED THE AUTH HEADER, presigned url's don't work with it.
|
||||
using var uploadResponse = await s3Client
|
||||
.SendAsync(uploadRequest, HttpCompletionOption.ResponseHeadersRead, _cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
uploadResponse.EnsureSuccessStatusCode();
|
||||
|
||||
// 4. Trigger processing
|
||||
var processUri = new Uri($"projects/{_projectId}/models/{_modelId}/uploads/process", UriKind.Relative);
|
||||
var processRequest = new ProcessUploadRequest { key = presignedUpload.Key, ingestionId = _ingestionId };
|
||||
|
||||
var processResponse = await HttpClientExtensions
|
||||
.PostAsJsonAsync(_client, processUri, processRequest, _cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
processResponse.EnsureSuccessStatusCode();
|
||||
|
||||
var processResult = await processResponse
|
||||
.Content.ReadFromJsonAsync<ProcessUploadResponse>(_cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (processResult == null)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to trigger upload processing");
|
||||
}
|
||||
|
||||
return new UploadResult { IngestionId = processResult.ingestionId };
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 5. Clean up temp file
|
||||
if (File.Exists(tempFilePath))
|
||||
{
|
||||
try
|
||||
{
|
||||
//File.Delete(tempFilePath);
|
||||
}
|
||||
#pragma warning disable CA1031
|
||||
catch
|
||||
#pragma warning restore CA1031
|
||||
{
|
||||
// Best effort
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() => _client.Dispose();
|
||||
}
|
||||
|
||||
// DTOs
|
||||
internal record PresignedUploadResponse
|
||||
{
|
||||
public required string Url { get; init; }
|
||||
public required string Key { get; init; }
|
||||
}
|
||||
|
||||
internal record ProcessUploadRequest
|
||||
{
|
||||
public required string key { get; init; }
|
||||
public required string ingestionId { get; init; }
|
||||
}
|
||||
|
||||
internal record ProcessUploadResponse
|
||||
{
|
||||
public required string ingestionId { get; init; }
|
||||
}
|
||||
|
||||
internal record UploadResult
|
||||
{
|
||||
public required string IngestionId { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
// using System.IO.Compression;
|
||||
// using System.Net.Http.Headers;
|
||||
// using System.Threading.Channels;
|
||||
//
|
||||
// namespace Speckle.Sdk.Pipeline;
|
||||
//
|
||||
// public sealed class UploaderOld : IDisposable
|
||||
// {
|
||||
// private readonly string _projectId;
|
||||
// private readonly string _modelId;
|
||||
// private readonly HttpClient _client;
|
||||
// private readonly Channel<UploadItem> _channel;
|
||||
// private readonly Task _sendTask;
|
||||
//
|
||||
// public UploaderOld(string projectId, string modelId, string? authToken, string? apiEndpoint)
|
||||
// {
|
||||
// _projectId = projectId;
|
||||
// _modelId = modelId;
|
||||
//
|
||||
// Uri apiBaseUrl = !string.IsNullOrEmpty(apiEndpoint)
|
||||
// ? new Uri(apiEndpoint)
|
||||
// // : new Uri("http://dimitries-macbook-pro.mermaid-emperor.ts.net/api/v1/");
|
||||
// : new Uri("http://zog.local:3000/api/v1/");
|
||||
// _client = new HttpClient { BaseAddress = apiBaseUrl, Timeout = TimeSpan.FromMinutes(10) };
|
||||
//
|
||||
// if (authToken != null)
|
||||
// {
|
||||
// _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authToken);
|
||||
// }
|
||||
//
|
||||
// _channel = Channel.CreateBounded<UploadItem>(
|
||||
// new BoundedChannelOptions(1000) { FullMode = BoundedChannelFullMode.Wait } // if we're not able to write fast enough, we'll block writes
|
||||
// );
|
||||
//
|
||||
// _sendTask = SendLoopAsync(projectId, "test");
|
||||
// }
|
||||
//
|
||||
// public ValueTask PushAsync(UploadItem item, CancellationToken ct = default) => _channel.Writer.WriteAsync(item, ct);
|
||||
//
|
||||
// public async Task CompleteAsync()
|
||||
// {
|
||||
// _channel.Writer.Complete();
|
||||
// await _sendTask.ConfigureAwait(false);
|
||||
// }
|
||||
//
|
||||
// private async Task SendLoopAsync(string projectId, string modelId)
|
||||
// {
|
||||
// var content = new PushStreamContent(
|
||||
// async (stream, _, _) =>
|
||||
// {
|
||||
// var gzip = new GZipStream(stream, CompressionLevel.Optimal);
|
||||
// var writer = new StreamWriter(gzip); // new StreamWriter(gzip, System.Text.Encoding.UTF8, 20 * 1024 * 1024); // potential lever for controlling memory pressure
|
||||
// try
|
||||
// {
|
||||
// // extra levers for memory pressure in here: we can manually flush every x items or every x bytes
|
||||
// await foreach (var item in _channel.Reader.ReadAllAsync().ConfigureAwait(false))
|
||||
// {
|
||||
// await writer.WriteLineAsync($"{item.Id}\t{item.Json}\t{item.SpeckleType}").ConfigureAwait(false);
|
||||
// }
|
||||
// }
|
||||
// finally
|
||||
// {
|
||||
// await writer.FlushAsync().ConfigureAwait(false);
|
||||
// await gzip.FlushAsync().ConfigureAwait(false);
|
||||
// writer.Dispose();
|
||||
// gzip.Dispose();
|
||||
// }
|
||||
// },
|
||||
// new MediaTypeHeaderValue("application/x-ndjson")
|
||||
// );
|
||||
//
|
||||
// var uri = new Uri($"projects/{projectId}/models/{modelId}/versions", UriKind.Relative);
|
||||
// var request = new HttpRequestMessage(HttpMethod.Post, uri) { Content = content };
|
||||
// request.Headers.TransferEncodingChunked = true; // NOTE: important for streaming to happen.
|
||||
// var response = await _client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
|
||||
// response.EnsureSuccessStatusCode();
|
||||
//
|
||||
// // Consume the response body to fully complete the request
|
||||
// await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
// }
|
||||
//
|
||||
// public void Dispose() => _client.Dispose();
|
||||
// }
|
||||
@@ -2,7 +2,13 @@ using System.Text;
|
||||
|
||||
namespace Speckle.Sdk.Serialisation.V2.Send;
|
||||
|
||||
public sealed record BaseItem(Id Id, Json Json, bool NeedsStorage, Dictionary<Id, int>? Closures) : IHasByteSize
|
||||
public sealed record BaseItem(
|
||||
Id Id,
|
||||
Json Json,
|
||||
bool NeedsStorage,
|
||||
Dictionary<Id, int>? Closures,
|
||||
bool? IsReference = false
|
||||
) : IHasByteSize
|
||||
{
|
||||
public int ByteSize { get; } = Encoding.UTF8.GetByteCount(Json.Value);
|
||||
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
using System.IO.Compression;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Threading.Channels;
|
||||
|
||||
namespace Speckle.Sdk.Serialisation.V2.Send;
|
||||
|
||||
#pragma warning disable CA1001
|
||||
public sealed class ObjectFlopper
|
||||
#pragma warning restore CA1001
|
||||
{
|
||||
private readonly Uri _url;
|
||||
private readonly string _streamId;
|
||||
private readonly HttpClient _client;
|
||||
private readonly Channel<BaseItem> _channel;
|
||||
private readonly Task _sendTask;
|
||||
|
||||
public ObjectFlopper(Uri _, string streamId, string? authToken)
|
||||
{
|
||||
_streamId = streamId;
|
||||
_url = new Uri("http://zog.local:3000/api/v1/");
|
||||
_client = new HttpClient { BaseAddress = _url, Timeout = TimeSpan.FromMinutes(10) };
|
||||
|
||||
if (authToken != null)
|
||||
{
|
||||
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authToken);
|
||||
}
|
||||
|
||||
_channel = Channel.CreateBounded<BaseItem>(
|
||||
new BoundedChannelOptions(1000) { FullMode = BoundedChannelFullMode.Wait }
|
||||
);
|
||||
|
||||
_sendTask = SendLoopAsync(streamId, "test");
|
||||
}
|
||||
|
||||
public ValueTask PushAsync(BaseItem item, CancellationToken ct = default) => _channel.Writer.WriteAsync(item, ct);
|
||||
|
||||
public async Task CompleteAsync()
|
||||
{
|
||||
_channel.Writer.Complete();
|
||||
await _sendTask.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task SendLoopAsync(string projectId, string modelId)
|
||||
{
|
||||
var content = new PushStreamContent(
|
||||
async (stream, _, _) =>
|
||||
{
|
||||
var gzip = new GZipStream(stream, CompressionLevel.Optimal);
|
||||
var writer = new StreamWriter(gzip); //new StreamWriter(gzip, System.Text.Encoding.UTF8, 20 * 1024 * 1024);
|
||||
try
|
||||
{
|
||||
await foreach (var item in _channel.Reader.ReadAllAsync().ConfigureAwait(false))
|
||||
{
|
||||
await writer.WriteLineAsync($"{item.Id}\t{item.Json}").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
await writer.FlushAsync().ConfigureAwait(false);
|
||||
await gzip.FlushAsync().ConfigureAwait(false);
|
||||
writer.Dispose();
|
||||
gzip.Dispose();
|
||||
}
|
||||
},
|
||||
new MediaTypeHeaderValue("application/x-ndjson")
|
||||
);
|
||||
|
||||
var uri = new Uri($"projects/{projectId}/models/{modelId}/versions", UriKind.Relative);
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, uri) { Content = content };
|
||||
request.Headers.TransferEncodingChunked = true; // NOTE: important for streaming to happen.
|
||||
var response = await _client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
// Consume the response body to fully complete the request
|
||||
await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void Dispose() => _client.Dispose();
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
using System.IO.Compression;
|
||||
using System.IO.Pipelines;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Threading.Channels;
|
||||
|
||||
namespace Speckle.Sdk.Serialisation.V2.Send;
|
||||
|
||||
#pragma warning disable CA1001
|
||||
public sealed class ObjectFlopperGandalf
|
||||
#pragma warning restore CA1001
|
||||
{
|
||||
private readonly Uri _url;
|
||||
private readonly string _streamId;
|
||||
private readonly HttpClient _client;
|
||||
private readonly Channel<BaseItem> _channel;
|
||||
private readonly Task _sendTask;
|
||||
|
||||
public ObjectFlopperGandalf(Uri _, string streamId, string? authToken)
|
||||
{
|
||||
_streamId = streamId;
|
||||
_url = new Uri("http://bender-2.local:3000/api/v1/");
|
||||
_client = new HttpClient { BaseAddress = _url, Timeout = TimeSpan.FromMinutes(10) };
|
||||
|
||||
if (authToken != null)
|
||||
{
|
||||
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authToken);
|
||||
}
|
||||
|
||||
_channel = Channel.CreateBounded<BaseItem>(
|
||||
new BoundedChannelOptions(1000) { FullMode = BoundedChannelFullMode.Wait }
|
||||
);
|
||||
|
||||
_sendTask = SendLoopAsync(streamId, "test");
|
||||
}
|
||||
|
||||
public ValueTask PushAsync(BaseItem item, CancellationToken ct = default) => _channel.Writer.WriteAsync(item, ct);
|
||||
|
||||
public async Task CompleteAsync()
|
||||
{
|
||||
_channel.Writer.Complete();
|
||||
await _sendTask.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task SendLoopAsync(string projectId, string modelId)
|
||||
{
|
||||
var pipe = new Pipe();
|
||||
|
||||
// Start writing to pipe immediately in background
|
||||
var writeTask = Task.Run(async () =>
|
||||
{
|
||||
var gzip = new GZipStream(pipe.Writer.AsStream(), CompressionLevel.Optimal);
|
||||
var writer = new StreamWriter(gzip);
|
||||
|
||||
try
|
||||
{
|
||||
await foreach (var item in _channel.Reader.ReadAllAsync().ConfigureAwait(false))
|
||||
{
|
||||
await writer.WriteLineAsync($"{item.Id}\t{item.Json}").ConfigureAwait(false);
|
||||
await writer.FlushAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
await writer.FlushAsync().ConfigureAwait(false);
|
||||
await gzip.FlushAsync().ConfigureAwait(false);
|
||||
writer.Dispose();
|
||||
gzip.Dispose();
|
||||
await pipe.Writer.CompleteAsync().ConfigureAwait(false);
|
||||
}
|
||||
});
|
||||
|
||||
// Start HTTP request immediately, reading from pipe
|
||||
var content = new StreamContent(pipe.Reader.AsStream());
|
||||
content.Headers.ContentType = new MediaTypeHeaderValue("application/x-ndjson");
|
||||
content.Headers.ContentEncoding.Add("gzip");
|
||||
|
||||
var uri = new Uri($"projects/{projectId}/models/{modelId}/objects", UriKind.Relative);
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, uri) { Content = content };
|
||||
|
||||
var responseTask = _client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
|
||||
|
||||
// Wait for both
|
||||
await Task.WhenAll(writeTask, responseTask).ConfigureAwait(false);
|
||||
|
||||
var response = await responseTask.ConfigureAwait(false);
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
|
||||
public void Dispose() => _client.Dispose();
|
||||
}
|
||||
@@ -113,16 +113,6 @@ public sealed class SerializeProcess(
|
||||
_processSource.Token
|
||||
);
|
||||
var findTotalObjectsTask = Task.CompletedTask;
|
||||
if (!options.SkipFindTotalObjects)
|
||||
{
|
||||
ThrowIfFailed();
|
||||
findTotalObjectsTask = Task.Factory.StartNew(
|
||||
() => TraverseTotal(root),
|
||||
_processSource.Token,
|
||||
TaskCreationOptions.AttachedToParent | TaskCreationOptions.PreferFairness,
|
||||
_highest
|
||||
);
|
||||
}
|
||||
|
||||
await Traverse(root).ConfigureAwait(false);
|
||||
ThrowIfFailed();
|
||||
@@ -133,6 +123,7 @@ public sealed class SerializeProcess(
|
||||
ThrowIfFailed();
|
||||
await WaitForSchedulerCompletion().ConfigureAwait(false);
|
||||
ThrowIfFailed();
|
||||
|
||||
return new(root.id.NotNull(), baseSerializer.ObjectReferences.Freeze());
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
|
||||
@@ -35,7 +35,8 @@ public class SerializeProcessFactory(
|
||||
IServerObjectManager serverObjectManager,
|
||||
IProgress<ProgressArgs>? progress,
|
||||
CancellationToken cancellationToken,
|
||||
SerializeProcessOptions? options = null
|
||||
SerializeProcessOptions? options = null,
|
||||
ObjectFlopper? objectFlopper = null
|
||||
) =>
|
||||
new SerializeProcess(
|
||||
progress,
|
||||
|
||||
@@ -23,19 +23,23 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="GraphQL.Client" />
|
||||
<PackageReference Include="Microsoft.AspNet.WebApi.Client" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" />
|
||||
<PackageReference Include="Speckle.DoubleNumerics" />
|
||||
<PackageReference Include="Speckle.Newtonsoft.Json" />
|
||||
<PackageReference Include="System.IO.Pipelines" />
|
||||
<PackageReference Include="System.Net.Http.Json" />
|
||||
<PackageReference Include="System.Threading.Channels" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0'">
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" OverrideVersion="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" OverrideVersion="8.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0'">
|
||||
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
|
||||
<PackageReference Include="Microsoft.CSharp" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" />
|
||||
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Speckle.Sdk.Dependencies\Speckle.Sdk.Dependencies.csproj" />
|
||||
|
||||
@@ -13,13 +13,25 @@
|
||||
"System.Reactive": "5.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.AspNet.WebApi.Client": {
|
||||
"type": "Direct",
|
||||
"requested": "[6.0.0, )",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "zXeWP03dTo67AoDHUzR+/urck0KFssdCKOC+dq7Nv1V2YbFh/nIg09L0/3wSvyRONEdwxGB/ssEGmPNIIhAcAw==",
|
||||
"dependencies": {
|
||||
"Newtonsoft.Json": "13.0.1",
|
||||
"Newtonsoft.Json.Bson": "1.0.2",
|
||||
"System.Memory": "4.5.5",
|
||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||
}
|
||||
},
|
||||
"Microsoft.Bcl.AsyncInterfaces": {
|
||||
"type": "Direct",
|
||||
"requested": "[5.0.0, )",
|
||||
"resolved": "5.0.0",
|
||||
"contentHash": "W8DPQjkMScOMTtJbPwmPyj9c3zYSFGawDW3jwlBOOsnY+EzZFLgNQ/UMkK35JmkNOVPdCyPr2Tw7Vv9N+KA3ZQ==",
|
||||
"requested": "[10.0.1, )",
|
||||
"resolved": "10.0.1",
|
||||
"contentHash": "E1HSLkPHXEO30JEij2pWbOuzz1Z5ND4a5l7IP1T2RgQuE0a0NzEIvtO64RNy3Otn6PFezbT80cfm3M/Cgt70PA==",
|
||||
"dependencies": {
|
||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||
"System.Threading.Tasks.Extensions": "4.6.3"
|
||||
}
|
||||
},
|
||||
"Microsoft.CSharp": {
|
||||
@@ -99,6 +111,39 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"System.IO.Pipelines": {
|
||||
"type": "Direct",
|
||||
"requested": "[10.0.1, )",
|
||||
"resolved": "10.0.1",
|
||||
"contentHash": "26LbFXHKd7PmRnWlkjnYgmjd5B6HYVG+1MpTO25BdxTJnx6D0O16JPAC/S4YBqjtt4YpfGj1QO/Ss6SPMGEGQw==",
|
||||
"dependencies": {
|
||||
"System.Buffers": "4.6.1",
|
||||
"System.Memory": "4.6.3",
|
||||
"System.Threading.Tasks.Extensions": "4.6.3"
|
||||
}
|
||||
},
|
||||
"System.Net.Http.Json": {
|
||||
"type": "Direct",
|
||||
"requested": "[10.0.1, )",
|
||||
"resolved": "10.0.1",
|
||||
"contentHash": "XGOWt78ccgO9esyNlCemlS2b8JZPrH85pk/MvdHJxp6KwwUY/GnDaw2fPpJa7lgotiTkWnXnhip+ULNKaP7a8A==",
|
||||
"dependencies": {
|
||||
"System.Buffers": "4.6.1",
|
||||
"System.Memory": "4.6.3",
|
||||
"System.Text.Json": "10.0.1",
|
||||
"System.Threading.Tasks.Extensions": "4.6.3"
|
||||
}
|
||||
},
|
||||
"System.Threading.Channels": {
|
||||
"type": "Direct",
|
||||
"requested": "[10.0.1, )",
|
||||
"resolved": "10.0.1",
|
||||
"contentHash": "YRqU6Y2Cl6C+HrG5h1ftgKZ5VDTSA7j1wMKs5RtlauPeQ2EZ639Jt5aOFHdX3naP01hDDWFOWPApmNDVKwOpmg==",
|
||||
"dependencies": {
|
||||
"Microsoft.Bcl.AsyncInterfaces": "10.0.1",
|
||||
"System.Threading.Tasks.Extensions": "4.6.3"
|
||||
}
|
||||
},
|
||||
"GraphQL.Client.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
@@ -196,6 +241,19 @@
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
|
||||
},
|
||||
"Newtonsoft.Json": {
|
||||
"type": "Transitive",
|
||||
"resolved": "13.0.1",
|
||||
"contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
|
||||
},
|
||||
"Newtonsoft.Json.Bson": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.2",
|
||||
"contentHash": "QYFyxhaABwmq3p/21VrZNYvCg3DaEoN/wUuw5nmfAf0X3HLjgupwhkEWdgfb9nvGAUIv3osmZoD3kKl4jxEmYQ==",
|
||||
"dependencies": {
|
||||
"Newtonsoft.Json": "12.0.1"
|
||||
}
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -228,8 +286,8 @@
|
||||
},
|
||||
"System.Buffers": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "AwarXzzoDwX6BgrhjoJsk6tUezZEozOT5Y9QKF94Gl4JK91I4PIIBkBco9068Y9/Dra8Dkbie99kXB8+1BaYKw=="
|
||||
"resolved": "4.6.1",
|
||||
"contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw=="
|
||||
},
|
||||
"System.ComponentModel.Annotations": {
|
||||
"type": "Transitive",
|
||||
@@ -238,18 +296,18 @@
|
||||
},
|
||||
"System.Memory": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.3",
|
||||
"contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA==",
|
||||
"resolved": "4.6.3",
|
||||
"contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==",
|
||||
"dependencies": {
|
||||
"System.Buffers": "4.4.0",
|
||||
"System.Numerics.Vectors": "4.4.0",
|
||||
"System.Runtime.CompilerServices.Unsafe": "4.5.2"
|
||||
"System.Buffers": "4.6.1",
|
||||
"System.Numerics.Vectors": "4.6.1",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.1.2"
|
||||
}
|
||||
},
|
||||
"System.Numerics.Vectors": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ=="
|
||||
"resolved": "4.6.1",
|
||||
"contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q=="
|
||||
},
|
||||
"System.Reactive": {
|
||||
"type": "Transitive",
|
||||
@@ -271,8 +329,8 @@
|
||||
},
|
||||
"System.Runtime.CompilerServices.Unsafe": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.3",
|
||||
"contentHash": "3TIsJhD1EiiT0w2CcDMN/iSSwnNnsrnbzeVHSKkaEgV85txMprmuO+Yq2AdSbeVGcg28pdNDTPK87tJhX7VFHw=="
|
||||
"resolved": "6.1.2",
|
||||
"contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw=="
|
||||
},
|
||||
"System.Runtime.InteropServices.WindowsRuntime": {
|
||||
"type": "Transitive",
|
||||
@@ -282,16 +340,75 @@
|
||||
"System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Text.Encodings.Web": {
|
||||
"type": "Transitive",
|
||||
"resolved": "10.0.1",
|
||||
"contentHash": "cVAka0o1rJJ5/De0pjNs7jcaZk5hUGf1HGzUyVmE2MEB1Vf0h/8qsWxImk1zjitCbeD2Avaq2P2+usdvqgbeVQ==",
|
||||
"dependencies": {
|
||||
"System.Buffers": "4.6.1",
|
||||
"System.Memory": "4.6.3",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.1.2"
|
||||
}
|
||||
},
|
||||
"System.Threading.Tasks.Extensions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.4",
|
||||
"contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
|
||||
"resolved": "4.6.3",
|
||||
"contentHash": "7sCiwilJLYbTZELaKnc7RecBBXWXA+xMLQWZKWawBxYjp6DBlSE3v9/UcvKBvr1vv2tTOhipiogM8rRmxlhrVA==",
|
||||
"dependencies": {
|
||||
"System.Runtime.CompilerServices.Unsafe": "4.5.3"
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.1.2"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.common": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[1.0.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.logging": {
|
||||
"type": "Project"
|
||||
},
|
||||
"speckle.converters.common": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[1.0.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.objects": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "[1.0.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.sdk.dependencies": {
|
||||
"type": "Project"
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
|
||||
}
|
||||
},
|
||||
"System.Text.Json": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[8.0.5, )",
|
||||
"resolved": "10.0.1",
|
||||
"contentHash": "EsgwDgU1PFqhrFA9l5n+RBu76wFhNGCEwu8ITrBNhjPP3MxLyklroU5GIF8o6JYpYg6T4KD/VICfMdgPAvNp5g==",
|
||||
"dependencies": {
|
||||
"Microsoft.Bcl.AsyncInterfaces": "10.0.1",
|
||||
"System.Buffers": "4.6.1",
|
||||
"System.IO.Pipelines": "10.0.1",
|
||||
"System.Memory": "4.6.3",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.1.2",
|
||||
"System.Text.Encodings.Web": "10.0.1",
|
||||
"System.Threading.Tasks.Extensions": "4.6.3"
|
||||
}
|
||||
}
|
||||
},
|
||||
"net8.0": {
|
||||
@@ -306,6 +423,18 @@
|
||||
"System.Reactive": "5.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.AspNet.WebApi.Client": {
|
||||
"type": "Direct",
|
||||
"requested": "[6.0.0, )",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "zXeWP03dTo67AoDHUzR+/urck0KFssdCKOC+dq7Nv1V2YbFh/nIg09L0/3wSvyRONEdwxGB/ssEGmPNIIhAcAw==",
|
||||
"dependencies": {
|
||||
"Newtonsoft.Json": "13.0.1",
|
||||
"Newtonsoft.Json.Bson": "1.0.2",
|
||||
"System.Memory": "4.5.5",
|
||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||
}
|
||||
},
|
||||
"Microsoft.Data.Sqlite": {
|
||||
"type": "Direct",
|
||||
"requested": "[7.0.5, )",
|
||||
@@ -368,6 +497,27 @@
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"System.IO.Pipelines": {
|
||||
"type": "Direct",
|
||||
"requested": "[10.0.1, )",
|
||||
"resolved": "10.0.1",
|
||||
"contentHash": "26LbFXHKd7PmRnWlkjnYgmjd5B6HYVG+1MpTO25BdxTJnx6D0O16JPAC/S4YBqjtt4YpfGj1QO/Ss6SPMGEGQw=="
|
||||
},
|
||||
"System.Net.Http.Json": {
|
||||
"type": "Direct",
|
||||
"requested": "[10.0.1, )",
|
||||
"resolved": "10.0.1",
|
||||
"contentHash": "XGOWt78ccgO9esyNlCemlS2b8JZPrH85pk/MvdHJxp6KwwUY/GnDaw2fPpJa7lgotiTkWnXnhip+ULNKaP7a8A==",
|
||||
"dependencies": {
|
||||
"System.Text.Json": "10.0.1"
|
||||
}
|
||||
},
|
||||
"System.Threading.Channels": {
|
||||
"type": "Direct",
|
||||
"requested": "[10.0.1, )",
|
||||
"resolved": "10.0.1",
|
||||
"contentHash": "YRqU6Y2Cl6C+HrG5h1ftgKZ5VDTSA7j1wMKs5RtlauPeQ2EZ639Jt5aOFHdX3naP01hDDWFOWPApmNDVKwOpmg=="
|
||||
},
|
||||
"GraphQL.Client.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
@@ -455,6 +605,19 @@
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
|
||||
},
|
||||
"Newtonsoft.Json": {
|
||||
"type": "Transitive",
|
||||
"resolved": "13.0.1",
|
||||
"contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
|
||||
},
|
||||
"Newtonsoft.Json.Bson": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.2",
|
||||
"contentHash": "QYFyxhaABwmq3p/21VrZNYvCg3DaEoN/wUuw5nmfAf0X3HLjgupwhkEWdgfb9nvGAUIv3osmZoD3kKl4jxEmYQ==",
|
||||
"dependencies": {
|
||||
"Newtonsoft.Json": "12.0.1"
|
||||
}
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
@@ -492,8 +655,8 @@
|
||||
},
|
||||
"System.Memory": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.3",
|
||||
"contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA=="
|
||||
"resolved": "4.5.5",
|
||||
"contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw=="
|
||||
},
|
||||
"System.Reactive": {
|
||||
"type": "Transitive",
|
||||
@@ -505,8 +668,62 @@
|
||||
"resolved": "4.5.1",
|
||||
"contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw=="
|
||||
},
|
||||
"System.Text.Encodings.Web": {
|
||||
"type": "Transitive",
|
||||
"resolved": "10.0.1",
|
||||
"contentHash": "cVAka0o1rJJ5/De0pjNs7jcaZk5hUGf1HGzUyVmE2MEB1Vf0h/8qsWxImk1zjitCbeD2Avaq2P2+usdvqgbeVQ=="
|
||||
},
|
||||
"System.Threading.Tasks.Extensions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.4",
|
||||
"contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg=="
|
||||
},
|
||||
"speckle.connectors.common": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Converters.Common": "[1.0.0, )",
|
||||
"Speckle.Objects": "[1.0.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.logging": {
|
||||
"type": "Project"
|
||||
},
|
||||
"speckle.converters.common": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[1.0.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.objects": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "[1.0.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.sdk.dependencies": {
|
||||
"type": "Project"
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
|
||||
}
|
||||
},
|
||||
"System.Text.Json": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[8.0.5, )",
|
||||
"resolved": "10.0.1",
|
||||
"contentHash": "EsgwDgU1PFqhrFA9l5n+RBu76wFhNGCEwu8ITrBNhjPP3MxLyklroU5GIF8o6JYpYg6T4KD/VICfMdgPAvNp5g==",
|
||||
"dependencies": {
|
||||
"System.IO.Pipelines": "10.0.1",
|
||||
"System.Text.Encodings.Web": "10.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user