Skip to content
This repository was archived by the owner on Oct 31, 2025. It is now read-only.

Commit 89400c3

Browse files
committed
Refactor, start unit testing
1 parent 50c745a commit 89400c3

File tree

12 files changed

+367
-86
lines changed

12 files changed

+367
-86
lines changed
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Reflection;
5+
using LocalStack.AwsLocal.Contracts;
6+
using LocalStack.AwsLocal.EnvironmentContext;
7+
using LocalStack.AwsLocal.Extensions;
8+
using LocalStack.Client.Contracts;
9+
using LocalStack.Client.Models;
10+
11+
namespace LocalStack.AwsLocal
12+
{
13+
public class CommandDispatcher
14+
{
15+
private const string UsageResource = "LocalStack.AwsLocal.Docs.Usage.txt";
16+
17+
private readonly IProcessHelper _processHelper;
18+
private readonly IConfig _config;
19+
private readonly TextWriter _textWriter;
20+
private readonly string[] _args;
21+
22+
private CommandDispatcher()
23+
{
24+
}
25+
26+
public CommandDispatcher(IProcessHelper processHelper, IConfig config, TextWriter textWriter, string[] args)
27+
{
28+
_processHelper = processHelper;
29+
_config = config;
30+
_textWriter = textWriter;
31+
_args = args;
32+
}
33+
34+
public void Run()
35+
{
36+
if (_args.Length == 0 || (_args[0] == "-h"))
37+
{
38+
string usageInfo = GetUsageInfo();
39+
_textWriter.WriteLine(usageInfo);
40+
EnvironmentControl.Current.Exit(0);
41+
return;
42+
}
43+
44+
string serviceName = _args.ExtractServiceName();
45+
46+
if (string.IsNullOrEmpty(serviceName))
47+
{
48+
_textWriter.WriteLine("ERROR: Invalid argument, please enter a valid aws cli command");
49+
EnvironmentControl.Current.Exit(1);
50+
return;
51+
}
52+
53+
AwsServiceEndpoint awsServiceEndpoint = _config.GetServiceEndpoint(serviceName);
54+
55+
if (awsServiceEndpoint == null)
56+
{
57+
_textWriter.WriteLine($"ERROR: Unable to find LocalStack endpoint for service {serviceName}");
58+
EnvironmentControl.Current.Exit(1);
59+
return;
60+
}
61+
62+
string cliCommand = _args.GetCliCommand(awsServiceEndpoint.ServiceUrl);
63+
64+
string awsDefaultRegion = Environment.GetEnvironmentVariable("AWS_DEFAULT_REGION") ?? "us-east-1";
65+
string awsAccessKeyId = Environment.GetEnvironmentVariable("AWS_ACCESS_KEY_ID") ?? "_not_needed_locally_";
66+
string awsSecretAccessKey = Environment.GetEnvironmentVariable("AWS_SECRET_ACCESS_KEY") ?? "_not_needed_locally_";
67+
68+
_processHelper.CmdExecute(cliCommand, null, true, true, new Dictionary<string, string>
69+
{
70+
{"AWS_DEFAULT_REGION", awsDefaultRegion},
71+
{"AWS_ACCESS_KEY_ID", awsAccessKeyId},
72+
{"AWS_SECRET_ACCESS_KEY", awsSecretAccessKey}
73+
});
74+
}
75+
76+
private static string GetUsageInfo()
77+
{
78+
using (Stream stream = Assembly.GetCallingAssembly().GetManifestResourceStream(UsageResource))
79+
{
80+
using (var reader = new StreamReader(stream))
81+
{
82+
string result = reader.ReadToEnd();
83+
84+
return result;
85+
}
86+
}
87+
}
88+
}
89+
}

