-
Notifications
You must be signed in to change notification settings - Fork 79
SLVS-1373 Validate credentials in OrganizationSelectionDialog #5634
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
Changes from 1 commit
64638d0
dce2080
eeba39f
aa09f6a
6a9c851
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 |
---|---|---|
|
@@ -42,35 +42,36 @@ public OrganizationSelectionDialog(IConnectedModeServices connectedModeServices, | |
|
||
private async void OkButton_OnClick(object sender, RoutedEventArgs e) | ||
{ | ||
var isConnectionValid = await ViewModel.ValidateConnectionAsync(); | ||
var isConnectionValid = await ValidateConnectionForSelectedOrganizationAsync(ViewModel.SelectedOrganization.Key); | ||
if(isConnectionValid) | ||
{ | ||
DialogResult = true; | ||
OnConnectionValidationSucceeded(ViewModel.SelectedOrganization.Key); | ||
} | ||
} | ||
|
||
private async void ChooseAnotherOrganizationButton_OnClick(object sender, RoutedEventArgs e) | ||
{ | ||
ViewModel.SelectedOrganization = null; | ||
var manualOrganizationSelectionDialog = new ManualOrganizationSelectionDialog(); | ||
var isSelectedManualOrganizationValid = await ValidateManualOrganizationAsync(manualOrganizationSelectionDialog); | ||
var manualSelectionDialogSucceeded = manualOrganizationSelectionDialog.ShowDialog(this); | ||
if(manualSelectionDialogSucceeded is not true) | ||
{ | ||
return; | ||
} | ||
|
||
var isSelectedManualOrganizationValid = await ValidateConnectionForSelectedOrganizationAsync(manualOrganizationSelectionDialog.ViewModel.OrganizationKey); | ||
if (isSelectedManualOrganizationValid) | ||
{ | ||
ViewModel.SelectedOrganization = new OrganizationDisplay(manualOrganizationSelectionDialog.ViewModel.OrganizationKey, null); | ||
DialogResult = true; | ||
OnConnectionValidationSucceeded(manualOrganizationSelectionDialog.ViewModel.OrganizationKey); | ||
} | ||
} | ||
|
||
private async Task<bool> ValidateManualOrganizationAsync(ManualOrganizationSelectionDialog manualOrganizationSelectionDialog) | ||
private async Task<bool> ValidateConnectionForSelectedOrganizationAsync(string selectedOrganizationKey) | ||
{ | ||
try | ||
{ | ||
var manualSelectionDialogSucceeded = manualOrganizationSelectionDialog.ShowDialog(this); | ||
var organizationSelectionInvalidMsg = string.Format(UiResources.ManualOrganziationSelectionFailedText, manualOrganizationSelectionDialog.ViewModel.OrganizationKey); | ||
var isConnectionValid = await ViewModel.ValidateConnectionForOrganizationAsync(manualOrganizationSelectionDialog.ViewModel.OrganizationKey, organizationSelectionInvalidMsg); | ||
|
||
return manualSelectionDialogSucceeded is true && isConnectionValid; | ||
var organizationSelectionInvalidMsg = string.Format(UiResources.ValidatingOrganziationSelectionFailedText, selectedOrganizationKey); | ||
return await ViewModel.ValidateConnectionForOrganizationAsync(selectedOrganizationKey, organizationSelectionInvalidMsg); | ||
} | ||
catch (Exception e) when (!ErrorHandler.IsCriticalException(e)) | ||
{ | ||
|
@@ -83,5 +84,11 @@ private async void OrganizationSelectionDialog_OnLoaded(object sender, RoutedEve | |
{ | ||
await ViewModel.LoadOrganizationsAsync(); | ||
} | ||
|
||
private void OnConnectionValidationSucceeded(string organizationKey) | ||
{ | ||
ViewModel.UpdateConnectionInfo(organizationKey); | ||
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. why not call this inside the validation method and use the ConnectionInfo object that was already created there? 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. Because it should only be done once the validation succeeded and the dialog is closed. |
||
DialogResult = true; | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,6 +27,11 @@ namespace SonarLint.VisualStudio.ConnectedMode.UI.OrganizationSelection; | |
|
||
public class OrganizationSelectionViewModel(ICredentialsModel credentialsModel, ISlCoreConnectionAdapter connectionAdapter, IProgressReporterViewModel progressReporterViewModel) : ViewModelBase | ||
{ | ||
/// <summary> | ||
/// The <see cref="ConnectionInfo"/> that is used to connect to the server, whose <see cref="ConnectionInfo.Id"/> can be different from the <see cref="SelectedOrganization"/> | ||
/// due to the fact that an organization key can also be entered manually rather than selected form the list of <see cref="Organizations"/>. | ||
/// </summary> | ||
public ConnectionInfo ConnectionInfo { get; private set; } = new(null, ConnectionServerType.SonarCloud); | ||
public IProgressReporterViewModel ProgressReporterViewModel { get; } = progressReporterViewModel; | ||
|
||
public OrganizationDisplay SelectedOrganization | ||
|
@@ -35,6 +40,7 @@ public OrganizationDisplay SelectedOrganization | |
set | ||
{ | ||
selectedOrganization = value; | ||
UpdateConnectionInfo(selectedOrganization?.Key); | ||
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. Here |
||
RaisePropertyChanged(); | ||
RaisePropertyChanged(nameof(IsValidSelectedOrganization)); | ||
} | ||
|
@@ -64,11 +70,6 @@ public async Task LoadOrganizationsAsync() | |
await ProgressReporterViewModel.ExecuteTaskWithProgressAsync(organizationLoadingParams); | ||
} | ||
|
||
public async Task<bool> ValidateConnectionAsync() | ||
{ | ||
return await ValidateConnectionForOrganizationAsync(SelectedOrganization.Key, UiResources.ValidatingConnectionFailedText); | ||
} | ||
|
||
internal async Task<AdapterResponseWithData<List<OrganizationDisplay>>> AdapterLoadOrganizationsAsync() | ||
{ | ||
return await connectionAdapter.GetOrganizationsAsync(credentialsModel); | ||
|
@@ -83,21 +84,17 @@ internal void UpdateOrganizations(AdapterResponseWithData<List<OrganizationDispl | |
|
||
internal async Task<bool> ValidateConnectionForOrganizationAsync(string organizationKey, string warningText) | ||
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. Can be made private, see other comment. The warning text does not need to be parametrized as well. 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. I think it does. For usability purposes. |
||
{ | ||
var connectionInfo = CreateConnectionInfo(organizationKey); | ||
var connectionInfoToValidate = ConnectionInfo with { Id = organizationKey }; | ||
var validationParams = new TaskToPerformParams<AdapterResponse>( | ||
async () => await connectionAdapter.ValidateConnectionAsync(connectionInfo, credentialsModel), | ||
async () => await connectionAdapter.ValidateConnectionAsync(connectionInfoToValidate, credentialsModel), | ||
UiResources.ValidatingConnectionProgressText, | ||
warningText); | ||
var adapterResponse = await ProgressReporterViewModel.ExecuteTaskWithProgressAsync(validationParams); | ||
return adapterResponse.Success; | ||
} | ||
|
||
internal static ConnectionInfo CreateConnectionInfo(string organizationKey) | ||
public void UpdateConnectionInfo(string organizationKey) | ||
{ | ||
if(organizationKey is null) | ||
{ | ||
throw new ArgumentException($"{nameof(organizationKey)} must be set."); | ||
} | ||
return new ConnectionInfo(organizationKey, ConnectionServerType.SonarCloud); | ||
ConnectionInfo = ConnectionInfo with { Id = organizationKey }; | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.
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.
I think this code can be simplified if you remove this check and put the old check back. It would assign SelectedOrganization whichever value is selected (list or manual), and then always validate the SelectedOrganization in ViewModel in a uniform way. The error text can be made generic to not worry about mentioning manual organization selection in it. This will be even more relevant once we add the duplication check.
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.
I removed the assigning of the SelectedOrganization from the manual selection for two reasons:
Once the SelectedOrganization is assigned, the OK button becomes enabled. This should not be the case, though, because the organization is not valid. Therefore not assigning the SelectedOrganization in the first place, makes it more consistent.
The SelectedOrganization refers to the organization selected from the list. But the manual one that was chosen is not part of the list. This brings a lot of confusion: the view model property (hence also the Combobox.SelectedItem) is set to a string, but that string is not part of the list. You will notice that WPF does not add a combobox item that is selected, if you bind its view model to a string (as long as it's not part of the list, then it should not be a selected one)
It is more understanding for the user to have separate error messages for the manual selection or for the list selection. Imagine that the list of organizations is long. It would be very difficult for the user to know which organization was wrong, without scrolling like crazy and trying to find out if the organization was from the list or a manual one that he entered.
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.
We can split the list selection from the value that is validated (the organization value then can be passed as a parameter to ViewModel.Validate). SelectedOrganization can be reset after validation fails. The error text can include the organization key. There will be no confusion if we reset selection and show the key in the error.
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.
What is the gain of setting the SelectedOrganization if we will anyway pass the organization value to the Validate method?
Uh oh!
There was an error while loading. Please reload this page.
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.
So the thing is. We will need to do a few common things for both scenarious
Try to tick all 3 of those boxes with refactoring. You don't have to do it in the way I initially suggested (I though it was a bit more simple to fix)
Uh oh!
There was an error while loading. Please reload this page.
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.
Point 1 and 2 are solved by the fact that we use the same method in the ViewModel: ValidateConnectionForOrganizationAsync
I could work on point 3 and find a different approach