Browse Source

Add support for JSON output to more commands (#45383)

* Add support for JSON output to more commands

* Tweak test
Safia Abdalla 3 years ago
parent
commit
a0244b2ed8

+ 1 - 6
src/Tools/dotnet-user-jwts/src/Commands/CreateCommand.cs

@@ -77,11 +77,6 @@ internal sealed class CreateCommand
                 Resources.CreateCommand_ValidForOption_Description,
                 CommandOptionType.SingleValue);
 
-            var outputOption = cmd.Option(
-                "-o|--output",
-                Resources.CreateCommand_OutputOption_Description,
-                CommandOptionType.SingleValue);
-
             cmd.HelpOption("-h|--help");
 
             cmd.OnExecute(() =>
@@ -94,7 +89,7 @@ internal sealed class CreateCommand
                     return 1;
                 }
 
-                return Execute(cmd.Reporter, cmd.ProjectOption.Value(), options, optionsString, outputOption.Value(), program);
+                return Execute(cmd.Reporter, cmd.ProjectOption.Value(), options, optionsString, cmd.OutputOption.Value(), program);
             });
         });
     }

+ 30 - 3
src/Tools/dotnet-user-jwts/src/Commands/ListCommand.cs

@@ -3,6 +3,7 @@
 
 using Microsoft.Extensions.CommandLineUtils;
 using Microsoft.Extensions.Tools.Internal;
+using System.Text.Json;
 
 namespace Microsoft.AspNetCore.Authentication.JwtBearer.Tools;
 
@@ -23,12 +24,12 @@ internal sealed class ListCommand
 
             cmd.OnExecute(() =>
             {
-                return Execute(cmd.Reporter, cmd.ProjectOption.Value(), showTokensOption.HasValue());
+                return Execute(cmd.Reporter, cmd.ProjectOption.Value(), showTokensOption.HasValue(), cmd.OutputOption.Value());
             });
         });
     }
 
-    private static int Execute(IReporter reporter, string projectPath, bool showTokens)
+    private static int Execute(IReporter reporter, string projectPath, bool showTokens, string outputFormat)
     {
         if (!DevJwtCliHelpers.GetProjectAndSecretsId(projectPath, reporter, out var project, out var userSecretsId))
         {
@@ -36,6 +37,33 @@ internal sealed class ListCommand
         }
         var jwtStore = new JwtStore(userSecretsId);
 
+        switch (outputFormat)
+        {
+            case "json":
+                PrintJwtsJson(reporter, jwtStore);
+                break;
+            default:
+                PrintJwtsDefault(reporter, project, userSecretsId, jwtStore, showTokens);
+                break;
+        }
+
+        return 0;
+    }
+
+    private static void PrintJwtsJson(IReporter reporter, JwtStore jwtStore)
+    {
+        if (jwtStore.Jwts is { Count: > 0 } jwts)
+        {
+            reporter.Output(JsonSerializer.Serialize(jwtStore.Jwts, new JsonSerializerOptions { WriteIndented = true }));
+        }
+        else
+        {
+            reporter.Output(JsonSerializer.Serialize(Array.Empty<Jwt>(), new JsonSerializerOptions { WriteIndented = true }));
+        }
+    }
+
+    private static void PrintJwtsDefault(IReporter reporter, string project, string userSecretsId, JwtStore jwtStore, bool showTokens)
+    {
         reporter.Output(Resources.FormatListCommand_Project(project));
         reporter.Output(Resources.FormatListCommand_UserSecretsId(userSecretsId));
 
@@ -69,6 +97,5 @@ internal sealed class ListCommand
             reporter.Output(Resources.ListCommand_NoJwts);
         }
 
-        return 0;
     }
 }

+ 4 - 4
src/Tools/dotnet-user-jwts/src/Commands/PrintCommand.cs

@@ -30,12 +30,13 @@ internal sealed class PrintCommand
                     cmd.Reporter,
                     cmd.ProjectOption.Value(),
                     idArgument.Value,
-                    showAllOption.HasValue());
+                    showAllOption.HasValue(),
+                    cmd.OutputOption.Value());
             });
         });
     }
 
-    private static int Execute(IReporter reporter, string projectPath, string id, bool showAll)
+    private static int Execute(IReporter reporter, string projectPath, string id, bool showAll, string outputFormat)
     {
         if (!DevJwtCliHelpers.GetProjectAndSecretsId(projectPath, reporter, out var _, out var userSecretsId))
         {
@@ -49,9 +50,8 @@ internal sealed class PrintCommand
             return 1;
         }
 
-        reporter.Output(Resources.FormatPrintCommand_Confirmed(id));
         JwtSecurityToken fullToken = JwtIssuer.Extract(jwt.Token);
-        DevJwtCliHelpers.PrintJwt(reporter, jwt, showAll, fullToken);
+        DevJwtCliHelpers.PrintJwt(reporter, jwt, showAll, outputFormat, fullToken);
 
         return 0;
     }

+ 8 - 0
src/Tools/dotnet-user-jwts/src/Commands/ProjectCommandLineApplication.cs

@@ -10,6 +10,8 @@ internal sealed class ProjectCommandLineApplication : CommandLineApplication
 {
     public CommandOption ProjectOption { get; private set; }
 
+    public CommandOption OutputOption { get; private set; }
+
     public IReporter Reporter { get; private set; }
 
     public ProjectCommandLineApplication(IReporter reporter, bool throwOnUnexpectedArg = true, bool continueAfterUnexpectedArg = false, bool treatUnmatchedOptionsAsArguments = false)
@@ -19,6 +21,12 @@ internal sealed class ProjectCommandLineApplication : CommandLineApplication
             "-p|--project",
             Resources.ProjectOption_Description,
             CommandOptionType.SingleValue);
+
+        OutputOption = Option(
+            "-o|--output",
+            Resources.CreateCommand_OutputOption_Description,
+            CommandOptionType.SingleValue);
+
         Reporter = reporter;
     }
 

+ 54 - 32
src/Tools/dotnet-user-jwts/src/Helpers/DevJwtCliHelpers.cs

@@ -143,48 +143,70 @@ internal static class DevJwtCliHelpers
         }
     }
 
-    public static void PrintJwt(IReporter reporter, Jwt jwt, bool showAll, JwtSecurityToken fullToken = null)
+    public static void PrintJwt(IReporter reporter, Jwt jwt, bool showAll, string outputFormat, JwtSecurityToken fullToken = null)
     {
-        reporter.Output($"{Resources.JwtPrint_Id}: {jwt.Id}");
-        reporter.Output($"{Resources.JwtPrint_Name}: {jwt.Name}");
-        reporter.Output($"{Resources.JwtPrint_Scheme}: {jwt.Scheme}");
-        reporter.Output($"{Resources.JwtPrint_Audiences}: {jwt.Audience}");
-        reporter.Output($"{Resources.JwtPrint_NotBefore}: {jwt.NotBefore:O}");
-        reporter.Output($"{Resources.JwtPrint_ExpiresOn}: {jwt.Expires:O}");
-        reporter.Output($"{Resources.JwtPrint_IssuedOn}: {jwt.Issued:O}");
-
-        if (!jwt.Scopes.IsNullOrEmpty() || showAll)
+        switch (outputFormat)
         {
-            var scopesValue = jwt.Scopes.IsNullOrEmpty()
-                ? "none"
-                : string.Join(", ", jwt.Scopes);
-            reporter.Output($"{Resources.JwtPrint_Scopes}: {scopesValue}");
+            case "token":
+                reporter.Output(fullToken is not null ? jwt.Token : string.Empty);
+                break;
+            case "json":
+                PrintJwtJson(reporter, jwt, showAll, fullToken);
+                break;
+            default:
+                PrintJwtDefault(reporter, jwt, showAll, fullToken);
+                break;
         }
 
-        if (!jwt.Roles.IsNullOrEmpty() || showAll)
+        static void PrintJwtJson(IReporter reporter, Jwt jwt, bool showAll, JwtSecurityToken fullToken)
         {
-            var rolesValue = jwt.Roles.IsNullOrEmpty()
-                ? "none"
-                : string.Join(", ", jwt.Roles);
-            reporter.Output($"{Resources.JwtPrint_Roles}: [{rolesValue}]");
+            reporter.Output(JsonSerializer.Serialize(jwt, new JsonSerializerOptions { WriteIndented = true }));
         }
 
-        if (!jwt.CustomClaims.IsNullOrEmpty() || showAll)
+        static void PrintJwtDefault(IReporter reporter, Jwt jwt, bool showAll, JwtSecurityToken fullToken)
         {
-            var customClaimsValue = jwt.CustomClaims.IsNullOrEmpty()
-                ? "none"
-                : string.Join(", ", jwt.CustomClaims.Select(kvp => $"{kvp.Key}={kvp.Value}"));
-            reporter.Output($"{Resources.JwtPrint_CustomClaims}: [{customClaimsValue}]");
-        }
+            reporter.Output(Resources.FormatPrintCommand_Confirmed(jwt.Id));
+            reporter.Output($"{Resources.JwtPrint_Id}: {jwt.Id}");
+            reporter.Output($"{Resources.JwtPrint_Name}: {jwt.Name}");
+            reporter.Output($"{Resources.JwtPrint_Scheme}: {jwt.Scheme}");
+            reporter.Output($"{Resources.JwtPrint_Audiences}: {jwt.Audience}");
+            reporter.Output($"{Resources.JwtPrint_NotBefore}: {jwt.NotBefore:O}");
+            reporter.Output($"{Resources.JwtPrint_ExpiresOn}: {jwt.Expires:O}");
+            reporter.Output($"{Resources.JwtPrint_IssuedOn}: {jwt.Issued:O}");
+
+            if (!jwt.Scopes.IsNullOrEmpty() || showAll)
+            {
+                var scopesValue = jwt.Scopes.IsNullOrEmpty()
+                    ? "none"
+                    : string.Join(", ", jwt.Scopes);
+                reporter.Output($"{Resources.JwtPrint_Scopes}: {scopesValue}");
+            }
 
-        if (showAll)
-        {
-            reporter.Output($"{Resources.JwtPrint_TokenHeader}: {fullToken.Header.SerializeToJson()}");
-            reporter.Output($"{Resources.JwtPrint_TokenPayload}: {fullToken.Payload.SerializeToJson()}");
-        }
+            if (!jwt.Roles.IsNullOrEmpty() || showAll)
+            {
+                var rolesValue = jwt.Roles.IsNullOrEmpty()
+                    ? "none"
+                    : string.Join(", ", jwt.Roles);
+                reporter.Output($"{Resources.JwtPrint_Roles}: [{rolesValue}]");
+            }
 
-        var tokenValueFieldName = showAll ? Resources.JwtPrint_CompactToken : Resources.JwtPrint_Token;
-        reporter.Output($"{tokenValueFieldName}: {jwt.Token}");
+            if (!jwt.CustomClaims.IsNullOrEmpty() || showAll)
+            {
+                var customClaimsValue = jwt.CustomClaims.IsNullOrEmpty()
+                    ? "none"
+                    : string.Join(", ", jwt.CustomClaims.Select(kvp => $"{kvp.Key}={kvp.Value}"));
+                reporter.Output($"{Resources.JwtPrint_CustomClaims}: [{customClaimsValue}]");
+            }
+
+            if (showAll)
+            {
+                reporter.Output($"{Resources.JwtPrint_TokenHeader}: {fullToken.Header.SerializeToJson()}");
+                reporter.Output($"{Resources.JwtPrint_TokenPayload}: {fullToken.Payload.SerializeToJson()}");
+            }
+
+            var tokenValueFieldName = showAll ? Resources.JwtPrint_CompactToken : Resources.JwtPrint_Token;
+            reporter.Output($"{tokenValueFieldName}: {jwt.Token}");
+        }
     }
 
     public static bool TryParseClaims(List<string> input, out Dictionary<string, string> claims)

