Skip to content
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

Making -ResourceGroupName parameter optional for Get-AzureRmSqlServer cmdlet #4188

Merged
merged 9 commits into from Jun 30, 2017

Conversation

akromm-zz
Copy link
Contributor

@akromm-zz akromm-zz commented Jun 23, 2017

Description


This checklist is used to make sure that common guidelines for a pull request are followed. You can find a more complete discussion of PowerShell cmdlet best practices here.

General Guidelines

  • Title of the pull request is clear and informative.
  • There are a small number of commits, each of which have an informative message. This means that previously merged commits do not appear in the history of the PR. For more information on cleaning up the commits in your PR, see this page.
  • The pull request does not introduce breaking changes (unless a major version change occurs in the assembly and module).

Testing Guidelines

  • Pull request includes test coverage for the included changes.
  • PowerShell scripts used in tests should do any necessary setup as part of the test or suite setup, and should not use hard-coded values for locations or existing resources.

Cmdlet Signature Guidelines

  • New cmdlets that make changes or have side effects should implement ShouldProcess and have SupportShouldProcess=true specified in the cmdlet attribute. You can find more information on ShouldProcess here.
  • Cmdlet specifies OutputType attribute if any output is produced - if the cmdlet produces no output, it should implement a PassThru parameter.

Cmdlet Parameter Guidelines

  • Parameter types should not expose types from the management library - complex parameter types should be defined in the module.
  • Complex parameter types are discouraged - a parameter type should be simple types as often as possible. If complex types are used, they should be shallow and easily creatable from a constructor or another cmdlet.
  • Cmdlet parameter sets should be mutually exclusive - each parameter set must have at least one mandatory parameter not in other parameter sets.

@akromm-zz
Copy link
Contributor Author

#635

Copy link
Member

@jaredmoo jaredmoo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test issue is the only blocker for me, other comments are just suggestions


# Test getting all servers in all resource groups
$all2 = Get-AzureRmSqlServer
Assert-AreEqual 3 $all2.Count
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will fail if I run it in my subscription that already has some sql servers unrelated to PS test. Can you change this so that it checks that the list contains the 3 previous servers?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I can do that.

/// <summary>
/// The base class for all Azure Sql cmdlets
/// </summary>
public abstract class AzureSqlCmdletBaseBase<M, A> : AzureRMCmdlet
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

haha okay ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My creativity shining through :)

