Re-introduced code analysers and fixed many violations (#92)

* Sdk

* Objects

* Supressed IDE warnings via editor config instead of nowarn

* Nullability and other warnings

* using

* Fixed warnings

* Important fix

* More fixes
This commit is contained in:
Jedd Morgan
2024-09-04 12:49:35 +01:00
committed by GitHub
parent b9180027c5
commit 5e0ea324c3
96 changed files with 441 additions and 1688 deletions
+12
View File
@@ -212,6 +212,7 @@ dotnet_diagnostic.ide0058.severity = none # Remove unnecessary expression value:
dotnet_diagnostic.ide0010.severity = none # Add missing cases to switch statement: Too verbose
dotnet_diagnostic.ide0200.severity = none # Remove unnecessary lambda expression: may be performance reasons not to
dotnet_diagnostic.ide0058.severity = none # Remove unnecessary expression value: Subjective
dotnet_diagnostic.ide0305.severity = none # Use collection expression for fluent: Can obfuscate intent
dotnet_diagnostic.ide0001.severity = suggestion # Name can be simplified: Non enforceable in build
dotnet_diagnostic.ide0046.severity = suggestion # Use conditional expression for return: Subjective
dotnet_diagnostic.ide0045.severity = suggestion # Use conditional expression for assignment: Subjective
@@ -233,6 +234,17 @@ dotnet_diagnostic.ide0042.severity = suggestion # Deconstruct variable declarati
dotnet_diagnostic.ide0028.severity = suggestion # Use collection initializers: Subjective
dotnet_diagnostic.ide0072.severity = suggestion # Populate switch statement: Subjective
dotnet_diagnostic.ide0074.severity = suggestion # Use compound assignment: Subjective
dotnet_diagnostic.ide0300.severity = suggestion # Use collection expression for array: Subjective, maybe aspirational
dotnet_diagnostic.ide0290.severity = suggestion # primary constructors: subjective, and readonly properties are not a thing
dotnet_diagnostic.ide0290.severity = suggestion # Use primary constructor: Subjective
dotnet_diagnostic.ide0037.severity = suggestion # Use inferred member names: Sometimes its nice to be explicit
dotnet_diagnostic.ide0301.severity = suggestion # Use collection expression for empty: Subjective, intent
dotnet_diagnostic.ide0021.severity = suggestion # Use expression body for constructors : Subjective
dotnet_diagnostic.ide0090.severity = suggestion # Simplify new expression : Subjective
dotnet_diagnostic.ide0047.severity = suggestion # Parentheses preferences: IDEs don't properly pick it up
dotnet_diagnostic.ide0130.severity = suggestion # Namespace does not match folder structure : Aspirational
dotnet_diagnostic.ide1006.severity = suggestion # Naming rule violation : Aspirational
# Maintainability rules
dotnet_diagnostic.ca1501.severity = warning # Avoid excessive inheritance
+4
View File
@@ -0,0 +1,4 @@
CA1502: 25
CA1501: 5
CA1506(Method): 50
CA1506(Type): 95
+38 -11
View File
@@ -6,17 +6,9 @@
</PropertyGroup>
<PropertyGroup>
<LangVersion>latest</LangVersion>
<LangVersion>12</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<AnalysisMode>Recommended</AnalysisMode>
<WarningsAsErrors>true</WarningsAsErrors>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<RunAnalyzersDuringLiveAnalysis>False</RunAnalyzersDuringLiveAnalysis>
<RunAnalyzersDuringBuild>False</RunAnalyzersDuringBuild>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<IsPackable>false</IsPackable>
@@ -25,6 +17,36 @@
<PropertyGroup Condition="'$(GITHUB_ACTIONS)' == 'true'">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>
<PropertyGroup Label="Analyers">
<EnableNetAnalyzers>true</EnableNetAnalyzers>
<AnalysisLevel>latest-AllEnabledByDefault</AnalysisLevel>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<!-- Ingored warnings, some aspirational but too noisy for now, some by design. -->
<NoWarn>
<!--Disabled by design-->
CA5399;CA1812;
<!--XML comment-->
CS1591;CS1573;
<!-- Globalization rules -->
CA1303;CA1304;CA1305;CA1307;CA1308;CA1309;CA1310;CA1311;
<!-- Logging -->
CA1848;CA2254;CA1727;
<!-- Others we don't want -->
CA1815;CA1725;
<!-- Naming things is hard enough -->
CA1710;CA1711;CA1724;CA1716;CA1720;CA1724;
<!-- Aspirational -->
CA1502;
$(NoWarn)
</NoWarn>
</PropertyGroup>
<PropertyGroup>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
@@ -39,8 +61,6 @@
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591;1573</NoWarn>
</PropertyGroup>
<ItemGroup>
<None Include="..\..\README.md" Pack="true" PackagePath="\"/>
@@ -51,4 +71,11 @@
PackagePath="\"
Visible="false"/>
</ItemGroup>
<ItemGroup>
<!-- This file contains the configuration for some analyzer warnings, such as cyclomatic
complexity threshold -->
<AdditionalFiles Include="$(RepositoryRoot)CodeMetricsConfig.txt"/>
</ItemGroup>
</Project>
+14
View File
@@ -0,0 +1,14 @@
<Project>
<PropertyGroup Condition="'$(IsTestProject)' == 'true'">
<AnalysisMode>Recommended</AnalysisMode>
<NoWarn>
$(NoWarn);
<!-- Things we need to test -->
CS0618;CA1034;CA2201;CA1051;CA1040;CA1724;
IDE0044;IDE0130;CA1508;
<!-- Analysers that provide no tangeable value to a test project -->
CA5394;CA2007;CA1852;CA1819;CA1711;CA1063;CA1816;CA2234;CS8618;CA1054;CA1810;CA2208;CA1019;
</NoWarn>
<EnforceCodeStyleInBuild>false</EnforceCodeStyleInBuild>
</PropertyGroup>
</Project>
+2
View File
@@ -22,6 +22,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{DA2AED
README.md = README.md
GitVersion.yml = GitVersion.yml
docker-compose.yml = docker-compose.yml
CodeMetricsConfig.txt = CodeMetricsConfig.txt
Directory.Build.Targets = Directory.Build.Targets
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{58D37DA9-F948-48CA-9A73-F5BBBD533DBF}"
+14 -9
View File
@@ -65,7 +65,7 @@ Target(
DependsOn(RESTORE),
async () =>
{
await RunAsync("dotnet", $"build Speckle.Sdk.sln -c Release --no-restore");
await RunAsync("dotnet", $"build Speckle.Sdk.sln -c Release --no-restore").ConfigureAwait(false);
}
);
@@ -76,9 +76,11 @@ Target(
async file =>
{
await RunAsync(
"dotnet",
$"test {file} -c Release --no-build --no-restore --verbosity=normal /p:AltCover=true /p:AltCoverAttributeFilter=ExcludeFromCodeCoverage /p:AltCoverVerbosity=Warning"
);
"dotnet",
$"test {file} -c Release --no-build --no-restore --verbosity=normal /p:AltCover=true /p:AltCoverAttributeFilter=ExcludeFromCodeCoverage /p:AltCoverVerbosity=Warning"
)
.ConfigureAwait(false);
;
}
);
@@ -87,15 +89,18 @@ Target(
DependsOn(BUILD),
async () =>
{
await RunAsync("docker", "compose -f docker-compose.yml up --wait");
await RunAsync("docker", "compose -f docker-compose.yml up --wait").ConfigureAwait(false);
;
foreach (var test in Glob.Files(".", "**/*.Tests.Integration.csproj"))
{
await RunAsync(
"dotnet",
$"test {test} -c Release --no-build --no-restore --verbosity=normal /p:AltCover=true /p:AltCoverAttributeFilter=ExcludeFromCodeCoverage"
);
"dotnet",
$"test {test} -c Release --no-build --no-restore --verbosity=normal /p:AltCover=true /p:AltCoverAttributeFilter=ExcludeFromCodeCoverage"
)
.ConfigureAwait(false);
;
}
await RunAsync("docker", "compose down");
await RunAsync("docker", "compose down").ConfigureAwait(false);
}
);
+1 -4
View File
@@ -9,11 +9,8 @@ namespace Speckle.Objects.BuiltElements;
/// <remarks>
/// This class is not suitable for freeform rebar, which can have multiple shapes.
/// </remarks>
public abstract class RebarGroup<T> : Base, IHasVolume, IDisplayValue<List<ICurve>>
where T : RebarShape
public abstract class RebarGroup : Base, IHasVolume, IDisplayValue<List<ICurve>>
{
public RebarGroup() { }
/// <summary>
/// The shape of the rebar group
/// </summary>
@@ -24,7 +24,7 @@ public class FreeformElement : Base, IDisplayValue<List<Base>>
this.subcategory = subcategory;
if (!IsValid())
{
throw new Exception("Freeform elements can only be created from BREPs or Meshes");
throw new ArgumentException("Freeform elements can only be created from BREPs or Meshes", nameof(baseGeometries));
}
this.parameters = parameters?.ToBase();
@@ -2,7 +2,6 @@ using Speckle.Objects.Geometry;
using Speckle.Objects.Utils;
using Speckle.Sdk;
using Speckle.Sdk.Host;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
namespace Speckle.Objects.BuiltElements.Revit;
@@ -5,7 +5,7 @@ using Speckle.Sdk.Models;
namespace Speckle.Objects.BuiltElements.Revit;
[SpeckleType("Objects.BuiltElements.Revit.RevitRebarGroup")]
public class RevitRebarGroup : RebarGroup<RevitRebarShape>
public class RevitRebarGroup : RebarGroup
{
public RevitRebarGroup() { }
@@ -147,13 +147,14 @@ public class RevitFaceWall : Wall
{
if (surface.Surfaces.Count == 0)
{
throw new Exception("Cannot create a RevitWall with an empty BREP");
throw new ArgumentException("Cannot create a RevitWall with an empty BREP", nameof(surface));
}
if (surface.Surfaces.Count > 1)
{
throw new Exception(
"The provided brep has more than 1 surface. Please deconstruct/explode it to create multiple instances"
throw new ArgumentException(
"The provided brep has more than 1 surface. Please deconstruct/explode it to create multiple instances",
nameof(surface)
);
}
+2 -2
View File
@@ -18,7 +18,7 @@ public static class CurveTypeEncoding
/// </summary>
public static class CurveArrayEncodingExtensions
{
public static List<double> ToArray(List<ICurve> curves)
public static List<double> ToArray(IReadOnlyCollection<ICurve> curves)
{
var list = new List<double>();
foreach (var curve in curves)
@@ -47,7 +47,7 @@ public static class CurveArrayEncodingExtensions
list.AddRange(p.ToList());
break;
default:
throw new Exception($"Unkown curve type: {curve.GetType()}.");
throw new ArgumentOutOfRangeException(nameof(curves), $"Unkown curve type: {curve.GetType()}.");
}
}
+2 -2
View File
@@ -12,8 +12,8 @@ public class GisPointFeature : Base, IGisFeature, IDisplayValue<List<Point>>
[JsonIgnore]
public required List<Point> geometry
{
get { return displayValue; }
set { displayValue = value; }
get => displayValue;
set => displayValue = value;
}
[DetachProperty]
@@ -12,8 +12,8 @@ public class GisPolylineFeature : Base, IGisFeature, IDisplayValue<List<Polyline
[JsonIgnore]
public required List<Polyline> geometry
{
get { return displayValue; }
set { displayValue = value; }
get => displayValue;
set => displayValue = value;
}
[DetachProperty]
+1 -2
View File
@@ -2,7 +2,6 @@ using Speckle.Objects.Other;
using Speckle.Objects.Primitive;
using Speckle.Sdk;
using Speckle.Sdk.Common;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
namespace Speckle.Objects.Geometry;
@@ -293,7 +292,7 @@ public class Arc : Base, IHasBoundingBox, ICurve, IHasArea, ITransformable<Arc>
endAngle = list[4],
angleRadians = list[5],
domain = new Interval { start = list[6], end = list[7] },
units = Units.GetUnitFromEncoding(list[list.Count - 1]),
units = Units.GetUnitFromEncoding(list[^1]),
plane = Plane.FromList(list.GetRange(8, 13))
};
arc.startPoint = Point.FromList(list.GetRange(21, 3), arc.units);
+1 -1
View File
@@ -74,7 +74,7 @@ public class Circle : Base, ICurve, IHasArea, IHasBoundingBox
radius = list[2],
domain = new Interval { start = list[3], end = list[4] },
plane = Plane.FromList(list.GetRange(5, 13)),
units = Units.GetUnitFromEncoding(list[list.Count - 1])
units = Units.GetUnitFromEncoding(list[^1])
};
return circle;
+1 -1
View File
@@ -69,7 +69,7 @@ public class Ellipse : Base, ICurve, IHasArea
secondRadius = list[3],
domain = new Interval { start = list[4], end = list[5] },
plane = Plane.FromList(list.GetRange(6, 13)),
units = Units.GetUnitFromEncoding(list[list.Count - 1])
units = Units.GetUnitFromEncoding(list[^1])
};
return ellipse;
}
+2 -3
View File
@@ -3,7 +3,6 @@ using Speckle.Objects.Other;
using Speckle.Objects.Primitive;
using Speckle.Sdk;
using Speckle.Sdk.Common;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
namespace Speckle.Objects.Geometry;
@@ -121,9 +120,9 @@ public class Line : Base, ICurve, IHasBoundingBox, ITransformable<Line>
return list;
}
public static Line FromList(List<double> list)
public static Line FromList(IReadOnlyList<double> list)
{
var units = Units.GetUnitFromEncoding(list[list.Count - 1]);
var units = Units.GetUnitFromEncoding(list[^1]);
var startPt = new Point(list[2], list[3], list[4], units);
var endPt = new Point(list[5], list[6], list[7], units);
var line = new Line(startPt, endPt, units)
-1
View File
@@ -2,7 +2,6 @@ using Speckle.Newtonsoft.Json;
using Speckle.Objects.Other;
using Speckle.Sdk;
using Speckle.Sdk.Common;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
namespace Speckle.Objects.Geometry;
+2 -2
View File
@@ -87,9 +87,9 @@ public class Plane : Base, ITransformable<Plane>
/// </summary>
/// <param name="list">The list of values representing this plane</param>
/// <returns>A new <see cref="Plane"/> with the provided values.</returns>
public static Plane FromList(List<double> list)
public static Plane FromList(IReadOnlyList<double> list)
{
var units = Units.GetUnitFromEncoding(list[list.Count - 1]);
var units = Units.GetUnitFromEncoding(list[^1]);
var plane = new Plane
{
origin = new Point(list[0], list[1], list[2], units),
+2 -2
View File
@@ -216,14 +216,14 @@ public class Point : Base, ITransformable<Point>
return Math.Sqrt(Math.Pow(x - point.x, 2) + Math.Pow(y - point.y, 2) + Math.Pow(z - point.z, 2));
}
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
if (ReferenceEquals(this, obj))
{
return true;
}
if (ReferenceEquals(obj, null))
if (obj is null)
{
return false;
}
@@ -2,7 +2,6 @@ using Speckle.Objects.Other;
using Speckle.Sdk;
using Speckle.Sdk.Common;
using Speckle.Sdk.Host;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
namespace Speckle.Objects.Geometry;
+1 -1
View File
@@ -250,7 +250,7 @@ public class Surface : Base, IHasBoundingBox, IHasArea, ITransformable<Surface>
srf.knotsU = list.GetRange(14 + pointCount, knotsUCount);
srf.knotsV = list.GetRange(14 + pointCount + knotsUCount, knotsVCount);
var u = list[list.Count - 1];
var u = list[^1];
srf.units = Units.GetUnitFromEncoding(u);
return srf;
}
-1
View File
@@ -3,7 +3,6 @@ using Speckle.Newtonsoft.Json;
using Speckle.Objects.Geometry;
using Speckle.Sdk;
using Speckle.Sdk.Common;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
using Vector = Speckle.Objects.Geometry.Vector;
@@ -13,6 +13,13 @@
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>
<PropertyGroup>
<NoWarn>
$(NoWarn);
IDE0060;CA1034;CA1819;CA1708;CA1008;CA2225;CA1024;
</NoWarn>
</PropertyGroup>
<ItemGroup>
<InternalsVisibleTo Include="Speckle.Objects.Tests.Unit" />
</ItemGroup>
+1 -1
View File
@@ -33,6 +33,6 @@ public static class Consts
}
var indexOfPlusSign = informationalVersion.IndexOf('+');
return indexOfPlusSign > 0 ? informationalVersion.Substring(0, indexOfPlusSign) : informationalVersion;
return indexOfPlusSign > 0 ? informationalVersion[..indexOfPlusSign] : informationalVersion;
}
}
+5 -5
View File
@@ -6,14 +6,14 @@ public interface ISpeckleLogger
void Write(SpeckleLogLevel speckleLogLevel, Exception? exception, string message, params object?[] arguments);
void Debug(string message, params object?[] arguments);
void Debug(Exception exception, string message, params object?[] arguments);
void Debug(Exception? exception, string message, params object?[] arguments);
void Warning(string message, params object?[] arguments);
void Warning(Exception exception, string message, params object?[] arguments);
void Warning(Exception? exception, string message, params object?[] arguments);
void Information(string message, params object?[] arguments);
void Information(Exception exception, string message, params object?[] arguments);
void Information(Exception? exception, string message, params object?[] arguments);
void Error(string message, params object?[] arguments);
void Error(Exception exception, string message, params object?[] arguments);
void Fatal(Exception exception, string message, params object?[] arguments);
void Error(Exception? exception, string message, params object?[] arguments);
void Fatal(Exception? exception, string message, params object?[] arguments);
}
+1 -1
View File
@@ -3,7 +3,7 @@ using OpenTelemetry.Trace;
namespace Speckle.Sdk.Logging;
public class SpeckleActivity(Activity activity) : ISpeckleActivity
public sealed class SpeckleActivity(Activity activity) : ISpeckleActivity
{
public void Dispose() => activity.Dispose();
+6 -6
View File
@@ -3,7 +3,7 @@ using Serilog.Events;
namespace Speckle.Sdk.Logging;
internal class SpeckleLogger : ISpeckleLogger
internal sealed class SpeckleLogger : ISpeckleLogger
{
private readonly Serilog.ILogger _logger;
@@ -36,24 +36,24 @@ internal class SpeckleLogger : ISpeckleLogger
public void Debug(string message, params object?[] arguments) => _logger.Debug(message, arguments);
public void Debug(Exception exception, string message, params object?[] arguments) =>
public void Debug(Exception? exception, string message, params object?[] arguments) =>
_logger.Debug(exception, message, arguments);
public void Warning(string message, params object?[] arguments) => _logger.Warning(message, arguments);
public void Warning(Exception exception, string message, params object?[] arguments) =>
public void Warning(Exception? exception, string message, params object?[] arguments) =>
_logger.Warning(exception, message, arguments);
public void Information(string message, params object?[] arguments) => _logger.Information(message, arguments);
public void Information(Exception exception, string message, params object?[] arguments) =>
public void Information(Exception? exception, string message, params object?[] arguments) =>
_logger.Information(exception, message, arguments);
public void Error(string message, params object?[] arguments) => _logger.Error(message, arguments);
public void Error(Exception exception, string message, params object?[] arguments) =>
public void Error(Exception? exception, string message, params object?[] arguments) =>
_logger.Error(exception, message, arguments);
public void Fatal(Exception exception, string message, params object?[] arguments) =>
public void Fatal(Exception? exception, string message, params object?[] arguments) =>
_logger.Fatal(exception, message, arguments);
}
-1
View File
@@ -1,5 +1,4 @@
using GraphQL;
using Speckle.Sdk.Logging;
namespace Speckle.Sdk.Api;
+5 -18
View File
@@ -1,4 +1,4 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Dynamic;
using System.Net.WebSockets;
using System.Reflection;
@@ -16,6 +16,7 @@ using Speckle.Sdk.Logging;
namespace Speckle.Sdk.Api;
[SuppressMessage("Maintainability", "CA1506:Avoid excessive class coupling", Justification = "Class needs refactor")]
public sealed partial class Client : ISpeckleGraphQLClient, IDisposable
{
public ProjectResource Project { get; }
@@ -27,11 +28,7 @@ public sealed partial class Client : ISpeckleGraphQLClient, IDisposable
public CommentResource Comment { get; }
public SubscriptionResource Subscription { get; }
public string ServerUrl => Account.serverInfo.url;
public string ApiToken => Account.token;
public System.Version? ServerVersion { get; private set; }
public Uri ServerUrl => new(Account.serverInfo.url);
[JsonIgnore]
public Account Account { get; }
@@ -65,16 +62,6 @@ public sealed partial class Client : ISpeckleGraphQLClient, IDisposable
try
{
Subscription.Dispose();
UserStreamAddedSubscription?.Dispose();
UserStreamRemovedSubscription?.Dispose();
StreamUpdatedSubscription?.Dispose();
BranchCreatedSubscription?.Dispose();
BranchUpdatedSubscription?.Dispose();
BranchDeletedSubscription?.Dispose();
CommitCreatedSubscription?.Dispose();
CommitUpdatedSubscription?.Dispose();
CommitDeletedSubscription?.Dispose();
CommentActivitySubscription?.Dispose();
GQLClient.Dispose();
}
catch (Exception ex) when (!ex.IsFatal()) { }
@@ -174,9 +161,9 @@ public sealed partial class Client : ISpeckleGraphQLClient, IDisposable
private Dictionary<string, object?> ConvertExpandoToDict(ExpandoObject expando)
{
var variables = new Dictionary<string, object?>();
foreach (KeyValuePair<string, object> kvp in expando)
foreach (KeyValuePair<string, object?> kvp in expando)
{
object value;
object? value;
if (kvp.Value is ExpandoObject ex)
{
value = ConvertExpandoToDict(ex);
@@ -1,4 +1,5 @@
using GraphQL;
using Speckle.Sdk.Api.GraphQL.Models;
namespace Speckle.Sdk.Api;
@@ -16,7 +17,7 @@ public partial class Client
/// <param name="limit">Max number of activity items to get</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<List<ActivityItem>> StreamGetActivity(
public async Task<List<Activity>> StreamGetActivity(
string id,
DateTime? after = null,
DateTime? before = null,
@@ -1,7 +1,6 @@
using System.Text.RegularExpressions;
using GraphQL;
using Speckle.Sdk.Api.GraphQL.Models.Responses;
using Speckle.Sdk.Logging;
namespace Speckle.Sdk.Api;
@@ -33,7 +32,7 @@ public partial class Client
return new System.Version(999, 999, 999);
}
ServerVersion = new System.Version(Regex.Replace(res.serverInfo.version, "[-a-zA-Z]+", ""));
return ServerVersion;
var serverVersion = new System.Version(Regex.Replace(res.serverInfo.version, "[-a-zA-Z]+", ""));
return serverVersion;
}
}
@@ -1,9 +1,7 @@
#nullable disable
using GraphQL;
using Speckle.Sdk.Api.GraphQL.Models;
using Speckle.Sdk.Api.GraphQL.Models.Responses;
using Speckle.Sdk.Api.GraphQL.Resources;
using Speckle.Sdk.Logging;
namespace Speckle.Sdk.Api;
@@ -96,6 +94,16 @@ public partial class Client
return (await ExecuteGraphQLRequest<StreamData>(request, cancellationToken).ConfigureAwait(false)).stream;
}
/// <summary>
/// Gets all favorite streams for the current user
/// </summary>
/// <param name="limit">Max number of streams to return</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[Obsolete("Favourite streams are no longer a supported feature", true)]
public Task<List<Stream>> FavoriteStreamsGet(int limit = 10, CancellationToken cancellationToken = default) =>
throw new NotImplementedException();
/// <summary>
/// Gets all streams for the current user
/// </summary>
@@ -103,161 +111,9 @@ public partial class Client
/// <param name="cancellationToken"></param>
/// <returns></returns>
/// <seealso cref="ActiveUserResource.GetProjects"/>
[Obsolete($"Use client.{nameof(ActiveUser)}.{nameof(ActiveUserResource.GetProjects)}")]
public async Task<List<Stream>> StreamsGet(int limit = 10, CancellationToken cancellationToken = default)
{
var request = new GraphQLRequest
{
Query =
$@"query User {{
activeUser{{
id,
email,
name,
bio,
company,
avatar,
verified,
profiles,
role,
streams(limit:{limit}) {{
totalCount,
cursor,
items {{
id,
name,
description,
isPublic,
role,
createdAt,
updatedAt,
favoritedDate,
commentCount
favoritesCount
collaborators {{
id,
name,
role,
avatar
}}
}}
}}
}}
}}"
};
var res = await ExecuteGraphQLRequest<ActiveUserResponse>(request, cancellationToken).ConfigureAwait(false);
if (res?.activeUser == null)
{
throw new SpeckleException(
"User is not authenticated, or the credentials were not valid. Check the provided account is still valid, remove it from manager and add it again."
);
}
return res.activeUser.streams.items;
}
//TODO: API GAP
/// <summary>
/// Gets all favorite streams for the current user
/// </summary>
/// <param name="limit">Max number of streams to return</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<List<Stream>> FavoriteStreamsGet(int limit = 10, CancellationToken cancellationToken = default)
{
var request = new GraphQLRequest
{
Query =
$@"query User {{
activeUser{{
id,
email,
name,
bio,
company,
avatar,
verified,
profiles,
role,
favoriteStreams(limit:{limit}) {{
totalCount,
cursor,
items {{
id,
name,
description,
isPublic,
role,
createdAt,
updatedAt,
favoritedDate,
commentCount
favoritesCount
collaborators {{
id,
name,
role,
avatar
}}
}}
}}
}}
}}"
};
return (await ExecuteGraphQLRequest<ActiveUserResponse>(request, cancellationToken).ConfigureAwait(false))
.activeUser
.favoriteStreams
.items;
}
/// <summary>
/// Searches the user's streams by name, description, and ID
/// </summary>
/// <param name="query">String query to search for</param>
/// <param name="limit">Max number of streams to return</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
/// <seealso cref="GraphQL.Resources.ActiveUserResource.GetProjects"/>
[Obsolete($"Use client.{nameof(ActiveUser)}.{nameof(ActiveUserResource.GetProjects)}")]
public async Task<List<Stream>> StreamSearch(
string query,
int limit = 10,
CancellationToken cancellationToken = default
)
{
var request = new GraphQLRequest
{
Query =
@"query Streams ($query: String!, $limit: Int!) {
streams(query: $query, limit: $limit) {
totalCount,
cursor,
items {
id,
name,
description,
isPublic,
role,
createdAt,
updatedAt,
commentCount
favoritesCount
collaborators {
id,
name,
role
}
}
}
}",
Variables = new { query, limit }
};
var res = await GQLClient.SendMutationAsync<StreamsData>(request, cancellationToken).ConfigureAwait(false); //WARN: Why do we do this?
return (await ExecuteGraphQLRequest<StreamsData>(request, cancellationToken).ConfigureAwait(false)).streams.items;
}
[Obsolete($"Use client.{nameof(ActiveUser)}.{nameof(ActiveUserResource.GetProjects)}", true)]
public Task<List<Stream>> StreamsGet(int limit = 10, CancellationToken cancellationToken = default) =>
throw new NotImplementedException();
/// <summary>
/// Creates a stream.
@@ -378,33 +234,10 @@ public partial class Client
/// <param name="cancellationToken"></param>
/// <returns></returns>
/// <seealso cref="GraphQL.Resources.ProjectResource.GetWithTeam"/>
[Obsolete($"Use client.{nameof(Project)}.{nameof(ProjectResource.GetWithTeam)}")]
public async Task<Stream> StreamGetPendingCollaborators(
string streamId,
CancellationToken cancellationToken = default
)
[Obsolete($"Use client.{nameof(Project)}.{nameof(ProjectResource.GetWithTeam)}", true)]
public Task<Stream> StreamGetPendingCollaborators(string streamId, CancellationToken cancellationToken = default)
{
var request = new GraphQLRequest
{
Query =
@"query Stream($id: String!) {
stream(id: $id) {
id
pendingCollaborators {
id
inviteId
title
role
user {
avatar
}
}
}
}",
Variables = new { id = streamId }
};
var res = await GQLClient.SendMutationAsync<StreamData>(request, cancellationToken).ConfigureAwait(false); //WARN: Why do we do this?
return (await ExecuteGraphQLRequest<StreamData>(request, cancellationToken).ConfigureAwait(false)).stream;
throw new NotImplementedException();
}
/// <summary>
@@ -1,85 +1,16 @@
#nullable disable
using GraphQL;
using Speckle.Sdk.Api.GraphQL.Resources;
namespace Speckle.Sdk.Api;
public partial class Client
{
#region BranchCreated
[Obsolete($"Use {nameof(Subscription)}.{nameof(SubscriptionResource.CreateProjectModelsUpdatedSubscription)}", true)]
public void SubscribeBranchCreated(string streamId) => throw new NotImplementedException();
public delegate void BranchCreatedHandler(object sender, BranchInfo e);
[Obsolete($"Use {nameof(Subscription)}.{nameof(SubscriptionResource.CreateProjectModelsUpdatedSubscription)}", true)]
public void SubscribeBranchUpdated(string streamId, string branchId = null) => throw new NotImplementedException();
public event BranchCreatedHandler OnBranchCreated;
public IDisposable BranchCreatedSubscription { get; private set; }
/// <summary>
/// Subscribe to events of branch created for a stream
/// </summary>
/// <returns></returns>
public void SubscribeBranchCreated(string streamId)
{
var request = new GraphQLRequest { Query = $@"subscription {{ branchCreated (streamId: ""{streamId}"") }}" };
BranchCreatedSubscription = SubscribeTo<BranchCreatedResult>(
request,
(sender, result) => OnBranchCreated?.Invoke(sender, result.branchCreated)
);
}
public bool HasSubscribedBranchCreated => BranchCreatedSubscription != null;
#endregion
#region BranchUpdated
public delegate void BranchUpdatedHandler(object sender, BranchInfo e);
public event BranchUpdatedHandler OnBranchUpdated;
public IDisposable BranchUpdatedSubscription { get; private set; }
/// <summary>
/// Subscribe to events of branch updated for a stream
/// </summary>
/// <returns></returns>
public void SubscribeBranchUpdated(string streamId, string branchId = null)
{
var request = new GraphQLRequest
{
Query = $@"subscription {{ branchUpdated (streamId: ""{streamId}"", branchId: ""{branchId}"") }}"
};
BranchUpdatedSubscription = SubscribeTo<BranchUpdatedResult>(
request,
(sender, result) => OnBranchUpdated?.Invoke(sender, result.branchUpdated)
);
}
public bool HasSubscribedBranchUpdated => BranchUpdatedSubscription != null;
#endregion
#region BranchDeleted
public delegate void BranchDeletedHandler(object sender, BranchInfo e);
public event BranchDeletedHandler OnBranchDeleted;
public IDisposable BranchDeletedSubscription { get; private set; }
/// <summary>
/// Subscribe to events of branch deleted for a stream
/// </summary>
/// <returns></returns>
public void SubscribeBranchDeleted(string streamId)
{
var request = new GraphQLRequest { Query = $@"subscription {{ branchDeleted (streamId: ""{streamId}"") }}" };
BranchDeletedSubscription = SubscribeTo<BranchDeletedResult>(
request,
(sender, result) => OnBranchDeleted?.Invoke(sender, result.branchDeleted)
);
}
public bool HasSubscribedBranchDeleted => BranchDeletedSubscription != null;
#endregion
[Obsolete($"Use {nameof(Subscription)}.{nameof(SubscriptionResource.CreateProjectModelsUpdatedSubscription)}", true)]
public void SubscribeBranchDeleted(string streamId) => throw new NotImplementedException();
}
@@ -1,85 +1,25 @@
#nullable disable
using GraphQL;
using Speckle.Sdk.Api.GraphQL.Resources;
namespace Speckle.Sdk.Api;
public partial class Client
{
#region CommitCreated
[Obsolete(
$"Use {nameof(Subscription)}.{nameof(SubscriptionResource.CreateProjectVersionsUpdatedSubscription)}",
true
)]
public void SubscribeCommitCreated(string streamId) => throw new NotImplementedException();
public delegate void CommitCreatedHandler(object sender, CommitInfo e);
[Obsolete(
$"Use {nameof(Subscription)}.{nameof(SubscriptionResource.CreateProjectVersionsUpdatedSubscription)}",
true
)]
public void SubscribeCommitUpdated(string streamId, string commitId = null) => throw new NotImplementedException();
public event CommitCreatedHandler OnCommitCreated;
public IDisposable CommitCreatedSubscription;
/// <summary>
/// Subscribe to events of commit created for a stream
/// </summary>
/// <returns></returns>
public void SubscribeCommitCreated(string streamId)
{
var request = new GraphQLRequest { Query = $@"subscription {{ commitCreated (streamId: ""{streamId}"") }}" };
CommitCreatedSubscription = SubscribeTo<CommitCreatedResult>(
request,
(sender, result) => OnCommitCreated?.Invoke(sender, result.commitCreated)
);
}
public bool HasSubscribedCommitCreated => CommitCreatedSubscription != null;
#endregion
#region CommitUpdated
public delegate void CommitUpdatedHandler(object sender, CommitInfo e);
public event CommitUpdatedHandler OnCommitUpdated;
public IDisposable CommitUpdatedSubscription;
/// <summary>
/// Subscribe to events of commit updated for a stream
/// </summary>
/// <returns></returns>
public void SubscribeCommitUpdated(string streamId, string commitId = null)
{
var request = new GraphQLRequest
{
Query = $@"subscription {{ commitUpdated (streamId: ""{streamId}"", commitId: ""{commitId}"") }}"
};
var res = GQLClient.CreateSubscriptionStream<CommitUpdatedResult>(request);
CommitUpdatedSubscription = SubscribeTo<CommitUpdatedResult>(
request,
(sender, result) => OnCommitUpdated?.Invoke(sender, result.commitUpdated)
);
}
public bool HasSubscribedCommitUpdated => CommitUpdatedSubscription != null;
#endregion
#region CommitDeleted
public delegate void CommitDeletedHandler(object sender, CommitInfo e);
public event CommitDeletedHandler OnCommitDeleted;
public IDisposable CommitDeletedSubscription;
/// <summary>
/// Subscribe to events of commit updated for a stream
/// </summary>
/// <returns></returns>
public void SubscribeCommitDeleted(string streamId)
{
var request = new GraphQLRequest { Query = $@"subscription {{ commitDeleted (streamId: ""{streamId}"") }}" };
CommitDeletedSubscription = SubscribeTo<CommitDeletedResult>(
request,
(sender, result) => OnCommitDeleted?.Invoke(sender, result.commitDeleted)
);
}
public bool HasSubscribedCommitDeleted => CommitDeletedSubscription != null;
#endregion
[Obsolete(
$"Use {nameof(Subscription)}.{nameof(SubscriptionResource.CreateProjectVersionsUpdatedSubscription)}",
true
)]
public void SubscribeCommitDeleted(string streamId) => throw new NotImplementedException();
}
@@ -1,108 +1,22 @@
#nullable disable
using GraphQL;
using Speckle.Sdk.Api.GraphQL.Resources;
namespace Speckle.Sdk.Api;
public partial class Client
{
#region UserStreamAdded
[Obsolete($"Use {nameof(Subscription)}.{nameof(SubscriptionResource.CreateUserProjectsUpdatedSubscription)}", true)]
public void SubscribeUserStreamAdded() => throw new NotImplementedException();
public delegate void UserStreamAddedHandler(object sender, StreamInfo e);
[Obsolete($"Use {nameof(Subscription)}.{nameof(SubscriptionResource.CreateProjectUpdatedSubscription)}", true)]
public void SubscribeStreamUpdated(string id) => throw new NotImplementedException();
public event UserStreamAddedHandler OnUserStreamAdded;
public IDisposable UserStreamAddedSubscription;
[Obsolete($"Use {nameof(Subscription)}.{nameof(SubscriptionResource.CreateUserProjectsUpdatedSubscription)}", true)]
public void SubscribeUserStreamRemoved() => throw new NotImplementedException();
/// <summary>
/// Subscribe to events of streams added for the current user
/// </summary>
/// <returns></returns>
public void SubscribeUserStreamAdded()
{
var request = new GraphQLRequest { Query = @"subscription { userStreamAdded }" };
UserStreamAddedSubscription = SubscribeTo<UserStreamAddedResult>(
request,
(sender, result) => OnUserStreamAdded?.Invoke(sender, result.userStreamAdded)
);
}
public bool HasSubscribedUserStreamAdded => UserStreamAddedSubscription != null;
#endregion
#region StreamUpdated
public delegate void StreamUpdatedHandler(object sender, StreamInfo e);
public event StreamUpdatedHandler OnStreamUpdated;
public IDisposable StreamUpdatedSubscription;
/// <summary>
/// Subscribe to events of streams updated for a specific streamId
/// </summary>
/// <param name="id">streamId</param>
public void SubscribeStreamUpdated(string id)
{
var request = new GraphQLRequest { Query = $@"subscription {{ streamUpdated( streamId: ""{id}"") }}" };
StreamUpdatedSubscription = SubscribeTo<StreamUpdatedResult>(
request,
(sender, result) => OnStreamUpdated?.Invoke(sender, result.streamUpdated)
);
}
public bool HasSubscribedStreamUpdated => StreamUpdatedSubscription != null;
#endregion
#region StreamRemoved
public delegate void UserStreamRemovedHandler(object sender, StreamInfo e);
public event UserStreamRemovedHandler OnUserStreamRemoved;
public IDisposable UserStreamRemovedSubscription;
/// <summary>
/// Subscribe to events of streams removed for the current user
/// </summary>
public void SubscribeUserStreamRemoved()
{
var request = new GraphQLRequest { Query = @"subscription { userStreamRemoved }" };
UserStreamRemovedSubscription = SubscribeTo<UserStreamRemovedResult>(
request,
(sender, result) => OnUserStreamRemoved?.Invoke(sender, result.userStreamRemoved)
);
}
public bool HasSubscribedUserStreamRemoved => UserStreamRemovedSubscription != null;
#endregion
#region CommentActivity
public delegate void CommentActivityHandler(object sender, CommentItem e);
public event CommentActivityHandler OnCommentActivity;
public IDisposable CommentActivitySubscription;
/// <summary>
/// Subscribe to new comment events
/// </summary>
///
public void SubscribeCommentActivity(string streamId)
{
var request = new GraphQLRequest
{
Query =
$@"subscription {{ commentActivity( streamId: ""{streamId}"") {{ type comment {{ id authorId archived screenshot rawText }} }} }}"
};
CommentActivitySubscription = SubscribeTo<CommentActivityResponse>(
request,
(sender, result) => OnCommentActivity?.Invoke(sender, result.commentActivity.comment)
);
}
public bool HasSubscribedCommentActivity => CommentActivitySubscription != null;
#endregion
[Obsolete(
$"Use {nameof(Subscription)}.{nameof(SubscriptionResource.CreateProjectCommentsUpdatedSubscription)}",
true
)]
public void SubscribeCommentActivity(string streamId) => throw new NotImplementedException();
}
@@ -154,7 +154,7 @@ public class Stream
/// </summary>
public Commits commits { get; set; }
public Activity activity { get; set; }
public ResourceCollection<Activity> activity { get; set; }
public SpeckleObject @object { get; set; }
@@ -222,40 +222,6 @@ public class Commit
}
}
public class Activity
{
public int totalCount { get; set; }
public DateTime cursor { get; set; }
public List<ActivityItem> items { get; set; }
}
public class ActivityItem
{
public string actionType { get; set; }
public string userId { get; set; }
public string streamId { get; set; }
public string resourceId { get; set; }
public string resourceType { get; set; }
public DateTime time { get; set; }
public Info info { get; set; }
public string message { get; set; }
}
public class Info
{
public string message { get; set; }
public string sourceApplication { get; set; }
public InfoCommit commit { get; set; }
}
public class InfoCommit
{
public string message { get; set; }
public string sourceApplication { get; set; }
public string branchName { get; set; }
}
[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)]
public class SpeckleObject
{
@@ -1,96 +0,0 @@
#nullable disable
using System.Text.Json.Serialization;
namespace Speckle.Sdk.Api;
#region manager api
public class Connector
{
public List<ConnectorVersion> Versions { get; set; } = new();
}
public class ConnectorVersion
{
public ConnectorVersion(string number, string url, Os os = Os.Win, Architecture architecture = Architecture.Any)
{
Number = number;
Url = url;
Date = DateTime.Now;
Prerelease = Number.Contains("-");
Os = os;
Architecture = architecture;
}
public string Number { get; set; }
public string Url { get; set; }
public Os Os { get; set; }
public Architecture Architecture { get; set; } = Architecture.Any;
public DateTime Date { get; set; }
[JsonIgnore]
public string DateTimeAgo => Helpers.TimeAgo(Date);
public bool Prerelease { get; set; }
}
/// <summary>
/// OS
/// NOTE: do not edit order and only append new items as they are serialized to ints
/// </summary>
public enum Os
{
Win, //0
OSX, //1
Linux, //2
Any //3
}
/// <summary>
/// Architecture
/// NOTE: do not edit order and only append new items as they are serialized to ints
/// </summary>
public enum Architecture
{
Any, //0
Arm, //1
Intel //2
}
//GHOST API
public class Meta
{
public Pagination pagination { get; set; }
}
public class Pagination
{
public int page { get; set; }
public string limit { get; set; }
public int pages { get; set; }
public int total { get; set; }
public object next { get; set; }
public object prev { get; set; }
}
public class Tags
{
public List<Tag> tags { get; set; }
public Meta meta { get; set; }
}
public class Tag
{
public string id { get; set; }
public string name { get; set; }
public string slug { get; set; }
public string description { get; set; }
public string feature_image { get; set; }
public string visibility { get; set; }
public string codeinjection_head { get; set; }
public object codeinjection_foot { get; set; }
public object canonical_url { get; set; }
public string accent_color { get; set; }
public string url { get; set; }
}
#endregion
@@ -0,0 +1,30 @@
#nullable disable
namespace Speckle.Sdk.Api.GraphQL.Models;
public sealed class Activity
{
public string actionType { get; init; }
public string id { get; init; }
public Info info { get; init; }
public string message { get; init; }
public string resourceId { get; init; }
public string resourceType { get; init; }
public string streamId { get; init; }
public DateTime time { get; init; }
public string userId { get; init; }
}
public sealed class Info
{
public string message { get; init; }
public string sourceApplication { get; init; }
public InfoCommit commit { get; init; }
}
public sealed class InfoCommit
{
public string message { get; init; }
public string sourceApplication { get; init; }
public string branchName { get; init; }
}
@@ -20,10 +20,4 @@ public sealed class FileUpload
public bool uploadComplete { get; init; }
public DateTime uploadDate { get; init; }
public string userId { get; init; }
[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)]
public string branchName { get; init; }
[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)]
public string streamId { get; init; }
}
@@ -13,15 +13,6 @@ public abstract class UserBase
public string role { get; init; }
public ResourceCollection<Activity> timeline { get; init; }
public bool? verified { get; init; }
[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)]
public int totalOwnedStreamsFavorites { get; init; }
[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)]
public ResourceCollection<Commit> commits { get; init; }
[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)]
public ResourceCollection<Stream> streams { get; init; }
}
public sealed class LimitedUser : UserBase
@@ -41,9 +32,6 @@ public sealed class User : UserBase
public List<PendingStreamCollaborator> projectInvites { get; init; }
public ResourceCollection<Project> projects { get; init; }
[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)]
public ResourceCollection<Stream> favoriteStreams { get; init; }
public override string ToString()
{
return $"User ({email} | {name} | {id})";
-122
View File
@@ -1,133 +1,11 @@
using System.Collections.Concurrent;
using System.Diagnostics.Contracts;
using System.Reflection;
using System.Runtime.InteropServices;
using Speckle.Newtonsoft.Json;
using Speckle.Sdk.Api.GraphQL.Models;
using Speckle.Sdk.Common;
using Speckle.Sdk.Credentials;
using Speckle.Sdk.Helpers;
using Speckle.Sdk.Host;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
using Speckle.Sdk.Transports;
namespace Speckle.Sdk.Api;
public static class Helpers
{
public const string RELEASES_URL = "https://releases.speckle.dev";
private const string FEEDS_ENDPOINT = RELEASES_URL + "/manager2/feeds";
/// <summary>
/// Helper method to Receive from a Speckle Server.
/// </summary>
/// <param name="stream">Stream URL or Id to receive from. If the URL contains branchName, commitId or objectId those will be used, otherwise the latest commit from main will be received.</param>
/// <param name="account">Account to use. If not provided the default account will be used.</param>
/// <param name="onProgressAction">Action invoked on progress iterations.</param>
/// <param name="onTotalChildrenCountKnown">Action invoked once the total count of objects is known.</param>
/// <returns></returns>
public static async Task<Base> Receive(
this IServerTransportFactory serverTransportFactory,
string stream,
Account? account = null,
Action<ConcurrentBag<ProgressArgs>>? onProgressAction = null,
Action<int>? onTotalChildrenCountKnown = null
)
{
var sw = new StreamWrapper(stream);
try
{
account ??= await sw.GetAccount().ConfigureAwait(false);
}
catch (SpeckleException)
{
if (string.IsNullOrEmpty(sw.StreamId))
{
throw;
}
//Fallback to a non authed account
account = new Account
{
token = "",
serverInfo = new ServerInfo { url = sw.ServerUrl },
userInfo = new UserInfo()
};
}
using var client = new Client(account);
using var transport = serverTransportFactory.Create(client.Account, sw.StreamId);
string objectId = "";
Commit? commit = null;
//OBJECT URL
if (!string.IsNullOrEmpty(sw.ObjectId))
{
objectId = sw.ObjectId.NotNull();
}
//COMMIT URL
else if (!string.IsNullOrEmpty(sw.CommitId))
{
commit = await client.CommitGet(sw.StreamId, sw.CommitId.NotNull()).ConfigureAwait(false);
objectId = commit.referencedObject;
}
//BRANCH URL OR STREAM URL
else
{
var branchName = string.IsNullOrEmpty(sw.BranchName) ? "main" : sw.BranchName;
var branch = await client.BranchGet(sw.StreamId, branchName.NotNull(), 1).ConfigureAwait(false);
if (branch.commits.items.Count == 0)
{
throw new SpeckleException("The selected branch has no commits.");
}
commit = branch.commits.items[0];
objectId = branch.commits.items[0].referencedObject;
}
Analytics.TrackEvent(
client.Account,
Analytics.Events.Receive,
new Dictionary<string, object>
{
{ "sourceHostApp", HostApplications.GetHostAppFromString(commit.NotNull().sourceApplication).Slug },
{ "sourceHostAppVersion", commit.sourceApplication }
}
);
var receiveRes = await Operations
.Receive(
objectId,
transport,
onProgressAction: onProgressAction,
onTotalChildrenCountKnown: onTotalChildrenCountKnown
)
.ConfigureAwait(false);
try
{
await client
.CommitReceived(
new CommitReceivedInput
{
streamId = sw.StreamId,
commitId = commit?.id,
message = commit?.message,
sourceApplication = "Other"
}
)
.ConfigureAwait(false);
}
catch (Exception ex) when (!ex.IsFatal())
{
// Do nothing!
}
return receiveRes;
}
/// <inheritdoc cref="TimeAgo(DateTime)"/>
/// <param name="fallback">value to fallback to if the given <paramref name="timestamp"/> is <see langword="null"/></param>
@@ -108,7 +108,7 @@ public static partial class Operations
using var activity = SpeckleActivityFactory.Start("Deserialize");
// Proceed to deserialize the object, now safely knowing that all its children are present in the local (fast) transport.
Base res = await serializer.Deserialize(objString);
Base res = await serializer.Deserialize(objString).ConfigureAwait(false);
timer.Stop();
SpeckleLog.Logger.Information(
@@ -1,5 +1,4 @@
using Speckle.Newtonsoft.Json;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
using Speckle.Sdk.Serialisation;
using Speckle.Sdk.Transports;
@@ -40,6 +39,6 @@ public static partial class Operations
public static async Task<Base> Deserialize(string value, CancellationToken cancellationToken = default)
{
var deserializer = new SpeckleObjectDeserializer { CancellationToken = cancellationToken };
return await deserializer.Deserialize(value);
return await deserializer.Deserialize(value).ConfigureAwait(false);
}
}
-2
View File
@@ -1,5 +1,3 @@
using Speckle.Sdk.Logging;
namespace Speckle.Sdk.Common;
/// <summary>
+20 -21
View File
@@ -7,21 +7,21 @@ namespace Speckle.Sdk.Common;
/// </summary>
public readonly struct HashCode : IEquatable<HashCode>
{
private const int EmptyCollectionPrimeNumber = 19;
private readonly int value;
private const int EMPTY_COLLECTION_PRIME_NUMBER = 19;
private readonly int _value;
/// <summary>
/// Initializes a new instance of the <see cref="HashCode"/> struct.
/// </summary>
/// <param name="value">The value.</param>
private HashCode(int value) => this.value = value;
private HashCode(int value) => _value = value;
/// <summary>
/// Performs an implicit conversion from <see cref="HashCode"/> to <see cref="int"/>.
/// </summary>
/// <param name="hashCode">The hash code.</param>
/// <returns>The result of the conversion.</returns>
public static implicit operator int(HashCode hashCode) => hashCode.value;
public static implicit operator int(HashCode hashCode) => hashCode.ToInt32();
/// <summary>
/// Implements the operator ==.
@@ -53,7 +53,7 @@ public readonly struct HashCode : IEquatable<HashCode>
/// <typeparam name="T">The type of the items.</typeparam>
/// <param name="items">The collection.</param>
/// <returns>The new hash code.</returns>
public static HashCode OfEach<T>(IEnumerable<T> items) =>
public static HashCode OfEach<T>(IEnumerable<T>? items) =>
items == null ? new HashCode(0) : new HashCode(GetHashCode(items, 0));
/// <summary>
@@ -62,7 +62,7 @@ public readonly struct HashCode : IEquatable<HashCode>
/// <typeparam name="T">The type of the item.</typeparam>
/// <param name="item">The item.</param>
/// <returns>The new hash code.</returns>
public HashCode And<T>(T item) => new HashCode(CombineHashCodes(this.value, GetHashCode(item)));
public HashCode And<T>(T item) => new HashCode(CombineHashCodes(this._value, GetHashCode(item)));
/// <summary>
/// Adds the hash code of the specified items in the collection.
@@ -70,38 +70,32 @@ public readonly struct HashCode : IEquatable<HashCode>
/// <typeparam name="T">The type of the items.</typeparam>
/// <param name="items">The collection.</param>
/// <returns>The new hash code.</returns>
public HashCode AndEach<T>(IEnumerable<T> items)
public HashCode AndEach<T>(IEnumerable<T>? items)
{
if (items == null)
{
return new HashCode(this.value);
return new HashCode(this._value);
}
return new HashCode(GetHashCode(items, this.value));
return new HashCode(GetHashCode(items, this._value));
}
/// <inheritdoc />
public bool Equals(HashCode other) => this.value.Equals(other.value);
public bool Equals(HashCode other) => this._value.Equals(other._value);
/// <inheritdoc />
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
if (obj is HashCode)
if (obj is HashCode code)
{
return this.Equals((HashCode)obj);
return this.Equals(code);
}
return false;
}
/// <summary>
/// Throws <see cref="NotSupportedException" />.
/// </summary>
/// <returns>Does not return.</returns>
/// <exception cref="NotSupportedException">Implicitly convert this struct to an <see cref="int" /> to get the hash code.</exception>
[EditorBrowsable(EditorBrowsableState.Never)]
public override int GetHashCode() =>
throw new NotSupportedException("Implicitly convert this struct to an int to get the hash code.");
public override int GetHashCode() => ToInt32();
private static int CombineHashCodes(int h1, int h2)
{
@@ -130,9 +124,14 @@ public readonly struct HashCode : IEquatable<HashCode>
}
else
{
temp = CombineHashCodes(temp, EmptyCollectionPrimeNumber);
temp = CombineHashCodes(temp, EMPTY_COLLECTION_PRIME_NUMBER);
}
return temp;
}
public int ToInt32()
{
return _value;
}
}
+20 -22
View File
@@ -206,9 +206,8 @@ public static class AccountManager
/// <summary>
/// The Default Server URL for authentication, can be overridden by placing a file with the alternatrive url in the Speckle folder or with an ENV_VAR
/// </summary>
public static string GetDefaultServerUrl()
public static Uri GetDefaultServerUrl()
{
var serverUrl = DEFAULT_SERVER_URL;
var customServerUrl = "";
// first mechanism, check for local file
@@ -227,14 +226,13 @@ public static class AccountManager
if (!string.IsNullOrEmpty(customServerUrl))
{
Uri.TryCreate(customServerUrl, UriKind.Absolute, out Uri url);
if (url != null)
if (Uri.TryCreate(customServerUrl, UriKind.Absolute, out Uri? url))
{
serverUrl = customServerUrl.TrimEnd('/');
return url;
}
}
return serverUrl;
return new Uri(DEFAULT_SERVER_URL);
}
/// <param name="id">The Id of the account to fetch</param>
@@ -465,9 +463,9 @@ public static class AccountManager
//TODO: reset default account
s_accountStorage.DeleteObject(id);
var accounts = GetAccounts();
//BUG: Clearly this is a bug bug bug!
if (accounts.Any() && !accounts.Any(x => x.isDefault))
var accounts = GetAccounts().ToArray();
if (accounts.Length != 0 && !accounts.Any(x => x.isDefault))
{
ChangeDefaultAccount(accounts.First().id);
}
@@ -529,10 +527,10 @@ public static class AccountManager
return searchResult;
}
private static string EnsureCorrectServerUrl(string server)
private static Uri EnsureCorrectServerUrl(Uri? server)
{
var localUrl = server;
if (string.IsNullOrEmpty(localUrl))
if (localUrl == null)
{
localUrl = GetDefaultServerUrl();
SpeckleLog.Logger.Debug(
@@ -540,7 +538,7 @@ public static class AccountManager
localUrl
);
}
return localUrl.TrimEnd('/');
return localUrl;
}
private static void EnsureGetAccessCodeFlowIsSupported()
@@ -552,7 +550,7 @@ public static class AccountManager
}
}
private static async Task<string> GetAccessCode(string server, string challenge, TimeSpan timeout)
private static async Task<string> GetAccessCode(Uri server, string challenge, TimeSpan timeout)
{
EnsureGetAccessCodeFlowIsSupported();
@@ -603,7 +601,7 @@ public static class AccountManager
"Local auth flow failed to complete within the timeout window. Access code is {accessCode}",
accessCode
);
throw new Exception("Local auth flow failed to complete within the timeout window");
throw new AuthFlowException("Local auth flow failed to complete within the timeout window");
}
if (task.IsFaulted)
@@ -611,9 +609,9 @@ public static class AccountManager
SpeckleLog.Logger.Error(
task.Exception,
"Getting access code flow failed with {exceptionMessage}",
task.Exception.Message
task.Exception?.Message
);
throw new Exception($"Auth flow failed: {task.Exception.Message}", task.Exception);
throw new AuthFlowException($"Auth flow failed: {task.Exception?.Message}", task.Exception);
}
// task completed within timeout
@@ -624,12 +622,12 @@ public static class AccountManager
return accessCode;
}
private static async Task<Account> CreateAccount(string accessCode, string challenge, string server)
private static async Task<Account> CreateAccount(string accessCode, string challenge, Uri server)
{
try
{
var tokenResponse = await GetToken(accessCode, challenge, server).ConfigureAwait(false);
var userResponse = await GetUserServerInfo(tokenResponse.token, new(server)).ConfigureAwait(false);
var userResponse = await GetUserServerInfo(tokenResponse.token, server).ConfigureAwait(false);
var account = new Account
{
@@ -703,7 +701,7 @@ public static class AccountManager
/// </summary>
/// <param name="server">Server to use to add the account, if not provied the default Server will be used</param>
/// <returns></returns>
public static async Task AddAccount(string server = "")
public static async Task AddAccount(Uri? server = null)
{
SpeckleLog.Logger.Debug("Starting to add account for {serverUrl}", server);
@@ -747,7 +745,7 @@ public static class AccountManager
}
}
private static async Task<TokenExchangeResponse> GetToken(string accessCode, string challenge, string server)
private static async Task<TokenExchangeResponse> GetToken(string accessCode, string challenge, Uri server)
{
try
{
@@ -763,7 +761,7 @@ public static class AccountManager
using var content = new StringContent(JsonConvert.SerializeObject(body));
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await client.PostAsync($"{server}/auth/token", content).ConfigureAwait(false);
var response = await client.PostAsync(new Uri(server, "/auth/token"), content).ConfigureAwait(false);
return JsonConvert
.DeserializeObject<TokenExchangeResponse>(await response.Content.ReadAsStringAsync().ConfigureAwait(false))
@@ -817,7 +815,7 @@ public static class AccountManager
var headers = response.Headers;
const string HEADER = "x-speckle-frontend-2";
if (!headers.TryGetValues(HEADER, out IEnumerable<string> values))
if (!headers.TryGetValues(HEADER, out IEnumerable<string>? values))
{
return false;
}
@@ -0,0 +1,12 @@
namespace Speckle.Sdk.Credentials;
public sealed class AuthFlowException : Exception
{
public AuthFlowException(string? message, Exception? innerException)
: base(message, innerException) { }
public AuthFlowException(string? message)
: base(message) { }
public AuthFlowException() { }
}
@@ -1,5 +1,3 @@
using Speckle.Sdk.Logging;
namespace Speckle.Sdk.Credentials;
public class SpeckleAccountManagerException : SpeckleException
-23
View File
@@ -1,5 +1,4 @@
using System.Runtime.InteropServices;
using Speckle.Sdk.Api;
using Speckle.Sdk.Api.GraphQL.Models;
namespace Speckle.Sdk.Credentials;
@@ -25,26 +24,4 @@ public sealed class UserInfo
public string email { get; init; }
public string? company { get; init; }
public string? avatar { get; init; }
[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)]
public Streams streams { get; init; }
[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)]
public Commits commits { get; init; }
}
[ClassInterface(ClassInterfaceType.AutoDual)]
[ComVisible(true)]
[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)]
public class Streams
{
public int totalCount { get; set; }
}
[ClassInterface(ClassInterfaceType.AutoDual)]
[ComVisible(true)]
[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)]
public class Commits
{
public int totalCount { get; set; }
}
+5 -4
View File
@@ -1,13 +1,14 @@
using System.Diagnostics.CodeAnalysis;
using System.Text.RegularExpressions;
using System.Web;
using Speckle.Sdk.Api;
using Speckle.Sdk.Api.GraphQL;
using Speckle.Sdk.Common;
using Speckle.Sdk.Helpers;
using Speckle.Sdk.Logging;
namespace Speckle.Sdk.Credentials;
[SuppressMessage("Design", "CA1054:URI-like parameters should not be strings", Justification = "Class needs re-write")]
[SuppressMessage("Usage", "CA2234:Pass system uri objects instead of strings", Justification = "Class needs re-write")]
public class StreamWrapper
{
private Account? _account;
@@ -212,7 +213,7 @@ public class StreamWrapper
switch (uri.Segments.Length)
{
case 3: // ie http://speckle.server/streams/8fecc9aa6d
if (uri.Segments[1].ToLowerInvariant() != "streams/")
if (!uri.Segments[1].Equals("streams/", StringComparison.InvariantCultureIgnoreCase))
{
throw new SpeckleException($"Cannot parse {uri} into a stream wrapper class.");
}
@@ -223,7 +224,7 @@ public class StreamWrapper
break;
case 4: // ie https://speckle.server/streams/0c6ad366c4/globals/
if (uri.Segments[3].ToLowerInvariant().StartsWith("globals"))
if (uri.Segments[3].StartsWith("globals", StringComparison.InvariantCultureIgnoreCase))
{
StreamId = uri.Segments[2].Replace("/", "");
BranchName = Uri.UnescapeDataString(uri.Segments[3].Replace("/", ""));
+5 -4
View File
@@ -49,7 +49,7 @@ public static class Http
/// <returns>True if the user is connected to the internet, false otherwise.</returns>
public static async Task<bool> UserHasInternet()
{
string? defaultServer = null;
Uri? defaultServer = null;
try
{
//Perform a quick ping test e.g. to cloudflaire dns, as is quicker than pinging server
@@ -59,8 +59,7 @@ public static class Http
}
defaultServer = AccountManager.GetDefaultServerUrl();
Uri serverUrl = new(defaultServer);
await HttpPing(serverUrl).ConfigureAwait(false);
await HttpPing(defaultServer).ConfigureAwait(false);
return true;
}
catch (HttpRequestException ex)
@@ -169,7 +168,9 @@ public static class Http
{
if (!string.IsNullOrEmpty(authToken))
{
bearerHeader = authToken.NotNull().ToLowerInvariant().Contains("bearer") ? authToken : $"Bearer {authToken}";
bearerHeader = authToken.NotNull().StartsWith("bearer", StringComparison.InvariantCultureIgnoreCase)
? authToken
: $"Bearer {authToken}";
return true;
}
@@ -54,10 +54,8 @@ public sealed class SpeckleHttpClientHandler : DelegatingHandler
activity?.SetTag("retryCount", retryCount);
SpeckleLog.Logger.Information(
"Execution of http request to {httpScheme}://{hostUrl}{relativeUrl} {resultStatus} with {httpStatusCode} after {elapsed} seconds and {retryCount} retries. Request correlation ID: {correlationId}",
request.RequestUri.Scheme,
request.RequestUri.Host,
request.RequestUri.PathAndQuery,
"Execution of http request to {url} {resultStatus} with {httpStatusCode} after {elapsed} seconds and {retryCount} retries. Request correlation ID: {correlationId}",
request.RequestUri,
policyResult.Outcome == OutcomeType.Successful ? "succeeded" : "failed",
policyResult.Result?.StatusCode,
sw.Elapsed.TotalSeconds,
-87
View File
@@ -1,87 +0,0 @@
#nullable disable
namespace Speckle.Sdk.Helpers;
public class State<T> : IDisposable
where T : State<T>, new()
{
private static T root;
private static T current;
private T previous = current;
private static readonly object StateChangeLock = new();
protected State()
{
lock (StateChangeLock)
{
current = (T)this;
if (root == null)
{
root = (T)this;
}
}
}
void IDisposable.Dispose()
{
lock (StateChangeLock)
{
if (previous == null)
{
return; // Already disposed or root
}
if (current == this)
{
current = previous;
}
else
{
// If this state is still in the stack is safe to pop it
var state = this;
do
{
if (state == root)
{
current = previous;
break;
}
state = state.previous;
} while (state != null);
}
previous = null;
}
}
public static T Peek => current;
public static T Push()
{
lock (StateChangeLock)
{
var peek = current ?? new T();
var top = (T)peek.MemberwiseClone();
top.previous = current;
return current = top;
}
}
public static void Pop()
{
lock (StateChangeLock)
{
((IDisposable)current).Dispose();
}
}
protected void Pull()
{
lock (StateChangeLock)
{
((IDisposable)this).Dispose();
}
}
}
+8 -8
View File
@@ -2,12 +2,12 @@
namespace Speckle.Sdk.Host;
[AttributeUsage(AttributeTargets.Constructor)]
public sealed class SchemaInfo : Attribute
public sealed class SchemaInfoAttribute : Attribute
{
public SchemaInfo(string name, string description)
public SchemaInfoAttribute(string name, string description)
: this(name, description, null, null) { }
public SchemaInfo(string name, string description, string category, string subcategory)
public SchemaInfoAttribute(string name, string description, string category, string subcategory)
{
Name = name;
Description = description;
@@ -25,12 +25,12 @@ public sealed class SchemaInfo : Attribute
}
[AttributeUsage(AttributeTargets.Constructor)]
public sealed class SchemaDeprecated : Attribute { }
public sealed class SchemaDeprecatedAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class SchemaParamInfo : Attribute
public sealed class SchemaParamInfoAttribute : Attribute
{
public SchemaParamInfo(string description)
public SchemaParamInfoAttribute(string description)
{
Description = description;
}
@@ -42,7 +42,7 @@ public sealed class SchemaParamInfo : Attribute
/// Used to indicate which is the main input parameter of the schema builder component. Schema info will be attached to this object.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class SchemaMainParam : Attribute { }
public sealed class SchemaMainParamAttribute : Attribute { }
// TODO: this could be nuked, as it's only used to hide props on Base,
// which we might want to expose anyways...
@@ -50,7 +50,7 @@ public sealed class SchemaMainParam : Attribute { }
/// Used to ignore properties from expand objects etc
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public sealed class SchemaIgnore : Attribute { }
public sealed class SchemaIgnoreAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Method)]
public sealed class SchemaComputedAttribute : Attribute
+6 -2
View File
@@ -14,7 +14,7 @@ public static class TypeLoader
private static ConcurrentDictionary<string, Type> s_cachedTypes = new();
private static ConcurrentDictionary<Type, string> s_fullTypeStrings = new();
private static ConcurrentDictionary<PropertyInfo, JsonPropertyAttribute> s_jsonPropertyAttribute = new();
private static ConcurrentDictionary<PropertyInfo, JsonPropertyAttribute?> s_jsonPropertyAttribute = new();
private static ConcurrentDictionary<Type, IReadOnlyList<PropertyInfo>> s_propInfoCache = new();
public static IEnumerable<LoadedType> Types => s_availableTypes;
@@ -171,6 +171,10 @@ public static class TypeLoader
throw new InvalidOperationException($"{type.FullName} inherits from Base has no SpeckleTypeAttribute");
}
var deprecatedSpeckleTypes = type.GetCustomAttributes<DeprecatedSpeckleTypeAttribute>();
return new LoadedType(speckleType.Name, type, deprecatedSpeckleTypes.Select(x => x.Name).ToList());
return new LoadedType(
speckleType.SpeckleTypeName,
type,
deprecatedSpeckleTypes.Select(x => x.SpeckleTypeName).ToList()
);
}
}
+7 -5
View File
@@ -75,7 +75,7 @@ public static class Analytics
}
private const string MIXPANEL_TOKEN = "acd87c5a50b56df91a795e999812a3a4";
private const string MIXPANEL_SERVER = "https://analytics.speckle.systems";
private static readonly Uri s_mixpanelServer = new("https://analytics.speckle.systems");
/// <summary>
/// Cached email
@@ -240,7 +240,7 @@ public static class Analytics
using HttpClient client = new();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain"));
query.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var res = await client.PostAsync(MIXPANEL_SERVER + "/track?ip=1", query).ConfigureAwait(false);
var res = await client.PostAsync(new Uri(s_mixpanelServer, "/track?ip=1"), query).ConfigureAwait(false);
res.EnsureSuccessStatusCode();
}
catch (Exception ex) when (!ex.IsFatal())
@@ -277,7 +277,9 @@ public static class Analytics
using HttpClient client = Http.GetHttpProxyClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain"));
query.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var res = await client.PostAsync(MIXPANEL_SERVER + "/engage#profile-union", query).ConfigureAwait(false);
var res = await client
.PostAsync(new Uri(s_mixpanelServer, "/engage#profile-union"), query)
.ConfigureAwait(false);
res.EnsureSuccessStatusCode();
}
catch (Exception ex) when (!ex.IsFatal())
@@ -288,7 +290,7 @@ public static class Analytics
});
}
internal static void IdentifyProfile(string hashedEmail, string connector)
internal static void IdentifyProfile(string hashedEmail)
{
Task.Run(async () =>
{
@@ -309,7 +311,7 @@ public static class Analytics
using HttpClient client = Http.GetHttpProxyClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain"));
query.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var res = await client.PostAsync(MIXPANEL_SERVER + "/engage#profile-set", query).ConfigureAwait(false);
var res = await client.PostAsync(new Uri(s_mixpanelServer, "/engage#profile-set"), query).ConfigureAwait(false);
res.EnsureSuccessStatusCode();
}
catch (Exception ex) when (!ex.IsFatal())
-138
View File
@@ -1,138 +0,0 @@
#nullable disable
using Speckle.Newtonsoft.Json;
namespace Speckle.Sdk.Models;
/// <summary>
/// A simple wrapper to keep track of the relationship between speckle objects and their host-application siblings in cases where the
/// <see cref="Base.applicationId"/> cannot correspond with the <see cref="ApplicationObject.CreatedIds"/> (ie, on receiving operations).
/// </summary>
public class ApplicationObject
{
public enum State
{
Unknown = default,
Created, // Speckle object created on send, or native objects created on receive
Skipped, // Speckle or Application object is not going to be sent or received
Updated, // Application object is replacing an existing object in the application
Failed, // Tried to convert & send or convert & bake but something went wrong
Removed, //Removed object from application
}
public ApplicationObject(string id, string type)
{
OriginalId = id;
Descriptor = type;
Status = State.Unknown;
}
/// <summary>
/// ID of the object from host application that generated it.
/// </summary>
public string applicationId { get; set; }
/// <summary>
/// The container for the object in the native application
/// </summary>
public string Container { get; set; }
/// <summary>
/// Indicates if conversion is supported by the converter
/// </summary>
public bool Convertible { get; set; }
/// <summary>
/// The fallback values if direct conversion is not available, typically displayValue
/// </summary>
[JsonIgnore]
public List<ApplicationObject> Fallback { get; set; } = new();
/// <summary>
/// The Speckle id (on receive) or native id (on send)
/// </summary>
/// <remarks>
/// Used to retrieve this object in <code>ProgressReport.GetReportObject()</code>, typically to pass between connectors and converters
/// </remarks>
public string OriginalId { get; set; }
/// <summary>
/// A descriptive string to describe the object. Use the object type as default.
/// </summary>
public string Descriptor { get; set; }
/// <summary>
/// The created object ids associated with this object
/// </summary>
/// <remarks>
/// On send, this is currently left empty as generating Speckle ids would be performance expensive
/// </remarks>
public List<string> CreatedIds { get; set; } = new();
/// <summary>
/// Conversion status of object
/// </summary>
public State Status { get; set; }
/// <summary>
/// Conversion notes or other important information to expose to the user
/// </summary>
public List<string> Log { get; set; } = new();
/// <summary>
/// Converted objects corresponding to this object
/// </summary>
/// <remarks>
/// Used during receive for convenience, corresponds to CreatedIds
/// </remarks>
[JsonIgnore]
public List<object> Converted { get; set; } = new();
public void Update(
string createdId = null,
List<string> createdIds = null,
State? status = null,
string container = null,
List<string> log = null,
string logItem = null,
List<object> converted = null,
object convertedItem = null,
string descriptor = null
)
{
createdIds?.Where(o => !string.IsNullOrEmpty(o) && !CreatedIds.Contains(o))?.ToList().ForEach(CreatedIds.Add);
if (createdId != null && !CreatedIds.Contains(createdId))
{
CreatedIds.Add(createdId);
}
if (status.HasValue)
{
Status = status.Value;
}
log?.Where(o => !string.IsNullOrEmpty(o) && !Log.Contains(o))?.ToList().ForEach(Log.Add);
if (!string.IsNullOrEmpty(logItem) && !Log.Contains(logItem))
{
Log.Add(logItem);
}
if (convertedItem != null && !Converted.Contains(convertedItem))
{
Converted.Add(convertedItem);
}
converted?.Where(o => o != null && !Converted.Contains(o))?.ToList().ForEach(Converted.Add);
if (!string.IsNullOrEmpty(container))
{
Container = container;
}
if (!string.IsNullOrEmpty(descriptor))
{
Descriptor = descriptor;
}
}
}
+5 -5
View File
@@ -6,7 +6,7 @@ namespace Speckle.Sdk.Models;
/// <para>Only applies to properties of types derived from the Base class.</para>
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public sealed class DetachProperty : Attribute
public sealed class DetachPropertyAttribute : Attribute
{
/// <summary>
/// <para>Flags an object's property as being detachable.</para>
@@ -14,7 +14,7 @@ public sealed class DetachProperty : Attribute
/// <para>Only applies to properties of types derived from the Base class.</para>
/// </summary>
/// <param name="detachable">Whether to detach the property or not.</param>
public DetachProperty(bool detachable = true)
public DetachPropertyAttribute(bool detachable = true)
{
Detachable = detachable;
}
@@ -23,14 +23,14 @@ public sealed class DetachProperty : Attribute
}
/// <summary>
/// Flags a list or array as splittable into chunks during serialisation. These chunks will be recomposed on deserialisation into the original list. Note: this attribute should be used in conjunction with <see cref="DetachProperty"/>.
/// Flags a list or array as splittable into chunks during serialisation. These chunks will be recomposed on deserialisation into the original list. Note: this attribute should be used in conjunction with <see cref="DetachPropertyAttribute"/>.
/// <para>Use this attribute on properties that can become very long and are not worth detaching into individual elements.</para>
/// <para>Objects per chunk: for simple types, like numbers, use a high value (>10000); for other objects, use a more conservative number depending on their serialised size.</para>
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public sealed class Chunkable : Attribute
public sealed class ChunkableAttribute : Attribute
{
public Chunkable(int maxObjCountPerChunk = 1000)
public ChunkableAttribute(int maxObjCountPerChunk = 1000)
{
MaxObjCountPerChunk = maxObjCountPerChunk;
}
+5 -5
View File
@@ -110,13 +110,13 @@ public class Base : DynamicBase, ISpeckleObject
continue;
}
var detachAttribute = prop.GetCustomAttribute<DetachProperty>(true);
var detachAttribute = prop.GetCustomAttribute<DetachPropertyAttribute>(true);
object value = prop.GetValue(@base);
object? value = prop.GetValue(@base);
if (detachAttribute is { Detachable: true })
{
var chunkAttribute = prop.GetCustomAttribute<Chunkable>(true);
var chunkAttribute = prop.GetCustomAttribute<ChunkableAttribute>(true);
if (chunkAttribute == null)
{
count += HandleObjectCount(value, parsed);
@@ -132,7 +132,7 @@ public class Base : DynamicBase, ISpeckleObject
}
}
var dynamicProps = @base.GetDynamicPropertyKeys();
var dynamicProps = @base.DynamicPropertyKeys;
foreach (var propName in dynamicProps)
{
if (!propName.StartsWith("@"))
@@ -144,7 +144,7 @@ public class Base : DynamicBase, ISpeckleObject
if (s_chunkSyntax.IsMatch(propName))
{
var match = s_chunkSyntax.Match(propName);
_ = int.TryParse(match.Groups[match.Groups.Count - 1].Value, out int chunkSize);
_ = int.TryParse(match.Groups[^1].Value, out int chunkSize);
if (chunkSize != -1 && @base[propName] is IList asList)
{
+1 -2
View File
@@ -1,5 +1,4 @@
#nullable disable
using System.Diagnostics.CodeAnalysis;
using System.Runtime.Serialization;
using Speckle.Newtonsoft.Json;
@@ -64,6 +63,6 @@ public class Blob : Base
public string GetLocalDestinationPath(string blobStorageFolder)
{
var fileName = Path.GetFileName(filePath);
return Path.Combine(blobStorageFolder, $"{id.Substring(0, 10)}-{fileName}");
return Path.Combine(blobStorageFolder, $"{id[..10]}-{fileName}");
}
}
@@ -1,8 +1,7 @@
#nullable disable
namespace Speckle.Sdk.Models;
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class DeprecatedSpeckleTypeAttribute(string speckleTypeName) : Attribute
public sealed class DeprecatedSpeckleTypeAttribute(string speckleTypeName) : Attribute
{
public string Name => speckleTypeName;
public string SpeckleTypeName => speckleTypeName;
}
+4 -5
View File
@@ -1,5 +1,6 @@
using System.Dynamic;
using System.Reflection;
using Speckle.Newtonsoft.Json;
using Speckle.Sdk.Host;
using Speckle.Sdk.Logging;
@@ -223,7 +224,7 @@ public class DynamicBase : DynamicObject, IDynamicMetaObjectProvider
.GetBaseProperties(GetType())
.Where(x =>
{
var hasIgnored = x.IsDefined(typeof(SchemaIgnore), true);
var hasIgnored = x.IsDefined(typeof(SchemaIgnoreAttribute), true);
var hasObsolete = x.IsDefined(typeof(ObsoleteAttribute), true);
// If obsolete is false and prop has obsolete attr
@@ -271,10 +272,8 @@ public class DynamicBase : DynamicObject, IDynamicMetaObjectProvider
/// Gets the dynamically added property names only.
/// </summary>
/// <returns></returns>
public IReadOnlyCollection<string> GetDynamicPropertyKeys()
{
return _properties.Keys;
}
[JsonIgnore]
public IReadOnlyCollection<string> DynamicPropertyKeys => _properties.Keys;
}
/// <summary>
@@ -24,7 +24,7 @@ public enum DynamicBaseMemberType
Obsolete = 4,
/// <summary>
/// The typed members flagged with <see cref="SchemaIgnore"/> attribute.
/// The typed members flagged with <see cref="SchemaIgnoreAttribute"/> attribute.
/// </summary>
SchemaIgnored = 8,
@@ -34,12 +34,12 @@ public enum DynamicBaseMemberType
SchemaComputed = 16,
/// <summary>
/// All the typed members, including ones with <see cref="ObsoleteAttribute"/> or <see cref="SchemaIgnore"/> attributes.
/// All the typed members, including ones with <see cref="ObsoleteAttribute"/> or <see cref="SchemaIgnoreAttribute"/> attributes.
/// </summary>
InstanceAll = Instance + Obsolete + SchemaIgnored,
/// <summary>
/// All the members, including dynamic and instance members flagged with <see cref="ObsoleteAttribute"/> or <see cref="SchemaIgnore"/> attributes
/// All the members, including dynamic and instance members flagged with <see cref="ObsoleteAttribute"/> or <see cref="SchemaIgnoreAttribute"/> attributes
/// </summary>
All = InstanceAll + Dynamic
}
+4 -7
View File
@@ -1,5 +1,3 @@
#nullable disable
namespace Speckle.Sdk.Models;
/// <summary>
@@ -9,15 +7,14 @@ namespace Speckle.Sdk.Models;
[SpeckleType("Speckle.Core.Models.DataChunk")]
public sealed class DataChunk : Base
{
public List<object> data { get; set; } = new();
public required List<object?> data { get; init; }
}
[SpeckleType("Speckle.Core.Models.ObjectReference")]
[DeprecatedSpeckleType("Speckle.Core.Models.ObjectReference")]
[SpeckleType("reference")]
public sealed class ObjectReference : Base
{
public new string speckle_type = "reference";
public string referencedId { get; set; }
public required string referencedId { get; init; }
public Dictionary<string, int> closure { get; set; }
}
@@ -13,6 +13,44 @@ public class GraphTraversal : GraphTraversal<TraversalContext>
{
return new TraversalContext<TraversalContext>(current, propName, parent);
}
/// <summary>
/// Traverses supported Collections yielding <see cref="Base"/> objects.
/// Does not traverse <see cref="Base"/>, only (potentially nested) collections.
/// </summary>
/// <param name="value">The value to traverse</param>
public static IEnumerable<Base> TraverseMember(object? value)
{
//TODO we should benchmark this, as yield returning like this could be suboptimal
switch (value)
{
case Base o:
yield return o;
break;
case IList list:
{
foreach (object? obj in list)
{
foreach (Base o in TraverseMember(obj))
{
yield return o;
}
}
break;
}
case IDictionary dictionary:
{
foreach (object? obj in dictionary.Values)
{
foreach (Base o in TraverseMember(obj))
{
yield return o;
}
}
break;
}
}
}
}
public abstract class GraphTraversal<T>
@@ -92,44 +130,6 @@ public abstract class GraphTraversal<T>
protected abstract T NewContext(Base current, string? propName, T? parent);
/// <summary>
/// Traverses supported Collections yielding <see cref="Base"/> objects.
/// Does not traverse <see cref="Base"/>, only (potentially nested) collections.
/// </summary>
/// <param name="value">The value to traverse</param>
public static IEnumerable<Base> TraverseMember(object? value)
{
//TODO we should benchmark this, as yield returning like this could be suboptimal
switch (value)
{
case Base o:
yield return o;
break;
case IList list:
{
foreach (object? obj in list)
{
foreach (Base o in TraverseMember(obj))
{
yield return o;
}
}
break;
}
case IDictionary dictionary:
{
foreach (object? obj in dictionary.Values)
{
foreach (Base o in TraverseMember(obj))
{
yield return o;
}
}
break;
}
}
}
private ITraversalRule GetActiveRuleOrDefault(Base o)
{
return GetActiveRule(o) ?? DefaultRule.Instance;
@@ -1,5 +1,3 @@
using Speckle.Sdk.Logging;
namespace Speckle.Sdk.Models;
public class InvalidPropNameException : SpeckleException
@@ -1,8 +1,7 @@
#nullable disable
namespace Speckle.Sdk.Models;
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class SpeckleTypeAttribute(string speckleTypeName) : Attribute
public sealed class SpeckleTypeAttribute(string speckleTypeName) : Attribute
{
public string Name => speckleTypeName;
public string SpeckleTypeName => speckleTypeName;
}
@@ -2,9 +2,11 @@
public class SpeckleDeserializeException : SpeckleException
{
public SpeckleDeserializeException(string message, Exception? inner = null)
public SpeckleDeserializeException(string? message, Exception? inner = null)
: base(message, inner) { }
public SpeckleDeserializeException(string message)
public SpeckleDeserializeException(string? message)
: base(message) { }
public SpeckleDeserializeException() { }
}
@@ -34,7 +34,7 @@ public sealed class SpeckleObjectDeserializer
public Action<ProgressArgs>? OnProgressAction { get; set; }
private long _currentCount;
private HashSet<string> _ids = new();
private readonly HashSet<string> _ids = new();
private long _processedCount;
public string? BlobStorageFolder { get; set; }
@@ -107,7 +107,7 @@ public class SpeckleObjectSerializer
// `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)
public object? SerializeProperty(
internal object? SerializeProperty(
object? obj,
JsonWriter writer,
bool computeClosures = false,
@@ -318,7 +318,7 @@ public class SpeckleObjectSerializer
private Dictionary<string, (object?, PropertyAttributeInfo)> ExtractAllProperties(Base baseObj)
{
IReadOnlyList<(PropertyInfo, PropertyAttributeInfo)> typedProperties = GetTypedPropertiesWithCache(baseObj);
IReadOnlyCollection<string> dynamicProperties = baseObj.GetDynamicPropertyKeys();
IReadOnlyCollection<string> dynamicProperties = baseObj.DynamicPropertyKeys;
// propertyName -> (originalValue, isDetachable, isChunkable, chunkSize)
Dictionary<string, (object?, PropertyAttributeInfo)> allProperties =
@@ -406,7 +406,7 @@ public class SpeckleObjectSerializer
if (baseValue is IEnumerable chunkableCollection && detachInfo.IsChunkable)
{
List<object> chunks = new();
DataChunk crtChunk = new() { data = new List<object>(detachInfo.ChunkSize) };
DataChunk crtChunk = new() { data = new List<object?>(detachInfo.ChunkSize) };
foreach (object element in chunkableCollection)
{
@@ -414,7 +414,7 @@ public class SpeckleObjectSerializer
if (crtChunk.data.Count >= detachInfo.ChunkSize)
{
chunks.Add(crtChunk);
crtChunk = new DataChunk { data = new List<object>(detachInfo.ChunkSize) };
crtChunk = new DataChunk { data = new List<object?>(detachInfo.ChunkSize) };
}
}
@@ -518,8 +518,12 @@ public class SpeckleObjectSerializer
_ = typedProperty.GetValue(baseObj);
List<DetachProperty> detachableAttributes = typedProperty.GetCustomAttributes<DetachProperty>(true).ToList();
List<Chunkable> chunkableAttributes = typedProperty.GetCustomAttributes<Chunkable>(true).ToList();
List<DetachPropertyAttribute> detachableAttributes = typedProperty
.GetCustomAttributes<DetachPropertyAttribute>(true)
.ToList();
List<ChunkableAttribute> chunkableAttributes = typedProperty
.GetCustomAttributes<ChunkableAttribute>(true)
.ToList();
bool isDetachable = detachableAttributes.Count > 0 && detachableAttributes[0].Detachable;
bool isChunkable = chunkableAttributes.Count > 0;
int chunkSize = isChunkable ? chunkableAttributes[0].MaxObjCountPerChunk : 1000;
@@ -531,7 +535,7 @@ public class SpeckleObjectSerializer
return ret;
}
public readonly struct PropertyAttributeInfo
internal readonly struct PropertyAttributeInfo
{
public PropertyAttributeInfo(
bool isDetachable,
@@ -2,9 +2,11 @@
public class SpeckleSerializeException : SpeckleException
{
public SpeckleSerializeException(string message, Exception? inner = null)
public SpeckleSerializeException(string? message, Exception? inner = null)
: base(message, inner) { }
public SpeckleSerializeException(string message)
public SpeckleSerializeException(string? message)
: base(message) { }
public SpeckleSerializeException() { }
}
@@ -1,4 +1,4 @@
using Speckle.Newtonsoft.Json;
using Speckle.Newtonsoft.Json;
using Speckle.Sdk.Common;
namespace Speckle.Sdk.Serialisation.Utilities;
@@ -1,5 +1,5 @@
using System.Buffers;
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Drawing;
using System.Globalization;
@@ -10,8 +10,13 @@ namespace Speckle.Sdk.Serialisation.Utilities;
internal static class ValueConverter
{
private static object[] _singleValue = new object[1];
private static readonly object[] s_singleValue = new object[1];
[SuppressMessage(
"Maintainability",
"CA1502:Avoid excessive complexity",
Justification = "To fix this requires rewrite of serializaiton"
)]
public static bool ConvertValue(Type type, object? value, out object? convertedValue)
{
// TODO: Document list of supported values in the SDK. (and grow it as needed)
@@ -169,9 +174,9 @@ internal static class ValueConverter
var targetType = typeof(List<>).MakeGenericType(type.GenericTypeArguments);
Type listElementType = type.GenericTypeArguments[0];
_singleValue[0] = valueList.Count;
s_singleValue[0] = valueList.Count;
//reuse array to avoid params array allocation
IList ret = (IList)Activator.CreateInstance(targetType, _singleValue);
IList ret = (IList)Activator.CreateInstance(targetType, s_singleValue);
foreach (object inputListElement in valueList)
{
+1 -2
View File
@@ -1,5 +1,4 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Speckle.Sdk.Credentials;
using Speckle.Sdk.Helpers;
using Speckle.Sdk.Host;
@@ -61,7 +60,7 @@ public static class Setup
foreach (var account in AccountManager.GetAccounts())
{
Analytics.AddConnectorToProfile(account.GetHashedEmail(), Application);
Analytics.IdentifyProfile(account.GetHashedEmail(), Application);
Analytics.IdentifyProfile(account.GetHashedEmail());
}
return LogBuilder.Initialize(
GetUserIdFromDefaultAccount(),
-2
View File
@@ -1,5 +1,3 @@
using Speckle.Sdk.Logging;
namespace Speckle.Sdk.Transports;
public class TransportException : SpeckleException
+1 -2
View File
@@ -1,5 +1,4 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Speckle.Sdk.Logging;
using Speckle.Sdk.Models;
@@ -96,7 +95,7 @@ public sealed class MemoryTransport : ITransport, ICloneable, IBlobCapableTransp
public string? GetObject(string id)
{
var stopwatch = Stopwatch.StartNew();
var ret = Objects.TryGetValue(id, out string o) ? o : null;
var ret = Objects.TryGetValue(id, out string? o) ? o : null;
stopwatch.Stop();
Elapsed += stopwatch.Elapsed;
return ret;
+1 -2
View File
@@ -1,6 +1,5 @@
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using System.Timers;
using Microsoft.Data.Sqlite;
@@ -282,7 +281,7 @@ public sealed class SQLiteTransport : IDisposable, ICloneable, ITransport, IBlob
/// <returns></returns>
public bool WriteCompletionStatus => _queue.IsEmpty && !_isWriting;
private void WriteTimerElapsed(object sender, ElapsedEventArgs e)
private void WriteTimerElapsed(object? sender, ElapsedEventArgs e)
{
_writeTimer.Enabled = false;
@@ -1,8 +1,6 @@
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;
@@ -15,7 +15,7 @@ internal class ProgressContent : HttpContent
innerContent.Headers.CopyTo(Headers);
}
protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
protected override Task SerializeToStreamAsync(Stream stream, TransportContext? context)
{
ProgressStream progressStream = new(stream, _innerContent.Headers.ContentLength, _progress, false);
return _innerContent.CopyToAsync(progressStream);
@@ -1,6 +1,7 @@
namespace Speckle.Sdk.Transports;
internal class ProgressStream(Stream input, long? streamLength, Action<ProgressArgs>? progress, bool useBuffer) : Stream
internal sealed class ProgressStream(Stream input, long? streamLength, Action<ProgressArgs>? progress, bool useBuffer)
: Stream
{
private long _position;
private readonly Stream _stream = useBuffer ? new BufferedStream(input, 80 * 1024) : input;
@@ -35,4 +36,10 @@ internal class ProgressStream(Stream input, long? streamLength, Action<ProgressA
get => _position;
set => throw new NotImplementedException();
}
protected override void Dispose(bool disposed)
{
_stream.Dispose();
base.Dispose(disposed);
}
}
@@ -235,12 +235,10 @@ public sealed class ServerApi : IDisposable, IServerApi
multipartFormDataContent.Add(fsc, $"hash:{hash}", fileName);
}
using var message = new HttpRequestMessage
{
RequestUri = new Uri($"/api/stream/{streamId}/blob", UriKind.Relative),
Method = HttpMethod.Post,
Content = new ProgressContent(multipartFormDataContent, progress)
};
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
{
@@ -268,11 +266,9 @@ public sealed class ServerApi : IDisposable, IServerApi
{
try
{
using var blobMessage = new HttpRequestMessage
{
RequestUri = new Uri($"api/stream/{streamId}/blob/{blobId}", UriKind.Relative),
Method = HttpMethod.Get
};
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<string>? cdHeaderValues);
@@ -280,12 +276,9 @@ public sealed class ServerApi : IDisposable, IServerApi
var cdHeader = cdHeaderValues.First();
var fileName = cdHeader.Split(s_filenameSeparator, StringSplitOptions.None)[1].TrimStart('"').TrimEnd('"');
string fileLocation = Path.Combine(
BlobStorageFolder,
$"{blobId.Substring(0, Blob.LocalHashPrefixLength)}-{fileName}"
);
string fileLocation = Path.Combine(BlobStorageFolder, $"{blobId[..Blob.LocalHashPrefixLength]}-{fileName}");
using var source = new ProgressStream(
await response.Content.ReadAsStreamAsync(),
await response.Content.ReadAsStreamAsync().ConfigureAwait(false),
response.Content.Headers.ContentLength,
progress,
true
+2 -2
View File
@@ -171,11 +171,11 @@ public sealed class ServerTransport : IServerTransport
.GetFiles(BlobStorageFolder)
.Select(fileName => fileName.Split(Path.DirectorySeparatorChar).Last())
.Where(fileName => fileName.Length > 10)
.Select(fileName => fileName.Substring(0, Blob.LocalHashPrefixLength))
.Select(fileName => fileName[..Blob.LocalHashPrefixLength])
.ToList();
var newBlobIds = blobIds
.Where(blobId => !localBlobTrimmedHashes.Contains(blobId.Substring(0, Blob.LocalHashPrefixLength)))
.Where(blobId => !localBlobTrimmedHashes.Contains(blobId[..Blob.LocalHashPrefixLength]))
.ToList();
await api.DownloadBlobs(StreamId, newBlobIds, OnProgressAction).ConfigureAwait(false);
+6 -6
View File
@@ -14,24 +14,24 @@ Uri modelUrl = new("https://testing1.speckle.dev/projects/cdedc63e6d/models/2d68
const string OBJECT_ID = "5cbf84a0061172102ef8a66ae914f232";
SetupSpeckle();
var testData = await GetSampleData(OBJECT_ID);
await SendToSpeckle(testData, modelUrl);
var testData = await GetSampleData(OBJECT_ID).ConfigureAwait(false);
await SendToSpeckle(testData, modelUrl).ConfigureAwait(false);
return;
static async Task SendToSpeckle(Base testData, Uri modelUrl)
{
SpeckleLog.Logger.Information("Starting Long Send Test Send");
var destinationTransport = await GetDestination(modelUrl);
var destinationTransport = await GetDestination(modelUrl).ConfigureAwait(false);
var (res, _) = await Operations.Send(testData, new[] { destinationTransport });
var (res, _) = await Operations.Send(testData, new[] { destinationTransport }).ConfigureAwait(false);
SpeckleLog.Logger.Information("Starting Send was successful: {objectId}", res);
}
static async Task<ITransport> GetDestination(Uri modelUrl)
{
StreamWrapper sw = new(modelUrl.ToString());
var acc = await sw.GetAccount();
var acc = await sw.GetAccount().ConfigureAwait(false);
return new ServerTransport(acc, sw.StreamId);
}
@@ -40,7 +40,7 @@ static async Task<Base> GetSampleData(string objectId)
SpeckleLog.Logger.Information("Gathering Sample Data Set");
using SQLiteTransport source = new(SpecklePathProvider.UserApplicationDataPath(), "longsendtest");
MemoryTransport memoryTransport = new();
return await Operations.Receive(objectId, source, memoryTransport);
return await Operations.Receive(objectId, source, memoryTransport).ConfigureAwait(false);
}
static void SetupSpeckle()
@@ -87,7 +87,11 @@ public class ModelPropertySupportedTypes
Type propType = prop.PropertyType;
Type typeDef = propType.IsGenericType ? propType.GetGenericTypeDefinition() : propType;
Assert.That(_allowedTypes, Does.Contain(typeDef), $"{typeDef} was not in allowedTypes");
Assert.That(
_allowedTypes,
Does.Contain(typeDef),
$"{typeDef} was not in allowedTypes. (Origin: {type}.{prop.Name})"
);
}
}
}
@@ -98,14 +98,6 @@ public class LegacyAPITests : IDisposable
InitServerTransport();
}
[Test, Order(10)]
public async Task StreamsGet()
{
var res = await _myClient.StreamsGet();
Assert.That(res, Is.Not.Null);
}
[Test, Order(11)]
public async Task StreamGet()
{
@@ -124,14 +116,6 @@ public class LegacyAPITests : IDisposable
Assert.That(res, Is.True);
}
[Test, Order(13)]
public async Task StreamSearch()
{
var res = await _myClient.StreamSearch(_streamId);
Assert.That(res, Is.Not.Null);
}
[Test, Order(20)]
public async Task StreamUpdate()
{
@@ -1,131 +0,0 @@
using Speckle.Sdk.Api;
using Speckle.Sdk.Credentials;
using Speckle.Sdk.Host;
using Speckle.Sdk.Models;
using Speckle.Sdk.Tests.Unit.Host;
namespace Speckle.Sdk.Tests.Integration.Api.GraphQL.Legacy.Subscriptions;
public class Branches : IDisposable
{
private BranchInfo _branchCreatedInfo;
private BranchInfo _branchDeletedInfo;
private string _branchId;
private BranchInfo _branchUpdatedInfo;
private Client _client;
private string _streamId;
private Account _testUserAccount;
[SetUp]
public void Setup()
{
TypeLoader.Reset();
TypeLoader.Initialize(typeof(Base).Assembly, typeof(Point).Assembly);
}
[OneTimeSetUp]
public async Task OneTimeSetUp()
{
_testUserAccount = await Fixtures.SeedUser();
_client = new Client(_testUserAccount);
}
[Test, Order(0)]
public async Task SubscribeBranchCreated()
{
var streamInput = new StreamCreateInput { description = "Hello World", name = "Super Stream 01" };
_streamId = await _client.StreamCreate(streamInput);
Assert.That(_streamId, Is.Not.Null);
_client.SubscribeBranchCreated(_streamId);
_client.OnBranchCreated += Client_OnBranchCreated;
Thread.Sleep(5000); //let server catch-up
var branchInput = new BranchCreateInput
{
description = "Just testing branch create...",
name = "awesome-features",
streamId = _streamId
};
_branchId = await _client.BranchCreate(branchInput);
Assert.That(_branchId, Is.Not.Null);
await Task.Run(() =>
{
Thread.Sleep(1000); //let client catch-up
Assert.That(_branchCreatedInfo, Is.Not.Null);
Assert.That(_branchCreatedInfo.name, Is.EqualTo(branchInput.name));
});
}
private void Client_OnBranchCreated(object sender, BranchInfo e)
{
_branchCreatedInfo = e;
}
[Test, Order(1)]
public async Task SubscribeBranchUpdated()
{
_client.SubscribeBranchUpdated(_streamId);
_client.OnBranchUpdated += Client_OnBranchUpdated;
Thread.Sleep(1000); //let server catch-up
var branchInput = new BranchUpdateInput
{
description = "Just testing branch bpdate...",
name = "cool-features",
streamId = _streamId,
id = _branchId
};
var res = await _client.BranchUpdate(branchInput);
Assert.That(res, Is.True);
await Task.Run(() =>
{
Thread.Sleep(1000); //let client catch-up
Assert.That(_branchUpdatedInfo, Is.Not.Null);
Assert.That(_branchUpdatedInfo.name, Is.EqualTo(branchInput.name));
});
}
private void Client_OnBranchUpdated(object sender, BranchInfo e)
{
_branchUpdatedInfo = e;
}
[Test, Order(3)]
public async Task SubscribeBranchDeleted()
{
_client.SubscribeBranchDeleted(_streamId);
_client.OnBranchDeleted += Client_OnBranchDeleted;
Thread.Sleep(1000); //let server catch-up
var branchInput = new BranchDeleteInput { streamId = _streamId, id = _branchId };
var res = await _client.BranchDelete(branchInput);
Assert.That(res, Is.True);
await Task.Run(() =>
{
Thread.Sleep(1000); //let client catch-up
Assert.That(_branchDeletedInfo, Is.Not.Null);
Assert.That(_branchDeletedInfo.id, Is.EqualTo(_branchId));
});
}
private void Client_OnBranchDeleted(object sender, BranchInfo e)
{
_branchDeletedInfo = e;
}
public void Dispose()
{
_client?.Dispose();
}
}
@@ -1,168 +0,0 @@
using Speckle.Sdk.Api;
using Speckle.Sdk.Credentials;
using Speckle.Sdk.Host;
using Speckle.Sdk.Models;
using Speckle.Sdk.Tests.Unit.Host;
using Speckle.Sdk.Transports;
namespace Speckle.Sdk.Tests.Integration.Api.GraphQL.Legacy.Subscriptions;
public class Commits : IDisposable
{
private Client _client;
private CommitInfo _commitCreatedInfo;
private CommitInfo _commitDeletedInfo;
private string _commitId;
private CommitInfo _commitUpdatedInfo;
private ServerTransport _myServerTransport;
private string _streamId;
private Account _testUserAccount;
[SetUp]
public void Setup()
{
TypeLoader.Reset();
TypeLoader.Initialize(typeof(Base).Assembly, typeof(Point).Assembly);
}
[OneTimeSetUp]
public async Task OneTimeSetUp()
{
_testUserAccount = await Fixtures.SeedUser();
_client = new Client(_testUserAccount);
}
private void InitServerTransport()
{
_myServerTransport = new ServerTransport(_testUserAccount, _streamId);
_myServerTransport.Api.CompressPayloads = false;
}
[Test, Order(0)]
//[Ignore("Ironically, it fails.")]
public async Task SubscribeCommitCreated()
{
var streamInput = new StreamCreateInput { description = "Hello World", name = "Super Stream 01" };
_streamId = await _client.StreamCreate(streamInput);
Assert.That(_streamId, Is.Not.Null);
InitServerTransport();
var branchInput = new BranchCreateInput
{
description = "Just testing branch create...",
name = "awesome-features",
streamId = _streamId
};
var branchId = await _client.BranchCreate(branchInput);
Assert.That(branchId, Is.Not.Null);
_client.SubscribeCommitCreated(_streamId);
_client.OnCommitCreated += Client_OnCommitCreated;
Thread.Sleep(1000); //let server catch-up
var myObject = new Base();
var ptsList = new List<Point>();
for (int i = 0; i < 100; i++)
{
ptsList.Add(new Point(i, i, i));
}
myObject["Points"] = ptsList;
var sendResult = await Operations.Send(myObject, _myServerTransport, false);
var commitInput = new CommitCreateInput
{
streamId = _streamId,
branchName = "awesome-features",
objectId = sendResult.rootObjId,
message = "sending some test points",
sourceApplication = "Tests",
totalChildrenCount = 20
};
_commitId = await _client.CommitCreate(commitInput);
Assert.That(_commitId, Is.Not.Null);
await Task.Run(() =>
{
Thread.Sleep(2000); //let client catch-up
Assert.That(_commitCreatedInfo, Is.Not.Null);
Assert.That(_commitCreatedInfo.message, Is.EqualTo(commitInput.message));
});
}
private void Client_OnCommitCreated(object sender, CommitInfo e)
{
_commitCreatedInfo = e;
}
[Test, Order(1)]
//[Ignore("Ironically, it fails.")]
public async Task SubscribeCommitUpdated()
{
_client.SubscribeCommitUpdated(_streamId);
_client.OnCommitUpdated += Client_OnCommitUpdated;
Thread.Sleep(1000); //let server catch-up
var commitInput = new CommitUpdateInput
{
message = "Just testing commit update...",
streamId = _streamId,
id = _commitId
};
var res = await _client.CommitUpdate(commitInput);
Assert.That(res, Is.True);
await Task.Run(() =>
{
Thread.Sleep(2000); //let client catch-up
Assert.That(_commitUpdatedInfo, Is.Not.Null);
Assert.That(_commitUpdatedInfo.message, Is.EqualTo(commitInput.message));
});
}
private void Client_OnCommitUpdated(object sender, CommitInfo e)
{
_commitUpdatedInfo = e;
}
[Test, Order(3)]
//[Ignore("Ironically, it fails.")]
public async Task SubscribeCommitDeleted()
{
_client.SubscribeCommitDeleted(_streamId);
_client.OnCommitDeleted += Client_OnCommitDeleted;
Thread.Sleep(1000); //let server catch-up
var commitInput = new CommitDeleteInput { streamId = _streamId, id = _commitId };
var res = await _client.CommitDelete(commitInput);
Assert.That(res, Is.True);
await Task.Run(() =>
{
Thread.Sleep(2000); //let client catch-up
Assert.That(_commitDeletedInfo, Is.Not.Null);
Assert.That(_commitDeletedInfo.id, Is.EqualTo(_commitId));
});
}
private void Client_OnCommitDeleted(object sender, CommitInfo e)
{
_commitDeletedInfo = e;
}
public void Dispose()
{
_client?.Dispose();
_myServerTransport?.Dispose();
}
}
@@ -1,122 +0,0 @@
using Speckle.Sdk.Api;
using Speckle.Sdk.Credentials;
using Speckle.Sdk.Host;
using Speckle.Sdk.Models;
using Speckle.Sdk.Tests.Unit.Host;
namespace Speckle.Sdk.Tests.Integration.Api.GraphQL.Legacy.Subscriptions;
public class Streams : IDisposable
{
private Client _client;
private StreamInfo _streamAddedInfo;
private string _streamId;
private StreamInfo _streamRemovedInfo;
private StreamInfo _streamUpdatedInfo;
private Account _testUserAccount;
[SetUp]
public void Setup()
{
TypeLoader.Reset();
TypeLoader.Initialize(typeof(Base).Assembly, typeof(Point).Assembly);
}
[OneTimeSetUp]
public async Task OneTimeSetUp()
{
_testUserAccount = await Fixtures.SeedUser();
_client = new Client(_testUserAccount);
}
[Test, Order(0)]
public async Task SubscribeStreamAdded()
{
_client.SubscribeUserStreamAdded();
_client.OnUserStreamAdded += Client_OnUserStreamAdded;
Thread.Sleep(1000); //let server catch-up
var streamInput = new StreamCreateInput { description = "Hello World", name = "Super Stream 01" };
var res = await _client.StreamCreate(streamInput);
_streamId = res;
Assert.That(res, Is.Not.Null);
await Task.Run(() =>
{
Thread.Sleep(1000); //let client catch-up
Assert.That(_streamAddedInfo, Is.Not.Null);
Assert.That(_streamAddedInfo.name, Is.EqualTo(streamInput.name));
});
}
private void Client_OnUserStreamAdded(object sender, StreamInfo e)
{
_streamAddedInfo = e;
}
[Test, Order(1)]
public async Task SubscribeStreamUpdated()
{
_client.SubscribeStreamUpdated(_streamId);
_client.OnStreamUpdated += Client_OnStreamUpdated;
Thread.Sleep(100); //let server catch-up
var streamInput = new StreamUpdateInput
{
id = _streamId,
description = "Hello World",
name = "Super Stream 01 EDITED"
};
var res = await _client.StreamUpdate(streamInput);
Assert.That(res, Is.True);
await Task.Run(() =>
{
Thread.Sleep(100); //let client catch-up
Assert.That(_streamUpdatedInfo, Is.Not.Null);
Assert.That(_streamUpdatedInfo.name, Is.EqualTo(streamInput.name));
});
}
private void Client_OnStreamUpdated(object sender, StreamInfo e)
{
_streamUpdatedInfo = e;
}
[Test, Order(2)]
public async Task SubscribeUserStreamRemoved()
{
_client.SubscribeUserStreamRemoved();
_client.OnUserStreamRemoved += Client_OnStreamRemoved;
;
Thread.Sleep(100); //let server catch-up
var res = await _client.StreamDelete(_streamId);
Assert.That(res, Is.True);
await Task.Run(() =>
{
Thread.Sleep(100); //let client catch-up
Assert.That(_streamRemovedInfo, Is.Not.Null);
Assert.That(_streamRemovedInfo.id, Is.EqualTo(_streamId));
});
}
private void Client_OnStreamRemoved(object sender, StreamInfo e)
{
_streamRemovedInfo = e;
}
public void Dispose()
{
_client?.Dispose();
}
}
@@ -41,6 +41,6 @@ public class GeneralDeserializer : IDisposable
public void Dispose()
{
// _dataSource.Dispose();
_dataSource.Dispose();
}
}
@@ -5,6 +5,7 @@
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
@@ -9,7 +9,6 @@ namespace Speckle.Sdk.Tests.Performance;
public sealed class TestDataHelper : IDisposable
{
private static readonly string s_basePath = $"./temp {Guid.NewGuid()}";
private const string APPLICATION_NAME = "Speckle Performance Tests";
public SQLiteTransport Transport { get; private set; }
public string ObjectId { get; private set; }
@@ -110,19 +110,19 @@ public class ObjectSerialization
[Test]
public async Task ChunkSerialisation()
{
var baseBasedChunk = new DataChunk();
var baseBasedChunk = new DataChunk() { data = new() };
for (var i = 0; i < 200; i++)
{
baseBasedChunk.data.Add(new SuperPoint { W = i });
}
var stringBasedChunk = new DataChunk();
var stringBasedChunk = new DataChunk() { data = new() };
for (var i = 0; i < 200; i++)
{
stringBasedChunk.data.Add(i + "_hai");
}
var doubleBasedChunk = new DataChunk();
var doubleBasedChunk = new DataChunk() { data = new() };
for (var i = 0; i < 200; i++)
{
doubleBasedChunk.data.Add(i + 0.33);
@@ -6,13 +6,13 @@ namespace Speckle.Sdk.Tests.Unit.Host;
public class HostApplicationTests
{
private static List<HostAppVersion> _hostAppVersion = Enum.GetValues<HostAppVersion>().ToList();
private static List<HostAppVersion> s_hostAppVersion = Enum.GetValues<HostAppVersion>().ToList();
[Test]
[TestCaseSource("_hostAppVersion")]
[TestCaseSource(nameof(s_hostAppVersion))]
public void HostAppVersionParsingTests(HostAppVersion appVersion)
{
appVersion.ToString().StartsWith("v").ShouldBeTrue();
appVersion.ToString().StartsWith('v').ShouldBeTrue();
var version = HostApplications.GetVersion(appVersion);
int.Parse(version).ShouldBePositive();
}