refactor: move page meta to common meta module
This commit is contained in:
parent
88aae9e3a6
commit
496d70360b
6 changed files with 804 additions and 186 deletions
291
src/Attachment.psm1
Executable file
291
src/Attachment.psm1
Executable file
|
|
@ -0,0 +1,291 @@
|
|||
#!/usr/bin/env pwsh
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
|
||||
function New-Attachment
|
||||
{
|
||||
<#
|
||||
.SYNOPSIS
|
||||
|
||||
Add a new attachment
|
||||
|
||||
.DESCRIPTION
|
||||
|
||||
|
||||
.OUTPUTS
|
||||
|
||||
When no $Title is provided and the $Manifest array only contains 1
|
||||
page metadata, the ``Count`` attribute is faulty. Why? Don't know.
|
||||
|
||||
.EXAMPLE
|
||||
Add-ConfluencePage `
|
||||
-Host 'confluence.contoso.com' `
|
||||
-Space 'TIARA' `
|
||||
-Title 'Testitest' `
|
||||
-Content @{}
|
||||
#>
|
||||
Param(
|
||||
# confluence instance hostname
|
||||
[Parameter(Mandatory)] [string]$Host,
|
||||
# name of the Confluence space to publish to
|
||||
[Parameter(Mandatory)] [string]$Space,
|
||||
# title of page to be published
|
||||
[Parameter()] [string]$Name,
|
||||
# attachments manifest
|
||||
[Parameter(Mandatory, ValueFromPipeline)]
|
||||
[PSCustomObject[]]$Manifest,
|
||||
# attachments manifest index, mandatory for ancestor lookup
|
||||
[Parameter(Mandatory)] [Collections.Hashtable]$Index,
|
||||
# pages manifest
|
||||
[Parameter(Mandatory)]
|
||||
[PSCustomObject[]]$PagesManifest,
|
||||
# pages manifest index, mandatory for container page lookup
|
||||
[Parameter(Mandatory)] [Collections.Hashtable]$PagesIndex,
|
||||
# flag on whether to fail hard, or just continue
|
||||
[Parameter()] [Switch]$Strict
|
||||
)
|
||||
|
||||
Begin
|
||||
{
|
||||
$pat = Get-PersonalAccessToken $Host
|
||||
}
|
||||
|
||||
Process
|
||||
{
|
||||
If ($Name -And $Manifest[$Index.$Name])
|
||||
{
|
||||
$Manifest = @(
|
||||
$Manifest[$Index.$Name]
|
||||
)
|
||||
}
|
||||
|
||||
ForEach($attachmentMeta in $Manifest)
|
||||
{
|
||||
If ($Name -And $attachmentMeta.Name -ne $Name) {continue}
|
||||
|
||||
|
||||
$containerPageMeta = $PagesManifest[
|
||||
$PagesIndex."$($attachmentMeta.ContainerPageTitle)"
|
||||
]
|
||||
|
||||
If (-Not $containerPageMeta)
|
||||
{
|
||||
throw (
|
||||
"Get-AttachmentMeta: ``$($attachmentMeta.Name)``: " +
|
||||
"unable to lookup metadata for container page " +
|
||||
"title ``$($attachmentMeta.ContainerPageTitle)``." +
|
||||
"This is fatal."
|
||||
)
|
||||
}
|
||||
|
||||
If (-Not $containerPageMeta.Id)
|
||||
{
|
||||
$errMsg = (
|
||||
"Get-AttachmentMeta: ``$($attachmentMeta.Name)``: " +
|
||||
"container page titled" +
|
||||
"``$($attachmentMeta.ContainerPageTitle)`` " +
|
||||
"has no id, which means that the page has " +
|
||||
"(presumably) not yet been published."
|
||||
)
|
||||
|
||||
If ($Strict) {throw $errMsg}
|
||||
|
||||
Write-Host "$errMsg Continuing nonetheless..."
|
||||
|
||||
$attachmentMeta
|
||||
|
||||
continue
|
||||
}
|
||||
ElseIf (-Not $attachmentMeta.Ref)
|
||||
{
|
||||
$errMsg = (
|
||||
"``$($attachmentMeta.Name)``: no reference to local " +
|
||||
'content for attachment .'
|
||||
)
|
||||
|
||||
If ($Strict) {throw $errMsg}
|
||||
|
||||
Write-Host $errMsg
|
||||
|
||||
# not outputting the metadata, since it's invalid anyway
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
ElseIf ($attachmentMeta.Id)
|
||||
{
|
||||
|
||||
Write-Debug (
|
||||
"New-Attachment: ``$($attacmentMeta.Name)``: skipping, " +
|
||||
"already published ($($attachmentMeta.Id))"
|
||||
)
|
||||
|
||||
$attachmentMeta
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
Else
|
||||
{
|
||||
Write-Host (
|
||||
"New-Attachment: ``$($attachmentMeta.Name)``: creating"
|
||||
)
|
||||
|
||||
Try
|
||||
{
|
||||
$rawContent = [IO.File]::ReadAllBytes($attachmentMeta.Ref)
|
||||
|
||||
$content = [Text.Encoding]::GetEncoding(
|
||||
'ISO-8859-1'
|
||||
).GetString($rawContent)
|
||||
}
|
||||
|
||||
Catch
|
||||
{
|
||||
$errMsg = "``New-Attachment: $($attachmentMeta.Name)``: $_"
|
||||
|
||||
If ($Strict) {throw $errMsg}
|
||||
|
||||
Write-Host $errMsg
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
$boundary = [Guid]::NewGuid().ToString()
|
||||
|
||||
$LF = "`r`n";
|
||||
|
||||
$transportBody = (
|
||||
"--$boundary",
|
||||
(
|
||||
"Content-Disposition: form-data; name=`"file`"; " +
|
||||
"filename=`"$($attachmentMeta.Name)`""
|
||||
),
|
||||
"Content-Type: $($attachmentMeta.MimeType)$LF",
|
||||
$content,
|
||||
"--$boundary--$LF"
|
||||
) -join $LF
|
||||
|
||||
$uri = (
|
||||
"https://${Host}/rest/api/content/" +
|
||||
"$($containerPageMeta.Id)/child/attachment"
|
||||
)
|
||||
|
||||
Try
|
||||
{
|
||||
Invoke-WebRequest `
|
||||
-Uri $uri `
|
||||
-Method 'Post' `
|
||||
-Headers @{
|
||||
'Authorization' = "Bearer $pat"
|
||||
'X-Atlassian-Token' = 'nocheck'
|
||||
} `
|
||||
-ContentType (
|
||||
"multipart/form-data; boundary=`"$boundary`""
|
||||
) `
|
||||
-Body $transportBody `
|
||||
-OutVariable rawResponse | Out-Null
|
||||
}
|
||||
|
||||
Catch
|
||||
{
|
||||
$errMsg = "skipping ``$($attachmentMeta.Name)``: $($_)"
|
||||
|
||||
If ($Strict)
|
||||
{
|
||||
$_
|
||||
|
||||
throw $errMsg
|
||||
}
|
||||
|
||||
Write-Host $errMsg
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
$response = ($rawResponse.Content | ConvertFrom-JSON)
|
||||
|
||||
$attachmentMeta | Add-Member `
|
||||
-NotePropertyName 'Id' `
|
||||
-NotePropertyValue $response.id `
|
||||
-Force
|
||||
|
||||
$attachmentMeta | Add-Member `
|
||||
-NotePropertyName 'Version' `
|
||||
-NotePropertyValue (
|
||||
$response.version.number
|
||||
) `
|
||||
-Force
|
||||
|
||||
$contentHash = (Get-StringHash $content).Hash
|
||||
|
||||
$attachmentMeta | Add-Member `
|
||||
-NotePropertyName 'Hash' `
|
||||
-NotePropertyValue $contentHash `
|
||||
-Force
|
||||
|
||||
If (
|
||||
($Title -And $attachmentMeta.Title -eq $Name) -Or
|
||||
$Manifest.Count -eq 1
|
||||
)
|
||||
{
|
||||
# TODO: further research mechanism of expanding single item
|
||||
# array pipelines. For now we have to apply the unary
|
||||
# operator, otherwise we get a wrong count on the output
|
||||
,@($attachmentMeta)
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
Else
|
||||
{
|
||||
$attachmentMeta
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Publish-Attachment
|
||||
{
|
||||
Param(
|
||||
# confluence instance hostname
|
||||
[Parameter(Mandatory)] [string]$Host,
|
||||
# name of the Confluence space to publish to
|
||||
[Parameter(Mandatory)] [string]$Space,
|
||||
# title of page to be published
|
||||
[Parameter()] [string]$Name,
|
||||
# attachments manifest
|
||||
[Parameter(Mandatory,ValueFromPipeline)]
|
||||
[PSCustomObject[]]$Manifest,
|
||||
# attachments manifest index, mandatory for ancestor lookup
|
||||
[Parameter(Mandatory)] [Collections.Hashtable]$Index,
|
||||
# pages manifest
|
||||
[Parameter(Mandatory)]
|
||||
[PSCustomObject[]]$PagesManifest,
|
||||
# pages manifest index, mandatory for container page lookup
|
||||
[Parameter(Mandatory)] [Collections.Hashtable]$PagesIndex,
|
||||
# flag on whether to fail hard, or just continue
|
||||
[Parameter()] [Switch]$Strict,
|
||||
# flag on whether to force update of page, regardless of content
|
||||
[Parameter()] [Switch]$Force
|
||||
)
|
||||
|
||||
Process
|
||||
{
|
||||
$result = New-Attachment `
|
||||
-Host $Host `
|
||||
-Space $Space `
|
||||
-Manifest $Manifest `
|
||||
-Index $Index `
|
||||
-PagesManifest $PagesManifest `
|
||||
-PagesIndex $PagesIndex `
|
||||
-Strict:$Strict
|
||||
}
|
||||
|
||||
End
|
||||
{
|
||||
$result
|
||||
}
|
||||
}
|
||||
|
||||
340
src/Meta.psm1
Executable file
340
src/Meta.psm1
Executable file
|
|
@ -0,0 +1,340 @@
|
|||
$ErrorActionPreference = "Stop"
|
||||
|
||||
|
||||
function Get-PageMeta
|
||||
{
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Get a Confluence page id
|
||||
|
||||
.DESCRIPTION
|
||||
First, tries to retrieve from local page id index (cache) through
|
||||
the local alias. If no cache hit, then polls the Confluence
|
||||
instance host for the id by providing a space key and page title.
|
||||
|
||||
.EXAMPLE
|
||||
Get-PageMeta `
|
||||
-Host 'confluence.contoso.com' `
|
||||
-Title 'Testitest' `
|
||||
-Space 'TIARA' `
|
||||
-CacheIndexFile 'confluence-page-cache.json'
|
||||
#>
|
||||
Param(
|
||||
# Confluence instance hostname
|
||||
[Parameter(Mandatory)] [string]$Host,
|
||||
# Page title
|
||||
[Parameter()] [string]$Title,
|
||||
# Confluence space id
|
||||
[Parameter(Mandatory)] [string]$Space,
|
||||
# pages manifest
|
||||
[Parameter(Mandatory, ValueFromPipeline)] [Array]$Manifest,
|
||||
# page metadata index for faster lookup of single page
|
||||
[Parameter()] [Collections.Hashtable]$Index,
|
||||
# force to get metadata from remote
|
||||
[Parameter()] [Switch]$Force,
|
||||
# throw an exception on error
|
||||
[Parameter()] [Switch]$Strict
|
||||
)
|
||||
|
||||
Process
|
||||
{
|
||||
If ($Title -And $Index -And $Manifest[$Index.$Title].Id)
|
||||
{
|
||||
$Manifest[$Index.$Title]
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
ForEach ($pageMeta in $Manifest)
|
||||
{
|
||||
If ($Title -And $pageMeta.Title -ne $Title) {continue}
|
||||
|
||||
If ($pageMeta.Id -And -Not $Force)
|
||||
{
|
||||
Write-Debug (
|
||||
"Get-PageMeta: ``$($pageMeta.Title)``: " +
|
||||
"using locally cached metadata ($($pageMeta.Id))"
|
||||
)
|
||||
|
||||
$pageMeta
|
||||
}
|
||||
|
||||
Else
|
||||
{
|
||||
$escapedTitle = [Uri]::EscapeDataString($pageMeta.Title)
|
||||
|
||||
$query = (
|
||||
"title=${escapedTitle}&spaceKey=${Space}&expand=version"
|
||||
)
|
||||
|
||||
Invoke-WebRequest `
|
||||
-Uri "https://${Host}/rest/api/content?$query" `
|
||||
-Method 'Get' `
|
||||
-Headers @{
|
||||
'Authorization' = 'Bearer ' +
|
||||
$(Get-PersonalAccessToken $Host)
|
||||
} `
|
||||
-OutVariable response | Out-Null
|
||||
|
||||
$results = ($response.Content | ConvertFrom-JSON).results
|
||||
|
||||
If ($results.Count -gt 1)
|
||||
{
|
||||
$errMsg = "error: more than one result for query: $query"
|
||||
|
||||
If ($Strict) {throw $errMsg}
|
||||
|
||||
Write-Host $errMsg
|
||||
|
||||
$pageMeta
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
ElseIf ($results.Count -eq 1)
|
||||
{
|
||||
Write-Debug (
|
||||
"Get-PageMetadata: ``$($pageMeta.Title)``: " +
|
||||
"updating metadata through remote ($($results[0].id))"
|
||||
)
|
||||
|
||||
$pageMeta | Add-Member `
|
||||
-NotePropertyName Id `
|
||||
-NotePropertyValue $results[0].id `
|
||||
-Force
|
||||
|
||||
$pageMeta | Add-Member `
|
||||
-NotePropertyName 'Version' `
|
||||
-NotePropertyValue `
|
||||
$results[0].version.number `
|
||||
-Force
|
||||
}
|
||||
|
||||
Else
|
||||
{
|
||||
Write-Debug (
|
||||
"Get-PageMetadata: ``$($pageMeta.Title)``: " +
|
||||
"no remote, using (partial) local"
|
||||
)
|
||||
|
||||
If ($pageMeta.Version)
|
||||
{
|
||||
$pageMeta.PSObject.Properties.Remove('Version')
|
||||
}
|
||||
|
||||
If ($pageMeta.Id)
|
||||
{
|
||||
$pageMeta.PSObject.Properties.Remove('Id')
|
||||
}
|
||||
}
|
||||
|
||||
If (-Not $pageMeta.Hash)
|
||||
{
|
||||
$content = Get-Content $pageMeta.Ref | Out-String
|
||||
|
||||
$hash = (Get-StringHash $content).Hash
|
||||
|
||||
$pageMeta | Add-Member `
|
||||
-NotePropertyName 'Hash' `
|
||||
-NotePropertyValue $hash `
|
||||
-Force
|
||||
}
|
||||
|
||||
$pageMeta
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function Get-AttachmentMeta
|
||||
{
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Get a Confluence page id
|
||||
|
||||
.DESCRIPTION
|
||||
First, tries to retrieve from local page id index (cache) through
|
||||
the local alias. If no cache hit, then polls the Confluence
|
||||
instance host for the id by providing a space key and page title.
|
||||
|
||||
.EXAMPLE
|
||||
Get-PageMeta `
|
||||
-Host 'confluence.contoso.com' `
|
||||
-Title 'Testitest' `
|
||||
-Space 'TIARA' `
|
||||
-CacheIndexFile 'confluence-page-cache.json'
|
||||
#>
|
||||
Param(
|
||||
# Confluence instance hostname
|
||||
[Parameter(Mandatory)] [string]$Host,
|
||||
# Confluence space id
|
||||
[Parameter(Mandatory)] [string]$Space,
|
||||
# Attachment name
|
||||
[Parameter()] [string]$Name,
|
||||
# attachments manifest
|
||||
[Parameter(Mandatory, ValueFromPipeline)] [Array]$Manifest,
|
||||
# page metadata index for faster lookup of single page
|
||||
[Parameter(Mandatory)] [Collections.Hashtable]$Index,
|
||||
# pages manifest
|
||||
[Parameter(Mandatory)] [Array]$PagesManifest,
|
||||
# page metadata index for faster lookup of single page
|
||||
[Parameter()] [Collections.Hashtable]$PagesIndex,
|
||||
# force to get metadata from remote
|
||||
[Parameter()] [Switch]$Force,
|
||||
# throw an exception on error
|
||||
[Parameter()] [Switch]$Strict
|
||||
)
|
||||
|
||||
Begin
|
||||
{
|
||||
$pat = Get-PersonalAccessToken $Host
|
||||
}
|
||||
|
||||
Process
|
||||
{
|
||||
If ($Name -And $Index -And $Manifest[$Index.$Name].Id)
|
||||
{
|
||||
$Manifest[$Index.$Name]
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
ForEach ($attachmentMeta in $Manifest)
|
||||
{
|
||||
If ($Name -And $attachmentMeta.Name -ne $Name) {continue}
|
||||
|
||||
$containerPageMeta = $PagesManifest[
|
||||
$PagesIndex."$($attachmentMeta.ContainerPageTitle)"
|
||||
]
|
||||
|
||||
If (-Not $containerPageMeta)
|
||||
{
|
||||
throw (
|
||||
"Get-AttachmentMeta: ``$($attachmentMeta.Name)``: " +
|
||||
"unable to lookup metadata for container page " +
|
||||
"title ``$($attachmentMeta.ContainerPageTitle)``." +
|
||||
"This is fatal."
|
||||
)
|
||||
}
|
||||
|
||||
If (-Not $containerPageMeta.Id)
|
||||
{
|
||||
$errMsg = (
|
||||
"Get-AttachmentMeta: ``$($attachmentMeta.Name)``: " +
|
||||
"container page titled" +
|
||||
"``$($attachmentMeta.ContainerPageTitle)`` " +
|
||||
"has no id, which means that the page has " +
|
||||
"(presumably) not yet been published."
|
||||
)
|
||||
|
||||
If ($Strict) {throw $errMsg}
|
||||
|
||||
Write-Host "$errMsg Continuing nonetheless..."
|
||||
|
||||
$attachmentMeta
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
If ($attachmentMeta.Id -And -Not $Force)
|
||||
{
|
||||
Write-Debug (
|
||||
"Get-AttachmentMeta: ``$($attachmentMeta.Name)``: " +
|
||||
"using locally cached metadata ($($attachmentMeta.Id))"
|
||||
)
|
||||
|
||||
$attachmentMeta
|
||||
}
|
||||
|
||||
Else
|
||||
{
|
||||
$escapedName = [Uri]::EscapeDataString($attachmentMeta.Name)
|
||||
|
||||
$query = "filename=${escapedName}&expand=version"
|
||||
|
||||
$uri = (
|
||||
"https://${Host}/rest/api/content/" +
|
||||
"$($containerPageMeta.Id)/child/attachment?$query"
|
||||
)
|
||||
|
||||
Invoke-WebRequest `
|
||||
-Uri $uri `
|
||||
-Method 'Get' `
|
||||
-Headers @{
|
||||
'Authorization' = "Bearer $pat"
|
||||
} `
|
||||
-OutVariable response | Out-Null
|
||||
|
||||
$results = ($response.Content | ConvertFrom-JSON).results
|
||||
|
||||
If ($results.Count -gt 1)
|
||||
{
|
||||
$errMsg = (
|
||||
"Get-AttachmentMeta: ``$($attachmentMeta.Name)``: " +
|
||||
"error: more than one result for query: $query"
|
||||
)
|
||||
|
||||
If ($Strict) {throw $errMsg}
|
||||
|
||||
Write-Host $errMsg
|
||||
|
||||
$attachmentMeta
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
ElseIf ($results.Count -eq 1)
|
||||
{
|
||||
Write-Debug (
|
||||
"Get-AttachmentMeta: ``$($attachmentMeta.Name)``: " +
|
||||
"updating metadata through remote ($($results[0].id))"
|
||||
)
|
||||
|
||||
$attachmentMeta | Add-Member `
|
||||
-NotePropertyName Id `
|
||||
-NotePropertyValue $results[0].id `
|
||||
-Force
|
||||
|
||||
$attachmentMeta | Add-Member `
|
||||
-NotePropertyName 'Version' `
|
||||
-NotePropertyValue `
|
||||
$results[0].version.number `
|
||||
-Force
|
||||
}
|
||||
|
||||
Else
|
||||
{
|
||||
Write-Debug (
|
||||
"Get-AttachmentMetadata: ``$($attachmentMeta.Name)``" +
|
||||
": no remote, using (partial) local"
|
||||
)
|
||||
|
||||
If ($attachmentMeta.Version)
|
||||
{
|
||||
$attachmentMeta.PSObject.Properties.Remove('Version')
|
||||
}
|
||||
|
||||
If ($attachmentMeta.Id)
|
||||
{
|
||||
$attachmentMeta.PSObject.Properties.Remove('Id')
|
||||
}
|
||||
}
|
||||
|
||||
If (-Not $attachmentMeta.Hash)
|
||||
{
|
||||
$content = Get-Content $attachmentMeta.Ref | Out-String
|
||||
|
||||
$hash = (Get-StringHash $content).Hash
|
||||
|
||||
$attachmentMeta | Add-Member `
|
||||
-NotePropertyName 'Hash' `
|
||||
-NotePropertyValue $hash `
|
||||
-Force
|
||||
}
|
||||
|
||||
$attachmentMeta
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -64,7 +64,8 @@ Description = 'External Confluence publisher for xconfluencebuilder'
|
|||
'Connection.psm1',
|
||||
'Manifest.psm1',
|
||||
'Page.psm1',
|
||||
'PageMeta.psm1',
|
||||
'Meta.psm1',
|
||||
'Attachment.psm1',
|
||||
'StringHelper.psm1'
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,42 +1,17 @@
|
|||
#!/usr/bin/env pwsh
|
||||
<#
|
||||
.SYNOPSIS
|
||||
PowerShell Publisher for sphinxcontrib.confluencebuilder
|
||||
|
||||
.DESCRIPTION
|
||||
|
||||
- support for ancestral pages and containered attachments
|
||||
- creates new pages if they don't exist
|
||||
- updates existing pages and attachments if checksum mismatches
|
||||
|
||||
.EXAMPLE
|
||||
|
||||
Import-Module (Join-Path 'vendor' 'tiara.rodney'
|
||||
'PSConfluencePublisher'
|
||||
'PSConfluencePublisher'
|
||||
'PSConfluencePublisher.psd1')
|
||||
|
||||
Register-PersonalAccessToken `
|
||||
-Host 'confluence.contoso.com' `
|
||||
-Token '123456789123456789'
|
||||
|
||||
Test-Connection confluence.contoso.com
|
||||
|
||||
Publish-All `
|
||||
-Url 'https://confluence.contoso.com/display/TIARA/Testitest' `
|
||||
-DumpIndex build/docs/confluence.out/data.json
|
||||
|
||||
.NOTES
|
||||
- tested with PowerShell Core (PSVersion 7.3.6)
|
||||
- tested with PowerShell Desktop (PSVersion 5.1.19041.3031)
|
||||
#>
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
|
||||
function Initialize-Manifest
|
||||
{
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Initialize a manifest (in-memory)
|
||||
|
||||
.DESCRIPTION
|
||||
This function initializes a manifest by loading a serialized
|
||||
manifest from the filesystem, generating indexes and sorting the
|
||||
pages manifest, so that the ancestral relation defines the order in
|
||||
which pages will be published.
|
||||
#>
|
||||
Param(
|
||||
# path of manifest to load
|
||||
|
|
@ -50,27 +25,40 @@ function Initialize-Manifest
|
|||
|
||||
Process
|
||||
{
|
||||
Write-Debug 'loading manifest...'
|
||||
Write-Debug 'Initialize-Manifest: loading manifest...'
|
||||
|
||||
$manifest = Get-Manifest $literalPath
|
||||
|
||||
Write-Debug 'creating pages manifest index...'
|
||||
Write-Debug 'Initialize-Manifest: creating pages manifest index...'
|
||||
|
||||
$pagesManifestIndex = New-PagesManifestIndex -Manifest $manifest.Pages
|
||||
|
||||
Write-Debug 'creating ancestral page generation cache...'
|
||||
Write-Debug (
|
||||
'Initialize-Manifest: creating ancestral page generation cache...'
|
||||
)
|
||||
|
||||
$ancestralGenerationCache = New-AncestralPageGenerationCache `
|
||||
-Manifest $manifest.Pages `
|
||||
-Index $pagesManifestIndex
|
||||
|
||||
Write-Debug 'sorting pages manifest...'
|
||||
Write-Debug 'Initialize-Manifest: sorting pages manifest...'
|
||||
|
||||
Optimize-PagesManifest `
|
||||
-Manifest $manifest.Pages `
|
||||
-Lo 0 `
|
||||
-Hi ($manifest.Pages.Count - 1) `
|
||||
-GenerationCache $ancestralGenerationCache | Out-Null
|
||||
|
||||
Write-Debug 'Initialize-Manifest: recreating pages manifest index...'
|
||||
|
||||
$pagesManifestIndex = New-PagesManifestIndex -Manifest $manifest.Pages
|
||||
|
||||
Write-Debug (
|
||||
'Initialize-Manifest: creating attachments manifest index...'
|
||||
)
|
||||
|
||||
$attachmentsManifestIndex = New-AttachmentsManifestIndex `
|
||||
-Manifest $manifest.Attachments
|
||||
}
|
||||
|
||||
End
|
||||
|
|
@ -79,10 +67,8 @@ function Initialize-Manifest
|
|||
'Path' = $literalPath
|
||||
'Manifest' = $manifest
|
||||
'Index' = @{
|
||||
'Pages' = New-PagesManifestIndex `
|
||||
-Manifest $manifest.Pages
|
||||
'Attachments' = New-AttachmentsManifestIndex `
|
||||
-Manifest $manifest.Attachments
|
||||
'Pages' = $pagesManifestIndex
|
||||
'Attachments' = $attachmentsManifestIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -91,9 +77,25 @@ function Initialize-Manifest
|
|||
|
||||
function Initialize-Connection
|
||||
{
|
||||
<#
|
||||
.SYNOPSIS
|
||||
initialize a connection to a Confluence instance
|
||||
|
||||
.DESCRIPTION
|
||||
This function registers a Personal Access Token (locally) and checks
|
||||
connectivity to a Confluence instance. It also verifies, that the
|
||||
Personal Access Tokens authenticates.
|
||||
|
||||
.NOTES
|
||||
TODO: extend verification to also verify that write access to the
|
||||
provided space is granted.
|
||||
#>
|
||||
Param(
|
||||
# hostname (or IP address) of Confluence instance
|
||||
[Parameter(Mandatory)] [String]$Host,
|
||||
# id of Confluence space
|
||||
[Parameter(Mandatory)] [String]$Space,
|
||||
# personal access token
|
||||
[Parameter(Mandatory)] [String]$PersonalAccessToken
|
||||
)
|
||||
|
||||
|
|
@ -118,6 +120,23 @@ function Initialize-Connection
|
|||
|
||||
function Publish-Pages
|
||||
{
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Publish pages to Confluence instance
|
||||
|
||||
.DESCRIPTION
|
||||
This function publishes all (or one) pages as defined in the pages
|
||||
manifest.
|
||||
|
||||
Since pipelining is supported within the low-level functions, this
|
||||
function is basically just a wrapper.
|
||||
|
||||
.NOTE
|
||||
TODO: Investigate on how we can pass-through the manifest as to
|
||||
retain pipeline functionality throughout. Currently it is broken,
|
||||
since the manifest isn't passed as a pieline input object from the
|
||||
top (which is this function).
|
||||
#>
|
||||
Param(
|
||||
# connection object created through Initialize-Connection
|
||||
[Parameter(Mandatory)] [Collections.Hashtable]$Connection,
|
||||
|
|
@ -133,11 +152,54 @@ function Publish-Pages
|
|||
|
||||
Process
|
||||
{
|
||||
$Manifest.Manifest.Pages | Publish-Page `
|
||||
Publish-Page `
|
||||
-Host $Connection.Host `
|
||||
-Space $Connection.Space `
|
||||
-Title $Title `
|
||||
-Index $Manifest.Index.Pages `
|
||||
-Manifest $Manifest.Manifest.Pages `
|
||||
-Strict:$Strict `
|
||||
-Force:$Force | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function Publish-Attachments
|
||||
{
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Publish attachments to Confluence instance
|
||||
|
||||
.DESCRIPTION
|
||||
This function publishes all (or one) attachments as defined in the
|
||||
attachments manifest.
|
||||
|
||||
Since pipelining is supported within the low-level functions, this
|
||||
function is basically just a wrapper.
|
||||
#>
|
||||
Param(
|
||||
# connection object created through Initialize-Connection
|
||||
[Parameter(Mandatory)] [Collections.Hashtable]$Connection,
|
||||
# manifest object created through Initialize-Manifest
|
||||
[Parameter(Mandatory)] [PSCustomObject]$Manifest,
|
||||
#
|
||||
[Parameter()] [Switch]$Strict,
|
||||
#
|
||||
[Parameter()] [Switch]$Force,
|
||||
# name of attachment to be published
|
||||
[Parameter()] [String]$Name
|
||||
)
|
||||
|
||||
Process
|
||||
{
|
||||
Publish-Attachment `
|
||||
-Host $Connection.Host `
|
||||
-Space $Connection.Space `
|
||||
-Name $Name `
|
||||
-Manifest $Manifest.Manifest.Attachments `
|
||||
-Index $Manifest.Index.Attachments `
|
||||
-PagesManifest $Manifest.Manifest.Pages `
|
||||
-PagesIndex $Manifest.Index.Pages `
|
||||
-Strict:$Strict `
|
||||
-Force:$Force | Out-Null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ function New-Page
|
|||
# title of page to be published
|
||||
[Parameter()] [string]$Title,
|
||||
# pages manifest
|
||||
[Parameter(Mandatory)]
|
||||
[Parameter(Mandatory,ValueFromPipeline)]
|
||||
[PSCustomObject[]]$Manifest,
|
||||
# pages manifest index, mandatory for ancestor lookup
|
||||
[Parameter(Mandatory)] [Collections.Hashtable]$Index,
|
||||
|
|
@ -123,7 +123,34 @@ function New-Page
|
|||
'representation' = 'storage'
|
||||
}
|
||||
}
|
||||
} | ConvertTo-JSON -Depth 5
|
||||
}
|
||||
|
||||
If ($pageMeta.AncestorTitle)
|
||||
{
|
||||
$ancestorPageMeta = $Manifest[
|
||||
$Index."$($pageMeta.AncestorTitle)"
|
||||
]
|
||||
|
||||
If (-Not $ancestorPageMeta)
|
||||
{
|
||||
Throw (
|
||||
"ancestor (``$($ancestorPageMeta.Title)``) of " +
|
||||
"``$($pageMeta.Title)`` does not have an id. " +
|
||||
"This indicates, that the ancestor has not been " +
|
||||
"published and therefore the pages manifest may " +
|
||||
"not be in the correct order."
|
||||
)
|
||||
}
|
||||
|
||||
$transportBody.ancestors = @(
|
||||
@{'id' = $ancestorPageMeta.Id}
|
||||
)
|
||||
}
|
||||
|
||||
$rawTransportBody = (
|
||||
$transportBody | ConvertTo-JSON `
|
||||
-WarningAction 'SilentlyContinue'
|
||||
)
|
||||
|
||||
Try
|
||||
{
|
||||
|
|
@ -134,7 +161,7 @@ function New-Page
|
|||
'Authorization' = "Bearer $pat"
|
||||
} `
|
||||
-ContentType "application/json" `
|
||||
-Body $transportBody `
|
||||
-Body $rawTransportBody `
|
||||
-OutVariable rawResponse | Out-Null
|
||||
}
|
||||
|
||||
|
|
@ -277,6 +304,8 @@ function Update-Page
|
|||
|
||||
Write-Host $errMsg
|
||||
|
||||
$pageMeta
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -351,7 +380,34 @@ function Update-Page
|
|||
'version' = @{
|
||||
'number' = $version
|
||||
}
|
||||
} | ConvertTo-JSON -WarningAction 'SilentlyContinue'
|
||||
}
|
||||
|
||||
If ($pageMeta.AncestorTitle)
|
||||
{
|
||||
$ancestorPageMeta = $Manifest[
|
||||
$Index."$($pageMeta.AncestorTitle)"
|
||||
]
|
||||
|
||||
If (-Not $ancestorPageMeta)
|
||||
{
|
||||
Throw (
|
||||
"ancestor (``$($ancestorPageMeta.Title)``) of " +
|
||||
"``$($pageMeta.Title)`` does not have an id. " +
|
||||
"This indicates, that the ancestor has not been " +
|
||||
"published and therefore the pages manifest may " +
|
||||
"not be in the correct order."
|
||||
)
|
||||
}
|
||||
|
||||
$transportBody.ancestors = @(
|
||||
@{'id' = $ancestorPageMeta.Id}
|
||||
)
|
||||
}
|
||||
|
||||
$rawTransportBody = (
|
||||
$transportBody | ConvertTo-JSON `
|
||||
-WarningAction 'SilentlyContinue'
|
||||
)
|
||||
|
||||
Try
|
||||
{
|
||||
|
|
@ -363,7 +419,7 @@ function Update-Page
|
|||
'Authorization' = "Bearer $pat"
|
||||
} `
|
||||
-ContentType "application/json" `
|
||||
-Body $transportBody `
|
||||
-Body $rawTransportBody `
|
||||
-OutVariable rawResponse | Out-Null
|
||||
}
|
||||
|
||||
|
|
@ -430,7 +486,7 @@ function Publish-Page
|
|||
# title of page to be published
|
||||
[Parameter()] [string]$Title,
|
||||
# pages manifest
|
||||
[Parameter(Mandatory, ValueFromPipeline)]
|
||||
[Parameter(Mandatory)]
|
||||
[PSCustomObject[]]$Manifest,
|
||||
# pages manifest index, mandatory for ancestor lookup
|
||||
[Parameter(Mandatory)] [Collections.Hashtable]$Index,
|
||||
|
|
|
|||
|
|
@ -1,132 +0,0 @@
|
|||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
|
||||
function Get-PageMeta
|
||||
{
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Get a Confluence page id
|
||||
|
||||
.DESCRIPTION
|
||||
First, tries to retrieve from local page id index (cache) through
|
||||
the local alias. If no cache hit, then polls the Confluence
|
||||
instance host for the id by providing a space key and page title.
|
||||
|
||||
.EXAMPLE
|
||||
Get-PageMeta `
|
||||
-Host 'confluence.contoso.com' `
|
||||
-Title 'Testitest' `
|
||||
-Space 'TIARA' `
|
||||
-CacheIndexFile 'confluence-page-cache.json'
|
||||
#>
|
||||
Param(
|
||||
# Confluence instance hostname
|
||||
[Parameter(Mandatory)] [string] $Host,
|
||||
# Page title
|
||||
[Parameter()] [string] $Title,
|
||||
# Confluence space id
|
||||
[Parameter(Mandatory)] [string] $Space,
|
||||
# pages manifest
|
||||
[Parameter(Mandatory, ValueFromPipeline)] [Array] $Manifest,
|
||||
# page metadata index for faster lookup of single page
|
||||
[Parameter()] [Collections.Hashtable] $Index,
|
||||
# force to get metadata from remote
|
||||
[Parameter()] [Switch] $Force = $false,
|
||||
# throw an exception on error
|
||||
[Parameter()] [Switch] $Strict = $true
|
||||
)
|
||||
|
||||
Process
|
||||
{
|
||||
If ($Title -And $Index -And $Manifest[$Index.$Title].Id)
|
||||
{
|
||||
$Manifest[$Index.$Title]
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
ForEach ($pageMeta in $Manifest)
|
||||
{
|
||||
If ($Title -And $pageMeta.Title -ne $Title) {continue}
|
||||
|
||||
If ($pageMeta.Id -And -Not $Force)
|
||||
{
|
||||
Write-Debug "local (cache): $($pageMeta.Title) ($($pageMeta.Id))"
|
||||
|
||||
$pageMeta
|
||||
}
|
||||
|
||||
Else
|
||||
{
|
||||
$escapedTitle = [Uri]::EscapeDataString($pageMeta.Title)
|
||||
|
||||
$query = (
|
||||
"title=${escapedTitle}&spaceKey=${Space}&expand=version"
|
||||
)
|
||||
|
||||
Invoke-WebRequest `
|
||||
-Uri "https://${Host}/rest/api/content?$query" `
|
||||
-Method 'Get' `
|
||||
-Headers @{
|
||||
'Authorization' = 'Bearer ' +
|
||||
$(Get-PersonalAccessToken $Host)
|
||||
} `
|
||||
-OutVariable response | Out-Null
|
||||
|
||||
$results = ($response.Content | ConvertFrom-JSON).results
|
||||
|
||||
If ($results.Count -gt 1)
|
||||
{
|
||||
$errMsg = "error: more than one result for query: $query"
|
||||
|
||||
If ($Strict) {throw $errMsg}
|
||||
|
||||
Write-Host $errMsg
|
||||
|
||||
$pageMeta
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
ElseIf ($results.Count -eq 1)
|
||||
{
|
||||
Write-Debug (
|
||||
"Get-PageMetadata: ``$($pageMeta.Title)``: " +
|
||||
"updating metadata through remote ($($results[0].id))"
|
||||
)
|
||||
|
||||
$pageMeta | Add-Member `
|
||||
-NotePropertyName Id `
|
||||
-NotePropertyValue $results[0].id `
|
||||
-Force
|
||||
|
||||
$pageMeta | Add-Member `
|
||||
-NotePropertyName 'Version' `
|
||||
-NotePropertyValue `
|
||||
$results[0].version.number `
|
||||
-Force
|
||||
}
|
||||
|
||||
Else
|
||||
{
|
||||
Write-Debug "local: $($pageMeta.Title) (no remote)"
|
||||
}
|
||||
|
||||
If (-Not $pageMeta.Hash)
|
||||
{
|
||||
$content = Get-Content $pageMeta.Ref | Out-String
|
||||
|
||||
$hash = (Get-StringHash $content).Hash
|
||||
|
||||
$pageMeta | Add-Member `
|
||||
-NotePropertyName 'Hash' `
|
||||
-NotePropertyValue $hash `
|
||||
-Force
|
||||
}
|
||||
|
||||
$pageMeta
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue