Compare commits

..

6 Commits

Author SHA1 Message Date
Dogukan Karatas 1046e2aafc removes the extra serializer (#402)
.NET Build and Publish / build (push) Has been cancelled
2025-10-14 16:20:30 +01:00
Jedd Morgan 5cb0eddf4e Update RenderMaterial.cs (#399) 2025-10-13 19:00:26 +00:00
Jedd Morgan e97ce83c6b chore(docs): Update doc comments (#398)
* path provider

* tweaks
2025-10-13 17:01:38 +00:00
Adam Hathcock ea23e72c77 Expose options for sending and receiving (#394) 2025-10-08 10:16:28 +01:00
Adam Hathcock 37358570ec Use new endpoint with attribute mask support (#392)
* Use new endpoint with attribute mask support

* fix test
2025-09-24 11:00:44 +02:00
Adam Hathcock 02b9a73164 Don't record cancelled exceptions (#391)
Co-authored-by: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com>
2025-09-17 11:05:21 +01:00
15 changed files with 46 additions and 28 deletions
+1 -1
View File
@@ -35,6 +35,6 @@ public class RenderMaterial : Base
public Color emissiveColor
{
get => Color.FromArgb(emissive);
set => diffuse = value.ToArgb();
set => emissive = value.ToArgb();
}
}
@@ -2,6 +2,7 @@ using Microsoft.Extensions.Logging;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
using Speckle.Sdk.Serialisation;
using Speckle.Sdk.Serialisation.V2.Receive;
using Speckle.Sdk.Transports;
namespace Speckle.Sdk.Api;
@@ -22,7 +23,8 @@ public partial class Operations
string objectId,
string? authorizationToken,
IProgress<ProgressArgs>? onProgressAction,
CancellationToken cancellationToken
CancellationToken cancellationToken,
DeserializeProcessOptions? options = null
)
{
using var receiveActivity = activityFactory.Start("Operations.Receive");
@@ -36,7 +38,8 @@ public partial class Operations
streamId,
authorizationToken,
onProgressAction,
cancellationToken
cancellationToken,
options
);
try
{
@@ -44,6 +47,11 @@ public partial class Operations
receiveActivity?.SetStatus(SdkActivityStatusCode.Ok);
return result;
}
catch (OperationCanceledException)
{
//this is handled by the caller
throw;
}
catch (Exception ex)
{
receiveActivity?.SetStatus(SdkActivityStatusCode.Error);
@@ -25,7 +25,8 @@ public partial class Operations
string? authorizationToken,
Base value,
IProgress<ProgressArgs>? onProgressAction,
CancellationToken cancellationToken
CancellationToken cancellationToken,
SerializeProcessOptions? options = null
)
{
using var receiveActivity = activityFactory.Start("Operations.Send");
@@ -38,7 +39,8 @@ public partial class Operations
streamId,
authorizationToken,
onProgressAction,
cancellationToken
cancellationToken,
options
);
try
{
+5 -16
View File
@@ -43,7 +43,7 @@ public static class SpecklePathProvider
/// <see cref="Environment.SpecialFolder.ApplicationData"/> path usually maps to
/// <ul>
/// <li>win: <c>%appdata%/</c></li>
/// <li>MacOS: <c>~/.config/</c></li>
/// <li>MacOS: <c>~/Library/Application Support</c></li>
/// <li>Linux: <c>~/.config/</c></li>
/// </ul>
/// </remarks>
@@ -57,29 +57,18 @@ public static class SpecklePathProvider
return pathOverride;
}
// on desktop linux and macos we use the appdata.
// but we might not have write access to the disk
// so the catch falls back to the user profile
try
{
return Environment.GetFolderPath(
Environment.SpecialFolder.ApplicationData,
// if the folder doesn't exist, we get back an empty string on OSX,
// which in turn, breaks other stuff down the line.
// passing in the Create option ensures that this directory exists,
// which is not a given on all OS-es.
// It's not a given that the folder is already there on all OS-es, so we'll create it
Environment.SpecialFolderOption.Create
);
}
catch (SystemException ex) when (ex is PlatformNotSupportedException or ArgumentException)
catch (PlatformNotSupportedException)
{
//Adding this log just so we confidently know which Exception type to catch here.
// TODO: Must re-add log call when (and if) this get's made as a service
//SpeckleLog.Logger.Warning(ex, "Falling back to user profile path");
// on server linux, there might not be a user setup, things can run under root
// in that case, the appdata variable is most probably not set up
// we fall back to the value of the home folder
// We might not have write access to the disk to create the folder,
// so we'll fall back to the user profile
return Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
}
}
@@ -9,6 +9,7 @@ public class MemoryServerObjectManager(ConcurrentDictionary<string, string> obje
{
public virtual async IAsyncEnumerable<(string, string)> DownloadObjects(
IReadOnlyCollection<string> objectIds,
string? attributeMask,
IProgress<ProgressArgs>? progress,
[EnumeratorCancellation] CancellationToken cancellationToken
)
@@ -14,7 +14,8 @@ public record DeserializeProcessOptions(
bool ThrowOnMissingReferences = true,
bool SkipInvalidConverts = false,
int? MaxParallelism = null,
bool SkipServer = false
bool SkipServer = false,
string? AttributeMask = null
);
public partial interface IDeserializeProcess : IAsyncDisposable;
@@ -44,6 +45,7 @@ public sealed class DeserializeProcess(
new ObjectLoader(
sqLiteJsonCacheManager,
serverObjectManager,
options?.AttributeMask,
progress,
loggerFactory.CreateLogger<ObjectLoader>(),
cancellationToken
@@ -16,6 +16,7 @@ public partial interface IObjectLoader : IDisposable;
public sealed class ObjectLoader(
ISqLiteJsonCacheManager sqLiteJsonCacheManager,
IServerObjectManager serverObjectManager,
string? attributeMask,
IProgress<ProgressArgs>? progress,
ILogger<ObjectLoader> logger,
CancellationToken cancellationToken
@@ -111,6 +112,7 @@ public sealed class ObjectLoader(
await foreach (
var (id, json) in serverObjectManager.DownloadObjects(
ids.Select(x => x.NotNull()).ToList(),
attributeMask,
progress,
cancellationToken
)
@@ -51,6 +51,7 @@ public class ServerObjectManager : IServerObjectManager
public async IAsyncEnumerable<(string, string)> DownloadObjects(
IReadOnlyCollection<string> objectIds,
string? attributeMask,
IProgress<ProgressArgs>? progress,
[EnumeratorCancellation] CancellationToken cancellationToken
)
@@ -59,10 +60,14 @@ public class ServerObjectManager : IServerObjectManager
cancellationToken.ThrowIfCancellationRequested();
using var childrenHttpMessage = new HttpRequestMessage();
childrenHttpMessage.RequestUri = new Uri($"/api/getobjects/{_streamId}", UriKind.Relative);
childrenHttpMessage.RequestUri = new Uri($"/api/v2/projects/{_streamId}/object-stream/", UriKind.Relative);
childrenHttpMessage.Method = HttpMethod.Post;
Dictionary<string, string> postParameters = new() { { "objects", JsonConvert.SerializeObject(objectIds) } };
Dictionary<string, object> postParameters = new() { { "objectIds", objectIds } };
if (!string.IsNullOrWhiteSpace(attributeMask))
{
postParameters.Add("attributeMask", attributeMask.NotNull());
}
string serializedPayload = JsonConvert.SerializeObject(postParameters);
childrenHttpMessage.Content = new StringContent(serializedPayload, Encoding.UTF8, "application/json");
childrenHttpMessage.Headers.Add("Accept", "text/plain");
@@ -338,6 +338,7 @@ public class DummyServerObjectManager : IServerObjectManager
{
public IAsyncEnumerable<(string, string)> DownloadObjects(
IReadOnlyCollection<string> objectIds,
string? attributeMask,
IProgress<ProgressArgs>? progress,
CancellationToken cancellationToken
) => throw new NotImplementedException();
@@ -112,6 +112,7 @@ public class ExceptionTests
new DummySqLiteReceiveManager(new Dictionary<string, string>()),
new ExceptionServerObjectManager(),
null,
null,
new NullLogger<ObjectLoader>(),
default
);
@@ -8,6 +8,7 @@ public class ExceptionServerObjectManager : IServerObjectManager
{
public IAsyncEnumerable<(string, string)> DownloadObjects(
IReadOnlyCollection<string> objectIds,
string? attributeMask,
IProgress<ProgressArgs>? progress,
CancellationToken cancellationToken
) => throw new NotImplementedException();
@@ -204,6 +204,7 @@ public class SerializationTests
new DummySqLiteReceiveManager(closures),
new DummyReceiveServerObjectManager(closures),
null,
null,
new NullLogger<ObjectLoader>(),
default
)
@@ -31,14 +31,17 @@ public class ServerObjectManagerTests : MoqTest
var jObject = new JObject { { "id", id }, { "value", true } };
var jObject2 = new JObject { { "id", id2 }, { "value", true } };
var mockHttp = new MockHttpMessageHandler();
Dictionary<string, string> postParameters = new()
Dictionary<string, object> postParameters = new()
{
{ "objects", JsonConvert.SerializeObject(new List<string> { id, id2 }) },
{
"objectIds",
new List<string> { id, id2 }
},
};
string serializedPayload = JsonConvert.SerializeObject(postParameters);
mockHttp
.When(HttpMethod.Post, $"http://localhost/api/getobjects/{streamId}")
.When(HttpMethod.Post, $"http://localhost/api/v2/projects/{streamId}/object-stream/")
.WithContent(serializedPayload)
.Respond(
"application/json",
@@ -59,7 +62,7 @@ public class ServerObjectManagerTests : MoqTest
token,
new(timeout: TimeSpan.FromSeconds(timeout))
);
var results = serverObjectManager.DownloadObjects(new List<string> { id, id2 }, null, ct);
var results = serverObjectManager.DownloadObjects(new List<string> { id, id2 }, null, null, ct);
var objects = new JObject();
await foreach (var (x, json) in results)
{
@@ -10,6 +10,7 @@ public class DummyReceiveServerObjectManager(IReadOnlyDictionary<string, string>
{
public async IAsyncEnumerable<(string, string)> DownloadObjects(
IReadOnlyCollection<string> objectIds,
string? attributeMask,
IProgress<ProgressArgs>? progress,
[EnumeratorCancellation] CancellationToken cancellationToken
)
@@ -9,6 +9,7 @@ public class DummySendServerObjectManager(ConcurrentDictionary<string, string> s
{
public IAsyncEnumerable<(string, string)> DownloadObjects(
IReadOnlyCollection<string> objectIds,
string? attributeMask,
IProgress<ProgressArgs>? progress,
CancellationToken cancellationToken
) => throw new NotImplementedException();