{
/// <summary>
/// Gets or sets the name of the resource group to use.
/// </summary>
[Parameter(Mandatory = false,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you or some comment explaining why BaseBase when you have ResourceGroup param? I understand that's it's for Mandatory=false when looking at this change in isolation, but it won't be obvious when reading this code later

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added a comment.

else
{
results = ModelAdapter.GetServers(this.ResourceGroupName);
throw new PSArgumentException("When specifying the serverName parameter the ResourceGroup parameter must also be used");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you split this into 2 ParamGroups so that it's enforced by powershell logic instead of custom code?
Group 1: Get-AzureRmSqlServer
Group 2: Get-AzureRmSqlServer -ResourceGroupName [- ServerName]

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might be wrong here, not sure if "no parameters" is possible to specify as a parameter set.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought a bit about this, but wasn't sure how to achieve it. According to MSDN-Parameter Set Requirements a parameter set must have at least 1 parameter.

Another option is to allow specifying the ServerName without the resourceGroupName and then just retrieve the list of all servers and filter it down to just the one with the matching name (potentially bad perf)? The underlying API doesn't support retrieving a specific server without the resource group name.

jaredmoo
jaredmoo previously approved these changes Jun 23, 2017
Assert-AreEqual 3 $all2.Count

# It is possible that there were existing servers in the subscription when the test was recorded, so make sure
# that the servers that we created are retrieved and ignore the other ones.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works, but it's a bit confusing to read. Does something like this work?
($server1, $server2, $server3) | foreach { Assert-True $_.ServerName -in $all2.ServerName }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interestingly that does work. I wasn't aware that trying to access a property on an array would return the property value for each object in the array.

@@ -59,87 +36,5 @@ protected virtual string GetResourceId(M model)
HelpMessage = "The name of the resource group.")]
[ValidateNotNullOrEmpty]
public string ResourceGroupName { get; set; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akromm changing ResourceGroupName from mandatory to non-mandatory is a breaking change?
Any reason you want to do this now?
What scenarios are you trying to fix? or is it just ease for user?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shahabhijeet is relaxing the requirements of parameters a breaking change? I'm doing this change because there have been multiple github issues requesting it. eg: #635

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akromm @shahabhijeet changing a parameter from mandatory to non-mandatory is not a breaking change; however, the other way around is a breaking change

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cormacpayne thanks for correcting. I wrote that comment and then a day later realized that is not right what I wrote, but by that time I was not able to come back to GitHub to correct it.

/// <summary>
/// The base class for all Azure Sql cmdlets
/// </summary>
public abstract class AzureSqlCmdletBaseBase<M, A> : AzureRMCmdlet
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akromm any reason there aren't any constraints for M, A?


protected virtual string GetResourceId(M model)
{
var serverProperty = model.GetType().GetProperty("ServerName");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akromm maybe I am missing to see how this will work when model is of type IEnumerable

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shahabhijeet I have updated the method to work with IEnumerable and other single entity types. I have also added a corresponding unittest to ensure it works.


protected virtual string GetResourceId(M model)
{
object m = null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akromm why m is not of type M? Why object?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So that overriding methods in subclasses can use the specific type

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shahabhijeet I think the right thing to do here is make the method Abstract and force each cmdlet to implement it.

To answer your question. It is of type object because M can be IEnumerable and I want an object of type T not M.


if (m != null)
{
var serverProperty = m.GetType().GetProperty("ServerName");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akromm if you make M to some constraint class, you don't have to deal with late bound objects.

{
object m = null;

if (typeof(IEnumerable).IsAssignableFrom(model.GetType()))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akromm you have now covered two scenarios, IEnumerable and M.
Are you sure those are the only two types your consumers are going to pass in?
That is the reason you need to define constraints on M.
And then you should have however many overloads GetResourceId e.g.
GetResourceId(IEumerable listOfModels)
GetResourceId(ICollection modelCollection) just to make my point, but overloads can handle IEnumerable.
Having generic class with no constraint is setting yourself for runtime errors.

{
}

protected virtual string GetResourceId(M model)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akromm plus anyone who overrides and have their own implementation of this method will have to make sure they have safety code for all possible types of M (similar to what you have)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shahabhijeet This would be overridden per cmdlet type (Database, Server, DataMasking, ...) and as such the type M will be well known and there shouldn't be the complex logic I have.

/// </summary>
/// <param name="model">The about to be written model object</param>
/// <returns>The prepared object to be written out</returns>
protected virtual object TransformModelToOutputObject(M model)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akromm any generic class with late bound return types defeats the purpose of your generic class.

Copy link
Contributor Author

@akromm-zz akromm-zz Jun 27, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shahabhijeet
This is only used in a couple places.
Get-AzureRmSqlDatabaseDataMaskingRule cmdlet uses it to filter the results before showing the user.
SqlDatabaseDataMaskingRuleCmdletBase also uses it to do client side filtering of the results.
New-AzureRmSqlDatabase Actually uses it to modify the response slightly.

That being said, all this code is pre-existing code that already existed. I'm just moving it to a new file. I might have a different way to achieve my main goal without moving this stuff around.

/// </summary>
public override void ExecuteCmdlet()
{
ModelAdapter = InitModelAdapter(DefaultProfile.DefaultContext.Subscription);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akromm can you point me where is InitModelAdapter implemented?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shahabhijeet InitModelAdapter is implemented in each individual cmdlet. Database Cmdlet base example

Making ResourceGroupName parameter virtual rather than creating a new base class.
jaredmoo
jaredmoo previously approved these changes Jun 28, 2017
@akromm-zz
Copy link
Contributor Author

@cormacpayne @shahabhijeet @jaredmoo Finally got the tests to pass (issues with dependencies and NuGet package versions). Can I please get this reviewed and merged now :)

Thanks.

@akromm-zz akromm-zz dismissed shahabhijeet’s stale review June 29, 2017 22:37

restructured how the changes are being done.

@shahabhijeet shahabhijeet changed the base branch from preview to release-4.2.0 June 30, 2017 17:41
@shahabhijeet
Copy link
Member

@cormacpayne
Copy link
Member

@azuresdkci test this please

1 similar comment
@shahabhijeet
Copy link
Member

@azuresdkci test this please

@shahabhijeet shahabhijeet merged commit a58a237 into Azure:release-4.2.0 Jun 30, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants