Skip to content
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
8773e01
adding test
abhishekkumams May 15, 2023
a01e75b
adding unit test
abhishekkumams May 16, 2023
67b16e7
fix formatting
abhishekkumams May 16, 2023
97b2e39
removing redundant change
abhishekkumams May 16, 2023
939f726
removing redundant change
abhishekkumams May 16, 2023
dad8aaa
Merge branch 'main' into dev/abhishekkuma/add_support_for_env_file
abhishekkumams May 16, 2023
efd36de
Merge branch 'main' into dev/abhishekkuma/add_support_for_env_file
abhishekkumams May 19, 2023
3fac17e
Merge branch 'main' into dev/abhishekkuma/add_support_for_env_file
abhishekkumams May 23, 2023
ef95732
fixing nits
abhishekkumams May 25, 2023
246aef0
adding a new Test file for environment related tests in CLI
abhishekkumams May 25, 2023
6d9f724
Merge branch 'main' of https://github.com/Azure/data-api-builder into…
abhishekkumams May 25, 2023
2b63b07
Update src/Cli.Tests/EnvironmentTests.cs
abhishekkumams May 26, 2023
4079a01
Merge branch 'main' into dev/abhishekkuma/add_support_for_env_file
abhishekkumams May 29, 2023
4702ce6
resolve nits
abhishekkumams May 29, 2023
ca16456
resolving merge conflicts
abhishekkumams May 29, 2023
6e0d5a9
adding new tests
abhishekkumams May 29, 2023
c0ef3cd
fix formatting
abhishekkumams May 29, 2023
ea3fbe3
updating env file lookup
abhishekkumams May 30, 2023
ac6a089
Merge branch 'main' into dev/abhishekkuma/add_support_for_env_file
abhishekkumams May 31, 2023
35d2413
Merge branch 'main' of https://github.com/Azure/data-api-builder into…
abhishekkumams Jun 1, 2023
e1bf19c
Merge branch 'dev/abhishekkuma/add_support_for_env_file' of https://g…
abhishekkumams Jun 1, 2023
74d9554
adding new test
abhishekkumams Jun 1, 2023
d11a199
adding summary
abhishekkumams Jun 1, 2023
366f9ac
fixing nits
abhishekkumams Jun 7, 2023
029ad35
fixing nits
abhishekkumams Jun 7, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 175 additions & 0 deletions src/Cli.Tests/EnvironmentTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Cli.Tests;

/// <summary>
/// Contains test involving environment variables.
/// </summary>
[TestClass]
public class EnvironmentTests
{
public const string TEST_ENV_VARIABLE = "DAB_TEST_ENVIRONMENT";
/// <summary>
/// Test to verify that environment variable setup in the system is picked up correctly
/// when no .env file is present.
/// </summary>
[TestMethod]
public void TestEnvironmentVariableIsConsumedCorrectly()
{
string jsonWithEnvVariable = @"{""envValue"": ""@env('DAB_TEST_ENVIRONMENT')""}";

// No environment File, No environment variable set in the system
Assert.AreEqual(null, Environment.GetEnvironmentVariable(TEST_ENV_VARIABLE));

// Configuring environment variable in the system
Environment.SetEnvironmentVariable(TEST_ENV_VARIABLE, "TEST");

// Test environment variable is correctly resolved in the config file
string? resolvedJson = RuntimeConfigPath.ParseConfigJsonAndReplaceEnvVariables(jsonWithEnvVariable);
Assert.IsNotNull(resolvedJson);
Assert.IsTrue(JToken.DeepEquals(
JObject.Parse(@"{""envValue"": ""TEST""}"),
JObject.Parse(resolvedJson)), "JSON resolved with environment variable correctly");

// removing Environment variable from the System
Environment.SetEnvironmentVariable(TEST_ENV_VARIABLE, null);
Assert.ThrowsException<DataApiBuilderException>(() =>
RuntimeConfigPath.ParseConfigJsonAndReplaceEnvVariables(jsonWithEnvVariable),
$"Environmental Variable, {TEST_ENV_VARIABLE}, not found.");
}

/// <summary>
/// This test creates a .env file and adds a variable and verifies that the variable is
/// correctly consumed.
/// For this test there were no existing environment variables. The values are picked up
/// directly from the `.env` file.
/// </summary>
[TestMethod]
public void TestEnvironmentFileIsConsumedCorrectly()
{
string jsonWithEnvVariable = @"{""envValue"": ""@env('DAB_TEST_ENVIRONMENT')""}";

// No environment File, No environment variable set in the system
Assert.IsNull(Environment.GetEnvironmentVariable(TEST_ENV_VARIABLE));

// Creating environment variable file
File.Create(".env").Close();
File.WriteAllText(".env", $"{TEST_ENV_VARIABLE}=DEVELOPMENT");
DotNetEnv.Env.Load();

// Test environment variable is picked up from the .env file and is correctly resolved in the config file.
Assert.AreEqual("DEVELOPMENT", Environment.GetEnvironmentVariable(TEST_ENV_VARIABLE));
string? resolvedJson = RuntimeConfigPath.ParseConfigJsonAndReplaceEnvVariables(jsonWithEnvVariable);
Assert.IsNotNull(resolvedJson);
Assert.IsTrue(JToken.DeepEquals(
JObject.Parse(@"{""envValue"": ""DEVELOPMENT""}"),
JObject.Parse(resolvedJson)), "JSON resolved with environment variable correctly");
}

/// <summary>
/// This test setups a environment variable in the system and also creates a .env file containing
/// same variable with different value to show the value stored in .env file is given
/// precedence over the system variable.
/// </summary>
[TestMethod]
public void TestPrecedenceOfEnvironmentFileOverExistingVariables()
{
// The variable set in the .env file takes precedence over the environment value set in the system.
Environment.SetEnvironmentVariable(TEST_ENV_VARIABLE, "TEST");

// Creating environment variable file
File.Create(".env").Close();
File.WriteAllText(".env", $"{TEST_ENV_VARIABLE}=DEVELOPMENT");
DotNetEnv.Env.Load(); // It contains value DEVELOPMENT
Assert.AreEqual("DEVELOPMENT", Environment.GetEnvironmentVariable(TEST_ENV_VARIABLE));

// If a variable is not present in the .env file then the system defined variable would be used if defined.
Environment.SetEnvironmentVariable("HOSTING_TEST_ENVIRONMENT", "PHOENIX_TEST");
string? resolvedJson = RuntimeConfigPath.ParseConfigJsonAndReplaceEnvVariables(
@"{
""envValue"": ""@env('DAB_TEST_ENVIRONMENT')"",
""hostingEnvValue"": ""@env('HOSTING_TEST_ENVIRONMENT')""
}"
);
Assert.IsNotNull(resolvedJson);
Assert.IsTrue(JToken.DeepEquals(
JObject.Parse(
@"{
""envValue"": ""DEVELOPMENT"",
""hostingEnvValue"": ""PHOENIX_TEST""
}"),
JObject.Parse(resolvedJson)), "JSON resolved with environment variable correctly");

// Removing the .env file it will then use the value of system environment variable.

}

/// <summary>
/// Test to verify that no error is thrown if .env file is not present, and existing system variables is used.
/// </summary>
[TestMethod]
public void TestSystemEnvironmentVariableIsUsedInAbsenceOfEnvironmentFile()
{
Environment.SetEnvironmentVariable(TEST_ENV_VARIABLE, "TEST");
Assert.IsFalse(File.Exists(".env"));
DotNetEnv.Env.Load(); // No error is thrown
Assert.AreEqual("TEST", Environment.GetEnvironmentVariable(TEST_ENV_VARIABLE));
}

/// <summary>
/// Test to verify that if the environment variables are not resolved correctly, runtime engine will not start.
/// Here, in the first scenario, engine failed to start because the variable defined in the environment file
/// is typed incorrectly and do not match with the one present in the config.
/// </summary>
[DataRow("COMM_STRINX=test_connection_string", true, DisplayName = "Incorrect Variable name used in the environment file.")]
[DataRow("CONN_STRING=test_connection_string", false, DisplayName = "Correct Variable name used in the environment file.")]
[DataTestMethod]
public void TestFailureToStartWithUnresolvedJsonConfig(
string environmentFileContent,
bool isFailure
)
{
// Creating environment variable file
File.Create(".env").Close();
File.WriteAllText(".env", environmentFileContent);
if (File.Exists(TEST_RUNTIME_CONFIG_FILE))
{
File.Delete(TEST_RUNTIME_CONFIG_FILE);
}

string[] initArgs = { "init", "-c", TEST_RUNTIME_CONFIG_FILE, "--database-type", "mssql", "--connection-string", "@env('CONN_STRING')" };
Program.Main(initArgs);

// Trying to start the runtime engine
using Process process = ExecuteDabCommand(
"start",
$"-c {TEST_RUNTIME_CONFIG_FILE}"
);

string? output = process.StandardOutput.ReadToEnd();
Assert.IsNotNull(output);

if (isFailure)
{
// Failed to resolve the environment variables in the config.
Assert.IsFalse(output.Contains("Starting the runtime engine..."));
Assert.IsTrue(output.Contains("Error: Failed due to: Environmental Variable, CONN_STRING, not found."));
Assert.IsTrue(output.Contains("Error: Failed to start the engine."));
}
else
{
// config resolved correctly.
Assert.IsTrue(output.Contains("Starting the runtime engine..."));
}
}

