diff --git a/CHANGELOG.md b/CHANGELOG.md index fad7089..36210bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [0.1.1] + +- `Read-FeatureFile` uses a new static method to read the file and set the + FilePath. + ## [0.1.0] Initial Release ### Added diff --git a/Gatekeeper/Classes/FeatureFlag.ps1 b/Gatekeeper/Classes/FeatureFlag.ps1 index 0824226..f3d0376 100644 --- a/Gatekeeper/Classes/FeatureFlag.ps1 +++ b/Gatekeeper/Classes/FeatureFlag.ps1 @@ -156,13 +156,23 @@ class FeatureFlag { } # Example usage: - # $json = Get-Content -Raw -Path 'd:\Gatekeeper\Gatekeeper\featureflag.json' + # $json = Get-Content -Raw -Path 'd:\Gatekeeper\Gatekeeper\featureFlag.json' # $featureFlag = [FeatureFlag]::FromJson($json) static [FeatureFlag] FromJson([string]$json) { $data = ConvertFrom-Json $json -AsHashtable return [FeatureFlag]::new($data) } + static [FeatureFlag] FromFile([string]$filePath) { + if (-not (Test-Path $filePath)) { + throw "File not found: $filePath" + } + $json = Get-Content -Raw -Path $filePath + $featureFlag = [FeatureFlag]::FromJson($json) + $featureFlag.FilePath = $filePath + return $featureFlag + } + [void]Save() { if ($null -eq $this.FilePath) { throw "No file path specified to save FeatureFlag." diff --git a/Gatekeeper/Gatekeeper.psd1 b/Gatekeeper/Gatekeeper.psd1 index 9308ea4..c947a7c 100644 --- a/Gatekeeper/Gatekeeper.psd1 +++ b/Gatekeeper/Gatekeeper.psd1 @@ -9,28 +9,28 @@ @{ # Script module or binary module file associated with this manifest. - RootModule = 'Gatekeeper.psm1' + RootModule = 'Gatekeeper.psm1' # Version number of this module. - ModuleVersion = '0.1.0' + ModuleVersion = '0.1.1' # Supported PSEditions # CompatiblePSEditions = @() # ID used to uniquely identify this module - GUID = '7c2fb6fe-e024-4c39-b687-84fbe7473e3f' + GUID = '7c2fb6fe-e024-4c39-b687-84fbe7473e3f' # Author of this module - Author = 'Gilbert Sanchez' + Author = 'Gilbert Sanchez' # Company or vendor of this module - CompanyName = 'Gilbert Sanchez' + CompanyName = 'Gilbert Sanchez' # Copyright statement for this module - Copyright = '(c) Gilbert Sanchez. All rights reserved.' + Copyright = '(c) Gilbert Sanchez. All rights reserved.' # Description of the functionality provided by this module - Description = 'Helps implement feature flags in your PowerShell projects.' + Description = 'Helps implement feature flags in your PowerShell projects.' # Minimum version of the PowerShell engine required by this module # PowerShellVersion = '' @@ -51,9 +51,9 @@ # ProcessorArchitecture = '' # Modules that must be imported into the global environment prior to importing this module - RequiredModules = @( + RequiredModules = @( @{ - ModuleName = 'Configuration' + ModuleName = 'Configuration' ModuleVersion = '1.6.0' } ) @@ -62,7 +62,7 @@ # RequiredAssemblies = @() # Script files (.ps1) that are run in the caller's environment prior to importing this module. - ScriptsToProcess = @('Enums\Effect.ps1', 'Classes\Property.ps1', 'Classes\FeatureFlag.ps1') + ScriptsToProcess = @('Enums\Effect.ps1', 'Classes\Property.ps1', 'Classes\FeatureFlag.ps1') # Type files (.ps1xml) to be loaded when importing this module # TypesToProcess = @() @@ -77,13 +77,13 @@ FunctionsToExport = '*' # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. - CmdletsToExport = '*' + CmdletsToExport = '*' # Variables to export from this module VariablesToExport = '*' # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. - AliasesToExport = '*' + AliasesToExport = '*' # DSC resources to export from this module # DscResourcesToExport = @() @@ -95,12 +95,12 @@ # FileList = @() # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. - PrivateData = @{ + PrivateData = @{ PSData = @{ # Tags applied to this module. These help with module discovery in online galleries. - Tags = @( + Tags = @( 'PSEdition_Core', 'Windows', 'Linux', @@ -108,16 +108,16 @@ ) # A URL to the license for this module. - LicenseUri = 'https://github.com/HeyItsGilbert/Gatekeeper/blob/master/LICENSE' + LicenseUri = 'https://github.com/HeyItsGilbert/Gatekeeper/blob/master/LICENSE' # A URL to the main website for this project. - ProjectUri = 'https://github.com/HeyItsGilbert/Gatekeeper/' + ProjectUri = 'https://github.com/HeyItsGilbert/Gatekeeper/' # A URL to an icon representing this module. - IconUri = 'https://raw.githubusercontent.com/HeyItsGilbert/Gatekeeper/main/static/icon.png' + IconUri = 'https://raw.githubusercontent.com/HeyItsGilbert/Gatekeeper/main/static/icon.png' # ReleaseNotes of this module - ReleaseNotes = 'https://github.com/HeyItsGilbert/Gatekeeper/blob/master/CHANGELOG.md' + ReleaseNotes = 'https://github.com/HeyItsGilbert/Gatekeeper/blob/master/CHANGELOG.md' # Prerelease string of this module # Prerelease = '' diff --git a/Gatekeeper/Public/Read-FeatureFlag.ps1 b/Gatekeeper/Public/Read-FeatureFlag.ps1 index 98a9baf..482eea5 100644 --- a/Gatekeeper/Public/Read-FeatureFlag.ps1 +++ b/Gatekeeper/Public/Read-FeatureFlag.ps1 @@ -20,7 +20,7 @@ function Read-FeatureFlag { Read the feature from disk. #> [CmdletBinding()] - [OutputType([System.Collections.Generic.List[PropertySet]])] + [OutputType([FeatureFlag])] param ( [Parameter(Mandatory, Position = 0, ParameterSetName = 'ByName')] [ValidateNotNullOrEmpty()] @@ -29,9 +29,7 @@ function Read-FeatureFlag { [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'ByFilePath')] $FilePath ) - begin { - $featureFlags = [System.Collections.Generic.List[PropertySet]]::new() - } + begin {} process { if ($PSBoundParameters.ContainsKey('FilePath')) { Write-Verbose "Reading FeatureFlag from file: $FilePath" @@ -40,13 +38,8 @@ function Read-FeatureFlag { $folder = Get-FeatureFlagFolder $FilePath = Join-Path $folder "$Name.json" } - $json = Get-Content -Raw $FilePath - $featureFlags.Add( - ([FeatureFlag]::FromJson($json)) - ) + [FeatureFlag]::FromFile($FilePath) } - end { - return $featureFlags - } + end {} } diff --git a/tests/NewFiles.tests.ps1 b/tests/NewFiles.tests.ps1 index cd69c3f..8e79c94 100644 --- a/tests/NewFiles.tests.ps1 +++ b/tests/NewFiles.tests.ps1 @@ -19,9 +19,32 @@ BeforeDiscovery { } Describe 'File Creations' { BeforeAll { + @( + 'PropertySet', + 'FeatureFlags', + 'Configuration' + ) | ForEach-Object { + $folder = Join-Path -Path (Get-PSDrive TestDrive).Root -ChildPath $_ + if (-not (Test-Path -Path $folder)) { + New-Item -Path $folder -ItemType Directory | Out-Null + } + } + Mock -CommandName 'Get-PropertySetFolder' -ModuleName $env:BHProjectName { + return (Join-Path -Path (Get-PSDrive TestDrive).Root -ChildPath 'PropertySet') + } + Mock -CommandName 'Get-FeatureFlagFolder' -ModuleName $env:BHProjectName { + return (Join-Path -Path (Get-PSDrive TestDrive).Root -ChildPath 'FeatureFlags') + } + Mock -CommandName 'Get-ConfigurationPath' -ModuleName Configuration { + return (Join-Path -Path (Get-PSDrive TestDrive).Root -ChildPath 'Configuration') + } # Override the default file path for testing - Mock Get-ConfigurationPath -ModuleName Configuration { - return (Get-PSDrive TestDrive).Root + $global:GatekeeperConfiguration = @{ + FilePaths = @{ + #Schemas = Join-Path (Get-PSDrive TestDrive).Root 'Schemas' + FeatureFlags = Join-Path (Get-PSDrive TestDrive).Root 'FeatureFlags' + PropertySet = Join-Path (Get-PSDrive TestDrive).Root 'PropertySet' + } } } # I'm doing a no-no IMO, but this is probably fine. @@ -39,8 +62,7 @@ Describe 'File Creations' { Context 'Feature Flag Creation' { BeforeAll { # Mock the Get-FeatureFlagFolder to return a test path - Mock Get-FeatureFlagFolder -ModuleName Gatekeeper { - + Mock Get-FeatureFlagFolder -ModuleName $env:BHProjectName { return (Get-PSDrive TestDrive).Root } $condition = New-Condition -Property 'UserRole' -Operator 'Equals' -Value 'Admin' diff --git a/tests/Read-FeatureFlag.Tests.ps1 b/tests/Read-FeatureFlag.Tests.ps1 new file mode 100644 index 0000000..9df2fe4 --- /dev/null +++ b/tests/Read-FeatureFlag.Tests.ps1 @@ -0,0 +1,32 @@ +BeforeDiscovery { + $manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest + $outputDir = Join-Path -Path $env:BHProjectPath -ChildPath 'Output' + $outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName + $outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion + $outputModVerManifest = Join-Path -Path $outputModVerDir -ChildPath "$($env:BHProjectName).psd1" + + # Get module commands + # Remove all versions of the module from the session. Pester can't handle multiple versions. + Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore + Import-Module -Name $outputModVerManifest -Verbose:$false -ErrorAction Stop +} +Describe 'Read-FeatureFlag' { + BeforeAll { + $script:actual = Read-FeatureFlag -FilePath "$PSScriptRoot\fixtures\FeatureFlag.json" + } + It 'Throws file path error' { + { Read-FeatureFlag -FilePath 'fakePath.json' } | Should -Throw -ExpectedMessage 'File not found: fakePath.json' + } + It 'Returns a FeatureFlag object' { + $script:actual | Should -BeOfType 'FeatureFlag' + } + It 'Has the correct property: <_.Name>' -ForEach @( + @{ Name = 'Name'; Type = 'String'; Value = 'New Startup Sound' }, + @{ Name = 'Description'; Type = 'String'; Value = 'Roll out new screaming goat start up sound.' }, + @{ Name = 'Tags'; Type = 'String'; Value = @('Goat', 'Managed') }, + @{ Name = 'DefaultEffect'; Type = 'Effect'; Value = "Deny" } + ) { + $script:actual.$($_.Name) | Should -BeOfType $_.Type + $script:actual.$($_.Name) | Should -Be $_.Value + } +} diff --git a/tests/fixtures/FeatureFlag.json b/tests/fixtures/FeatureFlag.json new file mode 100644 index 0000000..67b623e --- /dev/null +++ b/tests/fixtures/FeatureFlag.json @@ -0,0 +1,59 @@ +{ + "$schema": "../../Gatekeeper/Schemas/FeatureFlag.json", + "Name": "New Startup Sound", + "Description": "Roll out new screaming goat start up sound.", + "Version": "1.0.0", + "Author": "Your Name aka who to e-mail when customers are upset", + "Tags": [ + "Goat", + "Managed" + ], + "DefaultEffect": "Deny", + "Rules": [ + { + "Name": "Audit staging", + "Effect": "Audit", + "Conditions": { + "Property": "Environment", + "Operator": "Equals", + "Value": "Staging" + } + }, + { + "Name": "Warn Production", + "Effect": "Warn", + "Conditions": { + "Property": "Environment", + "Operator": "Equals", + "Value": "Production" + } + }, + { + "Name": "Allow Staging and Complaint or 10%", + "Effect": "Allow", + "Conditions": { + "AllOf": [ + { + "AnyOf": [ + { + "Property": "IsCompliant", + "Operator": "Equals", + "Value": "true" + }, + { + "Property": "Percent", + "Operator": "LessThan", + "Value": "11" + } + ] + }, + { + "Property": "Environment", + "Operator": "Equals", + "Value": "Staging" + } + ] + } + } + ] +}