From b0526654f5b72b226ce516961302a40d27028f52 Mon Sep 17 00:00:00 2001 From: Tom Ashworth Date: Fri, 16 Apr 2021 14:25:28 +0100 Subject: [PATCH] feat: set environment variables (#50) a mechanism to set Power Apps environment variables after the import has completed --- .editorconfig | 3 + README.md | 46 +++- .../Constants.cs | 56 ++++- .../PackageTemplateBase.cs | 84 ++++++-- .../EnvironmentVariableDeploymentService.cs | 124 +++++++++++ .../Services/MailboxDeploymentService.cs | 1 - .../PackageDeployerFixture.cs | 26 ++- .../PackageTemplateBaseTests.cs | 18 ++ ...nnectionReferenceDeploymentServiceTests.cs | 2 - ...vironmentVariableDeploymentServiceTests.cs | 198 ++++++++++++++++++ .../Services/MailboxDeploymentServiceTests.cs | 1 - .../environmentvariabledefinition.xml | 9 + 12 files changed, 526 insertions(+), 42 deletions(-) create mode 100644 src/Capgemini.PowerApps.PackageDeployerTemplate/Services/EnvironmentVariableDeploymentService.cs create mode 100644 tests/Capgemini.PowerApps.PackageDeployerTemplate.UnitTests/Services/EnvironmentVariableDeploymentServiceTests.cs create mode 100644 tests/pdt_PackageDeployerTemplate_MockSolution/src/environmentvariabledefinitions/pdt_TestVariable/environmentvariabledefinition.xml diff --git a/.editorconfig b/.editorconfig index e236169..dcbe5a7 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3,6 +3,9 @@ # SA1633: File should have header dotnet_diagnostic.SA1633.severity = none +# SA1124: Do not use regions +dotnet_diagnostic.SA1124.severity = none + [tests/**/*.cs] # SA1600: Elements should be documented diff --git a/README.md b/README.md index 8d079a0..0cbb14d 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,8 @@ This project's aim is to build a powerful base Package Deployer template that si - [Set SDK step states](#Set-sdk-step-states) - [Connection references](#Connection-references) - [Set connection references](#Set-connection-references) + - [Environment variables](#Environment-variables) + - [Set environment variables](#Set-environment-variables) - [Data](#Data) - [Import data](#Import-data) - [Word templates](#Word-templates) @@ -118,16 +120,16 @@ Environment variables must be prefixed with `PACKAGEDEPLOYER_SETTINGS_CONNREF_` ```powershell $env:PACKAGEDEPLOYER_SETTINGS_CONNREF_DEVHUB_SHAREDVISUALSTUDIOTEAMSERVICES_CA653 = "shared-visualstudiot-44dd3131-3292-482a-9ec3-32cd7f3e799b" + +Import-CrmPackage [...] ``` **Runtime setting** ```powershell -$runtimeSettings = @{ - "ConnRef:devhub_sharedvisualstudioteamservices_ca653" = "shared-visualstudiot-44dd3131-3292-482a-9ec3-32cd7f3e799b" -} +$runtimeSettings = "ConnRef:devhub_sharedvisualstudioteamservices_ca653=shared-visualstudiot-44dd3131-3292-482a-9ec3-32cd7f3e799b" -Import-CrmPackage –CrmConnection $conn –PackageDirectory $packageDir –PackageName Package.dll –RuntimePackageSettings $runtimeSettings +Import-CrmPackage [...] –RuntimePackageSettings $runtimeSettings ``` The runtime setting takes precedence if both an environment variable and runtime setting are found for the same connection reference. @@ -136,6 +138,34 @@ To get your flow connection names, go to your environment and navigate to _Data As above, you will need to pass a licensed user's email via runtime settings or environment variables if the Package Deployer is not running in the context of a licensed user. In addition, **the connections passed in need to be owned by the user doing the deployment or impersonated by the deployment**. +### Environment variables + +#### Set environment variables + +You can set Power App environment variables either through system environment variables (for example, those [exposed on Azure Pipelines](https://docs.microsoft.com/en-us/azure/devops/pipelines/process/variables?view=azure-devops&tabs=yaml%2Cbatch#access-variables-through-the-environment) from your variables or variable groups) or through Package Deployer [runtime settings](https://docs.microsoft.com/en-us/power-platform/admin/deploy-packages-using-package-deployer-windows-powershell#use-the-cmdlet-to-deploy-packages). + +Environment variables must be prefixed with `PACKAGEDEPLOYER_SETTINGS_ENVVAR_` and followed by the schema name. Similarly, runtime settings must be prefixed with `EnvVar:` and followed by the environment variable schema name. For example, if an enviroment variable schema name was `pdt_testvariable`, this could be set via either of the following: + +**Environment variable** + +```powershell +$env:PACKAGEDEPLOYER_SETTINGS_ENVVAR_PDT_TESTVARIABLE = "test_value" + +Import-CrmPackage [...] +``` + +**Runtime setting** + +```powershell +$runtimeSettings = "EnvVar:pdt_testvariable=test_value" + +Import-CrmPackage [...] –RuntimePackageSettings $runtimeSettings +``` + +The runtime setting takes precedence if both an environment variable and runtime setting are found for the same Power App environment variable. + +If a value has already been set in the target environment then it will be overridden, otherwise a new Environment Variable Value will be created ad related to the Environment Variable Definition determined by the given schema name. + ### Data #### Import data @@ -211,16 +241,16 @@ Environment variables must be prefixed with `PACKAGEDEPLOYER_SETTINGS_MAILBOX_` ```powershell $env:PACKAGEDEPLOYER_SETTINGS_MAILBOX_SUPPORT-DEV@FAKE.COM = "support-prod@fake.com" + +Import-CrmPackage [...] ``` **Runtime setting** ```powershell -$runtimeSettings = @{ - "Mailbox:support-dev@fake.com" = "support-prod@fake.com" -} +$runtimeSettings = "Mailbox:support-dev@fake.com=support-prod@fake.com" -Import-CrmPackage –CrmConnection $conn –PackageDirectory $packageDir –PackageName Package.dll –RuntimePackageSettings $runtimeSettings +Import-CrmPackage [...] –RuntimePackageSettings $runtimeSettings ``` The runtime setting takes precedence if both an environment variable and runtime setting are found for the same shared mailbox. diff --git a/src/Capgemini.PowerApps.PackageDeployerTemplate/Constants.cs b/src/Capgemini.PowerApps.PackageDeployerTemplate/Constants.cs index 961c044..2b1cfc4 100644 --- a/src/Capgemini.PowerApps.PackageDeployerTemplate/Constants.cs +++ b/src/Capgemini.PowerApps.PackageDeployerTemplate/Constants.cs @@ -15,6 +15,11 @@ public static class Settings /// public const string ConnectionReferencePrefix = "ConnRef"; + /// + /// The prefix for all Power Apps environment variables. + /// + public const string PowerAppsEnvironmentVariablePrefix = "EnvVar"; + /// /// The prefix for all environment variables. /// @@ -455,5 +460,54 @@ public static class Fields public const string AzureActiveDirectoryObjectId = "azureactivedirectoryobjectid"; } } + + /// + /// Constants relating to the environmentvariabledefinition entity. + /// + public static class EnvironmentVariableDefinition + { + /// + /// The logical name. + /// + public const string LogicalName = "environmentvariabledefinition"; + + /// + /// Field logical names. + /// + public static class Fields + { + /// + /// The schema name. + /// + public const string SchemaName = "schemaname"; + } + } + + /// + /// Constants relating to the environmentvariablevalue entity. + /// + public static class EnvironmentVariableValue + { + /// + /// The logical name. + /// + public const string LogicalName = "environmentvariablevalue"; + + /// + /// Field logical names. + /// + public static class Fields + { + /// + /// The variable definition id. + /// + public const string EnvironmentVariableDefinitonId = "environmentvariabledefinitionid"; + + /// + /// The variable value. + /// + public const string Value = "value"; + } + } } -} +} \ No newline at end of file diff --git a/src/Capgemini.PowerApps.PackageDeployerTemplate/PackageTemplateBase.cs b/src/Capgemini.PowerApps.PackageDeployerTemplate/PackageTemplateBase.cs index e762300..823a14b 100644 --- a/src/Capgemini.PowerApps.PackageDeployerTemplate/PackageTemplateBase.cs +++ b/src/Capgemini.PowerApps.PackageDeployerTemplate/PackageTemplateBase.cs @@ -18,6 +18,8 @@ /// public abstract class PackageTemplateBase : ImportExtension { + #region private-props + private ICrmServiceAdapter crmServiceAdapter; private string licensedUsername; private TemplateConfig templateConfig; @@ -32,6 +34,11 @@ public abstract class PackageTemplateBase : ImportExtension private TableColumnProcessingService autonumberSeedSettingSvc; private MailboxDeploymentService mailboxSvc; + private EnvironmentVariableDeploymentService environmentVariableService; + + #endregion + #region protected-props + /// /// Gets a value indicating whether whether the deployment is running on Azure DevOps. /// @@ -76,37 +83,62 @@ protected string LicensedUsername protected IDictionary MailboxMappings => this.GetSettings(Constants.Settings.MailboxPrefix); /// - /// Gets an extended . + /// Gets the PowerApps environment variables mappings. /// - /// - /// An extended . - /// - protected ICrmServiceAdapter CrmServiceAdapter + /// The Power App environment variables. + protected IDictionary PowerAppsEnvironmentVariables => this.GetSettings(Constants.Settings.PowerAppsEnvironmentVariablePrefix); + + /// + /// Gets a list of solutions that have been processed (i.e. has been ran for that solution.) + /// + protected IList ProcessedSolutions { get { - if (this.crmServiceAdapter == null) + if (this.processedSolutions == null) { - this.crmServiceAdapter = new CrmServiceAdapter(this.CrmSvc, this.TraceLoggerAdapter); + this.processedSolutions = new List(); } - return this.crmServiceAdapter; + return this.processedSolutions; } } /// - /// Gets a list of solutions that have been processed (i.e. has been ran for that solution.) + /// Gets provides access to the templateconfig section of the ImportConfig.xml. /// - protected IList ProcessedSolutions + protected TemplateConfig TemplateConfig { get { - if (this.processedSolutions == null) + if (this.templateConfig == null) { - this.processedSolutions = new List(); + this.templateConfig = TemplateConfig.Load(this.ImportConfigFilePath); } - return this.processedSolutions; + return this.templateConfig; + } + } + + #endregion + #region service-initialisers + + /// + /// Gets an extended . + /// + /// + /// An extended . + /// + protected ICrmServiceAdapter CrmServiceAdapter + { + get + { + if (this.crmServiceAdapter == null) + { + this.crmServiceAdapter = new CrmServiceAdapter(this.CrmSvc, this.TraceLoggerAdapter); + } + + return this.crmServiceAdapter; } } @@ -239,18 +271,18 @@ protected MailboxDeploymentService MailboxSvc } /// - /// Gets provides access to the templateconfig section of the ImportConfig.xml. + /// Gets provides deployment functionality relating to environment variables. /// - protected TemplateConfig TemplateConfig + protected EnvironmentVariableDeploymentService EnvironmentVariablesSvc { get { - if (this.templateConfig == null) + if (this.environmentVariableService == null) { - this.templateConfig = TemplateConfig.Load(this.ImportConfigFilePath); + this.environmentVariableService = new EnvironmentVariableDeploymentService(this.TraceLoggerAdapter, this.CrmServiceAdapter); } - return this.templateConfig; + return this.environmentVariableService; } } @@ -270,6 +302,9 @@ protected TraceLoggerAdapter TraceLoggerAdapter } } + #endregion + #region lifecycle-events + /// public override void InitializeCustomExtension() { @@ -330,6 +365,8 @@ public override bool AfterPrimaryImport() this.TemplateConfig.PostDeployDataImports, this.PackageFolderPath); + this.EnvironmentVariablesSvc.SetEnvironmentVariables(this.PowerAppsEnvironmentVariables); + this.SdkStepSvc.SetStatesBySolution( this.ProcessedSolutions, this.TemplateConfig.SdkStepsToDeactivate.Select(s => s.Name)); @@ -376,6 +413,9 @@ public override bool AfterPrimaryImport() return true; } + #endregion + #region settings-retrival + /// /// Gets a setting either from runtime arguments or an environment variable (in that order of preference). Environment variables should be prefixed with 'PACKAGEDEPLOYER_SETTINGS_'. /// @@ -462,6 +502,9 @@ protected IDictionary GetSettings(string prefix) return mappings; } + #endregion + #region lifecycle-event-helpers + private void ExecuteLifecycleEvent(string eventName, Action eventAction) { this.LogLifecycleEventStart(eventName); @@ -479,6 +522,9 @@ private void LogLifecycleEventEnd(string lifecycleEvent) this.TraceLoggerAdapter.LogInformation($"{nameof(PackageTemplateBase)}.{lifecycleEvent} completed."); } + #endregion + #region logging-helpers + // Excluded as it would require our CI or PR validation pipelines to be partially succeeding or failing [ExcludeFromCodeCoverage] private void LogTaskCompleteResult() @@ -492,5 +538,7 @@ private void LogTaskCompleteResult() Console.WriteLine("##vso[task.complete result=SucceededWithIssues;]DONE"); } } + + #endregion } } diff --git a/src/Capgemini.PowerApps.PackageDeployerTemplate/Services/EnvironmentVariableDeploymentService.cs b/src/Capgemini.PowerApps.PackageDeployerTemplate/Services/EnvironmentVariableDeploymentService.cs new file mode 100644 index 0000000..71fea26 --- /dev/null +++ b/src/Capgemini.PowerApps.PackageDeployerTemplate/Services/EnvironmentVariableDeploymentService.cs @@ -0,0 +1,124 @@ +namespace Capgemini.PowerApps.PackageDeployerTemplate.Services +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Capgemini.PowerApps.PackageDeployerTemplate.Adapters; + using Microsoft.Extensions.Logging; + using Microsoft.Xrm.Sdk; + using Microsoft.Xrm.Sdk.Query; + + /* + * Title: Development Hub + * Author: Max Ewing + * Date: 15 April 2021 + * Code version: v0.2.25 + * Availability: https://github.com/ewingjm/development-hub/blob/v0.2.25/deploy/EnvironmentVariableDeploymentService.cs + */ + + /// + /// Deployment functionality related to environment variables. + /// + public class EnvironmentVariableDeploymentService + { + private readonly ILogger logger; + private readonly ICrmServiceAdapter crmSvc; + + /// + /// Initializes a new instance of the class. + /// + /// The . + /// The . + public EnvironmentVariableDeploymentService(ILogger logger, ICrmServiceAdapter crmSvc) + { + this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); + this.crmSvc = crmSvc ?? throw new ArgumentNullException(nameof(crmSvc)); + } + + /// + /// Sets environment variables on the target Power Apps environment. + /// + /// A dictionary of keys and values to set. + public void SetEnvironmentVariables(IDictionary environmentVariables) + { + if (environmentVariables is null || !environmentVariables.Any()) + { + this.logger.LogInformation("No environment variables have been configured."); + return; + } + + foreach (KeyValuePair entry in environmentVariables) + { + this.SetEnvironmentVariable(entry.Key, entry.Value); + } + } + + /// + /// Sets an environment variable on the target Power Apps environment. + /// + /// Environment variable key. + /// Environment variable value. + public void SetEnvironmentVariable(string key, string value) + { + this.logger.LogInformation($"Setting {key} environment variable to {value}."); + + var definition = this.GetDefinitionByKey(key, new ColumnSet(false)); + if (definition == null) + { + this.logger.LogError($"Environment variable {key} not found on target instance."); + return; + } + + var definitionReference = definition.ToEntityReference(); + this.logger.LogTrace($"Found environment variable on target instance: {definition.Id}"); + + this.UpsertEnvironmentVariableValue(value, definitionReference); + } + + private void UpsertEnvironmentVariableValue(string value, EntityReference definitionReference) + { + var existingValue = this.GetValueByDefinitionId(definitionReference, new ColumnSet(Constants.EnvironmentVariableValue.Fields.Value)); + if (existingValue != null) + { + existingValue[Constants.EnvironmentVariableValue.Fields.Value] = value; + this.crmSvc.Update(existingValue); + } + else + { + this.SetValue(value, definitionReference); + } + } + + private void SetValue(string value, EntityReference definition) + { + var val = new Entity(Constants.EnvironmentVariableValue.LogicalName) + { + Attributes = new AttributeCollection + { + { Constants.EnvironmentVariableValue.Fields.Value, value }, + { Constants.EnvironmentVariableValue.Fields.EnvironmentVariableDefinitonId, definition }, + }, + }; + + this.crmSvc.Create(val); + } + + private Entity GetValueByDefinitionId(EntityReference definitionReference, ColumnSet columnSet) + { + return this.crmSvc.RetrieveMultipleByAttribute( + Constants.EnvironmentVariableValue.LogicalName, + Constants.EnvironmentVariableValue.Fields.EnvironmentVariableDefinitonId, + new object[] { definitionReference.Id }, + columnSet).Entities.FirstOrDefault(); + } + + private Entity GetDefinitionByKey(string key, ColumnSet columnSet) + { + return this.crmSvc.RetrieveMultipleByAttribute( + Constants.EnvironmentVariableDefinition.LogicalName, + Constants.EnvironmentVariableDefinition.Fields.SchemaName, + new string[] { key }, + columnSet).Entities.FirstOrDefault(); + } + } +} \ No newline at end of file diff --git a/src/Capgemini.PowerApps.PackageDeployerTemplate/Services/MailboxDeploymentService.cs b/src/Capgemini.PowerApps.PackageDeployerTemplate/Services/MailboxDeploymentService.cs index 6165857..44f94b6 100644 --- a/src/Capgemini.PowerApps.PackageDeployerTemplate/Services/MailboxDeploymentService.cs +++ b/src/Capgemini.PowerApps.PackageDeployerTemplate/Services/MailboxDeploymentService.cs @@ -5,7 +5,6 @@ using System.Linq; using System.Threading; using Capgemini.PowerApps.PackageDeployerTemplate.Adapters; - using Capgemini.PowerApps.PackageDeployerTemplate.Config; using Microsoft.Extensions.Logging; using Microsoft.Xrm.Sdk; using Microsoft.Xrm.Sdk.Query; diff --git a/tests/Capgemini.PowerApps.PackageDeployerTemplate.IntegrationTests/PackageDeployerFixture.cs b/tests/Capgemini.PowerApps.PackageDeployerTemplate.IntegrationTests/PackageDeployerFixture.cs index 35686a8..4368d12 100644 --- a/tests/Capgemini.PowerApps.PackageDeployerTemplate.IntegrationTests/PackageDeployerFixture.cs +++ b/tests/Capgemini.PowerApps.PackageDeployerTemplate.IntegrationTests/PackageDeployerFixture.cs @@ -1,4 +1,4 @@ -namespace Capgemini.PowerApps.PackageDeployerTemplate.IntegrationTests +namespace Capgemini.PowerApps.PackageDeployerTemplate.IntegrationTests { using System; using System.Diagnostics; @@ -11,8 +11,9 @@ public class PackageDeployerFixture : IDisposable { public PackageDeployerFixture() { - // Check approvals connection is set. - _ = this.GetApprovalsConnection(); + // Check values are set. + _ = GetApprovalsConnection(); + _ = GetTestEnvironmentVariable(); var process = new Process(); var startInfo = new ProcessStartInfo @@ -31,9 +32,9 @@ public PackageDeployerFixture() ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; this.ServiceClient = new CrmServiceClient( - $"Url={this.GetUrl()}; " + - $"Username={this.GetUsername()}; " + - $"Password={this.GetPassword()}; " + + $"Url={GetUrl()}; " + + $"Username={GetUsername()}; " + + $"Password={GetPassword()}; " + "AuthType=OAuth; " + "AppId=51f81489-12ee-4a9e-aaae-a2591f45987d; " + "RedirectUri=app://58145B91-0C36-4500-8554-080854F2AC97"); @@ -52,21 +53,24 @@ public void Dispose() this.ServiceClient.Dispose(); } - protected string GetApprovalsConnection() => + protected static string GetApprovalsConnection() => GetRequiredEnvironmentVariable("PACKAGEDEPLOYER_SETTINGS_CONNREF_PDT_SHAREDAPPROVALS_D7DCB", "No environment variable configured to set approvals connection."); - protected string GetLicensedUsername() => + protected static string GetLicensedUsername() => GetRequiredEnvironmentVariable("PACKAGEDEPLOYER_SETTINGS_LICENSEDUSERNAME", "No environment variable configured to connect and activate flows."); - protected string GetUrl() => + protected static string GetUrl() => GetRequiredEnvironmentVariable("CAPGEMINI_PACKAGE_DEPLOYER_TESTS_URL", "No environment variable configured to set deployment URL."); - protected string GetUsername() => + protected static string GetUsername() => GetRequiredEnvironmentVariable("CAPGEMINI_PACKAGE_DEPLOYER_TESTS_USERNAME", "No environment variable configured to set deployment username."); - protected string GetPassword() => + protected static string GetPassword() => GetRequiredEnvironmentVariable("CAPGEMINI_PACKAGE_DEPLOYER_TESTS_PASSWORD", "No environment variable configured to set deployment password."); + protected static string GetTestEnvironmentVariable() => + GetRequiredEnvironmentVariable("PACKAGEDEPLOYER_SETTINGS_ENVVAR_PDT_TESTVARIABLE", "No environment variable configured to set power apps test environment variable."); + private static string GetRequiredEnvironmentVariable(string name, string exceptionMessage) { var url = Environment.GetEnvironmentVariable(name); diff --git a/tests/Capgemini.PowerApps.PackageDeployerTemplate.IntegrationTests/PackageTemplateBaseTests.cs b/tests/Capgemini.PowerApps.PackageDeployerTemplate.IntegrationTests/PackageTemplateBaseTests.cs index faaac41..369f223 100644 --- a/tests/Capgemini.PowerApps.PackageDeployerTemplate.IntegrationTests/PackageTemplateBaseTests.cs +++ b/tests/Capgemini.PowerApps.PackageDeployerTemplate.IntegrationTests/PackageTemplateBaseTests.cs @@ -125,6 +125,24 @@ public void PackageTemplateBase_ConnectionNamePassed_ConnectionIsSet() connectionReference.GetAttributeValue(Constants.ConnectionReference.Fields.ConnectionId).Should().Be(Environment.GetEnvironmentVariable("PACKAGEDEPLOYER_SETTINGS_CONNREF_PDT_SHAREDAPPROVALS_D7DCB")); } + [Fact] + public void PackageTemplateBase_EnvironmentVariablePassed_EnvironmentVariableIsSet() + { + var variableDefinitionQuery = new QueryByAttribute(Constants.EnvironmentVariableDefinition.LogicalName); + variableDefinitionQuery.AddAttributeValue(Constants.EnvironmentVariableDefinition.Fields.SchemaName, "pdt_testvariable"); + variableDefinitionQuery.ColumnSet = new ColumnSet(false); + + var variableDefinition = this.fixture.ServiceClient.RetrieveMultiple(variableDefinitionQuery).Entities.First(); + + var variableValueQuery = new QueryByAttribute(Constants.EnvironmentVariableValue.LogicalName); + variableValueQuery.AddAttributeValue(Constants.EnvironmentVariableValue.Fields.EnvironmentVariableDefinitonId, variableDefinition.Id); + variableValueQuery.ColumnSet = new ColumnSet(Constants.EnvironmentVariableValue.Fields.Value); + + var variableValue = this.fixture.ServiceClient.RetrieveMultiple(variableValueQuery).Entities.First(); + + variableValue.GetAttributeValue(Constants.EnvironmentVariableValue.Fields.Value).Should().Be(Environment.GetEnvironmentVariable("PACKAGEDEPLOYER_SETTINGS_ENVVAR_PDT_TESTVARIABLE")); + } + [Theory] [InlineData("When a contact is created -> Terminate", Constants.Workflow.StateCodeInactive)] [InlineData("When a contact is created -> Create an approval", Constants.Workflow.StateCodeActive)] diff --git a/tests/Capgemini.PowerApps.PackageDeployerTemplate.UnitTests/Services/ConnectionReferenceDeploymentServiceTests.cs b/tests/Capgemini.PowerApps.PackageDeployerTemplate.UnitTests/Services/ConnectionReferenceDeploymentServiceTests.cs index 537582a..495c302 100644 --- a/tests/Capgemini.PowerApps.PackageDeployerTemplate.UnitTests/Services/ConnectionReferenceDeploymentServiceTests.cs +++ b/tests/Capgemini.PowerApps.PackageDeployerTemplate.UnitTests/Services/ConnectionReferenceDeploymentServiceTests.cs @@ -4,7 +4,6 @@ namespace Capgemini.PowerApps.PackageDeployerTemplate.UnitTests.Services using System.Collections.Generic; using System.Linq; using Capgemini.PowerApps.PackageDeployerTemplate.Adapters; - using Capgemini.PowerApps.PackageDeployerTemplate.Extensions; using Capgemini.PowerApps.PackageDeployerTemplate.Services; using FluentAssertions; using Microsoft.Extensions.Logging; @@ -12,7 +11,6 @@ namespace Capgemini.PowerApps.PackageDeployerTemplate.UnitTests.Services using Microsoft.Xrm.Sdk.Messages; using Microsoft.Xrm.Sdk.Query; using Moq; - using Moq.Language.Flow; using Xunit; public class ConnectionReferenceDeploymentServiceTests diff --git a/tests/Capgemini.PowerApps.PackageDeployerTemplate.UnitTests/Services/EnvironmentVariableDeploymentServiceTests.cs b/tests/Capgemini.PowerApps.PackageDeployerTemplate.UnitTests/Services/EnvironmentVariableDeploymentServiceTests.cs new file mode 100644 index 0000000..bc91a68 --- /dev/null +++ b/tests/Capgemini.PowerApps.PackageDeployerTemplate.UnitTests/Services/EnvironmentVariableDeploymentServiceTests.cs @@ -0,0 +1,198 @@ +namespace Capgemini.PowerApps.PackageDeployerTemplate.UnitTests.Services +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Capgemini.PowerApps.PackageDeployerTemplate.Adapters; + using Capgemini.PowerApps.PackageDeployerTemplate.Services; + using Microsoft.Extensions.Logging; + using Microsoft.Xrm.Sdk; + using Microsoft.Xrm.Sdk.Query; + using Moq; + using Xunit; + + public class EnvironmentVariableDeploymentServiceTests + { + private readonly Mock loggerMock; + private readonly Mock crmServiceAdapterMock; + private readonly EnvironmentVariableDeploymentService environmentVariableDeploymentService; + + public EnvironmentVariableDeploymentServiceTests() + { + this.loggerMock = new Mock(); + this.crmServiceAdapterMock = new Mock(); + + this.environmentVariableDeploymentService = new EnvironmentVariableDeploymentService(this.loggerMock.Object, this.crmServiceAdapterMock.Object); + } + + [Fact] + public void Construct_NullPackageLog_ThrowsArgumentNullException() + { + Assert.Throws(() => + { + new EnvironmentVariableDeploymentService(null, this.crmServiceAdapterMock.Object); + }); + } + + [Fact] + public void Construct_NullCrmService_ThrowsArgumentNullException() + { + Assert.Throws(() => + { + new EnvironmentVariableDeploymentService(this.loggerMock.Object, null); + }); + } + + [Fact] + public void SetEnvironmentVariables_NullConfig_LogNoConfig() + { + this.environmentVariableDeploymentService.SetEnvironmentVariables(null); + + this.loggerMock.VerifyLog(x => x.LogInformation("No environment variables have been configured.")); + } + + [Fact] + public void SetEnvironmentVariables_DefinitionDoesNotExist_LogNoVariableExist() + { + var environmentVariableConfigs = new Dictionary + { + { "variable1", "variable1_value" }, + }; + + var entityCollection = new EntityCollection + { + EntityName = Constants.EnvironmentVariableDefinition.LogicalName, + Entities = { }, + }; + + this.crmServiceAdapterMock + .Setup(x => x.RetrieveMultipleByAttribute( + Constants.EnvironmentVariableDefinition.LogicalName, + Constants.EnvironmentVariableDefinition.Fields.SchemaName, + It.Is>(values => values.Contains(environmentVariableConfigs.ElementAt(0).Key)), + It.IsAny())) + .Returns(entityCollection) + .Verifiable(); + + this.environmentVariableDeploymentService.SetEnvironmentVariables(environmentVariableConfigs); + this.loggerMock.VerifyLog(x => x.LogError($"Environment variable {environmentVariableConfigs.ElementAt(0).Key} not found on target instance.")); + } + + [Fact] + public void SetEnvironmentVariables_NoExistingValue_CreatesNewValue() + { + var environmentVariableConfigs = new Dictionary + { + { "variable1", "variable1_value" }, + }; + + var definitionId = Guid.NewGuid(); + + var definitionEntityCollection = new EntityCollection + { + EntityName = Constants.EnvironmentVariableDefinition.LogicalName, + Entities = + { + new Entity(Constants.EnvironmentVariableDefinition.LogicalName, definitionId), + }, + }; + + var valueEntityCollection = new EntityCollection + { + EntityName = Constants.EnvironmentVariableValue.LogicalName, + Entities = { }, + }; + + this.crmServiceAdapterMock + .Setup(x => x.RetrieveMultipleByAttribute( + Constants.EnvironmentVariableDefinition.LogicalName, + Constants.EnvironmentVariableDefinition.Fields.SchemaName, + It.Is>(values => values.Contains(environmentVariableConfigs.ElementAt(0).Key)), + It.IsAny())) + .Returns(definitionEntityCollection) + .Verifiable(); + + this.crmServiceAdapterMock + .Setup(x => x.RetrieveMultipleByAttribute( + Constants.EnvironmentVariableValue.LogicalName, + Constants.EnvironmentVariableValue.Fields.EnvironmentVariableDefinitonId, + It.Is>(values => values.Contains(definitionId)), + It.IsAny())) + .Returns(valueEntityCollection) + .Verifiable(); + + this.crmServiceAdapterMock + .Setup(x => x.Create( + It.Is(entity => + entity.LogicalName == Constants.EnvironmentVariableValue.LogicalName && + ((EntityReference)entity.Attributes[Constants.EnvironmentVariableValue.Fields.EnvironmentVariableDefinitonId]).Id == definitionId && + (string)entity.Attributes[Constants.EnvironmentVariableValue.Fields.Value] == environmentVariableConfigs.ElementAt(0).Value))) + .Returns(Guid.NewGuid()) + .Verifiable(); + + this.environmentVariableDeploymentService.SetEnvironmentVariables(environmentVariableConfigs); + + this.crmServiceAdapterMock.VerifyAll(); + } + + [Fact] + public void SetEnvironmentVariables_ExistingValue_UpdatesExistingValue() + { + var environmentVariableConfigs = new Dictionary + { + { "variable1", "variable1_value" }, + }; + + var definitionId = Guid.NewGuid(); + var valueId = Guid.NewGuid(); + + var definitionEntityCollection = new EntityCollection + { + EntityName = Constants.EnvironmentVariableDefinition.LogicalName, + Entities = + { + new Entity(Constants.EnvironmentVariableDefinition.LogicalName, definitionId), + }, + }; + + var valueEntityCollection = new EntityCollection + { + EntityName = Constants.EnvironmentVariableValue.LogicalName, + Entities = + { + new Entity(Constants.EnvironmentVariableValue.LogicalName, valueId), + }, + }; + + this.crmServiceAdapterMock + .Setup(x => x.RetrieveMultipleByAttribute( + Constants.EnvironmentVariableDefinition.LogicalName, + Constants.EnvironmentVariableDefinition.Fields.SchemaName, + It.Is>(values => values.Contains(environmentVariableConfigs.ElementAt(0).Key)), + It.IsAny())) + .Returns(definitionEntityCollection) + .Verifiable(); + + this.crmServiceAdapterMock + .Setup(x => x.RetrieveMultipleByAttribute( + Constants.EnvironmentVariableValue.LogicalName, + Constants.EnvironmentVariableValue.Fields.EnvironmentVariableDefinitonId, + It.Is>(values => values.Contains(definitionId)), + It.IsAny())) + .Returns(valueEntityCollection) + .Verifiable(); + + this.crmServiceAdapterMock + .Setup(x => x.Update( + It.Is(entity => + entity.LogicalName == Constants.EnvironmentVariableValue.LogicalName && + entity.Id == valueId && + (string)entity.Attributes[Constants.EnvironmentVariableValue.Fields.Value] == environmentVariableConfigs.ElementAt(0).Value))) + .Verifiable(); + + this.environmentVariableDeploymentService.SetEnvironmentVariables(environmentVariableConfigs); + + this.crmServiceAdapterMock.VerifyAll(); + } + } +} diff --git a/tests/Capgemini.PowerApps.PackageDeployerTemplate.UnitTests/Services/MailboxDeploymentServiceTests.cs b/tests/Capgemini.PowerApps.PackageDeployerTemplate.UnitTests/Services/MailboxDeploymentServiceTests.cs index 099e997..1e8a67d 100644 --- a/tests/Capgemini.PowerApps.PackageDeployerTemplate.UnitTests/Services/MailboxDeploymentServiceTests.cs +++ b/tests/Capgemini.PowerApps.PackageDeployerTemplate.UnitTests/Services/MailboxDeploymentServiceTests.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Linq; using Capgemini.PowerApps.PackageDeployerTemplate.Adapters; - using Capgemini.PowerApps.PackageDeployerTemplate.Config; using Capgemini.PowerApps.PackageDeployerTemplate.Services; using Microsoft.Extensions.Logging; using Microsoft.Xrm.Sdk; diff --git a/tests/pdt_PackageDeployerTemplate_MockSolution/src/environmentvariabledefinitions/pdt_TestVariable/environmentvariabledefinition.xml b/tests/pdt_PackageDeployerTemplate_MockSolution/src/environmentvariabledefinitions/pdt_TestVariable/environmentvariabledefinition.xml new file mode 100644 index 0000000..ca5ce71 --- /dev/null +++ b/tests/pdt_PackageDeployerTemplate_MockSolution/src/environmentvariabledefinitions/pdt_TestVariable/environmentvariabledefinition.xml @@ -0,0 +1,9 @@ + + + + 1.0.0.0 + 1 + 0 + 100000000 + \ No newline at end of file