diff --git a/src/Speckle.Sdk/Api/Blob/BlobApi.cs b/src/Speckle.Sdk/Api/Blob/BlobApi.cs
index 1dd68eb5..c9a17b8e 100644
--- a/src/Speckle.Sdk/Api/Blob/BlobApi.cs
+++ b/src/Speckle.Sdk/Api/Blob/BlobApi.cs
@@ -19,7 +19,6 @@ public partial interface IBlobApi : IDisposable;
/// Low level access to the blob API
///
///
-///
[GenerateAutoInterface]
public sealed class BlobApi : IBlobApi
{
diff --git a/src/Speckle.Sdk/Api/Operations/Operations.Receive.cs b/src/Speckle.Sdk/Api/Operations/Operations.Receive.cs
index b75db9fc..03fc5db4 100644
--- a/src/Speckle.Sdk/Api/Operations/Operations.Receive.cs
+++ b/src/Speckle.Sdk/Api/Operations/Operations.Receive.cs
@@ -1,7 +1,5 @@
-using Microsoft.Extensions.Logging;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
-using Speckle.Sdk.Serialisation;
using Speckle.Sdk.Transports;
namespace Speckle.Sdk.Api;
@@ -55,173 +53,4 @@ CancellationToken cancellationToken
await process.DisposeAsync().ConfigureAwait(false);
}
}
-
- ///
- /// Receives an object (and all its sub-children) from the two provided s.
- ///
- /// Will first try and find objects using the (the faster transport)
- /// If not found, will attempt to copy the objects from the into the before deserialization
- ///
- ///
- /// If Transports are properly implemented, there is no hard distinction between what is a local or remote transport; it's still just an .
- /// So, for example, if you want to receive an object without actually writing it first to a local transport, you can just pass a as a local transport.
- /// This is not recommended, but shows what you can do. Another tidbit: the local transport does not need to be disk-bound; it can easily be an in . In memory transports are the fastest ones, but they're of limited use for larger datasets
- ///
- /// The id of the object to receive
- /// The remote transport (slower). If , will assume all objects are present in
- /// The local transport (faster). If , will use a default cache
- /// Action invoked on progress iterations
- ///
- /// Failed to retrieve objects from the provided transport(s)
- /// Deserialization of the requested object(s) failed
- /// requested cancel
- /// The requested Speckle Object
- public async Task Receive(
- string objectId,
- ITransport? remoteTransport = null,
- ITransport? localTransport = null,
- IProgress? onProgressAction = null,
- CancellationToken cancellationToken = default
- )
- {
- using var receiveActivity = activityFactory.Start("Operations.Receive");
- metricsFactory.CreateCounter("Receive").Add(1);
-
- if (remoteTransport != null)
- {
- receiveActivity?.SetTags("remoteTransportContext", remoteTransport.TransportContext);
- }
- receiveActivity?.SetTag("objectId", objectId);
-
- try
- {
- using IDisposable? d1 = UseDefaultTransportIfNull(localTransport, out localTransport);
- receiveActivity?.SetTags("localTransportContext", localTransport.TransportContext);
-
- var result = await ReceiveImpl(objectId, remoteTransport, localTransport, onProgressAction, cancellationToken)
- .ConfigureAwait(false);
-
- receiveActivity?.SetStatus(SdkActivityStatusCode.Ok);
- return result;
- }
- catch (Exception ex)
- {
- receiveActivity?.SetStatus(SdkActivityStatusCode.Error);
- receiveActivity?.RecordException(ex);
- throw;
- }
- }
-
- ///
- private async Task ReceiveImpl(
- string objectId,
- ITransport? remoteTransport,
- ITransport localTransport,
- IProgress? internalProgressAction,
- CancellationToken cancellationToken
- )
- {
- // Setup Local Transport
- localTransport.OnProgressAction = internalProgressAction;
- localTransport.CancellationToken = cancellationToken;
-
- // Setup Remote Transport
- if (remoteTransport is not null)
- {
- remoteTransport.OnProgressAction = internalProgressAction;
- remoteTransport.CancellationToken = cancellationToken;
- }
-
- // Setup Serializer
- SpeckleObjectDeserializer serializer = new()
- {
- ReadTransport = localTransport,
- OnProgressAction = internalProgressAction,
- CancellationToken = cancellationToken,
- BlobStorageFolder = (remoteTransport as IBlobCapableTransport)?.BlobStorageFolder,
- };
-
- // Try Local Receive
- string? objString = await LocalReceive(objectId, localTransport).ConfigureAwait(false);
-
- if (objString is null)
- {
- // Fall back to remote
- if (remoteTransport is null)
- {
- throw new TransportException(
- $"Could not find specified object using the local transport {localTransport.TransportName}, and you didn't provide a fallback remote from which to pull it."
- );
- }
-
- logger.LogDebug(
- "Cannot find object {objectId} in the local transport, hitting remote {transportName}",
- objectId,
- remoteTransport.TransportName
- );
-
- objString = await RemoteReceive(objectId, remoteTransport, localTransport).ConfigureAwait(false);
- }
-
- using var serializerActivity = activityFactory.Start();
-
- // Proceed to deserialize the object, now safely knowing that all its children are present in the local (fast) transport.
- return await DeserializeActivity(objString, serializer).ConfigureAwait(false);
- }
-
- ///
- /// Try and get the object from the local transport. If it's there, we assume all its children are there
- /// This assumption is hard-wired into the
- ///
- ///
- ///
- ///
- ///
- internal static async Task LocalReceive(string objectId, ITransport localTransport)
- {
- string? objString = await localTransport.GetObject(objectId).ConfigureAwait(false);
- if (objString is null)
- {
- return null;
- }
- return objString;
- }
-
- ///
- /// Copies the requested object and all its children from to
- ///
- ///
- ///
- ///
- ///
- ///
- /// Remote transport was not specified
- private static async Task RemoteReceive(
- string objectId,
- ITransport remoteTransport,
- ITransport localTransport
- )
- {
- var objString = await remoteTransport.CopyObjectAndChildren(objectId, localTransport).ConfigureAwait(false);
-
- // DON'T THINK THIS IS NEEDED CopyObjectAndChildren should call this
- // Wait for the local transport to finish "writing" - in this case, it signifies that the remote transport has done pushing copying objects into it. (TODO: I can see some scenarios where latency can screw things up, and we should rather wait on the remote transport).
- await localTransport.WriteComplete().ConfigureAwait(false);
-
- return objString;
- }
-
- private static IDisposable? UseDefaultTransportIfNull(ITransport? userTransport, out ITransport actualLocalTransport)
- {
- if (userTransport is not null)
- {
- actualLocalTransport = userTransport;
- return null;
- }
-
- //User did not specify a transport, default to SQLite
- SQLiteTransport defaultLocalTransport = new();
- actualLocalTransport = defaultLocalTransport;
- return defaultLocalTransport;
- }
}
diff --git a/src/Speckle.Sdk/Api/Operations/Operations.Send.cs b/src/Speckle.Sdk/Api/Operations/Operations.Send.cs
index eaad0b18..d48d7526 100644
--- a/src/Speckle.Sdk/Api/Operations/Operations.Send.cs
+++ b/src/Speckle.Sdk/Api/Operations/Operations.Send.cs
@@ -1,9 +1,5 @@
-using System.Diagnostics;
-using Microsoft.Extensions.Logging;
-using Speckle.Newtonsoft.Json.Linq;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
-using Speckle.Sdk.Serialisation;
using Speckle.Sdk.Serialisation.V2.Send;
using Speckle.Sdk.Transports;
@@ -63,186 +59,4 @@ CancellationToken cancellationToken
await process.DisposeAsync().ConfigureAwait(false);
}
}
-
- ///
- /// Sends a Speckle Object to the provided and (optionally) the default local cache
- ///
- ///
- ///
- /// When , an additional will be included
- /// The or was
- ///
- /// using ServerTransport destination = new(account, streamId);
- /// var (objectId, references) = await Send(mySpeckleObject, destination, true);
- ///
- public async Task<(string rootObjId, IReadOnlyDictionary convertedReferences)> Send(
- Base value,
- IServerTransport transport,
- bool useDefaultCache,
- IProgress? onProgressAction = null,
- CancellationToken cancellationToken = default
- )
- {
- if (transport is null)
- {
- throw new ArgumentNullException(nameof(transport), "Expected a transport to be explicitly specified");
- }
-
- List transports = new() { transport };
- using SQLiteTransport2? localCache = useDefaultCache
- ? new SQLiteTransport2(transport.StreamId) { TransportName = "LC2" }
- : null;
- if (localCache is not null)
- {
- transports.Add(localCache);
- }
-
- return await Send(value, transports, onProgressAction, cancellationToken).ConfigureAwait(false);
- }
-
- ///
- /// Sends a Speckle Object to the provided and (optionally) the default local cache
- ///
- ///
- ///
- /// When , an additional will be included
- /// The or was
- ///
- /// using ServerTransport destination = new(account, streamId);
- /// var (objectId, references) = await Send(mySpeckleObject, destination, true);
- ///
- public async Task<(string rootObjId, IReadOnlyDictionary convertedReferences)> Send(
- Base value,
- ITransport transport,
- bool useDefaultCache,
- IProgress? onProgressAction = null,
- CancellationToken cancellationToken = default
- )
- {
- if (transport is null)
- {
- throw new ArgumentNullException(nameof(transport), "Expected a transport to be explicitly specified");
- }
-
- List transports = new() { transport };
- using SQLiteTransport? localCache = useDefaultCache ? new SQLiteTransport { TransportName = "LC" } : null;
- if (localCache is not null)
- {
- transports.Add(localCache);
- }
-
- return await Send(value, transports, onProgressAction, cancellationToken).ConfigureAwait(false);
- }
-
- ///
- /// Sends a Speckle Object to the provided
- ///
- /// Only sends to the specified transports, the default local cache won't be used unless you also pass it in
- /// The id (hash) of the object sent
- /// The object you want to send
- /// Where you want to send them
- /// Action that gets triggered on every progress tick (keeps track of all transports)
- ///
- /// No transports were specified
- /// The was
- /// Serialization or Send operation was unsuccessful
- /// One or more failed to send
- /// The requested cancellation
- public async Task<(string rootObjId, IReadOnlyDictionary convertedReferences)> Send(
- Base value,
- IReadOnlyCollection transports,
- IProgress? onProgressAction = null,
- CancellationToken cancellationToken = default
- )
- {
-#pragma warning disable CA1510
- if (value is null)
- {
- throw new ArgumentNullException(nameof(value));
- }
-#pragma warning restore CA1510
-
- if (transports.Count == 0)
- {
- throw new ArgumentException("Expected at least on transport to be specified", nameof(transports));
- }
-
- // make sure all logs in the operation have the proper context
- metricsFactory.CreateCounter("Send").Add(1);
- using var activity = activityFactory.Start();
- activity?.SetTag("correlationId", Guid.NewGuid().ToString());
- {
- var sendTimer = Stopwatch.StartNew();
- logger.LogDebug("Starting send operation");
-
- SpeckleObjectSerializer serializerV2 = new(transports, onProgressAction, true, cancellationToken);
-
- foreach (var t in transports)
- {
- t.OnProgressAction = onProgressAction;
- t.CancellationToken = cancellationToken;
- t.BeginWrite();
- }
-
- try
- {
- var rootObjectId = await SerializerSend(value, serializerV2, cancellationToken).ConfigureAwait(false);
-
- sendTimer.Stop();
- activity?.SetTags("transportElapsedBreakdown", transports.ToDictionary(t => t.TransportName, t => t.Elapsed));
- activity?.SetTag(
- "note",
- "the elapsed summary doesn't need to add up to the total elapsed... Threading magic..."
- );
- activity?.SetTag("serializerElapsed", serializerV2.Elapsed);
- logger.LogDebug(
- "Finished sending objects after {elapsed}, result {objectId}",
- sendTimer.Elapsed.TotalSeconds,
- rootObjectId
- );
-
- return (rootObjectId, serializerV2.ObjectReferences);
- }
- catch (Exception ex) when (!ex.IsFatal())
- {
- logger.LogInformation(ex, "Send operation failed after {elapsed} seconds", sendTimer.Elapsed.TotalSeconds);
- if (ex is OperationCanceledException or SpeckleException)
- {
- throw;
- }
-
- throw new SpeckleException("Send operation was unsuccessful", ex);
- }
- finally
- {
- foreach (var t in transports)
- {
- t.EndWrite();
- }
- }
- }
- }
-
- ///
- internal static async Task SerializerSend(
- Base value,
- SpeckleObjectSerializer serializer,
- CancellationToken cancellationToken = default
- )
- {
- string obj = serializer.Serialize(value);
- Task[] transportAwaits = serializer.WriteTransports.Select(t => t.WriteComplete()).ToArray();
-
- cancellationToken.ThrowIfCancellationRequested();
-
- await Task.WhenAll(transportAwaits).ConfigureAwait(false);
-
- JToken? idToken = JObject.Parse(obj).GetValue("id");
- if (idToken == null)
- {
- throw new SpeckleException("Failed to get id of serialized object");
- }
-
- return idToken.ToString();
- }
}
diff --git a/src/Speckle.Sdk/Api/Operations/Operations.Serialize.cs b/src/Speckle.Sdk/Api/Operations/Operations.Serialize.cs
index 075776f2..ce189cc0 100644
--- a/src/Speckle.Sdk/Api/Operations/Operations.Serialize.cs
+++ b/src/Speckle.Sdk/Api/Operations/Operations.Serialize.cs
@@ -1,64 +1,27 @@
-using System.Diagnostics.CodeAnalysis;
-using Speckle.Newtonsoft.Json;
-using Speckle.Sdk.Logging;
+using System.Collections.Concurrent;
using Speckle.Sdk.Models;
using Speckle.Sdk.Serialisation;
-using Speckle.Sdk.Transports;
namespace Speckle.Sdk.Api;
public partial class Operations
{
- ///
- /// Serializes a given object.
- ///
- ///
- /// If you want to save and persist an object to Speckle Transport or Server,
- /// please use any of the "Send" methods.
- ///
- ///
- /// The object to serialise
- ///
- /// A json string representation of the object.
- public string Serialize(Base value, CancellationToken cancellationToken = default)
- {
- var serializer = new SpeckleObjectSerializer { CancellationToken = cancellationToken };
- return serializer.Serialize(value);
- }
+ private static readonly Id s_emptyId = new(Guid.NewGuid().ToString());
- ///
- /// Note: if you want to pull an object from a Speckle Transport or Server,
- /// please use
- ///
- ///
- /// The json string representation of a speckle object that you want to deserialize
- ///
- ///
- /// was null
- /// was not valid JSON
- /// cannot be deserialised to type
- /// contains closure references (see Remarks)
- public async Task DeserializeAsync(string value, CancellationToken cancellationToken = default)
+ public string Serialize(Base value, CancellationToken cancellationToken = default)
{
- var deserializer = new SpeckleObjectDeserializer { CancellationToken = cancellationToken };
- return await DeserializeActivity(value, deserializer).ConfigureAwait(false);
+ using var serializer2 = objectSerializerFactory.Create(cancellationToken);
+ var items = serializer2.Serialize(value);
+ return items.First().Item2.Value;
}
- ///
- private async Task DeserializeActivity([NotNull] string? objString, SpeckleObjectDeserializer deserializer)
+ public Task DeserializeAsync(string value, CancellationToken cancellationToken = default)
{
- using var activity = activityFactory.Start();
- try
- {
- Base res = await deserializer.DeserializeAsync(objString).ConfigureAwait(false);
- activity?.SetStatus(SdkActivityStatusCode.Ok);
- return res;
- }
- catch (Exception ex)
- {
- activity?.SetStatus(SdkActivityStatusCode.Error);
- activity?.RecordException(ex);
- throw;
- }
+ var deserializer = objectDeserializerFactory.Create(
+ s_emptyId,
+ new List(),
+ new ConcurrentDictionary()
+ );
+ return Task.FromResult(deserializer.Deserialize(new(value), cancellationToken));
}
}
diff --git a/src/Speckle.Sdk/Api/Operations/Operations.cs b/src/Speckle.Sdk/Api/Operations/Operations.cs
index cc5aea13..d2e0ce10 100644
--- a/src/Speckle.Sdk/Api/Operations/Operations.cs
+++ b/src/Speckle.Sdk/Api/Operations/Operations.cs
@@ -1,7 +1,8 @@
-using Microsoft.Extensions.Logging;
using Speckle.InterfaceGenerator;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Serialisation.V2;
+using Speckle.Sdk.Serialisation.V2.Receive;
+using Speckle.Sdk.Serialisation.V2.Send;
namespace Speckle.Sdk.Api;
@@ -12,9 +13,10 @@ namespace Speckle.Sdk.Api;
///
[GenerateAutoInterface]
public partial class Operations(
- ILogger logger,
ISdkActivityFactory activityFactory,
ISdkMetricsFactory metricsFactory,
+ IObjectSerializerFactory objectSerializerFactory,
+ IObjectDeserializerFactory objectDeserializerFactory,
ISerializeProcessFactory serializeProcessFactory,
IDeserializeProcessFactory deserializeProcessFactory
) : IOperations;
diff --git a/src/Speckle.Sdk/Caching/ModelCacheManager.cs b/src/Speckle.Sdk/Caching/ModelCacheManager.cs
index fbbe0688..289ddbe3 100644
--- a/src/Speckle.Sdk/Caching/ModelCacheManager.cs
+++ b/src/Speckle.Sdk/Caching/ModelCacheManager.cs
@@ -1,7 +1,6 @@
using Microsoft.Extensions.Logging;
using Speckle.InterfaceGenerator;
using Speckle.Sdk.Logging;
-using Speckle.Sdk.Transports;
namespace Speckle.Sdk.Caching;
@@ -29,7 +28,7 @@ public static string GetDbPath(string streamId)
catch (Exception ex)
when (ex is ArgumentException or IOException or UnauthorizedAccessException or NotSupportedException)
{
- throw new TransportException($"Path was invalid or could not be created {db}", ex);
+ throw new ModelManagerException($"Path was invalid or could not be created {db}", ex);
}
}
@@ -57,7 +56,7 @@ public void ClearCache()
catch (Exception ex)
when (ex is ArgumentException or IOException or UnauthorizedAccessException or NotSupportedException)
{
- throw new TransportException($"Cache folder could not be cleared: {CacheFolder}", ex);
+ throw new ModelManagerException($"Cache folder could not be cleared: {CacheFolder}", ex);
}
}
@@ -87,7 +86,7 @@ public long GetCacheSize()
catch (Exception ex)
when (ex is ArgumentException or IOException or UnauthorizedAccessException or NotSupportedException)
{
- throw new TransportException($"Cache folder size could not be determined: {CacheFolder}", ex);
+ throw new ModelManagerException($"Cache folder size could not be determined: {CacheFolder}", ex);
}
}
}
diff --git a/src/Speckle.Sdk/Caching/ModelManagerException.cs b/src/Speckle.Sdk/Caching/ModelManagerException.cs
new file mode 100644
index 00000000..499fd8bf
--- /dev/null
+++ b/src/Speckle.Sdk/Caching/ModelManagerException.cs
@@ -0,0 +1,12 @@
+namespace Speckle.Sdk.Caching;
+
+public class ModelManagerException : SpeckleException
+{
+ public ModelManagerException() { }
+
+ public ModelManagerException(string message)
+ : base(message) { }
+
+ public ModelManagerException(string message, Exception inner)
+ : base(message, inner) { }
+}
diff --git a/src/Speckle.Sdk/Models/Base.cs b/src/Speckle.Sdk/Models/Base.cs
index 1065e6b0..2978d152 100644
--- a/src/Speckle.Sdk/Models/Base.cs
+++ b/src/Speckle.Sdk/Models/Base.cs
@@ -2,11 +2,8 @@
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using Speckle.Newtonsoft.Json;
-using Speckle.Newtonsoft.Json.Linq;
using Speckle.Sdk.Helpers;
using Speckle.Sdk.Host;
-using Speckle.Sdk.Serialisation;
-using Speckle.Sdk.Transports;
namespace Speckle.Sdk.Models;
@@ -25,7 +22,7 @@ public class Base : DynamicBase, ISpeckleObject
private string? _type;
///
- /// A speckle object's id is an unique hash based on its properties. NOTE: this field will be null unless the object was deserialised from a source. Use the function to get it.
+ /// A speckle object's id is an unique hash based on its properties. NOTE: this field will be null unless the object was deserialised from a source.
///
public virtual string? id { get; set; }
@@ -50,26 +47,6 @@ public virtual string speckle_type
}
}
- ///
- /// Calculates the id (a unique hash) of this object.
- ///
- ///
- /// This method fully serialize the object and any referenced objects. This has a tangible cost and should be avoided.
- /// Objects retrieved from a already have a property populated
- /// The hash of a decomposed object differs from the hash of a non-decomposed object.
- ///
- /// If , will decompose the object in the process of hashing.
- /// the resulting id (hash)
- public string GetId(bool decompose = false)
- {
- //TODO remove me
- var transports = decompose ? [new MemoryTransport()] : Array.Empty();
- var serializer = new SpeckleObjectSerializer(transports);
-
- string obj = serializer.Serialize(this);
- return JObject.Parse(obj).GetValue(nameof(id))?.ToString() ?? string.Empty;
- }
-
///
/// Attempts to count the total number of detachable objects.
///
diff --git a/src/Speckle.Sdk/Serialisation/SpeckleObjectDeserializer.cs b/src/Speckle.Sdk/Serialisation/SpeckleObjectDeserializer.cs
deleted file mode 100644
index 549e8a34..00000000
--- a/src/Speckle.Sdk/Serialisation/SpeckleObjectDeserializer.cs
+++ /dev/null
@@ -1,334 +0,0 @@
-using System.Collections.Concurrent;
-using System.Diagnostics.CodeAnalysis;
-using System.Numerics;
-using System.Reflection;
-using Speckle.Newtonsoft.Json;
-using Speckle.Sdk.Common;
-using Speckle.Sdk.Host;
-using Speckle.Sdk.Models;
-using Speckle.Sdk.Serialisation.Utilities;
-using Speckle.Sdk.Transports;
-
-namespace Speckle.Sdk.Serialisation;
-
-public sealed class SpeckleObjectDeserializer
-{
- private volatile bool _isBusy;
- private readonly object?[] _invokeNull = [null];
-
- // id -> Base if already deserialized or id -> ValueTask if was handled by a bg thread
- private readonly ConcurrentDictionary _deserializedObjects = new(StringComparer.Ordinal);
- private long _total;
-
- ///
- /// Property that describes the type of the object.
- ///
- private const string TYPE_DISCRIMINATOR = nameof(Base.speckle_type);
-
- public CancellationToken CancellationToken { get; set; }
-
- ///
- /// The sync transport. This transport will be used synchronously.
- ///
- public ITransport ReadTransport { get; set; }
-
- public IProgress? OnProgressAction { get; set; }
-
- public string? BlobStorageFolder { get; set; }
-
- /// The JSON string of the object to be deserialized
- /// A typed object deserialized from the
- /// Thrown when
- /// was null
- /// cannot be deserialised to type
- // /// did not contain the required json objects (closures)
- public async ValueTask DeserializeAsync([NotNull] string? rootObjectJson)
- {
- if (_isBusy)
- {
- throw new InvalidOperationException(
- "A deserializer instance can deserialize only 1 object at a time. Consider creating multiple deserializer instances"
- );
- }
-
- try
- {
- if (rootObjectJson is null)
- {
- throw new ArgumentNullException(
- nameof(rootObjectJson),
- $"Cannot deserialize {nameof(rootObjectJson)}, value was null"
- );
- }
-
- _isBusy = true;
-
- var result = (Base)await DeserializeJsonAsyncInternal(rootObjectJson).NotNull().ConfigureAwait(false);
- return result;
- }
- finally
- {
- _isBusy = false;
- }
- }
-
- private async ValueTask DeserializeJsonAsyncInternal(string objectJson)
- {
- // Apparently this automatically parses DateTimes in strings if it matches the format:
- // JObject doc1 = JObject.Parse(objectJson);
-
- // This is equivalent code that doesn't parse datetimes:
- using JsonTextReader reader = SpeckleObjectSerializerPool.Instance.GetJsonTextReader(new StringReader(objectJson));
- reader.DateParseHandling = DateParseHandling.None;
-
- object? converted;
- try
- {
- reader.Read();
- converted = await ReadObjectAsync(reader, CancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex) when (!ex.IsFatal() && ex is not OperationCanceledException)
- {
- throw new SpeckleDeserializeException("Failed to deserialize", ex);
- }
-
- OnProgressAction?.Report(new ProgressArgs(ProgressEvent.DeserializeObject, _deserializedObjects.Count, _total));
-
- return converted;
- }
-
- //this should be buffered
- private async ValueTask> ReadArrayAsync(JsonReader reader, CancellationToken ct)
- {
- reader.Read();
- List retList = new();
- while (reader.TokenType != JsonToken.EndArray)
- {
- object? convertedValue = await ReadPropertyAsync(reader, ct).ConfigureAwait(false);
- if (convertedValue is DataChunk chunk)
- {
- retList.AddRange(chunk.data);
- }
- else
- {
- retList.Add(convertedValue);
- }
- reader.Read(); //goes to next
- }
- return retList;
- }
-
- private async ValueTask ReadObjectAsync(JsonReader reader, CancellationToken ct)
- {
- reader.Read();
- Dictionary dict = new();
- while (reader.TokenType != JsonToken.EndObject)
- {
- switch (reader.TokenType)
- {
- case JsonToken.PropertyName:
- {
- string propName = (reader.Value?.ToString()).NotNull();
- if (propName == "__closure")
- {
- reader.Read(); //goes to prop value
- var closures = ClosureParser.GetClosures(reader, CancellationToken);
- if (closures.Any())
- {
- _total = 0;
- foreach (var closure in closures)
- {
- string objId = closure.Item1;
- //don't do anything with return value but later check if null
- // https://linear.app/speckle/issue/CXPLA-54/when-deserializing-dont-allow-closures-that-arent-downloadable
- await TryGetDeserializedAsync(objId).ConfigureAwait(false);
- }
- }
-
- reader.Read(); //goes to next
- continue;
- }
- reader.Read(); //goes prop value
- object? convertedValue = await ReadPropertyAsync(reader, ct).ConfigureAwait(false);
- dict[propName] = convertedValue;
- reader.Read(); //goes to next
- }
- break;
- default:
- throw new InvalidOperationException($"Unknown {reader.ValueType} with {reader.Value}");
- }
- }
-
- if (!dict.TryGetValue(TYPE_DISCRIMINATOR, out object? speckleType))
- {
- return dict;
- }
-
- if (speckleType as string == "reference" && dict.TryGetValue("referencedId", out object? referencedId))
- {
- var objId = (string)referencedId.NotNull();
- object? deserialized = await TryGetDeserializedAsync(objId).ConfigureAwait(false);
- return deserialized;
- }
-
- return Dict2Base(dict);
- }
-
- private async ValueTask TryGetDeserializedAsync(string objId)
- {
- object? deserialized = null;
- _deserializedObjects.NotNull();
- if (_deserializedObjects.TryGetValue(objId, out object? o))
- {
- deserialized = o;
- }
-
- if (deserialized is Task task)
- {
- try
- {
- deserialized = await task.ConfigureAwait(false);
- }
- catch (AggregateException ex)
- {
- throw new SpeckleDeserializeException("Failed to deserialize reference object", ex);
- }
-
- _deserializedObjects.TryAdd(objId, deserialized);
- }
- if (deserialized is ValueTask valueTask)
- {
- try
- {
- deserialized = await valueTask.ConfigureAwait(false);
- }
- catch (AggregateException ex)
- {
- throw new SpeckleDeserializeException("Failed to deserialize reference object", ex);
- }
-
- _deserializedObjects.TryAdd(objId, deserialized);
- }
-
- if (deserialized != null)
- {
- return deserialized;
- }
-
- // This reference was not already deserialized. Do it now in sync mode
- string? objectJson = await ReadTransport.GetObject(objId).ConfigureAwait(false);
- if (objectJson is null)
- {
- return null;
- }
-
- deserialized = await DeserializeJsonAsyncInternal(objectJson).ConfigureAwait(false);
-
- _deserializedObjects.TryAdd(objId, deserialized);
-
- return deserialized;
- }
-
- private async ValueTask ReadPropertyAsync(JsonReader reader, CancellationToken ct)
- {
- ct.ThrowIfCancellationRequested();
- switch (reader.TokenType)
- {
- case JsonToken.Undefined:
- case JsonToken.Null:
- case JsonToken.None:
- return null;
- case JsonToken.Boolean:
- return (bool)reader.Value.NotNull();
- case JsonToken.Integer:
- if (reader.Value is long longValue)
- {
- return longValue;
- }
- if (reader.Value is BigInteger bitInt)
- {
- // This is behaviour carried over from v2 to facilitate large numbers from Python
- // This is quite hacky, as it's a bit questionable exactly what numbers are supported, and with what tolerance
- // For this reason, this can be considered undocumented behaviour, and is only for values within the range of a 64bit integer.
- return (double)bitInt;
- }
-
- throw new ArgumentException(
- $"Found an unsupported integer type {reader.Value?.GetType()} with value {reader.Value}"
- );
- case JsonToken.Float:
- return (double)reader.Value.NotNull();
- case JsonToken.String:
- return (string?)reader.Value.NotNull();
- case JsonToken.Date:
- return (DateTime)reader.Value.NotNull();
- case JsonToken.StartArray:
- return await ReadArrayAsync(reader, ct).ConfigureAwait(false);
- case JsonToken.StartObject:
- var dict = await ReadObjectAsync(reader, ct).ConfigureAwait(false);
- return dict;
-
- default:
- throw new ArgumentException("Json value not supported: " + reader.ValueType);
- }
- }
-
- private Base Dict2Base(Dictionary dictObj)
- {
- string typeName = (string)dictObj[TYPE_DISCRIMINATOR].NotNull();
- Type type = TypeLoader.GetType(typeName);
- Base baseObj = (Base)Activator.CreateInstance(type).NotNull();
-
- dictObj.Remove(TYPE_DISCRIMINATOR);
- dictObj.Remove("__closure");
-
- var staticProperties = TypeCache.GetTypeProperties(typeName);
- foreach (var entry in dictObj)
- {
- if (staticProperties.TryGetValue(entry.Key, out PropertyInfo? value) && value.CanWrite)
- {
- if (entry.Value == null)
- {
- // Check for JsonProperty(NullValueHandling = NullValueHandling.Ignore) attribute
- JsonPropertyAttribute? attr = TypeLoader.GetJsonPropertyAttribute(value);
- if (attr is { NullValueHandling: NullValueHandling.Ignore })
- {
- continue;
- }
- }
-
- Type targetValueType = value.PropertyType;
- bool conversionOk = ValueConverter.ConvertValue(targetValueType, entry.Value, out object? convertedValue);
- if (conversionOk)
- {
- value.SetValue(baseObj, convertedValue);
- }
- else
- {
- // Cannot convert the value in the json to the static property type
- throw new SpeckleDeserializeException(
- $"Cannot deserialize {entry.Value?.GetType().FullName} to {targetValueType.FullName}"
- );
- }
- }
- else
- {
- // No writable property with this name
- CallSiteCache.SetValue(entry.Key, baseObj, entry.Value);
- }
- }
-
- if (baseObj is Blob bb && BlobStorageFolder != null)
- {
- bb.filePath = bb.GetLocalDestinationPath(BlobStorageFolder);
- }
-
- var onDeserializedCallbacks = TypeCache.GetOnDeserializedCallbacks(typeName);
- foreach (MethodInfo onDeserialized in onDeserializedCallbacks)
- {
- onDeserialized.Invoke(baseObj, _invokeNull);
- }
-
- return baseObj;
- }
-}
diff --git a/src/Speckle.Sdk/Serialisation/SpeckleObjectSerializer.cs b/src/Speckle.Sdk/Serialisation/SpeckleObjectSerializer.cs
deleted file mode 100644
index 618f7e6f..00000000
--- a/src/Speckle.Sdk/Serialisation/SpeckleObjectSerializer.cs
+++ /dev/null
@@ -1,541 +0,0 @@
-using System.Collections;
-using System.Diagnostics;
-using System.Drawing;
-using System.Globalization;
-using System.Reflection;
-using Speckle.DoubleNumerics;
-using Speckle.Newtonsoft.Json;
-using Speckle.Sdk.Common;
-using Speckle.Sdk.Dependencies;
-using Speckle.Sdk.Helpers;
-using Speckle.Sdk.Models;
-using Speckle.Sdk.Serialisation.Utilities;
-using Speckle.Sdk.Transports;
-
-namespace Speckle.Sdk.Serialisation;
-
-public class SpeckleObjectSerializer
-{
- private readonly Stopwatch _stopwatch = new();
- private volatile bool _isBusy;
- private List> _parentClosures = new();
- private HashSet _parentObjects = new();
- private readonly Dictionary> _typedPropertiesCache = new();
- private readonly IProgress? _onProgressAction;
-
- private readonly bool _trackDetachedChildren;
- private int _serializedCount;
-
- ///
- /// Keeps track of all detached children created during serialisation that have an applicationId (provided this serializer instance has been told to track detached children).
- /// This is currently used to cache previously converted objects and avoid their conversion if they haven't changed. See the DUI3 send bindings in rhino or another host app.
- ///
- public Dictionary ObjectReferences { get; } = new();
-
- /// The sync transport. This transport will be used synchronously.
- public IReadOnlyCollection WriteTransports { get; }
-
- public CancellationToken CancellationToken { get; set; }
-
- /// The current total elapsed time spent serializing
- public TimeSpan Elapsed => _stopwatch.Elapsed;
-
- public SpeckleObjectSerializer()
- : this(Array.Empty()) { }
-
- ///
- /// Creates a new Serializer instance.
- ///
- /// The transports detached children should be persisted to.
- /// Used to track progress.
- /// Whether to store all detachable objects while serializing. They can be retrieved via post serialization.
- ///
- public SpeckleObjectSerializer(
- IReadOnlyCollection writeTransports,
- IProgress? onProgressAction = null,
- bool trackDetachedChildren = false,
- CancellationToken cancellationToken = default
- )
- {
- WriteTransports = writeTransports;
- _onProgressAction = onProgressAction;
- CancellationToken = cancellationToken;
- _trackDetachedChildren = trackDetachedChildren;
- }
-
- /// The object to serialize
- /// The serialized JSON
- /// The serializer is busy (already serializing an object)
- /// Failed to save object in one or more
- /// Failed to extract (pre-serialize) properties from the
- /// One or more 's cancellation token requested cancel
- public string Serialize(Base baseObj)
- {
- if (_isBusy)
- {
- throw new InvalidOperationException(
- "A serializer instance can serialize only 1 object at a time. Consider creating multiple serializer instances"
- );
- }
-
- try
- {
- _stopwatch.Start();
- _isBusy = true;
- try
- {
- var result = SerializeBase(baseObj, true).NotNull();
- StoreObject(result.Id.NotNull(), result.Json);
- return result.Json.Value;
- }
- catch (Exception ex) when (!ex.IsFatal() && ex is not OperationCanceledException)
- {
- throw new SpeckleSerializeException($"Failed to extract (pre-serialize) properties from the {baseObj}", ex);
- }
- }
- finally
- {
- _parentClosures = new List>(); // cleanup in case of exceptions
- _parentObjects = new HashSet();
- _isBusy = false;
- _stopwatch.Stop();
- }
- }
-
- // `Preserialize` means transforming all objects into the final form that will appear in json, with basic .net objects
- // (primitives, lists and dictionaries with string keys)
- private void SerializeProperty(
- object? obj,
- JsonWriter writer,
- bool computeClosures = false,
- PropertyAttributeInfo inheritedDetachInfo = default
- )
- {
- CancellationToken.ThrowIfCancellationRequested();
-
- if (obj == null)
- {
- writer.WriteNull();
- return;
- }
-
- if (obj.GetType().IsPrimitive || obj is string)
- {
- writer.WriteValue(obj);
- return;
- }
-
- switch (obj)
- {
- // Start with object references so they're not captured by the Base class case below
- // Note: this change was needed as we've made the ObjectReference type inherit from Base for
- // the purpose of the "do not convert unchanged previously converted objects" POC.
- case ObjectReference r:
- Dictionary ret = new()
- {
- ["speckle_type"] = r.speckle_type,
- ["referencedId"] = r.referencedId,
- ["__closure"] = r.closure,
- };
- if (r.closure is not null)
- {
- foreach (var kvp in r.closure)
- {
- UpdateParentClosures(kvp.Key);
- }
- }
- UpdateParentClosures(r.referencedId);
- SerializeProperty(ret, writer);
- break;
- case Base b:
- var result = SerializeBase(b, computeClosures, inheritedDetachInfo);
- if (result is not null)
- {
- writer.WriteRawValue(result.Value.Json.Value);
- }
- else
- {
- writer.WriteNull();
- }
- break;
- case IDictionary d:
- {
- writer.WriteStartObject();
-
- foreach (DictionaryEntry kvp in d)
- {
- if (kvp.Key is not string key)
- {
- throw new ArgumentException(
- "Serializing dictionaries that are not string based keys is not supported",
- nameof(obj)
- );
- }
-
- writer.WritePropertyName(key);
- SerializeProperty(kvp.Value, writer, inheritedDetachInfo: inheritedDetachInfo);
- }
- writer.WriteEndObject();
- }
- break;
- case ICollection e:
- {
- writer.WriteStartArray();
- foreach (object? element in e)
- {
- SerializeProperty(element, writer, inheritedDetachInfo: inheritedDetachInfo);
- }
- writer.WriteEndArray();
- }
- break;
- case Enum:
- writer.WriteValue((int)obj);
- break;
- // Support for simple types
- case Guid g:
- writer.WriteValue(g.ToString());
- break;
- case Color c:
- writer.WriteValue(c.ToArgb());
- break;
- case DateTime t:
- writer.WriteValue(t.ToString("o", CultureInfo.InvariantCulture));
- break;
- case Matrix4x4 md:
- writer.WriteStartArray();
-
- writer.WriteValue(md.M11);
- writer.WriteValue(md.M12);
- writer.WriteValue(md.M13);
- writer.WriteValue(md.M14);
- writer.WriteValue(md.M21);
- writer.WriteValue(md.M22);
- writer.WriteValue(md.M23);
- writer.WriteValue(md.M24);
- writer.WriteValue(md.M31);
- writer.WriteValue(md.M32);
- writer.WriteValue(md.M33);
- writer.WriteValue(md.M34);
- writer.WriteValue(md.M41);
- writer.WriteValue(md.M42);
- writer.WriteValue(md.M43);
- writer.WriteValue(md.M44);
- writer.WriteEndArray();
- break;
- //BACKWARDS COMPATIBILITY: matrix4x4 changed from System.Numerics float to System.DoubleNumerics double in release 2.16
- case System.Numerics.Matrix4x4:
- throw new ArgumentException("Please use Speckle.DoubleNumerics.Matrix4x4 instead", nameof(obj));
- default:
- throw new ArgumentException($"Unsupported value in serialization: {obj.GetType()}", nameof(obj));
- }
- }
-
- internal SerializationResult? SerializeBase(
- Base baseObj,
- bool computeClosures = false,
- PropertyAttributeInfo inheritedDetachInfo = default
- )
- {
- // handle circular references
- bool alreadySerialized = !_parentObjects.Add(baseObj);
- if (alreadySerialized)
- {
- return null;
- }
-
- Dictionary closure = new();
- if (computeClosures || inheritedDetachInfo.IsDetachable || baseObj is Blob)
- {
- _parentClosures.Add(closure);
- }
-
- var stringBuilder = Pools.StringBuilders.Get();
- using var writer = new StringWriter();
- using var jsonWriter = SpeckleObjectSerializerPool.Instance.GetJsonTextWriter(writer);
- var id = SerializeBaseObject(baseObj, jsonWriter, closure);
- var json = new Json(writer.ToString());
- Pools.StringBuilders.Return(stringBuilder);
-
- if (computeClosures || inheritedDetachInfo.IsDetachable || baseObj is Blob)
- {
- _parentClosures.RemoveAt(_parentClosures.Count - 1);
- }
-
- _parentObjects.Remove(baseObj);
-
- if (baseObj is Blob myBlob)
- {
- StoreBlob(myBlob);
- UpdateParentClosures($"blob:{id}");
- return new(json, id);
- }
-
- if (inheritedDetachInfo.IsDetachable && WriteTransports.Count > 0)
- {
- StoreObject(id, json);
-
- var json2 = ReferenceGenerator.CreateReference(id);
- UpdateParentClosures(id.Value);
-
- _onProgressAction?.Report(new(ProgressEvent.SerializeObject, ++_serializedCount, null));
-
- // add to obj refs to return
- if (baseObj.applicationId != null && _trackDetachedChildren) // && baseObj is not DataChunk && baseObj is not Abstract) // not needed, as data chunks will never have application ids, and abstract objs are not really used.
- {
- ObjectReferences[baseObj.applicationId] = new ObjectReference()
- {
- referencedId = id.Value,
- applicationId = baseObj.applicationId,
- closure = closure,
- };
- }
- return new(json2, null);
- }
- return new(json, id);
- }
-
- private Dictionary ExtractAllProperties(Base baseObj)
- {
- IReadOnlyList<(PropertyInfo, PropertyAttributeInfo)> typedProperties = GetTypedPropertiesWithCache(baseObj);
- IReadOnlyCollection dynamicProperties = baseObj.DynamicPropertyKeys;
-
- // propertyName -> (originalValue, isDetachable, isChunkable, chunkSize)
- Dictionary allProperties = new(
- typedProperties.Count + dynamicProperties.Count
- );
-
- // Construct `allProperties`: Add typed properties
- foreach ((PropertyInfo propertyInfo, PropertyAttributeInfo detachInfo) in typedProperties)
- {
- object? baseValue = propertyInfo.GetValue(baseObj);
- allProperties[propertyInfo.Name] = (baseValue, detachInfo);
- }
-
- // Construct `allProperties`: Add dynamic properties
- foreach (string propName in dynamicProperties)
- {
- if (propName.StartsWith("__"))
- {
- continue;
- }
-
- object? baseValue = baseObj[propName];
-
- bool isDetachable = PropNameValidator.IsDetached(propName);
-
- int chunkSize = 1000;
- bool isChunkable = isDetachable && PropNameValidator.IsChunkable(propName, out chunkSize);
-
- allProperties[propName] = (baseValue, new PropertyAttributeInfo(isDetachable, isChunkable, chunkSize, null));
- }
-
- return allProperties;
- }
-
- private Id SerializeBaseObject(Base baseObj, JsonWriter writer, IReadOnlyDictionary closure)
- {
- var allProperties = ExtractAllProperties(baseObj);
-
- if (baseObj is not Blob)
- {
- writer = new SerializerIdWriter(writer);
- }
-
- writer.WriteStartObject();
- // Convert all properties
- foreach (var prop in allProperties)
- {
- if (prop.Value.info.JsonPropertyInfo is { NullValueHandling: NullValueHandling.Ignore })
- {
- continue;
- }
-
- writer.WritePropertyName(prop.Key);
- SerializeProperty(prop.Value.value, writer, prop.Value.info);
- }
-
- Id id;
- if (writer is SerializerIdWriter serializerIdWriter)
- {
- (var json, writer) = serializerIdWriter.FinishIdWriter();
- id = IdGenerator.ComputeId(json);
- }
- else
- {
- id = new Id(((Blob)baseObj).id.NotNull());
- }
- writer.WritePropertyName("id");
- writer.WriteValue(id.Value);
- baseObj.id = id.Value;
-
- if (closure.Count > 0)
- {
- writer.WritePropertyName("__closure");
- writer.WriteStartObject();
- foreach (var c in closure)
- {
- writer.WritePropertyName(c.Key);
- writer.WriteValue(c.Value);
- }
- writer.WriteEndObject();
- }
-
- writer.WriteEndObject();
- return id;
- }
-
- private void SerializeProperty(object? baseValue, JsonWriter jsonWriter, PropertyAttributeInfo detachInfo)
- {
- // If there are no WriteTransports, keep everything attached.
- if (WriteTransports.Count == 0)
- {
- SerializeProperty(baseValue, jsonWriter, inheritedDetachInfo: detachInfo);
- return;
- }
-
- if (baseValue is IEnumerable chunkableCollection && detachInfo.IsChunkable)
- {
- List chunks = new();
- DataChunk crtChunk = new() { data = new List(detachInfo.ChunkSize) };
-
- foreach (object element in chunkableCollection)
- {
- crtChunk.data.Add(element);
- if (crtChunk.data.Count >= detachInfo.ChunkSize)
- {
- chunks.Add(crtChunk);
- crtChunk = new DataChunk { data = new List(detachInfo.ChunkSize) };
- }
- }
-
- if (crtChunk.data.Count > 0)
- {
- chunks.Add(crtChunk);
- }
- SerializeProperty(chunks, jsonWriter, inheritedDetachInfo: new PropertyAttributeInfo(true, false, 0, null));
- return;
- }
-
- SerializeProperty(baseValue, jsonWriter, inheritedDetachInfo: detachInfo);
- }
-
- private void UpdateParentClosures(string objectId)
- {
- for (int parentLevel = 0; parentLevel < _parentClosures.Count; parentLevel++)
- {
- int childDepth = _parentClosures.Count - parentLevel;
- if (!_parentClosures[parentLevel].TryGetValue(objectId, out int currentValue))
- {
- currentValue = childDepth;
- }
-
- _parentClosures[parentLevel][objectId] = Math.Min(currentValue, childDepth);
- }
- }
-
- private void StoreObject(Id objectId, Json objectJson)
- {
- _stopwatch.Stop();
- foreach (var transport in WriteTransports)
- {
- transport.SaveObject(objectId.Value, objectJson.Value);
- }
-
- _stopwatch.Start();
- }
-
- private void StoreBlob(Blob obj)
- {
- bool hasBlobTransport = false;
-
- _stopwatch.Stop();
-
- foreach (var transport in WriteTransports)
- {
- if (transport is IBlobCapableTransport blobTransport)
- {
- hasBlobTransport = true;
- blobTransport.SaveBlob(obj);
- }
- }
-
- _stopwatch.Start();
- if (!hasBlobTransport)
- {
- throw new InvalidOperationException(
- "Object tree contains a Blob (file), but the serializer has no blob saving capable transports."
- );
- }
- }
-
- // (propertyInfo, isDetachable, isChunkable, chunkSize, JsonPropertyAttribute)
- private IReadOnlyList<(PropertyInfo, PropertyAttributeInfo)> GetTypedPropertiesWithCache(Base baseObj)
- {
- Type type = baseObj.GetType();
-
- if (
- _typedPropertiesCache.TryGetValue(
- type.FullName.NotNull(),
- out List<(PropertyInfo, PropertyAttributeInfo)>? cached
- )
- )
- {
- return cached;
- }
-
- var typedProperties = baseObj.GetInstanceMembers().ToList();
- List<(PropertyInfo, PropertyAttributeInfo)> ret = new(typedProperties.Count);
-
- foreach (PropertyInfo typedProperty in typedProperties)
- {
- if (typedProperty.Name.StartsWith("__") || typedProperty.Name == "id")
- {
- continue;
- }
-
- bool jsonIgnore = typedProperty.IsDefined(typeof(JsonIgnoreAttribute), false);
- if (jsonIgnore)
- {
- continue;
- }
-
- _ = typedProperty.GetValue(baseObj);
-
- List detachableAttributes = typedProperty
- .GetCustomAttributes(true)
- .ToList();
- List chunkableAttributes = typedProperty
- .GetCustomAttributes(true)
- .ToList();
- bool isDetachable = detachableAttributes.Count > 0 && detachableAttributes[0].Detachable;
- bool isChunkable = chunkableAttributes.Count > 0;
- int chunkSize = isChunkable ? chunkableAttributes[0].MaxObjCountPerChunk : 1000;
- JsonPropertyAttribute? jsonPropertyAttribute = typedProperty.GetCustomAttribute();
- ret.Add((typedProperty, new PropertyAttributeInfo(isDetachable, isChunkable, chunkSize, jsonPropertyAttribute)));
- }
-
- _typedPropertiesCache[type.FullName] = ret;
- return ret;
- }
-
- internal readonly struct PropertyAttributeInfo
- {
- public PropertyAttributeInfo(
- bool isDetachable,
- bool isChunkable,
- int chunkSize,
- JsonPropertyAttribute? jsonPropertyAttribute
- )
- {
- IsDetachable = isDetachable || isChunkable;
- IsChunkable = isChunkable;
- ChunkSize = chunkSize;
- JsonPropertyInfo = jsonPropertyAttribute;
- }
-
- public readonly bool IsDetachable;
- public readonly bool IsChunkable;
- public readonly int ChunkSize;
- public readonly JsonPropertyAttribute? JsonPropertyInfo;
- }
-}
diff --git a/src/Speckle.Sdk/ServiceRegistration.cs b/src/Speckle.Sdk/ServiceRegistration.cs
index 94e147cc..8d62e0eb 100644
--- a/src/Speckle.Sdk/ServiceRegistration.cs
+++ b/src/Speckle.Sdk/ServiceRegistration.cs
@@ -12,8 +12,6 @@
using Speckle.Sdk.Serialisation.V2.Receive;
using Speckle.Sdk.Serialisation.V2.Send;
using Speckle.Sdk.SQLite;
-using Speckle.Sdk.Transports;
-using Speckle.Sdk.Transports.ServerUtils;
namespace Speckle.Sdk;
@@ -82,9 +80,7 @@ SpeckleSdkOptions speckleSdkOptions
serviceCollection.TryAddSingleton();
serviceCollection.AddMatchingInterfacesAsTransient(
Assembly.GetExecutingAssembly(),
- typeof(ServerTransport),
typeof(Account),
- typeof(ServerApi),
typeof(SqLiteJsonCacheManager),
typeof(ServerObjectManager),
typeof(BlobApi),
diff --git a/src/Speckle.Sdk/Transports/Exceptions.cs b/src/Speckle.Sdk/Transports/Exceptions.cs
deleted file mode 100644
index 90cd81b7..00000000
--- a/src/Speckle.Sdk/Transports/Exceptions.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-namespace Speckle.Sdk.Transports;
-
-public class TransportException : SpeckleException
-{
- public ITransport? Transport { get; }
-
- public TransportException(ITransport? transport, string? message, Exception? innerException = null)
- : this(message, innerException)
- {
- Transport = transport;
- }
-
- public TransportException() { }
-
- public TransportException(string? message)
- : base(message) { }
-
- public TransportException(string? message, Exception? innerException)
- : base(message, innerException) { }
-}
diff --git a/src/Speckle.Sdk/Transports/IServerTransport.cs b/src/Speckle.Sdk/Transports/IServerTransport.cs
deleted file mode 100644
index 41ecb700..00000000
--- a/src/Speckle.Sdk/Transports/IServerTransport.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Speckle.Sdk.Transports;
-
-public interface IServerTransport : IDisposable, ITransport, IBlobCapableTransport, ICloneable
-{
- Credentials.Account Account { get; }
- Uri BaseUri { get; }
- string StreamId { get; }
- int TimeoutSeconds { get; set; }
-}
diff --git a/src/Speckle.Sdk/Transports/ITransport.cs b/src/Speckle.Sdk/Transports/ITransport.cs
deleted file mode 100644
index 6d315966..00000000
--- a/src/Speckle.Sdk/Transports/ITransport.cs
+++ /dev/null
@@ -1,94 +0,0 @@
-using Speckle.Sdk.Models;
-
-namespace Speckle.Sdk.Transports;
-
-///
-/// Interface defining the contract for transport implementations.
-///
-public interface ITransport
-{
- ///
- /// Human readable name for the transport
- ///
- public string TransportName { get; set; }
-
- ///
- /// Extra descriptor properties of the given transport.
- ///
- public Dictionary TransportContext { get; }
-
- ///
- /// Show how much time the transport was busy for.
- ///
- public TimeSpan Elapsed { get; }
-
- ///
- /// Should be checked often and gracefully stop all in progress sending if requested.
- ///
- public CancellationToken CancellationToken { get; set; }
-
- ///
- /// Used to report progress during the transport's longer operations.
- ///
- public IProgress? OnProgressAction { get; set; }
-
- ///
- /// Signals to the transport that writes are about to begin.
- ///
- public void BeginWrite();
-
- ///
- /// Signals to the transport that no more items will need to be written.
- ///
- public void EndWrite();
-
- ///
- /// Saves an object.
- ///
- /// The hash of the object.
- /// The full string representation of the object
- /// Failed to save object
- /// requested cancel
- public void SaveObject(string id, string serializedObject);
-
- ///
- /// Awaitable method to figure out whether writing is completed.
- ///
- ///
- public Task WriteComplete();
-
- /// The object's hash.
- /// The serialized object data, or if the transport cannot find the object
- /// requested cancel
- public Task GetObject(string id);
-
- ///
- /// Copies the parent object and all its children to the provided transport.
- ///
- /// The id of the object you want to copy.
- /// The transport you want to copy the object to.
- /// The string representation of the root object.
- /// The provided arguments are not valid
- /// The transport could not complete the operation
- /// requested cancel
- public Task CopyObjectAndChildren(string id, ITransport targetTransport);
-
- ///
- /// Checks if objects are present in the transport
- ///
- /// List of object ids to check
- /// A dictionary with the specified object ids as keys and boolean values, whether each object is present in the transport or not
- /// The transport could not complete the operation
- /// requested cancel
- public Task> HasObjects(IReadOnlyList objectIds);
-}
-
-public interface IBlobCapableTransport
-{
- public string BlobStorageFolder { get; }
-
- public void SaveBlob(Blob obj);
-
- // NOTE: not needed, should be implemented in "CopyObjectsAndChildren"
- //public void GetBlob(Blob obj);
-}
diff --git a/src/Speckle.Sdk/Transports/MemoryTransport.cs b/src/Speckle.Sdk/Transports/MemoryTransport.cs
deleted file mode 100644
index cc6aa1d5..00000000
--- a/src/Speckle.Sdk/Transports/MemoryTransport.cs
+++ /dev/null
@@ -1,142 +0,0 @@
-using System.Collections.Concurrent;
-using System.Diagnostics;
-using Speckle.Sdk.Logging;
-using Speckle.Sdk.Models;
-
-namespace Speckle.Sdk.Transports;
-
-///
-/// An in memory storage of speckle objects.
-///
-public sealed class MemoryTransport : ITransport, ICloneable, IBlobCapableTransport
-{
- private readonly string _basePath;
- private readonly string _applicationName;
- private readonly bool _blobStorageEnabled;
- public IReadOnlyDictionary Objects => _objects;
- private readonly ConcurrentDictionary _objects;
-
- public MemoryTransport(
- ConcurrentDictionary? objects = null,
- bool blobStorageEnabled = false,
- string? basePath = null,
- string? applicationName = null
- )
- {
- _objects = objects ?? new ConcurrentDictionary(StringComparer.Ordinal);
- _blobStorageEnabled = blobStorageEnabled;
- _basePath = basePath ?? SpecklePathProvider.UserApplicationDataPath();
- _applicationName = applicationName ?? "Speckle";
- var dir = Path.Combine(_basePath, _applicationName);
- try
- {
- Directory.CreateDirectory(dir); //ensure dir is there
- }
- catch (Exception ex)
- when (ex is ArgumentException or IOException or UnauthorizedAccessException or NotSupportedException)
- {
- throw new TransportException($"Path was invalid or could not be created {dir}", ex);
- }
- }
-
- public object Clone()
- {
- return new MemoryTransport(_objects, _blobStorageEnabled, _basePath, _applicationName)
- {
- TransportName = TransportName,
- OnProgressAction = OnProgressAction,
- CancellationToken = CancellationToken,
- SavedObjectCount = SavedObjectCount,
- };
- }
-
- public CancellationToken CancellationToken { get; set; }
-
- public string TransportName { get; set; } = "Memory";
-
- public IProgress? OnProgressAction { get; set; }
-
- public int SavedObjectCount { get; private set; }
-
- public Dictionary TransportContext =>
- new()
- {
- { "name", TransportName },
- { "type", GetType().Name },
- { "basePath", _basePath },
- { "applicationName", _applicationName },
- { "blobStorageFolder", BlobStorageFolder },
- };
-
- public TimeSpan Elapsed { get; private set; } = TimeSpan.Zero;
-
- public void BeginWrite()
- {
- SavedObjectCount = 0;
- }
-
- public void EndWrite() { }
-
- public void SaveObject(string id, string serializedObject)
- {
- CancellationToken.ThrowIfCancellationRequested();
- var stopwatch = Stopwatch.StartNew();
-
- _objects[id] = serializedObject;
-
- SavedObjectCount++;
- stopwatch.Stop();
- Elapsed += stopwatch.Elapsed;
- }
-
- public Task GetObject(string id)
- {
- var stopwatch = Stopwatch.StartNew();
- var ret = Objects.TryGetValue(id, out string? o) ? o : null;
- stopwatch.Stop();
- Elapsed += stopwatch.Elapsed;
- return Task.FromResult(ret);
- }
-
- public async Task CopyObjectAndChildren(string id, ITransport targetTransport)
- {
- string res = await TransportHelpers
- .CopyObjectAndChildrenAsync(id, this, targetTransport, CancellationToken)
- .ConfigureAwait(false);
- return res;
- }
-
- public Task WriteComplete()
- {
- return Task.CompletedTask;
- }
-
- public Task> HasObjects(IReadOnlyList objectIds)
- {
- Dictionary ret = new(objectIds.Count);
- foreach (string objectId in objectIds)
- {
- ret[objectId] = Objects.ContainsKey(objectId);
- }
-
- return Task.FromResult(ret);
- }
-
- public override string ToString()
- {
- return $"Memory Transport {TransportName}";
- }
-
- public string BlobStorageFolder => SpecklePathProvider.BlobStoragePath(Path.Combine(_basePath, _applicationName));
-
- public void SaveBlob(Blob obj)
- {
- if (!_blobStorageEnabled)
- {
- return;
- }
- var blobPath = obj.originalPath;
- var targetPath = obj.GetLocalDestinationPath(BlobStorageFolder);
- File.Copy(blobPath, targetPath, true);
- }
-}
diff --git a/src/Speckle.Sdk/Transports/SQLiteTransport.cs b/src/Speckle.Sdk/Transports/SQLiteTransport.cs
deleted file mode 100644
index 46b2985d..00000000
--- a/src/Speckle.Sdk/Transports/SQLiteTransport.cs
+++ /dev/null
@@ -1,433 +0,0 @@
-using System.Collections.Concurrent;
-using System.Diagnostics;
-using System.Text;
-using System.Timers;
-using Microsoft.Data.Sqlite;
-using Speckle.Sdk.Logging;
-using Speckle.Sdk.Models;
-using Timer = System.Timers.Timer;
-
-namespace Speckle.Sdk.Transports;
-
-public sealed class SQLiteTransport : IDisposable, ICloneable, ITransport, IBlobCapableTransport
-{
- private bool _isWriting;
- private const int MAX_TRANSACTION_SIZE = 1000;
- private const int POLL_INTERVAL = 500;
-
- private ConcurrentQueue<(string id, string serializedObject, int byteCount)> _queue = new();
-
- ///
- /// Timer that ensures queue is consumed if less than MAX_TRANSACTION_SIZE objects are being sent.
- ///
- private readonly Timer _writeTimer;
-
- ///
- /// Connects to an SQLite DB at { }/{ }/{ }.db
- /// Will attempt to create db + directory structure as needed
- ///
- /// defaults to if
- /// defaults to "Speckle" if
- /// defaults to "Data" if
- /// Failed to initialize a connection to the db
- /// Path was invalid or could not be created
- public SQLiteTransport(string? basePath = null, string? applicationName = null, string? scope = null)
- {
- _basePath = basePath ?? SpecklePathProvider.UserApplicationDataPath();
- _applicationName = applicationName ?? "Speckle";
- _scope = scope ?? "Data";
-
- try
- {
- var dir = Path.Combine(_basePath, _applicationName);
- _rootPath = Path.Combine(dir, $"{_scope}.db");
-
- Directory.CreateDirectory(dir); //ensure dir is there
- }
- catch (Exception ex)
- when (ex is ArgumentException or IOException or UnauthorizedAccessException or NotSupportedException)
- {
- throw new TransportException($"Path was invalid or could not be created {_rootPath}", ex);
- }
-
- _connectionString = $"Data Source={_rootPath};";
-
- Initialize();
-
- _writeTimer = new Timer
- {
- AutoReset = true,
- Enabled = false,
- Interval = POLL_INTERVAL,
- };
- _writeTimer.Elapsed += WriteTimerElapsed;
- }
-
- private readonly string _rootPath;
-
- private readonly string _basePath;
- private readonly string _applicationName;
- private readonly string _scope;
- private readonly string _connectionString;
-
- private SqliteConnection Connection { get; set; }
- private readonly SemaphoreSlim _connectionLock = new(1, 1);
-
- public string BlobStorageFolder => SpecklePathProvider.BlobStoragePath(Path.Combine(_basePath, _applicationName));
-
- public void SaveBlob(Blob obj)
- {
- var blobPath = obj.originalPath;
- var targetPath = obj.GetLocalDestinationPath(BlobStorageFolder);
- File.Copy(blobPath, targetPath, true);
- }
-
- public object Clone()
- {
- return new SQLiteTransport(_basePath, _applicationName, _scope)
- {
- OnProgressAction = OnProgressAction,
- CancellationToken = CancellationToken,
- };
- }
-
- public void Dispose()
- {
- // TODO: Check if it's still writing?
- Connection.Close();
- Connection.Dispose();
- _writeTimer.Dispose();
- _connectionLock.Dispose();
- }
-
- public string TransportName { get; set; } = "SQLite";
-
- public Dictionary TransportContext =>
- new()
- {
- { "name", TransportName },
- { "type", GetType().Name },
- { "basePath", _basePath },
- { "applicationName", _applicationName },
- { "scope", _scope },
- { "blobStorageFolder", BlobStorageFolder },
- };
-
- public CancellationToken CancellationToken { get; set; }
-
- public IProgress? OnProgressAction { get; set; }
-
- public int SavedObjectCount { get; private set; }
-
- public TimeSpan Elapsed { get; private set; }
-
- public void BeginWrite()
- {
- _queue = new();
- SavedObjectCount = 0;
- }
-
- public void EndWrite() { }
-
- public Task> HasObjects(IReadOnlyList objectIds)
- {
- Dictionary ret = new(objectIds.Count);
- // Initialize with false so that canceled queries still return a dictionary item for every object id
- foreach (string objectId in objectIds)
- {
- ret[objectId] = false;
- }
-
- try
- {
- const string COMMAND_TEXT = "SELECT 1 FROM objects WHERE hash = @hash LIMIT 1 ";
- using var command = new SqliteCommand(COMMAND_TEXT, Connection);
-
- foreach (string objectId in objectIds)
- {
- CancellationToken.ThrowIfCancellationRequested();
-
- command.Parameters.Clear();
- command.Parameters.AddWithValue("@hash", objectId);
-
- using var reader = command.ExecuteReader();
- bool rowFound = reader.Read();
- ret[objectId] = rowFound;
- }
- }
- catch (SqliteException ex)
- {
- throw new TransportException("SQLite transport failed", ex);
- }
-
- return Task.FromResult(ret);
- }
-
- /// Failed to initialize connection to the SQLite DB
- private void Initialize()
- {
- // NOTE: used for creating partioned object tables.
- //string[] HexChars = new string[] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
- //var cart = new List();
- //foreach (var str in HexChars)
- // foreach (var str2 in HexChars)
- // cart.Add(str + str2);
-
- using (var c = new SqliteConnection(_connectionString))
- {
- c.Open();
- const string COMMAND_TEXT =
- @"
- CREATE TABLE IF NOT EXISTS objects(
- hash TEXT PRIMARY KEY,
- content TEXT
- ) WITHOUT ROWID;
- ";
- using (var command = new SqliteCommand(COMMAND_TEXT, c))
- {
- command.ExecuteNonQuery();
- }
-
- // Insert Optimisations
-
- using SqliteCommand cmd0 = new("PRAGMA journal_mode='wal';", c);
- cmd0.ExecuteNonQuery();
-
- //Note / Hack: This setting has the potential to corrupt the db.
- //cmd = new SqliteCommand("PRAGMA synchronous=OFF;", Connection);
- //cmd.ExecuteNonQuery();
-
- using SqliteCommand cmd1 = new("PRAGMA count_changes=OFF;", c);
- cmd1.ExecuteNonQuery();
-
- using SqliteCommand cmd2 = new("PRAGMA temp_store=MEMORY;", c);
- cmd2.ExecuteNonQuery();
- }
-
- Connection = new SqliteConnection(_connectionString);
- Connection.Open();
- }
-
- ///
- /// Returns all the objects in the store. Note: do not use for large collections.
- ///
- ///
- /// This function uses a separate so is safe to call concurrently (unlike most other transport functions)
- internal IEnumerable GetAllObjects()
- {
- CancellationToken.ThrowIfCancellationRequested();
-
- using SqliteConnection connection = new(_connectionString);
- connection.Open();
-
- using var command = new SqliteCommand("SELECT * FROM objects", connection);
-
- using var reader = command.ExecuteReader();
- while (reader.Read())
- {
- CancellationToken.ThrowIfCancellationRequested();
- yield return reader.GetString(1);
- }
- }
-
- ///
- /// Deletes an object. Note: do not use for any speckle object transport, as it will corrupt the database.
- ///
- ///
- public void DeleteObject(string hash)
- {
- CancellationToken.ThrowIfCancellationRequested();
-
- using var command = new SqliteCommand("DELETE FROM objects WHERE hash = @hash", Connection);
- command.Parameters.AddWithValue("@hash", hash);
- command.ExecuteNonQuery();
- }
-
- ///
- /// Updates an object.
- ///
- ///
- ///
- public void UpdateObject(string hash, string serializedObject)
- {
- CancellationToken.ThrowIfCancellationRequested();
-
- using var c = new SqliteConnection(_connectionString);
- c.Open();
- const string COMMAND_TEXT = "REPLACE INTO objects(hash, content) VALUES(@hash, @content)";
- using var command = new SqliteCommand(COMMAND_TEXT, c);
- command.Parameters.AddWithValue("@hash", hash);
- command.Parameters.AddWithValue("@content", serializedObject);
- command.ExecuteNonQuery();
- }
-
- public override string ToString()
- {
- return $"Sqlite Transport @{_rootPath}";
- }
-
- #region Writes
-
- ///
- /// Awaits untill write completion (ie, the current queue is fully consumed).
- ///
- ///
- public async Task WriteComplete() =>
- await Utilities.WaitUntil(() => WriteCompletionStatus, 500).ConfigureAwait(false);
-
- ///
- /// Returns true if the current write queue is empty and comitted.
- ///
- ///
- public bool WriteCompletionStatus => _queue.IsEmpty && !_isWriting;
-
- private void WriteTimerElapsed(object? sender, ElapsedEventArgs e)
- {
- _writeTimer.Enabled = false;
-
- if (CancellationToken.IsCancellationRequested)
- {
- _queue = new ConcurrentQueue<(string, string, int)>();
- return;
- }
-
- if (!_isWriting && !_queue.IsEmpty)
- {
- ConsumeQueue();
- }
- }
-
- private void ConsumeQueue()
- {
- var stopwatch = Stopwatch.StartNew();
- _isWriting = true;
- try
- {
- CancellationToken.ThrowIfCancellationRequested();
-
- var i = 0; //BUG: This never gets incremented!
-
- var saved = 0;
-
- using (var c = new SqliteConnection(_connectionString))
- {
- c.Open();
- using var t = c.BeginTransaction();
- const string COMMAND_TEXT = "INSERT OR IGNORE INTO objects(hash, content) VALUES(@hash, @content)";
-
- while (i < MAX_TRANSACTION_SIZE && _queue.TryPeek(out var result))
- {
- using var command = new SqliteCommand(COMMAND_TEXT, c, t);
- _queue.TryDequeue(out result);
- command.Parameters.AddWithValue("@hash", result.id);
- command.Parameters.AddWithValue("@content", result.serializedObject);
- command.ExecuteNonQuery();
-
- saved++;
- }
-
- t.Commit();
- CancellationToken.ThrowIfCancellationRequested();
- }
-
- CancellationToken.ThrowIfCancellationRequested();
-
- if (!_queue.IsEmpty)
- {
- ConsumeQueue();
- }
- }
- catch (SqliteException ex)
- {
- throw new TransportException(this, "SQLite Command Failed", ex);
- }
- catch (OperationCanceledException)
- {
- _queue = new();
- }
- finally
- {
- stopwatch.Stop();
- Elapsed += stopwatch.Elapsed;
- _isWriting = false;
- }
- }
-
- ///
- /// Adds an object to the saving queue.
- ///
- ///
- ///
- public void SaveObject(string id, string serializedObject)
- {
- CancellationToken.ThrowIfCancellationRequested();
- _queue.Enqueue((id, serializedObject, Encoding.UTF8.GetByteCount(serializedObject)));
-
- _writeTimer.Enabled = true;
- _writeTimer.Start();
- }
-
- ///
- /// Directly saves the object in the db.
- ///
- ///
- ///
- public void SaveObjectSync(string hash, string serializedObject)
- {
- const string COMMAND_TEXT = "INSERT OR IGNORE INTO objects(hash, content) VALUES(@hash, @content)";
-
- try
- {
- using var command = new SqliteCommand(COMMAND_TEXT, Connection);
- command.Parameters.AddWithValue("@hash", hash);
- command.Parameters.AddWithValue("@content", serializedObject);
- command.ExecuteNonQuery();
- }
- catch (SqliteException ex)
- {
- throw new TransportException(this, "SQLite Command Failed", ex);
- }
- }
-
- #endregion
-
- #region Reads
-
- ///
- /// Gets an object.
- ///
- ///
- ///
- public async Task GetObject(string id)
- {
- CancellationToken.ThrowIfCancellationRequested();
- await _connectionLock.WaitAsync(CancellationToken).ConfigureAwait(false);
- var startTime = Stopwatch.GetTimestamp();
- try
- {
- using var command = new SqliteCommand("SELECT * FROM objects WHERE hash = @hash LIMIT 1 ", Connection);
- command.Parameters.AddWithValue("@hash", id);
- using var reader = command.ExecuteReader();
- if (reader.Read())
- {
- return reader.GetString(1);
- }
- }
- finally
- {
- Elapsed += LoggingHelpers.GetElapsedTime(startTime, Stopwatch.GetTimestamp());
- _connectionLock.Release();
- }
- return null; // pass on the duty of null checks to consumers
- }
-
- public async Task CopyObjectAndChildren(string id, ITransport targetTransport)
- {
- string res = await TransportHelpers
- .CopyObjectAndChildrenAsync(id, this, targetTransport, CancellationToken)
- .ConfigureAwait(false);
- return res;
- }
-
- #endregion
-}
diff --git a/src/Speckle.Sdk/Transports/SQLiteTransport2.cs b/src/Speckle.Sdk/Transports/SQLiteTransport2.cs
deleted file mode 100644
index 3009d3cb..00000000
--- a/src/Speckle.Sdk/Transports/SQLiteTransport2.cs
+++ /dev/null
@@ -1,408 +0,0 @@
-using System.Collections.Concurrent;
-using System.Diagnostics;
-using System.Text;
-using System.Timers;
-using Microsoft.Data.Sqlite;
-using Speckle.Sdk.Caching;
-using Speckle.Sdk.Logging;
-using Speckle.Sdk.Models;
-using Timer = System.Timers.Timer;
-
-namespace Speckle.Sdk.Transports;
-
-public sealed class SQLiteTransport2 : IDisposable, ICloneable, ITransport, IBlobCapableTransport
-{
- private readonly string _streamId;
- private bool _isWriting;
- private const int MAX_TRANSACTION_SIZE = 1000;
- private const int POLL_INTERVAL = 500;
-
- private ConcurrentQueue<(string id, string serializedObject, int byteCount)> _queue = new();
-
- ///
- /// Timer that ensures queue is consumed if less than MAX_TRANSACTION_SIZE objects are being sent.
- ///
- private readonly Timer _writeTimer;
-
- public SQLiteTransport2(string streamId)
- {
- _streamId = streamId;
-
- _rootPath = ModelCacheManager.GetDbPath(streamId);
-
- _connectionString = $"Data Source={_rootPath};";
-
- Initialize();
-
- _writeTimer = new Timer
- {
- AutoReset = true,
- Enabled = false,
- Interval = POLL_INTERVAL,
- };
- _writeTimer.Elapsed += WriteTimerElapsed;
- }
-
- private readonly string _rootPath;
-
- private readonly string _connectionString;
-
- private SqliteConnection Connection { get; set; }
- private readonly SemaphoreSlim _connectionLock = new(1, 1);
-
- public string BlobStorageFolder => SpecklePathProvider.UserSpeckleFolderPath;
-
- public void SaveBlob(Blob obj)
- {
- var blobPath = obj.originalPath;
- var targetPath = obj.GetLocalDestinationPath(BlobStorageFolder);
- File.Copy(blobPath, targetPath, true);
- }
-
- public object Clone()
- {
- return new SQLiteTransport2(_streamId)
- {
- OnProgressAction = OnProgressAction,
- CancellationToken = CancellationToken,
- };
- }
-
- public void Dispose()
- {
- // TODO: Check if it's still writing?
- Connection.Close();
- Connection.Dispose();
- _writeTimer.Dispose();
- _connectionLock.Dispose();
- }
-
- public string TransportName { get; set; } = "SQLite";
-
- public Dictionary TransportContext =>
- new()
- {
- { "name", TransportName },
- { "type", GetType().Name },
- { "streamId", _streamId },
- { "blobStorageFolder", BlobStorageFolder },
- };
-
- public CancellationToken CancellationToken { get; set; }
-
- public IProgress? OnProgressAction { get; set; }
-
- public int SavedObjectCount { get; private set; }
-
- public TimeSpan Elapsed { get; private set; }
-
- public void BeginWrite()
- {
- _queue = new();
- SavedObjectCount = 0;
- }
-
- public void EndWrite() { }
-
- public Task> HasObjects(IReadOnlyList objectIds)
- {
- Dictionary ret = new(objectIds.Count);
- // Initialize with false so that canceled queries still return a dictionary item for every object id
- foreach (string objectId in objectIds)
- {
- ret[objectId] = false;
- }
-
- try
- {
- const string COMMAND_TEXT = "SELECT 1 FROM objects WHERE hash = @hash LIMIT 1 ";
- using var command = new SqliteCommand(COMMAND_TEXT, Connection);
-
- foreach (string objectId in objectIds)
- {
- CancellationToken.ThrowIfCancellationRequested();
-
- command.Parameters.Clear();
- command.Parameters.AddWithValue("@hash", objectId);
-
- using var reader = command.ExecuteReader();
- bool rowFound = reader.Read();
- ret[objectId] = rowFound;
- }
- }
- catch (SqliteException ex)
- {
- throw new TransportException("SQLite transport failed", ex);
- }
-
- return Task.FromResult(ret);
- }
-
- /// Failed to initialize connection to the SQLite DB
- private void Initialize()
- {
- // NOTE: used for creating partioned object tables.
- //string[] HexChars = new string[] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
- //var cart = new List();
- //foreach (var str in HexChars)
- // foreach (var str2 in HexChars)
- // cart.Add(str + str2);
-
- using (var c = new SqliteConnection(_connectionString))
- {
- c.Open();
- const string COMMAND_TEXT =
- @"
- CREATE TABLE IF NOT EXISTS objects(
- hash TEXT PRIMARY KEY,
- content TEXT
- ) WITHOUT ROWID;
- ";
- using (var command = new SqliteCommand(COMMAND_TEXT, c))
- {
- command.ExecuteNonQuery();
- }
-
- // Insert Optimisations
-
- using SqliteCommand cmd0 = new("PRAGMA journal_mode='wal';", c);
- cmd0.ExecuteNonQuery();
-
- //Note / Hack: This setting has the potential to corrupt the db.
- //cmd = new SqliteCommand("PRAGMA synchronous=OFF;", Connection);
- //cmd.ExecuteNonQuery();
-
- using SqliteCommand cmd1 = new("PRAGMA count_changes=OFF;", c);
- cmd1.ExecuteNonQuery();
-
- using SqliteCommand cmd2 = new("PRAGMA temp_store=MEMORY;", c);
- cmd2.ExecuteNonQuery();
- }
-
- Connection = new SqliteConnection(_connectionString);
- Connection.Open();
- }
-
- ///
- /// Returns all the objects in the store. Note: do not use for large collections.
- ///
- ///
- /// This function uses a separate so is safe to call concurrently (unlike most other transport functions)
- internal IEnumerable GetAllObjects()
- {
- CancellationToken.ThrowIfCancellationRequested();
-
- using SqliteConnection connection = new(_connectionString);
- connection.Open();
-
- using var command = new SqliteCommand("SELECT * FROM objects", connection);
-
- using var reader = command.ExecuteReader();
- while (reader.Read())
- {
- CancellationToken.ThrowIfCancellationRequested();
- yield return reader.GetString(1);
- }
- }
-
- ///
- /// Deletes an object. Note: do not use for any speckle object transport, as it will corrupt the database.
- ///
- ///
- public void DeleteObject(string hash)
- {
- CancellationToken.ThrowIfCancellationRequested();
-
- using var command = new SqliteCommand("DELETE FROM objects WHERE hash = @hash", Connection);
- command.Parameters.AddWithValue("@hash", hash);
- command.ExecuteNonQuery();
- }
-
- ///
- /// Updates an object.
- ///
- ///
- ///
- public void UpdateObject(string hash, string serializedObject)
- {
- CancellationToken.ThrowIfCancellationRequested();
-
- using var c = new SqliteConnection(_connectionString);
- c.Open();
- const string COMMAND_TEXT = "REPLACE INTO objects(hash, content) VALUES(@hash, @content)";
- using var command = new SqliteCommand(COMMAND_TEXT, c);
- command.Parameters.AddWithValue("@hash", hash);
- command.Parameters.AddWithValue("@content", serializedObject);
- command.ExecuteNonQuery();
- }
-
- public override string ToString()
- {
- return $"Sqlite Transport @{_rootPath}";
- }
-
- #region Writes
-
- ///
- /// Awaits untill write completion (ie, the current queue is fully consumed).
- ///
- ///
- public async Task WriteComplete() =>
- await Utilities.WaitUntil(() => WriteCompletionStatus, 500).ConfigureAwait(false);
-
- ///
- /// Returns true if the current write queue is empty and comitted.
- ///
- ///
- public bool WriteCompletionStatus => _queue.IsEmpty && !_isWriting;
-
- private void WriteTimerElapsed(object? sender, ElapsedEventArgs e)
- {
- _writeTimer.Enabled = false;
-
- if (CancellationToken.IsCancellationRequested)
- {
- _queue = new ConcurrentQueue<(string, string, int)>();
- return;
- }
-
- if (!_isWriting && !_queue.IsEmpty)
- {
- ConsumeQueue();
- }
- }
-
- private void ConsumeQueue()
- {
- var stopwatch = Stopwatch.StartNew();
- _isWriting = true;
- try
- {
- CancellationToken.ThrowIfCancellationRequested();
-
- var i = 0; //BUG: This never gets incremented!
-
- var saved = 0;
-
- using (var c = new SqliteConnection(_connectionString))
- {
- c.Open();
- using var t = c.BeginTransaction();
- const string COMMAND_TEXT = "INSERT OR IGNORE INTO objects(hash, content) VALUES(@hash, @content)";
-
- while (i < MAX_TRANSACTION_SIZE && _queue.TryPeek(out var result))
- {
- using var command = new SqliteCommand(COMMAND_TEXT, c, t);
- _queue.TryDequeue(out result);
- command.Parameters.AddWithValue("@hash", result.id);
- command.Parameters.AddWithValue("@content", result.serializedObject);
- command.ExecuteNonQuery();
-
- saved++;
- }
-
- t.Commit();
- CancellationToken.ThrowIfCancellationRequested();
- }
-
- CancellationToken.ThrowIfCancellationRequested();
-
- if (!_queue.IsEmpty)
- {
- ConsumeQueue();
- }
- }
- catch (SqliteException ex)
- {
- throw new TransportException(this, "SQLite Command Failed", ex);
- }
- catch (OperationCanceledException)
- {
- _queue = new();
- }
- finally
- {
- stopwatch.Stop();
- Elapsed += stopwatch.Elapsed;
- _isWriting = false;
- }
- }
-
- ///
- /// Adds an object to the saving queue.
- ///
- ///
- ///
- public void SaveObject(string id, string serializedObject)
- {
- CancellationToken.ThrowIfCancellationRequested();
- _queue.Enqueue((id, serializedObject, Encoding.UTF8.GetByteCount(serializedObject)));
-
- _writeTimer.Enabled = true;
- _writeTimer.Start();
- }
-
- ///
- /// Directly saves the object in the db.
- ///
- ///
- ///
- public void SaveObjectSync(string hash, string serializedObject)
- {
- const string COMMAND_TEXT = "INSERT OR IGNORE INTO objects(hash, content) VALUES(@hash, @content)";
-
- try
- {
- using var command = new SqliteCommand(COMMAND_TEXT, Connection);
- command.Parameters.AddWithValue("@hash", hash);
- command.Parameters.AddWithValue("@content", serializedObject);
- command.ExecuteNonQuery();
- }
- catch (SqliteException ex)
- {
- throw new TransportException(this, "SQLite Command Failed", ex);
- }
- }
-
- #endregion
-
- #region Reads
-
- ///
- /// Gets an object.
- ///
- ///
- ///
- public async Task GetObject(string id)
- {
- CancellationToken.ThrowIfCancellationRequested();
- await _connectionLock.WaitAsync(CancellationToken).ConfigureAwait(false);
- var startTime = Stopwatch.GetTimestamp();
- try
- {
- using var command = new SqliteCommand("SELECT * FROM objects WHERE hash = @hash LIMIT 1 ", Connection);
- command.Parameters.AddWithValue("@hash", id);
- using var reader = command.ExecuteReader();
- if (reader.Read())
- {
- return reader.GetString(1);
- }
- }
- finally
- {
- Elapsed += LoggingHelpers.GetElapsedTime(startTime, Stopwatch.GetTimestamp());
- _connectionLock.Release();
- }
- return null; // pass on the duty of null checks to consumers
- }
-
- public async Task CopyObjectAndChildren(string id, ITransport targetTransport)
- {
- string res = await TransportHelpers
- .CopyObjectAndChildrenAsync(id, this, targetTransport, CancellationToken)
- .ConfigureAwait(false);
- return res;
- }
-
- #endregion
-}
diff --git a/src/Speckle.Sdk/Transports/ServerTransport.cs b/src/Speckle.Sdk/Transports/ServerTransport.cs
deleted file mode 100644
index 7ca7536c..00000000
--- a/src/Speckle.Sdk/Transports/ServerTransport.cs
+++ /dev/null
@@ -1,374 +0,0 @@
-using System.Diagnostics;
-using Speckle.Sdk.Common;
-using Speckle.Sdk.Credentials;
-using Speckle.Sdk.Helpers;
-using Speckle.Sdk.Logging;
-using Speckle.Sdk.Models;
-using Speckle.Sdk.Serialisation.Utilities;
-using Speckle.Sdk.Transports.ServerUtils;
-
-namespace Speckle.Sdk.Transports;
-
-public sealed class ServerTransport : IServerTransport
-{
- private readonly ISpeckleHttp _http;
- private readonly ISdkActivityFactory _activityFactory;
- private readonly object _elapsedLock = new();
-
- private Exception? _exception;
- private bool IsInErrorState => _exception is not null;
- private bool _isWriteComplete;
-
- // TODO: make send buffer more flexible to accept blobs too
- private List<(string id, string data)> _sendBuffer = new();
- private readonly object _sendBufferLock = new();
- private Thread? _sendingThread;
-
- private volatile bool _shouldSendThreadRun;
-
- ///
- ///
- ///
- /// Defaults to
- /// was not formatted as valid stream id
- public ServerTransport(
- ISpeckleHttp http,
- ISdkActivityFactory activityFactory,
- Account account,
- string streamId,
- int timeoutSeconds = 60,
- string? blobStorageFolder = null
- )
- {
- if (string.IsNullOrWhiteSpace(streamId))
- {
- throw new ArgumentException($"{streamId} is not a valid id", streamId);
- }
-
- _http = http;
- _activityFactory = activityFactory;
-
- Account = account;
- BaseUri = new(account.serverInfo.url);
- StreamId = streamId;
- AuthorizationToken = account.token;
- TimeoutSeconds = timeoutSeconds;
- BlobStorageFolder = blobStorageFolder ?? SpecklePathProvider.BlobStoragePath();
- Api = new ParallelServerApi(http, activityFactory, BaseUri, AuthorizationToken, BlobStorageFolder, TimeoutSeconds);
-
- Directory.CreateDirectory(BlobStorageFolder);
- }
-
- public Account Account { get; }
- public Uri BaseUri { get; }
- public string StreamId { get; internal set; }
-
- public int TimeoutSeconds { get; set; }
- private string AuthorizationToken { get; }
-
- internal ParallelServerApi Api { get; private set; }
-
- public string BlobStorageFolder { get; set; }
-
- public void SaveBlob(Blob obj)
- {
- var hash = obj.GetFileHash();
-
- lock (_sendBufferLock)
- {
- if (IsInErrorState)
- {
- throw new TransportException("Server transport is in an errored state", _exception);
- }
-
- _sendBuffer.Add(($"blob:{hash}", obj.filePath));
- }
- }
-
- public object Clone()
- {
- return new ServerTransport(_http, _activityFactory, Account, StreamId, TimeoutSeconds, BlobStorageFolder)
- {
- OnProgressAction = OnProgressAction,
- CancellationToken = CancellationToken,
- };
- }
-
- public void Dispose()
- {
- if (_sendingThread != null)
- {
- _shouldSendThreadRun = false;
- _sendingThread.Join();
- }
- Api.Dispose();
- }
-
- public string TransportName { get; set; } = "RemoteTransport";
-
- public Dictionary TransportContext =>
- new()
- {
- { "name", TransportName },
- { "type", GetType().Name },
- { "streamId", StreamId },
- { "serverUrl", BaseUri },
- { "blobStorageFolder", BlobStorageFolder },
- };
-
- public CancellationToken CancellationToken { get; set; }
- public IProgress? OnProgressAction { get; set; }
- public TimeSpan Elapsed { get; private set; } = TimeSpan.Zero;
-
- public async Task CopyObjectAndChildren(string id, ITransport targetTransport)
- {
- if (string.IsNullOrEmpty(id))
- {
- throw new ArgumentException("Cannot copy object with empty id", nameof(id));
- }
-
- CancellationToken.ThrowIfCancellationRequested();
-
- using ParallelServerApi api = new(
- _http,
- _activityFactory,
- BaseUri,
- AuthorizationToken,
- BlobStorageFolder,
- TimeoutSeconds
- );
-
- var stopwatch = Stopwatch.StartNew();
- api.CancellationToken = CancellationToken;
-
- string? rootObjectJson = await api.DownloadSingleObject(StreamId, id, OnProgressAction).ConfigureAwait(false);
- var allIds = ClosureParser.GetChildrenIds(rootObjectJson.NotNull(), CancellationToken).ToList();
-
- var childrenIds = allIds.Where(x => !x.Contains("blob:"));
- var blobIds = allIds.Where(x => x.Contains("blob:")).Select(x => x.Remove(0, 5));
-
- //
- // Objects download
- //
-
- // Check which children are not already in the local transport
- var childrenFoundMap = await targetTransport.HasObjects(childrenIds.ToList()).ConfigureAwait(false);
- List newChildrenIds = new(from objId in childrenFoundMap.Keys where !childrenFoundMap[objId] select objId);
-
- targetTransport.BeginWrite();
-
- await api.DownloadObjects(
- StreamId,
- newChildrenIds,
- OnProgressAction,
- (childId, childData) =>
- {
- stopwatch.Stop();
- targetTransport.SaveObject(childId, childData);
- stopwatch.Start();
- }
- )
- .ConfigureAwait(false);
-
- // pausing until writing to the target transport
- stopwatch.Stop();
- targetTransport.SaveObject(id, rootObjectJson);
-
- await targetTransport.WriteComplete().ConfigureAwait(false);
- targetTransport.EndWrite();
- stopwatch.Start();
-
- //
- // Blobs download
- //
- var localBlobTrimmedHashes = Directory
- .GetFiles(BlobStorageFolder)
- .Select(fileName => fileName.Split(Path.DirectorySeparatorChar).Last())
- .Where(fileName => fileName.Length > 10)
- .Select(fileName => fileName[..Blob.LocalHashPrefixLength])
- .ToList();
-
- var newBlobIds = blobIds
- .Where(blobId => !localBlobTrimmedHashes.Contains(blobId[..Blob.LocalHashPrefixLength]))
- .ToList();
-
- await api.DownloadBlobs(StreamId, newBlobIds, OnProgressAction).ConfigureAwait(false);
-
- stopwatch.Stop();
- Elapsed += stopwatch.Elapsed;
- return rootObjectJson;
- }
-
- public async Task GetObject(string id)
- {
- CancellationToken.ThrowIfCancellationRequested();
- var stopwatch = Stopwatch.StartNew();
- var result = await Api.DownloadSingleObject(StreamId, id, OnProgressAction).ConfigureAwait(false);
- stopwatch.Stop();
- Elapsed += stopwatch.Elapsed;
- return result;
- }
-
- public async Task> HasObjects(IReadOnlyList objectIds)
- {
- return await Api.HasObjects(StreamId, objectIds).ConfigureAwait(false);
- }
-
- public void SaveObject(string id, string serializedObject)
- {
- lock (_sendBufferLock)
- {
- if (IsInErrorState)
- {
- throw new TransportException($"{TransportName} transport failed", _exception);
- }
-
- _sendBuffer.Add((id, serializedObject));
- _isWriteComplete = false;
- }
- }
-
- public void BeginWrite()
- {
- if (_shouldSendThreadRun || _sendingThread != null)
- {
- throw new InvalidOperationException("ServerTransport already sending");
- }
-
- _exception = null;
- _shouldSendThreadRun = true;
- _sendingThread = new Thread(SendingThreadMain) { Name = "ServerTransportSender", IsBackground = true };
- _sendingThread.Start();
- }
-
- public async Task WriteComplete()
- {
- while (true)
- {
- lock (_sendBufferLock)
- {
- if (_isWriteComplete || IsInErrorState)
- {
- CancellationToken.ThrowIfCancellationRequested();
-
- if (_exception is not null)
- {
- throw new TransportException(this, $"{TransportName} transport failed", _exception);
- }
-
- return;
- }
- }
-
- await Task.Delay(50, CancellationToken).ConfigureAwait(false);
- }
- }
-
- public void EndWrite()
- {
- if (!_shouldSendThreadRun || _sendingThread == null)
- {
- throw new InvalidOperationException("ServerTransport not sending");
- }
-
- _shouldSendThreadRun = false;
- _sendingThread.Join();
- _sendingThread = null;
- }
-
- public override string ToString()
- {
- return $"Server Transport @{Account.serverInfo.url}";
- }
-
- private async void SendingThreadMain()
- {
- while (true)
- {
- var stopwatch = Stopwatch.StartNew();
- if (!_shouldSendThreadRun || CancellationToken.IsCancellationRequested)
- {
- return;
- }
-
- List<(string id, string data)>? buffer = null;
- lock (_sendBufferLock)
- {
- if (_sendBuffer.Count > 0)
- {
- buffer = _sendBuffer;
- _sendBuffer = new();
- }
- else
- {
- _isWriteComplete = true;
- }
- }
-
- if (buffer is null)
- {
- Thread.Sleep(100);
- continue;
- }
- try
- {
- var bufferObjects = buffer.Where(tuple => !tuple.id.Contains("blob")).ToList();
- var bufferBlobs = buffer.Where(tuple => tuple.id.Contains("blob")).ToList();
-
- List objectIds = new(bufferObjects.Count);
-
- foreach ((string id, _) in bufferObjects)
- {
- if (id != "blob")
- {
- objectIds.Add(id);
- }
- }
-
- Dictionary hasObjects = await Api.HasObjects(StreamId, objectIds).ConfigureAwait(false);
- List<(string, string)> newObjects = new();
- foreach ((string id, object json) in bufferObjects)
- {
- if (!hasObjects[id])
- {
- newObjects.Add((id, (string)json));
- }
- }
-
- await Api.UploadObjects(StreamId, newObjects, OnProgressAction).ConfigureAwait(false);
-
- if (bufferBlobs.Count != 0)
- {
- var blobIdsToUpload = await Api.HasBlobs(StreamId, bufferBlobs).ConfigureAwait(false);
- var formattedIds = blobIdsToUpload.Select(id => $"blob:{id}").ToList();
- var newBlobs = bufferBlobs.Where(tuple => formattedIds.IndexOf(tuple.id) != -1).ToList();
- if (newBlobs.Count != 0)
- {
- await Api.UploadBlobs(StreamId, newBlobs, OnProgressAction).ConfigureAwait(false);
- }
- }
- }
- catch (Exception ex)
- {
- lock (_sendBufferLock)
- {
- _sendBuffer.Clear();
- _exception = ex;
- }
-
- if (ex.IsFatal())
- {
- throw;
- }
- }
- finally
- {
- stopwatch.Stop();
- lock (_elapsedLock)
- {
- Elapsed += stopwatch.Elapsed;
- }
- }
- }
- }
-}
diff --git a/src/Speckle.Sdk/Transports/ServerTransportFactory.cs b/src/Speckle.Sdk/Transports/ServerTransportFactory.cs
deleted file mode 100644
index 0710cfe6..00000000
--- a/src/Speckle.Sdk/Transports/ServerTransportFactory.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System.Diagnostics.CodeAnalysis;
-using Speckle.InterfaceGenerator;
-using Speckle.Sdk.Credentials;
-using Speckle.Sdk.Helpers;
-using Speckle.Sdk.Logging;
-
-namespace Speckle.Sdk.Transports;
-
-[GenerateAutoInterface]
-[ExcludeFromCodeCoverage] //factories don't need coverage
-public class ServerTransportFactory(ISpeckleHttp http, ISdkActivityFactory activityFactory) : IServerTransportFactory
-{
- public ServerTransport Create(
- Account account,
- string streamId,
- int timeoutSeconds = 60,
- string? blobStorageFolder = null
- ) => new ServerTransport(http, activityFactory, account, streamId, timeoutSeconds, blobStorageFolder);
-}
diff --git a/src/Speckle.Sdk/Transports/ServerUtils/IServerApi.cs b/src/Speckle.Sdk/Transports/ServerUtils/IServerApi.cs
deleted file mode 100644
index 0fa65d33..00000000
--- a/src/Speckle.Sdk/Transports/ServerUtils/IServerApi.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-namespace Speckle.Sdk.Transports.ServerUtils;
-
-public delegate void CbObjectDownloaded(string id, string json);
-
-internal interface IServerApi
-{
- public Task DownloadSingleObject(string streamId, string objectId, IProgress? progress);
-
- public Task DownloadObjects(
- string streamId,
- IReadOnlyList objectIds,
- IProgress? progress,
- CbObjectDownloaded onObjectCallback
- );
-
- public Task> HasObjects(string streamId, IReadOnlyList objectIds);
-
- public Task UploadObjects(
- string streamId,
- IReadOnlyList<(string id, string data)> objects,
- IProgress? progress
- );
-
- public Task UploadBlobs(
- string streamId,
- IReadOnlyList<(string id, string data)> objects,
- IProgress? progress
- );
-
- public Task DownloadBlobs(string streamId, IReadOnlyList blobIds, IProgress? progress);
-}
diff --git a/src/Speckle.Sdk/Transports/ServerUtils/ParallelServerAPI.cs b/src/Speckle.Sdk/Transports/ServerUtils/ParallelServerAPI.cs
deleted file mode 100644
index 7e048b40..00000000
--- a/src/Speckle.Sdk/Transports/ServerUtils/ParallelServerAPI.cs
+++ /dev/null
@@ -1,325 +0,0 @@
-using System.Collections.Concurrent;
-using System.Diagnostics;
-using Speckle.Sdk.Common;
-using Speckle.Sdk.Helpers;
-using Speckle.Sdk.Logging;
-using Speckle.Sdk.Serialisation.Utilities;
-
-namespace Speckle.Sdk.Transports.ServerUtils;
-
-internal enum ServerApiOperation
-{
- NoOp = default,
- DownloadSingleObject,
- DownloadObjects,
- HasObjects,
- UploadObjects,
- UploadBlobs,
- DownloadBlobs,
- HasBlobs,
-}
-
-internal class ParallelServerApi : ParallelOperationExecutor, IServerApi
-{
- private readonly string _authToken;
-
- private readonly ISpeckleHttp _http;
- private readonly ISdkActivityFactory _activityFactory;
- private readonly Uri _baseUri;
-
- private readonly int _timeoutSeconds;
-
- public ParallelServerApi(
- ISpeckleHttp http,
- ISdkActivityFactory activityFactory,
- Uri baseUri,
- string authorizationToken,
- string blobStorageFolder,
- int timeoutSeconds,
- int numThreads = 4,
- int numBufferedOperations = 8
- )
- {
- _http = http;
- _activityFactory = activityFactory;
- _baseUri = baseUri;
- _authToken = authorizationToken;
- _timeoutSeconds = timeoutSeconds;
- NumThreads = numThreads;
-
- BlobStorageFolder = blobStorageFolder;
-
- NumThreads = numThreads;
- Tasks = new BlockingCollection>(numBufferedOperations);
- }
-
- public CancellationToken CancellationToken { get; set; }
- public bool CompressPayloads { get; set; } = true;
-
- public string BlobStorageFolder { get; set; }
-
- #region Operations
-
- public async Task> HasObjects(string streamId, IReadOnlyList objectIds)
- {
- EnsureStarted();
- List> tasks = new();
- IReadOnlyList> splitObjectsIds;
- if (objectIds.Count <= 50)
- {
- splitObjectsIds = new List> { objectIds };
- }
- else
- {
- splitObjectsIds = SplitList(objectIds, NumThreads);
- }
-
- for (int i = 0; i < NumThreads; i++)
- {
- if (splitObjectsIds.Count <= i || splitObjectsIds[i].Count == 0)
- {
- continue;
- }
-
- var op = QueueOperation(ServerApiOperation.HasObjects, (streamId, splitObjectsIds[i]));
- tasks.Add(op);
- }
- Dictionary ret = new();
- foreach (var task in tasks)
- {
- var taskResult = (IReadOnlyDictionary?)(await task.ConfigureAwait(false));
- foreach (KeyValuePair kv in taskResult.Empty())
- {
- ret[kv.Key] = kv.Value;
- }
- }
-
- return ret;
- }
-
- public async Task DownloadSingleObject(string streamId, string objectId, IProgress? progress)
- {
- EnsureStarted();
- Task op = QueueOperation(ServerApiOperation.DownloadSingleObject, (streamId, objectId, progress));
- object? result = await op.ConfigureAwait(false);
- return (string?)result;
- }
-
- public async Task DownloadObjects(
- string streamId,
- IReadOnlyList objectIds,
- IProgress? progress,
- CbObjectDownloaded onObjectCallback
- )
- {
- EnsureStarted();
- List> tasks = new();
- IReadOnlyList> splitObjectsIds = SplitList(objectIds, NumThreads);
- object callbackLock = new();
-
- CbObjectDownloaded callbackWrapper = (id, json) =>
- {
- lock (callbackLock)
- {
- onObjectCallback(id, json);
- }
- };
-
- for (int i = 0; i < NumThreads; i++)
- {
- if (splitObjectsIds[i].Count == 0)
- {
- continue;
- }
-
- Task op = QueueOperation(
- ServerApiOperation.DownloadObjects,
- (streamId, splitObjectsIds[i], progress, callbackWrapper)
- );
- tasks.Add(op);
- }
- await Task.WhenAll(tasks.ToArray()).ConfigureAwait(false);
- }
-
- public async Task UploadObjects(
- string streamId,
- IReadOnlyList<(string, string)> objects,
- IProgress? progress
- )
- {
- EnsureStarted();
- List> tasks = new();
- IReadOnlyList> splitObjects;
-
- // request count optimization: if objects are < 500k, send in 1 request
- int totalSize = 0;
- foreach ((_, string json) in objects)
- {
- totalSize += json.Length;
- if (totalSize >= 500_000)
- {
- break;
- }
- }
- splitObjects =
- totalSize >= 500_000 ? SplitList(objects, NumThreads) : new List> { objects };
-
- for (int i = 0; i < NumThreads; i++)
- {
- if (splitObjects.Count <= i || splitObjects[i].Count == 0)
- {
- continue;
- }
-
- var op = QueueOperation(ServerApiOperation.UploadObjects, (streamId, splitObjects[i], progress));
- tasks.Add(op);
- }
- await Task.WhenAll(tasks.ToArray()).ConfigureAwait(false);
- }
-
- public async Task UploadBlobs(
- string streamId,
- IReadOnlyList<(string, string)> blobs,
- IProgress? progress
- )
- {
- EnsureStarted();
- var op = QueueOperation(ServerApiOperation.UploadBlobs, (streamId, blobs, progress));
- await op.ConfigureAwait(false);
- }
-
- public async Task DownloadBlobs(string streamId, IReadOnlyList blobIds, IProgress? progress)
- {
- EnsureStarted();
- var op = QueueOperation(ServerApiOperation.DownloadBlobs, (streamId, blobIds, progress));
- await op.ConfigureAwait(false);
- }
-
- public async Task> HasBlobs(string streamId, IReadOnlyList<(string, string)> blobs)
- {
- EnsureStarted();
- Task op = QueueOperation(ServerApiOperation.HasBlobs, (streamId, blobs));
- var res = (List?)await op.ConfigureAwait(false);
- Debug.Assert(res is not null);
- return res.NotNull();
- }
-
- #endregion
-
- public void EnsureStarted()
- {
- if (Threads.Count == 0)
- {
- Start();
- }
- }
-
- protected override void ThreadMain()
- {
- using ServerApi serialApi = new(_http, _activityFactory, _baseUri, _authToken, BlobStorageFolder, _timeoutSeconds);
- serialApi.CancellationToken = CancellationToken;
- serialApi.CompressPayloads = CompressPayloads;
-
- while (true)
- {
- if (IsDisposed)
- {
- return;
- }
-
- var (operation, inputValue, tcs) = Tasks.Take();
-
- if (operation == ServerApiOperation.NoOp || tcs == null)
- {
- return;
- }
-
- try
- {
- var result = RunOperation(operation, inputValue.NotNull(), serialApi).GetAwaiter().GetResult();
- tcs.SetResult(result);
- }
- catch (Exception ex)
- {
- tcs.SetException(ex);
-
- if (ex.IsFatal())
- {
- throw;
- }
- }
- }
- }
-
- private static async Task RunOperation(ServerApiOperation operation, object inputValue, ServerApi serialApi)
- {
- switch (operation)
- {
- case ServerApiOperation.DownloadSingleObject:
- var (dsoStreamId, dsoObjectId, progress) = ((string, string, IProgress?))inputValue;
- return await serialApi.DownloadSingleObject(dsoStreamId, dsoObjectId, progress).ConfigureAwait(false);
- case ServerApiOperation.DownloadObjects:
- var (doStreamId, doObjectIds, progress2, doCallback) = ((
- string,
- IReadOnlyList,
- IProgress?,
- CbObjectDownloaded
- ))inputValue;
- await serialApi.DownloadObjects(doStreamId, doObjectIds, progress2, doCallback).ConfigureAwait(false);
- return null;
- case ServerApiOperation.HasObjects:
- var (hoStreamId, hoObjectIds) = ((string, IReadOnlyList))inputValue;
- return await serialApi.HasObjects(hoStreamId, hoObjectIds).ConfigureAwait(false);
- case ServerApiOperation.UploadObjects:
- var (uoStreamId, uoObjects, progress3) = ((
- string,
- IReadOnlyList<(string, string)>,
- IProgress?
- ))inputValue;
- await serialApi.UploadObjects(uoStreamId, uoObjects, progress3).ConfigureAwait(false);
- return null;
- case ServerApiOperation.UploadBlobs:
- var (ubStreamId, ubBlobs, progress4) = ((
- string,
- IReadOnlyList<(string, string)>,
- IProgress?
- ))inputValue;
- await serialApi.UploadBlobs(ubStreamId, ubBlobs, progress4).ConfigureAwait(false);
- return null;
- case ServerApiOperation.HasBlobs:
- var (hbStreamId, hBlobs) = ((string, IReadOnlyList<(string, string)>))inputValue;
- return await serialApi
- .HasBlobs(hbStreamId, hBlobs.Select(b => b.Item1.Split(':')[1]).ToList())
- .ConfigureAwait(false);
- case ServerApiOperation.DownloadBlobs:
- var (dbStreamId, blobIds, progress5) = ((string, IReadOnlyList, IProgress?))inputValue;
- await serialApi.DownloadBlobs(dbStreamId, blobIds, progress5).ConfigureAwait(false);
- return null;
- default:
- throw new ArgumentOutOfRangeException(nameof(operation), operation, null);
- }
- }
-
- private Task QueueOperation(ServerApiOperation operation, object? inputValue)
- {
- TaskCompletionSource tcs = new(TaskCreationOptions.RunContinuationsAsynchronously);
- Tasks.Add(new(operation, inputValue, tcs));
- return tcs.Task;
- }
-
- private static List> SplitList(IReadOnlyList list, int parts)
- {
- List> ret = new(parts);
- for (int i = 0; i < parts; i++)
- {
- ret.Add(new List(list.Count / parts + 1));
- }
-
- for (int i = 0; i < list.Count; i++)
- {
- ret[i % parts].Add(list[i]);
- }
-
- return ret;
- }
-}
diff --git a/src/Speckle.Sdk/Transports/ServerUtils/ServerAPI.cs b/src/Speckle.Sdk/Transports/ServerUtils/ServerAPI.cs
deleted file mode 100644
index 68ac2a74..00000000
--- a/src/Speckle.Sdk/Transports/ServerUtils/ServerAPI.cs
+++ /dev/null
@@ -1,460 +0,0 @@
-using System.Net;
-using System.Net.Http.Headers;
-using System.Text;
-using Speckle.Newtonsoft.Json;
-using Speckle.Newtonsoft.Json.Linq;
-using Speckle.Sdk.Common;
-using Speckle.Sdk.Helpers;
-using Speckle.Sdk.Logging;
-using Speckle.Sdk.Models;
-
-namespace Speckle.Sdk.Transports.ServerUtils;
-
-public sealed class ServerApi : IDisposable, IServerApi
-{
- private readonly ISdkActivityFactory _activityFactory;
- private const int BATCH_SIZE_GET_OBJECTS = 10000;
- private const int BATCH_SIZE_HAS_OBJECTS = 100000;
-
- private const int MAX_MULTIPART_COUNT = 5;
- private const int MAX_MULTIPART_SIZE = 25_000_000;
- private const int MAX_OBJECT_SIZE = 25_000_000;
-
- private const int MAX_REQUEST_SIZE = 100_000_000;
-
- private static readonly char[] s_separator = { '\t' };
- private static readonly string[] s_filenameSeparator = { "filename=" };
-
- private readonly HttpClient _client;
-
- public ServerApi(
- ISpeckleHttp speckleHttp,
- ISdkActivityFactory activityFactory,
- Uri baseUri,
- string? authorizationToken,
- string blobStorageFolder,
- int timeoutSeconds = 120
- )
- {
- _activityFactory = activityFactory;
- CancellationToken = CancellationToken.None;
-
- BlobStorageFolder = blobStorageFolder;
-
- _client = speckleHttp.CreateHttpClient(
- new HttpClientHandler { AutomaticDecompression = DecompressionMethods.GZip },
- timeoutSeconds: timeoutSeconds,
- authorizationToken: authorizationToken
- );
- _client.BaseAddress = baseUri;
- }
-
- public CancellationToken CancellationToken { get; set; }
- public bool CompressPayloads { get; set; } = true;
-
- public string BlobStorageFolder { get; set; }
-
- public void Dispose()
- {
- _client.Dispose();
- }
-
- public async Task DownloadSingleObject(string streamId, string objectId, IProgress? progress)
- {
- using var _ = _activityFactory.Start();
- CancellationToken.ThrowIfCancellationRequested();
-
- // Get root object
- using var rootHttpMessage = new HttpRequestMessage
- {
- RequestUri = new Uri($"/objects/{streamId}/{objectId}/single", UriKind.Relative),
- Method = HttpMethod.Get,
- };
-
- var rootHttpResponse = await _client
- .SendAsync(rootHttpMessage, HttpCompletionOption.ResponseContentRead, CancellationToken)
- .ConfigureAwait(false);
-
- string? rootObjectStr = null;
- await ResponseProgress(rootHttpResponse, progress, (_, json) => rootObjectStr = json, true).ConfigureAwait(false);
- return rootObjectStr;
- }
-
- public async Task DownloadObjects(
- string streamId,
- IReadOnlyList objectIds,
- IProgress? progress,
- CbObjectDownloaded onObjectCallback
- )
- {
- if (objectIds.Count == 0)
- {
- return;
- }
- using var _ = _activityFactory.Start();
-
- if (objectIds.Count < BATCH_SIZE_GET_OBJECTS)
- {
- await DownloadObjectsImpl(streamId, objectIds, progress, onObjectCallback).ConfigureAwait(false);
- return;
- }
-
- List crtRequest = new();
- foreach (string id in objectIds)
- {
- if (crtRequest.Count >= BATCH_SIZE_GET_OBJECTS)
- {
- await DownloadObjectsImpl(streamId, crtRequest, progress, onObjectCallback).ConfigureAwait(false);
- crtRequest = new List();
- }
- crtRequest.Add(id);
- }
- await DownloadObjectsImpl(streamId, crtRequest, progress, onObjectCallback).ConfigureAwait(false);
- }
-
- public async Task> HasObjects(string streamId, IReadOnlyList objectIds)
- {
- if (objectIds.Count <= BATCH_SIZE_HAS_OBJECTS)
- {
- return await HasObjectsImpl(streamId, objectIds).ConfigureAwait(false);
- }
-
- Dictionary ret = new();
- List crtBatch = new(BATCH_SIZE_HAS_OBJECTS);
- foreach (string objectId in objectIds)
- {
- crtBatch.Add(objectId);
- if (crtBatch.Count >= BATCH_SIZE_HAS_OBJECTS)
- {
- Dictionary batchResult = await HasObjectsImpl(streamId, crtBatch).ConfigureAwait(false);
- foreach (KeyValuePair kv in batchResult)
- {
- ret[kv.Key] = kv.Value;
- }
-
- crtBatch = new List(BATCH_SIZE_HAS_OBJECTS);
- }
- }
- if (crtBatch.Count > 0)
- {
- Dictionary batchResult = await HasObjectsImpl(streamId, crtBatch).ConfigureAwait(false);
- foreach (KeyValuePair kv in batchResult)
- {
- ret[kv.Key] = kv.Value;
- }
- }
- return ret;
- }
-
- public async Task UploadObjects(
- string streamId,
- IReadOnlyList<(string, string)> objects,
- IProgress? progress
- )
- {
- if (objects.Count == 0)
- {
- return;
- }
-
- // 1. Split into parts of MAX_MULTIPART_SIZE size (can be exceptions until a max of MAX_OBJECT_SIZE if a single obj is larger than MAX_MULTIPART_SIZE)
- List> multipartedObjects = new();
- List multipartedObjectsSize = new();
-
- List<(string, string)> crtMultipart = new();
- int crtMultipartSize = 0;
-
- foreach ((string id, string json) in objects)
- {
- int objSize = Encoding.UTF8.GetByteCount(json);
- if (objSize > MAX_OBJECT_SIZE)
- {
- throw new ArgumentException(
- $"Object {id} too large (size {objSize}, max size {MAX_OBJECT_SIZE}). Consider using detached/chunked properties",
- nameof(objects)
- );
- }
-
- if (crtMultipartSize + objSize <= MAX_MULTIPART_SIZE)
- {
- crtMultipart.Add((id, json));
- crtMultipartSize += objSize;
- continue;
- }
-
- // new multipart
- if (crtMultipart.Count > 0)
- {
- multipartedObjects.Add(crtMultipart);
- multipartedObjectsSize.Add(crtMultipartSize);
- }
- crtMultipart = new List<(string, string)> { (id, json) };
- crtMultipartSize = objSize;
- }
- multipartedObjects.Add(crtMultipart);
- multipartedObjectsSize.Add(crtMultipartSize);
-
- // 2. Split multiparts into individual server requests of max size MAX_REQUEST_SIZE or max length MAX_MULTIPART_COUNT and send them
- List> crtRequest = new();
- int crtRequestSize = 0;
- for (int i = 0; i < multipartedObjects.Count; i++)
- {
- List<(string, string)> multipart = multipartedObjects[i];
- int multipartSize = multipartedObjectsSize[i];
- if (crtRequestSize + multipartSize > MAX_REQUEST_SIZE || crtRequest.Count >= MAX_MULTIPART_COUNT)
- {
- await UploadObjectsImpl(streamId, crtRequest, progress).ConfigureAwait(false);
- crtRequest = new List>();
- crtRequestSize = 0;
- }
- crtRequest.Add(multipart);
- crtRequestSize += multipartSize;
- }
- if (crtRequest.Count > 0)
- {
- await UploadObjectsImpl(streamId, crtRequest, progress).ConfigureAwait(false);
- }
- }
-
- public async Task UploadBlobs(
- string streamId,
- IReadOnlyList<(string, string)> objects,
- IProgress? progress
- )
- {
- CancellationToken.ThrowIfCancellationRequested();
- if (objects.Count == 0)
- {
- return;
- }
-
- var multipartFormDataContent = new MultipartFormDataContent();
- var streams = new List();
- foreach (var (id, filePath) in objects)
- {
- var fileName = Path.GetFileName(filePath);
- var stream = File.OpenRead(filePath);
- streams.Add(stream);
- StreamContent fsc = new(stream);
- var hash = id.Split(':')[1];
-
- multipartFormDataContent.Add(fsc, $"hash:{hash}", fileName);
- }
-
- using var message = new HttpRequestMessage();
- message.RequestUri = new Uri($"/api/stream/{streamId}/blob", UriKind.Relative);
- message.Method = HttpMethod.Post;
- message.Content = new ProgressContent(multipartFormDataContent, progress);
-
- try
- {
- var response = await _client.SendAsync(message, CancellationToken).ConfigureAwait(false);
-
- response.EnsureSuccessStatusCode();
-
- foreach (var stream in streams)
- {
- stream.Dispose();
- }
- }
- finally
- {
- foreach (var stream in streams)
- {
- stream.Dispose();
- }
- }
- }
-
- public async Task DownloadBlobs(string streamId, IReadOnlyList blobIds, IProgress? progress)
- {
- foreach (var blobId in blobIds)
- {
- try
- {
- using var blobMessage = new HttpRequestMessage();
- blobMessage.RequestUri = new Uri($"api/stream/{streamId}/blob/{blobId}", UriKind.Relative);
- blobMessage.Method = HttpMethod.Get;
-
- using var response = await _client.SendAsync(blobMessage, CancellationToken).ConfigureAwait(false);
- response.Content.Headers.TryGetValues("Content-Disposition", out IEnumerable? cdHeaderValues);
-
- var cdHeader = cdHeaderValues?.FirstOrDefault();
- string? fileName = cdHeader?.Split(s_filenameSeparator, StringSplitOptions.None)[1].TrimStart('"').TrimEnd('"');
-
- string fileLocation = Path.Combine(BlobStorageFolder, $"{blobId[..Blob.LocalHashPrefixLength]}-{fileName}");
- using var source = new ProgressStream(
- await response.Content.ReadAsStreamAsync().ConfigureAwait(false),
- response.Content.Headers.ContentLength,
- progress,
- true
- );
- using var fs = new FileStream(fileLocation, FileMode.OpenOrCreate);
- await source.CopyToAsync(fs).ConfigureAwait(false);
- }
- catch (Exception ex) when (!ex.IsFatal())
- {
- throw new SpeckleException($"Failed to download blob {blobId}", ex);
- }
- }
- }
-
- private async Task DownloadObjectsImpl(
- string streamId,
- IReadOnlyList objectIds,
- IProgress? progress,
- CbObjectDownloaded onObjectCallback
- )
- {
- // Stopwatch sw = new Stopwatch(); sw.Start();
-
- CancellationToken.ThrowIfCancellationRequested();
-
- using var childrenHttpMessage = new HttpRequestMessage
- {
- RequestUri = new Uri($"/api/getobjects/{streamId}", UriKind.Relative),
- Method = HttpMethod.Post,
- };
-
- Dictionary postParameters = new() { { "objects", JsonConvert.SerializeObject(objectIds) } };
- string serializedPayload = JsonConvert.SerializeObject(postParameters);
- childrenHttpMessage.Content = new StringContent(serializedPayload, Encoding.UTF8, "application/json");
- childrenHttpMessage.Headers.Add("Accept", "text/plain");
-
- HttpResponseMessage childrenHttpResponse = await _client
- .SendAsync(childrenHttpMessage, CancellationToken)
- .ConfigureAwait(false);
-
- await ResponseProgress(childrenHttpResponse, progress, onObjectCallback, false).ConfigureAwait(false);
- }
-
- private async Task ResponseProgress(
- HttpResponseMessage childrenHttpResponse,
- IProgress? progress,
- CbObjectDownloaded onObjectCallback,
- bool isSingle
- )
- {
- childrenHttpResponse.EnsureSuccessStatusCode();
- var length = childrenHttpResponse.Content.Headers.ContentLength;
- using Stream childrenStream = await childrenHttpResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);
-
- using var reader = new StreamReader(new ProgressStream(childrenStream, length, progress, true), Encoding.UTF8);
- while (await reader.ReadLineAsync().ConfigureAwait(false) is { } line)
- {
- CancellationToken.ThrowIfCancellationRequested();
-
- if (!isSingle)
- {
- var pcs = line.Split(s_separator, 2);
- onObjectCallback(pcs[0], pcs[1]);
- }
- else
- {
- onObjectCallback(string.Empty, line);
- break;
- }
- }
- }
-
- private async Task> HasObjectsImpl(string streamId, IReadOnlyList objectIds)
- {
- CancellationToken.ThrowIfCancellationRequested();
-
- // Stopwatch sw = new Stopwatch(); sw.Start();
-
- string objectsPostParameter = JsonConvert.SerializeObject(objectIds);
- var payload = new Dictionary { { "objects", objectsPostParameter } };
- string serializedPayload = JsonConvert.SerializeObject(payload);
- var uri = new Uri($"/api/diff/{streamId}", UriKind.Relative);
-
- using StringContent stringContent = new(serializedPayload, Encoding.UTF8, "application/json");
- var response = await _client.PostAsync(uri, stringContent, CancellationToken).ConfigureAwait(false);
-
- response.EnsureSuccessStatusCode();
-
- var hasObjectsJson = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
- Dictionary hasObjects = new();
-
- JObject doc = JObject.Parse(hasObjectsJson);
- foreach (KeyValuePair prop in doc)
- {
- hasObjects[prop.Key] = (bool)prop.Value.NotNull();
- }
- return hasObjects;
- }
-
- private async Task UploadObjectsImpl(
- string streamId,
- List> multipartedObjects,
- IProgress? progress
- )
- {
- CancellationToken.ThrowIfCancellationRequested();
-
- using HttpRequestMessage message = new()
- {
- RequestUri = new Uri($"/objects/{streamId}", UriKind.Relative),
- Method = HttpMethod.Post,
- };
-
- MultipartFormDataContent multipart = new();
-
- int mpId = 0;
- foreach (List<(string, string)> mpData in multipartedObjects)
- {
- mpId++;
-
- var ctBuilder = new StringBuilder("[");
- for (int i = 0; i < mpData.Count; i++)
- {
- if (i > 0)
- {
- ctBuilder.Append(',');
- }
-
- ctBuilder.Append(mpData[i].Item2);
- }
- ctBuilder.Append(']');
- string ct = ctBuilder.ToString();
-
- if (CompressPayloads)
- {
- var content = new GzipContent(new StringContent(ct, Encoding.UTF8));
- content.Headers.ContentType = new MediaTypeHeaderValue("application/gzip");
- multipart.Add(content, $"batch-{mpId}", $"batch-{mpId}");
- }
- else
- {
- multipart.Add(new StringContent(ct, Encoding.UTF8), $"batch-{mpId}", $"batch-{mpId}");
- }
- }
- message.Content = new ProgressContent(multipart, progress);
- var response = await _client.SendAsync(message, CancellationToken).ConfigureAwait(false);
-
- response.EnsureSuccessStatusCode();
- }
-
- public async Task> HasBlobs(string streamId, IReadOnlyList blobIds)
- {
- CancellationToken.ThrowIfCancellationRequested();
-
- var payload = JsonConvert.SerializeObject(blobIds);
- var uri = new Uri($"/api/stream/{streamId}/blob/diff", UriKind.Relative);
-
- using StringContent stringContent = new(payload, Encoding.UTF8, "application/json");
-
- var response = await _client.PostAsync(uri, stringContent, CancellationToken).ConfigureAwait(false);
-
- response.EnsureSuccessStatusCode();
-
- var responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
- var parsed = JsonConvert.DeserializeObject>(responseString);
- if (parsed is null)
- {
- throw new SpeckleException($"Failed to deserialize successful response {response.Content}");
- }
-
- return parsed;
- }
-}
diff --git a/src/Speckle.Sdk/Transports/TransportHelpers.cs b/src/Speckle.Sdk/Transports/TransportHelpers.cs
deleted file mode 100644
index cdc52c98..00000000
--- a/src/Speckle.Sdk/Transports/TransportHelpers.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-using System.Diagnostics.CodeAnalysis;
-using Speckle.Sdk.Serialisation.Utilities;
-
-namespace Speckle.Sdk.Transports;
-
-public static class TransportHelpers
-{
- public static async Task CopyObjectAndChildrenAsync(
- string id,
- ITransport sourceTransport,
- ITransport targetTransport,
- CancellationToken cancellationToken
- )
- {
- if (string.IsNullOrEmpty(id))
- {
- throw new ArgumentException("Cannot copy object with empty id", nameof(id));
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- var parent = await sourceTransport.GetObject(id).ConfigureAwait(false);
- if (parent is null)
- {
- throw new TransportException(
- $"Requested id {id} was not found within this transport {sourceTransport.TransportName}"
- );
- }
-
- targetTransport.SaveObject(id, parent);
-
- var closures = ClosureParser.GetChildrenIds(parent, cancellationToken).ToList();
-
- foreach (var closure in closures)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- //skips blobs because ServerTransport downloads things separately
- if (closure.StartsWith("blob:"))
- {
- continue;
- }
- var child = await sourceTransport.GetObject(closure).ConfigureAwait(false);
- if (child is null)
- {
- throw new TransportException(
- $"Closure id {closure} was not found within this transport {sourceTransport.TransportName}"
- );
- }
-
- targetTransport.SaveObject(closure, child);
- }
-
- return parent;
- }
-
- [SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Deserialization target for DTO")]
- internal sealed class Placeholder
- {
- public Dictionary? __closure { get; set; }
- }
-}
diff --git a/src/Speckle.Sdk/Transports/Utilities.cs b/src/Speckle.Sdk/Transports/Utilities.cs
deleted file mode 100644
index 3fe06faf..00000000
--- a/src/Speckle.Sdk/Transports/Utilities.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-namespace Speckle.Sdk.Transports;
-
-public static class Utilities
-{
- ///
- /// Waits until the provided function returns true.
- ///
- ///
- ///
- ///
- public static async Task WaitUntil(Func condition, int frequency = 25)
- {
- while (!condition())
- {
- await Task.Delay(frequency).ConfigureAwait(false);
- }
- }
-}
diff --git a/tests/Speckle.Objects.Tests.Unit/ModelPropertySupportedTypes.cs b/tests/Speckle.Objects.Tests.Unit/ModelPropertySupportedTypes.cs
index c3e8e805..68936423 100644
--- a/tests/Speckle.Objects.Tests.Unit/ModelPropertySupportedTypes.cs
+++ b/tests/Speckle.Objects.Tests.Unit/ModelPropertySupportedTypes.cs
@@ -4,7 +4,6 @@
using Speckle.Newtonsoft.Json;
using Speckle.Sdk.Host;
using Speckle.Sdk.Models;
-using Speckle.Sdk.Serialisation;
namespace Speckle.Objects.Tests.Unit;
@@ -28,7 +27,6 @@ public ModelPropertySupportedTypes()
///
/// If you're tempted to add to this list, please ensure both our serializer and deserializer support properties of this type
/// Check the
- /// Check the
/// (or is an interface where all concrete types are supported)
/// You should also consider adding a test in SerializerNonBreakingChanges
///
diff --git a/tests/Speckle.Sdk.Serialization.Tests/DetachedTests.cs b/tests/Speckle.Sdk.Serialization.Tests/DetachedTests.cs
index f41a5c10..693c5ab4 100644
--- a/tests/Speckle.Sdk.Serialization.Tests/DetachedTests.cs
+++ b/tests/Speckle.Sdk.Serialization.Tests/DetachedTests.cs
@@ -48,21 +48,6 @@ public async Task CanSerialize_New_Detached()
await VerifyJsonDictionary(objects);
}
- [Fact]
- public async Task CanSerialize_Old_Detached()
- {
- var @base = new SampleObjectBase();
- @base["dynamicProp"] = 123;
- @base.detachedProp = new SamplePropBase() { name = "detachedProp" };
- @base.attachedProp = new SamplePropBase() { name = "attachedProp" };
-
- var objects = new ConcurrentDictionary();
- var serializer = new SpeckleObjectSerializer(new[] { new MemoryTransport(objects) });
- serializer.Serialize(@base);
-
- await VerifyJsonDictionary(objects);
- }
-
[Fact]
public async Task GetPropertiesExpected_Detached()
{
diff --git a/tests/Speckle.Sdk.Serialization.Tests/Framework/TestTransport.cs b/tests/Speckle.Sdk.Serialization.Tests/Framework/TestTransport.cs
deleted file mode 100644
index 0ca8d458..00000000
--- a/tests/Speckle.Sdk.Serialization.Tests/Framework/TestTransport.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using Speckle.Sdk.Transports;
-
-namespace Speckle.Sdk.Serialization.Tests.Framework;
-
-public class TestTransport(IReadOnlyDictionary objects) : ITransport
-{
- public string TransportName
- {
- get => "Test";
- set { }
- }
-
- public Dictionary TransportContext { get; }
- public TimeSpan Elapsed { get; }
- public int SavedObjectCount { get; }
- public CancellationToken CancellationToken { get; set; }
- public IProgress? OnProgressAction { get; set; }
- public Action? OnErrorAction { get; set; }
-
- public void BeginWrite() => throw new NotImplementedException();
-
- public void EndWrite() => throw new NotImplementedException();
-
- public void SaveObject(string id, string serializedObject) => throw new NotImplementedException();
-
- public Task WriteComplete() => throw new NotImplementedException();
-
- public Task GetObject(string id) => Task.FromResult(objects.GetValueOrDefault(id));
-
- public Task CopyObjectAndChildren(string id, ITransport targetTransport) =>
- throw new NotImplementedException();
-
- public Task> HasObjects(IReadOnlyList objectIds) =>
- throw new NotImplementedException();
-}
diff --git a/tests/Speckle.Sdk.Serialization.Tests/Framework/TestTransport2.cs b/tests/Speckle.Sdk.Serialization.Tests/Framework/TestTransport2.cs
deleted file mode 100644
index e9a3f1f6..00000000
--- a/tests/Speckle.Sdk.Serialization.Tests/Framework/TestTransport2.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using Speckle.Sdk.Transports;
-
-namespace Speckle.Sdk.Serialization.Tests.Framework;
-
-public class TestTransport2(IDictionary objects) : ITransport
-{
- public IDictionary Objects { get; } = objects;
-
- public string TransportName
- {
- get => "Test";
- set { }
- }
-
- public Dictionary TransportContext { get; }
- public TimeSpan Elapsed { get; }
- public int SavedObjectCount { get; }
- public CancellationToken CancellationToken { get; set; }
- public IProgress? OnProgressAction { get; set; }
- public Action? OnErrorAction { get; set; }
-
- public void BeginWrite() => throw new NotImplementedException();
-
- public void EndWrite() => throw new NotImplementedException();
-
- public void SaveObject(string id, string serializedObject) => Objects[id] = serializedObject;
-
- public Task WriteComplete() => throw new NotImplementedException();
-
- public Task GetObject(string id) => Task.FromResult(Objects.TryGetValue(id, out string? o) ? o : null);
-
- public Task CopyObjectAndChildren(string id, ITransport targetTransport) =>
- throw new NotImplementedException();
-
- public Task> HasObjects(IReadOnlyList objectIds) =>
- throw new NotImplementedException();
-}
diff --git a/tests/Speckle.Sdk.Serialization.Tests/SerializationTests.cs b/tests/Speckle.Sdk.Serialization.Tests/SerializationTests.cs
deleted file mode 100644
index f2b3bc6a..00000000
--- a/tests/Speckle.Sdk.Serialization.Tests/SerializationTests.cs
+++ /dev/null
@@ -1,250 +0,0 @@
-using System.Collections.Concurrent;
-using FluentAssertions;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging.Abstractions;
-using Speckle.Newtonsoft.Json;
-using Speckle.Newtonsoft.Json.Linq;
-using Speckle.Objects.Geometry;
-using Speckle.Sdk.Common;
-using Speckle.Sdk.Host;
-using Speckle.Sdk.Models;
-using Speckle.Sdk.Serialisation;
-using Speckle.Sdk.Serialisation.Utilities;
-using Speckle.Sdk.Serialisation.V2;
-using Speckle.Sdk.Serialisation.V2.Receive;
-using Speckle.Sdk.Serialisation.V2.Send;
-using Speckle.Sdk.Serialization.Tests.Framework;
-using Speckle.Sdk.SQLite;
-using Speckle.Sdk.Testing.Framework;
-
-namespace Speckle.Sdk.Serialization.Tests;
-
-public class SerializationTests
-{
- private readonly ISerializeProcessFactory _factory;
-
- public SerializationTests()
- {
- var serviceCollection = new ServiceCollection();
- serviceCollection.AddSpeckleSdk(new("Tests", "test"), "v3", typeof(TestClass).Assembly, typeof(Polyline).Assembly);
- var serviceProvider = serviceCollection.BuildServiceProvider();
-
- _factory = serviceProvider.GetRequiredService();
- }
-
- public class TestObjectLoader(IReadOnlyDictionary idToObject) : IObjectLoader
- {
- public Task<(Json, IReadOnlyCollection)> GetAndCache(string rootId, DeserializeProcessOptions? options)
- {
- var json = idToObject.GetValueOrDefault(rootId);
- if (json == null)
- {
- throw new KeyNotFoundException("Root not found");
- }
-
- var allChildren = ClosureParser.GetChildrenIds(json, default).Select(x => new Id(x)).ToList();
- return Task.FromResult<(Json, IReadOnlyCollection)>((new(json), allChildren));
- }
-
- public string? LoadId(string id) => idToObject.GetValueOrDefault(id);
-
- public void Dispose() { }
- }
-
- /* [Theory]
- [InlineData("RevitObject.json.gz")]
- public async Task Basic_Namespace_Validation(string fileName)
- {
- var closures = TestFileManager.GetFileAsClosures(fileName);
- var deserializer = new SpeckleObjectDeserializer
- {
- ReadTransport = new TestTransport(closures),
- CancellationToken = default,
- };
-
- foreach (var (id, objJson) in closures)
- {
- var jObject = JObject.Parse(objJson);
- var oldSpeckleType = jObject["speckle_type"].NotNull().Value().NotNull();
- var starts = oldSpeckleType.StartsWith("Speckle.Core.") || oldSpeckleType.StartsWith("Objects.");
- starts.Should().BeTrue($"{oldSpeckleType} isn't expected");
-
- var baseType = await deserializer.DeserializeAsync(objJson);
- baseType.id.Should().Be(id);
-
- var oldType = TypeLoader.GetAtomicType(oldSpeckleType);
- if (oldType == typeof(Base))
- {
- oldSpeckleType.Should().NotContain("Base");
- }
- else
- {
- starts = baseType.speckle_type.StartsWith("Speckle.Core.") || baseType.speckle_type.StartsWith("Objects.");
- starts.Should().BeTrue($"{baseType.speckle_type} isn't expected");
-
- var type = TypeLoader.GetAtomicType(baseType.speckle_type);
- type.Should().NotBeNull();
- var name = TypeLoader.GetTypeString(type) ?? throw new ArgumentNullException($"Could not find: {type}");
- starts = name.StartsWith("Speckle.Core") || name.StartsWith("Objects");
- starts.Should().BeTrue($"{name} isn't expected");
- }
- }
- }*/
-
- [Theory]
- [InlineData("RevitObject.json.gz")]
- public async Task Basic_Namespace_Validation_New(string fileName)
- {
- var closures = TestFileManager.GetFileAsClosures(fileName);
- await using var process = new DeserializeProcess(
- new TestObjectLoader(closures),
- null,
- new BaseDeserializer(new ObjectDeserializerFactory()),
- new NullLoggerFactory(),
- default
- );
- await process.Deserialize("3416d3fe01c9196115514c4a2f41617b");
- foreach (var (id, objJson) in closures)
- {
- var jObject = JObject.Parse(objJson);
- var oldSpeckleType = jObject["speckle_type"].NotNull().Value().NotNull();
- var starts = oldSpeckleType.StartsWith("Speckle.Core.") || oldSpeckleType.StartsWith("Objects.");
- starts.Should().BeTrue($"{oldSpeckleType} isn't expected");
-
- var oldType = TypeLoader.GetAtomicType(oldSpeckleType);
- if (oldType == typeof(Base))
- {
- oldSpeckleType.Should().NotContain("Base");
- }
- else
- {
- var baseType = process.BaseCache[new Id(id)];
-
- starts = baseType.speckle_type.StartsWith("Speckle.Core.") || baseType.speckle_type.StartsWith("Objects.");
- starts.Should().BeTrue($"{baseType.speckle_type} isn't expected");
-
- var type = TypeLoader.GetAtomicType(baseType.speckle_type);
- type.Should().NotBeNull();
- var name = TypeLoader.GetTypeString(type) ?? throw new ArgumentNullException();
- starts = name.StartsWith("Speckle.Core") || name.StartsWith("Objects");
- starts.Should().BeTrue($"{name} isn't expected");
- }
- }
- }
-
- [Theory]
- [InlineData(
- "{\"applicationId\":null,\"speckle_type\":\"Base\",\"IFC_GUID\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcGUID\",\"units\":null,\"value\":\"18HX_ys0P5uu77f1wwA7bn\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"IFC_GUID\",\"id\":\"1f4e29b7198e25221300c684876ec187\"},\"DOOR_COST\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Cost\",\"units\":\"\u0e3f\",\"value\":null,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:currency-1.0.0\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"DOOR_COST\",\"id\":\"80ff4c5df5170b75916a873a394cfbdf\"},\"ALL_MODEL_URL\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"URL\",\"units\":null,\"value\":null,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ALL_MODEL_URL\",\"id\":\"140c53fcea5deaa35115b23cd2ba48c6\"},\"IFC_TYPE_GUID\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type IfcGUID\",\"units\":null,\"value\":\"0w69BRwHvBsBXN3bEBjQin\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"IFC_TYPE_GUID\",\"id\":\"99d5d914df5c50c879e73c50246a9249\"},\"KEYNOTE_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Keynote\",\"units\":null,\"value\":\"S0905\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"KEYNOTE_PARAM\",\"id\":\"c2272311800b04ab4d2b0052df68ecdc\"},\"PHASE_CREATED\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Phase Created\",\"units\":null,\"value\":\"0\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"PHASE_CREATED\",\"id\":\"72ecbbd5d29ea1b48df89d8f88b29120\"},\"ALL_MODEL_MARK\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Mark\",\"units\":null,\"value\":null,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ALL_MODEL_MARK\",\"id\":\"f2e0ed6ebfbab4d4780c5143b774558e\"},\"FUNCTION_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Function\",\"units\":null,\"value\":0,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"FUNCTION_PARAM\",\"id\":\"a43000484f3fa3c5cf60a2ccd79a573c\"},\"UNIFORMAT_CODE\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Assembly Code\",\"units\":null,\"value\":\"\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"UNIFORMAT_CODE\",\"id\":\"b797bb20d49af57eecbe718df4ebd411\"},\"WINDOW_TYPE_ID\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type Mark\",\"units\":null,\"value\":null,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"WINDOW_TYPE_ID\",\"id\":\"d74f026a13a539bd24369ea78b34aa6b\"},\"ALL_MODEL_IMAGE\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Image\",\"units\":null,\"value\":\"-1\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ALL_MODEL_IMAGE\",\"id\":\"4ef25c5fcd2ee32d9b3d6ce9b1047904\"},\"ALL_MODEL_MODEL\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Model\",\"units\":null,\"value\":null,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ALL_MODEL_MODEL\",\"id\":\"13597261389f532c0778e134623eff85\"},\"CLEAR_COVER_TOP\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Rebar Cover - Top Face\",\"units\":null,\"value\":\"95743\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"CLEAR_COVER_TOP\",\"id\":\"ed5f5e056314ee8435a1658a54261e94\"},\"ELEM_TYPE_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type\",\"units\":null,\"value\":\"5432827\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ELEM_TYPE_PARAM\",\"id\":\"e502cb5aed1fda357926c7ca9927c42c\"},\"RELATED_TO_MASS\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Related to Mass\",\"units\":null,\"value\":false,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"RELATED_TO_MASS\",\"id\":\"a43b8424564b8f14738f4dbaa78be150\"},\"SYMBOL_ID_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type Id\",\"units\":null,\"value\":\"5432827\",\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"SYMBOL_ID_PARAM\",\"id\":\"1947cb98e61f79da57f573a3a785b436\"},\"DESIGN_OPTION_ID\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Design Option\",\"units\":null,\"value\":\"-1\",\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"DESIGN_OPTION_ID\",\"id\":\"cf30f731c41543dd134a4877fbdab105\"},\"PHASE_DEMOLISHED\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Phase Demolished\",\"units\":null,\"value\":\"-1\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"PHASE_DEMOLISHED\",\"id\":\"47066bac7728f9b93f4acdb697284a59\"},\"CLEAR_COVER_OTHER\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Rebar Cover - Other Faces\",\"units\":null,\"value\":\"95743\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"CLEAR_COVER_OTHER\",\"id\":\"1afe7d22a897aff809bd92aea1acafd2\"},\"ELEM_FAMILY_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Family\",\"units\":null,\"value\":\"5432827\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ELEM_FAMILY_PARAM\",\"id\":\"ec3a159572a58b85b3a3650e5cc23e90\"},\"CLEAR_COVER_BOTTOM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Rebar Cover - Bottom Face\",\"units\":null,\"value\":\"95743\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"CLEAR_COVER_BOTTOM\",\"id\":\"476161776fc4c6ceb3c544c792a08120\"},\"HOST_AREA_COMPUTED\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Area\",\"units\":\"m\u00b2\",\"value\":7.128858225722908,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":\"autodesk.unit.unit:squareMeters-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"HOST_AREA_COMPUTED\",\"id\":\"5e7567fea07a98c0cdd4903cabd897a3\"},\"IFC_EXPORT_ELEMENT\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Export to IFC\",\"units\":null,\"value\":0,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"IFC_EXPORT_ELEMENT\",\"id\":\"7de623e201f3fcfb16dbcacefe7f8403\"},\"totalChildrenCount\":0,\"ALL_MODEL_TYPE_NAME\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type Name\",\"units\":null,\"value\":null,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ALL_MODEL_TYPE_NAME\",\"id\":\"4699f3fc2fd4e84cc3b6296ded7225b5\"},\"DESIGN_OPTION_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Design Option\",\"units\":null,\"value\":\"Main Model\",\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"DESIGN_OPTION_PARAM\",\"id\":\"771449eae7f2fb96345b165954b2c797\"},\"ELEM_CATEGORY_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Category\",\"units\":null,\"value\":\"-2000032\",\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ELEM_CATEGORY_PARAM\",\"id\":\"fb0b948f7360b9415ea9ede20fb3cdd2\"},\"ALL_MODEL_TYPE_IMAGE\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type Image\",\"units\":null,\"value\":\"-1\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ALL_MODEL_TYPE_IMAGE\",\"id\":\"4c95be61c11f5609f1fa649804bf9814\"},\"ANALYTICAL_ROUGHNESS\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Roughness\",\"units\":null,\"value\":1,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ANALYTICAL_ROUGHNESS\",\"id\":\"eab64895ad089cf272ae6a7431f4cdac\"},\"HOST_VOLUME_COMPUTED\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Volume\",\"units\":\"m\u00b3\",\"value\":1.413211687704679,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":\"autodesk.unit.unit:cubicMeters-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"HOST_VOLUME_COMPUTED\",\"id\":\"4cac83d2757bc70d7e1f299de124d028\"},\"SCHEDULE_LEVEL_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Level\",\"units\":null,\"value\":\"1100600\",\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"SCHEDULE_LEVEL_PARAM\",\"id\":\"d0ab715757ddbaedf5dc2df0726ed38c\"},\"ALL_MODEL_DESCRIPTION\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Description\",\"units\":null,\"value\":null,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ALL_MODEL_DESCRIPTION\",\"id\":\"4abdaadbe23c12c349c65abcd5979f56\"},\"IFC_EXPORT_ELEMENT_AS\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Export to IFC As\",\"units\":null,\"value\":null,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"IFC_EXPORT_ELEMENT_AS\",\"id\":\"ccacac43b32ffd10c88a870492f98f96\"},\"UNIFORMAT_DESCRIPTION\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Assembly Description\",\"units\":null,\"value\":\"\",\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"UNIFORMAT_DESCRIPTION\",\"id\":\"abfe7173561e8b86cae8aa8dc34743d1\"},\"ALL_MODEL_MANUFACTURER\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Manufacturer\",\"units\":null,\"value\":null,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ALL_MODEL_MANUFACTURER\",\"id\":\"e280ae740be9133f1001f218a137bb2f\"},\"ANALYTICAL_ABSORPTANCE\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Absorptance\",\"units\":null,\"value\":0.1,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ANALYTICAL_ABSORPTANCE\",\"id\":\"e1618d04224fb3e11e650f8854e5eddb\"},\"ELEM_CATEGORY_PARAM_MT\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Category\",\"units\":null,\"value\":\"-2000032\",\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ELEM_CATEGORY_PARAM_MT\",\"id\":\"d4119e43880a3cc8632a137d4f3372ae\"},\"ALL_MODEL_TYPE_COMMENTS\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type Comments\",\"units\":null,\"value\":null,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ALL_MODEL_TYPE_COMMENTS\",\"id\":\"8ea15d6198e1f5c632df36270be5433e\"},\"ANALYTICAL_THERMAL_MASS\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Thermal Mass\",\"units\":\"kJ/(m\u00b2·K)\",\"value\":null,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":\"autodesk.unit.unit:kilojoulesPerSquareMeterKelvin-1.0.0\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ANALYTICAL_THERMAL_MASS\",\"id\":\"d8b711b81d9e0ad7f072b60b69bd0239\"},\"HOST_PERIMETER_COMPUTED\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Perimeter\",\"units\":\"mm\",\"value\":11098.801755409942,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":\"autodesk.unit.unit:millimeters-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"HOST_PERIMETER_COMPUTED\",\"id\":\"eb73365794668bf73b3ffd2c80162ee1\"},\"IFC_EXPORT_ELEMENT_TYPE\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Export Type to IFC\",\"units\":null,\"value\":0,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"IFC_EXPORT_ELEMENT_TYPE\",\"id\":\"0a96867bd313e951c229fb92b346b516\"},\"WALL_ATTR_ROOM_BOUNDING\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Room Bounding\",\"units\":null,\"value\":true,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"WALL_ATTR_ROOM_BOUNDING\",\"id\":\"fdf5bd19ac0a9f2878323c71e4ae80ea\"},\"FLOOR_STRUCTURE_ID_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Structure\",\"units\":null,\"value\":null,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"FLOOR_STRUCTURE_ID_PARAM\",\"id\":\"43b858d8cfaf2bd27cb0b466dc6d425b\"},\"SYMBOL_FAMILY_NAME_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Family Name\",\"units\":null,\"value\":null,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"SYMBOL_FAMILY_NAME_PARAM\",\"id\":\"d96f492f43f2b0e11ce86d66c23caf0f\"},\"IFC_EXPORT_PREDEFINEDTYPE\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IFC Predefined Type\",\"units\":null,\"value\":null,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"IFC_EXPORT_PREDEFINEDTYPE\",\"id\":\"b927074616bc0e6e323b52a99867b907\"},\"STRUCTURAL_MATERIAL_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Structural Material\",\"units\":null,\"value\":\"215194\",\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"STRUCTURAL_MATERIAL_PARAM\",\"id\":\"1f7ffc00602d1944892885d68dff8867\"},\"ELEM_FAMILY_AND_TYPE_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Family and Type\",\"units\":null,\"value\":\"5432827\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ELEM_FAMILY_AND_TYPE_PARAM\",\"id\":\"9d58f36db73c0c248a6db682a6c6a6a0\"},\"FLOOR_ATTR_THICKNESS_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Thickness\",\"units\":\"mm\",\"value\":200,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":\"autodesk.unit.unit:millimeters-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"FLOOR_ATTR_THICKNESS_PARAM\",\"id\":\"22c96d409372e700936805b825b574e6\"},\"IFC_EXPORT_ELEMENT_TYPE_AS\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Export Type to IFC As\",\"units\":null,\"value\":null,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"IFC_EXPORT_ELEMENT_TYPE_AS\",\"id\":\"41a53fec385581b6af53942aff3cd2d3\"},\"ALL_MODEL_INSTANCE_COMMENTS\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Comments\",\"units\":null,\"value\":\"\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ALL_MODEL_INSTANCE_COMMENTS\",\"id\":\"ca8cdcc0b3fd824a34dcb42749151cd1\"},\"STRUCTURAL_ELEVATION_AT_TOP\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Elevation at Top\",\"units\":\"mm\",\"value\":21099.999999999898,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":\"autodesk.unit.unit:millimeters-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"STRUCTURAL_ELEVATION_AT_TOP\",\"id\":\"b1f293a63ff03c0c7456f8ba7b703f4f\"},\"FLOOR_HEIGHTABOVELEVEL_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Height Offset From Level\",\"units\":\"mm\",\"value\":-100,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:millimeters-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"FLOOR_HEIGHTABOVELEVEL_PARAM\",\"id\":\"312773813c84648fc5ff2d78a8d8d8bc\"},\"ANALYTICAL_THERMAL_RESISTANCE\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Thermal Resistance (R)\",\"units\":\"(m\u00b2·K)/W\",\"value\":null,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":\"autodesk.unit.unit:squareMeterKelvinsPerWatt-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ANALYTICAL_THERMAL_RESISTANCE\",\"id\":\"fcfa5d36d656d4f8ca2b883a17c310b8\"},\"IFC_EXPORT_PREDEFINEDTYPE_TYPE\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type IFC Predefined Type\",\"units\":null,\"value\":null,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"IFC_EXPORT_PREDEFINEDTYPE_TYPE\",\"id\":\"ac166cbccbcd8335272956f09d8d5d42\"},\"STRUCTURAL_ELEVATION_AT_BOTTOM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Elevation at Bottom\",\"units\":\"mm\",\"value\":20899.9999999999,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":\"autodesk.unit.unit:millimeters-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"STRUCTURAL_ELEVATION_AT_BOTTOM\",\"id\":\"d9e8f0e4b57b00ca99d13df99ea6ac26\"},\"COARSE_SCALE_FILL_PATTERN_COLOR\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Coarse Scale Fill Color\",\"units\":null,\"value\":0,\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"COARSE_SCALE_FILL_PATTERN_COLOR\",\"id\":\"854d889fd71071f3b81d0e06f7f1095c\"},\"STRUCTURAL_FLOOR_CORE_THICKNESS\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Core Thickness\",\"units\":\"mm\",\"value\":199.99999999999784,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":\"autodesk.unit.unit:millimeters-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"STRUCTURAL_FLOOR_CORE_THICKNESS\",\"id\":\"6fb6fb65a394c5c68d5a760289c1129d\"},\"STRUCTURAL_ELEVATION_AT_TOP_CORE\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Elevation at Top Core\",\"units\":\"mm\",\"value\":21099.999999999898,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":\"autodesk.unit.unit:millimeters-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"STRUCTURAL_ELEVATION_AT_TOP_CORE\",\"id\":\"aed8eedfb2527594e14ae4e5f74fb5c1\"},\"ANALYTICAL_ELEMENT_HAS_ASSOCIATION\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Has Association\",\"units\":null,\"value\":true,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ANALYTICAL_ELEMENT_HAS_ASSOCIATION\",\"id\":\"c9a6f771f05ef6072100c59c672dfb77\"},\"COARSE_SCALE_FILL_PATTERN_ID_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Coarse Scale Fill Pattern\",\"units\":null,\"value\":\"-1\",\"isShared\":false,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"COARSE_SCALE_FILL_PATTERN_ID_PARAM\",\"id\":\"1d271f4d80ffe772f9f8896971050ccc\"},\"FLOOR_ATTR_DEFAULT_THICKNESS_PARAM\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Default Thickness\",\"units\":\"mm\",\"value\":200,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":\"autodesk.unit.unit:millimeters-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"FLOOR_ATTR_DEFAULT_THICKNESS_PARAM\",\"id\":\"271b8b7f7e29c45065c1ccaa1095b32e\"},\"STRUCTURAL_ELEVATION_AT_TOP_SURVEY\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Elevation at Top Survey\",\"units\":\"mm\",\"value\":30899.999999999894,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":\"autodesk.unit.unit:millimeters-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"STRUCTURAL_ELEVATION_AT_TOP_SURVEY\",\"id\":\"8f2b9f55e373736263d14002838194b4\"},\"STRUCTURAL_ELEVATION_AT_BOTTOM_CORE\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Elevation at Bottom Core\",\"units\":\"mm\",\"value\":20899.9999999999,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":\"autodesk.unit.unit:millimeters-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"STRUCTURAL_ELEVATION_AT_BOTTOM_CORE\",\"id\":\"7a0b7e496383d08605ccb8c776cedbbf\"},\"02b58af4-afcd-404b-9011-3a25d6816e1b\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"ClassificationCode\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"02b58af4-afcd-404b-9011-3a25d6816e1b\",\"id\":\"141f6b021c701e5b4b4ee430652f7f91\"},\"042673e7-8ac4-413d-a393-e0785fbf8889\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Data 3\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"042673e7-8ac4-413d-a393-e0785fbf8889\",\"id\":\"f415ff5c972f45d7e0090b0849c54677\"},\"07afa150-f11f-40a1-a173-7a77ea32cf96\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Data 6\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"07afa150-f11f-40a1-a173-7a77ea32cf96\",\"id\":\"0e3acdf0b385d32d68caa8753c710849\"},\"07b6cf99-a3d2-4d7a-9ea4-246058cfae1a\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Classification.OmniClass.22.Description\",\"units\":null,\"value\":\"High-Tolerance Concrete Floor Finishing\",\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"07b6cf99-a3d2-4d7a-9ea4-246058cfae1a\",\"id\":\"8957914412a138b6255452dd485a25bd\"},\"082d16bb-7cf9-4968-ac22-b6f6ae068028\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcName\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"082d16bb-7cf9-4968-ac22-b6f6ae068028\",\"id\":\"f313bddfb2c5cf9f825ee6653021b04e\"},\"087f96a5-2dd2-42bb-a170-c22485216c09\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Element Condition\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"087f96a5-2dd2-42bb-a170-c22485216c09\",\"id\":\"8665b9cec7a39bffa030e6b415f78fa9\"},\"098e8d4f-1431-49e8-8ef6-69516cf72354\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"3D Model Element GUID\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"098e8d4f-1431-49e8-8ef6-69516cf72354\",\"id\":\"b81c608ec3bd9aa08ea1a2c5a2cea206\"},\"0a004b99-d4e6-4db6-8c88-9b77da33f012\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcDescription\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"0a004b99-d4e6-4db6-8c88-9b77da33f012\",\"id\":\"79b720b93988fd7840213380399408dd\"},\"0bf3a5d2-06c0-4b6c-9ba1-6985ef40c2b0\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type Data 6\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"0bf3a5d2-06c0-4b6c-9ba1-6985ef40c2b0\",\"id\":\"622eab526502ce2a848c4aa932554f96\"},\"0c273fd8-260b-4f34-996e-921fa14a47fc\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Data 4\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"0c273fd8-260b-4f34-996e-921fa14a47fc\",\"id\":\"f70edec21839dfc410d94659d18d52c2\"},\"11f34dfe-4592-4c86-a455-2f020d9376e8\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"ClassificationCode\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"11f34dfe-4592-4c86-a455-2f020d9376e8\",\"id\":\"cd817060f1920a0194139bf2cdddecd4\"},\"12e4c976-0b76-4735-8664-e882b410ac7e\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon A1\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"12e4c976-0b76-4735-8664-e882b410ac7e\",\"id\":\"0d2df557e73500e0c3d72c527d4c36fe\"},\"1336888e-1fed-4e9e-b74b-794bff5b6046\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"GrossArea(BaseQuantities)\",\"units\":\"m\u00b2\",\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:squareMeters-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"1336888e-1fed-4e9e-b74b-794bff5b6046\",\"id\":\"7e0f5932a6c5ad37872436b3ed0cf07b\"},\"15212817-1c39-4c7f-bae4-436acd0e4598\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcDescription\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"15212817-1c39-4c7f-bae4-436acd0e4598\",\"id\":\"f2bba2dcccf5786df17c279367baab39\"},\"18a3daed-8579-45e2-97a0-412159986104\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Data 8\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"18a3daed-8579-45e2-97a0-412159986104\",\"id\":\"173b9745cf4e7cc4b67748c5a03acf04\"},\"18ab825f-ba4c-4a8f-b509-5ddd7c378267\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Item\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"18ab825f-ba4c-4a8f-b509-5ddd7c378267\",\"id\":\"2c130ba41bf9797a0e7e64315695dd7b\"},\"1948bc31-5a23-482a-b337-4bd1fce08aec\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Renovation Status(AC_Pset_RenovationAndPhasing)\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"1948bc31-5a23-482a-b337-4bd1fce08aec\",\"id\":\"59b7e7b981f77ff4e7b01e7d794234f9\"},\"1d9a0983-608b-4aed-b03f-f27e8e0e677a\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon B3\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"1d9a0983-608b-4aed-b03f-f27e8e0e677a\",\"id\":\"ec604873c2b3c4d53f5ea9c2e203fc70\"},\"219c8c15-4722-4b40-9e19-7fbbddeee30f\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Data 1\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"219c8c15-4722-4b40-9e19-7fbbddeee30f\",\"id\":\"abc83e3214698cefe7bbd9326b536b1d\"},\"226b84c2-b3b5-4a04-93f7-9523a21ef4e0\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Discipline\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"226b84c2-b3b5-4a04-93f7-9523a21ef4e0\",\"id\":\"f0e8a4a841062c6b5517b30002fd2325\"},\"244a8c27-edd6-4b09-8905-4cf403c61235\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Schedule Type Code/Number\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"244a8c27-edd6-4b09-8905-4cf403c61235\",\"id\":\"1ff35615eaa5e568fdb3c7c1bb21b72a\"},\"2cbf5041-2b36-4c7f-b65e-439af251d9f7\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Unique Asset ID - Equipment Type\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"2cbf5041-2b36-4c7f-b65e-439af251d9f7\",\"id\":\"1339498cfd39967f51d7d5a31feba974\"},\"2eac0fd8-0c8a-4c5a-9d54-62415d708f37\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Elemental Code\",\"units\":null,\"value\":\"UFSB\",\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"2eac0fd8-0c8a-4c5a-9d54-62415d708f37\",\"id\":\"e20483f71786c3e27291a3eeaa049b48\"},\"2f1ef0a4-09a2-4e80-ba30-57984a475e1d\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Unique Asset ID\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"2f1ef0a4-09a2-4e80-ba30-57984a475e1d\",\"id\":\"d083dfba92681370be68f19d761a9628\"},\"2fb9b7d9-d0b0-4ce2-bbc0-02464fda354c\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon C1\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"2fb9b7d9-d0b0-4ce2-bbc0-02464fda354c\",\"id\":\"71196d8fbc135c7386841bb439f8aaee\"},\"3490690f-a8be-46d9-9607-47c255e9ee89\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcExportAs [Type]\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"3490690f-a8be-46d9-9607-47c255e9ee89\",\"id\":\"cf2c87ffe9b6babcd2659d619fe3a4b7\"},\"35064971-5814-4b17-b572-49ea1320c516\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Data 7\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"35064971-5814-4b17-b572-49ea1320c516\",\"id\":\"ecf761dcb34a3e098f355f401eec5738\"},\"36d9a077-9301-47a0-b049-4f29e17d51dd\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcExportAs [Type]\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"36d9a077-9301-47a0-b049-4f29e17d51dd\",\"id\":\"f547ba1d204747353092fccb1970b8ee\"},\"392cae56-cd6b-4946-817f-242686e12441\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Zone\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"392cae56-cd6b-4946-817f-242686e12441\",\"id\":\"37152ed7e04c0ea23c442aad0be9a611\"},\"3c3d55ea-8a2f-41c1-97fd-d222d586b0b1\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Omniclass Classification Type Code\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"3c3d55ea-8a2f-41c1-97fd-d222d586b0b1\",\"id\":\"861b129e42aac2de8166ad76ded0781c\"},\"3d134cec-2e95-4d43-bc4b-e552d382f73c\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcPresentationLayer\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"3d134cec-2e95-4d43-bc4b-e552d382f73c\",\"id\":\"030b7e2842cde1ce8a1d8e545d0e068a\"},\"3f146225-bd3e-448b-b180-034b880bd662\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon A5\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"3f146225-bd3e-448b-b180-034b880bd662\",\"id\":\"3d573a80fc3bd747d292d75c7751c523\"},\"3f9a284a-7485-460c-b827-9df8cd50720e\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Classification.OmniClass.21.Description\",\"units\":null,\"value\":\"Insitu Concrete\",\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"3f9a284a-7485-460c-b827-9df8cd50720e\",\"id\":\"632da2ce6a52e51df22a05b776421b49\"},\"40e844db-ba22-4ddc-bc15-1fc47f5b12e7\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcSpatialContainer\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"40e844db-ba22-4ddc-bc15-1fc47f5b12e7\",\"id\":\"a8e9d80b93cd5f965d00aaffad6786e9\"},\"444d97fc-d9b6-4424-943d-37ac498a46c4\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Unique Asset ID - Item Number\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"444d97fc-d9b6-4424-943d-37ac498a46c4\",\"id\":\"d7cf7e867db30b45ac62757a6cc11b1b\"},\"46baf2f0-9232-4c37-aa7c-57e37fd5db17\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Product Type\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"46baf2f0-9232-4c37-aa7c-57e37fd5db17\",\"id\":\"b87714b8187aabde851b900a3755655a\"},\"4803c7b6-ded1-46b1-b5eb-ffe9ddcdc20b\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcSpatialContainer\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"4803c7b6-ded1-46b1-b5eb-ffe9ddcdc20b\",\"id\":\"9d6e8c6691db06081c2332c589ed2a31\"},\"48e76a50-9a4f-47a9-8074-79cc7fce9f14\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcPropertySetList\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"48e76a50-9a4f-47a9-8074-79cc7fce9f14\",\"id\":\"38621e3cf898c0fa404d29b88cf6ea5a\"},\"4ac5fa74-7864-45a3-9d89-1ab998b7731a\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon A3\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"4ac5fa74-7864-45a3-9d89-1ab998b7731a\",\"id\":\"34e84e5eed664e1ca94385e405141ef1\"},\"4c575161-247d-46a5-8ae2-72829f37725f\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"NetArea(BaseQuantities)\",\"units\":\"m\u00b2\",\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:squareMeters-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"4c575161-247d-46a5-8ae2-72829f37725f\",\"id\":\"c5741774b669ce685c5e53a704cc0320\"},\"4d293b70-da1a-4830-80a0-4f63b356ff61\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon A2\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"4d293b70-da1a-4830-80a0-4f63b356ff61\",\"id\":\"e629dd3ed399e1b8328aeaa2e90afae9\"},\"4dabccff-7cc0-42ff-a6db-29a28162d3f3\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type IfcPropertySetList\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"4dabccff-7cc0-42ff-a6db-29a28162d3f3\",\"id\":\"3aa01feade7f65bbd785f7083c04bacf\"},\"4fc2bd83-f0b1-41ba-8663-89e3d7f3e660\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcDescription [Type]\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"4fc2bd83-f0b1-41ba-8663-89e3d7f3e660\",\"id\":\"e7639f4355cebda699431a60345609c4\"},\"50a015d9-917f-4ac5-884e-42f7f36b47b1\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon B5\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"50a015d9-917f-4ac5-884e-42f7f36b47b1\",\"id\":\"9a71039a2e2272f527084f2096f9429b\"},\"51778754-e984-42cf-8a6d-a2226baf316f\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcPresentationLayer\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"51778754-e984-42cf-8a6d-a2226baf316f\",\"id\":\"392005ecea2408b03939ef80fbb34a8f\"},\"5188c780-2bf1-460c-8bbf-043dcb4649eb\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcExportAs\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"5188c780-2bf1-460c-8bbf-043dcb4649eb\",\"id\":\"f38369c4acdfa96b7d45ac6187f8a183\"},\"5402c013-1b09-474f-b399-344a0e55a182\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type Material\",\"units\":null,\"value\":\"PT\",\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"5402c013-1b09-474f-b399-344a0e55a182\",\"id\":\"e8252b753c969222390cf77fc56237ef\"},\"579138d4-c882-45f1-bfd6-5ec6f8189161\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Rate Reo Area\",\"units\":null,\"value\":12,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"579138d4-c882-45f1-bfd6-5ec6f8189161\",\"id\":\"09ca602feb656474737d2cf84e89e26f\"},\"5d8a425f-4cff-44fe-9896-932e8e5639ef\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type Data 5\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"5d8a425f-4cff-44fe-9896-932e8e5639ef\",\"id\":\"805c08afb49a184af8649f63531be0e3\"},\"6248687a-e43d-4380-9f28-b98a14157187\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcExportAs [Type]\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"6248687a-e43d-4380-9f28-b98a14157187\",\"id\":\"c300499e47eea08ec5bfd25acc9abae3\"},\"6cbcfae1-3598-4ddd-a606-41f3788c0362\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Managed Asset YesNo\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"6cbcfae1-3598-4ddd-a606-41f3788c0362\",\"id\":\"b9473955cae7232a6d5ba4a2c7669e7c\"},\"76daee20-cdee-48b9-bf5f-1dc46079927e\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type Data 1\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"76daee20-cdee-48b9-bf5f-1dc46079927e\",\"id\":\"90563b7e8ed0acad2128d08383966907\"},\"7949a6c6-d3e4-45bf-bad0-208f3ba33483\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Unique Asset ID - Discipline Abbreviation\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"7949a6c6-d3e4-45bf-bad0-208f3ba33483\",\"id\":\"e427ecb62bb5a5f69dc56b12dfd4583a\"},\"79dbaeea-7a11-4cb4-a521-bc04d1b7a25b\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcName [Type]\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"79dbaeea-7a11-4cb4-a521-bc04d1b7a25b\",\"id\":\"d8a8a1f5d6315d9748e29bdd293d77a5\"},\"7a4a2609-0307-4c38-9a8c-4ffcd19a2d00\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Location\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"7a4a2609-0307-4c38-9a8c-4ffcd19a2d00\",\"id\":\"a4c9b4d5ce413090459c99efad3f1832\"},\"7d013fce-228f-4f3c-aa01-db40e458cc6e\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type Data 8\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"7d013fce-228f-4f3c-aa01-db40e458cc6e\",\"id\":\"07281437b9e050cd5ade4cca8a96227b\"},\"834ba6cf-7f91-49e5-ad0c-717d52a2507a\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcExportAs\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"834ba6cf-7f91-49e5-ad0c-717d52a2507a\",\"id\":\"7783908b6376a626de1c39d735b6cbfc\"},\"86d20f83-240f-44b9-8b27-6311eab2abcd\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon B6\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"86d20f83-240f-44b9-8b27-6311eab2abcd\",\"id\":\"006791a1d0d5566c3b892202b08943fd\"},\"8a91c179-c4cb-471a-b108-ad540b8267e3\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Global Inherited Properties.Level Number\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"8a91c179-c4cb-471a-b108-ad540b8267e3\",\"id\":\"3d3d901b52bacdd137ac67f702680f3d\"},\"8c59bf6c-99ff-455b-bdfa-aeb7861e522e\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcTag\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"8c59bf6c-99ff-455b-bdfa-aeb7861e522e\",\"id\":\"363ec752016b710969c8b4454fd2d35f\"},\"8f645e8b-7523-4462-a0af-858ffeaf44dc\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon D\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"8f645e8b-7523-4462-a0af-858ffeaf44dc\",\"id\":\"0e36907ad605957b17ba8556547d8ceb\"},\"91b7eb2f-0caf-45b0-a65d-83ce1eaca70e\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcExportAs\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"91b7eb2f-0caf-45b0-a65d-83ce1eaca70e\",\"id\":\"04140c6ff8418bd2c731affd5691de8e\"},\"93b76d01-67c3-4799-a5f3-296f97489bd3\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Calc Room Number\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"93b76d01-67c3-4799-a5f3-296f97489bd3\",\"id\":\"4e270753bda04b81fd1c11bf1da5d852\"},\"9898cedf-179a-42e7-8cdc-c6c4212ec3e8\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcPresentationLayer\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"9898cedf-179a-42e7-8cdc-c6c4212ec3e8\",\"id\":\"7fd3f00069f6f1fcdab2ea0307661061\"},\"9a208060-4948-49b2-a1bf-1ab383705469\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcDescription\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"9a208060-4948-49b2-a1bf-1ab383705469\",\"id\":\"75340685a08f1a15df5c7aebbc41a85a\"},\"9a49a9a9-21c9-4f52-aeab-ae4727be6e1d\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type IfcPropertySetList\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"9a49a9a9-21c9-4f52-aeab-ae4727be6e1d\",\"id\":\"93f8195337085871545af0176f0ba282\"},\"9c2f84e0-2489-4a03-b4c8-eb44bb26fb0a\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Data 2\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"9c2f84e0-2489-4a03-b4c8-eb44bb26fb0a\",\"id\":\"fa24d55d23008ef6be1dbf3e8636c67e\"},\"9dd225d2-722b-4cb1-b972-babca7520f7e\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"System Name\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"9dd225d2-722b-4cb1-b972-babca7520f7e\",\"id\":\"056eec36214c733f8ecc448c27785434\"},\"9f484ce3-e8ae-4c20-b21c-0210b770935c\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Unique Asset ID - Tenancy Identifier\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"9f484ce3-e8ae-4c20-b21c-0210b770935c\",\"id\":\"416b24b324db272078942a4420775ec0\"},\"ANALYTICAL_HEAT_TRANSFER_COEFFICIENT\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Heat Transfer Coefficient (U)\",\"units\":\"W/(m\u00b2·K)\",\"value\":null,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":\"autodesk.unit.unit:wattsPerSquareMeterKelvin-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ANALYTICAL_HEAT_TRANSFER_COEFFICIENT\",\"id\":\"8d290ae8d6ec3896b8dda96a83bb2d12\"},\"a5cb3364-d1f0-4ea5-a2d2-44114efbcf65\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcName\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"a5cb3364-d1f0-4ea5-a2d2-44114efbcf65\",\"id\":\"23ac4c2000599c233e5c390330e9108e\"},\"a77ddcdc-4c89-42c3-9d28-bc9e476c0fbe\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcName [Type]\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"a77ddcdc-4c89-42c3-9d28-bc9e476c0fbe\",\"id\":\"c60915bcbf4e2e070ccabc17694ee836\"},\"aa967357-e0b5-49f3-95a0-085e5d7d8951\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcMaterial\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"aa967357-e0b5-49f3-95a0-085e5d7d8951\",\"id\":\"08650ab84cd1be1235a60fccfcb1f39e\"},\"ac9b78b9-e138-483b-9796-6214cf7a5bd8\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Documentation.Home Story Name\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ac9b78b9-e138-483b-9796-6214cf7a5bd8\",\"id\":\"759d341196e21570a2f2d6199b5a9f2f\"},\"aeb679c1-1b82-4476-9099-7d13fd8ae3b8\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type IfcPropertySetList\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"aeb679c1-1b82-4476-9099-7d13fd8ae3b8\",\"id\":\"0a235c1836cd2ce0c5fd3869b938946e\"},\"af8efc07-fec4-4419-8513-4a268c4141c8\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon C3\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"af8efc07-fec4-4419-8513-4a268c4141c8\",\"id\":\"181c0245de21c9820a83396914565762\"},\"afb0a36f-1fa3-4f07-9b05-f86f48b3c3f0\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon C2\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"afb0a36f-1fa3-4f07-9b05-f86f48b3c3f0\",\"id\":\"cd929dfb8d143639a136ff3716800dfc\"},\"b13fb213-450c-4f92-859a-05cd5779daf1\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type Data 7\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"b13fb213-450c-4f92-859a-05cd5779daf1\",\"id\":\"1972d1f9e161892f2ba47ccb04f5e784\"},\"b41f116c-c6f7-418e-bf4b-61cc815f8d99\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Level Number(Global Inherited Properties)\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"b41f116c-c6f7-418e-bf4b-61cc815f8d99\",\"id\":\"09caeb5d7565c779852e7802437af4a2\"},\"b594497d-7b5e-4221-aa1d-063f073aa326\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type Data 2\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"b594497d-7b5e-4221-aa1d-063f073aa326\",\"id\":\"ee718148bffebbcfb7e50f5f2be7f91f\"},\"b753aced-e142-45f9-9bb6-d7edce1df108\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Calc Location\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"b753aced-e142-45f9-9bb6-d7edce1df108\",\"id\":\"ab32fc38096b2f538301b596be3ab123\"},\"b90ec63a-c51f-400a-bff1-66b8d0765f47\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Rate Reo Volume\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"b90ec63a-c51f-400a-bff1-66b8d0765f47\",\"id\":\"5642967c62f928019c93b1bbc0e81c26\"},\"b958fd3c-5ea1-43a2-bc5c-df212ed8cf33\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcDescription [Type]\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"b958fd3c-5ea1-43a2-bc5c-df212ed8cf33\",\"id\":\"ea40993e42d6379ab80b5b7d526347c2\"},\"b9d05d9c-a5f5-4a92-8811-9c5e2eefabd8\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"ClassificationCode\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"b9d05d9c-a5f5-4a92-8811-9c5e2eefabd8\",\"id\":\"c93f07ca1b6c3ac1c114474dc9aeeaca\"},\"bf3519b8-7b28-497e-97d8-afd4bf76203b\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcDescription [Type]\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"bf3519b8-7b28-497e-97d8-afd4bf76203b\",\"id\":\"3c46724925e301e2f88d4ecf4b6150f9\"},\"bfb5b2c0-aa1f-47ae-9cc9-70f7feaef0ea\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcName [Type]\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"bfb5b2c0-aa1f-47ae-9cc9-70f7feaef0ea\",\"id\":\"e8a6f622ac2589ad9fadd82e76c4c10c\"},\"bfd7311c-35b9-447d-9683-8ce244f8c1ad\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type Remarks\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"bfd7311c-35b9-447d-9683-8ce244f8c1ad\",\"id\":\"05d34f2e81b12d25b60b1cd6a3788468\"},\"c4520aa3-cdf4-46b7-9539-180edc16d223\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon A4\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"c4520aa3-cdf4-46b7-9539-180edc16d223\",\"id\":\"54c0d9d278b045cbcb57fe4b2d477b86\"},\"c5b0f410-b4ee-4552-ac04-06fa0c13ec3b\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Unique Asset ID - Level/Floor\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"c5b0f410-b4ee-4552-ac04-06fa0c13ec3b\",\"id\":\"7365bc4a64abd37f8dbed69316983d60\"},\"c67d4cb4-b2d6-426f-8d05-ac6fbe0bd267\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Data 5\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"c67d4cb4-b2d6-426f-8d05-ac6fbe0bd267\",\"id\":\"7caf73cac38e203374836cef4827f0ae\"},\"c7ce9441-9aba-45ab-acbb-74e687481466\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Classification.OmniClass.22.Number\",\"units\":null,\"value\":\"22-03 35 13\",\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"c7ce9441-9aba-45ab-acbb-74e687481466\",\"id\":\"8b96e2130eddfb81c6a35635c5654079\"},\"cab1938b-5da8-4b53-9f0c-bb473c1966a4\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Level Number(Global Inherited Properties)\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"cab1938b-5da8-4b53-9f0c-bb473c1966a4\",\"id\":\"baed41d276e750b374a5ca62366d0e56\"},\"cda17719-cef4-4c69-92f1-e111e85353b1\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcTag\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"cda17719-cef4-4c69-92f1-e111e85353b1\",\"id\":\"a00fb37b573181a2bb6070b416ac2c87\"},\"ce24f3b1-369d-42bb-987e-ac0b45c4f8da\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Classification.OmniClass.23.Description\",\"units\":null,\"value\":\"Concrete Structural Floor Decks\",\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ce24f3b1-369d-42bb-987e-ac0b45c4f8da\",\"id\":\"57445c90784f528c2b0f414f9233a42d\"},\"ce25a030-e3d0-4856-bbc0-a1cdd8f4d4ff\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon B4\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ce25a030-e3d0-4856-bbc0-a1cdd8f4d4ff\",\"id\":\"deb71e96bcf8157d78c59266aaaa86d1\"},\"d05b3c99-0643-409d-ad3b-2704d324bbcd\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon B1\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"d05b3c99-0643-409d-ad3b-2704d324bbcd\",\"id\":\"34d4a2c23bc26a1545523ae8597223ca\"},\"d608342a-e8f5-4ec9-9626-771914eb3da2\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type Data 4\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"d608342a-e8f5-4ec9-9626-771914eb3da2\",\"id\":\"b640302338235776966b78b0735abcca\"},\"d8b20410-414f-4777-8614-a7564519c6cd\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Classification.OmniClass.21.Number\",\"units\":null,\"value\":\"21-02 10 10 20 04\",\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"d8b20410-414f-4777-8614-a7564519c6cd\",\"id\":\"60e194032a186046e1a6db66376b97fb\"},\"db575143-7118-4f29-813e-2ced4535a170\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon B2\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"db575143-7118-4f29-813e-2ced4535a170\",\"id\":\"29e7f431d56d49569acb66b3f0621eb5\"},\"dd0cf380-59d8-4d9f-82da-3e2be59e23a1\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon B7\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"dd0cf380-59d8-4d9f-82da-3e2be59e23a1\",\"id\":\"b2887224f02b48130a75948e3f924e61\"},\"dedec34c-f507-4242-a85f-07a816ff1128\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcName\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"dedec34c-f507-4242-a85f-07a816ff1128\",\"id\":\"eef57991b50ee252c88dcee0dc06fddd\"},\"e4af54de-6137-43b0-97d4-c2260a1a68c3\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcTag\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"e4af54de-6137-43b0-97d4-c2260a1a68c3\",\"id\":\"f00202e050fa8a6ea223906e11e870bd\"},\"ea3ba87c-ae3c-47ed-886d-754f2359389c\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcMaterial\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ea3ba87c-ae3c-47ed-886d-754f2359389c\",\"id\":\"0cd769e3fd9905a59cae6cea37f77b46\"},\"ed6b5c87-b77c-45ab-9d90-8f26e365bca1\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcSpatialContainer\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ed6b5c87-b77c-45ab-9d90-8f26e365bca1\",\"id\":\"76cd1f61df7f11197ead15a92693abae\"},\"ee8153af-4866-45f2-a9a2-b6342ccb1dd6\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcMaterial\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ee8153af-4866-45f2-a9a2-b6342ccb1dd6\",\"id\":\"c32a465b6682dbc0908107858c6b9b6b\"},\"ef2d5da9-0b71-4617-a78c-cf4395808169\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Type Data 3\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"ef2d5da9-0b71-4617-a78c-cf4395808169\",\"id\":\"28aa3437bdf40659590c753a3e842193\"},\"f3b20cd0-059c-48a6-b744-7a9babf1cb29\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Embodied Carbon C4\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"f3b20cd0-059c-48a6-b744-7a9babf1cb29\",\"id\":\"094df4c6e1d89bda1c3d32d19b859e57\"},\"f69d55e2-e23c-4a77-999d-45bae64d5856\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcPropertySetList\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"f69d55e2-e23c-4a77-999d-45bae64d5856\",\"id\":\"3fffe0348a0f398a26003c9552c4dbc9\"},\"f6a1fceb-536f-4261-ad25-4f1fb4dcda76\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Concrete Grade\",\"units\":null,\"value\":\"40 MPa\",\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"f6a1fceb-536f-4261-ad25-4f1fb4dcda76\",\"id\":\"acd05a5a6632bf49f56fe8d9b32ca1ff\"},\"f7c3a959-7884-46fd-97a6-cca3cea07fe7\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"IfcPropertySetList\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"f7c3a959-7884-46fd-97a6-cca3cea07fe7\",\"id\":\"131c5cc690b61877eac2f73f73cd69ad\"},\"fac5d675-3756-485d-9500-4f2aa3096a38\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Product Name\",\"units\":null,\"value\":null,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"fac5d675-3756-485d-9500-4f2aa3096a38\",\"id\":\"b5ba47412a41ee8f0e0b6309435f08e7\"},\"fb16e643-73bd-4c8d-a506-99506b010546\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Rate PT Area\",\"units\":null,\"value\":4.5,\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":\"autodesk.unit.unit:general-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"fb16e643-73bd-4c8d-a506-99506b010546\",\"id\":\"8607ae9e4c1350ec9eee23830a12c77e\"},\"fb272f85-666a-45a4-ae16-fa4d620d81b7\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Classification.OmniClass.23.Number\",\"units\":null,\"value\":\"23-13 35 23 11 11\",\"isShared\":true,\"isReadOnly\":false,\"applicationUnit\":null,\"isTypeParameter\":true,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"fb272f85-666a-45a4-ae16-fa4d620d81b7\",\"id\":\"5bcbae5ccbbdc5366970db0d9dd64eca\"},\"STRUCTURAL_ELEVATION_AT_BOTTOM_SURVEY\":{\"applicationId\":null,\"speckle_type\":\"Base\",\"name\":\"Elevation at Bottom Survey\",\"units\":\"mm\",\"value\":30699.999999999898,\"isShared\":false,\"isReadOnly\":true,\"applicationUnit\":\"autodesk.unit.unit:millimeters-1.0.1\",\"isTypeParameter\":false,\"totalChildrenCount\":0,\"applicationUnitType\":null,\"applicationInternalName\":\"STRUCTURAL_ELEVATION_AT_BOTTOM_SURVEY\",\"id\":\"830d5e76c5b5b84bbab7f9f52e80dfef\"},\"id\":\"e24896645d6932e8d2edc7b56bcd65b2\"}"
- )]
- [InlineData(
- "{\n \"applicationId\": null,\n \"speckle_type\": \"Base\",\n \"name\": \"Physically Based (1)\",\n \"diffuse\": -11810867,\n \"opacity\": 1,\n \"emissive\": -16777216,\n \"metalness\": 0,\n \"roughness\": 0.2,\n \"totalChildrenCount\": 0,\n \"id\": \"3ef9f1e3deb7e8057f9eceb29ff2ea88\"\n}"
- )]
- public void Serialize_Id_Stable(string json)
- {
- var jObject = JObject.Parse(json);
- var id = jObject["id"].NotNull().Value();
- jObject.Remove("id");
- jObject.Remove("__closure");
- var jsonWithoutId = jObject.ToString(Formatting.None);
- var newId = IdGenerator.ComputeId(new Json(jsonWithoutId));
- id.Should().Be(newId.Value);
- }
-
- [Theory]
- [InlineData("RevitObject.json.gz", "3416d3fe01c9196115514c4a2f41617b", 7818)]
- public async Task Roundtrip_Test_Old(string fileName, string _, int count)
- {
- var closures = TestFileManager.GetFileAsClosures(fileName);
- var deserializer = new SpeckleObjectDeserializer
- {
- ReadTransport = new TestTransport(closures),
- CancellationToken = default,
- };
-
- var writtenObjects = new Dictionary();
- var writeTransport = new TestTransport2(writtenObjects);
- var serializer = new SpeckleObjectSerializer([writeTransport]);
- var newIds = new Dictionary();
- var oldIds = new Dictionary();
- var idToBase = new Dictionary();
- closures.Count.Should().Be(count);
- foreach (var (id, objJson) in closures)
- {
- var base1 = await deserializer.DeserializeAsync(objJson);
- base1.id.Should().Be(id);
- var j = serializer.Serialize(base1);
- //j.Should().Be(objJson);
- JToken.DeepEquals(JObject.Parse(j), JObject.Parse(objJson));
- newIds.Add(base1.id.NotNull(), j);
- oldIds.Add(id, j);
- idToBase.Add(id, base1);
- }
- newIds.Count.Should().Be(count);
- oldIds.Count.Should().Be(count);
- idToBase.Count.Should().Be(count);
- }
-
- [Theory]
- [InlineData(1)]
- [InlineData(2)]
- [InlineData(3)]
- [InlineData(4)]
- public async Task Roundtrip_Test_New(int concurrency)
- {
- string fileName = "RevitObject.json.gz";
- string rootId = "3416d3fe01c9196115514c4a2f41617b";
- int oldCount = 7818;
- int newCount = 4674;
- var closures = TestFileManager.GetFileAsClosures(fileName);
- closures.Count.Should().Be(oldCount);
-
- Base root;
- using (
- var o = new ObjectLoader(
- new DummySqLiteReceiveManager(closures),
- new DummyReceiveServerObjectManager(closures),
- null,
- new NullLogger(),
- default
- )
- )
- {
- await using var process = new DeserializeProcess(
- o,
- null,
- new BaseDeserializer(new ObjectDeserializerFactory()),
- new NullLoggerFactory(),
- default,
- new(true)
- );
- root = await process.Deserialize(rootId);
- process.BaseCache.Count.Should().Be(oldCount);
- process.Total.Should().Be(oldCount);
- }
-
- var newIdToJson = new ConcurrentDictionary();
-
- await using (
- var serializeProcess = _factory.CreateSerializeProcess(
- SqLiteJsonCacheManager.FromMemory(1),
- new MemoryServerObjectManager(newIdToJson),
- null,
- default,
- new SerializeProcessOptions(false, false, false, true) { MaxCacheBatchSize = 1, MaxParallelism = concurrency }
- )
- )
- {
- var (rootId2, _) = await serializeProcess.Serialize(root);
- rootId2.Should().Be(root.id);
- }
- newIdToJson.Count.Should().Be(newCount);
-
- foreach (var newKvp in newIdToJson)
- {
- if (closures.TryGetValue(newKvp.Key, out var newValue))
- {
- JToken.DeepEquals(JObject.Parse(newValue), JObject.Parse(newKvp.Value));
- }
- }
- }
-}
diff --git a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/CommentResourceTests.cs b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/CommentResourceTests.cs
index a3670d09..0fd6c42f 100644
--- a/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/CommentResourceTests.cs
+++ b/tests/Speckle.Sdk.Tests.Integration/Api/GraphQL/Resources/CommentResourceTests.cs
@@ -91,7 +91,7 @@ public async Task Archive()
[Fact(Skip = SERVER_SKIP_MESSAGE)]
public async Task Edit()
{
- var blobs = await Fixtures.SendBlobData(_testUser.Account, _project.id);
+ var blobs = await Fixtures.SendBlobData(_testUser, _model.id);
var blobIds = blobs.Select(b => b.id.NotNull()).ToList();
var input = new EditCommentInput(new(blobIds, null), _comment.id, _project.id);
@@ -107,7 +107,7 @@ public async Task Edit()
[Fact(Skip = SERVER_SKIP_MESSAGE)]
public async Task Reply()
{
- var blobs = await Fixtures.SendBlobData(_testUser.Account, _project.id);
+ var blobs = await Fixtures.SendBlobData(_testUser, _model.id);
var blobIds = blobs.Select(b => b.id.NotNull()).ToList();
var input = new CreateCommentReplyInput(new(blobIds, null), _comment.id, _project.id);
diff --git a/tests/Speckle.Sdk.Tests.Integration/Fixtures.cs b/tests/Speckle.Sdk.Tests.Integration/Fixtures.cs
index 49af3a6a..babde30a 100644
--- a/tests/Speckle.Sdk.Tests.Integration/Fixtures.cs
+++ b/tests/Speckle.Sdk.Tests.Integration/Fixtures.cs
@@ -12,7 +12,6 @@
using Speckle.Sdk.Host;
using Speckle.Sdk.Models;
using Speckle.Sdk.Tests.Integration.API.GraphQL.Resources;
-using Speckle.Sdk.Transports;
using Version = Speckle.Sdk.Api.GraphQL.Models.Version;
namespace Speckle.Sdk.Tests.Integration;
@@ -42,10 +41,9 @@ public static async Task SeedUserWithClient()
public static async Task CreateVersion(IClient client, string projectId, string modelId)
{
- using var remote = ServiceProvider.GetRequiredService().Create(client.Account, projectId);
var (objectId, _) = await ServiceProvider
.GetRequiredService()
- .Send(new() { applicationId = "ASDF" }, remote, false);
+ .Send2(client.ServerUrl, modelId, client.Account.token, new() { applicationId = "ASDF" }, null, default);
CreateVersionInput input = new(objectId, modelId, projectId);
return await client.Version.Create(input);
}
@@ -146,18 +144,19 @@ private static Blob GenerateBlob(string content)
[Obsolete(CommentResourceTests.SERVER_SKIP_MESSAGE)]
internal static async Task CreateComment(IClient client, string projectId, string modelId, string versionId)
{
- var blobs = await SendBlobData(client.Account, projectId);
+ var blobs = await SendBlobData(client, modelId);
var blobIds = blobs.Select(b => b.id.NotNull()).ToList();
CreateCommentInput input = new(new(blobIds, null), projectId, $"{projectId},{modelId},{versionId}", null, null);
return await client.Comment.Create(input);
}
- internal static async Task SendBlobData(Account account, string projectId)
+ internal static async Task SendBlobData(IClient client, string modelId)
{
- using var remote = ServiceProvider.GetRequiredService().Create(account, projectId);
var blobs = Fixtures.GenerateThreeBlobs();
Base myObject = new() { ["blobs"] = blobs };
- await ServiceProvider.GetRequiredService().Send(myObject, remote, false);
+ await ServiceProvider
+ .GetRequiredService()
+ .Send2(client.ServerUrl, modelId, client.Account.token, myObject, null, default);
return blobs;
}
}
diff --git a/tests/Speckle.Sdk.Tests.Integration/MemoryTransportTests.cs b/tests/Speckle.Sdk.Tests.Integration/MemoryTransportTests.cs
deleted file mode 100644
index f1c7cc8b..00000000
--- a/tests/Speckle.Sdk.Tests.Integration/MemoryTransportTests.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-using System.Reflection;
-using FluentAssertions;
-using Microsoft.Extensions.DependencyInjection;
-using Speckle.Sdk.Api;
-using Speckle.Sdk.Host;
-using Speckle.Sdk.Models;
-using Speckle.Sdk.Transports;
-
-namespace Speckle.Sdk.Tests.Integration;
-
-public class MemoryTransportTests : IDisposable
-{
- private readonly MemoryTransport _memoryTransport = new(blobStorageEnabled: true);
- private IOperations _operations;
-
- public MemoryTransportTests()
- {
- CleanData();
- TypeLoader.Reset();
- TypeLoader.Initialize(typeof(Base).Assembly, Assembly.GetExecutingAssembly());
- var serviceProvider = TestServiceSetup.GetServiceProvider();
- _operations = serviceProvider.GetRequiredService();
- }
-
- public void Dispose() => CleanData();
-
- private void CleanData()
- {
- if (Directory.Exists(_memoryTransport.BlobStorageFolder))
- {
- Directory.Delete(_memoryTransport.BlobStorageFolder, true);
- }
-
- Directory.CreateDirectory(_memoryTransport.BlobStorageFolder);
- }
-
- [Fact]
- public async Task SendAndReceiveObjectWithBlobs()
- {
- var myObject = Fixtures.GenerateSimpleObject();
- myObject["blobs"] = Fixtures.GenerateThreeBlobs();
-
- var sendResult = await _operations.Send(myObject, _memoryTransport, false);
-
- // NOTE: used to debug diffing
- // await Operations.Send(myObject, new List { transport });
-
- var receivedObject = await _operations.Receive(sendResult.rootObjId, _memoryTransport, new MemoryTransport());
-
- var allFiles = Directory
- .GetFiles(_memoryTransport.BlobStorageFolder)
- .Select(fp => fp.Split(Path.DirectorySeparatorChar).Last())
- .ToList();
-
- var blobPaths = allFiles
- .Where(fp => fp.Length > Blob.LocalHashPrefixLength) // excludes things like .DS_store
- .ToList();
-
- // Check that there are three downloaded blobs!
- blobPaths.Count.Should().Be(3);
-
- var objectBlobs = receivedObject["blobs"] as IList;
- objectBlobs.Should().NotBeNull();
-
- var blobs = objectBlobs!.Cast().ToList();
- // Check that we have three blobs
- blobs.Count.Should().Be(3);
-
- // Check that received blobs point to local path (where they were received)
- blobs[0].filePath.Should().Contain(_memoryTransport.BlobStorageFolder);
- blobs[1].filePath.Should().Contain(_memoryTransport.BlobStorageFolder);
- blobs[2].filePath.Should().Contain(_memoryTransport.BlobStorageFolder);
- }
-}
diff --git a/tests/Speckle.Sdk.Tests.Performance/Benchmarks/GeneralDeserializerTest.cs b/tests/Speckle.Sdk.Tests.Performance/Benchmarks/GeneralDeserializerTest.cs
deleted file mode 100644
index eb55c025..00000000
--- a/tests/Speckle.Sdk.Tests.Performance/Benchmarks/GeneralDeserializerTest.cs
+++ /dev/null
@@ -1,82 +0,0 @@
-using BenchmarkDotNet.Attributes;
-using BenchmarkDotNet.Engines;
-using Microsoft.Extensions.DependencyInjection;
-using Speckle.Objects.Geometry;
-using Speckle.Sdk.Credentials;
-using Speckle.Sdk.Helpers;
-using Speckle.Sdk.Host;
-using Speckle.Sdk.Logging;
-using Speckle.Sdk.Models;
-using Speckle.Sdk.Serialisation.V2;
-using Speckle.Sdk.Serialisation.V2.Receive;
-using Speckle.Sdk.SQLite;
-
-namespace Speckle.Sdk.Tests.Performance.Benchmarks;
-
-///
-/// How many threads on our Deserializer is optimal
-///
-[MemoryDiagnoser]
-[SimpleJob(RunStrategy.Monitoring, 0, 0, 2)]
-public class GeneralDeserializer : IDisposable
-{
- private const bool skipCache = true;
-
- /*
- private const string url = "https://latest.speckle.systems/projects/a3ac1b2706/models/59d3b0f3c6"; //small?
- private const string streamId = "a3ac1b2706";
- private const string rootId = "7d53bcf28c6696ecac8781684a0aa006";*/
-
- private const string url = "https://latest.speckle.systems/projects/2099ac4b5f/models/da511c4d1e"; //perf?
- private const string streamId = "2099ac4b5f";
- private const string rootId = "30fb4cbe6eb2202b9e7b4a4fcc3dd2b6";
- private TestDataHelper _dataSource;
-
- [GlobalSetup]
- public async Task Setup()
- {
- TypeLoader.Initialize(typeof(Base).Assembly, typeof(Point).Assembly);
- _dataSource = new TestDataHelper();
- await _dataSource
- .SeedTransport(new Account() { serverInfo = new() { url = url } }, streamId, rootId, skipCache)
- .ConfigureAwait(false);
- }
-
- [Benchmark]
- public async Task RunTest_New()
- {
- var sqlite = TestDataHelper
- .ServiceProvider.GetRequiredService()
- .CreateFromStream(streamId);
- var serverObjects = new ServerObjectManager(
- TestDataHelper.ServiceProvider.GetRequiredService(),
- TestDataHelper.ServiceProvider.GetRequiredService(),
- new Uri(url),
- streamId,
- null
- );
- await using var process = new DeserializeProcess(
- sqlite,
- serverObjects,
- null,
- new BaseDeserializer(new ObjectDeserializerFactory()),
- default,
- new(skipCache)
- );
- return await process.Deserialize(rootId).ConfigureAwait(false);
- }
-
- /*
- [Benchmark]
- public async Task RunTest_Old()
- {
- SpeckleObjectDeserializer sut = new() { ReadTransport = _dataSource.Transport };
- string data = await _dataSource.Transport.GetObject(_dataSource.ObjectId)!;
- return await sut.DeserializeAsync(data);
- }
- */
- [GlobalCleanup]
- public void Cleanup() => Dispose();
-
- public void Dispose() => _dataSource.Dispose();
-}
diff --git a/tests/Speckle.Sdk.Tests.Performance/Benchmarks/GeneralReceiveTest.cs b/tests/Speckle.Sdk.Tests.Performance/Benchmarks/GeneralReceiveTest.cs
deleted file mode 100644
index fbb060ca..00000000
--- a/tests/Speckle.Sdk.Tests.Performance/Benchmarks/GeneralReceiveTest.cs
+++ /dev/null
@@ -1,64 +0,0 @@
-using BenchmarkDotNet.Attributes;
-using BenchmarkDotNet.Engines;
-using Microsoft.Extensions.DependencyInjection;
-using Speckle.Objects.Geometry;
-using Speckle.Sdk.Api;
-using Speckle.Sdk.Credentials;
-using Speckle.Sdk.Host;
-using Speckle.Sdk.Models;
-using Speckle.Sdk.Transports;
-
-namespace Speckle.Sdk.Tests.Performance.Benchmarks;
-
-///
-/// How many threads on our Deserializer is optimal
-///
-[MemoryDiagnoser]
-[SimpleJob(RunStrategy.Monitoring, 0, 0, 1)]
-public class GeneralReceiveTest : IDisposable
-{
- /*
- private const string url = "https://latest.speckle.systems/projects/a3ac1b2706/models/59d3b0f3c6"; //small?
- private const string streamId = "a3ac1b2706";S
- private const string rootId = "7d53bcf28c6696ecac8781684a0aa006";*/
-
- private const string url = "https://latest.speckle.systems/projects/2099ac4b5f/models/da511c4d1e"; //perf?
- private readonly Uri _baseUrl = new("https://latest.speckle.systems");
- private const string streamId = "2099ac4b5f";
- private const string rootId = "30fb4cbe6eb2202b9e7b4a4fcc3dd2b6";
- private TestDataHelper _dataSource;
- private IOperations _operations;
- private ITransport remoteTransport;
-
- [GlobalSetup]
- public async Task Setup()
- {
- TypeLoader.Initialize(typeof(Base).Assembly, typeof(Point).Assembly);
- _dataSource = new TestDataHelper();
- var acc = new Account() { serverInfo = new() { url = url } };
- await _dataSource.SeedTransport(acc, streamId, rootId, true).ConfigureAwait(false);
- _operations = TestDataHelper.ServiceProvider.GetRequiredService();
- // await _operations.Receive2(_baseUrl, streamId, rootId, null);
-
- remoteTransport = TestDataHelper
- .ServiceProvider.GetRequiredService()
- .Create(acc, streamId);
- }
-
- [Benchmark]
- public async Task RunTest_Receive()
- {
- return await _operations.Receive(rootId, remoteTransport, _dataSource.Transport);
- }
-
- [Benchmark]
- public async Task RunTest_Receive2()
- {
- return await _operations.Receive2(_baseUrl, streamId, rootId, null, null, default);
- }
-
- [GlobalCleanup]
- public void Cleanup() => Dispose();
-
- public void Dispose() => _dataSource.Dispose();
-}
diff --git a/tests/Speckle.Sdk.Tests.Performance/Benchmarks/GeneralSendTest.cs b/tests/Speckle.Sdk.Tests.Performance/Benchmarks/GeneralSendTest.cs
deleted file mode 100644
index d70c9735..00000000
--- a/tests/Speckle.Sdk.Tests.Performance/Benchmarks/GeneralSendTest.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-using BenchmarkDotNet.Attributes;
-using BenchmarkDotNet.Engines;
-using Microsoft.Extensions.DependencyInjection;
-using Speckle.Objects.Geometry;
-using Speckle.Sdk.Api;
-using Speckle.Sdk.Api.GraphQL.Enums;
-using Speckle.Sdk.Api.GraphQL.Models;
-using Speckle.Sdk.Common;
-using Speckle.Sdk.Credentials;
-using Speckle.Sdk.Host;
-using Speckle.Sdk.Models;
-using Speckle.Sdk.Serialisation;
-using Speckle.Sdk.Transports;
-using Version = Speckle.Sdk.Api.GraphQL.Models.Version;
-
-namespace Speckle.Sdk.Tests.Performance.Benchmarks;
-
-///
-/// How many threads on our Deserializer is optimal
-///
-[MemoryDiagnoser]
-[SimpleJob(RunStrategy.Monitoring, iterationCount: 1)]
-public class GeneralSendTest
-{
- private Base _testData;
- private IOperations _operations;
- private ServerTransport _remote;
- private Account acc;
- private IClient client;
-
- private Project _project;
-
- [GlobalSetup]
- public async Task Setup()
- {
- TypeLoader.Initialize(typeof(Base).Assembly, typeof(Point).Assembly);
- using var dataSource = new TestDataHelper();
- await dataSource
- .SeedTransport(
- new Account() { serverInfo = new() { url = "https://latest.speckle.systems/" } },
- "2099ac4b5f",
- "30fb4cbe6eb2202b9e7b4a4fcc3dd2b6",
- false
- )
- .ConfigureAwait(false);
-
- SpeckleObjectDeserializer deserializer = new() { ReadTransport = dataSource.Transport };
- string data = await dataSource.Transport.GetObject(dataSource.ObjectId).NotNull();
- _testData = await deserializer.DeserializeAsync(data).NotNull();
- _operations = TestDataHelper.ServiceProvider.GetRequiredService();
-
- acc = TestDataHelper
- .ServiceProvider.GetRequiredService()
- .GetAccounts("https://latest.speckle.systems")
- .First();
-
- client = TestDataHelper.ServiceProvider.GetRequiredService().Create(acc);
-
- _project = await client.Project.Create(
- new($"General Send Test run {Guid.NewGuid()}", null, ProjectVisibility.Public)
- );
- _remote = TestDataHelper.ServiceProvider.GetRequiredService().Create(acc, _project.id);
- }
-
- [Benchmark(Baseline = true)]
- public async Task Send_old()
- {
- using SQLiteTransport local = new();
- var res = await _operations.Send(_testData, [_remote, local]);
- return await TagVersion($"Send_old {Guid.NewGuid()}", res.rootObjId);
- }
-
- [Benchmark]
- public async Task