init
This commit is contained in:
commit
17266ecb99
30 changed files with 1731 additions and 0 deletions
3
.gitignore
vendored
Executable file
3
.gitignore
vendored
Executable file
|
|
@ -0,0 +1,3 @@
|
||||||
|
/_*
|
||||||
|
/._*
|
||||||
|
.DS_Store
|
||||||
14
.nuspec
Executable file
14
.nuspec
Executable file
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
|
<metadata>
|
||||||
|
<id>ConfluencePublisher</id>
|
||||||
|
<version>1.1.0</version>
|
||||||
|
<description>Übergangslösung für die Integration von sphinx-confluencebuilder generierter Confluence Dokumentation</description>
|
||||||
|
<authors>tiara.rodney@adesso.de</authors>
|
||||||
|
<owners>irina.ternovykh@vkb.de</owners>
|
||||||
|
<license type="expression">UNLICENSED</license>
|
||||||
|
<readme>README.md</readme>
|
||||||
|
<copyright>VKBit Betrieb GmbH</copyright>
|
||||||
|
<title>Powershell Confluence Publisher</title>
|
||||||
|
</metadata>
|
||||||
|
</package>
|
||||||
BIN
PSConfluencePublisher/._.DS_Store
Executable file
BIN
PSConfluencePublisher/._.DS_Store
Executable file
Binary file not shown.
BIN
PSConfluencePublisher/._Connection.Tests.ps1
Executable file
BIN
PSConfluencePublisher/._Connection.Tests.ps1
Executable file
Binary file not shown.
BIN
PSConfluencePublisher/._Connection.psm1
Executable file
BIN
PSConfluencePublisher/._Connection.psm1
Executable file
Binary file not shown.
BIN
PSConfluencePublisher/._Manifest.Tests.ps1
Executable file
BIN
PSConfluencePublisher/._Manifest.Tests.ps1
Executable file
Binary file not shown.
BIN
PSConfluencePublisher/._Manifest.psm1
Executable file
BIN
PSConfluencePublisher/._Manifest.psm1
Executable file
Binary file not shown.
BIN
PSConfluencePublisher/._PSConfluencePublisher.psd1
Executable file
BIN
PSConfluencePublisher/._PSConfluencePublisher.psd1
Executable file
Binary file not shown.
BIN
PSConfluencePublisher/._PSConfluencePublisher.psm1
Executable file
BIN
PSConfluencePublisher/._PSConfluencePublisher.psm1
Executable file
Binary file not shown.
BIN
PSConfluencePublisher/._Page.psm1
Executable file
BIN
PSConfluencePublisher/._Page.psm1
Executable file
Binary file not shown.
BIN
PSConfluencePublisher/._PersonalAccessToken.Tests.ps1
Executable file
BIN
PSConfluencePublisher/._PersonalAccessToken.Tests.ps1
Executable file
Binary file not shown.
BIN
PSConfluencePublisher/._PersonalAccessToken.psm1
Executable file
BIN
PSConfluencePublisher/._PersonalAccessToken.psm1
Executable file
Binary file not shown.
BIN
PSConfluencePublisher/._manifest.schema.json
Executable file
BIN
PSConfluencePublisher/._manifest.schema.json
Executable file
Binary file not shown.
BIN
PSConfluencePublisher/._scripts.deps.json
Executable file
BIN
PSConfluencePublisher/._scripts.deps.json
Executable file
Binary file not shown.
65
PSConfluencePublisher/Connection.Tests.ps1
Executable file
65
PSConfluencePublisher/Connection.Tests.ps1
Executable file
|
|
@ -0,0 +1,65 @@
|
||||||
|
#!/usr/bin/env pwsh
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
BeforeAll {
|
||||||
|
Import-Module (Join-Path $PSScriptRoot 'PSConfluencePublisher.psd1') -Force
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Describe 'Test-Connection' `
|
||||||
|
{
|
||||||
|
|
||||||
|
Context 'Parameterized' {
|
||||||
|
|
||||||
|
It 'throws no exception' {
|
||||||
|
|
||||||
|
InModuleScope Connection {
|
||||||
|
|
||||||
|
Mock Get-PersonalAccessToken {'01234567890123456789'}
|
||||||
|
|
||||||
|
Mock Invoke-WebRequest {
|
||||||
|
return @{
|
||||||
|
'Content' = "{'type': 'known'}"
|
||||||
|
'StatusCode' = 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Test-Connection -Host 'confluence.contoso.com'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'detects anonymous authentication' {
|
||||||
|
|
||||||
|
InModuleScope Connection {
|
||||||
|
|
||||||
|
Mock Get-PersonalAccessToken {'01234567890123456789'}
|
||||||
|
|
||||||
|
Mock Invoke-WebRequest {
|
||||||
|
return @{
|
||||||
|
'Content' = "{'type': 'anonymous'}"
|
||||||
|
'StatusCode' = 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{Test-Connection -Host 'confluence.contoso.com'} | Should -Throw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'detects non 200 status codes' {
|
||||||
|
|
||||||
|
InModuleScope Connection {
|
||||||
|
|
||||||
|
Mock Get-PersonalAccessToken {'01234567890123456789'}
|
||||||
|
|
||||||
|
Mock Invoke-WebRequest {
|
||||||
|
return @{
|
||||||
|
'Content' = "{'type': 'anonymous'}"
|
||||||
|
'StatusCode' = 500
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{Test-Connection -Host 'confluence.contoso.com'} | Should -Throw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
54
PSConfluencePublisher/Connection.psm1
Executable file
54
PSConfluencePublisher/Connection.psm1
Executable file
|
|
@ -0,0 +1,54 @@
|
||||||
|
#!/usr/bin/env pwsh
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
|
||||||
|
function Test-Connection
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Test the connectivity to a Confluence instance.
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Just making an arbitrary authenticated HTTP request and making sure
|
||||||
|
that we're getting a 2xx status code back. This way we make sure
|
||||||
|
that network connectivity is fine, and that the PAT is valid.
|
||||||
|
|
||||||
|
It is required to register a PAT through
|
||||||
|
``Register-PersonalAccessToken`` beforehand.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Test-Connection confluence.contoso.com
|
||||||
|
#>
|
||||||
|
Param(
|
||||||
|
[Parameter(Mandatory, Position = 0)] [string] $Host
|
||||||
|
)
|
||||||
|
|
||||||
|
Process
|
||||||
|
{
|
||||||
|
# Screw Invoke-RestMethod, how am i supposed to get a non 4xx status
|
||||||
|
# code? Catch a non-existent exception 🤷♀️????
|
||||||
|
Invoke-WebRequest `
|
||||||
|
-Uri "https://${Host}/rest/api/user/current" `
|
||||||
|
-Method 'Get' `
|
||||||
|
-Headers @{
|
||||||
|
'Authorization' = "Bearer $(Get-PersonalAccessToken $Host)"
|
||||||
|
} `
|
||||||
|
-OutVariable response
|
||||||
|
|
||||||
|
if(($response.Content | ConvertFrom-JSON).type -ne "known")
|
||||||
|
{
|
||||||
|
throw "personal access token for host '$Host' does not " +
|
||||||
|
"authenticate."
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($response.StatusCode -eq 200)
|
||||||
|
{
|
||||||
|
Write-Host "Verified connectivity ($Host)."
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw "received status code other than 200 " +
|
||||||
|
"($($response.StatusCode))"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
70
PSConfluencePublisher/Manifest.Tests.ps1
Executable file
70
PSConfluencePublisher/Manifest.Tests.ps1
Executable file
|
|
@ -0,0 +1,70 @@
|
||||||
|
#!/usr/bin/env pwsh
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
BeforeAll {
|
||||||
|
Import-Module (Join-Path $PSScriptRoot 'PSConfluencePublisher.psd1') -Force
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
AfterAll {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Describe 'Get-Manifest' `
|
||||||
|
{
|
||||||
|
|
||||||
|
Context 'Parameterized' {
|
||||||
|
|
||||||
|
It 'throws no exception' {
|
||||||
|
|
||||||
|
InModuleScope Connection {
|
||||||
|
|
||||||
|
Mock Get-PersonalAccessToken {'01234567890123456789'}
|
||||||
|
|
||||||
|
Mock Invoke-WebRequest {
|
||||||
|
return @{
|
||||||
|
'Content' = "{'type': 'known'}"
|
||||||
|
'StatusCode' = 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Test-Connection -Host 'confluence.contoso.com'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'detects anonymous authentication' {
|
||||||
|
|
||||||
|
InModuleScope Connection {
|
||||||
|
|
||||||
|
Mock Get-PersonalAccessToken {'01234567890123456789'}
|
||||||
|
|
||||||
|
Mock Invoke-WebRequest {
|
||||||
|
return @{
|
||||||
|
'Content' = "{'type': 'anonymous'}"
|
||||||
|
'StatusCode' = 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{Test-Connection -Host 'confluence.contoso.com'} | Should -Throw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'detects non 200 status codes' {
|
||||||
|
|
||||||
|
InModuleScope Connection {
|
||||||
|
|
||||||
|
Mock Get-PersonalAccessToken {'01234567890123456789'}
|
||||||
|
|
||||||
|
Mock Invoke-WebRequest {
|
||||||
|
return @{
|
||||||
|
'Content' = "{'type': 'anonymous'}"
|
||||||
|
'StatusCode' = 500
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{Test-Connection -Host 'confluence.contoso.com'} | Should -Throw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
74
PSConfluencePublisher/Manifest.psm1
Executable file
74
PSConfluencePublisher/Manifest.psm1
Executable file
|
|
@ -0,0 +1,74 @@
|
||||||
|
#!/usr/bin/env pwsh
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
|
||||||
|
$script:schema = Get-Content (
|
||||||
|
Join-Path $PSScriptRoot 'manifest.schema.json'
|
||||||
|
) | Out-String
|
||||||
|
|
||||||
|
|
||||||
|
function Get-Manifest
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Load the archive manifest
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Get-Manifest 'manifest.json'
|
||||||
|
#>
|
||||||
|
Param(
|
||||||
|
# filesystem location of manifest
|
||||||
|
[Parameter(Mandatory)] [string] $File
|
||||||
|
)
|
||||||
|
|
||||||
|
Process
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$raw = Get-Content $File
|
||||||
|
}
|
||||||
|
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
$raw = '{"pages":{}, "attachments": {}}'
|
||||||
|
}
|
||||||
|
|
||||||
|
$raw | Test-JSON -Schema $script:schema | Out-Null
|
||||||
|
|
||||||
|
$data = $raw | ConvertFrom-JSON
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Set-Manifest
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Dump the archive manifest
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Set-Manifest 'manifest.json'
|
||||||
|
#>
|
||||||
|
Param(
|
||||||
|
# manifest object
|
||||||
|
[Parameter(Mandatory)] [PSObject] $Manifest,
|
||||||
|
# filesystem location of manifest
|
||||||
|
[Parameter(Mandatory)] [string] $File,
|
||||||
|
# create a backup first
|
||||||
|
[Parameter()] [bool] $Backup = $false
|
||||||
|
)
|
||||||
|
|
||||||
|
Process
|
||||||
|
{
|
||||||
|
$raw = $Manifest | ConvertTo-JSON
|
||||||
|
|
||||||
|
$raw | Test-JSON -Schema $script:schema
|
||||||
|
|
||||||
|
if ($Backup)
|
||||||
|
{
|
||||||
|
Copy-Item -Path $File -Destination "$(Split-Path -Leaf $File).bck"
|
||||||
|
}
|
||||||
|
|
||||||
|
Set-Content -Path $File -Value $raw
|
||||||
|
}
|
||||||
|
}
|
||||||
131
PSConfluencePublisher/PSConfluencePublisher.psd1
Executable file
131
PSConfluencePublisher/PSConfluencePublisher.psd1
Executable file
|
|
@ -0,0 +1,131 @@
|
||||||
|
@{
|
||||||
|
|
||||||
|
# Script module or binary module file associated with this manifest.
|
||||||
|
#RootModule = 'ConfluencePublisher.psm1'
|
||||||
|
|
||||||
|
ModuleVersion = '1.1.0'
|
||||||
|
|
||||||
|
# Supported PSEditions
|
||||||
|
# CompatiblePSEditions = @()
|
||||||
|
|
||||||
|
# ID used to uniquely identify this module
|
||||||
|
GUID = 'b51d47f9-19b9-4c34-9a88-36eb8cf4c9bd'
|
||||||
|
|
||||||
|
# Author of this module
|
||||||
|
Author = 'Theodor Rodweil'
|
||||||
|
|
||||||
|
# Company or vendor of this module
|
||||||
|
CompanyName = 'Victory Karma IT'
|
||||||
|
|
||||||
|
# Copyright statement for this module
|
||||||
|
Copyright = '(c) victory-k.it. All rights reserved.'
|
||||||
|
|
||||||
|
RootModule = 'PSConfluencePublisher.psm1'
|
||||||
|
|
||||||
|
# Description of the functionality provided by this module
|
||||||
|
# Description = ''
|
||||||
|
|
||||||
|
# Minimum version of the PowerShell engine required by this module
|
||||||
|
# PowerShellVersion = '6.0'
|
||||||
|
|
||||||
|
# Name of the PowerShell host required by this module
|
||||||
|
# PowerShellHostName = ''
|
||||||
|
|
||||||
|
# Minimum version of the PowerShell host required by this module
|
||||||
|
# PowerShellHostVersion = ''
|
||||||
|
|
||||||
|
# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
|
||||||
|
# DotNetFrameworkVersion = ''
|
||||||
|
|
||||||
|
# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
|
||||||
|
# ClrVersion = ''
|
||||||
|
|
||||||
|
# Processor architecture (None, X86, Amd64) required by this module
|
||||||
|
# ProcessorArchitecture = ''
|
||||||
|
|
||||||
|
# Modules that must be imported into the global environment prior to importing this module
|
||||||
|
# RequiredModules = @('sdf')
|
||||||
|
|
||||||
|
# Assemblies that must be loaded prior to importing this module
|
||||||
|
# RequiredAssemblies = @()
|
||||||
|
|
||||||
|
# Script files (.ps1) that are run in the caller's environment prior to importing this module.
|
||||||
|
# ScriptsToProcess = @()
|
||||||
|
|
||||||
|
# Type files (.ps1xml) to be loaded when importing this module
|
||||||
|
# TypesToProcess = @()
|
||||||
|
|
||||||
|
# Format files (.ps1xml) to be loaded when importing this module
|
||||||
|
# FormatsToProcess = @()
|
||||||
|
|
||||||
|
# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
|
||||||
|
NestedModules = @(
|
||||||
|
'PersonalAccessToken.psm1',
|
||||||
|
'Connection.psm1',
|
||||||
|
'Manifest.psm1'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Functions 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 functions to export.
|
||||||
|
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 = '*'
|
||||||
|
|
||||||
|
# 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 = @()
|
||||||
|
|
||||||
|
# DSC resources to export from this module
|
||||||
|
# DscResourcesToExport = @()
|
||||||
|
|
||||||
|
# List of all modules packaged with this module
|
||||||
|
# ModuleList = @()
|
||||||
|
|
||||||
|
# List of all files packaged with this module
|
||||||
|
FileList = @(
|
||||||
|
"./manifest.schema.json"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 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 = @{
|
||||||
|
|
||||||
|
PSData = @{
|
||||||
|
|
||||||
|
# Tags applied to this module. These help with module discovery in online galleries.
|
||||||
|
# Tags = @()
|
||||||
|
|
||||||
|
# A URL to the license for this module.
|
||||||
|
# LicenseUri = ''
|
||||||
|
|
||||||
|
# A URL to the main website for this project.
|
||||||
|
# ProjectUri = ''
|
||||||
|
|
||||||
|
# A URL to an icon representing this module.
|
||||||
|
# IconUri = ''
|
||||||
|
|
||||||
|
# ReleaseNotes of this module
|
||||||
|
# ReleaseNotes = ''
|
||||||
|
|
||||||
|
# Prerelease string of this module
|
||||||
|
# Prerelease = ''
|
||||||
|
|
||||||
|
# Flag to indicate whether the module requires explicit user acceptance for install/update/save
|
||||||
|
# RequireLicenseAcceptance = $false
|
||||||
|
|
||||||
|
# External dependent modules of this module
|
||||||
|
# ExternalModuleDependencies = @()
|
||||||
|
|
||||||
|
} # End of PSData hashtable
|
||||||
|
|
||||||
|
} # End of PrivateData hashtable
|
||||||
|
|
||||||
|
# HelpInfo URI of this module
|
||||||
|
# HelpInfoURI = ''
|
||||||
|
|
||||||
|
# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
|
||||||
|
# DefaultCommandPrefix = ''
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
508
PSConfluencePublisher/PSConfluencePublisher.psm1
Executable file
508
PSConfluencePublisher/PSConfluencePublisher.psm1
Executable file
|
|
@ -0,0 +1,508 @@
|
||||||
|
#!/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 New-ConfluencePage
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Add a confluence page
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Add-ConfluencePage
|
||||||
|
-Host 'confluence.contoso.com' `
|
||||||
|
-Space 'TIARA' `
|
||||||
|
-Title 'Testitest' `
|
||||||
|
-Content @{}
|
||||||
|
#>
|
||||||
|
Param(
|
||||||
|
[Parameter(Mandatory)] [string] $Host,
|
||||||
|
# The name of the Confluence space to publish to
|
||||||
|
[Parameter(Mandatory)] [string] $Space,
|
||||||
|
# title of page to be published
|
||||||
|
[Parameter(Mandatory)] [string] $Title,
|
||||||
|
# content of page
|
||||||
|
[Parameter(Mandatory)] [string] $Content,
|
||||||
|
# parent page id
|
||||||
|
[Parameter()] [string] $Ancestor
|
||||||
|
)
|
||||||
|
|
||||||
|
Process
|
||||||
|
{
|
||||||
|
Assert-PersonalAccessToken $Host
|
||||||
|
|
||||||
|
$transportBody = @{
|
||||||
|
'type' = 'page'
|
||||||
|
'title' = $Title
|
||||||
|
'space' = @{
|
||||||
|
'key' = $Space
|
||||||
|
}
|
||||||
|
'body' = @{
|
||||||
|
'storage' = @{
|
||||||
|
'value' = $Content
|
||||||
|
'representation' = 'storage'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} | ConvertTo-JSON
|
||||||
|
|
||||||
|
Invoke-WebRequest `
|
||||||
|
-Uri "https://${Host}/rest/api/content" `
|
||||||
|
-Method 'Post' `
|
||||||
|
-Headers @{
|
||||||
|
'Authorization' = "Bearer $([System.Net.NetworkCredential]::new('', $script:PATS[$Host_]).Password)"
|
||||||
|
} `
|
||||||
|
-ContentType "application/json" `
|
||||||
|
-Body $transportBody `
|
||||||
|
-OutVariable rawResponse | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
End
|
||||||
|
{
|
||||||
|
$response = ($rawResponse.Content | ConvertFrom-JSON)
|
||||||
|
|
||||||
|
@{
|
||||||
|
'PageId' = $response.Id
|
||||||
|
'Version' = $response.version | Select -ExpandProperty 'number'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Update-Page
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Add a confluence page
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Add-ConfluencePage
|
||||||
|
-Host 'confluence.contoso.com' `
|
||||||
|
-Space 'TIARA' `
|
||||||
|
-Title 'Testitest' `
|
||||||
|
-Content @{}
|
||||||
|
#>
|
||||||
|
Param(
|
||||||
|
[Parameter(Mandatory)] [string] $Host,
|
||||||
|
# The page id of an existing page
|
||||||
|
[Parameter(Mandatory)] [string] $PageId,
|
||||||
|
# The name of the Confluence space to publish to
|
||||||
|
[Parameter(Mandatory)] [string] $Space,
|
||||||
|
# title of page to be published
|
||||||
|
[Parameter(Mandatory)] [string] $Title,
|
||||||
|
# version of content
|
||||||
|
[Parameter(Mandatory)] [int] $Version,
|
||||||
|
# content of page
|
||||||
|
[Parameter(Mandatory)] [string] $Content,
|
||||||
|
# parent page id
|
||||||
|
[Parameter()] [string] $Ancestor
|
||||||
|
)
|
||||||
|
|
||||||
|
Process
|
||||||
|
{
|
||||||
|
Assert-PersonalAccessToken $Host
|
||||||
|
|
||||||
|
$transportBody = @{
|
||||||
|
'id' = $PageId
|
||||||
|
'type' = 'page'
|
||||||
|
'title' = $Title
|
||||||
|
'space' = @{
|
||||||
|
'key' = $Space
|
||||||
|
}
|
||||||
|
'body' = @{
|
||||||
|
'storage' = @{
|
||||||
|
'value' = $Content
|
||||||
|
'representation' = 'storage'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'version' = @{
|
||||||
|
'number' = $Version
|
||||||
|
}
|
||||||
|
} | ConvertTo-JSON
|
||||||
|
|
||||||
|
Invoke-WebRequest `
|
||||||
|
-Uri "https://${Host}/rest/api/content/$PageId" `
|
||||||
|
-Method 'Put' `
|
||||||
|
-Headers @{
|
||||||
|
'Authorization' = "Bearer $([System.Net.NetworkCredential]::new('', $script:PATS[$Host_]).Password)"
|
||||||
|
} `
|
||||||
|
-ContentType "application/json" `
|
||||||
|
-Body $transportBody `
|
||||||
|
-OutVariable rawResponse | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
End
|
||||||
|
{
|
||||||
|
$response = ($rawResponse.Content | ConvertFrom-JSON)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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(
|
||||||
|
[Parameter(Mandatory)] [string] $Host,
|
||||||
|
[Parameter(Mandatory)] [string] $Title,
|
||||||
|
[Parameter(Mandatory)] [string] $Space,
|
||||||
|
[Parameter(Mandatory)] [string] $CacheIndexFile
|
||||||
|
)
|
||||||
|
|
||||||
|
Process
|
||||||
|
{
|
||||||
|
if ($Title)
|
||||||
|
{
|
||||||
|
$cachedPageMeta = Get-CachedPageMeta `
|
||||||
|
-Title $Title `
|
||||||
|
-CacheIndexFile $CacheIndexFile
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($cachedPageMeta)
|
||||||
|
{
|
||||||
|
return $cachedPageMeta
|
||||||
|
}
|
||||||
|
|
||||||
|
$escapedTitle = [uri]::EscapeDataString($Title)
|
||||||
|
|
||||||
|
$query = "title=${escapedTitle}&spaceKey=${Space}&expand=history"
|
||||||
|
|
||||||
|
Assert-PersonalAccessToken $Host
|
||||||
|
|
||||||
|
Invoke-WebRequest `
|
||||||
|
-Uri "https://${Host}/rest/api/content?$query" `
|
||||||
|
-Method 'Get' `
|
||||||
|
-Headers @{
|
||||||
|
'Authorization' = "Bearer $([System.Net.NetworkCredential]::new('', $script:PATS[$Host_]).Password)"
|
||||||
|
} `
|
||||||
|
-OutVariable response
|
||||||
|
|
||||||
|
$results = ($response.Content | ConvertFrom-JSON).results
|
||||||
|
|
||||||
|
if ($results.Count -gt 1)
|
||||||
|
{
|
||||||
|
throw "more than one result for query: $query"
|
||||||
|
}
|
||||||
|
elseif ($results.Count -eq 1)
|
||||||
|
{
|
||||||
|
Register-PageMeta `
|
||||||
|
-PageId $results[0].id `
|
||||||
|
-Version ($results[0]._expandable | Select -ExpandProperty 'version') `
|
||||||
|
-Title $Title `
|
||||||
|
-CacheIndexFile $CacheIndexFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Get-CachedPageMeta
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Get a locally indexed/cached Confluence page id
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Get-CachedPageMeta `
|
||||||
|
-Title 'd231cc3422bfdf96.xml' `
|
||||||
|
-CacheIndexFile 'confluence-page-cache.json'
|
||||||
|
#>
|
||||||
|
Param(
|
||||||
|
[Parameter(Mandatory)] [string] $Title,
|
||||||
|
[Parameter(Mandatory)] [string] $CacheIndexFile
|
||||||
|
)
|
||||||
|
|
||||||
|
Process
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$raw = Get-Content $CacheIndexFile
|
||||||
|
}
|
||||||
|
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
$raw = "{}"
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = $raw | ConvertFrom-JSON
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$pageMeta = $data | Select -ExpandProperty $Title
|
||||||
|
|
||||||
|
$pageMeta
|
||||||
|
|
||||||
|
Write-Debug "page id cache hit: $Title -> $($pageMeta.PageId)"
|
||||||
|
}
|
||||||
|
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
$null
|
||||||
|
|
||||||
|
Write-Debug "page id cache miss: $Title"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Register-PageMeta
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Register a Confluence page's metadata in the local cache
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Add-ConfluencePage
|
||||||
|
-Host 'confluence.contoso.com' `
|
||||||
|
-Space 'TIARA' `
|
||||||
|
-Title 'Testitest' `
|
||||||
|
-Content @{}
|
||||||
|
#>
|
||||||
|
Param(
|
||||||
|
[Parameter(Mandatory)] [string] $PageId,
|
||||||
|
[Parameter()] [int] $Version = 0,
|
||||||
|
[Parameter(Mandatory)] [string] $Title,
|
||||||
|
[Parameter()] [string] $ContentHash = '',
|
||||||
|
[Parameter(Mandatory)] [string] $CacheIndexFile
|
||||||
|
)
|
||||||
|
|
||||||
|
Process
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$raw = Get-Content $CacheIndexFile
|
||||||
|
}
|
||||||
|
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
$raw = "{}"
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = $raw | ConvertFrom-JSON
|
||||||
|
|
||||||
|
$data | Add-Member -Name $Title `
|
||||||
|
-Value @{
|
||||||
|
'PageId' = $PageId
|
||||||
|
'Version' = $Version
|
||||||
|
'ContentHash' = $ContentHash
|
||||||
|
} `
|
||||||
|
-MemberType NoteProperty `
|
||||||
|
-Force
|
||||||
|
|
||||||
|
Set-Content -Path $CacheIndexFile -Value ($data | ConvertTo-JSON)
|
||||||
|
|
||||||
|
Write-Debug "indexed page id: $Title -> $PageId"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Publish-Page
|
||||||
|
{
|
||||||
|
Param(
|
||||||
|
# title of the page (used for manifest lookup)
|
||||||
|
[Parameter(Mandatory)] [string] $Title,
|
||||||
|
# hostname of Confluence instance
|
||||||
|
[Parameter(Mandatory)] [string] $Host,
|
||||||
|
# name of Confluence space
|
||||||
|
[Parameter(Mandatory)] [string] $Space,
|
||||||
|
# manifest object
|
||||||
|
[Parameter(Mandatory)] [PSObject] $Manifest
|
||||||
|
)
|
||||||
|
|
||||||
|
Begin
|
||||||
|
{
|
||||||
|
$pageMeta = Get-PageMeta `
|
||||||
|
-Host $hostname `
|
||||||
|
-Space $spaceName `
|
||||||
|
-Title $Title `
|
||||||
|
-Manifest $Manifest
|
||||||
|
}
|
||||||
|
|
||||||
|
Process
|
||||||
|
{
|
||||||
|
if ($pageMeta.ContentHash -eq $_)
|
||||||
|
{
|
||||||
|
Write-Host "skipping (no changes): $Title"
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$pageId = $pageMeta.PageId
|
||||||
|
|
||||||
|
$path = Join-Path $basepath 'content' "$_"
|
||||||
|
|
||||||
|
$pageContent = Get-Content $path | Out-String
|
||||||
|
|
||||||
|
$prettyName = $Title
|
||||||
|
|
||||||
|
if ($data.pages[$_].ancestor_id)
|
||||||
|
{
|
||||||
|
$ancestorTitle = $data.pages[$data.pages[$_].ancestor_id].title
|
||||||
|
|
||||||
|
$ancestorPageMeta = Get-PageMeta `
|
||||||
|
-Host $hostname `
|
||||||
|
-Space $spaceName `
|
||||||
|
-Title $ancestorTitle `
|
||||||
|
-CacheIndexFile $cacheIndexFile
|
||||||
|
|
||||||
|
if ($ancestorPageMeta)
|
||||||
|
{
|
||||||
|
$ancestorPageId = $ancestorPageMeta.PageId
|
||||||
|
}
|
||||||
|
|
||||||
|
$prettyName += " [$ancestorPageId]"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-Not $pageId)
|
||||||
|
{
|
||||||
|
Write-Host ("create ${_}: $prettyName")
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pageMeta = New-ConfluencePage `
|
||||||
|
-Host $hostname `
|
||||||
|
-Space $spaceName `
|
||||||
|
-Title $pageTitle `
|
||||||
|
-Content $pageContent `
|
||||||
|
-Ancestor $ancestorPageId
|
||||||
|
}
|
||||||
|
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Write-Host "error (skipping): $prettyName"
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Register-PageMeta `
|
||||||
|
-PageId $pageMeta.PageId `
|
||||||
|
-Version $pageMeta.Version `
|
||||||
|
-Title $pageTitle `
|
||||||
|
-ContentHash $_ `
|
||||||
|
-CacheIndexFile $cacheIndexFile
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Write-Host ("update ${_} (${pageId}): $prettyName")
|
||||||
|
|
||||||
|
$version = $pageMeta.Version + 1
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Update-Page `
|
||||||
|
-Host $hostname `
|
||||||
|
-PageId $pageId `
|
||||||
|
-Space $spaceName `
|
||||||
|
-Title $pageTitle `
|
||||||
|
-Version $version `
|
||||||
|
-Content $pageContent `
|
||||||
|
-Ancestor $ancestorPageId
|
||||||
|
}
|
||||||
|
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Write-Host "error (skipping): $prettyName"
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Register-PageMeta `
|
||||||
|
-PageId $pageMeta.PageId `
|
||||||
|
-Version $version `
|
||||||
|
-Title $pageTitle `
|
||||||
|
-ContentHash $_ `
|
||||||
|
-CacheIndexFile $cacheIndexFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Publish-All
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
1. cast index hash table to array
|
||||||
|
2. (quick) sort the array
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Get-Help -Name Test-Help
|
||||||
|
|
||||||
|
This shows the help for the example function.
|
||||||
|
#>
|
||||||
|
Param(
|
||||||
|
[Parameter(Mandatory, Position = 0)] [string] $Url,
|
||||||
|
[Parameter(Mandatory, Position = 1)] [string] $Manifest
|
||||||
|
)
|
||||||
|
|
||||||
|
Begin
|
||||||
|
{
|
||||||
|
$hostname = ([uri]$url).Host
|
||||||
|
|
||||||
|
$spaceName = (Split-Path -Leaf (Split-Path $Url))
|
||||||
|
|
||||||
|
$ancestorName = Split-Path -Leaf $url
|
||||||
|
|
||||||
|
$data = Get-Content -Raw $Manifest | ConvertFrom-JSON -AsHashtable
|
||||||
|
|
||||||
|
$basepath = Split-Path $Manifest
|
||||||
|
|
||||||
|
$cacheIndexFile = 'confluence-page-cache.json'
|
||||||
|
}
|
||||||
|
|
||||||
|
Process
|
||||||
|
{
|
||||||
|
$data.pages.keys | ForEach-Object `
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
435
PSConfluencePublisher/Page.psm1
Executable file
435
PSConfluencePublisher/Page.psm1
Executable file
|
|
@ -0,0 +1,435 @@
|
||||||
|
#!/usr/bin/env pwsh
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
|
||||||
|
function Get-CachedPageMeta
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Get a locally indexed/cached Confluence page id
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Get-CachedPageMeta `
|
||||||
|
-Title 'd231cc3422bfdf96.xml' `
|
||||||
|
-CacheIndexFile 'confluence-page-cache.json'
|
||||||
|
#>
|
||||||
|
Param(
|
||||||
|
[Parameter(Mandatory)] [string] $Title,
|
||||||
|
[Parameter(Mandatory)] [string] $CacheIndexFile
|
||||||
|
)
|
||||||
|
|
||||||
|
Process
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$raw = Get-Content $CacheIndexFile
|
||||||
|
}
|
||||||
|
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
$raw = "{}"
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = $raw | ConvertFrom-JSON
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$pageMeta = $data | Select -ExpandProperty $Title
|
||||||
|
|
||||||
|
$pageMeta
|
||||||
|
|
||||||
|
Write-Debug "page id cache hit: $Title -> $($pageMeta.PageId)"
|
||||||
|
}
|
||||||
|
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
$null
|
||||||
|
|
||||||
|
Write-Debug "page id cache miss: $Title"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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(
|
||||||
|
[Parameter(Mandatory)] [string] $Host,
|
||||||
|
[Parameter(Mandatory)] [string] $Title,
|
||||||
|
[Parameter(Mandatory)] [string] $Space,
|
||||||
|
[Parameter(Mandatory)] [string] $CacheIndexFile
|
||||||
|
)
|
||||||
|
|
||||||
|
Process
|
||||||
|
{
|
||||||
|
if ($Title)
|
||||||
|
{
|
||||||
|
$cachedPageMeta = Get-CachedPageMeta `
|
||||||
|
-Title $Title `
|
||||||
|
-CacheIndexFile $CacheIndexFile
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($cachedPageMeta)
|
||||||
|
{
|
||||||
|
return $cachedPageMeta
|
||||||
|
}
|
||||||
|
|
||||||
|
$escapedTitle = [uri]::EscapeDataString($Title)
|
||||||
|
|
||||||
|
$query = "title=${escapedTitle}&spaceKey=${Space}&expand=history"
|
||||||
|
|
||||||
|
Assert-PersonalAccessToken $Host
|
||||||
|
|
||||||
|
Invoke-WebRequest `
|
||||||
|
-Uri "https://${Host}/rest/api/content?$query" `
|
||||||
|
-Method 'Get' `
|
||||||
|
-Headers @{
|
||||||
|
'Authorization' = "Bearer $([System.Net.NetworkCredential]::new('', $script:PATS[$Host_]).Password)"
|
||||||
|
} `
|
||||||
|
-OutVariable response
|
||||||
|
|
||||||
|
$results = ($response.Content | ConvertFrom-JSON).results
|
||||||
|
|
||||||
|
if ($results.Count -gt 1)
|
||||||
|
{
|
||||||
|
throw "more than one result for query: $query"
|
||||||
|
}
|
||||||
|
elseif ($results.Count -eq 1)
|
||||||
|
{
|
||||||
|
Register-PageMeta `
|
||||||
|
-PageId $results[0].id `
|
||||||
|
-Version ($results[0]._expandable | Select -ExpandProperty 'version') `
|
||||||
|
-Title $Title `
|
||||||
|
-CacheIndexFile $CacheIndexFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Register-PageMeta
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Register a Confluence page's metadata in the local cache
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Add-ConfluencePage
|
||||||
|
-Host 'confluence.contoso.com' `
|
||||||
|
-Space 'TIARA' `
|
||||||
|
-Title 'Testitest' `
|
||||||
|
-Content @{}
|
||||||
|
#>
|
||||||
|
Param(
|
||||||
|
[Parameter(Mandatory)] [string] $PageId,
|
||||||
|
[Parameter()] [int] $Version = 0,
|
||||||
|
[Parameter(Mandatory)] [string] $Title,
|
||||||
|
[Parameter()] [string] $ContentHash = '',
|
||||||
|
[Parameter(Mandatory)] [string] $CacheIndexFile
|
||||||
|
)
|
||||||
|
|
||||||
|
Process
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$raw = Get-Content $CacheIndexFile
|
||||||
|
}
|
||||||
|
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
$raw = "{}"
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = $raw | ConvertFrom-JSON
|
||||||
|
|
||||||
|
$data | Add-Member -Name $Title `
|
||||||
|
-Value @{
|
||||||
|
'PageId' = $PageId
|
||||||
|
'Version' = $Version
|
||||||
|
'ContentHash' = $ContentHash
|
||||||
|
} `
|
||||||
|
-MemberType NoteProperty `
|
||||||
|
-Force
|
||||||
|
|
||||||
|
Set-Content -Path $CacheIndexFile -Value ($data | ConvertTo-JSON)
|
||||||
|
|
||||||
|
Write-Debug "indexed page id: $Title -> $PageId"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function New-Page
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Add a confluence page
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Add-ConfluencePage
|
||||||
|
-Host 'confluence.contoso.com' `
|
||||||
|
-Space 'TIARA' `
|
||||||
|
-Title 'Testitest' `
|
||||||
|
-Content @{}
|
||||||
|
#>
|
||||||
|
Param(
|
||||||
|
[Parameter(Mandatory)] [string] $Host,
|
||||||
|
# The name of the Confluence space to publish to
|
||||||
|
[Parameter(Mandatory)] [string] $Space,
|
||||||
|
# title of page to be published
|
||||||
|
[Parameter(Mandatory)] [string] $Title,
|
||||||
|
# content of page
|
||||||
|
[Parameter(Mandatory)] [string] $Content,
|
||||||
|
# parent page id
|
||||||
|
[Parameter()] [string] $Ancestor
|
||||||
|
)
|
||||||
|
|
||||||
|
Process
|
||||||
|
{
|
||||||
|
Assert-PersonalAccessToken $Host
|
||||||
|
|
||||||
|
$transportBody = @{
|
||||||
|
'type' = 'page'
|
||||||
|
'title' = $Title
|
||||||
|
'space' = @{
|
||||||
|
'key' = $Space
|
||||||
|
}
|
||||||
|
'body' = @{
|
||||||
|
'storage' = @{
|
||||||
|
'value' = $Content
|
||||||
|
'representation' = 'storage'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} | ConvertTo-JSON
|
||||||
|
|
||||||
|
Invoke-WebRequest `
|
||||||
|
-Uri "https://${Host}/rest/api/content" `
|
||||||
|
-Method 'Post' `
|
||||||
|
-Headers @{
|
||||||
|
'Authorization' = "Bearer $([System.Net.NetworkCredential]::new('', $script:PATS[$Host_]).Password)"
|
||||||
|
} `
|
||||||
|
-ContentType "application/json" `
|
||||||
|
-Body $transportBody `
|
||||||
|
-OutVariable rawResponse | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
End
|
||||||
|
{
|
||||||
|
$response = ($rawResponse.Content | ConvertFrom-JSON)
|
||||||
|
|
||||||
|
@{
|
||||||
|
'PageId' = $response.Id
|
||||||
|
'Version' = $response.version | Select -ExpandProperty 'number'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Update-Page
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Add a confluence page
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Add-ConfluencePage
|
||||||
|
-Host 'confluence.contoso.com' `
|
||||||
|
-Space 'TIARA' `
|
||||||
|
-Title 'Testitest' `
|
||||||
|
-Content @{}
|
||||||
|
#>
|
||||||
|
Param(
|
||||||
|
[Parameter(Mandatory)] [string] $Host,
|
||||||
|
# The page id of an existing page
|
||||||
|
[Parameter(Mandatory)] [string] $PageId,
|
||||||
|
# The name of the Confluence space to publish to
|
||||||
|
[Parameter(Mandatory)] [string] $Space,
|
||||||
|
# title of page to be published
|
||||||
|
[Parameter(Mandatory)] [string] $Title,
|
||||||
|
# version of content
|
||||||
|
[Parameter(Mandatory)] [int] $Version,
|
||||||
|
# content of page
|
||||||
|
[Parameter(Mandatory)] [string] $Content,
|
||||||
|
# parent page id
|
||||||
|
[Parameter()] [string] $Ancestor
|
||||||
|
)
|
||||||
|
|
||||||
|
Process
|
||||||
|
{
|
||||||
|
Assert-PersonalAccessToken $Host
|
||||||
|
|
||||||
|
$transportBody = @{
|
||||||
|
'id' = $PageId
|
||||||
|
'type' = 'page'
|
||||||
|
'title' = $Title
|
||||||
|
'space' = @{
|
||||||
|
'key' = $Space
|
||||||
|
}
|
||||||
|
'body' = @{
|
||||||
|
'storage' = @{
|
||||||
|
'value' = $Content
|
||||||
|
'representation' = 'storage'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'version' = @{
|
||||||
|
'number' = $Version
|
||||||
|
}
|
||||||
|
} | ConvertTo-JSON
|
||||||
|
|
||||||
|
Invoke-WebRequest `
|
||||||
|
-Uri "https://${Host}/rest/api/content/$PageId" `
|
||||||
|
-Method 'Put' `
|
||||||
|
-Headers @{
|
||||||
|
'Authorization' = "Bearer $([System.Net.NetworkCredential]::new('', $script:PATS[$Host_]).Password)"
|
||||||
|
} `
|
||||||
|
-ContentType "application/json" `
|
||||||
|
-Body $transportBody `
|
||||||
|
-OutVariable rawResponse | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
End
|
||||||
|
{
|
||||||
|
$response = ($rawResponse.Content | ConvertFrom-JSON)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Publish-Page
|
||||||
|
{
|
||||||
|
Param(
|
||||||
|
# title of the page (used for manifest lookup)
|
||||||
|
[Parameter(Mandatory)] [string] $Title,
|
||||||
|
# hostname of Confluence instance
|
||||||
|
[Parameter(Mandatory)] [string] $Host,
|
||||||
|
# name of Confluence space
|
||||||
|
[Parameter(Mandatory)] [string] $Space,
|
||||||
|
# manifest object
|
||||||
|
[Parameter(Mandatory)] [PSObject] $Manifest
|
||||||
|
)
|
||||||
|
|
||||||
|
Begin
|
||||||
|
{
|
||||||
|
$pageMeta = Get-PageMeta `
|
||||||
|
-Host $hostname `
|
||||||
|
-Space $spaceName `
|
||||||
|
-Title $Title `
|
||||||
|
-Manifest $Manifest
|
||||||
|
}
|
||||||
|
|
||||||
|
Process
|
||||||
|
{
|
||||||
|
if ($pageMeta.ContentHash -eq $_)
|
||||||
|
{
|
||||||
|
Write-Host "skipping (no changes): $Title"
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$pageId = $pageMeta.PageId
|
||||||
|
|
||||||
|
$path = Join-Path $basepath 'content' "$_"
|
||||||
|
|
||||||
|
$pageContent = Get-Content $path | Out-String
|
||||||
|
|
||||||
|
$prettyName = $Title
|
||||||
|
|
||||||
|
if ($data.pages[$_].ancestor_id)
|
||||||
|
{
|
||||||
|
$ancestorTitle = $data.pages[$data.pages[$_].ancestor_id].title
|
||||||
|
|
||||||
|
$ancestorPageMeta = Get-PageMeta `
|
||||||
|
-Host $hostname `
|
||||||
|
-Space $spaceName `
|
||||||
|
-Title $ancestorTitle `
|
||||||
|
-CacheIndexFile $cacheIndexFile
|
||||||
|
|
||||||
|
if ($ancestorPageMeta)
|
||||||
|
{
|
||||||
|
$ancestorPageId = $ancestorPageMeta.PageId
|
||||||
|
}
|
||||||
|
|
||||||
|
$prettyName += " [$ancestorPageId]"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-Not $pageId)
|
||||||
|
{
|
||||||
|
Write-Host ("create ${_}: $prettyName")
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pageMeta = New-Page `
|
||||||
|
-Host $hostname `
|
||||||
|
-Space $spaceName `
|
||||||
|
-Title $pageTitle `
|
||||||
|
-Content $pageContent `
|
||||||
|
-Ancestor $ancestorPageId
|
||||||
|
}
|
||||||
|
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Write-Host "error (skipping): $prettyName"
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Register-PageMeta `
|
||||||
|
-PageId $pageMeta.PageId `
|
||||||
|
-Version $pageMeta.Version `
|
||||||
|
-Title $pageTitle `
|
||||||
|
-ContentHash $_ `
|
||||||
|
-CacheIndexFile $cacheIndexFile
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Write-Host ("update ${_} (${pageId}): $prettyName")
|
||||||
|
|
||||||
|
$version = $pageMeta.Version + 1
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Update-Page `
|
||||||
|
-Host $hostname `
|
||||||
|
-PageId $pageId `
|
||||||
|
-Space $spaceName `
|
||||||
|
-Title $pageTitle `
|
||||||
|
-Version $version `
|
||||||
|
-Content $pageContent `
|
||||||
|
-Ancestor $ancestorPageId
|
||||||
|
}
|
||||||
|
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Write-Host "error (skipping): $prettyName"
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Register-PageMeta `
|
||||||
|
-PageId $pageMeta.PageId `
|
||||||
|
-Version $version `
|
||||||
|
-Title $pageTitle `
|
||||||
|
-ContentHash $_ `
|
||||||
|
-CacheIndexFile $cacheIndexFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
65
PSConfluencePublisher/PersonalAccessToken.Tests.ps1
Executable file
65
PSConfluencePublisher/PersonalAccessToken.Tests.ps1
Executable file
|
|
@ -0,0 +1,65 @@
|
||||||
|
#!/usr/bin/env pwsh
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
BeforeAll {
|
||||||
|
Import-Module (Join-Path $PSScriptRoot 'PSConfluencePublisher.psd1') -Force
|
||||||
|
|
||||||
|
$mockHost = 'confluence.contoso.com'
|
||||||
|
|
||||||
|
$mockPat = '01234567890123456789'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Describe 'Register-PersonalAccessToken' `
|
||||||
|
{
|
||||||
|
BeforeEach {
|
||||||
|
Initialize-PersonalAccessTokenStore
|
||||||
|
}
|
||||||
|
|
||||||
|
Context 'Parameterized' {
|
||||||
|
|
||||||
|
It 'throws no exception' {
|
||||||
|
Register-PersonalAccessToken -Host $mockHost -Token $mockPat
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Context 'Shorthand' {
|
||||||
|
|
||||||
|
It 'throws no exception' {
|
||||||
|
Register-PersonalAccessToken $mockHost $mockPat
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Describe 'Get-PersonalAccessToken' `
|
||||||
|
{
|
||||||
|
BeforeEach {
|
||||||
|
Initialize-PersonalAccessTokenStore
|
||||||
|
}
|
||||||
|
|
||||||
|
Context 'Parameterized' {
|
||||||
|
|
||||||
|
It 'gets an existing PAT' {
|
||||||
|
|
||||||
|
Register-PersonalAccessToken -Host $mockHost -Token $mockPat
|
||||||
|
|
||||||
|
Get-PersonalAccessToken -Host $mockHost | Should -Be $mockPat
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'requires PAT to exist' {
|
||||||
|
|
||||||
|
{Get-PersonalAccessToken -Host $mockHost} | Should -Throw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Context 'Shorthand' {
|
||||||
|
|
||||||
|
It 'throws no exception' {
|
||||||
|
|
||||||
|
Register-PersonalAccessToken -Host $mockHost -Token $mockPat
|
||||||
|
|
||||||
|
Get-PersonalAccessToken $mockHost | Should -Be $mockPat
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
99
PSConfluencePublisher/PersonalAccessToken.psm1
Executable file
99
PSConfluencePublisher/PersonalAccessToken.psm1
Executable file
|
|
@ -0,0 +1,99 @@
|
||||||
|
#!/usr/bin/env pwsh
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Utilities for working with Confluence Personal Access Tokens
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
|
||||||
|
Register-PersonalAccessToken `
|
||||||
|
-Host 'confluence.contoso.com' `
|
||||||
|
-Token '123456789123456789'
|
||||||
|
|
||||||
|
Get-PersonalAccessToken -Host 'confluence.contoso.com'
|
||||||
|
#>
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
|
||||||
|
#session storage of Confluence personal access tokens
|
||||||
|
$script:PATS = @{}
|
||||||
|
|
||||||
|
|
||||||
|
function Initialize-PersonalAccessTokenStore
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Initialize the store within this script's scope.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Initialize-PersonalAccessTokenStore
|
||||||
|
#>
|
||||||
|
Process
|
||||||
|
{
|
||||||
|
$script:PATS = @{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Register-PersonalAccessToken
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Register a Confluence Personal Access Token (PAT)
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
The PAT is stored in the pseudo-local ``script`` scope as a
|
||||||
|
SecureString. Implementors of functions accessing PATs MUST stall
|
||||||
|
conversion to plain text string until the string is actually needed
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Register-PersonalAccessToken confluence.contoso.com 0123456789
|
||||||
|
#>
|
||||||
|
[CmdletBinding()]
|
||||||
|
|
||||||
|
Param(
|
||||||
|
[Parameter(Mandatory, Position = 0)] [string] $Host,
|
||||||
|
[Parameter(Mandatory, Position = 1)] [string] $Token
|
||||||
|
)
|
||||||
|
|
||||||
|
Process
|
||||||
|
{
|
||||||
|
if ($script:PATS[$Host])
|
||||||
|
{
|
||||||
|
Write-Debug "PAT for '$Host' already registered, overwriting."
|
||||||
|
}
|
||||||
|
|
||||||
|
$script:PATS[$Host] = ConvertTo-SecureString $Token -AsPlainText -Force
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Get-PersonalAccessToken
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Get a Confluence Personal Access Token (PAT) registered in this
|
||||||
|
script scope.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Get-PersonalAccessToken confluence.contoso.com
|
||||||
|
#>
|
||||||
|
Param(
|
||||||
|
# Confluence instance hostname
|
||||||
|
[Parameter(Mandatory, Position = 0)] [string] $Host
|
||||||
|
)
|
||||||
|
|
||||||
|
Process
|
||||||
|
{
|
||||||
|
if (-Not $PATS[$Host])
|
||||||
|
{
|
||||||
|
throw "No personal access token for host '$Host' registered. " +
|
||||||
|
"Hint: Call ``Register-PersonalAccessToken``"
|
||||||
|
}
|
||||||
|
|
||||||
|
$([Net.NetworkCredential]::new('', $script:PATS[$Host]).Password)
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
PSConfluencePublisher/_mock/._test-manifest1.json
Executable file
BIN
PSConfluencePublisher/_mock/._test-manifest1.json
Executable file
Binary file not shown.
BIN
PSConfluencePublisher/_mock/._test-manifest2.json
Executable file
BIN
PSConfluencePublisher/_mock/._test-manifest2.json
Executable file
Binary file not shown.
4
PSConfluencePublisher/_mock/test-manifest1.json
Executable file
4
PSConfluencePublisher/_mock/test-manifest1.json
Executable file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"pages": {},
|
||||||
|
"attachments": {}
|
||||||
|
}
|
||||||
12
PSConfluencePublisher/_mock/test-manifest2.json
Executable file
12
PSConfluencePublisher/_mock/test-manifest2.json
Executable file
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"pages": {
|
||||||
|
"": {
|
||||||
|
"Ref": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"attachments": {
|
||||||
|
"": {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
93
PSConfluencePublisher/manifest.schema.json
Executable file
93
PSConfluencePublisher/manifest.schema.json
Executable file
|
|
@ -0,0 +1,93 @@
|
||||||
|
{
|
||||||
|
"$id": "https://spec.victory-k.it/psconfluencepublisher.json",
|
||||||
|
"x-authors": [
|
||||||
|
"theodor.rodweil@victory-k.it"
|
||||||
|
],
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"pages": {
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
".*": {
|
||||||
|
"$ref": "#/definitions/page"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"attachments": {
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
".*": {
|
||||||
|
"$ref": "#/definitions/attachment"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"pages",
|
||||||
|
"attachments"
|
||||||
|
],
|
||||||
|
"definitions": {
|
||||||
|
"page": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Local Confluence page/container attachment metadata",
|
||||||
|
"properties": {
|
||||||
|
"PageId": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Id of attachment defined by Confluence instance. The id is generated after the publishing of a page."
|
||||||
|
},
|
||||||
|
"Version": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"Hash": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "SHA512 hexadecimal content hash value"
|
||||||
|
},
|
||||||
|
"Ref": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Local filesystem reference/path"
|
||||||
|
},
|
||||||
|
"AncestorTitle": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Title of Confluence page this page is a child of. The title must be a property key of the pages object."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"Hash",
|
||||||
|
"Ref"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"attachment": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Local Confluence page/container attachment metadata",
|
||||||
|
"properties": {
|
||||||
|
"AttachmentId": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Id of attachment defined by Confluence instance. The id is generated after the publishing of an attachment."
|
||||||
|
},
|
||||||
|
"Hash": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "SHA512 hexadecimal attachment content hash value"
|
||||||
|
},
|
||||||
|
"MimeType": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "MIME type of attachment",
|
||||||
|
"default": "binary/octet-stream"
|
||||||
|
},
|
||||||
|
"ContainerPageTitle": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Title of Confluence page this attachment is contained in. The title must be a property key of the pages object."
|
||||||
|
},
|
||||||
|
"Ref": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Local filesystem reference/path"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"Hash",
|
||||||
|
"MimeType",
|
||||||
|
"ContainerPageTitle",
|
||||||
|
"Ref"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
PSConfluencePublisher/scripts.deps.json
Executable file
19
PSConfluencePublisher/scripts.deps.json
Executable file
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"runtimeTarget": {
|
||||||
|
"name": ".NETStandard,Version=v2.0/",
|
||||||
|
"signature": ""
|
||||||
|
},
|
||||||
|
"compilationOptions": {},
|
||||||
|
"targets": {
|
||||||
|
".NETStandard,Version=v2.0": {},
|
||||||
|
".NETStandard,Version=v2.0/": {
|
||||||
|
"PSConfluencePublisher/1.0.0": {
|
||||||
|
"dependencies": {
|
||||||
|
"NETStandard.Library": "2.0.3"
|
||||||
|
},
|
||||||
|
"runtime": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"libraries": {}
|
||||||
|
}
|
||||||
85
README.md
Executable file
85
README.md
Executable file
|
|
@ -0,0 +1,85 @@
|
||||||
|
# PSConfluencePublisher
|
||||||
|
|
||||||
|
This program is a standalone publisher component for the
|
||||||
|
`sphinxcontrib.confluencebuilder` Sphinx extension.
|
||||||
|
|
||||||
|
It consumes, a JSON-formatted manifest of a *Sphinx build* dump generated by
|
||||||
|
the ``sphinxcontrib.xconfluencebuilder`` and unidirectionally synchronizes
|
||||||
|
pages, page ancestry, and attachments.
|
||||||
|
|
||||||
|
Publishing is supported via the Confluence Server REST API through
|
||||||
|
[Personal Access Token (PAT) authorization](https://confluence.atlassian.com/enterprise/using-personal-access-tokens-1026032365.html).
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
You can install the module via [nuget](https://www.nuget.org).
|
||||||
|
|
||||||
|
```
|
||||||
|
Install-Module victorykit.PSConfluencePublisher
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, you can import the module from source. In order to do that,
|
||||||
|
clone the
|
||||||
|
[Git repository](https://bitbucket.org/victorykit/psconfluencepublisher/src)
|
||||||
|
, change into the directory and import it.
|
||||||
|
|
||||||
|
```
|
||||||
|
PS> git clone git@bitbucket.org:victorykit/psconfluencepublisher.git
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
PS> # universal import statement compatible with PowerShell Core & Desktop
|
||||||
|
PS> Import-Module (Join-Path 'PSConfluencePublisher'
|
||||||
|
'PSConfluencePublisher.psd1')
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, register your personal access token for your Confluence server instance.
|
||||||
|
The token is stored as a *SecureString* within the *Script* scope.
|
||||||
|
|
||||||
|
```
|
||||||
|
Register-PersonalAccessToken `
|
||||||
|
-Host 'confluence.contoso.com' `
|
||||||
|
-Token '123456789123456789'
|
||||||
|
```
|
||||||
|
|
||||||
|
Optionally, you may test the connectivity to your Confluence instance. The test
|
||||||
|
will try to retrieve your user profile, in order to determine whether the PAT
|
||||||
|
authenticates, since an invalid PAT simply results in anonymous authentication
|
||||||
|
for some REST API functions.
|
||||||
|
|
||||||
|
```
|
||||||
|
Test-Connection confluence.contoso.com
|
||||||
|
```
|
||||||
|
|
||||||
|
Now you may publish by supplying the URL of the root Confluence page
|
||||||
|
you want to publish to, in addition to the location of the local dump manifest.
|
||||||
|
Make sure to use the full URL, with the same hostname as the one you used to
|
||||||
|
register your personal access token.
|
||||||
|
|
||||||
|
```
|
||||||
|
Publish-Dump `
|
||||||
|
-Url 'https://confluence.contoso.com/display/TIARA/Testitest' `
|
||||||
|
-DumpIndex build/docs/confluence.out/data.json
|
||||||
|
```
|
||||||
|
|
||||||
|
The manifest may be writable, where it is then used to cache the publishing
|
||||||
|
status of each page and attachment.
|
||||||
|
|
||||||
|
You may publish a single page, which however requires it's direct ancestor page
|
||||||
|
to exist.
|
||||||
|
|
||||||
|
```
|
||||||
|
Publish-Page
|
||||||
|
```
|
||||||
|
|
||||||
|
## Debugging
|
||||||
|
|
||||||
|
To display debug messages, set
|
||||||
|
[$DebugPreference](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_preference_variables?view=powershell-7.3#debugpreference)
|
||||||
|
to `Continue`, or `Inquire` in your shell's *Global* scope.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
This program requires [Pester](https://pester.dev/) to execute it's test suite.
|
||||||
|
|
||||||
|
``PS> Invoke-Pester PSConfluencePublisher/*.Tests.ps1 -Show 'All'``
|
||||||
Loading…
Add table
Add a link
Reference in a new issue