src/LocalStack.AwsLocal/Contracts/IProcessHelper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace LocalStack.AwsLocal.Contracts
44
{
5-
internal interface IProcessHelper
5+
public interface IProcessHelper
66
{
77
int CmdExecute(string command, string workingDirectoryPath, bool output = true, bool waitForExit = true, IDictionary<string, string> environmentVariables = null);
88
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System;
2+
3+
namespace LocalStack.AwsLocal.EnvironmentContext
4+
{
5+
public class DefaultEnvironmentControl : EnvironmentControl
6+
{
7+
private static readonly Lazy<DefaultEnvironmentControl> LazyInstance = new Lazy<DefaultEnvironmentControl>(() => new DefaultEnvironmentControl());
8+
9+
public override void Exit(int value)
10+
{
11+
Environment.Exit(value);
12+
}
13+
14+
public static DefaultEnvironmentControl Instance => LazyInstance.Value;
15+
}
16+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System;
2+
3+
namespace LocalStack.AwsLocal.EnvironmentContext
4+
{
5+
public abstract class EnvironmentControl
6+
{
7+
private static EnvironmentControl _current = DefaultEnvironmentControl.Instance;
8+
9+
public static EnvironmentControl Current
10+
{
11+
get => _current;
12+
13+
set => _current = value ?? throw new ArgumentNullException(nameof(value));
14+
}
15+
16+
public abstract void Exit(int value);
17+
18+
public static void ResetToDefault()
19+
{
20+
_current = DefaultEnvironmentControl.Instance;
21+
}
22+
}
23+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
4+
namespace LocalStack.AwsLocal.Extensions
5+
{
6+
public static class ArgumentExtensions
7+
{
8+
public static string ExtractServiceName(this IEnumerable<string> args)
9+
{
10+
foreach (string arg in args)
11+
{
12+
if (arg.StartsWith('-'))
13+
{
14+
continue;
15+
}
16+
17+
return arg == "s3api" ? "s3" : arg;
18+
}
19+
20+
return string.Empty;
21+
}
22+
23+
public static string GetCliCommand(this IEnumerable<string> args, string serviceUrl)
24+
{
25+
var arguments = args.ToList();
26+
arguments.Insert(0, "aws");
27+
arguments.Insert(1, $"--endpoint-url={serviceUrl}");
28+
29+
if (serviceUrl.StartsWith("https"))
30+
{
31+
arguments.Insert(2, "--no-verify-ssl");
32+
}
33+
34+
return string.Join(' ', arguments);
35+
}
36+
}
37+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System.Linq;
2+
using LocalStack.Client.Contracts;
3+
using LocalStack.Client.Models;
4+
5+
namespace LocalStack.AwsLocal.Extensions
6+
{
7+
public static class ConfigExtensions
8+
{
9+
public static AwsServiceEndpoint GetServiceEndpoint(this IConfig config, string serviceName)
10+
{
11+
var awsServiceEndpoints = config.GetAwsServiceEndpoints();
12+
13+
return awsServiceEndpoints.SingleOrDefault(endpoint => endpoint.CliName == serviceName);
14+
}
15+
}
16+
}

src/LocalStack.AwsLocal/Program.cs

Lines changed: 6 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,100 +1,21 @@
1-
using LocalStack.AwsLocal.Contracts;
2-
using LocalStack.Client;
3-
using LocalStack.Client.Contracts;
4-
using LocalStack.Client.Models;
1+
using LocalStack.Client;
52
using System;
6-
using System.Collections.Generic;
7-
using System.IO;
8-
using System.Linq;
9-
using System.Reflection;
103

114
namespace LocalStack.AwsLocal
125
{
136
internal static class Program
147
{
15-
private const string UsageResource = "LocalStack.AwsLocal.Docs.Usage.txt";
16-
17-
private static readonly IProcessHelper ProcessHelper = new ProcessHelper();
188
private static readonly string LocalStackHost = Environment.GetEnvironmentVariable("LOCALSTACK_HOST");
19-
private static readonly IConfig Config = new Config(LocalStackHost);
20-
21-
private static IEnumerable<string> Args { get; set; }
229

2310
private static void Main(string[] args)
2411
{
25-
Args = args;
26-
27-
if (args.Length == 0 || (args[0] == "-h"))
28-
{
29-
Usage();
30-
}
31-
32-
(string service, AwsServiceEndpoint awsServiceEndpoint) = GetServiceEndpoint();
33-
34-
if (awsServiceEndpoint == null)
35-
{
36-
Console.WriteLine($"ERROR: Unable to find LocalStack endpoint for service {service}");
37-
Environment.Exit(1);
38-
}
39-
40-
var arguments = args.ToList();
41-
arguments.Insert(0, "aws");
42-
arguments.Insert(1, $"--endpoint-url={awsServiceEndpoint.ServiceUrl}");
43-
44-
if (awsServiceEndpoint.Host.Contains("https"))
45-
{
46-
arguments.Insert(2, "--no-verify-ssl");
47-
}
48-
49-
string awsDefaultRegion = Environment.GetEnvironmentVariable("AWS_DEFAULT_REGION") ?? "us-east-1";
50-
string awsAccessKeyId = Environment.GetEnvironmentVariable("AWS_ACCESS_KEY_ID") ?? "_not_needed_locally_";
51-
string awsSecretAccessKey = Environment.GetEnvironmentVariable("AWS_SECRET_ACCESS_KEY") ?? "_not_needed_locally_";
52-
53-
ProcessHelper.CmdExecute(string.Join(' ', arguments), null, true, true, new Dictionary<string, string>
54-
{
55-
{"AWS_DEFAULT_REGION", awsDefaultRegion},
56-
{"AWS_ACCESS_KEY_ID", awsAccessKeyId},
57-
{"AWS_SECRET_ACCESS_KEY", awsSecretAccessKey}
58-
});
59-
}
12+
var processHelper = new ProcessHelper();
13+
var config = new Config(LocalStackHost);
14+
var textWriter = Console.Out;
6015

61-
private static string GetService()
62-
{
63-
foreach (string arg in Args)
64-
{
65-
if (!arg.StartsWith('-'))
66-
{
67-
return arg;
68-
}
69-
}
70-
71-
return string.Empty;
72-
}
73-
74-
private static (string service, AwsServiceEndpoint awsServiceEndpoint) GetServiceEndpoint()
75-
{
76-
string service = GetService();
77-
if (service == "s3api")
78-
{
79-
service = "s3";
80-
}
81-
82-
var awsServiceEndpoints = Config.GetAwsServiceEndpoints();
83-
return (service, awsServiceEndpoints.SingleOrDefault(endpoint => endpoint.CliName == service));
84-
}
85-
86-
private static void Usage()
87-
{
88-
using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(UsageResource))
89-
{
90-
using (var reader = new StreamReader(stream))
91-
{
92-
string result = reader.ReadToEnd();
93-
Console.WriteLine(result);
94-
}
95-
}
16+
var commandDispatcher = new CommandDispatcher(processHelper, config, textWriter, args);
9617

97-
Environment.Exit(0);
18+
commandDispatcher.Run();
9819
}
9920
}
10021
}

src/LocalStack.sln

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ VisualStudioVersion = 16.0.28803.452
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LocalStack.AwsLocal", "LocalStack.AwsLocal\LocalStack.AwsLocal.csproj", "{D5116356-24F8-4B01-AB33-64CCFC5F0713}"
77
EndProject
8+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{D9F1CEF9-248C-49EA-BD93-5B5E6FD02967}"
9+
EndProject
10+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LocalStack.AwsLocal.Tests", "..\tests\LocalStack.AwsLocal.Tests\LocalStack.AwsLocal.Tests.csproj", "{5D6C622C-F940-4851-BFD9-B2755641C6D5}"
11+
EndProject
812
Global
913
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1014
Debug|Any CPU = Debug|Any CPU
@@ -15,10 +19,17 @@ Global
1519
{D5116356-24F8-4B01-AB33-64CCFC5F0713}.Debug|Any CPU.Build.0 = Debug|Any CPU
1620
{D5116356-24F8-4B01-AB33-64CCFC5F0713}.Release|Any CPU.ActiveCfg = Release|Any CPU
1721
{D5116356-24F8-4B01-AB33-64CCFC5F0713}.Release|Any CPU.Build.0 = Release|Any CPU
22+
{5D6C622C-F940-4851-BFD9-B2755641C6D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23+
{5D6C622C-F940-4851-BFD9-B2755641C6D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
24+
{5D6C622C-F940-4851-BFD9-B2755641C6D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
25+
{5D6C622C-F940-4851-BFD9-B2755641C6D5}.Release|Any CPU.Build.0 = Release|Any CPU
1826
EndGlobalSection
1927
GlobalSection(SolutionProperties) = preSolution
2028
HideSolutionNode = FALSE
2129
EndGlobalSection
30+
GlobalSection(NestedProjects) = preSolution
31+
{5D6C622C-F940-4851-BFD9-B2755641C6D5} = {D9F1CEF9-248C-49EA-BD93-5B5E6FD02967}
32+
EndGlobalSection
2233
GlobalSection(ExtensibilityGlobals) = postSolution
2334
SolutionGuid = {479893D2-EC31-4B10-8F7C-DDA704071C4D}
2435
EndGlobalSection

0 commit comments

Comments
 (0)