Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 739befc3fa | |||
| 5ed6ab2f69 | |||
| 470b3d93de | |||
| b9059e1b85 | |||
| 3694e5703c | |||
| 80189e2414 | |||
| 213221e7d5 | |||
| f2cb5c30be | |||
| 4ec42cde84 | |||
| 86e4759531 |
@@ -1,16 +1,15 @@
|
||||
using Objects;
|
||||
using Speckle.Automate.Sdk;
|
||||
using Speckle.Automate.Sdk.Schema;
|
||||
using Speckle.Core.Api;
|
||||
using Speckle.Core.Models;
|
||||
using Speckle.Core.Models.Extensions;
|
||||
using Speckle.Core.Transports;
|
||||
using SpeckleAutomateDotnetExample;
|
||||
|
||||
static class AutomateFunction
|
||||
{
|
||||
public static string ADDED = "Added";
|
||||
public static string MODIFIED = "Modified";
|
||||
public static string DELETED = "Deleted";
|
||||
public static string ADDED = "ADDED";
|
||||
public static string MODIFIED = "MODIFIED";
|
||||
public static string DELETED = "DELETED";
|
||||
public static string UNCHANGED = "UNCHANGED";
|
||||
|
||||
public static async Task Run(
|
||||
AutomationContext automationContext,
|
||||
@@ -20,9 +19,12 @@ static class AutomateFunction
|
||||
Console.WriteLine("Starting execution");
|
||||
_ = typeof(ObjectsKit).Assembly; // INFO: Force objects kit to initialize
|
||||
|
||||
// get the test and release branch name
|
||||
var testBranchName = automationContext.AutomationRunData.BranchName;
|
||||
var releaseBranchName = testBranchName.Replace("/testing", "/release");
|
||||
double tolerance = functionInputs.Tolerance;
|
||||
|
||||
// get the testing and release branches
|
||||
string testBranchName = automationContext.AutomationRunData.BranchName;
|
||||
string releaseBranchName = testBranchName.Replace("/testing", "/release");
|
||||
Console.WriteLine($"Comparing {testBranchName} against {releaseBranchName}");
|
||||
Branch? releaseBranch = await automationContext.SpeckleClient
|
||||
.BranchGet(automationContext.AutomationRunData.ProjectId, releaseBranchName, 1)
|
||||
.ConfigureAwait(false);
|
||||
@@ -31,165 +33,240 @@ static class AutomateFunction
|
||||
throw new Exception("Release branch was null");
|
||||
}
|
||||
|
||||
// get the release branch latest commit
|
||||
Commit releaseCommit = releaseBranch.commits.items.First();
|
||||
if (releaseCommit is null)
|
||||
{
|
||||
throw new Exception("Release branch has no commits");
|
||||
}
|
||||
|
||||
var tolerance = functionInputs.Tolerance;
|
||||
|
||||
Console.WriteLine($"Comparing {testBranchName} against {releaseBranchName}");
|
||||
|
||||
// get the test and release commits
|
||||
// get the test and release commit base
|
||||
Console.WriteLine("Receiving test version");
|
||||
Base? testingCommitObject = await automationContext.ReceiveVersion();
|
||||
Base testingCommitObject = await automationContext.ReceiveVersion();
|
||||
Console.WriteLine("Received test version: " + testingCommitObject);
|
||||
Console.WriteLine("Receiving release version");
|
||||
using ServerTransport transport =
|
||||
new(
|
||||
automationContext.SpeckleClient.Account,
|
||||
automationContext.AutomationRunData.ProjectId
|
||||
);
|
||||
Base? releaseCommitObject = await Operations
|
||||
.Receive(releaseCommit.referencedObject, transport)
|
||||
.ConfigureAwait(continueOnCapturedContext: false);
|
||||
if (releaseCommitObject == null)
|
||||
{
|
||||
throw new Exception("Commit root object was null");
|
||||
}
|
||||
|
||||
Base releaseCommitObject = await Utils.RecieveVersionAsync(
|
||||
releaseCommit.id,
|
||||
automationContext
|
||||
);
|
||||
Console.WriteLine("Received release version: " + releaseCommitObject);
|
||||
|
||||
// flatten both commits
|
||||
IEnumerable<Base> releaseCommitObjects = releaseCommitObject.Flatten();
|
||||
IEnumerable<Base> testCommitObjects = testingCommitObject.Flatten();
|
||||
var releaseCommitObjectsDict = new Dictionary<string, Base>();
|
||||
foreach (var releaseObject in releaseCommitObjects)
|
||||
// Create dictionaries by appId (or speckle id if no appId exists) for the release and testing commits
|
||||
// note that it is possible for multiple objects to have the same app id
|
||||
// (eg mapped schema objects have the same id as their parent geometry)
|
||||
// and it is also possible for multiple objects (with no app id) to have the same speckle id
|
||||
// (eg the same mesh sent from gh multiple times)
|
||||
Dictionary<string, List<Base>> releaseCommitAppIdDict = new();
|
||||
Dictionary<string, List<Base>> releaseCommitSpeckleIdDict = new();
|
||||
Dictionary<string, List<Base>> testingCommitAppIdDict = new();
|
||||
Dictionary<string, List<Base>> testingCommitSpeckleIdDict = new();
|
||||
Utils.CreateDictionaryFromBaseById(
|
||||
releaseCommitObject,
|
||||
out releaseCommitAppIdDict,
|
||||
out releaseCommitSpeckleIdDict,
|
||||
out int releaseObjectCount
|
||||
);
|
||||
Console.WriteLine(
|
||||
$"Found {releaseObjectCount} objects in RELEASE with {releaseCommitAppIdDict.Count} unique applicationIds and {releaseCommitSpeckleIdDict} unique speckle ids (for objects with no application id)."
|
||||
);
|
||||
Utils.CreateDictionaryFromBaseById(
|
||||
testingCommitObject,
|
||||
out testingCommitAppIdDict,
|
||||
out testingCommitSpeckleIdDict,
|
||||
out int testingObjectCount
|
||||
);
|
||||
Console.WriteLine(
|
||||
$"Found {testingObjectCount} objects in TESTING with {testingCommitAppIdDict.Count} unique applicationIds and {testingCommitSpeckleIdDict} unique speckle ids (for objects with no application id)."
|
||||
);
|
||||
|
||||
// COMPARE COMMIT OBJECTS WITH APPLICATION IDS
|
||||
// and store in hash lists where each object is (id, appId, type), and for modified, and additional string of property changes.
|
||||
HashSet<Tuple<string, string?, string>> deletedAppIdObjects = new();
|
||||
HashSet<Tuple<string, string?, string>> unchangedAppIdObjects = new();
|
||||
HashSet<Tuple<string, string?, string>> addedAppIdObjects = new();
|
||||
HashSet<Tuple<string, string?, string, string>> modifiedAppIdObjects = new();
|
||||
|
||||
// first find deleted objects in the testing commit and remove their keys from the release commit dict
|
||||
foreach (string releaseAppId in releaseCommitAppIdDict.Keys)
|
||||
{
|
||||
if (!releaseCommitObjectsDict.ContainsKey(releaseObject.applicationId))
|
||||
if (!testingCommitAppIdDict.ContainsKey(releaseAppId))
|
||||
{
|
||||
releaseCommitObjectsDict.Add(releaseObject.applicationId, releaseObject);
|
||||
releaseCommitAppIdDict[releaseAppId].ForEach(
|
||||
o =>
|
||||
deletedAppIdObjects.Add(
|
||||
new Tuple<string, string?, string>(o.id, releaseAppId, o.speckle_type)
|
||||
)
|
||||
);
|
||||
releaseCommitAppIdDict.Remove(releaseAppId);
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine(
|
||||
$"Found {releaseCommitObjects.Count()} objects in release version"
|
||||
);
|
||||
Console.WriteLine($"Found {testCommitObjects.Count()} objects in release version");
|
||||
|
||||
// compare objects
|
||||
int addedCount = 0;
|
||||
int modifiedCount = 0;
|
||||
int unchangedCount = 0;
|
||||
foreach (Base testObject in testCommitObjects)
|
||||
// then find unchanged, added, and modified objects by iterating through testing commit app ids
|
||||
foreach (string testingAppId in testingCommitAppIdDict.Keys)
|
||||
{
|
||||
if (releaseCommitObjectsDict.ContainsKey(testObject.applicationId))
|
||||
List<Base> testObjects = testingCommitAppIdDict[testingAppId];
|
||||
|
||||
// test for added objects
|
||||
if (!releaseCommitAppIdDict.ContainsKey(testingAppId))
|
||||
{
|
||||
Base releaseObject = releaseCommitObjectsDict[testObject.applicationId];
|
||||
|
||||
// if these have the same hash, no properties have changed
|
||||
if (testObject.id == releaseObject.id)
|
||||
{
|
||||
unchangedCount++;
|
||||
}
|
||||
// if ids are different, find property differences
|
||||
else
|
||||
{
|
||||
modifiedCount++;
|
||||
var diffDictionary = new Dictionary<string, string>();
|
||||
Dictionary<string, object> releaseObjectPropDict = releaseObject.GetMembers();
|
||||
Dictionary<string, object> testObjectPropDict = testObject.GetMembers();
|
||||
foreach (var entry in testObjectPropDict)
|
||||
{
|
||||
if (releaseObjectPropDict.ContainsKey(entry.Key))
|
||||
{
|
||||
bool changed = false;
|
||||
try
|
||||
{
|
||||
changed = entry.Value != releaseObjectPropDict[entry.Key];
|
||||
}
|
||||
catch { }
|
||||
if (changed)
|
||||
{
|
||||
string diff =
|
||||
$"Property ({entry.Key}) changed from ({releaseObjectPropDict[entry.Key]}) to ({entry.Value})";
|
||||
if (!diffDictionary.ContainsKey(entry.Key))
|
||||
{
|
||||
diffDictionary.Add(entry.Key, diff);
|
||||
}
|
||||
}
|
||||
|
||||
releaseObjectPropDict.Remove(entry.Key);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!diffDictionary.ContainsKey(entry.Key))
|
||||
{
|
||||
diffDictionary.Add(entry.Key, ADDED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if there are any props left on the release object - these were missing in the test object
|
||||
foreach (var entry in releaseObjectPropDict)
|
||||
{
|
||||
if (!diffDictionary.ContainsKey(entry.Key))
|
||||
{
|
||||
diffDictionary.Add(entry.Key, DELETED);
|
||||
}
|
||||
}
|
||||
|
||||
// add the diff dict info to the object
|
||||
if (diffDictionary.Count > 0)
|
||||
{
|
||||
var sb = new System.Text.StringBuilder();
|
||||
foreach (var entry in diffDictionary)
|
||||
{
|
||||
sb.AppendLine($"{entry.Key}: {entry.Value}");
|
||||
}
|
||||
|
||||
automationContext.AttachWarningToObjects(
|
||||
MODIFIED,
|
||||
new List<string>() { testObject.id },
|
||||
sb.ToString()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
releaseCommitObjectsDict.Remove(testObject.applicationId);
|
||||
testObjects.ForEach(
|
||||
o =>
|
||||
addedAppIdObjects.Add(
|
||||
new Tuple<string, string?, string>(o.id, testingAppId, o.speckle_type)
|
||||
)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
addedCount++;
|
||||
automationContext.AttachInfoToObjects(
|
||||
ADDED,
|
||||
new List<string>() { testObject.id }
|
||||
);
|
||||
List<Base> releaseObjects = releaseCommitAppIdDict[testingAppId];
|
||||
|
||||
// test for unchanged objects
|
||||
// by filtering the testing and release objects with matching speckle ids
|
||||
Utils
|
||||
.FilterListsBySpeckleIdMatch(testObjects, releaseObjects)
|
||||
.ForEach(o => unchangedAppIdObjects.Add(o));
|
||||
|
||||
// for remaining objects, determine deleted objects
|
||||
// and then compare them in order (assume modified) and handle leftovers (added)
|
||||
// this is imperfect, as there's a chance we are not comparing the correct objects.
|
||||
if (releaseObjects.Count > testObjects.Count)
|
||||
{
|
||||
for (int i = releaseObjects.Count - 1; i >= testObjects.Count; i--)
|
||||
{
|
||||
deletedAppIdObjects.Add(
|
||||
new Tuple<string, string?, string>(
|
||||
releaseObjects[i].id,
|
||||
testingAppId,
|
||||
releaseObjects[i].speckle_type
|
||||
)
|
||||
);
|
||||
releaseObjects.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < testObjects.Count; i++)
|
||||
{
|
||||
Base testObject = testObjects[i];
|
||||
|
||||
if (i < releaseObjectCount)
|
||||
{
|
||||
Base releaseObject = releaseObjects[i];
|
||||
|
||||
// compare object properties to determine changes
|
||||
List<string> addedProps = new();
|
||||
List<string> deletedProps = new();
|
||||
List<Tuple<string, string>> modifiedProps = new();
|
||||
Utils.CompareBaseProperties(
|
||||
testObject,
|
||||
releaseObject,
|
||||
out addedProps,
|
||||
out deletedProps,
|
||||
out modifiedProps
|
||||
);
|
||||
var sb = new System.Text.StringBuilder();
|
||||
addedProps.ForEach(s => sb.AppendLine($"{ADDED} prop ({s})."));
|
||||
deletedProps.ForEach(s => sb.AppendLine($"{DELETED} prop ({s})."));
|
||||
modifiedProps.ForEach(
|
||||
t => sb.AppendLine($"{MODIFIED} ({t.Item2}) of prop ({t.Item1})")
|
||||
);
|
||||
modifiedAppIdObjects.Add(
|
||||
new Tuple<string, string?, string, string>(
|
||||
testObject.id,
|
||||
testObject.applicationId,
|
||||
testObject.speckle_type,
|
||||
sb.ToString()
|
||||
)
|
||||
);
|
||||
}
|
||||
// remaining test objects are considered added
|
||||
else
|
||||
{
|
||||
addedAppIdObjects.Add(
|
||||
new Tuple<string, string?, string>(
|
||||
testObject.id,
|
||||
testingAppId,
|
||||
testObject.speckle_type
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if there are any remaining release commit objects, this indicates missing objects in the test run.
|
||||
// mark run as failed and list missing object details.
|
||||
if (releaseCommitObjectsDict.Keys.Count > 0)
|
||||
// COMPARE COMMIT OBJECTS WITHOUT APPLICATION IDS USING SPECKLE IDS
|
||||
// since we only have 1 parameter of comparison, we can rule out any matching speckle ids as unchanged.
|
||||
// for all other objects, mark as modified, and indicate quantity of any added or deleted objects
|
||||
// store modified in hash lists of (id, type)
|
||||
HashSet<Tuple<string, string?, string>> unchangedSpeckleIdObjects = new();
|
||||
HashSet<Tuple<string, string>> changedSpeckleIdObjects = new();
|
||||
|
||||
// first filter out matching speckle ids
|
||||
List<Base> flattenedTestingSpeckleIdDict = testingCommitSpeckleIdDict.Values
|
||||
.SelectMany(o => o)
|
||||
.ToList();
|
||||
List<Base> flattenedReleaseSpeckleIdDict = releaseCommitSpeckleIdDict.Values
|
||||
.SelectMany(o => o)
|
||||
.ToList();
|
||||
Utils
|
||||
.FilterListsBySpeckleIdMatch(
|
||||
flattenedTestingSpeckleIdDict,
|
||||
flattenedReleaseSpeckleIdDict
|
||||
)
|
||||
.ForEach(o => unchangedSpeckleIdObjects.Add(o));
|
||||
|
||||
// then store all remaining testing objects as changed
|
||||
flattenedTestingSpeckleIdDict.ForEach(
|
||||
o => changedSpeckleIdObjects.Add(new Tuple<string, string>(o.id, o.speckle_type))
|
||||
);
|
||||
|
||||
// calculate count difference
|
||||
int speckleIdObjectCountDifference =
|
||||
flattenedTestingSpeckleIdDict.Count - flattenedReleaseSpeckleIdDict.Count;
|
||||
|
||||
// REPORT ALL DIFF RESULTS FOR APP IDS AND SPECKLE IDS
|
||||
// mark run succeeded if there are no added, modified, or deleted app id objects, and no changed speckle id objects
|
||||
// mark run failed otherwise
|
||||
if (
|
||||
addedAppIdObjects.Count
|
||||
+ deletedAppIdObjects.Count
|
||||
+ modifiedAppIdObjects.Count
|
||||
+ changedSpeckleIdObjects.Count
|
||||
== 0
|
||||
)
|
||||
{
|
||||
automationContext.MarkRunFailed(
|
||||
$"Missing {releaseCommitObjectsDict.Keys.Count} objects compared to the release commit."
|
||||
);
|
||||
|
||||
foreach (var missingObject in releaseCommitObjectsDict)
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"Missing object info: id( {missingObject.Value.id} ), applicationId( {missingObject.Key} ), type( {missingObject.Value.speckle_type} )"
|
||||
);
|
||||
}
|
||||
automationContext.MarkRunSuccess($"Run passed with no changes to objects.");
|
||||
}
|
||||
// mark run as succeeded, noting any changed objects and added objects
|
||||
else
|
||||
{
|
||||
automationContext.MarkRunSuccess(
|
||||
$"Run passed with {addedCount} new objects, {modifiedCount} objects, and {unchangedCount} unchanged objects."
|
||||
automationContext.MarkRunFailed(
|
||||
$"Run failed due to: {addedAppIdObjects.Count} {ADDED}, {modifiedAppIdObjects.Count} {MODIFIED}, and {deletedAppIdObjects.Count} {DELETED} objects WITH APP IDS, and {(speckleIdObjectCountDifference > 0 ? $"{speckleIdObjectCountDifference} {ADDED}" : $"{Math.Abs(speckleIdObjectCountDifference)} {DELETED}")} and {changedSpeckleIdObjects.Count} CHANGED objects WITHOUT APP IDS compared to the release commit. "
|
||||
);
|
||||
|
||||
foreach (var added in addedAppIdObjects)
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"{ADDED} {added.Item3} object: id( {added.Item1} ), appId: {added.Item2}"
|
||||
);
|
||||
}
|
||||
|
||||
foreach (var deleted in deletedAppIdObjects)
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"{DELETED} {deleted.Item3} object: id( {deleted.Item1} ), appId: {deleted.Item2}"
|
||||
);
|
||||
}
|
||||
|
||||
foreach (var modified in modifiedAppIdObjects)
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"{MODIFIED} {modified.Item3} object: id( {modified.Item1} ), appId: {modified.Item2}. Changed props: {modified.Item4}"
|
||||
);
|
||||
}
|
||||
|
||||
foreach (var changed in changedSpeckleIdObjects)
|
||||
{
|
||||
Console.WriteLine($"CHANGED {changed.Item2} object: id( {changed.Item1} )");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ using System.ComponentModel.DataAnnotations;
|
||||
/// are valid and match the required schema.
|
||||
struct FunctionInputs
|
||||
{
|
||||
[Required]
|
||||
//[Required]
|
||||
public double Tolerance;
|
||||
|
||||
//public string Exclusions;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Speckle.Automate.Sdk" Version="2.17.0-automate3" />
|
||||
<PackageReference Include="Speckle.Objects" Version="2.17.0-automate3" />
|
||||
<PackageReference Include="Speckle.Automate.Sdk" Version="2.18.0-fileInput" />
|
||||
<PackageReference Include="Speckle.Objects" Version="2.18.0-fileInput" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
using Speckle.Automate.Sdk;
|
||||
using Speckle.Automate.Sdk.Schema;
|
||||
using Speckle.Core.Api;
|
||||
using Speckle.Core.Models;
|
||||
using Speckle.Core.Models.Extensions;
|
||||
using Speckle.Core.Transports;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SpeckleAutomateDotnetExample
|
||||
{
|
||||
internal class Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Receives a commit object from the same project and account as the <paramref name="context"/>
|
||||
/// </summary>
|
||||
/// <param name="commitId"> The id of the commit to receive</param>
|
||||
/// <param name="context">The Automation context</param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public static async Task<Base> RecieveVersionAsync(
|
||||
string commitId,
|
||||
AutomationContext context
|
||||
)
|
||||
{
|
||||
ServerTransport serverTransport = new ServerTransport(
|
||||
context.SpeckleClient.Account,
|
||||
context.AutomationRunData.ProjectId
|
||||
);
|
||||
|
||||
Base? receivedCommitObject = await Operations
|
||||
.Receive(
|
||||
(
|
||||
await context.SpeckleClient
|
||||
.CommitGet(context.AutomationRunData.ProjectId, commitId)
|
||||
.ConfigureAwait(continueOnCapturedContext: false)
|
||||
).referencedObject,
|
||||
serverTransport,
|
||||
new MemoryTransport()
|
||||
)
|
||||
.ConfigureAwait(continueOnCapturedContext: false);
|
||||
|
||||
if (receivedCommitObject == null)
|
||||
{
|
||||
throw new Exception("Commit root object was null");
|
||||
}
|
||||
|
||||
return receivedCommitObject;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates Dictionaries from a commit Base, using applicationId if available or speckle id if no applicationId exists.
|
||||
/// </summary>
|
||||
/// <param name="commit">The commit Base to create dictionaries from</param>
|
||||
/// <param name="appIdDict">The dictionary of commit objects sorted by applicationId</param>
|
||||
/// <param name="speckleIdDict">The dictionary of commit objects without applicationIds, sorted by speckle id</param>
|
||||
public static void CreateDictionaryFromBaseById(
|
||||
Base commit,
|
||||
out Dictionary<string, List<Base>> appIdDict,
|
||||
out Dictionary<string, List<Base>> speckleIdDict,
|
||||
out int objectCount
|
||||
)
|
||||
{
|
||||
IEnumerable<Base> commitObjects = commit.Flatten();
|
||||
objectCount = commitObjects.Count();
|
||||
|
||||
appIdDict = new Dictionary<string, List<Base>>();
|
||||
speckleIdDict = new Dictionary<string, List<Base>>();
|
||||
foreach (var commitObject in commitObjects)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(commitObject.applicationId))
|
||||
{
|
||||
if (appIdDict.ContainsKey(commitObject.applicationId))
|
||||
{
|
||||
appIdDict[commitObject.applicationId].Add(commitObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
appIdDict.Add(
|
||||
commitObject.applicationId,
|
||||
new List<Base>() { commitObject }
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (speckleIdDict.ContainsKey(commitObject.id))
|
||||
{
|
||||
speckleIdDict[commitObject.id].Add(commitObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
speckleIdDict.Add(commitObject.id, new List<Base>() { commitObject });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Filters two lists by removing all objects from <paramref name="setA"/> with a matching speckle id in <paramref name="setB"/>
|
||||
/// </summary>
|
||||
/// <param name="setA">The list to filter</param>
|
||||
/// <param name="setB">The list to filter against</param>
|
||||
/// <returns>A list of matches found in <paramref name="setA"/></returns>
|
||||
public static List<Tuple<string, string?, string>> FilterListsBySpeckleIdMatch(
|
||||
List<Base> setA,
|
||||
List<Base> setB
|
||||
)
|
||||
{
|
||||
var matches = new List<Tuple<string, string?, string>>();
|
||||
|
||||
for (int i = setA.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var testObject = setA[i];
|
||||
for (int j = setB.Count - 1; j >= 0; j--)
|
||||
{
|
||||
var releaseObject = setB[j];
|
||||
|
||||
// if a match was found, remove from both lists and add to matches
|
||||
if (testObject.id == releaseObject.id)
|
||||
{
|
||||
matches.Add(
|
||||
new Tuple<string, string?, string>(
|
||||
testObject.id,
|
||||
testObject.applicationId,
|
||||
testObject.speckle_type
|
||||
)
|
||||
);
|
||||
setA.RemoveAt(i);
|
||||
setB.RemoveAt(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares the properties of <paramref name="a"/> against <paramref name="b"/>
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <param name="addedProps">Properties on <paramref name="a"/> that do not exist on <paramref name="b"/></param>
|
||||
/// <param name="deletedProps">Properties on <paramref name="b"/> that do not exist on <paramref name="a"/></param>
|
||||
/// <param name="modifiedProps">Properties on <paramref name="a"/> that have a different value on <paramref name="b"/>. The second string is the category of modification (eg count/primitive/base)</param>
|
||||
public static void CompareBaseProperties(
|
||||
Base a,
|
||||
Base b,
|
||||
out List<string> addedProps,
|
||||
out List<string> deletedProps,
|
||||
out List<Tuple<string, string>> modifiedProps
|
||||
)
|
||||
{
|
||||
addedProps = new List<string>();
|
||||
deletedProps = new List<string>();
|
||||
modifiedProps = new List<Tuple<string, string>>();
|
||||
Dictionary<string, object?> bObjectPropDict = b.GetMembers();
|
||||
Dictionary<string, object?> aObjectPropDict = a.GetMembers();
|
||||
foreach (KeyValuePair<string, object?> entry in aObjectPropDict)
|
||||
{
|
||||
if (bObjectPropDict.ContainsKey(entry.Key))
|
||||
{
|
||||
bool changed = !IsEqual(
|
||||
entry.Value,
|
||||
bObjectPropDict[entry.Key],
|
||||
out string category
|
||||
);
|
||||
if (changed)
|
||||
{
|
||||
modifiedProps.Add(new Tuple<string, string>(entry.Key, category));
|
||||
}
|
||||
|
||||
bObjectPropDict.Remove(entry.Key);
|
||||
}
|
||||
else
|
||||
{
|
||||
addedProps.Add(entry.Key);
|
||||
}
|
||||
}
|
||||
|
||||
// check if there are any props left on the b - these were missing in the a
|
||||
foreach (var entry in bObjectPropDict)
|
||||
{
|
||||
deletedProps.Add(entry.Key);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines equality between two objects
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <param name="category">The category of the modification, if objects are not equal</param>
|
||||
/// <returns></returns>
|
||||
public static bool IsEqual<T>(T a, T b, out string category)
|
||||
{
|
||||
category = "Value";
|
||||
switch (a)
|
||||
{
|
||||
case Base aBase:
|
||||
if (b is Base bBase && bBase.speckle_type == aBase.speckle_type)
|
||||
{
|
||||
return aBase.id == bBase.id;
|
||||
}
|
||||
else
|
||||
{
|
||||
category = "Type";
|
||||
return false;
|
||||
}
|
||||
|
||||
case List<object> aList:
|
||||
if (b is List<object> bList)
|
||||
{
|
||||
if (aList.Count != bList.Count)
|
||||
{
|
||||
category = "Count";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < aList.Count; i++)
|
||||
{
|
||||
if (!Equals(aList[i], bList[i]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
category = "Type";
|
||||
return false;
|
||||
}
|
||||
|
||||
case Dictionary<string, object> aDictionary:
|
||||
if (b is Dictionary<string, object> bDictionary)
|
||||
{
|
||||
if (aDictionary.Count != bDictionary.Count)
|
||||
{
|
||||
category = "Count";
|
||||
return false;
|
||||
}
|
||||
foreach (var entry in aDictionary)
|
||||
{
|
||||
if (
|
||||
!bDictionary.ContainsKey(entry.Key)
|
||||
|| !Equals(entry.Value, bDictionary[entry.Key])
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
category = "Type";
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
try
|
||||
{
|
||||
return EqualityComparer<T>.Default.Equals(a, b);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user