Skip to content
This repository was archived by the owner on Dec 18, 2018. It is now read-only.

Add support for assembly attribute containing user secret id #525

Merged
merged 1 commit into from
Oct 6, 2016

Conversation

natemcmaster
Copy link
Contributor

@natemcmaster natemcmaster commented Oct 4, 2016

After vacillating between approaches, I think we've found a solution that will preserve functionality for project.json users and help us move to an MSBuild world.

This adds API for configuring user secret ID from an assembly attribute, UserSecretsIdAttribute.

When using .AddUserSecrets(), this method will first check Assembly.GetEntryAssembly() for the attribute. If no attribute is present, it will fallback to current behavior of reading project.json.

This also adds a code gen build task. Users can set the <UserSecretsId>xxxx-xxxx</UserSecretsId> property in MSBuild and it will generate UserSecretsIdAttribute on build.

NB: the target of this is a rel/1.0.1 release since we need MSBuild support in LTS.

Part of aspnet/DotNetTools#169

cc @Eilon @glennc @muratg

/// <summary>
/// The user secrets id.
/// </summary>
public string UserSecretsId { get; private set; }
Copy link
Member

Choose a reason for hiding this comment

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

nit: i believe you can just omit the private set; without any change in behavior

@@ -14,6 +14,18 @@ public class PathHelper
internal const string Secrets_File_Name = "secrets.json";
internal const string Config_File_Name = "project.json";

/// <summary>
/// <para>
/// This method is obsolete and will be removed in a future version. The recommended alternative is Microsoft.Extensions.Configuration.UserSecrets.PathHelper.GetSecretsPath(string, string).
Copy link
Member

Choose a reason for hiding this comment

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

Can the "alternative" method be an actual doc link?

{
if (string.IsNullOrEmpty(userSecretId))
{
throw new ArgumentNullException(nameof(userSecretId));
Copy link
Member

Choose a reason for hiding this comment

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

ArgNull is only for null args.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So ArgumentException? ...we should probably clean up a few other places in our code where we do this. 😳

Copy link
Member

Choose a reason for hiding this comment

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

ArgException works, yes. Can you file a bug for other places where we throw the wrong exception?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

},
"dependencies": {
"Microsoft.Extensions.Configuration.Json": "1.0.0"
},
"frameworks": {
"net451": {},
"netstandard1.3": {}
"netstandard1.5": {}
Copy link
Member

Choose a reason for hiding this comment

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

I forget. Is this ok? Or is this a breaking change?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's not a breaking change. All currently supported netstandard platforms will continue to work.

Copy link
Member

Choose a reason for hiding this comment

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

Got it!

@@ -14,5 +14,8 @@
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
Copy link
Member

Choose a reason for hiding this comment

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

Is this something important?

Copy link
Contributor

Choose a reason for hiding this comment

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

VS will keep adding this...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's VS fluff that's required for Test Explorer. Other projects have it already..

public void AddUserSecrets_FindsAssemblyAttribute()
{
var configKey = nameof(AddUserSecrets_FindsAssemblyAttribute);
SetSecret("Microsoft.Extensions.Configuration.UserSecrets.Test", configKey, "true");
Copy link
Member

Choose a reason for hiding this comment

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

Not sure I understand this test. What is "true" and "false" here? Is that the "user secrets id"? I.e. the "application name id" that I would use? If so, can we put a clearer dummy value here, e.g. "MyUserSecretsIdValue" or something?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The secret id and values aren't important to the test. I can make them dummy values if it makes the purpose of the test clearer.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah I had to re-read this several times. The problem I had is that "true" and "false" have actual meanings. Changing these to be more meaningless, but perhaps also to be clearer about their semantic meaning, could certainly make this easier to understand.


Assert.Equal("true", config[configKey]);

SetSecret("Microsoft.Extensions.Configuration.UserSecrets.Test", configKey, "false");
Copy link
Member

Choose a reason for hiding this comment

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

Why is this setting the value again? Is it trying to "undo" the change? Because if so, this isn't safe - the file could be left dirty if this test fails.

var ex = Assert.Throws<InvalidOperationException>(() => builder.AddUserSecrets());
Assert.Equal(Resources.FormatError_Missing_UserSecretsIdAttribute(Assembly.GetEntryAssembly().FullName), ex.Message);

UserSecretHelper.DeleteTempSecretProject(projectPath);
Copy link
Member

Choose a reason for hiding this comment

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

Is this also cleanup code? This isn't reliable - the test might not get here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

...yeah, I know. I've already fixed up the 1.1 branch.

@@ -0,0 +1,44 @@
<!--
Copy link
Member

Choose a reason for hiding this comment

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

This file is beautiful. I just wanted to say that 😄

Copy link
Contributor Author

@natemcmaster natemcmaster Oct 4, 2016

Choose a reason for hiding this comment

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

XML is as much an art as it is a language. 🎨

Copy link
Member

Choose a reason for hiding this comment

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

Which begs the question: Why are .resx files so ugly? 😈

@natemcmaster
Copy link
Contributor Author

🆙 📅

throw new ArgumentNullException(nameof(assembly));
}

var attribute = assembly.GetCustomAttribute<UserSecretsIdAttribute>();
Copy link

Choose a reason for hiding this comment

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

Did you consider extracting common code between this method and the one above?

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, could be done, but as I was implementing it didn't seem like it would make it any cleaner.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah I had a similar thought at first, but I couldn't come up with any meaningful way to refactor. The methods are similar, but there's not much actual common code.

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 found one simplification: created a private method for constructing the exception message. I couldn't find other useful reductions.

@@ -19,7 +19,7 @@ public UserSecretsIdAttribute(string userSecretId)
{
if (string.IsNullOrEmpty(userSecretId))
{
throw new ArgumentNullException(nameof(userSecretId));
throw new ArgumentException(nameof(userSecretId));
Copy link
Member

Choose a reason for hiding this comment

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

This now needs a proper message. We often have a Common_StringNullOrEmpty message that we use everywhere. Otherwise this exception has little meaning because it doesn't say what was invalid.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated to include error message.

{
while (_disposables.Count > 0)
{
_disposables.Pop()?.Invoke();
Copy link
Member

Choose a reason for hiding this comment

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

Pop() never returns null here, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In theory it could return null. In current implementation, it doesn't.

Copy link
Member

Choose a reason for hiding this comment

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

In theory lots of things can happen 😄 Why would it ever be null in any implementation? Presumably we'd only push stuff if there's an actual action to push? It's of course safe to leave it the way it is, though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fair enough. Updated :)

@natemcmaster
Copy link
Contributor Author

cc @abpiskunov

@natemcmaster natemcmaster force-pushed the namc/usersecrets-msbuild branch from 6fd21d9 to ff2b07c Compare October 5, 2016 23:28
@natemcmaster natemcmaster force-pushed the namc/usersecrets-msbuild branch from ff2b07c to 9ce21a6 Compare October 6, 2016 03:16
@natemcmaster natemcmaster changed the base branch from namc/usersecrets-1.0 to rel/1.0.1 October 6, 2016 17:09
@natemcmaster natemcmaster force-pushed the namc/usersecrets-msbuild branch from 9ce21a6 to 25d9ec8 Compare October 6, 2016 18:30
@natemcmaster natemcmaster merged commit 25d9ec8 into rel/1.0.1 Oct 6, 2016
@natemcmaster natemcmaster deleted the namc/usersecrets-msbuild branch October 6, 2016 18:40
natemcmaster pushed a commit to aspnet/DotNetTools that referenced this pull request Oct 6, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants