Skip to content

Commit 0daf783

Browse files
committed
Add a recursive parameter to the add-files command
Add a recursive parameter to the dotnet-grpc add-files CLI command. The new parameter will add files in the requested folder recursively. A unit test which verifies the functionality has been added.
1 parent 03d9b01 commit 0daf783

File tree

8 files changed

+78
-14
lines changed

8 files changed

+78
-14
lines changed

src/dotnet-grpc/Commands/AddFileCommand.cs

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#region Copyright notice and license
1+
#region Copyright notice and license
22

33
// Copyright 2019 The gRPC Authors
44
//
@@ -41,6 +41,7 @@ public static Command Create(HttpClient httpClient)
4141
var serviceOption = CommonOptions.ServiceOption();
4242
var additionalImportDirsOption = CommonOptions.AdditionalImportDirsOption();
4343
var accessOption = CommonOptions.AccessOption();
44+
var recursiveOption = CommonOptions.RecursiveOption();
4445
var filesArgument = new Argument<string[]>
4546
{
4647
Name = "files",
@@ -52,6 +53,7 @@ public static Command Create(HttpClient httpClient)
5253
command.AddOption(serviceOption);
5354
command.AddOption(accessOption);
5455
command.AddOption(additionalImportDirsOption);
56+
command.AddOption(recursiveOption);
5557
command.AddArgument(filesArgument);
5658

5759
command.SetHandler(
@@ -61,12 +63,13 @@ public static Command Create(HttpClient httpClient)
6163
var services = context.ParseResult.GetValueForOption(serviceOption);
6264
var access = context.ParseResult.GetValueForOption(accessOption);
6365
var additionalImportDirs = context.ParseResult.GetValueForOption(additionalImportDirsOption);
66+
var searchOption = context.ParseResult.GetValueForOption(recursiveOption) ? SearchOption.TopDirectoryOnly : SearchOption.TopDirectoryOnly;
6467
var files = context.ParseResult.GetValueForArgument(filesArgument);
6568

6669
try
6770
{
6871
var command = new AddFileCommand(context.Console, project, httpClient);
69-
await command.AddFileAsync(services, access, additionalImportDirs, files);
72+
await command.AddFileAsync(services, access, additionalImportDirs, files, searchOption);
7073

7174
context.ExitCode = 0;
7275
}
@@ -81,11 +84,11 @@ public static Command Create(HttpClient httpClient)
8184
return command;
8285
}
8386

84-
public async Task AddFileAsync(Services services, Access access, string? additionalImportDirs, string[] files)
87+
public async Task AddFileAsync(Services services, Access access, string? additionalImportDirs, string[] files, SearchOption searchOption)
8588
{
8689
var resolvedServices = ResolveServices(services);
8790
await EnsureNugetPackagesAsync(resolvedServices);
88-
files = GlobReferences(files);
91+
files = GlobReferences(files, searchOption);
8992

9093
foreach (var file in files)
9194
{

src/dotnet-grpc/Commands/CommandBase.cs

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#region Copyright notice and license
1+
#region Copyright notice and license
22

33
// Copyright 2019 The gRPC Authors
44
//
@@ -174,7 +174,7 @@ public void AddProtobufReference(Services services, string? additionalImportDirs
174174
}
175175

176176
var normalizedFile = NormalizePath(file);
177-
177+
178178
var normalizedAdditionalImportDirs = string.Empty;
179179

180180
if (!string.IsNullOrWhiteSpace(additionalImportDirs))
@@ -298,7 +298,7 @@ public IEnumerable<ProjectItem> ResolveReferences(string[] references)
298298
return resolvedReferences;
299299
}
300300

301-
internal string[] GlobReferences(string[] references)
301+
internal string[] GlobReferences(string[] references, SearchOption searchOption = SearchOption.TopDirectoryOnly)
302302
{
303303
var expandedReferences = new List<string>();
304304

@@ -315,7 +315,7 @@ internal string[] GlobReferences(string[] references)
315315
var directoryToSearch = Path.GetPathRoot(reference)!;
316316
var searchPattern = reference.Substring(directoryToSearch.Length);
317317

318-
var resolvedFiles = Directory.GetFiles(directoryToSearch, searchPattern);
318+
var resolvedFiles = Directory.GetFiles(directoryToSearch, searchPattern, searchOption);
319319

320320
if (resolvedFiles.Length == 0)
321321
{
@@ -328,7 +328,7 @@ internal string[] GlobReferences(string[] references)
328328

329329
if (Directory.Exists(Path.Combine(Project.DirectoryPath, Path.GetDirectoryName(reference)!)))
330330
{
331-
var resolvedFiles = Directory.GetFiles(Project.DirectoryPath, reference);
331+
var resolvedFiles = Directory.GetFiles(Project.DirectoryPath, reference, searchOption);
332332

333333
if (resolvedFiles.Length == 0)
334334
{

src/dotnet-grpc/Options/CommonOptions.cs

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#region Copyright notice and license
1+
#region Copyright notice and license
22

33
// Copyright 2019 The gRPC Authors
44
//
@@ -54,4 +54,13 @@ public static Option<string> AdditionalImportDirsOption()
5454
description: CoreStrings.AdditionalImportDirsOption);
5555
return o;
5656
}
57+
58+
public static Option<bool> RecursiveOption()
59+
{
60+
var o = new Option<bool>(
61+
aliases: new[] { "-r", "--recursive" },
62+
description: CoreStrings.RecursiveOptionDescription
63+
);
64+
return o;
65+
}
5766
}

src/dotnet-grpc/Properties/CoreStrings.Designer.cs

+10-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/dotnet-grpc/Properties/CoreStrings.resx

+3
Original file line numberDiff line numberDiff line change
@@ -231,4 +231,7 @@
231231
<data name="LogNoReferences" xml:space="preserve">
232232
<value>No protobuf references in the gRPC project.</value>
233233
</data>
234+
<data name="RecursiveOptionDescription" xml:space="preserve">
235+
<value>Whether to add the protobuf file references recursively. Default value is false.</value>
236+
</data>
234237
</root>

test/dotnet-grpc.Tests/AddFileCommandTests.cs

+40-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#region Copyright notice and license
1+
#region Copyright notice and license
22

33
// Copyright 2019 The gRPC Authors
44
//
@@ -81,19 +81,56 @@ public async Task AddFileCommand_AddsPackagesAndReferences()
8181
// Act
8282
Directory.SetCurrentDirectory(tempDir);
8383
var command = new AddFileCommand(new TestConsole(), projectPath: null, CreateClient());
84-
await command.AddFileAsync(Services.Server, Access.Internal, "ImportDir", new[] { Path.Combine("Proto", "*.proto") });
84+
await command.AddFileAsync(Services.Server, Access.Internal, "ImportDir", new[] { Path.Combine("Proto", "*.proto") }, SearchOption.TopDirectoryOnly);
8585
command.Project.ReevaluateIfNecessary();
8686

8787
// Assert
8888
var packageRefs = command.Project.GetItems(CommandBase.PackageReferenceElement);
8989
Assert.AreEqual(1, packageRefs.Count);
9090
Assert.NotNull(packageRefs.SingleOrDefault(r => r.UnevaluatedInclude == "Grpc.AspNetCore" && !r.HasMetadata(CommandBase.PrivateAssetsElement)));
9191

92-
9392
var protoRefs = command.Project.GetItems(CommandBase.ProtobufElement);
9493
Assert.AreEqual(2, protoRefs.Count);
9594
Assert.NotNull(protoRefs.SingleOrDefault(r => r.UnevaluatedInclude == "Proto\\a.proto"));
9695
Assert.NotNull(protoRefs.SingleOrDefault(r => r.UnevaluatedInclude == "Proto\\b.proto"));
96+
Assert.Null(protoRefs.SingleOrDefault(r => r.UnevaluatedInclude == "Proto\\Subfolder\\c.proto"));
97+
foreach (var protoRef in protoRefs)
98+
{
99+
Assert.AreEqual("Server", protoRef.GetMetadataValue(CommandBase.GrpcServicesElement));
100+
Assert.AreEqual("ImportDir", protoRef.GetMetadataValue(CommandBase.AdditionalImportDirsElement));
101+
Assert.AreEqual("Internal", protoRef.GetMetadataValue(CommandBase.AccessElement));
102+
}
103+
104+
// Cleanup
105+
Directory.SetCurrentDirectory(currentDir);
106+
Directory.Delete(tempDir, true);
107+
}
108+
109+
[Test]
110+
[NonParallelizable]
111+
public async Task AddFileCommand_AddsPackagesAndReferencesRecursively()
112+
{
113+
// Arrange
114+
var currentDir = Directory.GetCurrentDirectory();
115+
var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
116+
new DirectoryInfo(Path.Combine(currentDir, "TestAssets", "EmptyProject")).CopyTo(tempDir);
117+
118+
// Act
119+
Directory.SetCurrentDirectory(tempDir);
120+
var command = new AddFileCommand(new TestConsole(), projectPath: null, CreateClient());
121+
await command.AddFileAsync(Services.Server, Access.Internal, "ImportDir", new[] { Path.Combine("Proto", "*.proto") }, SearchOption.AllDirectories);
122+
command.Project.ReevaluateIfNecessary();
123+
124+
// Assert
125+
var packageRefs = command.Project.GetItems(CommandBase.PackageReferenceElement);
126+
Assert.AreEqual(1, packageRefs.Count);
127+
Assert.NotNull(packageRefs.SingleOrDefault(r => r.UnevaluatedInclude == "Grpc.AspNetCore" && !r.HasMetadata(CommandBase.PrivateAssetsElement)));
128+
129+
var protoRefs = command.Project.GetItems(CommandBase.ProtobufElement);
130+
Assert.AreEqual(3, protoRefs.Count);
131+
Assert.NotNull(protoRefs.SingleOrDefault(r => r.UnevaluatedInclude == "Proto\\a.proto"));
132+
Assert.NotNull(protoRefs.SingleOrDefault(r => r.UnevaluatedInclude == "Proto\\b.proto"));
133+
Assert.NotNull(protoRefs.SingleOrDefault(r => r.UnevaluatedInclude == "Proto\\Subfolder\\c.proto"));
97134
foreach (var protoRef in protoRefs)
98135
{
99136
Assert.AreEqual("Server", protoRef.GetMetadataValue(CommandBase.GrpcServicesElement));

test/dotnet-grpc.Tests/TestAssets/EmptyProject/Proto/Subfolder/c.proto

Whitespace-only changes.

test/dotnet-grpc.Tests/dotnet-grpc.Tests.csproj

+3
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,8 @@
5959
</AssemblyAttribute>
6060

6161
</ItemGroup>
62+
<ItemGroup>
63+
<Folder Include="TestAssets\EmptyProject\Proto\Subfolder\" />
64+
</ItemGroup>
6265

6366
</Project>

0 commit comments

Comments
 (0)