-
Notifications
You must be signed in to change notification settings - Fork 677
DYN-10716: Disable Autodesk Assistant when IDSDK is not initialized #17251
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 4 commits
f09b38d
c7de5c8
408361e
c9efe64
64a6ec6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -537,6 +537,9 @@ private void OnPythonEngineUpgradeToastRequested(string msg, bool stayOpen, stri | |
| /// <returns>True if the control was added, false if it already existed</returns> | ||
| internal bool AddOrFocusExtensionControl(IViewExtension viewExtension, UIElement content) | ||
| { | ||
| if (DisableExtensionWhenNoNetworkMode(viewExtension.UniqueId, viewExtension.Name, "opened")) | ||
| return false; | ||
|
|
||
| var window = ExtensionWindows.ContainsKey(viewExtension.Name) ? ExtensionWindows[viewExtension.Name] : null; | ||
| var tab = FindExtensionTab(viewExtension); | ||
| var addExtensionControl = window == null && tab == null; | ||
|
|
@@ -1019,6 +1022,10 @@ private void ExtensionWindow_Closed(object sender, EventArgs e) | |
| private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) | ||
| { | ||
| this.HideOrShowRightSideBar(e.Action); | ||
| if (e.Action == NotifyCollectionChangedAction.Add) | ||
| { | ||
| DisableExtensionTabsWhenIDSDKNotInitialized(); | ||
| } | ||
| } | ||
|
|
||
| private TabItem FindExtensionTab(IViewExtension viewExtension) | ||
|
|
@@ -1422,6 +1429,7 @@ private void DynamoLoadedViewExtensionHandler(ViewLoadedParams loadedParams, IEn | |
| Log(ext.Name + ": " + exc.Message); | ||
| } | ||
| } | ||
| DisableExtensionTabsWhenIDSDKNotInitialized(); | ||
|
|
||
| EnsureGraphPropertiesBinding(); | ||
| } | ||
|
|
||
|
|
@@ -3414,5 +3422,27 @@ internal bool DisableExtensionWhenNoNetworkMode(string extensionId, string exten | |
|
|
||
| return false; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Disables (but keeps visible) the Autodesk Assistant and MCP sidebar tabs when IDSDK is | ||
| /// not initialized, preventing the cascade of native error dialogs that occur when the user | ||
| /// opens the Assistant without Autodesk Identity installed. | ||
| /// </summary> | ||
| internal void DisableExtensionTabsWhenIDSDKNotInitialized() | ||
| { | ||
| if (dynamoViewModel.Model.AuthenticationManager.IsIDSDKInitialized()) | ||
| return; | ||
|
|
||
| foreach (var extensionId in new[] { AutodeskAssistantExtensionId, McpViewExtensionId }) | ||
| { | ||
| var tab = dynamoViewModel.SideBarTabItems.OfType<TabItem>() | ||
| .SingleOrDefault(t => string.Equals(t.Uid, extensionId, StringComparison.OrdinalIgnoreCase)); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SingleOrDefault() throws InvalidOperationException if two tabs ever share a Uid ... rare, but possible when re-adding a tab. Better to be safe and not assume uniqueness. An exception here can crash the UI. |
||
| if (tab != null) | ||
| { | ||
| tab.IsEnabled = false; | ||
| Log($"Extension tab {tab.Header} disabled because IDSDK is not initialized"); | ||
| } | ||
|
Comment on lines
+3438
to
+3444
|
||
| } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,9 @@ | ||
| using System; | ||
| using Dynamo.Controls; | ||
| using Dynamo.Core; | ||
| using Dynamo.Models; | ||
| using Dynamo.ViewModels; | ||
| using Dynamo.Wpf.Extensions; | ||
| using NUnit.Framework; | ||
| using TestServices; | ||
|
|
||
|
|
@@ -100,5 +103,194 @@ public void UnrecognizedExtensionIsNotDisabledWhenNoNetworkModeIsEnabled() | |
|
|
||
| Assert.IsFalse(shouldDisable); | ||
| } | ||
|
|
||
| [Test] | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These tests assume the IDSDK is unavailable, but on a test machine or a dev machine, it might be installed and fail. Add Assume.That(!...IsIDSDKInitialized()) or Assert.Ignore |
||
| public void AssistantAndMcpTabsAreDisabledWhenIDSDKIsNotInitialized() | ||
| { | ||
| var pathResolver = new TestPathResolver(); | ||
| DynamoModel modelWithUninitializedIDSDK = null; | ||
| DynamoViewModel viewModelWithUninitializedIDSDK = null; | ||
| DynamoView viewWithUninitializedIDSDK = null; | ||
|
|
||
| try | ||
| { | ||
| // IDSDKManager.IsIDSDKInitialized returns false when the native IDSDK library | ||
| // is not installed (e.g. test environments, VMs without Autodesk Identity). | ||
| modelWithUninitializedIDSDK = DynamoModel.Start(new DynamoModel.DefaultStartConfiguration() | ||
| { | ||
| PathResolver = pathResolver, | ||
| StartInTestMode = true, | ||
| GeometryFactoryPath = preloader.GeometryFactoryPath, | ||
| ProcessMode = Dynamo.Scheduler.TaskProcessMode.Synchronous, | ||
| NoNetworkMode = false, | ||
| AuthProvider = new IDSDKManager() | ||
| }); | ||
|
|
||
|
Comment on lines
+128
to
+129
|
||
| viewModelWithUninitializedIDSDK = DynamoViewModel.Start(new DynamoViewModel.StartConfiguration() | ||
| { | ||
| DynamoModel = modelWithUninitializedIDSDK | ||
| }); | ||
|
|
||
| viewWithUninitializedIDSDK = new DynamoView(viewModelWithUninitializedIDSDK); | ||
|
|
||
| // Simulate the extension having loaded its tab into the sidebar. | ||
| // Adding to SideBarTabItems triggers CollectionChanged → DisableExtensionTabsWhenIDSDKNotInitialized automatically. | ||
| var assistantTab = new System.Windows.Controls.TabItem { Uid = DynamoView.AutodeskAssistantExtensionId }; | ||
| var mcpTab = new System.Windows.Controls.TabItem { Uid = DynamoView.McpViewExtensionId }; | ||
| viewModelWithUninitializedIDSDK.SideBarTabItems.Add(assistantTab); | ||
| viewModelWithUninitializedIDSDK.SideBarTabItems.Add(mcpTab); | ||
|
|
||
| Assert.IsFalse(assistantTab.IsEnabled); | ||
| Assert.IsFalse(mcpTab.IsEnabled); | ||
| } | ||
| finally | ||
| { | ||
| if (viewWithUninitializedIDSDK != null && viewWithUninitializedIDSDK.IsLoaded) | ||
| { | ||
| viewWithUninitializedIDSDK.Close(); | ||
| } | ||
|
|
||
| if (viewModelWithUninitializedIDSDK != null) | ||
| { | ||
| var shutdownParams = new DynamoViewModel.ShutdownParams(shutdownHost: false, allowCancellation: false); | ||
| viewModelWithUninitializedIDSDK.PerformShutdownSequence(shutdownParams); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| [Test] | ||
| public void AssistantTabRemainsDisabledWhenReAddedToSidebarAfterWorkspaceOpen() | ||
| { | ||
| var pathResolver = new TestPathResolver(); | ||
| DynamoModel modelWithUninitializedIDSDK = null; | ||
| DynamoViewModel viewModelWithUninitializedIDSDK = null; | ||
| DynamoView viewWithUninitializedIDSDK = null; | ||
|
|
||
| try | ||
| { | ||
| modelWithUninitializedIDSDK = DynamoModel.Start(new DynamoModel.DefaultStartConfiguration() | ||
| { | ||
| PathResolver = pathResolver, | ||
| StartInTestMode = true, | ||
| GeometryFactoryPath = preloader.GeometryFactoryPath, | ||
| ProcessMode = Dynamo.Scheduler.TaskProcessMode.Synchronous, | ||
| NoNetworkMode = false, | ||
| AuthProvider = new IDSDKManager() | ||
| }); | ||
|
|
||
| viewModelWithUninitializedIDSDK = DynamoViewModel.Start(new DynamoViewModel.StartConfiguration() | ||
| { | ||
| DynamoModel = modelWithUninitializedIDSDK | ||
| }); | ||
|
|
||
| viewWithUninitializedIDSDK = new DynamoView(viewModelWithUninitializedIDSDK); | ||
|
|
||
| // Initial add — extension loads its tab during startup. | ||
| var assistantTab = new System.Windows.Controls.TabItem { Uid = DynamoView.AutodeskAssistantExtensionId }; | ||
| viewModelWithUninitializedIDSDK.SideBarTabItems.Add(assistantTab); | ||
| Assert.IsFalse(assistantTab.IsEnabled, "Tab should be disabled on initial add"); | ||
|
|
||
| // Simulate the extension removing and re-adding its tab (e.g. on workspace open). | ||
| viewModelWithUninitializedIDSDK.SideBarTabItems.Remove(assistantTab); | ||
| var reAddedTab = new System.Windows.Controls.TabItem { Uid = DynamoView.AutodeskAssistantExtensionId }; | ||
| viewModelWithUninitializedIDSDK.SideBarTabItems.Add(reAddedTab); | ||
|
|
||
| Assert.IsFalse(reAddedTab.IsEnabled, "Re-added tab should still be disabled when IDSDK is not initialized"); | ||
| } | ||
| finally | ||
| { | ||
| if (viewWithUninitializedIDSDK != null && viewWithUninitializedIDSDK.IsLoaded) | ||
| { | ||
| viewWithUninitializedIDSDK.Close(); | ||
| } | ||
|
|
||
| if (viewModelWithUninitializedIDSDK != null) | ||
| { | ||
| var shutdownParams = new DynamoViewModel.ShutdownParams(shutdownHost: false, allowCancellation: false); | ||
| viewModelWithUninitializedIDSDK.PerformShutdownSequence(shutdownParams); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| [Test] | ||
| public void AssistantAndMcpTabsAreNotDisabledWhenIDSDKIsInitialized() | ||
| { | ||
| var pathResolver = new TestPathResolver(); | ||
| DynamoModel modelWithNullAuthProvider = null; | ||
| DynamoViewModel viewModelWithNullAuthProvider = null; | ||
| DynamoView viewWithNullAuthProvider = null; | ||
|
|
||
| try | ||
| { | ||
| // When AuthProvider is null (host environment or no IDSDK configured), | ||
| // IsIDSDKInitialized() returns true — tabs should remain enabled. | ||
| modelWithNullAuthProvider = DynamoModel.Start(new DynamoModel.DefaultStartConfiguration() | ||
| { | ||
| PathResolver = pathResolver, | ||
| StartInTestMode = true, | ||
| GeometryFactoryPath = preloader.GeometryFactoryPath, | ||
| ProcessMode = Dynamo.Scheduler.TaskProcessMode.Synchronous, | ||
| NoNetworkMode = false | ||
| }); | ||
|
|
||
| viewModelWithNullAuthProvider = DynamoViewModel.Start(new DynamoViewModel.StartConfiguration() | ||
| { | ||
| DynamoModel = modelWithNullAuthProvider | ||
| }); | ||
|
|
||
| viewWithNullAuthProvider = new DynamoView(viewModelWithNullAuthProvider); | ||
|
|
||
| var assistantTab = new System.Windows.Controls.TabItem { Uid = DynamoView.AutodeskAssistantExtensionId }; | ||
| var mcpTab = new System.Windows.Controls.TabItem { Uid = DynamoView.McpViewExtensionId }; | ||
| viewModelWithNullAuthProvider.SideBarTabItems.Add(assistantTab); | ||
| viewModelWithNullAuthProvider.SideBarTabItems.Add(mcpTab); | ||
|
|
||
| viewWithNullAuthProvider.DisableExtensionTabsWhenIDSDKNotInitialized(); | ||
|
|
||
| Assert.IsTrue(assistantTab.IsEnabled); | ||
| Assert.IsTrue(mcpTab.IsEnabled); | ||
| } | ||
| finally | ||
| { | ||
| if (viewWithNullAuthProvider != null && viewWithNullAuthProvider.IsLoaded) | ||
| { | ||
| viewWithNullAuthProvider.Close(); | ||
| } | ||
|
|
||
| if (viewModelWithNullAuthProvider != null) | ||
| { | ||
| var shutdownParams = new DynamoViewModel.ShutdownParams(shutdownHost: false, allowCancellation: false); | ||
| viewModelWithNullAuthProvider.PerformShutdownSequence(shutdownParams); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| [Test] | ||
| public void AssistantTabCannotBeAddedViaSideBarWhenNoNetworkModeIsEnabled() | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this testing NoNetworkMode? |
||
| { | ||
| // Simulates the code path where an extension calls AddToExtensionsSideBar() after | ||
| // initial load (e.g. from IExtensionStorageAccess.WorkspaceOpened), which bypasses | ||
| // the DisableExtensionWhenNoNetworkMode guard in DynamoLoadedViewExtensionHandler. | ||
| // AddOrFocusExtensionControl must block these late attempts in NoNetworkMode. | ||
| var stubExtension = new StubViewExtension(DynamoView.AutodeskAssistantExtensionId); | ||
| var added = View.AddOrFocusExtensionControl(stubExtension, null); | ||
|
|
||
| Assert.IsFalse(added); | ||
| Assert.IsFalse(ViewModel.SideBarTabItems | ||
| .OfType<System.Windows.Controls.TabItem>() | ||
| .Any(t => string.Equals(t.Uid, DynamoView.AutodeskAssistantExtensionId, | ||
| StringComparison.OrdinalIgnoreCase))); | ||
| } | ||
|
|
||
| private class StubViewExtension : IViewExtension | ||
| { | ||
| public StubViewExtension(string uniqueId) { UniqueId = uniqueId; } | ||
| public string UniqueId { get; } | ||
| public string Name => "Stub"; | ||
| public void Startup(ViewStartupParams p) { } | ||
| public void Loaded(ViewLoadedParams p) { } | ||
| public void Shutdown() { } | ||
| public void Dispose() { } | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Returns false when the extension is blocked, but returning false is actually an indication that the extension is already present and the caller AddToExtensionsSideBar treats it as such and will log ExtensionAlreadyPresent for a blocked extension. ... This might require a switch from a bool to an enum. It is an internal method and would not be a breaking change.