Skip to content

Commit

Permalink
[AGD-2060] Linting API (#11673)
Browse files Browse the repository at this point in the history
* Linter manager (#11606)

* Initial commit

* Move event subscription to extension base class + add property changed filter to rules

* comment updates

* Update LinterExtensionBase.cs

* Update InputNodesNotAllowedRule.cs

* Add Deactivate method to linterExtensionBase + LinterManager tests

* comment updates

* Dispose LinterManager

* comment updates

* Serialize linter manager to dyn file

* clear ruleEvaluationResults on workspace change

* Remove test extension

* Handle GraphRuleEvaluationResults nodeIds changes

* Update SerializationConverters.cs

* Fix failing serialization test

* comment updates

* revert changes to test file

* Linter ViewExtension (#11634)

* Initial commit

* Move event subscription to extension base class + add property changed filter to rules

* comment updates

* Update LinterExtensionBase.cs

* Update InputNodesNotAllowedRule.cs

* Add Deactivate method to linterExtensionBase + LinterManager tests

* comment updates

* Initial commit

* Add issues count to WorkspaceSaving

* add scrollviewer

* Dispose LinterManager

* Make concrete issues internal

* summaries on IRuleIssue

* comment updates

* Update severity code icon

* Serialize linter manager to dyn file

* clear ruleEvaluationResults on workspace change

* Remove test extension

* Show names instead of GUIDs

* add help doc

* clean up

* Handle GraphRuleEvaluationResults nodeIds changes

* Update LintingViewExtension.csproj

* Update LintingViewExtension.csproj

* Update SerializationConverters.cs

* Fix failing serialization test

* comment updates

* revert changes to test file

* fix available linters binding

* add rules back in test ext

* comment updates

* remove output path

* Update LinterManagerTests.cs

* comment updates

* hide LinterViewExtension and LinterExtensionBase

* Update LinterViewModel.cs

* remove unused test files

* Fix pr comments (#11677)

* Linter manager (#11606)

* Initial commit

* Move event subscription to extension base class + add property changed filter to rules

* comment updates

* Update LinterExtensionBase.cs

* Update InputNodesNotAllowedRule.cs

* Add Deactivate method to linterExtensionBase + LinterManager tests

* comment updates

* Dispose LinterManager

* comment updates

* Serialize linter manager to dyn file

* clear ruleEvaluationResults on workspace change

* Remove test extension

* Handle GraphRuleEvaluationResults nodeIds changes

* Update SerializationConverters.cs

* Fix failing serialization test

* comment updates

* revert changes to test file

* Linter ViewExtension (#11634)

* Initial commit

* Move event subscription to extension base class + add property changed filter to rules

* comment updates

* Update LinterExtensionBase.cs

* Update InputNodesNotAllowedRule.cs

* Add Deactivate method to linterExtensionBase + LinterManager tests

* comment updates

* Initial commit

* Add issues count to WorkspaceSaving

* add scrollviewer

* Dispose LinterManager

* Make concrete issues internal

* summaries on IRuleIssue

* comment updates

* Update severity code icon

* Serialize linter manager to dyn file

* clear ruleEvaluationResults on workspace change

* Remove test extension

* Show names instead of GUIDs

* add help doc

* clean up

* Handle GraphRuleEvaluationResults nodeIds changes

* Update LintingViewExtension.csproj

* Update LintingViewExtension.csproj

* Update SerializationConverters.cs

* Fix failing serialization test

* comment updates

* revert changes to test file

* fix available linters binding

* add rules back in test ext

* comment updates

* remove output path

* Update LinterManagerTests.cs

* comment updates

* hide LinterViewExtension and LinterExtensionBase

* Update LinterViewModel.cs

* if statements

* remove Prism

* clean up

Co-authored-by: BogdanZavu <zavu_bogdan@yahoo.com>
  • Loading branch information
SHKnudsen and BogdanZavu committed May 11, 2021
1 parent e6a30ec commit ca18e9d
Show file tree
Hide file tree
Showing 48 changed files with 3,157 additions and 10 deletions.
11 changes: 11 additions & 0 deletions src/Dynamo.All.sln
Expand Up @@ -237,6 +237,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NodeAutoCompleteViewExtensi
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Md2Html", "Tools\Md2Html\Md2Html.csproj", "{0893F745-CB1A-427A-8E87-CA927273039A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LintingViewExtension", "LintingViewExtension\LintingViewExtension.csproj", "{C86F9058-229D-40A9-95D5-D6F081AA9230}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -869,6 +871,14 @@ Global
{0893F745-CB1A-427A-8E87-CA927273039A}.Release|Any CPU.Build.0 = Release|Any CPU
{0893F745-CB1A-427A-8E87-CA927273039A}.Release|x64.ActiveCfg = Release|Any CPU
{0893F745-CB1A-427A-8E87-CA927273039A}.Release|x64.Build.0 = Release|Any CPU
{C86F9058-229D-40A9-95D5-D6F081AA9230}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C86F9058-229D-40A9-95D5-D6F081AA9230}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C86F9058-229D-40A9-95D5-D6F081AA9230}.Debug|x64.ActiveCfg = Debug|x64
{C86F9058-229D-40A9-95D5-D6F081AA9230}.Debug|x64.Build.0 = Debug|x64
{C86F9058-229D-40A9-95D5-D6F081AA9230}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C86F9058-229D-40A9-95D5-D6F081AA9230}.Release|Any CPU.Build.0 = Release|Any CPU
{C86F9058-229D-40A9-95D5-D6F081AA9230}.Release|x64.ActiveCfg = Release|x64
{C86F9058-229D-40A9-95D5-D6F081AA9230}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -946,6 +956,7 @@ Global
{10AF430D-0D3A-49CE-A63D-848912959745} = {88D45B00-E564-41DB-B57C-9509646CAA49}
{51511AFD-F326-4995-8E27-5D711419EF6F} = {88D45B00-E564-41DB-B57C-9509646CAA49}
{0893F745-CB1A-427A-8E87-CA927273039A} = {D114C59C-CF66-4CC2-980F-9301FB4EA4E1}
{C86F9058-229D-40A9-95D5-D6F081AA9230} = {88D45B00-E564-41DB-B57C-9509646CAA49}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {89CB19C6-BF0A-4E6A-BFDA-79D143EAB59D}
Expand Down
11 changes: 11 additions & 0 deletions src/DynamoCore/DynamoCore.csproj
Expand Up @@ -120,6 +120,7 @@ limitations under the License.
<Compile Include="Exceptions\LibraryLoadFailedException.cs" />
<Compile Include="Extensions\ExtensionData.cs" />
<Compile Include="Extensions\IExtensionStorageAccess.cs" />
<Compile Include="Extensions\LinterExtensionBase.cs" />
<Compile Include="Graph\Workspaces\PackageDependencyInfo.cs" />
<Compile Include="Graph\Nodes\NodeOutputData.cs" />
<Compile Include="Graph\Nodes\NodeInputData.cs" />
Expand All @@ -130,6 +131,16 @@ limitations under the License.
<Compile Include="Graph\Workspaces\SerializationExtensions.cs" />
<Compile Include="Graph\Workspaces\UndoRedo.cs" />
<Compile Include="Extensions\IServiceManager.cs" />
<Compile Include="Linting\Interfaces\RuleEvaluationStatusEnum.cs" />
<Compile Include="Linting\Interfaces\IRuleEvaluationResult.cs" />
<Compile Include="Linting\Interfaces\SeverityCodesEnum.cs" />
<Compile Include="Linting\LinterExtensionDescriptor.cs" />
<Compile Include="Linting\LinterManager.cs" />
<Compile Include="Linting\Rules\GraphLinterRule.cs" />
<Compile Include="Linting\Rules\GraphRuleEvaluationResult.cs" />
<Compile Include="Linting\Rules\LinterRule.cs" />
<Compile Include="Linting\Rules\NodeLinterRule.cs" />
<Compile Include="Linting\Rules\NodeRuleEvaluationResult.cs" />
<Compile Include="Logging\AnalyticsService.cs" />
<Compile Include="Logging\DynamoAnalyticsClient.cs" />
<Compile Include="Logging\IAnalyticsSession.cs" />
Expand Down
275 changes: 275 additions & 0 deletions src/DynamoCore/Extensions/LinterExtensionBase.cs
@@ -0,0 +1,275 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Dynamo.Graph.Nodes;
using Dynamo.Graph.Workspaces;
using Dynamo.Linting;
using Dynamo.Linting.Rules;

namespace Dynamo.Extensions
{
/// <summary>
/// Base class for all LinterExtensions
/// </summary>
internal abstract class LinterExtensionBase : IExtension
{
private const string NODE_ADDED_PROPERTY = "NodeAdded";
private const string NODE_REMOVED_PROPERTY = "NodeRemoved";

#region Private/Internal properties
private HashSet<LinterRule> linterRules = new HashSet<LinterRule>();
private LinterManager linterManager;
private WorkspaceModel currentWorkspace;

internal ReadyParams ReadyParamsRef { get; set; }

internal bool IsActive => this.linterManager?.IsExtensionActive(UniqueId) ?? false;

internal LinterExtensionDescriptor ExtensionDescriptor { get; private set; }
#endregion

#region Public properties

///<inheritdoc/>
public abstract string UniqueId { get; }

///<inheritdoc/>
public abstract string Name { get; }

/// <summary>
/// Collection of the rules in this extension
/// </summary>
public HashSet<LinterRule> LinterRules => linterRules;

#endregion

#region Internal methods

/// <summary>
/// Add a LinterRule
/// </summary>
/// <param name="linterRule"></param>
public void AddLinterRule(LinterRule linterRule)
{
linterRules.Add(linterRule);
}

/// <summary>
/// Remove a LinterRule
/// </summary>
/// <param name="linterRule"></param>
public void RemoveLinterRule(LinterRule linterRule)
{
linterRules.Remove(linterRule);
}

/// <summary>
/// Activate this linter by subscribing the workspace and initializing its rules
/// </summary>
internal void Activate()
{
if (IsActive)
return;

ReadyParamsRef.CurrentWorkspaceChanged += OnCurrentWorkspaceChanged;
OnCurrentWorkspaceChanged(ReadyParamsRef.CurrentWorkspaceModel);
}

/// <summary>
/// Deactivates this extension by unsubscribing all its events
/// </summary>
internal void Deactivate()
{
ReadyParamsRef.CurrentWorkspaceChanged -= OnCurrentWorkspaceChanged;
UnsubscribeGraphEvents(currentWorkspace);
this.linterManager.RuleEvaluationResults.Clear();
}

#endregion

internal void InitializeBase(LinterManager linterManager)
{
this.linterManager = linterManager;
}

private void EvaluateGraphRules(NodeModel modifiedNode, string changedProperty)
{
if (!IsActive)
return;

var graphRules = linterRules.
Where(x => x is GraphLinterRule).
Cast<GraphLinterRule>().
ToList();

if (graphRules is null)
return;

foreach (var rule in graphRules)
{
if (changedProperty != NODE_ADDED_PROPERTY &&
changedProperty != NODE_REMOVED_PROPERTY &&
!rule.EvaluationTriggerEvents.Contains(changedProperty))
continue;

rule.Evaluate(currentWorkspace, changedProperty, modifiedNode);
}
}

private void EvaluateNodeRules(NodeModel modifiedNode, string changedProperty)
{
if (!IsActive)
return;

var nodeRules = linterRules.
Where(x => x is NodeLinterRule).
Cast<NodeLinterRule>().
ToList();

if (nodeRules is null)
return;

foreach (var rule in nodeRules)
{
if (changedProperty != NODE_ADDED_PROPERTY && !rule.EvaluationTriggerEvents.Contains(changedProperty))
continue;

rule.Evaluate(modifiedNode, changedProperty);
}
}

private void InitializeRules()
{
if (!(ReadyParamsRef.CurrentWorkspaceModel is WorkspaceModel wm))
return;

foreach (var rule in LinterRules)
{
rule.InitializeBase(wm);
}
}

#region Extension Lifecycle

///<inheritdoc/>
public virtual void Ready(ReadyParams sp)
{
ReadyParamsRef = sp;
if (IsActive)
InitializeRules();
}

///<inheritdoc/>
public virtual void Startup(StartupParams sp)
{
ExtensionDescriptor = new LinterExtensionDescriptor(UniqueId, Name);
OnLinterExtensionReady();
}

///<inheritdoc/>
public abstract void Shutdown();

///<inheritdoc/>
public virtual void Dispose()
{
ReadyParamsRef.CurrentWorkspaceChanged -= OnCurrentWorkspaceChanged;
UnsubscribeGraphEvents(currentWorkspace);
}

#endregion

#region Events

/// <summary>
/// Represents the method that will handle rule evaluated related events.
/// </summary>
internal delegate void LinterExtensionReadyHandler(LinterExtensionDescriptor descriptor);

internal static event LinterExtensionReadyHandler LinterExtensionReady;

private void OnLinterExtensionReady()
{
LinterExtensionReady?.Invoke(ExtensionDescriptor);
}

private void OnCurrentWorkspaceChanged(IWorkspaceModel obj)
{
if (this.currentWorkspace != null)
UnsubscribeGraphEvents(this.currentWorkspace);

this.linterManager.RuleEvaluationResults.Clear();
this.currentWorkspace = ReadyParamsRef.CurrentWorkspaceModel as WorkspaceModel;
this.SubscribeNodeEvents();
this.SubscribeGraphEvents();
this.InitializeRules();
}

private void SubscribeGraphEvents()
{
this.currentWorkspace.NodeRemoved += OnNodeRemoved;
this.currentWorkspace.NodeAdded += OnNodeAdded;
}


private void SubscribeNodeEvents()
{
foreach (var node in currentWorkspace.Nodes)
{
node.PropertyChanged += OnNodePropertyChanged;
}
}

private void UnsubscribeGraphEvents(WorkspaceModel workspaceModel)
{
workspaceModel.NodeRemoved -= OnNodeRemoved;
workspaceModel.NodeAdded -= OnNodeAdded;
workspaceModel.Nodes.
ToList().
ForEach(x => UnsubscribeNodeEvents(x));
}

private void UnsubscribeNodeEvents(NodeModel node)
{
node.PropertyChanged -= OnNodePropertyChanged;
}

private void OnNodeAdded(NodeModel node)
{
EvaluateGraphRules(node, NODE_ADDED_PROPERTY);
EvaluateNodeRules(node, NODE_ADDED_PROPERTY);
node.PropertyChanged += OnNodePropertyChanged;
}

private void OnNodePropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
EvaluateNodeRules(sender as NodeModel, e.PropertyName);
EvaluateGraphRules(sender as NodeModel, e.PropertyName);

}

private void OnNodeRemoved(Graph.Nodes.NodeModel node)
{
UnsubscribeNodeEvents(node);
EvaluateGraphRules(node, NODE_REMOVED_PROPERTY);

var nodeRules = LinterRules.
Where(x => x is NodeLinterRule).
Cast<NodeLinterRule>().
ToList();

if (nodeRules is null)
return;

foreach (var rule in nodeRules)
{
var result = new NodeRuleEvaluationResult(rule.Id, Linting.Interfaces.RuleEvaluationStatusEnum.Passed, rule.SeverityCode, node.GUID.ToString());
rule.OnRuleEvaluated(result);
}

}
#endregion
}
}

0 comments on commit ca18e9d

Please sign in to comment.