├── .gitignore ├── SpeckleCore ├── Models │ ├── ModelObjects.cs │ ├── ModelObjectsExt.cs │ ├── ModelResponses.cs │ └── ModelBase.cs ├── Data │ ├── SpeckleError.cs │ └── SpeckleException.cs ├── Conversion │ ├── Converter.cs │ ├── ConverterUtilities.cs │ ├── ConverterSerialisation.cs │ ├── JsonConverters.cs │ └── ConverterDeserialisation.cs ├── SpeckleCore.nuspec ├── SpeckleCore.sln ├── Generic Utils │ └── GzipContent.cs ├── SpeckleCore.csproj ├── ApiClient │ ├── SpeckleApiClientHead.cs │ └── SpeckleApiClientExtension.cs ├── LocalData │ ├── Models.cs │ └── SpeckleLocalContext.cs ├── ModuleInitializer.cs ├── Telemetry.cs └── AssemblyCatalogue.cs ├── ISSUE_TEMPLATE.md ├── SpeckleTests ├── SpeckleTests.csproj └── Kits.cs ├── LICENSE ├── README.md ├── appveyor.yml ├── .gitattributes ├── CODE_OF_CONDUCT.md └── CONTRIBUTING.md /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | obj/ 3 | packages/ 4 | bin/ 5 | *.nupkg 6 | *.snupkg 7 | -------------------------------------------------------------------------------- /SpeckleCore/Models/ModelObjects.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace SpeckleCore 10 | { 11 | } 12 | -------------------------------------------------------------------------------- /SpeckleCore/Data/SpeckleError.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SpeckleCore.Data 8 | { 9 | public class SpeckleError 10 | { 11 | public string Message { get; set; } 12 | public string Details { get; set; } 13 | } 14 | public class SpeckleConversionError : SpeckleError 15 | { 16 | public object SourceObject { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Step 0: 2 | - [ ] I've read the [contribution guidelines](https://github.com/speckleworks/SpeckleOrg/blob/master/CONTRIBUTING.md)! 3 | #### Expected Behaviour 4 | How things should actually work! 5 | 6 | 7 | #### Actual Behaviour 8 | What's actually going on! 9 | 10 | 11 | #### Affected Projects 12 | Does this issue propagate to other dependencies or dependants? If so, list them here! 13 | 14 | 15 | #### Reproduction Steps & System Config (win, osx, web, etc.) 16 | 17 | #### Proposed Solution (if any) 18 | -------------------------------------------------------------------------------- /SpeckleTests/SpeckleTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /SpeckleTests/Kits.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using SpeckleCore; 3 | 4 | namespace SpeckleTests 5 | { 6 | public class GenericTests 7 | { 8 | [SetUp] 9 | public void Setup() 10 | { 11 | System.IO.Directory.CreateDirectory(System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData) + @"\SpeckleKits\"); 12 | SpeckleCore.SpeckleInitializer.Initialize(); 13 | } 14 | 15 | [Test] 16 | public void LoadingKits() 17 | { 18 | var kits = SpeckleCore.SpeckleInitializer.GetAssemblies(); 19 | var types = SpeckleCore.SpeckleInitializer.GetTypes(); 20 | 21 | Assert.GreaterOrEqual(types.Count, 1); 22 | Assert.GreaterOrEqual(kits.Count, 1); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /SpeckleCore/Conversion/Converter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Concurrent; 4 | using System.Collections.Generic; 5 | using System.ComponentModel; 6 | using System.Drawing; 7 | using System.Linq; 8 | using System.Reflection; 9 | using System.Runtime.CompilerServices; 10 | using System.Runtime.InteropServices; 11 | using System.Text; 12 | using System.Threading.Tasks; 13 | 14 | 15 | namespace SpeckleCore 16 | { 17 | public abstract partial class Converter 18 | { 19 | public static Dictionary toSpeckleMethods = new Dictionary(); 20 | 21 | public static Dictionary toNativeMethods = new Dictionary(); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /SpeckleCore/Data/SpeckleException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SpeckleCore.Data 8 | { 9 | public class SpeckleException : Exception 10 | { 11 | public SpeckleException() 12 | { 13 | } 14 | 15 | public SpeckleException(string message) 16 | : base(message) 17 | { 18 | } 19 | 20 | public SpeckleException(string message, Exception inner) 21 | : base(message, inner) 22 | { 23 | } 24 | } 25 | 26 | public class RevitFamilyNotFoundException : SpeckleException 27 | { 28 | public RevitFamilyNotFoundException() 29 | { 30 | } 31 | 32 | public RevitFamilyNotFoundException(string message) 33 | : base(message) 34 | { 35 | } 36 | 37 | public RevitFamilyNotFoundException(string message, Exception inner) 38 | : base(message, inner) 39 | { 40 | } 41 | } 42 | } 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /SpeckleCore/SpeckleCore.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $id$ 5 | $version$ 6 | $title$ 7 | $author$ 8 | $author$ 9 | MIT 10 | https://github.com/speckleworks/SpeckleCore 11 | https://avatars2.githubusercontent.com/u/31284874 12 | false 13 | $description$ 14 | 15 | $copyright$ 16 | speckle core 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Dimitrie A. Stefanescu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SpeckleCore 2 | [![Build status](https://ci.appveyor.com/api/projects/status/k0n0853v26f1thl4/branch/master?svg=true)](https://ci.appveyor.com/project/SpeckleWorks/specklecore/branch/master) [![DOI](https://zenodo.org/badge/100398062.svg)](https://zenodo.org/badge/latestdoi/100398062) 3 | 4 | 5 | 6 | ## ⚠️ IMPORTANT ⚠️ 7 | 8 | Speckle v1 has reached EOL and is no longer mainatined. Check out our brand new 👉 [Speckle v2](https://github.com/specklesystems)! 9 | Read more about the announcemnt [here](https://speckle.systems/blog/speckle2-vision-and-faq) and check out or new [website](https://speckle.systems). 10 | 11 | 12 | ## Overview 13 | 14 | This is the core .NET client lib of Speckle. It provides: 15 | - async methods for calling the speckle [api](https://speckleworks.github.io/SpeckleOpenApi/) 16 | - methods for interacting with the speckle's websocket api 17 | - the core conversion methods (`Serialise` and `Deserialise`) & other helper methods 18 | - a base SpeckleObject from which you can inherit to create your own speckle kits 19 | 20 | Pretty much all of speckle's connectors are using this library, including: 21 | - Rhino 22 | - Grasshopper 23 | - Revit 24 | - Dynamo 25 | - Unity (with flavours) 26 | 27 | ## Example usage 28 | 29 | Please check [out the docs and sample starter project on the speckle website](https://speckle.systems/docs/developers/dotnet-sdk)! 30 | 31 | ## License 32 | MIT 33 | -------------------------------------------------------------------------------- /SpeckleCore/SpeckleCore.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29519.87 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpeckleCore", "SpeckleCore.csproj", "{2160CC3B-A7E6-4BC5-8C65-742487B16553}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpeckleTests", "..\SpeckleTests\SpeckleTests.csproj", "{C8A44343-4376-4A57-9199-072F08F19E57}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {2160CC3B-A7E6-4BC5-8C65-742487B16553}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {2160CC3B-A7E6-4BC5-8C65-742487B16553}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {2160CC3B-A7E6-4BC5-8C65-742487B16553}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {2160CC3B-A7E6-4BC5-8C65-742487B16553}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {C8A44343-4376-4A57-9199-072F08F19E57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {C8A44343-4376-4A57-9199-072F08F19E57}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {C8A44343-4376-4A57-9199-072F08F19E57}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {C8A44343-4376-4A57-9199-072F08F19E57}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {4FFC29F0-AC63-4B46-87B2-946DDC4F1454} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /SpeckleCore/Generic Utils/GzipContent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.IO.Compression; 5 | using System.Linq; 6 | using System.Net; 7 | using System.Net.Http; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace SpeckleCore 12 | { 13 | /// 14 | /// https://cymbeline.ch/2014/03/16/gzip-encoding-an-http-post-request-body/ 15 | /// 16 | internal sealed class GzipContent : HttpContent 17 | { 18 | private readonly HttpContent content; 19 | 20 | public GzipContent( HttpContent content ) 21 | { 22 | if(content == null) 23 | { 24 | return; 25 | } 26 | 27 | this.content = content; 28 | 29 | // Keep the original content's headers ... 30 | if ( content != null ) 31 | foreach ( KeyValuePair> header in content.Headers ) 32 | { 33 | Headers.TryAddWithoutValidation( header.Key, header.Value ); 34 | } 35 | 36 | // ... and let the server know we've Gzip-compressed the body of this request. 37 | Headers.ContentEncoding.Add( "gzip" ); 38 | } 39 | 40 | protected override async Task SerializeToStreamAsync( Stream stream, TransportContext context ) 41 | { 42 | // Open a GZipStream that writes to the specified output stream. 43 | using ( GZipStream gzip = new GZipStream( stream, CompressionMode.Compress, true ) ) 44 | { 45 | // Copy all the input content to the GZip stream. 46 | if ( content != null ) 47 | await content.CopyToAsync( gzip ); 48 | else 49 | await ( new System.Net.Http.StringContent( string.Empty ) ).CopyToAsync( gzip ); 50 | } 51 | } 52 | 53 | protected override bool TryComputeLength( out long length ) 54 | { 55 | length = -1; 56 | return false; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /SpeckleCore/SpeckleCore.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net45;netstandard2.0 4 | https://speckle.works 5 | 8.0 6 | Speckle 7 | Speckle 8 | SpeckleCore 9 | .NET base for the speckle api 10 | .NET base for the speckle api 11 | en-US 12 | Copyright © Speckle 13 | SpeckleCore 14 | SpeckleCore 15 | https://speckle.works 16 | https://github.com/speckleworks/SpeckleCore 17 | true 18 | true 19 | MIT 20 | snupkg 21 | 1.0.0.0 22 | 1.0.0.0 23 | 1.0.0.0 24 | 1.0.0.0 25 | 1.0.0.0 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /SpeckleCore/ApiClient/SpeckleApiClientHead.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Net.Http; 5 | using System.IO.Compression; 6 | using System.IO; 7 | using System.Text; 8 | 9 | 10 | namespace SpeckleCore 11 | { 12 | [System.CodeDom.Compiler.GeneratedCode( "NSwag", "11.3.3.0" )] 13 | public partial class SpeckleApiClient 14 | { 15 | private System.Lazy _settings; 16 | 17 | partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings ); 18 | 19 | partial void PrepareRequest( System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url ); 20 | 21 | partial void PrepareRequest( System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder ); 22 | 23 | partial void ProcessResponse( System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response ); 24 | 25 | partial void ProcessResponse( HttpClient client, HttpResponseMessage response ) 26 | { 27 | 28 | } 29 | 30 | partial void PrepareRequest( HttpClient client, HttpRequestMessage request, string url ) 31 | { 32 | // Try and attach the auth token if present 33 | if ( AuthToken != "" && AuthToken != null ) 34 | request.Headers.Add( "Authorization", AuthToken ); 35 | 36 | // Let the server know about our aspiration to accept gzipped content. 37 | request.Headers.Add( "Accept-Encoding", "gzip" ); 38 | 39 | // If actually sending a payload, deflate it. 40 | if ( UseGzip && request.Method != HttpMethod.Get ) 41 | request.Content = new GzipContent( request.Content ); 42 | } 43 | 44 | private HttpClient GetHttpClient(double timeoutMillisecondsOverride = 0) 45 | { 46 | var handler = new HttpClientHandler 47 | { 48 | AutomaticDecompression = System.Net.DecompressionMethods.GZip 49 | }; 50 | return new HttpClient(handler, true) 51 | { 52 | Timeout = TimeSpan.FromMilliseconds(timeoutMillisecondsOverride == 0 ? defaultTimeoutMilliseconds : timeoutMillisecondsOverride) 53 | }; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: '{build}' 2 | image: Visual Studio 2019 3 | init: 4 | - ps: | 5 | If($Env:APPVEYOR_REPO_TAG -eq "true") 6 | { 7 | Write-Host "We now have a tagged release. Proper." 8 | Write-Host "Tag is $Env:APPVEYOR_REPO_TAG_NAME" 9 | $base = $env:APPVEYOR_REPO_TAG_NAME.split('-')[0] 10 | $arr = $base.split(".") 11 | $major = $arr[0] 12 | $minor = $arr[1] 13 | $patch = $arr[2] 14 | Update-AppveyorBuild -Version "$major.$minor.$patch.$Env:APPVEYOR_BUILD_VERSION" 15 | $env:CLEANTAG=$base 16 | } 17 | else 18 | { 19 | $releases = "https://api.github.com/repos/$env:APPVEYOR_REPO_NAME/releases" 20 | $tag = (Invoke-WebRequest $releases | ConvertFrom-Json)[0].tag_name 21 | 22 | $base = $tag.Split("-")[0] 23 | $arr = $base.split(".") 24 | $major = $arr[0] 25 | $minor = $arr[1] 26 | $patch = $arr[2] 27 | Update-AppveyorBuild -Version "$major.$minor.$patch.$Env:APPVEYOR_BUILD_VERSION-wip" 28 | $env:CLEANTAG=$base 29 | } 30 | Write-Host "Hello. Build version is: $Env:APPVEYOR_BUILD_VERSION; Clean Tag: $Env:CLEANTAG" 31 | 32 | before_build: 33 | - cmd: >- 34 | nuget restore SpeckleCore\SpeckleCore.sln 35 | 36 | # version 37 | dotnet_csproj: 38 | patch: true 39 | file: '**\*.csproj' 40 | version: $(CLEANTAG) 41 | assembly_version: $(CLEANTAG) 42 | file_version: $(CLEANTAG) 43 | informational_version: $(CLEANTAG) 44 | package_version: '{version}' 45 | 46 | 47 | build: 48 | publish_nuget: true 49 | publish_nuget_symbols: true 50 | use_snupkg_format: true 51 | project: SpeckleCore\SpeckleCore.sln 52 | verbosity: minimal 53 | 54 | deploy: 55 | provider: NuGet 56 | server: # remove to push to NuGet.org 57 | api_key: 58 | secure: 5avZp2CuKwWIDcMTTQhXtRWX297z/dnZraZoSeGSey8W4stBeZIm5pZU38tTmJWZ 59 | skip_symbols: false 60 | symbol_server: # remove to push symbols to SymbolSource.org 61 | on: 62 | appveyor_repo_tag: true 63 | notifications: 64 | - provider: Slack 65 | incoming_webhook: 66 | secure: 9Kt/ImvGdsQ/dzmRdSaMIG+BO0R29MOJC7eZZnK5m5TyWwE+4nofEcYlb/r4lh9Z1Dx/bR+7oQtgAYO3W5s+qJshkT6Is9irYw3TfMZX7/M= 67 | channel: '#devtalk' 68 | template: Build <{{buildUrl}}|#{{buildVersion}}> (<{{commitUrl}}|{{commitId}}>) of {{repositoryName}}@{{branch}}) by {{commitAuthor}} {{status}} in {{duration}} 69 | on_build_success: false 70 | on_build_failure: true 71 | on_build_status_changed: true 72 | configuration: 73 | - Release 74 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces (github, slack, forum) and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at hello@speckle.works. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /SpeckleCore/LocalData/Models.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using SQLite; 7 | 8 | namespace SpeckleCore 9 | { 10 | 11 | /// 12 | /// Simple class to keep track of telemetry settings. 13 | /// 14 | public class TelemetrySettings 15 | { 16 | [PrimaryKey, AutoIncrement] 17 | public int SettingsId { get; set; } 18 | 19 | public bool Enabled { get; set; } = true; 20 | } 21 | 22 | /// 23 | /// A class for a generic speckle account, composed of all the identification props for a user & server combination. 24 | /// 25 | public class Account 26 | { 27 | [PrimaryKey, AutoIncrement] 28 | public int AccountId { get; set; } 29 | 30 | public string ServerName { get; set; } 31 | 32 | [Indexed] 33 | public string RestApi { get; set; } 34 | 35 | [Indexed] 36 | public string Email { get; set; } 37 | 38 | public string Token { get; set; } 39 | 40 | public bool IsDefault { get; set; } = false; 41 | } 42 | 43 | /// 44 | /// Special class for efficiently storing sent objects. Why? We do not want to store them fully as they are already stored in the users's file. Kind of duplicates the CachedObject. 45 | /// 46 | public class SentObject 47 | { 48 | /// 49 | /// Represents the api this object came from 50 | /// 51 | [Indexed] 52 | public string RestApi { get; set; } 53 | 54 | [Indexed] 55 | public string DatabaseId { get; set; } 56 | 57 | [Indexed] 58 | public string Hash { get; set; } 59 | } 60 | 61 | /// 62 | /// A class for storing cached objects (that have been retrieved from a database). 63 | /// 64 | public class CachedObject 65 | { 66 | /// 67 | /// Represents hash(databaseId + restApi) 68 | /// 69 | [PrimaryKey, Indexed( Unique = true )] 70 | public string CombinedHash { get; set; } 71 | 72 | /// 73 | /// Represents the api this object came from 74 | /// 75 | [Indexed] 76 | public string RestApi { get; set; } 77 | 78 | [Indexed] 79 | public string DatabaseId { get; set; } 80 | 81 | [Indexed] 82 | public string Hash { get; set; } 83 | 84 | public DateTime AddedOn {get;set;} 85 | 86 | public byte[ ] Bytes { get; set; } 87 | 88 | /// 89 | /// Returns the speckle object from cache. 90 | /// 91 | /// 92 | public SpeckleObject ToSpeckle( ) 93 | { 94 | return SpeckleCore.Converter.getObjFromBytes( this.Bytes ) as SpeckleObject; 95 | } 96 | } 97 | 98 | public class CachedStream 99 | { 100 | /// 101 | /// Represents hash(streamId + restApi) 102 | /// 103 | [PrimaryKey, Indexed( Unique = true )] 104 | public string CombinedHash { get; set; } 105 | 106 | /// 107 | /// Represents the api this object came from 108 | /// 109 | [Indexed] 110 | public string RestApi { get; set; } 111 | 112 | [Indexed] 113 | public string StreamId { get; set; } 114 | 115 | public DateTime AddedOn { get; set; } 116 | 117 | public DateTime UpdatedOn { get; set; } 118 | 119 | public byte[ ] Bytes { get; set; } 120 | 121 | public SpeckleStream ToSpeckle() 122 | { 123 | return SpeckleStream.FromJson( SpeckleCore.Converter.getObjFromBytes( this.Bytes ) as string ); // ((SpeckleCore.Converter.getObjFromBytes( this.Bytes ) as SpeckleStream; 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /SpeckleCore/ModuleInitializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Security.Cryptography; 6 | using System.Threading.Tasks; 7 | using CountlySDK; 8 | using CountlySDK.Entities; 9 | using DeviceId; 10 | using DeviceId.Encoders; 11 | using DeviceId.Formatters; 12 | 13 | namespace SpeckleCore 14 | { 15 | /// 16 | /// Use this interface to make sure static extension methods/object defintions are loaded from SpeckleKits AND/OR that you force a reference to SpeckleCore (thus making sure the assembly is loaded. 17 | /// 18 | public interface ISpeckleInitializer { } 19 | 20 | /// 21 | /// Initialisation class to be called from any application that hosts the speckle plugin 22 | /// at the begginning of the rituals 23 | /// 24 | public static class SpeckleInitializer 25 | { 26 | private static bool IsInit = false; 27 | private static IReadOnlyCollection Assembiles; 28 | private static IReadOnlyCollection Types; 29 | 30 | public static void Initialize( string pathToKits = null) 31 | { 32 | if ( IsInit ) return; 33 | 34 | IsInit = true; 35 | 36 | LocalContext.Init(); 37 | Assembiles = new SpeckleKitLoader(pathToKits).GetAssemblies(); 38 | 39 | var types = new List(); 40 | foreach ( var assembly in Assembiles ) 41 | { 42 | types.AddRange( FindDerivedTypes( assembly, typeof( SpeckleObject ) ).ToList() ); 43 | } 44 | types.Add( typeof( SpeckleObject ) ); 45 | Types = types; 46 | 47 | //////////////////////////////////////////////////////////////////////////////////////////////////// 48 | /// /// 49 | /// /// 50 | /// Hello devs! Uncomment the line below to disable telemetry. /// 51 | /// This will make speckle sad, but it's your call. /// 52 | /// See community discussions here: /// 53 | /// https://speckle-works.slack.com/archives/C4TE17LGH/p1567520201017900 /// 54 | /// https://discourse.speckle.works/t/community-consultation-time-telemetry/410 /// 55 | /// /// 56 | /// /// 57 | //////////////////////////////////////////////////////////////////////////////////////////////////// 58 | 59 | // LocalContext.SetTelemetrySettings( false ); 60 | 61 | // Note: if telemetry settings is set to false, then this will do nothing. 62 | SpeckleTelemetry.Initialize(); 63 | } 64 | 65 | /// 66 | /// Returns all the assemblies that have been loaded and are referencing SpeckleCore. 67 | /// 68 | /// 69 | public static IReadOnlyCollection GetAssemblies( ) 70 | { 71 | return Assembiles; 72 | } 73 | 74 | /// 75 | /// Gets the available speckle types, from core and other speckle kits. 76 | /// 77 | /// 78 | public static IReadOnlyCollection GetTypes( ) 79 | { 80 | return Types; 81 | } 82 | 83 | private static IEnumerable FindDerivedTypes( Assembly assembly, Type baseType ) 84 | { 85 | var types = assembly.GetTypes(); 86 | return assembly.GetTypes().Where( t => t.IsSubclassOf( baseType ) ); 87 | } 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /SpeckleCore/Telemetry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Security.Cryptography; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using CountlySDK; 8 | using CountlySDK.Entities; 9 | using DeviceId; 10 | using DeviceId.Encoders; 11 | using DeviceId.Formatters; 12 | 13 | namespace SpeckleCore 14 | { 15 | public static class SpeckleTelemetry 16 | { 17 | private static bool isInitialized = false; 18 | 19 | public static string DeviceId { get; set; } 20 | 21 | public static void Initialize( ) 22 | { 23 | if ( isInitialized ) 24 | return; 25 | 26 | if ( LocalContext.GetTelemetrySettings() == false ) 27 | return; 28 | try 29 | { 30 | DeviceId = new DeviceIdBuilder() 31 | .AddMachineName() 32 | .AddProcessorId() 33 | .UseFormatter(new HashDeviceIdFormatter(() => SHA256.Create(), new Base64UrlByteArrayEncoder())) 34 | .ToString(); 35 | 36 | var config = new CountlyConfig() 37 | { 38 | serverUrl = "https://telemetry.speckle.works", 39 | appKey = "cd6db5058036aafb6a3a82681d434ad74ee50ad9", 40 | deviceIdMethod = Countly.DeviceIdMethod.developerSupplied, 41 | developerProvidedDeviceId = DeviceId 42 | }; 43 | 44 | Countly.IsLoggingEnabled = true; 45 | Countly.Instance.Init(config); 46 | Countly.Instance.RecordView("speckle-init"); 47 | Countly.Instance.RecordView("speckle-init/version/" + typeof(SpeckleTelemetry).Assembly.GetName().Version); 48 | 49 | isInitialized = true; 50 | }catch(Exception e) 51 | { 52 | // POKEMON 53 | isInitialized = false; 54 | } 55 | } 56 | 57 | public static void RecordTestEvent( string clientType ) 58 | { 59 | Initialize(); 60 | 61 | if ( LocalContext.GetTelemetrySettings() == false ) 62 | return; 63 | 64 | var segmentation = GetSegmentation( clientType ); 65 | var test = Countly.RecordEvent( "test-event", 1, segmentation ).Result; 66 | } 67 | 68 | public static void RecordStreamCreated( string clientType ) 69 | { 70 | Initialize(); 71 | 72 | if ( LocalContext.GetTelemetrySettings() == false ) 73 | return; 74 | 75 | Task.Run( ( ) => 76 | { 77 | try 78 | { 79 | Countly.RecordEvent( "stream-created", 1, GetSegmentation( clientType ) ); 80 | } 81 | catch ( Exception e ) 82 | { 83 | } 84 | } ); 85 | } 86 | 87 | public static void RecordStreamUpdated( string clientType ) 88 | { 89 | Initialize(); 90 | 91 | if ( LocalContext.GetTelemetrySettings() == false ) 92 | return; 93 | 94 | Task.Run( ( ) => 95 | { 96 | try 97 | { 98 | Countly.RecordEvent( "stream-updated", 1, GetSegmentation( clientType ) ); 99 | } 100 | catch { } 101 | } ); 102 | } 103 | 104 | public static void RecordStreamReceived( string clientType ) 105 | { 106 | Initialize(); 107 | 108 | if ( LocalContext.GetTelemetrySettings() == false ) 109 | return; 110 | 111 | Task.Run( ( ) => 112 | { 113 | try 114 | { 115 | Countly.RecordEvent( "stream-received", 1, GetSegmentation( clientType ) ); 116 | } 117 | catch ( Exception e ) 118 | { 119 | 120 | } 121 | } ); 122 | } 123 | 124 | private static Segmentation GetSegmentation( string clientType = "undefined" ) 125 | { 126 | Initialize(); 127 | var segmentation = new Segmentation(); 128 | segmentation.Add( "machineId", DeviceId ); 129 | segmentation.Add( "clientType", clientType ); 130 | segmentation.Add( "coreVersion", GetAssemblyVersion() ); 131 | return segmentation; 132 | } 133 | 134 | private static string GetAssemblyVersion( ) 135 | { 136 | return System.Reflection.Assembly.GetCallingAssembly().GetName().Version.ToString(); 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /SpeckleCore/Conversion/ConverterUtilities.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace SpeckleCore 9 | { 10 | /// 11 | /// Utility functions. 12 | /// 13 | public partial class Converter 14 | { 15 | public static string getBase64( object obj ) 16 | { 17 | using ( System.IO.MemoryStream ms = new System.IO.MemoryStream() ) 18 | { 19 | new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter().Serialize( ms, obj ); 20 | return Convert.ToBase64String( ms.ToArray() ); 21 | } 22 | } 23 | 24 | public static byte[ ] getBytes( object obj ) 25 | { 26 | using ( System.IO.MemoryStream ms = new System.IO.MemoryStream() ) 27 | { 28 | new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter().Serialize( ms, obj ); 29 | return ms.ToArray(); 30 | } 31 | } 32 | 33 | public static object getObjFromString( string base64String ) 34 | { 35 | if ( base64String == null ) return null; 36 | byte[ ] bytes = Convert.FromBase64String( base64String ); 37 | return getObjFromBytes( bytes ); 38 | } 39 | 40 | public static object getObjFromBytes( byte[ ] bytes ) 41 | { 42 | using ( System.IO.MemoryStream ms = new System.IO.MemoryStream( bytes, 0, bytes.Length ) ) 43 | { 44 | ms.Write( bytes, 0, bytes.Length ); 45 | ms.Position = 0; 46 | return new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter().Deserialize( ms ); 47 | } 48 | } 49 | 50 | public static string bytesToBase64( byte[ ] arr ) 51 | { 52 | return Convert.ToBase64String( arr ); 53 | } 54 | 55 | public static byte[ ] base64ToBytes( string str ) 56 | { 57 | return Convert.FromBase64String( str ); 58 | } 59 | 60 | /// 61 | /// Returns a stringifed MD5 hash of a string. 62 | /// 63 | /// String from which to generate the hash 64 | /// If 0, the full hasdh will be returned, otherwise it will be trimmed to the specified lenght 65 | /// 66 | public static string getMd5Hash( string str, int length = 0 ) 67 | { 68 | using ( System.IO.MemoryStream ms = new System.IO.MemoryStream() ) 69 | { 70 | new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter().Serialize( ms, str ); 71 | byte[ ] hash; 72 | using ( System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create() ) 73 | { 74 | hash = md5.ComputeHash( ms.ToArray() ); 75 | StringBuilder sb = new StringBuilder(); 76 | foreach ( byte bbb in hash ) 77 | sb.Append( bbb.ToString( "X2" ) ); 78 | 79 | if ( length != 0 ) 80 | return sb.ToString().ToLower().Substring( 0, length ); 81 | else 82 | return sb.ToString().ToLower(); 83 | } 84 | } 85 | } 86 | 87 | // https://stackoverflow.com/a/299526/3446736 88 | private static IEnumerable GetExtensionMethods( Assembly assembly, Type extendedType, string methodName ) 89 | { 90 | var query = from type in assembly.GetTypes() 91 | where type.IsSealed && !type.IsGenericType && !type.IsNested 92 | from method in type.GetMethods( BindingFlags.Static 93 | | BindingFlags.Public | BindingFlags.NonPublic ) 94 | where method.IsDefined( typeof( System.Runtime.CompilerServices.ExtensionAttribute ), false ) 95 | where method.GetParameters()[ 0 ].ParameterType == extendedType 96 | where method.Name == methodName 97 | select method; 98 | return query; 99 | } 100 | } 101 | 102 | /// 103 | /// ref: https://stackoverflow.com/a/24087164 104 | /// 105 | public static class ListExtensions 106 | { 107 | public static List> ChunkBy( this List source, int chunkSize ) 108 | { 109 | return source 110 | .Select( ( x, i ) => new { Index = i, Value = x } ) 111 | .GroupBy( x => x.Index / chunkSize ) 112 | .Select( x => x.Select( v => v.Value ).ToList() ) 113 | .ToList(); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Speckle Contribution Guidelines 2 | ## Introduction 3 | 4 | Thank you for reading this! Speckle's a rather wide network of parts that depend on each other, either directly, indirectly or even just cosmetically. 5 | 6 | > Here's the gist: **Speckle** is an ecosystem consisting of a server, a contract and various clients. All manifestations of speckle that you see and interact with are, essentially, clients or composed of several clients. 7 | 8 | This means that what might look like a simple quick change in one repo may have a big hidden cost that propagates around other parts of the project. We're all here to help each other, and this guide is meant to help you get started and promote a framework that can untangle all these dependecies through discussion! 9 | 10 | ## Bugs & Issues 🐞 11 | 12 | Found a new bug? 13 | - First step is to check whether this is a new bug! We encourage you to search through the issues of the project in question **and** associated repos! 14 | 15 | - If you come up with nothing, open a new issue with a clear title and description, as much relevant information as possible: system configuration, code samples & steps to reproduce the problem. 16 | 17 | - Also, most importantly, do reference & note all potentially affected projects. 18 | 19 | > **Example**: https://github.com/speckleworks/SpeckleCore/issues/67 20 | Adding a project number to `SpeckleProjects`: this is a minor change in Speckle's .NET SDK. Nevertheless, this also requires an update in the SpeckleServer's models definition and, furthermore, an update of the respective schema in Speckle Specs & a rebuild of the docs. What initially looks like a 1 line addition to a c# class suddenly becomes a proper quest by itself! 21 | 22 | ## Patches For Bugs ⚠️ 23 | You fixed something! 24 | 25 | - If the bug was logged previously as an issue, do reference that issue! 26 | - If it's a previously unreported bug, do describe it and the resolution you implmemented. 27 | 28 | > Make sure though that you've covered the lateral thinking needed for a bug report, as described above, also in your implementation! 29 | 30 | ## Patches For New Features 🎉 31 | Discuss first! 32 | 33 | - Before embarking on adding a new feature, suggest it first as an issue with the `enhancement` label and/or title - this will allow relevant people to pitch in 34 | - Start writing code & submit a PR once you're moderately happy. 35 | 36 | > Many clients may potentially have overlapping scopes, some features might already be in dev somewhere else, or might have been postponed to the next major release due to api instability in that area. For example, adding a delete stream button in the accounts panel in rhino: this feature was planned for speckle admin, and the whole functionality of the accounts panel in rhino is to be greatly reduced! 37 | 38 | ## Cosmetic Patches ✨ 39 | 40 | Changes that are cosmetic in nature and do not add anything substantial to the stability or functionality of Speckle **will generally not be accepted**. 41 | 42 | Why? However trivial the changes might seem, there might be subtle reasons for the original code to be as it is. Furthermore, there are a lot of potential hidden costs (that even maintainers themselves are not aware of fully!) and they eat up review time unncessarily. 43 | 44 | > **Examples**: modifying the colour of an UI element in one client may have a big hidden cost and need propagation in several other clients that implement a similar ui element. Changing the default port or specifiying `localhost` instead of `0.0.0.0` breaks cross-vm debugging and developing. 45 | 46 | 47 | ## Wrap up 48 | Don't worry if you get things wrong. We all do, including project owners: this document should've been here a long time ago. There's plenty of room for discussion either on the [slack group](https://slacker.speckle.works) or [the forum](https://discourse.speckle.works). 49 | 50 | 🙌❤️💙💚💜🙌 51 | 52 | #### Appendix: Ecosystem quick overview 53 | 54 | **The Server** is at the core of all things Speckle, but is not necessarily the primary source of truth. It also relays client messages (which, at the moment, are loosely regulated and not necessarily bound by a contract). 55 | 56 | **That source of truth is (should be!) the SpeckleSpecs**, the OpenApi 2 specification document that describes the REST api as well as the models (schemas) speckle operates with. This is the document that all speckle clients rely on to be able to talk with the server and each other. 57 | 58 | **The clients** all talk to the server via the REST api and optionally to each other via WS messages (that the server relays, and their only bounding box is the `streamId` room). Example client apps include SpeckleRhino (Rhino and Grasshopper components), SpeckleAdmin and the SpeckleViewer. Speckle Rhino depends on SpeckleCore, the .NET Speckle SDK (which is also dependend upon by SpeckleDynamo and other future .net bound clients) and SpeckleView, the UI frontend. 59 | -------------------------------------------------------------------------------- /SpeckleCore/AssemblyCatalogue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Reflection; 6 | using System.Threading.Tasks; 7 | using System.IO; 8 | using System.Diagnostics; 9 | 10 | namespace SpeckleCore 11 | { 12 | /// 13 | /// Thanks to @radugidei for the idea: we're attempting to rip off NancyFX's guerilla assembly loading. 14 | /// See original src (MIT): 15 | /// https://raw.githubusercontent.com/NancyFx/Nancy/de458a9b42db6478e0c2bb8adef0f9fa342a2674/src/Nancy/AppDomainAssemblyCatalog.cs 16 | /// 17 | public class SpeckleKitLoader 18 | { 19 | private readonly AssemblyName SpeckleAssemblyName; 20 | private readonly Lazy> assemblies; 21 | 22 | public string SpeckleKitsDirectory; 23 | 24 | public SpeckleKitLoader( string pathToKits = null) 25 | { 26 | SpeckleKitsDirectory = pathToKits != null ? pathToKits : System.Environment.GetFolderPath( System.Environment.SpecialFolder.LocalApplicationData ) + @"\SpeckleKits\"; 27 | 28 | SpeckleAssemblyName = typeof( SpeckleObject ).GetTypeInfo().Assembly.GetName(); 29 | assemblies = new Lazy>( GetAvailableAssemblies ); 30 | } 31 | 32 | public virtual IReadOnlyCollection GetAssemblies( ) 33 | { 34 | return this.assemblies.Value; 35 | } 36 | 37 | private IReadOnlyCollection GetAvailableAssemblies( ) 38 | { 39 | var assemblies = GetLoadedSpeckleReferencingAssemblies(); 40 | 41 | var loaded = LoadSpeckleReferencingAssemblies( assemblies ); 42 | 43 | return assemblies.Union( loaded ).ToArray(); 44 | } 45 | 46 | private List GetLoadedSpeckleReferencingAssemblies( ) 47 | { 48 | var assemblies = new List(); 49 | 50 | foreach ( var assembly in AppDomain.CurrentDomain.GetAssemblies() ) 51 | { 52 | if ( !assembly.IsDynamic && !assembly.ReflectionOnly && assembly.IsReferencing( SpeckleAssemblyName ) ) 53 | { 54 | assemblies.Add( assembly ); 55 | } 56 | } 57 | 58 | return assemblies; 59 | } 60 | 61 | private IEnumerable LoadSpeckleReferencingAssemblies( IEnumerable loadedAssemblies ) 62 | { 63 | var assemblies = new HashSet(); 64 | var loadedSpeckleReferencingAssemblyNames = loadedAssemblies.Select( assembly => assembly.GetName() ).ToArray(); 65 | var directories = Directory.GetDirectories( SpeckleKitsDirectory ); 66 | var currDomain = AppDomain.CurrentDomain; 67 | 68 | 69 | foreach ( var directory in directories ) 70 | { 71 | foreach ( var assemblyPath in System.IO.Directory.EnumerateFiles( directory, "*.dll" ) ) 72 | { 73 | var unloadedAssemblyName = SafeGetAssemblyName( assemblyPath ); 74 | 75 | if ( unloadedAssemblyName == null ) 76 | { 77 | continue; 78 | } 79 | 80 | if ( !loadedSpeckleReferencingAssemblyNames.Any( loadedSpeckleReferencingAssemblyName => AssemblyName.ReferenceMatchesDefinition( loadedSpeckleReferencingAssemblyName, unloadedAssemblyName ) ) ) 81 | { 82 | //var relfectionLoadAssembly = Assembly.ReflectionOnlyLoadFrom( assemblyPath ); 83 | var relfectionLoadAssembly = Assembly.LoadFrom( assemblyPath ); 84 | var isReferencingCore = relfectionLoadAssembly.IsReferencing( SpeckleAssemblyName ); 85 | 86 | if ( isReferencingCore ) 87 | { 88 | Debug.WriteLine( "Load converter: " + unloadedAssemblyName ); 89 | var assembly = SafeLoadAssembly( AppDomain.CurrentDomain, unloadedAssemblyName ); 90 | if ( assembly != null ) 91 | { 92 | var res = assembly.GetTypes(); 93 | var copy = res; 94 | 95 | assemblies.Add( assembly ); 96 | } 97 | } 98 | } 99 | } 100 | } 101 | return assemblies.ToArray(); 102 | } 103 | 104 | private static AssemblyName SafeGetAssemblyName( string assemblyPath ) 105 | { 106 | try 107 | { 108 | return AssemblyName.GetAssemblyName( assemblyPath ); 109 | } 110 | catch 111 | { 112 | return null; 113 | } 114 | } 115 | 116 | private static Assembly SafeLoadAssembly( AppDomain domain, AssemblyName assemblyName ) 117 | { 118 | try 119 | { 120 | return domain.Load( assemblyName ); 121 | } 122 | catch 123 | { 124 | return null; 125 | } 126 | } 127 | } 128 | 129 | public static class AssemblyExtensions 130 | { 131 | /// 132 | /// Indicates if a given assembly references another which is identified by its name. 133 | /// 134 | /// The assembly which will be probed. 135 | /// The reference assembly name. 136 | /// A boolean value indicating if there is a reference. 137 | public static bool IsReferencing( this Assembly assembly, AssemblyName referenceName ) 138 | { 139 | if ( AssemblyName.ReferenceMatchesDefinition( assembly.GetName(), referenceName ) ) 140 | { 141 | return true; 142 | } 143 | 144 | foreach ( var referencedAssemblyName in assembly.GetReferencedAssemblies() ) 145 | { 146 | if ( AssemblyName.ReferenceMatchesDefinition( referencedAssemblyName, referenceName ) ) 147 | { 148 | return true; 149 | } 150 | } 151 | 152 | return false; 153 | } 154 | 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /SpeckleCore/Models/ModelObjectsExt.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | /// 10 | /// Extends the functionality of some DTO classes to be more accesible. 11 | /// So wow. Much partial. 12 | /// 13 | 14 | namespace SpeckleCore 15 | { 16 | 17 | public partial class SpeckleObject 18 | { 19 | /// 20 | /// Generates a truncated (to 12) md5 hash of an object. Set length to zero to get the full hash. 21 | /// 22 | /// 23 | public string GetMd5FromObject( object fromWhat, int length = 0 ) 24 | { 25 | if ( fromWhat == null ) 26 | { 27 | return "null"; 28 | } 29 | using ( System.IO.MemoryStream ms = new System.IO.MemoryStream() ) 30 | { 31 | new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter().Serialize( ms, fromWhat ); 32 | 33 | byte[ ] hash; 34 | using ( System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create() ) 35 | { 36 | hash = md5.ComputeHash( ms.ToArray() ); 37 | StringBuilder sb = new StringBuilder(); 38 | foreach ( byte bbb in hash ) 39 | sb.Append( bbb.ToString( "X2" ) ); 40 | 41 | if ( length != 0 ) 42 | return sb.ToString().ToLower().Substring( 0, length ); 43 | else 44 | return sb.ToString().ToLower(); 45 | } 46 | } 47 | } 48 | 49 | /// 50 | /// Recomputes the object's current hash; takes into account all values besides the hash itself, which is set to null before the calculation. 51 | /// 52 | public virtual void GenerateHash( ) 53 | { 54 | this.Hash = null; 55 | this.Hash = this.GetMd5FromObject( this ); 56 | } 57 | 58 | /// 59 | /// Use only for unit conversions. This will not affect the object hashes, thus potentially causing 60 | /// inconsistencies if used to save objects on a server. 61 | /// 62 | /// Scaling factor 63 | public virtual void Scale( double factor ) 64 | { 65 | // Implemented object type by object type, if it makes sense. 66 | } 67 | 68 | /// 69 | /// Scales any speckle objects that can be found in an Dictionary. 70 | /// 71 | /// 72 | /// 73 | /// 74 | public Dictionary ScaleProperties( Dictionary dict, double factor ) 75 | { 76 | if ( dict == null ) return null; 77 | foreach ( var kvp in dict ) 78 | { 79 | try 80 | { 81 | var scaleMethod = kvp.Value.GetType().GetMethod( "Scale" ); 82 | scaleMethod.Invoke( kvp.Value, new object[ ] { factor } ); 83 | } 84 | catch ( Exception e ) 85 | { 86 | Debug.WriteLine( "Error while scaling object." ); 87 | } 88 | } 89 | return dict; 90 | } 91 | } 92 | 93 | public partial class SpeckleInput : SpeckleObject 94 | { 95 | public SpeckleInput( ) { } 96 | 97 | [Newtonsoft.Json.JsonProperty( "name", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 98 | public string Name { get; set; } 99 | 100 | [Newtonsoft.Json.JsonProperty( "guid", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 101 | public string Guid { get; set; } 102 | 103 | [Newtonsoft.Json.JsonProperty( "value", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 104 | public double Value { get; set; } 105 | 106 | [Newtonsoft.Json.JsonProperty( "inputType", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 107 | public string InputType { get; set; } 108 | 109 | [Newtonsoft.Json.JsonProperty( "max", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 110 | public double Max { get; set; } 111 | 112 | [Newtonsoft.Json.JsonProperty( "min", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 113 | public double Min { get; set; } 114 | 115 | [Newtonsoft.Json.JsonProperty( "step", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 116 | public double Step { get; set; } 117 | 118 | public SpeckleInput( string name, float min, float max, float value, string inputType, string guid ) 119 | { 120 | this.Name = name; 121 | this.Guid = guid; 122 | this.Min = min; 123 | this.Max = max; 124 | this.Value = value; 125 | this.InputType = inputType; 126 | } 127 | } 128 | 129 | // Output parameter (price, area) 130 | public partial class SpeckleOutput : SpeckleObject 131 | { 132 | public SpeckleOutput( ) { } 133 | public string Name { get; set; } 134 | public string Guid { get; set; } 135 | public string Value { get; set; } 136 | 137 | public SpeckleOutput( string name, string value, string guid ) 138 | { 139 | this.Name = name; 140 | this.Guid = guid; 141 | this.Value = value; 142 | } 143 | } 144 | 145 | public partial class Layer : IEquatable 146 | { 147 | public Layer( ) { } 148 | 149 | public Layer( string name, string guid, string topology, int objectCount, int startIndex, int orderIndex ) 150 | { 151 | this.Name = name; 152 | this.Guid = guid; 153 | this.Topology = topology; 154 | this.StartIndex = startIndex; 155 | this.ObjectCount = objectCount; 156 | this.OrderIndex = orderIndex; 157 | } 158 | 159 | public static void DiffLayerLists( IEnumerable oldLayers, IEnumerable newLayers, ref List toRemove, ref List toAdd, ref List toUpdate ) 160 | { 161 | toRemove = oldLayers.Except( newLayers, new SpeckleLayerComparer() ).ToList(); 162 | toAdd = newLayers.Except( oldLayers, new SpeckleLayerComparer() ).ToList(); 163 | toUpdate = newLayers.Intersect( oldLayers, new SpeckleLayerComparer() ).ToList(); 164 | } 165 | 166 | public bool Equals( Layer other ) 167 | { 168 | return this.Guid == other.Guid; 169 | } 170 | } 171 | 172 | internal class SpeckleLayerComparer : IEqualityComparer 173 | { 174 | public bool Equals( Layer x, Layer y ) 175 | { 176 | return x.Guid == y.Guid; 177 | } 178 | 179 | public int GetHashCode( Layer obj ) 180 | { 181 | return obj.Guid.GetHashCode(); 182 | } 183 | } 184 | 185 | } 186 | -------------------------------------------------------------------------------- /SpeckleCore/Conversion/ConverterSerialisation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace SpeckleCore 10 | { 11 | public abstract partial class Converter 12 | { 13 | /// 14 | /// This method will convert an object to a SpeckleObject, if possible. 15 | /// 16 | /// The object. 17 | /// Null or a speckle object (SpeckleAbstract if no explicit conversion method is found). 18 | public static List Serialise(IEnumerable objectList, IEnumerable excludeAssebmlies = null) 19 | { 20 | return objectList.SelectMany(obj => 21 | { 22 | var result = Serialise(obj, excludeAssebmlies: excludeAssebmlies); 23 | if (result is IEnumerable && !(result is string)) 24 | return result as IEnumerable; 25 | else 26 | return new List() { (SpeckleObject)result }; 27 | }).ToList(); 28 | } 29 | 30 | /// 31 | /// Serialises an object to a speckle object. 32 | /// 33 | /// The object you want to serialise. 34 | /// Leave this blank, unless you really know what you're doing. 35 | /// Leave this blank, unless you really know what you're doing. 36 | /// Leave this blank, unless you really know what you're doing. 37 | /// List of speckle kits assembly names to exclude from the search. 38 | /// 39 | public static object Serialise(object source, int recursionDepth = 0, Dictionary traversed = null, string path = "", IEnumerable excludeAssebmlies = null) 40 | { 41 | if (source == null) return new SpeckleNull(); 42 | 43 | //if ( source is IEnumerable ) return Converter.Serialise( source as IEnumerable ); 44 | 45 | if (traversed == null) traversed = new Dictionary(); 46 | 47 | if (path == "") path = "root"; 48 | 49 | // check references 50 | if (traversed.ContainsKey(source.GetHashCode())) 51 | return new SpeckleAbstract() { _type = "ref", _ref = traversed[source.GetHashCode()] }; 52 | else 53 | traversed.Add(source.GetHashCode(), path); 54 | 55 | // check if it already is a speckle object 56 | if (source is SpeckleObject) 57 | return source as SpeckleObject; 58 | 59 | // check assemblies 60 | if (toSpeckleMethods.ContainsKey(source.GetType().ToString())) 61 | return toSpeckleMethods[source.GetType().ToString()].Invoke(source, new object[] { source }); 62 | 63 | var methods = new List(); 64 | var currentType = source.GetType(); 65 | var baseTypes = new List(); 66 | 67 | // create a list of base types 68 | while (currentType != null) 69 | { 70 | baseTypes.Add(currentType); 71 | currentType = currentType.BaseType; 72 | } 73 | 74 | // populate the ToSpeckle method array 75 | foreach (var ass in SpeckleCore.SpeckleInitializer.GetAssemblies().Where(ass => (excludeAssebmlies != null ? !excludeAssebmlies.Contains(ass.FullName) : true))) 76 | { 77 | foreach (var type in baseTypes) 78 | { 79 | try 80 | { 81 | methods.AddRange(Converter.GetExtensionMethods(ass, type, "ToSpeckle")); 82 | } 83 | catch { } 84 | } 85 | } 86 | 87 | // iterate through the ToSpeckle method array 88 | if (methods.Count > 0) 89 | { 90 | foreach (var method in methods) 91 | { 92 | try 93 | { 94 | var obj = method.Invoke(source, new object[] { source }); 95 | if (obj != null) 96 | { 97 | toSpeckleMethods.Add(source.GetType().ToString(), method); 98 | return obj; 99 | } 100 | } 101 | catch 102 | { 103 | return new SpeckleNull(); 104 | } 105 | } 106 | } 107 | 108 | // if we haven't returned anything by this point, we should go abstract 109 | SpeckleAbstract result = new SpeckleAbstract(); 110 | result._type = source.GetType().Name; 111 | result._assembly = source.GetType().Assembly.FullName; 112 | 113 | Dictionary dict = new Dictionary(); 114 | 115 | var properties = source.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public); 116 | 117 | foreach (var prop in properties) 118 | { 119 | if (!prop.CanWrite) 120 | continue; 121 | 122 | try 123 | { 124 | var value = prop.GetValue(source); 125 | 126 | if (value == null) 127 | continue; 128 | 129 | dict[prop.Name] = WriteValue(value, recursionDepth, traversed, path + "/" + prop.Name); 130 | } 131 | catch { } 132 | } 133 | 134 | var fields = source.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public); 135 | foreach (var field in fields) 136 | { 137 | if (field.IsNotSerialized ) 138 | continue; 139 | try 140 | { 141 | var value = field.GetValue(source); 142 | if (value == null) 143 | continue; 144 | dict[field.Name] = WriteValue(value, recursionDepth, traversed, path + "/" + field.Name); 145 | } 146 | catch { } 147 | } 148 | 149 | result.Properties = dict; 150 | result.Hash = result.GeometryHash = result.GetMd5FromObject(result.GetMd5FromObject(result._assembly) + result.GetMd5FromObject(result._type) + result.GetMd5FromObject(result.Properties)); 151 | 152 | return result; 153 | } 154 | 155 | private static object WriteValue(object myObject, int recursionDepth, Dictionary traversed = null, string path = "") 156 | { 157 | if (myObject == null || recursionDepth > 8) return null; 158 | 159 | if (myObject is Enum) return Convert.ChangeType((Enum)myObject, ((Enum)myObject).GetTypeCode()); 160 | 161 | if (myObject.GetType().IsPrimitive || myObject is string) 162 | return myObject; 163 | 164 | if (myObject is Guid) 165 | return myObject.ToString(); 166 | 167 | if (myObject is IEnumerable && !(myObject is IDictionary)) 168 | { 169 | var rlist = new List(); int index = 0; 170 | 171 | foreach (var x in (IEnumerable)myObject) 172 | { 173 | var obj = WriteValue(x, recursionDepth + 1, traversed, path + "/[" + index++ + "]"); 174 | if (obj != null) 175 | rlist.Add(obj); 176 | } 177 | return rlist; 178 | } 179 | 180 | if (myObject is IDictionary) 181 | { 182 | var myDict = myObject as IDictionary; 183 | var returnDict = new Dictionary(); 184 | foreach (DictionaryEntry x in myDict) 185 | { 186 | var y = x.Key; 187 | returnDict.Add(x.Key.ToString(), WriteValue(x.Value, recursionDepth, traversed, path + "/{" + x.Key.ToString() + "}")); 188 | } 189 | return returnDict; 190 | } 191 | 192 | if (!myObject.GetType().AssemblyQualifiedName.Contains("System")) 193 | return Converter.Serialise(myObject, recursionDepth + 1, traversed, path); 194 | 195 | return null; 196 | } 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /SpeckleCore/Conversion/JsonConverters.cs: -------------------------------------------------------------------------------- 1 | 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Linq; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace SpeckleCore 11 | { 12 | /// 13 | /// Base custom serialiser/deserialiser for Newtonsoft.Json. It implements a custom discrimantor field as well as helps 14 | /// with the properties field of the base speckle object. 15 | /// 16 | [System.CodeDom.Compiler.GeneratedCode( "NJsonSchema", "9.4.2.0" )] 17 | public class SpeckleObjectConverter : Newtonsoft.Json.JsonConverter 18 | { 19 | internal static readonly string DefaultDiscriminatorName = "discriminator"; 20 | 21 | private readonly string _discriminator; 22 | 23 | private Dictionary CachedTypes = new Dictionary(); 24 | 25 | [System.ThreadStatic] 26 | private static bool _isReading; 27 | 28 | [System.ThreadStatic] 29 | private static bool _isWriting; 30 | 31 | public SpeckleObjectConverter( ) 32 | { 33 | _discriminator = DefaultDiscriminatorName; 34 | } 35 | 36 | public SpeckleObjectConverter( string discriminator ) 37 | { 38 | _discriminator = discriminator; 39 | } 40 | 41 | public override void WriteJson( Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer ) 42 | { 43 | try 44 | { 45 | _isWriting = true; 46 | var jObject = Newtonsoft.Json.Linq.JObject.FromObject( value, serializer ); 47 | writer.WriteToken( jObject.CreateReader() ); 48 | } 49 | finally 50 | { 51 | _isWriting = false; 52 | } 53 | } 54 | 55 | public override bool CanWrite 56 | { 57 | get 58 | { 59 | if ( _isWriting ) 60 | { 61 | _isWriting = false; 62 | return false; 63 | } 64 | return true; 65 | } 66 | } 67 | 68 | public override bool CanRead 69 | { 70 | get 71 | { 72 | if ( _isReading ) 73 | { 74 | _isReading = false; 75 | return false; 76 | } 77 | return true; 78 | } 79 | } 80 | 81 | public override bool CanConvert( System.Type objectType ) 82 | { 83 | return true; 84 | } 85 | 86 | public override object ReadJson( Newtonsoft.Json.JsonReader reader, System.Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer ) 87 | { 88 | 89 | JObject jObject = null; 90 | jObject = serializer.Deserialize( reader ); 91 | 92 | if ( jObject == null ) 93 | return null; 94 | 95 | var discriminator = Newtonsoft.Json.Linq.Extensions.Value( jObject.GetValue( _discriminator ) ); 96 | var subtype = GetObjectSubtypeBetter( jObject, objectType, discriminator ); 97 | try 98 | { 99 | _isReading = true; 100 | return serializer.Deserialize( jObject.CreateReader(), subtype ); 101 | } 102 | finally 103 | { 104 | _isReading = false; 105 | } 106 | } 107 | 108 | private System.Type GetObjectSubtypeBetter( Newtonsoft.Json.Linq.JObject jObject, System.Type objectType, string discriminator ) 109 | { 110 | // TODO: Cleanup in 2.0.0, we should not have special cases where we need to prefix "Speckle" to things. 111 | // For now, we're going to stick to the following. 112 | 113 | // TODO: not elegant at all, should be broken up and recursified if needed. 114 | // This part makes sure that we deserialise to what we can - in case we get an object from a speckle kit that we do not posses, that nevertheless inherits from 115 | // an object that we do posses, we degrade to that (ie, GridLine -> Line). 116 | var pieces = discriminator.Split( '/' ).ToList(); 117 | pieces.Reverse(); 118 | foreach ( var piece in pieces ) 119 | { 120 | var subtypePiece = piece; 121 | 122 | if ( CachedTypes.ContainsKey( subtypePiece ) ) 123 | return CachedTypes[ subtypePiece ]; 124 | 125 | var secondaryType = SpeckleCore.SpeckleInitializer.GetTypes().FirstOrDefault( t => t.Name == subtypePiece ); 126 | 127 | if ( secondaryType == null ) 128 | { 129 | subtypePiece = "Speckle" + subtypePiece; 130 | secondaryType = SpeckleCore.SpeckleInitializer.GetTypes().FirstOrDefault( t => t.Name == subtypePiece ); 131 | } 132 | 133 | if ( secondaryType != null ) 134 | { 135 | if ( !CachedTypes.ContainsKey( subtypePiece ) ) 136 | CachedTypes.Add( subtypePiece, secondaryType ); 137 | return secondaryType; 138 | } 139 | } 140 | 141 | throw new System.InvalidOperationException( "Could not find subtype of '" + objectType.Name + "' with discriminator '" + discriminator + "'." ); 142 | } 143 | 144 | 145 | } 146 | 147 | /// 148 | /// Speckle Properties mixed converter. Checks if there are any embedded Speckle Objects and casts them as appropriate. 149 | /// 150 | public class SpecklePropertiesConverter : JsonConverter 151 | { 152 | public string discriminatorName = "type"; 153 | 154 | public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer ) 155 | { 156 | this.WriteObject( writer, value ); 157 | } 158 | 159 | private void WriteValue( JsonWriter writer, object value ) 160 | { 161 | if ( value != null ) 162 | { 163 | var t = JToken.FromObject( value ); 164 | switch ( t.Type ) 165 | { 166 | case JTokenType.Object: 167 | this.WriteObject( writer, value ); 168 | break; 169 | case JTokenType.Array: 170 | this.WriteArray( writer, value ); 171 | break; 172 | default: 173 | writer.WriteValue( value ); 174 | break; 175 | } 176 | } 177 | else writer.WriteValue( "null" ); 178 | } 179 | 180 | private void WriteObject( JsonWriter writer, object value ) 181 | { 182 | var obj = value as IDictionary; 183 | if ( obj != null ) 184 | { 185 | writer.WriteStartObject(); 186 | foreach ( var kvp in obj ) 187 | { 188 | if( kvp.Value == null ) continue; 189 | writer.WritePropertyName( kvp.Key ); 190 | this.WriteValue( writer, kvp.Value ); 191 | } 192 | writer.WriteEndObject(); 193 | } 194 | else 195 | { 196 | if ( value is IntPtr ) 197 | { 198 | // DO NOTHING 199 | } 200 | else 201 | writer.WriteRawValue( ( ( SpeckleObject ) value ).ToJson() ); 202 | } 203 | } 204 | 205 | private void WriteArray( JsonWriter writer, object value ) 206 | { 207 | writer.WriteStartArray(); 208 | 209 | var array = value as System.Collections.IEnumerable; 210 | foreach ( var o in array ) 211 | this.WriteValue( writer, o ); 212 | writer.WriteEndArray(); 213 | } 214 | 215 | public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer ) 216 | { 217 | var dict = new Dictionary(); 218 | 219 | JObject jObj = serializer.Deserialize( reader ); 220 | 221 | return parseObject( jObj ); 222 | } 223 | 224 | private object parseObject( JObject obj ) 225 | { 226 | if ( obj == null ) 227 | return null; 228 | var dict = new Dictionary(); 229 | bool isSpeckleObject = false; 230 | string type = ""; 231 | 232 | try 233 | { 234 | var jObject = obj[ discriminatorName ]; 235 | if ( jObject != null ) 236 | { 237 | type = obj[ discriminatorName ].Value(); 238 | isSpeckleObject = true; 239 | } 240 | } 241 | catch 242 | { 243 | isSpeckleObject = false; 244 | } 245 | 246 | if ( !isSpeckleObject ) 247 | { 248 | foreach ( var sub in obj ) 249 | dict[ sub.Key ] = getValue( sub.Value ); 250 | return dict; 251 | } 252 | else 253 | return JsonConvert.DeserializeObject( JsonConvert.SerializeObject( obj ) ); 254 | } 255 | 256 | private object getValue( JToken myToken ) 257 | { 258 | switch ( myToken.Type ) 259 | { 260 | case JTokenType.Object: 261 | var myobj = myToken.Value(); 262 | return parseObject( myobj ); 263 | case JTokenType.Boolean: 264 | return myToken.ToObject( typeof( bool ) ); 265 | case JTokenType.Float: 266 | case JTokenType.Integer: 267 | return myToken.ToObject( typeof( double ) ); 268 | case JTokenType.String: 269 | return myToken.ToObject( typeof( string ) ); 270 | case JTokenType.Array: 271 | List arr = ( List ) myToken.ToObject( typeof( List ) ); 272 | for ( int i = 0; i < arr.Count; i++ ) 273 | { 274 | if ( arr[ i ] is JObject ) arr[ i ] = parseObject( arr[ i ] as JObject ); 275 | } 276 | return arr; 277 | default: 278 | return "Problem deserialising."; 279 | } 280 | } 281 | 282 | public override bool CanConvert( Type objectType ) { return typeof( IDictionary ).IsAssignableFrom( objectType ); } 283 | } 284 | 285 | /// 286 | /// No clue what this robocode does. 287 | /// 288 | [System.CodeDom.Compiler.GeneratedCode( "NJsonSchema", "9.4.2.0" )] 289 | [System.AttributeUsage( System.AttributeTargets.Class, AllowMultiple = true )] 290 | internal class JsonInheritanceAttribute : System.Attribute 291 | { 292 | public JsonInheritanceAttribute( string key, System.Type type ) 293 | { 294 | Key = key; 295 | Type = type; 296 | } 297 | 298 | public string Key { get; } 299 | 300 | public System.Type Type { get; } 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /SpeckleCore/Models/ModelResponses.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | 6 | namespace SpeckleCore 7 | { 8 | [System.CodeDom.Compiler.GeneratedCode( "NJsonSchema", "9.10.41.0 (Newtonsoft.Json v9.0.0.0)" )] 9 | [Serializable] 10 | public partial class ResponseBase 11 | { 12 | /// Besides the http status code, this tells you whether the call succeeded or not. 13 | [Newtonsoft.Json.JsonProperty( "success", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 14 | public bool? Success { get; set; } 15 | 16 | /// Either an error or a confirmation. 17 | [Newtonsoft.Json.JsonProperty( "message", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 18 | public string Message { get; set; } 19 | 20 | /// Returned resource (if querying by id) 21 | [Newtonsoft.Json.JsonProperty( "resource", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 22 | public object Resource { get; set; } 23 | 24 | /// Returned resources array (if it's a bulk query) 25 | [Newtonsoft.Json.JsonProperty( "resources", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 26 | public List Resources { get; set; } 27 | 28 | public string ToJson( ) 29 | { 30 | return Newtonsoft.Json.JsonConvert.SerializeObject( this ); 31 | } 32 | 33 | public static ResponseBase FromJson( string data ) 34 | { 35 | return Newtonsoft.Json.JsonConvert.DeserializeObject( data ); 36 | } 37 | 38 | } 39 | 40 | [System.CodeDom.Compiler.GeneratedCode( "NJsonSchema", "9.10.41.0 (Newtonsoft.Json v9.0.0.0)" )] 41 | [Serializable] 42 | public partial class ResponseUser : ResponseBase 43 | { 44 | [Newtonsoft.Json.JsonProperty( "resource", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 45 | public User Resource { get; set; } 46 | 47 | [Newtonsoft.Json.JsonProperty( "resources", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 48 | public List Resources { get; set; } 49 | 50 | public string ToJson( ) 51 | { 52 | return Newtonsoft.Json.JsonConvert.SerializeObject( this ); 53 | } 54 | 55 | public static ResponseUser FromJson( string data ) 56 | { 57 | return Newtonsoft.Json.JsonConvert.DeserializeObject( data ); 58 | } 59 | 60 | } 61 | 62 | [System.CodeDom.Compiler.GeneratedCode( "NJsonSchema", "9.10.41.0 (Newtonsoft.Json v9.0.0.0)" )] 63 | [Serializable] 64 | public partial class ResponseClient : ResponseBase 65 | { 66 | [Newtonsoft.Json.JsonProperty( "resource", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 67 | public AppClient Resource { get; set; } 68 | 69 | [Newtonsoft.Json.JsonProperty( "resources", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 70 | public List Resources { get; set; } 71 | 72 | public string ToJson( ) 73 | { 74 | return Newtonsoft.Json.JsonConvert.SerializeObject( this ); 75 | } 76 | 77 | public static ResponseClient FromJson( string data ) 78 | { 79 | return Newtonsoft.Json.JsonConvert.DeserializeObject( data ); 80 | } 81 | 82 | } 83 | 84 | [System.CodeDom.Compiler.GeneratedCode( "NJsonSchema", "9.10.41.0 (Newtonsoft.Json v9.0.0.0)" )] 85 | [Serializable] 86 | public partial class ResponseProject : ResponseBase 87 | { 88 | [Newtonsoft.Json.JsonProperty( "resource", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 89 | public Project Resource { get; set; } 90 | 91 | [Newtonsoft.Json.JsonProperty( "resources", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 92 | public List Resources { get; set; } 93 | 94 | public string ToJson( ) 95 | { 96 | return Newtonsoft.Json.JsonConvert.SerializeObject( this ); 97 | } 98 | 99 | public static ResponseProject FromJson( string data ) 100 | { 101 | return Newtonsoft.Json.JsonConvert.DeserializeObject( data ); 102 | } 103 | 104 | } 105 | 106 | [System.CodeDom.Compiler.GeneratedCode( "NJsonSchema", "9.10.41.0 (Newtonsoft.Json v9.0.0.0)" )] 107 | [Serializable] 108 | public partial class ResponseComment : ResponseBase 109 | { 110 | [Newtonsoft.Json.JsonProperty( "resource", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 111 | public Comment Resource { get; set; } 112 | 113 | [Newtonsoft.Json.JsonProperty( "resources", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 114 | public List Resources { get; set; } 115 | 116 | public string ToJson( ) 117 | { 118 | return Newtonsoft.Json.JsonConvert.SerializeObject( this ); 119 | } 120 | 121 | public static ResponseComment FromJson( string data ) 122 | { 123 | return Newtonsoft.Json.JsonConvert.DeserializeObject( data ); 124 | } 125 | 126 | } 127 | 128 | [System.CodeDom.Compiler.GeneratedCode( "NJsonSchema", "9.10.41.0 (Newtonsoft.Json v9.0.0.0)" )] 129 | [Serializable] 130 | public partial class ResponseStream : ResponseBase 131 | { 132 | [Newtonsoft.Json.JsonProperty( "resource", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 133 | public SpeckleStream Resource { get; set; } 134 | 135 | [Newtonsoft.Json.JsonProperty( "resources", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 136 | public List Resources { get; set; } 137 | 138 | public string ToJson( ) 139 | { 140 | return Newtonsoft.Json.JsonConvert.SerializeObject( this ); 141 | } 142 | 143 | public static ResponseStream FromJson( string data ) 144 | { 145 | return Newtonsoft.Json.JsonConvert.DeserializeObject( data ); 146 | } 147 | 148 | } 149 | 150 | [System.CodeDom.Compiler.GeneratedCode( "NJsonSchema", "9.10.41.0 (Newtonsoft.Json v9.0.0.0)" )] 151 | [Serializable] 152 | public partial class ResponseObject : ResponseBase 153 | { 154 | [Newtonsoft.Json.JsonProperty( "resource", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 155 | public SpeckleObject Resource { get; set; } 156 | 157 | [Newtonsoft.Json.JsonProperty( "resources", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 158 | public List Resources { get; set; } 159 | 160 | public string ToJson( ) 161 | { 162 | return Newtonsoft.Json.JsonConvert.SerializeObject( this ); 163 | } 164 | 165 | public static ResponseObject FromJson( string data ) 166 | { 167 | return Newtonsoft.Json.JsonConvert.DeserializeObject( data ); 168 | } 169 | 170 | } 171 | 172 | [System.CodeDom.Compiler.GeneratedCode( "NJsonSchema", "9.10.41.0 (Newtonsoft.Json v9.0.0.0)" )] 173 | [Serializable] 174 | public partial class ResponseStreamClone : ResponseBase 175 | { 176 | [Newtonsoft.Json.JsonProperty( "clone", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 177 | public SpeckleStream Clone { get; set; } 178 | 179 | [Newtonsoft.Json.JsonProperty( "parent", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 180 | public SpeckleStream Parent { get; set; } 181 | 182 | public string ToJson( ) 183 | { 184 | return Newtonsoft.Json.JsonConvert.SerializeObject( this ); 185 | } 186 | 187 | public static ResponseStreamClone FromJson( string data ) 188 | { 189 | return Newtonsoft.Json.JsonConvert.DeserializeObject( data ); 190 | } 191 | 192 | } 193 | 194 | [System.CodeDom.Compiler.GeneratedCode( "NJsonSchema", "9.10.41.0 (Newtonsoft.Json v9.0.0.0)" )] 195 | [Serializable] 196 | public partial class ResponseStreamDiff : ResponseBase 197 | { 198 | [Newtonsoft.Json.JsonProperty( "objects", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 199 | public DiffObjectsResult Objects { get; set; } 200 | 201 | [Newtonsoft.Json.JsonProperty( "layers", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 202 | public DiffLayersResult Layers { get; set; } 203 | 204 | public string ToJson( ) 205 | { 206 | return Newtonsoft.Json.JsonConvert.SerializeObject( this ); 207 | } 208 | 209 | public static ResponseStreamDiff FromJson( string data ) 210 | { 211 | return Newtonsoft.Json.JsonConvert.DeserializeObject( data ); 212 | } 213 | 214 | } 215 | 216 | [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "9.10.41.0 (Newtonsoft.Json v9.0.0.0)")] 217 | [Serializable] 218 | public partial class ResponseStreamDeltaDiff : ResponseBase 219 | { 220 | [Newtonsoft.Json.JsonProperty("delta", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] 221 | public SpeckleDelta Delta { get; set; } 222 | 223 | public string ToJson() 224 | { 225 | return Newtonsoft.Json.JsonConvert.SerializeObject(this); 226 | } 227 | 228 | public static ResponseStreamDeltaDiff FromJson(string data) 229 | { 230 | return Newtonsoft.Json.JsonConvert.DeserializeObject(data); 231 | } 232 | 233 | } 234 | 235 | /// The resource type you want to comment on. 236 | [System.CodeDom.Compiler.GeneratedCode( "NJsonSchema", "9.10.41.0 (Newtonsoft.Json v9.0.0.0)" )] 237 | [Serializable] 238 | public enum ResourceType 239 | { 240 | [System.Runtime.Serialization.EnumMember( Value = "stream" )] 241 | Stream = 0, 242 | 243 | [System.Runtime.Serialization.EnumMember( Value = "object" )] 244 | Object = 1, 245 | 246 | [System.Runtime.Serialization.EnumMember( Value = "project" )] 247 | Project = 2, 248 | 249 | [System.Runtime.Serialization.EnumMember( Value = "comment" )] 250 | Comment = 3, 251 | 252 | } 253 | 254 | /// it's a timestamp for each login. 255 | [System.CodeDom.Compiler.GeneratedCode( "NJsonSchema", "9.10.41.0 (Newtonsoft.Json v9.0.0.0)" )] 256 | [Serializable] 257 | public partial class LoginDateProperty 258 | { 259 | [Newtonsoft.Json.JsonProperty( "date", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 260 | public string Date { get; set; } 261 | 262 | public string ToJson( ) 263 | { 264 | return Newtonsoft.Json.JsonConvert.SerializeObject( this ); 265 | } 266 | 267 | public static LoginDateProperty FromJson( string data ) 268 | { 269 | return Newtonsoft.Json.JsonConvert.DeserializeObject( data ); 270 | } 271 | 272 | } 273 | 274 | [System.CodeDom.Compiler.GeneratedCode( "NJsonSchema", "9.10.41.0 (Newtonsoft.Json v9.0.0.0)" )] 275 | [Serializable] 276 | public partial class Resource 277 | { 278 | [Newtonsoft.Json.JsonProperty( "resourceType", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 279 | public string ResourceType { get; set; } 280 | 281 | [Newtonsoft.Json.JsonProperty( "resourceId", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 282 | public string ResourceId { get; set; } 283 | 284 | public string ToJson( ) 285 | { 286 | return Newtonsoft.Json.JsonConvert.SerializeObject( this ); 287 | } 288 | 289 | public static Resource FromJson( string data ) 290 | { 291 | return Newtonsoft.Json.JsonConvert.DeserializeObject( data ); 292 | } 293 | 294 | } 295 | 296 | [System.CodeDom.Compiler.GeneratedCode( "NJsonSchema", "9.10.41.0 (Newtonsoft.Json v9.0.0.0)" )] 297 | [Serializable] 298 | public partial class SpeckleBaseColor 299 | { 300 | /// alpha value 301 | [Newtonsoft.Json.JsonProperty( "a", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 302 | public double? A { get; set; } 303 | 304 | /// hex color value 305 | [Newtonsoft.Json.JsonProperty( "hex", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 306 | public string Hex { get; set; } 307 | 308 | public string ToJson( ) 309 | { 310 | return Newtonsoft.Json.JsonConvert.SerializeObject( this ); 311 | } 312 | 313 | public static SpeckleBaseColor FromJson( string data ) 314 | { 315 | return Newtonsoft.Json.JsonConvert.DeserializeObject( data ); 316 | } 317 | 318 | } 319 | 320 | [System.CodeDom.Compiler.GeneratedCode( "NJsonSchema", "9.10.41.0 (Newtonsoft.Json v9.0.0.0)" )] 321 | [Serializable] 322 | public partial class DiffObjectsResult 323 | { 324 | [Newtonsoft.Json.JsonProperty( "common", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 325 | public List Common { get; set; } 326 | 327 | [Newtonsoft.Json.JsonProperty( "inA", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 328 | public List InA { get; set; } 329 | 330 | [Newtonsoft.Json.JsonProperty( "inB", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 331 | public List InB { get; set; } 332 | 333 | public string ToJson( ) 334 | { 335 | return Newtonsoft.Json.JsonConvert.SerializeObject( this ); 336 | } 337 | 338 | public static DiffObjectsResult FromJson( string data ) 339 | { 340 | return Newtonsoft.Json.JsonConvert.DeserializeObject( data ); 341 | } 342 | 343 | } 344 | 345 | 346 | [System.CodeDom.Compiler.GeneratedCode( "NJsonSchema", "9.10.41.0 (Newtonsoft.Json v9.0.0.0)" )] 347 | [Serializable] 348 | public partial class DiffLayersResult 349 | { 350 | [Newtonsoft.Json.JsonProperty( "common", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 351 | public List Common { get; set; } 352 | 353 | [Newtonsoft.Json.JsonProperty( "inA", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 354 | public List InA { get; set; } 355 | 356 | [Newtonsoft.Json.JsonProperty( "inB", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 357 | public List InB { get; set; } 358 | 359 | public string ToJson( ) 360 | { 361 | return Newtonsoft.Json.JsonConvert.SerializeObject( this ); 362 | } 363 | 364 | public static DiffLayersResult FromJson( string data ) 365 | { 366 | return Newtonsoft.Json.JsonConvert.DeserializeObject( data ); 367 | } 368 | 369 | } 370 | 371 | [System.CodeDom.Compiler.GeneratedCode( "NSwag", "11.16.1.0 (NJsonSchema v9.10.41.0 (Newtonsoft.Json v9.0.0.0))" )] 372 | [Serializable] 373 | public partial class SpeckleException : System.Exception 374 | { 375 | public int StatusCode { get; private set; } 376 | 377 | public string Response { get; private set; } 378 | 379 | public System.Collections.Generic.Dictionary> Headers { get; private set; } 380 | 381 | public SpeckleException( string message, int statusCode, string response, System.Collections.Generic.Dictionary> headers, System.Exception innerException ) 382 | : base( message, innerException ) 383 | { 384 | StatusCode = statusCode; 385 | Response = response; 386 | Headers = headers; 387 | } 388 | 389 | public string ToString( ) 390 | { 391 | return string.Format( "HTTP Response: \n\n{0}\n\n{1}", Response, base.ToString() ); 392 | } 393 | } 394 | 395 | [System.CodeDom.Compiler.GeneratedCode( "NSwag", "11.16.1.0 (NJsonSchema v9.10.41.0 (Newtonsoft.Json v9.0.0.0))" )] 396 | [Serializable] 397 | public partial class SpeckleException : SpeckleException 398 | { 399 | public TResult Result { get; private set; } 400 | 401 | public SpeckleException( string message, int statusCode, string response, System.Collections.Generic.Dictionary> headers, TResult result, System.Exception innerException ) 402 | : base( message, statusCode, response, headers, innerException ) 403 | { 404 | Result = result; 405 | } 406 | } 407 | } 408 | -------------------------------------------------------------------------------- /SpeckleCore/ApiClient/SpeckleApiClientExtension.cs: -------------------------------------------------------------------------------- 1 | 2 | using Newtonsoft.Json; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.Linq; 7 | using System.Net; 8 | using System.Runtime.Serialization; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using System.Timers; 12 | using WebSocketSharp; 13 | 14 | namespace SpeckleCore 15 | { 16 | [Serializable] 17 | public enum ClientRole { Sender, Receiver, Mixed }; 18 | 19 | public class SpeckleEventArgs : EventArgs 20 | { 21 | public string EventName { get; set; } 22 | public string EventData { get; set; } 23 | public dynamic EventObject { get; set; } 24 | } 25 | 26 | public delegate void SpeckleEvent( object source, SpeckleEventArgs e ); 27 | 28 | [Serializable] 29 | public partial class SpeckleApiClient : ISerializable 30 | { 31 | public bool UseGzip { get; set; } = true; 32 | public string BaseUrl { get; set; } 33 | public string AuthToken { get; set; } 34 | 35 | public string StreamId { get; set; } 36 | public string ClientId { get; set; } 37 | 38 | public User User { get; private set; } 39 | public SpeckleStream Stream { get; set; } 40 | 41 | public ClientRole? Role { get; private set; } = null; 42 | 43 | public bool IsAuthorized { get; private set; } = false; 44 | public bool IsPersistent { get; private set; } = false; 45 | 46 | WebSocket WebsocketClient; 47 | public bool WsConnected { get; private set; } = false; 48 | public bool IsDisposed { get; private set; } = false; 49 | public bool IsConnected { get; private set; } = false; 50 | 51 | public event SpeckleEvent OnError; 52 | public event SpeckleEvent OnReady; 53 | public event SpeckleEvent OnWsMessage; 54 | public event SpeckleEvent OnLogData; 55 | 56 | Timer IsReady, WsReconnecter; 57 | 58 | //Default timeouts, pending further discussion 59 | private double defaultTimeoutMilliseconds = 3000; 60 | private double defaultBulkTimeoutMilliseconds = 60000; 61 | 62 | private Dictionary ObjectCache = new Dictionary(); 63 | 64 | // stores the client type: Grasshopper, Revit, etc. 65 | public string ClientType { get; set; } 66 | 67 | public SpeckleApiClient( string baseUrl, bool isPersistent = false, string documentType = "unknown" ) 68 | { 69 | ClientType = documentType; 70 | 71 | SetSerialisationSettings(); 72 | 73 | BaseUrl = baseUrl; 74 | IsPersistent = isPersistent; 75 | 76 | SetReadyTimer(); 77 | } 78 | 79 | public SpeckleApiClient( bool useGzip = true, string documentType = "unknown" ) 80 | { 81 | ClientType = documentType; 82 | SetSerialisationSettings(); 83 | UseGzip = useGzip; 84 | } 85 | 86 | public SpeckleApiClient( ) 87 | { 88 | SetSerialisationSettings(); 89 | } 90 | 91 | private void SetSerialisationSettings( ) 92 | { 93 | _settings = new System.Lazy( ( ) => 94 | { 95 | var settings = new Newtonsoft.Json.JsonSerializerSettings() 96 | { 97 | ContractResolver = new Newtonsoft.Json.Serialization.DefaultContractResolver() { NamingStrategy = new Newtonsoft.Json.Serialization.CamelCaseNamingStrategy() }, 98 | ReferenceLoopHandling = ReferenceLoopHandling.Ignore, 99 | NullValueHandling = NullValueHandling.Ignore 100 | }; 101 | UpdateJsonSerializerSettings( settings ); 102 | return settings; 103 | } ); 104 | } 105 | 106 | /// 107 | /// Initialises this client as a receiver for a specific stream. 108 | /// 109 | /// 110 | /// 111 | /// 112 | /// 113 | /// 114 | /// 115 | public async Task IntializeReceiver( string streamId, string documentName, string documentType, string documentGuid, string authToken = null ) 116 | { 117 | ClientType = documentType; 118 | 119 | if ( Role != null ) 120 | throw new Exception( "Role changes are not permitted. Maybe create a new client?" ); 121 | 122 | Role = ClientRole.Receiver; 123 | StreamId = streamId; 124 | 125 | try 126 | { 127 | AuthToken = authToken; 128 | User = ( await this.UserGetAsync() ).Resource; 129 | 130 | } 131 | catch ( SpeckleException e ) 132 | { 133 | OnError?.Invoke( this, new SpeckleEventArgs() { EventName = e.StatusCode.ToString(), EventData = e.Message } ); 134 | } 135 | 136 | try 137 | { 138 | Stream = ( await this.StreamGetAsync( streamId, null ) ).Resource; 139 | await SetupClient( documentName, documentType, documentGuid ); 140 | SetupWebsocket(); 141 | } 142 | catch ( SpeckleException e ) 143 | { 144 | OnError?.Invoke( this, new SpeckleEventArgs() { EventName = e.StatusCode.ToString(), EventData = e.Message } ); 145 | } 146 | 147 | } 148 | /// 149 | /// Initialises this client as a Sender by creating a new stream. 150 | /// 151 | /// 152 | /// 153 | /// 154 | /// 155 | /// 156 | public async Task IntializeSender( string authToken, string documentName, string documentType, string documentGuid ) 157 | { 158 | ClientType = documentType; 159 | 160 | if ( Role != null ) 161 | throw new Exception( "Role changes are not permitted. Maybe create a new client?" ); 162 | 163 | Role = ClientRole.Sender; 164 | 165 | try 166 | { 167 | AuthToken = authToken; 168 | User = ( await this.UserGetAsync() ).Resource; 169 | } 170 | catch ( SpeckleException e ) 171 | { 172 | OnError?.Invoke( this, new SpeckleEventArgs() { EventName = "error", EventData = "Could not log in: " + e.Message } ); 173 | return null; 174 | } 175 | 176 | try 177 | { 178 | Stream = ( await this.StreamCreateAsync( new SpeckleStream() ) ).Resource; 179 | StreamId = Stream.StreamId; 180 | 181 | await SetupClient( documentName, documentType, documentGuid ); 182 | SetupWebsocket(); 183 | 184 | return Stream.StreamId; 185 | } 186 | catch ( SpeckleException e ) 187 | { 188 | OnError?.Invoke( this, new SpeckleEventArgs() { EventName = e.StatusCode.ToString(), EventData = e.Message } ); 189 | return null; 190 | } 191 | 192 | } 193 | 194 | private async Task SetupClient( string documentName = null, string documentType = null, string documentGuid = null ) 195 | { 196 | if ( ClientId == null ) 197 | { 198 | LogEvent( "Creating a new client." ); 199 | var myClient = new AppClient() { StreamId = StreamId, Role = Role.ToString(), Online = true, DocumentGuid = documentGuid, DocumentName = documentName, DocumentType = documentType }; 200 | 201 | ClientId = ( await this.ClientCreateAsync( myClient ) ).Resource._id; 202 | } 203 | else 204 | { 205 | LogEvent( "Setting client to alive." ); 206 | await ClientUpdateAsync( ClientId, new AppClient() { Online = true } ); 207 | } 208 | } 209 | 210 | private void LogEvent( string what ) 211 | { 212 | OnLogData?.Invoke( this, new SpeckleEventArgs() { EventData = what } ); 213 | } 214 | 215 | private void SetReadyTimer( ) 216 | { 217 | IsReady = new Timer( 200 ) { AutoReset = false, Enabled = true }; 218 | IsReady.Elapsed += ( sender, e ) => 219 | { 220 | LogEvent( "Checking readiness..." ); 221 | if ( StreamId == null || ClientId == null || WsConnected == false ) { IsReady.Start(); return; } 222 | OnReady?.Invoke( this, new SpeckleEventArgs() { EventName = "client-ready" } ); 223 | IsConnected = true; 224 | LogEvent( "Client is ready!" ); 225 | }; 226 | } 227 | 228 | private void SetWsReconnectTimer( ) 229 | { 230 | WsReconnecter = new Timer( 1000 ) { AutoReset = false, Enabled = false }; 231 | WsReconnecter.Elapsed += ( sender, e ) => 232 | { 233 | if ( IsDisposed ) return; 234 | WebsocketClient.Connect(); 235 | }; 236 | } 237 | 238 | /// 239 | /// Sets up the websocket client & its events.. 240 | /// 241 | public void SetupWebsocket( ) 242 | { 243 | SetWsReconnectTimer(); 244 | 245 | //generates a random guid 246 | if ( ClientId == null ) 247 | ClientId = Guid.NewGuid().ToString(); 248 | 249 | WebsocketClient = new WebSocket( BaseUrl.Replace( "http", "ws" ) + "?access_token=" + AuthToken + "&stream_id=" + StreamId + "&client_id=" + ClientId ); 250 | 251 | WebsocketClient.OnOpen += ( sender, e ) => 252 | { 253 | WsConnected = true; 254 | WsReconnecter.Stop(); 255 | }; 256 | 257 | WebsocketClient.OnClose += ( sender, e ) => 258 | { 259 | WsConnected = false; 260 | WsReconnecter.Start(); 261 | OnError?.Invoke( this, new SpeckleEventArgs() { EventName = "websocket-disconnected" } ); 262 | }; 263 | 264 | WebsocketClient.OnMessage += ( sender, e ) => 265 | { 266 | if ( e.Data == "ping" ) { WebsocketClient.Send( "alive" ); LogEvent( "Got a ws ping." ); return; } 267 | 268 | LogEvent( "Got a ws message." ); 269 | try 270 | { 271 | OnWsMessage?.Invoke( this, new SpeckleEventArgs() { EventName = "websocket-message", EventObject = JsonConvert.DeserializeObject( e.Data ), EventData = e.Data } ); 272 | } 273 | catch 274 | { 275 | OnWsMessage?.Invoke( this, new SpeckleEventArgs() 276 | { 277 | EventName = "websocket-message-unparsed", 278 | EventData = e.Data 279 | } ); 280 | } 281 | }; 282 | 283 | WebsocketClient.Connect(); 284 | } 285 | 286 | /// 287 | /// Sends a direct message to another websocket client. 288 | /// 289 | /// The clientId of the socket you want to send the message to. 290 | /// What you want to send. Make it serialisable and small. 291 | public void SendMessage( string receipientId, dynamic args ) 292 | { 293 | if ( !WsConnected ) 294 | { 295 | OnError?.Invoke( this, new SpeckleEventArgs() { EventName = "Websocket client not connected.", EventData = "Websocket client not connected." } ); 296 | return; 297 | } 298 | 299 | var eventData = new 300 | { 301 | eventName = "message", 302 | senderId = ClientId, 303 | recipientId = receipientId, 304 | streamId = StreamId, 305 | args = args 306 | }; 307 | 308 | WebsocketClient.Send( JsonConvert.SerializeObject( eventData ) ); 309 | } 310 | 311 | /// 312 | /// Broadcasts a message in a specific websocket room, as defined by resourceType and resourceId. 313 | /// 314 | /// Can be stream, object, project, comment, user. 315 | /// The database id of the resource. 316 | /// The message. Make it serialisable and small. 317 | public void BroadcastMessage( string resourceType, string resourceId, dynamic args ) 318 | { 319 | if ( !WsConnected ) 320 | { 321 | OnError?.Invoke( this, new SpeckleEventArgs() { EventName = "Websocket client not connected.", EventData = "Websocket client not connected." } ); 322 | return; 323 | } 324 | 325 | var eventData = new 326 | { 327 | eventName = "broadcast", 328 | senderId = ClientId, 329 | resourceType = resourceType, 330 | resourceId = resourceId, 331 | args = args 332 | }; 333 | 334 | WebsocketClient.Send( JsonConvert.SerializeObject( eventData ) ); 335 | } 336 | 337 | /// 338 | /// Joins a websocket room based a resource type and its id. This will subscribe you to any broadcasts in that room. 339 | /// 340 | /// Can be stream, object, project, comment, user. 341 | /// The database id of the resource. 342 | public void JoinRoom( string resourceType, string resourceId ) 343 | { 344 | if ( !WsConnected ) 345 | { 346 | OnError?.Invoke( this, new SpeckleEventArgs() { EventName = "Websocket client not connected.", EventData = "Websocket client not connected." } ); 347 | return; 348 | } 349 | 350 | var eventData = new 351 | { 352 | eventName = "join", 353 | senderId = ClientId, 354 | resourceType = resourceType, 355 | resourceId = resourceId 356 | }; 357 | 358 | WebsocketClient.Send( JsonConvert.SerializeObject( eventData ) ); 359 | } 360 | 361 | /// 362 | /// Leaves a websocket room based a resource type and its id. This will stop you from receiving any broadcasts in that room. 363 | /// 364 | /// Can be stream, object, project, comment, user. 365 | /// The database id of the resource. 366 | public void LeaveRoom( string resourceType, string resourceId ) 367 | { 368 | if ( !WsConnected ) 369 | { 370 | OnError?.Invoke( this, new SpeckleEventArgs() { EventName = "Websocket client not connected.", EventData = "Websocket client not connected." } ); 371 | return; 372 | } 373 | 374 | var eventData = new 375 | { 376 | eventName = "leave", 377 | resourceType = resourceType, 378 | resourceId = resourceId 379 | }; 380 | 381 | WebsocketClient.Send( JsonConvert.SerializeObject( eventData ) ); 382 | } 383 | 384 | public void LogError( SpeckleException err ) 385 | { 386 | OnError?.Invoke( this, new SpeckleEventArgs() { EventName = err.StatusCode.ToString(), EventData = err.Message, EventObject = err } ); 387 | } 388 | 389 | protected SpeckleApiClient( SerializationInfo info, StreamingContext context ) 390 | { 391 | _settings = new System.Lazy( ( ) => 392 | { 393 | var settings = new Newtonsoft.Json.JsonSerializerSettings() 394 | { 395 | ContractResolver = new Newtonsoft.Json.Serialization.DefaultContractResolver() { NamingStrategy = new Newtonsoft.Json.Serialization.CamelCaseNamingStrategy() }, 396 | ReferenceLoopHandling = ReferenceLoopHandling.Ignore 397 | }; 398 | UpdateJsonSerializerSettings( settings ); 399 | return settings; 400 | } ); 401 | 402 | UseGzip = true; 403 | 404 | BaseUrl = info.GetString( "BaseUrl" ); 405 | StreamId = info.GetString( "StreamId" ); 406 | Role = ( ClientRole ) info.GetInt32( "Role" ); 407 | ClientId = info.GetString( "ClientId" ); 408 | 409 | try 410 | { 411 | ClientType = info.GetString( "ClientType" ); 412 | } 413 | catch ( Exception e ) 414 | { 415 | // Meep, no client type present. old client. 416 | // NOTE: the end clients (rhino, grasshopper, dynamo, etc) should attempt to set this value again on their re-initialisation. 417 | // This is so that we can actually get types out for pre-existing clients that didn't save the ClientType property. 418 | ClientType = "undefined"; 419 | } 420 | 421 | // old clients will not have a user email field :/ 422 | try 423 | { 424 | var userEmail = info.GetString( "UserEmail" ); 425 | var acc = LocalContext.GetAccountByEmailAndRestApi( userEmail, BaseUrl ); 426 | if ( acc != null ) 427 | { 428 | AuthToken = acc.Token; 429 | User = new User() { Email = acc.Email }; 430 | } 431 | else 432 | { 433 | throw new Exception( "You do not have an account that matches this stream's server." ); 434 | } 435 | } 436 | catch 437 | { 438 | var accs = LocalContext.GetAccountsByRestApi( BaseUrl ); 439 | var sorted = accs.OrderByDescending( acc => acc.IsDefault ).ToList(); 440 | if ( sorted.Count == 0 ) 441 | { 442 | throw new Exception( "You do not have an account that matches this stream's server." ); 443 | } 444 | else 445 | { 446 | AuthToken = accs[ 0 ].Token; 447 | User = new User() { Email = sorted[ 0 ].Email }; 448 | } 449 | } 450 | 451 | Stream = StreamGetAsync( StreamId, null ).Result.Resource; 452 | 453 | // does not need waiting for, as we already have a clientid. 454 | SetupClient(); 455 | SetupWebsocket(); 456 | 457 | SetReadyTimer(); 458 | SetWsReconnectTimer(); 459 | } 460 | 461 | public void GetObjectData( SerializationInfo info, StreamingContext context ) 462 | { 463 | info.AddValue( "UserEmail", User?.Email ); 464 | info.AddValue( "BaseUrl", BaseUrl ); 465 | info.AddValue( "StreamId", StreamId ); 466 | info.AddValue( "Role", Role ); 467 | info.AddValue( "ClientId", ClientId ); 468 | info.AddValue( "ClientType", ClientType ); 469 | } 470 | 471 | public void Dispose( bool delete = false ) 472 | { 473 | IsDisposed = true; 474 | 475 | if ( !delete ) 476 | { 477 | ClientUpdateAsync( ClientId, new AppClient() { Online = false } ); 478 | WebsocketClient?.Close(); 479 | return; 480 | } 481 | ClientDeleteAsync( ClientId ); 482 | WebsocketClient?.Close(); 483 | } 484 | } 485 | } 486 | -------------------------------------------------------------------------------- /SpeckleCore/Conversion/ConverterDeserialisation.cs: -------------------------------------------------------------------------------- 1 | using SpeckleCore.Data; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace SpeckleCore 11 | { 12 | public abstract partial class Converter 13 | { 14 | 15 | /// 16 | /// Deserialises a list of speckle objects. 17 | /// 18 | /// The object. 19 | /// A native type, a SpeckleAbstract if no explicit conversion found, or null. 20 | public static List Deserialise( IEnumerable objectList, IEnumerable excludeAssebmlies = null ) 21 | { 22 | var copy = objectList.ToArray(); 23 | return copy.Select( obj => Deserialise( obj, excludeAssebmlies: excludeAssebmlies ) ).ToList(); 24 | } 25 | 26 | /// 27 | /// Deserialises a speckle object. 28 | /// 29 | /// 30 | /// an object, a SpeckleAbstract or null. 31 | public static object Deserialise( SpeckleObject obj, object root = null, IEnumerable excludeAssebmlies = null ) 32 | { 33 | var t = obj.GetType(); 34 | try 35 | { 36 | // null check 37 | if ( obj == null ) return null; 38 | 39 | // if it's not a speckle abstract object 40 | if ( !( obj is SpeckleAbstract ) ) 41 | { 42 | var typeString = obj.GetType().ToString(); 43 | if ( toNativeMethods.ContainsKey( typeString ) ) 44 | { 45 | return toNativeMethods[ typeString ].Invoke( obj, new object[ ] { obj } ); 46 | } 47 | 48 | var currentType = obj.GetType(); 49 | var methods = new List(); 50 | var baseTypes = new List(); 51 | 52 | // create a list of base types for this object 53 | while ( currentType != null ) 54 | { 55 | baseTypes.Add( currentType ); 56 | currentType = currentType.BaseType; 57 | } 58 | 59 | var assembliesToSearch = SpeckleCore.SpeckleInitializer.GetAssemblies().Where( ass => (excludeAssebmlies != null ? !excludeAssebmlies.Contains( ass.FullName.Split( ',' )[ 0 ] ) : true) ); 60 | 61 | // Type first search 62 | foreach( var type in baseTypes) 63 | { 64 | foreach( var assembly in assembliesToSearch) 65 | { 66 | try 67 | { 68 | methods.AddRange( Converter.GetExtensionMethods( assembly, type, "ToNative" ) ); 69 | } 70 | catch { } 71 | } 72 | } 73 | 74 | // iterate through the ToNative method array 75 | if ( methods.Count > 0 ) 76 | { 77 | Data.SpeckleException ex = null; 78 | foreach ( var method in methods ) 79 | { 80 | try 81 | { 82 | var convRes = method.Invoke( obj, new object[ ] { obj } ); 83 | if ( convRes != null ) 84 | { 85 | toNativeMethods.Add( obj.GetType().ToString(), method ); 86 | return convRes; 87 | } 88 | } 89 | catch ( Exception e ) 90 | { 91 | // to native method failed, try another one if present! 92 | if(e.InnerException!=null && e.InnerException is Data.SpeckleException) 93 | ex = e.InnerException as Data.SpeckleException; 94 | } 95 | } 96 | 97 | if(ex!=null) 98 | { 99 | return new SpeckleConversionError 100 | { 101 | Message = $"Could not convert object of type '{t}'", 102 | Details = ex.Message, 103 | SourceObject = obj 104 | }; 105 | } 106 | } 107 | 108 | return new SpeckleConversionError 109 | { 110 | Message = $"Could not convert object of type '{t}'", 111 | Details = $"No Speckle kit capable of converting objects of type '{t}' was found, this either means we haven't developed it yet or that you're missing the required kit ¯\\_(ツ)_/¯", 112 | SourceObject = obj 113 | }; 114 | } 115 | else 116 | { 117 | return DeserializeSpeckleAbstract(obj, root); 118 | } 119 | 120 | } 121 | catch (Exception e) 122 | { 123 | return new SpeckleConversionError 124 | { 125 | Message = $"Failed to convert object of type '{t}'", 126 | Details = e.Message, 127 | SourceObject = obj 128 | }; 129 | } 130 | } 131 | 132 | private static object DeserializeSpeckleAbstract(object obj, object root) 133 | { 134 | // we have a speckle abstract object 135 | var absObj = obj as SpeckleAbstract; 136 | 137 | if (absObj._type == "ref") 138 | return null; 139 | 140 | //var shortName = absObj._assembly.Split( ',' )[ 0 ]; 141 | 142 | var assembly = System.AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == absObj._assembly); 143 | 144 | //try again, without version control 145 | if (assembly == null) 146 | { 147 | var shortName = absObj._assembly.Split(',')[0]; 148 | assembly = System.AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName.Contains(shortName)); 149 | } 150 | 151 | if (assembly == null) // we can't deserialise for sure 152 | return Converter.ShallowConvert(absObj); 153 | 154 | var type = assembly.GetTypes().FirstOrDefault(t => t.Name == absObj._type); 155 | if (type == null) // type not present in the assembly 156 | return Converter.ShallowConvert(absObj); 157 | 158 | object myObject = null; 159 | 160 | try 161 | { 162 | var constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { }, null); 163 | if (constructor != null) 164 | myObject = constructor.Invoke(new object[] { }); 165 | if (myObject == null) 166 | myObject = Activator.CreateInstance(type); 167 | } 168 | catch 169 | { 170 | myObject = System.Runtime.Serialization.FormatterServices.GetUninitializedObject(type); 171 | } 172 | 173 | if (myObject == null) 174 | return absObj; 175 | 176 | if (root == null) 177 | root = myObject; 178 | 179 | var keys = absObj.Properties.Keys; 180 | foreach (string key in keys) 181 | { 182 | var prop = TryGetProperty(type, key); 183 | var field = type.GetField(key); 184 | 185 | if (prop == null && field == null) continue; 186 | 187 | if (absObj.Properties[key] == null) continue; 188 | 189 | var value = ReadValue(absObj.Properties[key], root); 190 | 191 | // handles both hashsets and lists or whatevers 192 | if (value is IEnumerable && !(value is IDictionary) && value.GetType() != typeof(string)) 193 | { 194 | try 195 | { 196 | 197 | if ((prop != null && prop.PropertyType.IsArray) || (field != null && field.FieldType.IsArray)) 198 | { 199 | value = ((List)value).ToArray(); 200 | } 201 | else 202 | { 203 | var mySubList = Activator.CreateInstance(prop != null ? prop.PropertyType : field.FieldType); 204 | foreach (var myObj in ((IEnumerable)value)) 205 | mySubList.GetType().GetMethod("Add").Invoke(mySubList, new object[] { Convert.ChangeType(myObj, mySubList.GetType().GetGenericArguments().Single()) }); 206 | 207 | value = mySubList; 208 | } 209 | } 210 | catch { } 211 | } 212 | 213 | // handles dictionaries of all sorts (kind-of!) 214 | if (value is IDictionary) 215 | { 216 | try 217 | { 218 | var MyDict = Activator.CreateInstance(prop != null ? prop.PropertyType : field.FieldType); 219 | 220 | foreach (DictionaryEntry kvp in (IDictionary)value) 221 | MyDict.GetType().GetMethod("Add").Invoke(MyDict, new object[] { Convert.ChangeType(kvp.Key, MyDict.GetType().GetGenericArguments()[0]), kvp.Value }); 222 | 223 | value = MyDict; 224 | } 225 | catch (Exception e) 226 | { 227 | System.Diagnostics.Debug.WriteLine(e.Message); 228 | } 229 | } 230 | 231 | // guids are a pain 232 | if ((prop != null && prop.PropertyType == typeof(Guid)) || (field != null && field.FieldType == typeof(Guid))) 233 | value = new Guid((string)value); 234 | 235 | // Actually set the value below, whether it's a property or field 236 | // if it is a property 237 | if (prop != null && prop.CanWrite) 238 | { 239 | if (prop.PropertyType.IsEnum) 240 | prop.SetValue(myObject, Enum.ToObject(prop.PropertyType, Convert.ChangeType(value, TypeCode.Int32))); 241 | else 242 | { 243 | try 244 | { 245 | prop.SetValue(myObject, value); 246 | } 247 | catch 248 | { 249 | try 250 | { 251 | prop.SetValue(myObject, Convert.ChangeType(value, prop.PropertyType)); 252 | } 253 | catch 254 | { 255 | } 256 | } 257 | } 258 | } 259 | // if it is a field 260 | else if (field != null) 261 | { 262 | if (field.FieldType.IsEnum) 263 | field.SetValue(myObject, Enum.ToObject(field.FieldType, Convert.ChangeType(value, TypeCode.Int32))); 264 | else 265 | { 266 | try 267 | { 268 | field.SetValue(absObj, value); 269 | } 270 | catch 271 | { 272 | try 273 | { 274 | field.SetValue(myObject, Convert.ChangeType(value, field.FieldType)); 275 | } 276 | catch { } 277 | } 278 | } 279 | } 280 | } 281 | 282 | // set references too. 283 | if (root == myObject) 284 | Converter.ResolveRefs(absObj, myObject, "root"); 285 | 286 | return myObject; 287 | } 288 | 289 | private static object ShallowConvert( SpeckleAbstract obj ) 290 | { 291 | var keys = obj.Properties.Keys; 292 | var newDict = new Dictionary(); 293 | foreach ( string key in keys ) 294 | { 295 | newDict.Add( key, Converter.ReadValue( obj.Properties[ key ], obj ) ); 296 | } 297 | obj.Properties = newDict; 298 | return obj; 299 | } 300 | 301 | private static object ReadValue( object myObject, object root = null ) 302 | { 303 | if ( myObject == null ) return null; 304 | 305 | if ( myObject.GetType().IsPrimitive || myObject.GetType() == typeof( string ) ) 306 | return myObject; 307 | 308 | if ( myObject is SpeckleAbstract ) 309 | return Converter.Deserialise( ( SpeckleAbstract ) myObject, root ); 310 | 311 | if ( myObject is SpeckleObject ) 312 | return Converter.Deserialise( ( SpeckleObject ) myObject ); 313 | 314 | if ( myObject is IEnumerable ) 315 | return ( ( IEnumerable ) myObject ).Select( o => ReadValue( o, root ) ).ToList(); 316 | 317 | if ( myObject is IDictionary ) 318 | { 319 | var genericDict = new Dictionary(); 320 | foreach ( DictionaryEntry kvp in ( IDictionary ) myObject ) 321 | genericDict.Add( kvp.Key, ReadValue( kvp.Value, root ) ); 322 | return genericDict; 323 | } 324 | 325 | return null; 326 | } 327 | 328 | private static void ResolveRefs( object original, object root, string currentPath ) 329 | { 330 | if ( original is SpeckleAbstract ) 331 | { 332 | SpeckleAbstract myObj = ( SpeckleAbstract ) original; 333 | if ( myObj._type == "ref" ) 334 | Converter.LinkRef( root, myObj._ref, currentPath ); 335 | else 336 | foreach ( var key in myObj.Properties.Keys ) 337 | Converter.ResolveRefs( myObj.Properties[ key ], root, currentPath + "/" + key ); 338 | } 339 | 340 | if ( original is IDictionary ) 341 | { 342 | foreach ( DictionaryEntry kvp in ( IDictionary ) original ) 343 | Converter.ResolveRefs( kvp.Value, root, currentPath + "/{" + kvp.Key.ToString() + "}" ); 344 | } 345 | 346 | if ( original is List ) 347 | { 348 | List myList = ( List ) original; int index = 0; 349 | foreach ( object obj in myList ) 350 | Converter.ResolveRefs( obj, root, currentPath + "/[" + index++ + "]" ); 351 | } 352 | } 353 | 354 | private static void LinkRef( object target, string fromWhere, string toWhere ) 355 | { 356 | var sourceAddress = fromWhere.Split( '/' ); 357 | var targetAddress = toWhere.Split( '/' ); 358 | 359 | object propSource = target; 360 | foreach ( string s in sourceAddress ) 361 | { 362 | if ( s == "root" ) continue; 363 | if ( s.Contains( "{" ) ) // special handler for dicts 364 | { 365 | var keySrc = s.Substring( 1, s.Length - 2 ); 366 | propSource = ( ( IDictionary ) propSource )[ Convert.ChangeType( keySrc, ( ( IDictionary ) propSource ).GetType().GetGenericArguments()[ 0 ] ) ]; 367 | continue; 368 | } 369 | if ( s.Contains( "[" ) ) // special handler for lists 370 | { 371 | var propSourceList = ((IEnumerable)propSource).Cast().ToList(); 372 | var index = int.Parse(s.Substring(1, s.Length - 2)); 373 | if (index < propSourceList.Count) // make sure we don't try to access index out of bounds 374 | { 375 | propSource = propSourceList[index]; 376 | } 377 | continue; 378 | } 379 | var propertySource = TryGetProperty( propSource, s ); 380 | if ( propertySource != null ) 381 | { 382 | propSource = propertySource.GetValue( propSource ); 383 | } 384 | else 385 | { 386 | propSource = propSource.GetType().GetField( s ).GetValue( propSource ); 387 | } 388 | } 389 | 390 | object propTarget = target; 391 | for ( int i = 1; i < targetAddress.Length - 1; i++ ) 392 | { 393 | var s = targetAddress[ i ]; 394 | 395 | if ( s == "root" ) continue; 396 | if ( s.Contains( "{" ) ) // special handler for dicts 397 | { 398 | var keySrc = s.Substring( 1, s.Length - 2 ); 399 | propTarget = ( ( IDictionary ) propTarget )[ Convert.ChangeType( keySrc, ( ( IDictionary ) propTarget ).GetType().GetGenericArguments()[ 0 ] ) ]; 400 | //propTarget = ( ( Dictionary ) propTarget )[ s.Substring( 1, s.Length - 2 ) ]; 401 | continue; 402 | } 403 | 404 | if ( s.Contains( "[" ) ) // special handler for lists 405 | { 406 | var propTargetList = ((IList)propTarget); 407 | var index = int.Parse(s.Substring(1, s.Length - 2)); 408 | if (propTargetList.Count > index) // make sure we don't try to access index out of bounds 409 | { 410 | propSource = propTargetList[index]; 411 | } 412 | continue; 413 | } 414 | 415 | var propertyTarget = TryGetProperty( propTarget.GetType(), s ); 416 | if ( propertyTarget != null ) 417 | { 418 | propTarget = propertyTarget.GetValue( propTarget ); 419 | } 420 | else 421 | { 422 | propTarget = propTarget.GetType().GetField( s ).GetValue( propTarget ); 423 | } 424 | } 425 | 426 | var last = targetAddress.Last(); 427 | 428 | if ( last.Contains( '{' ) ) 429 | { 430 | var keySrc = last.Substring( 1, last.Length - 2 ); 431 | ( ( IDictionary ) propTarget )[ Convert.ChangeType( keySrc, ( ( IDictionary ) propTarget ).GetType().GetGenericArguments()[ 0 ] ) ] = propSource; 432 | //( ( Dictionary ) propTarget )[ last.Substring( 1, last.Length - 2 ) ] = propSource; 433 | return; 434 | } 435 | if ( last.Contains( '[' ) ) 436 | { 437 | var propTargetList = ((IList)propTarget); 438 | var index = int.Parse(last.Substring(1, last.Length - 2)); 439 | if (propTargetList.Count > index) // make sure we don't try to access index out of bounds 440 | { 441 | propTargetList[index] = propSource; 442 | } 443 | return; 444 | } 445 | PropertyInfo toSet = TryGetProperty( propTarget.GetType(), last ); 446 | if ( toSet != null ) 447 | { 448 | if ( toSet.CanWrite ) 449 | toSet.SetValue( propTarget, propSource, null ); 450 | } 451 | else 452 | { 453 | var toSetField = propTarget.GetType().GetField( last ); 454 | toSetField.SetValue( propTarget, propSource ); 455 | } 456 | } 457 | 458 | private static PropertyInfo TryGetProperty( object obj, string name ) 459 | { 460 | return TryGetProperty( obj.GetType(), name ); 461 | } 462 | 463 | private static PropertyInfo TryGetProperty( Type type, string name ) 464 | { 465 | try 466 | { 467 | return type.GetProperty( name ); 468 | } 469 | catch ( AmbiguousMatchException ) 470 | { 471 | PropertyInfo property = null; 472 | Type declaringType = null; 473 | foreach ( var propInfo in type.GetProperties() ) 474 | { 475 | if ( string.Compare( name, propInfo.Name ) == 0 ) 476 | { 477 | if ( property == null || propInfo.DeclaringType.IsSubclassOf( declaringType ) ) 478 | { 479 | property = propInfo; 480 | declaringType = propInfo.DeclaringType; 481 | } 482 | } 483 | } 484 | return property; 485 | } 486 | } 487 | 488 | } 489 | } 490 | -------------------------------------------------------------------------------- /SpeckleCore/LocalData/SpeckleLocalContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using SQLite; 7 | 8 | namespace SpeckleCore 9 | { 10 | /// 11 | /// This class holds the keys to the local sqlite database that acts as a local cache for various speckle things. 12 | /// You can access accounts from here across speckle integrations, as well as the local object cache. 13 | /// The cache holds the following tables: Accounts, CachedObjects, SentObjects, CachedStreams. 14 | /// Cached objects are objects that a receiver requested. They are stored fully (with a binary blob of their speckle representation). SentObjects are objects that have been previously sent by a sender and are stored without their speckle representation (they're just refs) as a log against which to diff what to send or not. 15 | /// 16 | public static partial class LocalContext 17 | { 18 | private static bool IsInit = false; 19 | 20 | private static SQLiteConnection Database; 21 | 22 | public static string DbPath = System.Environment.GetFolderPath( System.Environment.SpecialFolder.LocalApplicationData ) + @"\SpeckleSettings\SpeckleCache.db"; 23 | 24 | public static string SettingsFolderPath = System.Environment.GetFolderPath( System.Environment.SpecialFolder.LocalApplicationData ) + @"\SpeckleSettings\"; 25 | 26 | /// 27 | /// Initialises the database context, ensures tables are created and powers up the rocket engines. 28 | /// 29 | public static void Init( ) 30 | { 31 | if ( IsInit ) return; 32 | 33 | if ( !Directory.Exists( SettingsFolderPath ) ) 34 | Directory.CreateDirectory( SettingsFolderPath ); 35 | 36 | Database = new SQLiteConnection( DbPath ); 37 | Database.CreateTable(); 38 | Database.CreateTable(); 39 | Database.CreateTable(); 40 | Database.CreateTable(); 41 | Database.CreateTable(); 42 | 43 | MigrateAccounts(); 44 | 45 | IsInit = true; 46 | } 47 | 48 | public static void Close( ) 49 | { 50 | Database?.Close(); 51 | } 52 | 53 | #region Cleanup & Table Purging 54 | /// 55 | /// Purges the sent objects table. WARNING: Don't do this unless you know what you're doing. 56 | /// 57 | public static void PurgeSentObjects( ) 58 | { 59 | LocalContext.Init(); 60 | Database?.Execute( "DELETE FROM SentObject" ); 61 | } 62 | 63 | /// 64 | /// Purges the received objects table. WARNING: Don't do this unless you know what you're doing. 65 | /// 66 | public static void PurgeCachedObjects( ) 67 | { 68 | LocalContext.Init(); 69 | Database?.Execute( "DELETE FROM CachedObject" ); 70 | } 71 | 72 | /// 73 | /// Purges the accounts. WARNING: Don't do this unless you know what you're doing. 74 | /// 75 | public static void PurgeAccounts( ) 76 | { 77 | LocalContext.Init(); 78 | Database?.Execute( "DELETE FROM Account" ); 79 | } 80 | 81 | /// 82 | /// Purges the streams table. WARNING: Don't do this unless you know what you're doing. 83 | /// 84 | public static void PurgeCachedStreams( ) 85 | { 86 | LocalContext.Init(); 87 | Database?.Execute( "DELETE FROM CachedStream" ); 88 | } 89 | 90 | #endregion 91 | 92 | #region Accounts 93 | /// 94 | /// Migrates existing accounts stored in text files to the sqlite db. 95 | /// 96 | private static void MigrateAccounts( ) 97 | { 98 | List accounts = new List(); 99 | 100 | if ( Directory.Exists( SettingsFolderPath ) && Directory.EnumerateFiles( SettingsFolderPath, "*.txt" ).Count() > 0 ) 101 | { 102 | foreach ( string file in Directory.EnumerateFiles( SettingsFolderPath, "*.txt" ) ) 103 | { 104 | string content = File.ReadAllText( file ); 105 | string[ ] pieces = content.TrimEnd( '\r', '\n' ).Split( ',' ); 106 | 107 | accounts.Add( new Account() { Email = pieces[ 0 ], Token = pieces[ 1 ], ServerName = pieces[ 2 ], RestApi = pieces[ 3 ] } ); 108 | } 109 | 110 | var res = Database.InsertAll( accounts ); 111 | 112 | Directory.CreateDirectory( SettingsFolderPath + @"\MigratedAccounts\" ); 113 | 114 | foreach ( string file in Directory.EnumerateFiles( SettingsFolderPath, "*.txt" ) ) 115 | { 116 | try 117 | { 118 | var newName = Path.Combine( SettingsFolderPath, "MigratedAccounts", Path.GetFileName( file ) ); 119 | if ( File.Exists( newName ) ) 120 | { 121 | File.Delete( newName ); 122 | } 123 | 124 | File.Move( file, newName ); 125 | } 126 | catch ( Exception e ) 127 | { 128 | Debug.WriteLine( e.Message ); 129 | } 130 | } 131 | 132 | } 133 | else 134 | { 135 | Debug.WriteLine( "No existing account text files found." ); 136 | } 137 | } 138 | 139 | /// 140 | /// Adds a new account. 141 | /// 142 | /// 143 | public static void AddAccount( Account account ) 144 | { 145 | LocalContext.Init(); 146 | var res = Database.Insert( account ); 147 | } 148 | 149 | /// 150 | /// Gets all accounts present. 151 | /// 152 | /// 153 | public static List GetAllAccounts( ) 154 | { 155 | LocalContext.Init(); 156 | return Database.Query( "SELECT * FROM Account" ); 157 | } 158 | 159 | /// 160 | /// Gets all the accounts associated with the provided rest api. 161 | /// 162 | /// 163 | /// 164 | public static List GetAccountsByRestApi( string RestApi ) 165 | { 166 | LocalContext.Init(); 167 | return Database.Query( "SELECT * from Account WHERE RestApi = ?", RestApi ); 168 | } 169 | 170 | /// 171 | /// Gets all the accounts associated with the provided email. 172 | /// 173 | /// 174 | /// 175 | public static List GetAccountsByEmail( string email ) 176 | { 177 | LocalContext.Init(); 178 | return Database.Query( "SELECT * from Account WHERE Email = ?", email ); 179 | } 180 | 181 | /// 182 | /// If more accounts present, will return the first one only. 183 | /// 184 | /// 185 | /// 186 | /// null if no account is found. 187 | public static Account GetAccountByEmailAndRestApi( string email, string restApi ) 188 | { 189 | LocalContext.Init(); 190 | var res = Database.Query( String.Format( "SELECT * from Account WHERE RestApi = '{0}' AND Email='{1}'", restApi, email ) ); 191 | if ( res.Count >= 1 ) 192 | { 193 | return res[ 0 ]; 194 | } 195 | else 196 | { 197 | return null; 198 | //throw new Exception("Could not find account."); 199 | } 200 | } 201 | 202 | /// 203 | /// Returns the default account, if any. Otherwise throws an error. 204 | /// 205 | /// 206 | public static Account GetDefaultAccount( ) 207 | { 208 | LocalContext.Init(); 209 | var res = Database.Query( "SELECT * FROM Account WHERE IsDefault=1 LIMIT 1" ); 210 | if ( res.Count == 1 ) 211 | { 212 | return res[ 0 ]; 213 | } 214 | else 215 | { 216 | throw new Exception( "No default account set." ); 217 | } 218 | } 219 | 220 | /// 221 | /// Sets an account as being the default one, and de-sets defaultness on all others. 222 | /// 223 | /// 224 | public static void SetDefaultAccount( Account account ) 225 | { 226 | LocalContext.Init(); 227 | 228 | ClearDefaultAccount(); 229 | 230 | account.IsDefault = true; 231 | Database.Update( account ); 232 | } 233 | 234 | /// 235 | /// Clears any default account. (You will no longer have a default account) 236 | /// 237 | /// 238 | public static void ClearDefaultAccount( ) 239 | { 240 | LocalContext.Init(); 241 | Database.Execute( "UPDATE Account SET IsDefault=0" ); 242 | } 243 | 244 | /// 245 | /// Udates an account by its primary key. 246 | /// 247 | /// 248 | public static void UpdateAccount( Account account ) 249 | { 250 | LocalContext.Init(); 251 | Database.Update( account ); 252 | } 253 | 254 | public static void RemoveAccount( Account ac ) 255 | { 256 | LocalContext.Init(); 257 | Database.Delete( ac.AccountId ); 258 | } 259 | 260 | #endregion 261 | 262 | #region Received Objects (CachedObject) 263 | 264 | /// 265 | /// Adds a speckle object to the local cache. 266 | /// 267 | /// The object to add. 268 | /// The server url of where it has been persisted. 269 | public static void AddCachedObject( SpeckleObject obj, string restApi ) 270 | { 271 | LocalContext.Init(); 272 | var bytes = SpeckleCore.Converter.getBytes( obj ); 273 | var combinedHash = Converter.getMd5Hash( obj._id + restApi ); 274 | var cached = new CachedObject() 275 | { 276 | RestApi = restApi, 277 | Bytes = bytes, 278 | DatabaseId = obj._id, 279 | CombinedHash = combinedHash, 280 | Hash = obj.Hash, 281 | AddedOn = DateTime.Now 282 | }; 283 | 284 | try 285 | { 286 | Database.Insert( cached ); 287 | } 288 | catch 289 | { 290 | // object was already there 291 | } 292 | } 293 | 294 | /// 295 | /// Does a cache check on a list of speckle object placeholders. It will populate the original list with any objects it can find in the cache. If none are found, the list is returned unmodified. 296 | /// 297 | /// Speckle object placeholders to check against the cache. 298 | /// The rest api these objects are expected to come from. 299 | /// 300 | public static List GetCachedObjects( List objs, string restApi ) 301 | { 302 | LocalContext.Init(); 303 | var MaxSqlVars = 900; 304 | 305 | var combinedHashes = objs.Select( obj => Converter.getMd5Hash( obj._id + restApi ) ).ToList(); 306 | 307 | var partitionedList = new List>(); 308 | 309 | for ( int i = 0; i < combinedHashes.Count; i += MaxSqlVars ) 310 | { 311 | partitionedList.Add( combinedHashes.GetRange( i, Math.Min( MaxSqlVars, combinedHashes.Count - i ) ) ); 312 | } 313 | 314 | var fullRes = new List(); 315 | var speckleObjs = new List(); 316 | 317 | foreach ( var subList in partitionedList ) 318 | { 319 | //var res = Database.Table().Where( obj => subList.Contains( obj.CombinedHash ) ).Select( o => o.ToSpeckle() ).ToList(); 320 | var res = Database.Table().Where( obj => subList.Contains( obj.CombinedHash ) ); 321 | 322 | foreach ( var cachedObj in res ) 323 | { 324 | // compatibility layer for old cache objects 325 | try 326 | { 327 | var spk = cachedObj.ToSpeckle(); 328 | speckleObjs.Add( spk ); 329 | } 330 | catch 331 | { 332 | 333 | } 334 | } 335 | //fullRes.AddRange( res ); 336 | } 337 | 338 | // populate the original list with whatever objects we found in the database. 339 | for ( int i = 0; i < objs.Count; i++ ) 340 | { 341 | var placeholder = objs[ i ]; 342 | var myObject = speckleObjs.Find( o => o._id == placeholder._id ); 343 | if ( myObject != null ) 344 | { 345 | objs[ i ] = myObject; 346 | } 347 | } 348 | 349 | return objs; 350 | } 351 | 352 | #endregion 353 | 354 | #region Sent Objects (SentObject) 355 | 356 | /// 357 | /// Adds an object that has been sent by a sender in the local cache. 358 | /// This does not store the full object, it's just a log that it has been sent 359 | /// to a server so it does not get sent again. 360 | /// 361 | /// Object to store as sent ref in the local database. 362 | /// The server's url. 363 | public static void AddSentObject( SpeckleObject obj, string restApi ) 364 | { 365 | LocalContext.Init(); 366 | var sentObj = new SentObject() 367 | { 368 | RestApi = restApi, 369 | DatabaseId = obj._id, 370 | Hash = obj.Hash 371 | }; 372 | 373 | try 374 | { 375 | Database.Insert( sentObj ); 376 | } 377 | catch ( Exception e ) 378 | { 379 | var dick = e; 380 | // object was already there, no panic! 381 | } 382 | } 383 | 384 | /// 385 | /// Replaces any objects in the given list with placeholders if they're found in the local cache, as this means they were sent before and most probably exist on the server. 386 | /// 387 | /// 388 | /// 389 | /// (Optinoal) The modified list. 390 | public static List PruneExistingObjects( List objs, string restApi ) 391 | { 392 | LocalContext.Init(); 393 | // MAX SQL Vars is 900 394 | var MaxSqlVars = 900; 395 | 396 | var objHashes = objs.Select( obj => true ? obj.Hash : restApi ).ToList(); 397 | 398 | var partitionedList = new List>(); 399 | 400 | for ( int i = 0; i < objHashes.Count; i += MaxSqlVars ) 401 | { 402 | partitionedList.Add( objHashes.GetRange( i, Math.Min( MaxSqlVars, objHashes.Count - i ) ) ); 403 | } 404 | 405 | var fullRes = new List(); 406 | 407 | foreach ( var subList in partitionedList ) 408 | { 409 | var res = Database.Table().Where( obj => subList.Contains( obj.Hash ) && obj.RestApi == restApi ).ToList(); 410 | fullRes.AddRange( res ); 411 | } 412 | 413 | for ( int i = 0; i < objs.Count; i++ ) 414 | { 415 | var placeholder = objs[ i ]; 416 | var myObject = fullRes.Find( o => o.Hash == objs[ i ].Hash ); 417 | if ( myObject != null ) 418 | { 419 | objs[ i ] = new SpecklePlaceholder() { _id = myObject.DatabaseId }; 420 | } 421 | } 422 | 423 | return objs; 424 | } 425 | 426 | #endregion 427 | 428 | #region Streams (CachedStream) 429 | 430 | /// 431 | /// Updates or inserts a stream in the local cache. 432 | /// 433 | /// 434 | /// 435 | public static void AddOrUpdateStream( SpeckleStream stream, string restApi ) 436 | { 437 | LocalContext.Init(); 438 | var bytes = SpeckleCore.Converter.getBytes( stream.ToJson() ); 439 | var combinedHash = Converter.getMd5Hash( stream._id + restApi ); 440 | 441 | var cacheRes = Database.Table().Where( existing => existing.CombinedHash == combinedHash ).ToList(); 442 | 443 | if ( cacheRes.Count >= 1 ) 444 | { 445 | var toUpdate = cacheRes[ 0 ]; 446 | toUpdate.Bytes = bytes; 447 | toUpdate.UpdatedOn = DateTime.Now; 448 | Database.Update( toUpdate ); 449 | } 450 | else 451 | { 452 | var toCache = new CachedStream() 453 | { 454 | CombinedHash = combinedHash, 455 | Bytes = bytes, 456 | RestApi = restApi, 457 | StreamId = stream.StreamId, 458 | AddedOn = DateTime.Now, 459 | UpdatedOn = DateTime.Now 460 | }; 461 | Database.Insert( toCache ); 462 | } 463 | //throw new NotImplementedException(); 464 | } 465 | 466 | /// 467 | /// Gets a stream from the local cache. 468 | /// 469 | /// 470 | /// 471 | /// Null, if nothing found, or the speckle stream. 472 | public static SpeckleStream GetStream( string streamId, string restApi ) 473 | { 474 | LocalContext.Init(); 475 | var combinedHash = Converter.getMd5Hash( streamId + restApi ); 476 | var res = Database.Table().Where( str => str.CombinedHash == combinedHash ).ToArray(); 477 | if ( res.Length > 0 ) 478 | { 479 | return res[ 0 ].ToSpeckle(); 480 | } 481 | 482 | return null; 483 | } 484 | 485 | #endregion 486 | 487 | #region Telemetry 488 | 489 | /// 490 | /// Returns true/false depending on wether the user has enabled telemetry. 491 | /// 492 | /// 493 | public static bool GetTelemetrySettings( ) 494 | { 495 | LocalContext.Init(); 496 | var settings = Database.Query( "SELECT * FROM TelemetrySettings" ).FirstOrDefault(); 497 | if ( settings != null ) 498 | { 499 | return settings.Enabled; 500 | } 501 | else 502 | { 503 | var ts = new TelemetrySettings(); 504 | Database.Insert( ts ); 505 | return true; // defaults to true 506 | } 507 | } 508 | 509 | /// 510 | /// Enables or disables telemetry. 511 | /// 512 | /// 513 | public static void SetTelemetrySettings( bool status ) 514 | { 515 | LocalContext.Init(); 516 | var settings = Database.Query( "SELECT * FROM TelemetrySettings" ).FirstOrDefault(); 517 | if ( settings != null ) 518 | { 519 | settings.Enabled = status; 520 | Database.Update( settings ); 521 | } 522 | else 523 | { 524 | var ts = new TelemetrySettings(); 525 | ts.Enabled = status; 526 | Database.Insert( ts ); 527 | } 528 | } 529 | #endregion 530 | } 531 | 532 | 533 | 534 | } 535 | -------------------------------------------------------------------------------- /SpeckleCore/Models/ModelBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SpeckleCore 8 | { 9 | /// Base class that adds a set of simple properties related to authorisation and commenting to all applicable resources (not users). 10 | [System.CodeDom.Compiler.GeneratedCode( "NJsonSchema", "9.10.41.0 (Newtonsoft.Json v9.0.0.0)" )] 11 | [Serializable] 12 | public partial class ResourceBase 13 | { 14 | [Newtonsoft.Json.JsonProperty( "_id", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 15 | public string _id { get; set; } 16 | 17 | [Newtonsoft.Json.JsonProperty( "owner", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 18 | public string Owner { get; set; } 19 | 20 | [Newtonsoft.Json.JsonProperty( "private", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 21 | public bool? Private { get; set; } 22 | 23 | [Newtonsoft.Json.JsonProperty( "anonymousComments", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 24 | public bool? AnonymousComments { get; set; } 25 | 26 | [Newtonsoft.Json.JsonProperty( "canRead", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 27 | public List CanRead { get; set; } 28 | 29 | [Newtonsoft.Json.JsonProperty( "canWrite", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 30 | public List CanWrite { get; set; } 31 | 32 | /// An array of comment ids. 33 | [Newtonsoft.Json.JsonProperty( "comments", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 34 | public List Comments { get; set; } 35 | 36 | /// Controls archival status - does not actually delete anything 37 | [Newtonsoft.Json.JsonProperty( "deleted", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 38 | public bool? Deleted { get; set; } 39 | 40 | public virtual string ToJson( ) 41 | { 42 | return Newtonsoft.Json.JsonConvert.SerializeObject( this ); 43 | } 44 | 45 | public static ResourceBase FromJson( string data ) 46 | { 47 | return Newtonsoft.Json.JsonConvert.DeserializeObject( data ); 48 | } 49 | 50 | } 51 | 52 | /// Describes a user. 53 | [System.CodeDom.Compiler.GeneratedCode( "NJsonSchema", "9.10.41.0 (Newtonsoft.Json v9.0.0.0)" )] 54 | [Serializable] 55 | public partial class User 56 | { 57 | /// Database uuid. 58 | [Newtonsoft.Json.JsonProperty( "_id", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 59 | public string _id { get; set; } 60 | 61 | /// Password. 62 | [Newtonsoft.Json.JsonProperty( "password", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 63 | public string Password { get; set; } 64 | 65 | /// User's role. Defaults to "user". 66 | [Newtonsoft.Json.JsonProperty( "role", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 67 | public string Role { get; set; } 68 | 69 | /// We will need profile pics at one point. 70 | [Newtonsoft.Json.JsonProperty( "avatar", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 71 | public string Avatar { get; set; } 72 | 73 | /// a signed jwt token that expires in 1 year. 74 | [Newtonsoft.Json.JsonProperty( "apitoken", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 75 | public string Apitoken { get; set; } 76 | 77 | /// a signed jwt token that expires in 1 day. 78 | [Newtonsoft.Json.JsonProperty( "token", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 79 | public string Token { get; set; } 80 | 81 | /// user's email 82 | [Newtonsoft.Json.JsonProperty( "email", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 83 | public string Email { get; set; } 84 | 85 | /// User's given name 86 | [Newtonsoft.Json.JsonProperty( "name", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 87 | public string Name { get; set; } 88 | 89 | /// User's family name 90 | [Newtonsoft.Json.JsonProperty( "surname", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 91 | public string Surname { get; set; } 92 | 93 | /// Users's company 94 | [Newtonsoft.Json.JsonProperty( "company", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 95 | public string Company { get; set; } 96 | 97 | /// an array storing each time the user logged in. 98 | [Newtonsoft.Json.JsonProperty( "logins", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 99 | public List Logins { get; set; } 100 | 101 | public string ToJson( ) 102 | { 103 | return Newtonsoft.Json.JsonConvert.SerializeObject( this ); 104 | } 105 | 106 | public static User FromJson( string data ) 107 | { 108 | return Newtonsoft.Json.JsonConvert.DeserializeObject( data ); 109 | } 110 | 111 | } 112 | 113 | /// A speckle client. 114 | [System.CodeDom.Compiler.GeneratedCode( "NJsonSchema", "9.10.41.0 (Newtonsoft.Json v9.0.0.0)" )] 115 | [Serializable] 116 | public partial class AppClient : ResourceBase 117 | { 118 | /// Database uuid. 119 | [Newtonsoft.Json.JsonProperty( "_id", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 120 | public string _id { get; set; } 121 | 122 | /// Either Sender, Receiver or anything else you can think of. 123 | [Newtonsoft.Json.JsonProperty( "role", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 124 | public string Role { get; set; } 125 | 126 | [Newtonsoft.Json.JsonProperty( "documentGuid", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 127 | public string DocumentGuid { get; set; } 128 | 129 | [Newtonsoft.Json.JsonProperty( "documentName", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 130 | public string DocumentName { get; set; } 131 | 132 | [Newtonsoft.Json.JsonProperty( "documentType", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 133 | public string DocumentType { get; set; } 134 | 135 | [Newtonsoft.Json.JsonProperty( "documentLocation", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 136 | public string DocumentLocation { get; set; } 137 | 138 | /// The streamId that this client is attached to. 139 | [Newtonsoft.Json.JsonProperty( "streamId", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 140 | public string StreamId { get; set; } 141 | 142 | /// Is it accessible from the server or not? 143 | [Newtonsoft.Json.JsonProperty( "online", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 144 | public bool? Online { get; set; } 145 | 146 | public string ToJson( ) 147 | { 148 | return Newtonsoft.Json.JsonConvert.SerializeObject( this ); 149 | } 150 | 151 | public static AppClient FromJson( string data ) 152 | { 153 | return Newtonsoft.Json.JsonConvert.DeserializeObject( data ); 154 | } 155 | 156 | } 157 | 158 | /// A project contains a group of streams and users. 159 | [System.CodeDom.Compiler.GeneratedCode( "NJsonSchema", "9.10.41.0 (Newtonsoft.Json v9.0.0.0)" )] 160 | [Serializable] 161 | public partial class Project : ResourceBase 162 | { 163 | [Newtonsoft.Json.JsonProperty( "_id", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 164 | public string _id { get; set; } 165 | 166 | [Newtonsoft.Json.JsonProperty( "name", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 167 | public string Name { get; set; } 168 | 169 | [Newtonsoft.Json.JsonProperty( "number", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 170 | public string number { get; set; } 171 | 172 | [Newtonsoft.Json.JsonProperty( "users", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 173 | public List Users { get; set; } 174 | 175 | [Newtonsoft.Json.JsonProperty( "streams", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 176 | public List Streams { get; set; } 177 | 178 | [Newtonsoft.Json.JsonProperty( "subProjects", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 179 | public List SubProjects { get; set; } 180 | 181 | public string ToJson( ) 182 | { 183 | return Newtonsoft.Json.JsonConvert.SerializeObject( this ); 184 | } 185 | 186 | public static Project FromJson( string data ) 187 | { 188 | return Newtonsoft.Json.JsonConvert.DeserializeObject( data ); 189 | } 190 | 191 | } 192 | 193 | /// A comment/issue. 194 | [System.CodeDom.Compiler.GeneratedCode( "NJsonSchema", "9.10.41.0 (Newtonsoft.Json v9.0.0.0)" )] 195 | [Serializable] 196 | public partial class Comment : ResourceBase 197 | { 198 | [Newtonsoft.Json.JsonProperty( "resource", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 199 | public Resource Resource { get; set; } 200 | 201 | [Newtonsoft.Json.JsonProperty( "text", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 202 | public string Text { get; set; } 203 | 204 | [Newtonsoft.Json.JsonProperty( "assignedTo", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 205 | public List AssignedTo { get; set; } 206 | 207 | [Newtonsoft.Json.JsonProperty( "closed", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 208 | public bool? Closed { get; set; } 209 | 210 | [Newtonsoft.Json.JsonProperty( "labels", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 211 | public List Labels { get; set; } 212 | 213 | [Newtonsoft.Json.JsonProperty( "view", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 214 | public object View { get; set; } 215 | 216 | [Newtonsoft.Json.JsonProperty( "screenshot", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 217 | public string Screenshot { get; set; } 218 | 219 | public string ToJson( ) 220 | { 221 | return Newtonsoft.Json.JsonConvert.SerializeObject( this ); 222 | } 223 | 224 | public static Comment FromJson( string data ) 225 | { 226 | return Newtonsoft.Json.JsonConvert.DeserializeObject( data ); 227 | } 228 | 229 | } 230 | 231 | /// A stream is essentially a collection of objects, with added properties. 232 | [System.CodeDom.Compiler.GeneratedCode( "NJsonSchema", "9.10.41.0 (Newtonsoft.Json v9.0.0.0)" )] 233 | [Serializable] 234 | public partial class SpeckleStream : ResourceBase 235 | { 236 | /// The stream's short id. 237 | [Newtonsoft.Json.JsonProperty( "streamId", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 238 | public string StreamId { get; set; } 239 | 240 | /// The data stream's name 241 | [Newtonsoft.Json.JsonProperty( "name", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 242 | public string Name { get; set; } 243 | 244 | /// The data stream's description 245 | [Newtonsoft.Json.JsonProperty( "description", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 246 | public string Description { get; set; } 247 | 248 | /// The data stream's name 249 | [Newtonsoft.Json.JsonProperty( "tags", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 250 | public List Tags { get; set; } 251 | 252 | /// An array of SpeckleObject ids. 253 | [Newtonsoft.Json.JsonProperty( "objects", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 254 | public List Objects { get; set; } 255 | 256 | /// An array of speckle layers. 257 | [Newtonsoft.Json.JsonProperty( "layers", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 258 | public List Layers { get; set; } 259 | 260 | /// Units, tolerances, etc. 261 | [Newtonsoft.Json.JsonProperty( "baseProperties", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 262 | public dynamic BaseProperties { get; set; } 263 | 264 | /// Any performance measures can go in here. 265 | [Newtonsoft.Json.JsonProperty( "globalMeasures", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 266 | public dynamic GlobalMeasures { get; set; } 267 | 268 | [Newtonsoft.Json.JsonProperty( "isComputedResult", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 269 | public bool? IsComputedResult { get; set; } 270 | 271 | [Newtonsoft.Json.JsonProperty( "viewerLayers", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 272 | public List ViewerLayers { get; set; } 273 | 274 | /// If this stream is a child, the parent's streamId. 275 | [Newtonsoft.Json.JsonProperty( "parent", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 276 | public string Parent { get; set; } 277 | 278 | /// An array of the streamId of any children of this stream. 279 | [Newtonsoft.Json.JsonProperty( "children", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 280 | public List Children { get; set; } 281 | 282 | /// If resulting from a merge, the streams that this one was born out of. 283 | [Newtonsoft.Json.JsonProperty( "ancestors", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 284 | public List Ancestors { get; set; } 285 | 286 | public string ToJson( ) 287 | { 288 | return Newtonsoft.Json.JsonConvert.SerializeObject( this ); 289 | } 290 | 291 | public static SpeckleStream FromJson( string data ) 292 | { 293 | return Newtonsoft.Json.JsonConvert.DeserializeObject( data ); 294 | } 295 | 296 | } 297 | 298 | /// Describes a speckle layer. To assign objects to a speckle layer, you'll need to start at `objects[ layer.startIndex ]` and finish at `objects[ layer.startIndex + layer.objectCount ]`. 299 | [System.CodeDom.Compiler.GeneratedCode( "NJsonSchema", "9.10.41.0 (Newtonsoft.Json v9.0.0.0)" )] 300 | [Serializable] 301 | public partial class Layer 302 | { 303 | /// Layer's name 304 | [Newtonsoft.Json.JsonProperty( "name", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 305 | public string Name { get; set; } 306 | 307 | /// Layer's guid (must be unique) 308 | [Newtonsoft.Json.JsonProperty( "guid", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 309 | public string Guid { get; set; } 310 | 311 | /// Describes this layer's position in the list of layers. 312 | [Newtonsoft.Json.JsonProperty( "orderIndex", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 313 | public int? OrderIndex { get; set; } 314 | 315 | /// The index of the first object relative to the stream's objects array 316 | [Newtonsoft.Json.JsonProperty( "startIndex", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 317 | public double? StartIndex { get; set; } 318 | 319 | /// How many objects does this layer have. 320 | [Newtonsoft.Json.JsonProperty( "objectCount", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 321 | public double? ObjectCount { get; set; } 322 | 323 | /// String describing the nested tree structure (GH centric). 324 | [Newtonsoft.Json.JsonProperty( "topology", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 325 | public string Topology { get; set; } 326 | 327 | [Newtonsoft.Json.JsonProperty( "properties", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 328 | public LayerProperties Properties { get; set; } 329 | 330 | public string ToJson( ) 331 | { 332 | return Newtonsoft.Json.JsonConvert.SerializeObject( this ); 333 | } 334 | 335 | public static Layer FromJson( string data ) 336 | { 337 | return Newtonsoft.Json.JsonConvert.DeserializeObject( data ); 338 | } 339 | 340 | } 341 | 342 | /// Holds stream layer properties, mostly for displaying purposes. This object will be filled up with garbage from threejs and others, but below is a minimal schema. 343 | [System.CodeDom.Compiler.GeneratedCode( "NJsonSchema", "9.10.41.0 (Newtonsoft.Json v9.0.0.0)" )] 344 | [Serializable] 345 | public partial class LayerProperties 346 | { 347 | [Newtonsoft.Json.JsonProperty( "color", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 348 | public SpeckleBaseColor Color { get; set; } 349 | 350 | /// toggles layer visibility. 351 | [Newtonsoft.Json.JsonProperty( "visible", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 352 | public bool? Visible { get; set; } 353 | 354 | /// defines point size in threejs 355 | [Newtonsoft.Json.JsonProperty( "pointsize", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 356 | public double? Pointsize { get; set; } 357 | 358 | /// defines line thickness in threejs 359 | [Newtonsoft.Json.JsonProperty( "linewidth", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 360 | public double? Linewidth { get; set; } 361 | 362 | /// says it all. speckle is superficial. 363 | [Newtonsoft.Json.JsonProperty( "shininess", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 364 | public double? Shininess { get; set; } 365 | 366 | /// smooth shading toggle 367 | [Newtonsoft.Json.JsonProperty( "smooth", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 368 | public bool? Smooth { get; set; } 369 | 370 | /// display edges or not yo. 371 | [Newtonsoft.Json.JsonProperty( "showEdges", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 372 | public bool? ShowEdges { get; set; } 373 | 374 | /// i'm bored. 375 | [Newtonsoft.Json.JsonProperty( "wireframe", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 376 | public bool? Wireframe { get; set; } 377 | 378 | public string ToJson( ) 379 | { 380 | return Newtonsoft.Json.JsonConvert.SerializeObject( this ); 381 | } 382 | 383 | public static LayerProperties FromJson( string data ) 384 | { 385 | return Newtonsoft.Json.JsonConvert.DeserializeObject( data ); 386 | } 387 | 388 | } 389 | 390 | [Serializable] 391 | [System.CodeDom.Compiler.GeneratedCode( "NJsonSchema", "9.10.41.0 (Newtonsoft.Json v9.0.0.0)" ) ] 392 | [Newtonsoft.Json.JsonConverter( typeof( SpeckleObjectConverter ), "type" )] 393 | public partial class SpeckleObject : ResourceBase 394 | { 395 | [Newtonsoft.Json.JsonProperty( "type", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 396 | public virtual string Type { get; set; } = "Object"; 397 | 398 | /// Object's unique hash. 399 | [Newtonsoft.Json.JsonProperty( "hash", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 400 | public string Hash { get; set; } 401 | 402 | /// Object's transform. 403 | [Newtonsoft.Json.JsonProperty( "transform", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 404 | public List Transform { get; set; } 405 | 406 | /// Object's geometry hash 407 | [Newtonsoft.Json.JsonProperty( "geometryHash", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 408 | public string GeometryHash { get; set; } 409 | 410 | /// The id/guid that the origin application identifies this object by. 411 | [Newtonsoft.Json.JsonProperty( "applicationId", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 412 | public string ApplicationId { get; set; } 413 | 414 | /// The name of this object in the origin application GUI. 415 | [Newtonsoft.Json.JsonProperty( "name", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 416 | public string Name { get; set; } 417 | 418 | /// The extra properties field of a speckle object. 419 | [Newtonsoft.Json.JsonProperty( "properties", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 420 | [Newtonsoft.Json.JsonConverter( typeof( SpecklePropertiesConverter ) )] 421 | public Dictionary Properties { get; set; } = new Dictionary(); 422 | 423 | /// If this object is a child, the parent's objectid. 424 | [Newtonsoft.Json.JsonProperty( "parent", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 425 | public string Parent { get; set; } 426 | 427 | /// An array of the ids of any children of this object. 428 | [Newtonsoft.Json.JsonProperty( "children", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 429 | public List Children { get; set; } 430 | 431 | /// If resulting from a merge, the objects that this one was born out of. 432 | [Newtonsoft.Json.JsonProperty( "ancestors", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 433 | public List Ancestors { get; set; } 434 | 435 | public string ToJson( ) 436 | { 437 | return Newtonsoft.Json.JsonConvert.SerializeObject( this ); 438 | } 439 | 440 | public static SpeckleObject FromJson( string data ) 441 | { 442 | return Newtonsoft.Json.JsonConvert.DeserializeObject( data ); 443 | } 444 | 445 | public bool Equals( SpeckleObject x, SpeckleObject y ) 446 | { 447 | return x.Hash == y.Hash; 448 | } 449 | 450 | public int GetHashCode( SpeckleObject obj ) 451 | { 452 | return obj.Hash.GetHashCode(); 453 | } 454 | } 455 | 456 | [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "9.10.41.0 (Newtonsoft.Json v9.0.0.0)")] 457 | [Serializable] 458 | public partial class SpeckleDelta 459 | { 460 | [Newtonsoft.Json.JsonProperty("created", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] 461 | public List Created { get; set; } 462 | 463 | [Newtonsoft.Json.JsonProperty("deleted", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] 464 | public List Deleted { get; set; } 465 | 466 | [Newtonsoft.Json.JsonProperty("common", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] 467 | public List Common { get; set; } 468 | 469 | [Newtonsoft.Json.JsonProperty("revisionA", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] 470 | public object revisionA { get; set; } // the streamid you want to apply this delta to 471 | 472 | [Newtonsoft.Json.JsonProperty("revisionB", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] 473 | public object revisionB { get; set; } 474 | 475 | public string ToJson() 476 | { 477 | return Newtonsoft.Json.JsonConvert.SerializeObject(this); 478 | } 479 | 480 | public static SpeckleDelta FromJson(string data) 481 | { 482 | return Newtonsoft.Json.JsonConvert.DeserializeObject(data); 483 | } 484 | 485 | } 486 | 487 | 488 | [System.CodeDom.Compiler.GeneratedCode( "NJsonSchema", "9.10.41.0 (Newtonsoft.Json v9.0.0.0)" )] 489 | [Serializable] 490 | public partial class SpeckleAbstract : SpeckleObject 491 | { 492 | [Newtonsoft.Json.JsonProperty( "type", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 493 | public override string Type { get; set; } = "Abstract"; 494 | 495 | /// the original type of the object 496 | [Newtonsoft.Json.JsonProperty( "_type", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 497 | public string _type { get; set; } 498 | 499 | /// the original type of the object 500 | [Newtonsoft.Json.JsonProperty( "_ref", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 501 | public string _ref { get; set; } 502 | 503 | /// the original assembly of this object 504 | [Newtonsoft.Json.JsonProperty( "_assembly", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 505 | public string _assembly { get; set; } 506 | 507 | public string ToJson( ) 508 | { 509 | return Newtonsoft.Json.JsonConvert.SerializeObject( this ); 510 | } 511 | 512 | public static SpeckleAbstract FromJson( string data ) 513 | { 514 | return Newtonsoft.Json.JsonConvert.DeserializeObject( data ); 515 | } 516 | 517 | } 518 | 519 | [System.CodeDom.Compiler.GeneratedCode( "NJsonSchema", "9.10.41.0 (Newtonsoft.Json v9.0.0.0)" )] 520 | [Serializable] 521 | public partial class SpeckleNull : SpeckleObject 522 | { 523 | [Newtonsoft.Json.JsonProperty( "type", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 524 | public override string Type { get; set; } = "Null"; 525 | 526 | public SpeckleNull( ) 527 | { 528 | this.GeometryHash = "Null.0"; this.Hash = "Null.0"; 529 | } 530 | } 531 | 532 | [System.CodeDom.Compiler.GeneratedCode( "NJsonSchema", "9.10.41.0 (Newtonsoft.Json v9.0.0.0)" )] 533 | [Serializable] 534 | public partial class SpecklePlaceholder : SpeckleObject 535 | { 536 | [Newtonsoft.Json.JsonProperty( "type", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore )] 537 | public override string Type { get; set; } = "Placeholder"; 538 | 539 | public string ToJson( ) 540 | { 541 | return Newtonsoft.Json.JsonConvert.SerializeObject( this ); 542 | } 543 | 544 | public static SpecklePlaceholder FromJson( string data ) 545 | { 546 | return Newtonsoft.Json.JsonConvert.DeserializeObject( data ); 547 | } 548 | 549 | } 550 | 551 | } 552 | --------------------------------------------------------------------------------