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:
@@ -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
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
CA1502: 25
|
||||
CA1501: 5
|
||||
CA1506(Method): 50
|
||||
CA1506(Type): 95
|
||||
+38
-11
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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()}.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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,5 +1,4 @@
|
||||
using GraphQL;
|
||||
using Speckle.Sdk.Logging;
|
||||
|
||||
namespace Speckle.Sdk.Api;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
+2
-1
@@ -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,
|
||||
|
||||
+2
-3
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+16
-183
@@ -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>
|
||||
|
||||
+7
-76
@@ -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();
|
||||
}
|
||||
|
||||
+16
-76
@@ -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();
|
||||
}
|
||||
|
||||
+12
-98
@@ -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})";
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
using Speckle.Sdk.Logging;
|
||||
|
||||
namespace Speckle.Sdk.Common;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -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("/", ""));
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,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(),
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
using Speckle.Sdk.Logging;
|
||||
|
||||
namespace Speckle.Sdk.Transports;
|
||||
|
||||
public class TransportException : SpeckleException
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user