[TestCleanup]
public void CleanUp()
{
if (File.Exists(".env"))
{
File.Delete(".env");
}
}
}
1 change: 1 addition & 0 deletions src/Cli.Tests/Usings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
global using System.Diagnostics;
global using System.Text.Json;
global using Azure.DataApiBuilder.Config;
global using Azure.DataApiBuilder.Service.Exceptions;
global using Microsoft.Extensions.Logging;
global using Microsoft.VisualStudio.TestTools.UnitTesting;
global using Moq;
Expand Down
3 changes: 3 additions & 0 deletions src/Cli/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ public static int Main(string[] args)
}
);

// Load environment variables from .env file if present.
DotNetEnv.Env.Load();

// Setting up Logger for CLI.
ILoggerFactory loggerFactory = new LoggerFactory();
loggerFactory.AddProvider(new CustomLoggerProvider());
Expand Down
18 changes: 14 additions & 4 deletions src/Cli/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using static Azure.DataApiBuilder.Config.AuthenticationConfig;
using static Azure.DataApiBuilder.Config.MergeJsonProvider;
using static Azure.DataApiBuilder.Config.RuntimeConfigPath;
using static Azure.DataApiBuilder.Service.Configurations.RuntimeConfigProvider;
using static Azure.DataApiBuilder.Service.Configurations.RuntimeConfigValidator;
using PermissionOperation = Azure.DataApiBuilder.Config.PermissionOperation;

Expand Down Expand Up @@ -582,6 +583,7 @@ public static bool TryGetConfigFileBasedOnCliPrecedence(
else
{
_logger.LogInformation("Config not provided. Trying to get default config based on DAB_ENVIRONMENT...");
_logger.LogInformation("Environment variable DAB_ENVIRONMENT is {value}", Environment.GetEnvironmentVariable("DAB_ENVIRONMENT"));
/// Need to reset to true explicitly so any that any re-invocations of this function
/// get simulated as being called for the first time specifically useful for tests.
RuntimeConfigPath.CheckPrecedenceForConfigInEngine = true;
Expand All @@ -597,7 +599,7 @@ public static bool TryGetConfigFileBasedOnCliPrecedence(
}

/// <summary>
/// Checks if config can be correctly parsed by deserializing the
/// Checks if config can be correctly resolved and parsed by deserializing the
/// json config into runtime config object.
/// Also checks that connection-string is not null or empty whitespace.
/// If parsing is successful and the config has valid connection-string, it
Expand All @@ -608,13 +610,20 @@ public static bool CanParseConfigCorrectly(
[NotNullWhen(true)] out RuntimeConfig? deserializedRuntimeConfig)
{
deserializedRuntimeConfig = null;
if (!TryReadRuntimeConfig(configFile, out string runtimeConfigJson))
string? runtimeConfigJson;

try
{
// Tries to read the config and resolve environment variables.
runtimeConfigJson = GetRuntimeConfigJsonString(configFile);
}
catch (Exception e)
{
_logger.LogError($"Failed to read the config file: {configFile}.");
_logger.LogError("Failed due to: {exceptionMessage}", e.Message);
return false;
}

if (!RuntimeConfig.TryGetDeserializedRuntimeConfig(
if (string.IsNullOrEmpty(runtimeConfigJson) || !RuntimeConfig.TryGetDeserializedRuntimeConfig(
runtimeConfigJson,
out deserializedRuntimeConfig,
logger: null))
Expand Down Expand Up @@ -906,6 +915,7 @@ public static bool TryMergeConfigsIfAvailable([NotNullWhen(true)] out string? me
string baseConfigJson = File.ReadAllText(baseConfigFile);
string overrideConfigJson = File.ReadAllText(environmentBasedConfigFile);
string currentDir = Directory.GetCurrentDirectory();
_logger.LogInformation("Using DAB_ENVIRONMENT = {value}", environmentValue);
_logger.LogInformation($"Merging {Path.Combine(currentDir, baseConfigFile)}"
+ $" and {Path.Combine(currentDir, environmentBasedConfigFile)}");
string mergedConfigJson = Merge(baseConfigJson, overrideConfigJson);
Expand Down
1 change: 1 addition & 0 deletions src/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<PackageVersion Include="HotChocolate.AspNetCore.Authorization" Version="12.18.0" />
<PackageVersion Include="Humanizer" Version="2.14.1" />
<PackageVersion Include="Humanizer.Core" Version="2.14.1" />
<PackageVersion Include="DotNetEnv" Version="2.5.0" />
<PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="6.0.14" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.14" />
<PackageVersion Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
Expand Down
1 change: 1 addition & 0 deletions src/Service/Azure.DataApiBuilder.Service.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
<PackageReference Include="Newtonsoft.Json" />
<PackageReference Include="Npgsql" />
<PackageReference Include="Polly" />
<PackageReference Include="DotNetEnv" />
<PackageReference Include="StyleCop.Analyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down