+ 54 - 1
src/Tools/dotnet-user-jwts/test/UserJwtsTests.cs

@@ -94,7 +94,6 @@ public class UserJwtsTests : IClassFixture<UserJwtsTestFixture>
     public void List_ReturnsIdForGeneratedToken()
     {
         var project = Path.Combine(_fixture.CreateProject(), "TestProject.csproj");
-        var appsettings = Path.Combine(Path.GetDirectoryName(project), "appsettings.Development.json");
         var app = new Program(_console);
 
         app.Run(new[] { "create", "--project", project, "--scheme", "MyCustomScheme" });
@@ -104,6 +103,40 @@ public class UserJwtsTests : IClassFixture<UserJwtsTestFixture>
         Assert.Contains("MyCustomScheme", _console.GetOutput());
     }
 
+    [Fact]
+    public void List_ReturnsIdForGeneratedToken_WithJsonFormat()
+    {
+        var schemeName = "MyCustomScheme";
+        var project = Path.Combine(_fixture.CreateProject(), "TestProject.csproj");
+        var app = new Program(_console);
+
+        app.Run(new[] { "create", "--project", project, "--scheme", schemeName });
+        var matches = Regex.Matches(_console.GetOutput(), "New JWT saved with ID '(.*?)'");
+        var id = matches.SingleOrDefault().Groups[1].Value;
+        _console.ClearOutput();
+
+        app.Run(new[] { "list", "--project", project, "--output", "json" });
+        var output = _console.GetOutput();
+        var deserialized = JsonSerializer.Deserialize<Dictionary<string, Jwt>>(output);
+
+        var jwt = deserialized[id];
+
+        Assert.NotNull(deserialized);
+        Assert.Equal(schemeName, jwt.Scheme);
+    }
+
+    [Fact]
+    public void List_ReturnsEmptyListWhenNoTokens_WithJsonFormat()
+    {
+        var project = Path.Combine(_fixture.CreateProject(), "TestProject.csproj");
+        var app = new Program(_console);
+
+        app.Run(new[] { "list", "--project", project, "--output", "json" });
+        var output = _console.GetOutput();
+
+        Assert.Equal("[]", output.Trim());
+    }
+
     [Fact]
     public void Remove_RemovesGeneratedToken()
     {
@@ -269,6 +302,26 @@ public class UserJwtsTests : IClassFixture<UserJwtsTestFixture>
         Assert.Contains($"Audience(s): http://localhost:23528, https://localhost:44395, https://localhost:5001, http://localhost:5000", output);
     }
 
+    [Fact]
+    public void PrintCommand_ShowsBasicOptions_WithJsonFormat()
+    {
+        var project = Path.Combine(_fixture.CreateProject(), "TestProject.csproj");
+        var app = new Program(_console);
+
+        app.Run(new[] { "create", "--project", project });
+        var matches = Regex.Matches(_console.GetOutput(), "New JWT saved with ID '(.*?)'");
+        var id = matches.SingleOrDefault().Groups[1].Value;
+        _console.ClearOutput();
+
+        app.Run(new[] { "print", id, "--project", project, "--output", "json" });
+        var output = _console.GetOutput();
+        var deserialized = JsonSerializer.Deserialize<Jwt>(output);
+
+        Assert.Equal(Environment.UserName, deserialized.Name);
+        Assert.Equal(DevJwtsDefaults.Scheme, deserialized.Scheme);
+        Assert.Equal($"http://localhost:23528, https://localhost:44395, https://localhost:5001, http://localhost:5000", deserialized.Audience);
+    }
+
     [Fact]
     public void PrintCommand_ShowsCustomizedOptions()
     {