forked from d365collaborative/d365fo.tools
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request d365collaborative#871 from FH-Inway/deploy-resources
✨ Adds new function Publish-D365WebResources to simplify this process
- Loading branch information
Showing
6 changed files
with
406 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
|
||
<# | ||
.SYNOPSIS | ||
Deploy web resources | ||
.DESCRIPTION | ||
Deploys the Dynamics 365 for Finance and Operations web resources to the AOS service web root path. | ||
.PARAMETER PackageDirectory | ||
Path to the package directory containing the web resources. | ||
.PARAMETER AosServiceWebRootPath | ||
Path to the AOS service web root path. | ||
.EXAMPLE | ||
PS C:\> Publish-D365WebResources | ||
This will deploy the web resources to the AOS service web root path. | ||
.NOTES | ||
Author: Florian Hopfner (@FH-Inway) | ||
#> | ||
function Publish-D365WebResources { | ||
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', 'Publish-D365WebResources')] | ||
[CmdletBinding()] | ||
param ( | ||
[Parameter(Mandatory = $false)] | ||
[PsfDirectory] $PackageDirectory = $Script:PackageDirectory, | ||
|
||
[Parameter(Mandatory = $false)] | ||
[PsfDirectory] $AosServiceWebRootPath = $Script:AOSPath | ||
) | ||
|
||
Invoke-TimeSignal -Start | ||
|
||
Write-PSFMessage -Level Verbose -Message "Initializing web resources deplyoment." | ||
|
||
$webResourceTypes = @("Images", "Scripts", "Styles", "Html") | ||
|
||
Write-PSFMessage -Level Debug -Message "Creating web resources directory." | ||
$resourcesDirectory = Join-Path $AosServiceWebRootPath "Resources" | ||
Test-PathExists -Path $resourcesDirectory -Type Container -Create | Out-Null | ||
|
||
$params = @{ | ||
ResourceTypes = $webResourceTypes | ||
PublishingDirectory = $resourcesDirectory | ||
PackageDirectory = $PackageDirectory | ||
AosServiceWebRootPath = $AosServiceWebRootPath | ||
} | ||
Publish-D365FOResources @params | ||
|
||
Write-PSFMessage -Level Host -Message "Web resources deployment completed." | ||
|
||
Invoke-TimeSignal -End | ||
} |
196 changes: 196 additions & 0 deletions
196
d365fo.tools/internal/functions/publish-d365foresources.ps1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
|
||
<# | ||
.SYNOPSIS | ||
Publish resources | ||
.DESCRIPTION | ||
Publishes Dynamics 365 for Finance and Operations resources to the publishing directory. | ||
.PARAMETER ResourceTypes | ||
The types of resources to publish. | ||
.PARAMETER PublishingDirectory | ||
The directory to publish the resources to. Each resource type will be published to a subdirectory. | ||
.PARAMETER PackageDirectory | ||
The directory containing the resources. | ||
.PARAMETER AosServiceWebRootPath | ||
The path to the AOS service web root containing the metadata assemblies to access the resources. | ||
.EXAMPLE | ||
PS C:\> Publish-D365FOResources -ResourceTypes Images,Scripts,Styles,Html -PublishingDirectory C:\temp\resources | ||
This will publish the resources of the types Images, Scripts, Styles, and Html to the directory C:\temp\resources. | ||
.NOTES | ||
Author: Florian Hopfner (@FH-Inway) | ||
#> | ||
function Publish-D365FOResources { | ||
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', 'Publish-D365FOResources')] | ||
[CmdletBinding()] | ||
param ( | ||
[Parameter(Mandatory = $true)] | ||
[string[]] $ResourceTypes, | ||
|
||
[Parameter(Mandatory = $true)] | ||
[string] $PublishingDirectory, | ||
|
||
[Parameter(Mandatory = $false)] | ||
[PsfDirectory] $PackageDirectory = $Script:PackageDirectory, | ||
|
||
[Parameter(Mandatory = $false)] | ||
[PsfDirectory] $AosServiceWebRootPath = $Script:AOSPath | ||
) | ||
|
||
Invoke-TimeSignal -Start | ||
|
||
Write-PSFMessage -Level Verbose -Message "Initializing resources publishing." | ||
|
||
$resourcesDirectory = $PublishingDirectory | ||
foreach ($resourceType in $ResourceTypes) { | ||
$resourceTypeDirectory = Join-Path $resourcesDirectory $resourceType | ||
Test-PathExists -Path $resourceTypeDirectory -Type Container -Create | Out-Null | ||
} | ||
|
||
Import-Assemblies -AosServiceWebRootPath $AosServiceWebRootPath | ||
# For unknown reasons, the provider cannot be initialized in a separate function. | ||
# If this is done, $metadataProviderViaRuntime.Resources is null. | ||
Write-PSFMessage -Level Debug -Message "Initializing metadata runtime provider." | ||
$runtimeProviderConfiguration = New-Object Microsoft.Dynamics.AX.Metadata.Storage.Runtime.RuntimeProviderConfiguration -ArgumentList $PackageDirectory | ||
$metadataProviderFactoryViaRuntime = New-Object Microsoft.Dynamics.AX.Metadata.Storage.MetadataProviderFactory | ||
$metadataProviderViaRuntime = $metadataProviderFactoryViaRuntime.CreateRuntimeProvider($runtimeProviderConfiguration) | ||
|
||
Write-PSFMessage -Level Verbose -Message "Starting resources publishing" | ||
$resources = $metadataProviderViaRuntime.Resources.GetPrimaryKeys() | ||
foreach ($resourceItem in $resources) { | ||
$params = @{ | ||
ResourceItem = $resourceItem | ||
MetadataProviderViaRuntime = $metadataProviderViaRuntime | ||
ResourceTypes = $ResourceTypes | ||
ResourcesDirectory = $resourcesDirectory | ||
} | ||
Publish-Resource @params | ||
} | ||
Write-PSFMessage -Level Host -Message "Resources publishing completed." | ||
|
||
Invoke-TimeSignal -End | ||
} | ||
|
||
function Import-Assemblies { | ||
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', 'Import-Assemblies')] | ||
[CmdletBinding()] | ||
param ( | ||
[Parameter(Mandatory = $true)] | ||
[PsfDirectory] $AosServiceWebRootPath | ||
) | ||
|
||
Write-PSFMessage -Level Debug -Message "Importing required assemblies." | ||
[System.Collections.ArrayList] $Files2Process = New-Object -TypeName "System.Collections.ArrayList" | ||
$binDir = Join-Path $AosServiceWebRootPath "bin" | ||
$null = $Files2Process.Add((Join-Path $binDir Microsoft.Dynamics.AX.Metadata.Core.dll)) | ||
$null = $Files2Process.Add((Join-Path $binDir Microsoft.Dynamics.AX.Metadata.dll)) | ||
$null = $Files2Process.Add((Join-Path $binDir Microsoft.Dynamics.AX.Metadata.Storage.dll)) | ||
$null = $Files2Process.Add((Join-Path $binDir Microsoft.Dynamics.Performance.Instrumentation.dll)) | ||
$null = $Files2Process.Add((Join-Path $binDir Microsoft.Dynamics.ApplicationPlatform.XppServices.Instrumentation.dll)) | ||
Import-AssemblyFileIntoMemory -Path $($Files2Process.ToArray()) | ||
} | ||
|
||
function Publish-Resource { | ||
[CmdletBinding()] | ||
param ( | ||
[Parameter(Mandatory = $true)] | ||
[object] $ResourceItem, | ||
|
||
[Parameter(Mandatory = $true)] | ||
[object] $MetadataProviderViaRuntime, | ||
|
||
[Parameter(Mandatory = $true)] | ||
[string[]] $ResourceTypes, | ||
|
||
[Parameter(Mandatory = $true)] | ||
[string] $ResourcesDirectory | ||
) | ||
|
||
$resourceName = [System.String]$ResourceItem | ||
Write-PSFMessage -Level Debug -Message "Processing resource '$resourceName'." | ||
$resourceHeader = New-Object -TypeName Microsoft.Dynamics.AX.Metadata.MetaModel.MetaReadHeader | ||
$resource = $MetadataProviderViaRuntime.Resources.Read($resourceName, [ref]$resourceHeader) | ||
$resourceTimestamp = $MetadataProviderViaRuntime.Resources.GetContentTimestampUtc($resource, $resourceHeader) | ||
$resourceType = $resource.TypeOfResource | ||
|
||
$resourcePath = Join-Path $ResourcesDirectory $resourceType | ||
$resourceFilePath = Join-Path $resourcePath $resource.FileName | ||
|
||
Write-PSFMessage -Level Debug -Message "Checking resource '$resourceName' of type '$resourceType' for publishing." | ||
$resourceData = @{ | ||
ResourceType = $resourceType | ||
ResourceName = $resourceName | ||
ResourceTimestamp = $resourceTimestamp | ||
} | ||
$params = @{ | ||
ResourceData = $resourceData | ||
AllowedResourceTypes = $ResourceTypes | ||
ResourceFilePath = $resourceFilePath | ||
} | ||
$shouldPublishResource = Test-PublishResource @params | ||
if (-not $shouldPublishResource) { | ||
Write-PSFMessage -Level Debug -Message "Resource '$resourceName' is not being published." | ||
continue | ||
} | ||
|
||
Write-PSFMessage -Level Debug -Message "Publishing resource '$resourceName' to '$resourceFilePath'." | ||
$sourceStream = $MetadataProviderViaRuntime.Resources.GetContent($resource, $resourceHeader) | ||
if ($sourceStream) { | ||
$argumentList = @( | ||
$resourceFilePath, | ||
[System.IO.FileMode]::Create, | ||
[System.IO.FileAccess]::Write | ||
) | ||
$targetStream = New-Object -TypeName System.IO.FileStream -ArgumentList $argumentList | ||
$sourceStream.CopyTo($targetStream) | ||
|
||
$sourceStream.Close() | ||
$targetStream.Close() | ||
Write-PSFMessage -Level Debug -Message "Resource '$resourceName' published successfully." | ||
} | ||
} | ||
|
||
function Test-PublishResource { | ||
[CmdletBinding()] | ||
[OutputType([System.Boolean])] | ||
param ( | ||
[Parameter(Mandatory = $true)] | ||
[hashtable] $ResourceData, | ||
|
||
[Parameter(Mandatory = $true)] | ||
[string[]] $AllowedResourceTypes, | ||
|
||
[Parameter(Mandatory = $true)] | ||
[string] $ResourceFilePath | ||
) | ||
|
||
$resourceType = $ResourceData.ResourceType | ||
$resourceName = $ResourceData.ResourceName | ||
$resourceTimestamp = $ResourceData.ResourceTimestamp | ||
|
||
$isAResourceTypeToPublish = $AllowedResourceTypes -contains $resourceType | ||
if (-not $isAResourceTypeToPublish) { | ||
Write-PSFMessage -Level Debug -Message "Resource '$resourceName' of type '$resourceType' is not a resource type to publish. Skipping." | ||
return $false | ||
} | ||
|
||
if (-not (Test-PathExists -Path $ResourceFilePath -Type Leaf -WarningAction SilentlyContinue -ErrorAction SilentlyContinue)) { | ||
Write-PSFMessage -Level Debug -Message "Resource '$resourceName' does not exist. Will be published." | ||
return $true | ||
} | ||
|
||
$existingFileTimestamp = (Get-ItemProperty $resourceFilePath).LastWriteTimeUtc | ||
if ($existingFileTimestamp -ne $ResourceTimestamp) { | ||
Write-PSFMessage -Level Debug -Message "Resource '$ResourceName' is outdated (resource time stamp: $ResouceTimestamp, existing file time stamp: $existingFileTimestamp). Will be published." | ||
return $true | ||
} | ||
|
||
Write-PSFMessage -Level Debug -Message "Resource '$ResourceName' is up to date." | ||
return $false | ||
} |
49 changes: 49 additions & 0 deletions
49
d365fo.tools/tests/functions/Publish-D365WebResources.Tests.ps1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
Describe "Publish-D365WebResources Unit Tests" -Tag "Unit" { | ||
BeforeAll { | ||
# Place here all things needed to prepare for the tests | ||
} | ||
AfterAll { | ||
# Here is where all the cleanup tasks go | ||
} | ||
|
||
Describe "Ensuring unchanged command signature" { | ||
It "should have the expected parameter sets" { | ||
(Get-Command Publish-D365WebResources).ParameterSets.Name | Should -Be '__AllParameterSets' | ||
} | ||
|
||
It 'Should have the expected parameter PackageDirectory' { | ||
$parameter = (Get-Command Publish-D365WebResources).Parameters['PackageDirectory'] | ||
$parameter.Name | Should -Be 'PackageDirectory' | ||
$parameter.ParameterType.ToString() | Should -Be PSFramework.Parameter.PathDirectoryParameter | ||
$parameter.IsDynamic | Should -Be $False | ||
$parameter.ParameterSets.Keys | Should -Be '__AllParameterSets' | ||
$parameter.ParameterSets.Keys | Should -Contain '__AllParameterSets' | ||
$parameter.ParameterSets['__AllParameterSets'].IsMandatory | Should -Be $False | ||
$parameter.ParameterSets['__AllParameterSets'].Position | Should -Be 0 | ||
$parameter.ParameterSets['__AllParameterSets'].ValueFromPipeline | Should -Be $False | ||
$parameter.ParameterSets['__AllParameterSets'].ValueFromPipelineByPropertyName | Should -Be $False | ||
$parameter.ParameterSets['__AllParameterSets'].ValueFromRemainingArguments | Should -Be $False | ||
} | ||
It 'Should have the expected parameter AosServiceWebRootPath' { | ||
$parameter = (Get-Command Publish-D365WebResources).Parameters['AosServiceWebRootPath'] | ||
$parameter.Name | Should -Be 'AosServiceWebRootPath' | ||
$parameter.ParameterType.ToString() | Should -Be PSFramework.Parameter.PathDirectoryParameter | ||
$parameter.IsDynamic | Should -Be $False | ||
$parameter.ParameterSets.Keys | Should -Be '__AllParameterSets' | ||
$parameter.ParameterSets.Keys | Should -Contain '__AllParameterSets' | ||
$parameter.ParameterSets['__AllParameterSets'].IsMandatory | Should -Be $False | ||
$parameter.ParameterSets['__AllParameterSets'].Position | Should -Be 1 | ||
$parameter.ParameterSets['__AllParameterSets'].ValueFromPipeline | Should -Be $False | ||
$parameter.ParameterSets['__AllParameterSets'].ValueFromPipelineByPropertyName | Should -Be $False | ||
$parameter.ParameterSets['__AllParameterSets'].ValueFromRemainingArguments | Should -Be $False | ||
} | ||
} | ||
|
||
Describe "Testing parameterset __AllParameterSets" { | ||
<# | ||
__AllParameterSets - | ||
__AllParameterSets -PackageDirectory -AosServiceWebRootPath | ||
#> | ||
} | ||
|
||
} |
Oops, something went wrong.