Skip to content

Commit 4e867fc

Browse files
🚀 [Feature]: Connect by site name; add accessible-resources and v1/v2 map
Connect-Confluence now takes -Site (name/host/URL, resolved to a cloud ID via the public tenant_info endpoint) or -CloudId (faster, direct); it builds the api.atlassian.com/ex/confluence/<cloudId> gateway base internally. Adds Get-ConfluenceAccessibleResource (lists sites/cloudIds/scopes a bearer token can reach) and an internal v1/v2 API-path map (v1 -> /wiki/rest/api, v2 -> /wiki/api/v2) usable via Invoke-ConfluenceRestMethod -ApiVersion. Integration test connects with -Site from CONFLUENCE_SITE.
1 parent 56b279a commit 4e867fc

6 files changed

Lines changed: 122 additions & 16 deletions

File tree

‎.github/workflows/Process-PSModule.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ jobs:
4444
# Non-secret configuration the module's tests need, exposed as environment variables (not masked).
4545
TestVariables: >-
4646
{
47-
"CONFLUENCE_API_BASE_URI": ${{ toJSON(vars.CONFLUENCE_API_BASE_URI) }},
47+
"CONFLUENCE_SITE": ${{ toJSON(vars.CONFLUENCE_SITE) }},
4848
"CONFLUENCE_USERNAME": ${{ toJSON(vars.CONFLUENCE_USERNAME) }},
4949
"CONFLUENCE_SPACE_KEY": ${{ toJSON(vars.CONFLUENCE_SPACE_KEY) }}
5050
}

‎src/functions/public/API/Invoke-ConfluenceRestMethod.ps1‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,16 @@ function Invoke-ConfluenceRestMethod {
4040
param(
4141
# The API path beginning with '/wiki/', e.g. '/wiki/api/v2/pages'.
4242
# A full absolute URL is also accepted (used when following pagination links).
43+
# When -ApiVersion is supplied this is treated as a path relative to that
44+
# API family's root (e.g. -ApiVersion v2 -ApiEndpoint 'spaces').
4345
[Parameter(Mandatory)]
4446
[string]$ApiEndpoint,
4547

48+
# Optional API family. Uses the internal map to prepend the version prefix
49+
# (v1 -> /wiki/rest/api, v2 -> /wiki/api/v2) to a relative -ApiEndpoint.
50+
[ValidateSet('v1', 'v2')]
51+
[string]$ApiVersion,
52+
4653
# The HTTP method. Defaults to GET.
4754
[ValidateSet('GET', 'POST', 'PUT', 'DELETE')]
4855
[string]$Method = 'GET',
@@ -67,6 +74,11 @@ function Invoke-ConfluenceRestMethod {
6774
throw "The -All switch follows pagination and is only valid for GET requests, not $Method."
6875
}
6976

77+
# Resolve a relative endpoint against the internal v1/v2 map when a version is given.
78+
if ($ApiVersion -and $ApiEndpoint -notmatch '^https?://') {
79+
$ApiEndpoint = '{0}/{1}' -f $script:Confluence.ApiPaths[$ApiVersion], $ApiEndpoint.TrimStart('/')
80+
}
81+
7082
$resolved = Resolve-ConfluenceContext -Context $Context
7183
$token = Resolve-ConfluenceToken -Token $resolved.Token
7284
$pair = '{0}:{1}' -f $resolved.Username, $token

‎src/functions/public/Auth/Connect-Confluence.ps1‎

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,26 @@ function Connect-Confluence {
77
.DESCRIPTION
88
Validates the supplied credentials with a lightweight authenticated call,
99
stores them as a named context (the token is kept as a SecureString), and
10-
records the context as the module default. A token that authenticates but
11-
lacks the read:space scope still connects (with a warning).
10+
records the context as the module default. Supply the site with -Site (a
11+
name such as 'msxorg', a host, or a URL) and the cloud ID is resolved
12+
automatically; or pass a known -CloudId directly to skip the lookup. A
13+
token that authenticates but lacks the read:space scope still connects
14+
(with a warning).
1215
1316
.EXAMPLE
1417
```powershell
1518
$token = Read-Host -AsSecureString
16-
Connect-Confluence -ApiBaseUri $uri -Username $user -Token $token -SpaceKey 'DOCS'
19+
Connect-Confluence -Site 'msxorg' -Username $user -Token $token -SpaceKey 'DOCS'
1720
```
1821
19-
Connects with a scoped token and stores the profile with 'DOCS' as the default space.
22+
Resolves the cloud ID for msxorg.atlassian.net, connects, and stores 'DOCS' as the default space.
23+
24+
.EXAMPLE
25+
```powershell
26+
Connect-Confluence -CloudId 'fff64f40-36b7-4578-92be-b9d9b6b17658' -Username $user -Token $token
27+
```
28+
29+
Connects directly with a known cloud ID, skipping the site lookup.
2030
2131
.LINK
2232
https://psmodule.io/Confluence/Functions/Auth/Connect-Confluence/
@@ -27,12 +37,18 @@ function Connect-Confluence {
2737
.LINK
2838
https://developer.atlassian.com/cloud/confluence/scopes-for-oauth-2-3LO-and-forge-apps/
2939
#>
30-
[CmdletBinding(SupportsShouldProcess)]
40+
[CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Site')]
3141
[OutputType([pscustomobject])]
3242
param(
33-
# The Confluence API-gateway base URI, for example `https://api.atlassian.com/ex/confluence/<cloudId>`.
34-
[Parameter(Mandatory)]
35-
[string]$ApiBaseUri,
43+
# The Confluence Cloud site: a name (`msxorg`), a host (`msxorg.atlassian.net`),
44+
# or any URL on the site. The cloud ID is resolved automatically.
45+
[Parameter(Mandatory, ParameterSetName = 'Site')]
46+
[string]$Site,
47+
48+
# The Confluence Cloud ID - a faster alternative to -Site that skips the lookup.
49+
# Find it with `ConvertTo-ConfluenceCloudId` or `Get-ConfluenceAccessibleResource`.
50+
[Parameter(Mandatory, ParameterSetName = 'CloudId')]
51+
[string]$CloudId,
3652

3753
# The service-account user (email) used for HTTP Basic authentication.
3854
[Parameter(Mandatory)]
@@ -52,6 +68,13 @@ function Connect-Confluence {
5268
[switch]$PassThru
5369
)
5470

71+
if ($PSCmdlet.ParameterSetName -eq 'Site') {
72+
# Resolve a site name/host/URL to its cloud ID via the public tenant_info endpoint.
73+
$CloudId = ConvertTo-ConfluenceCloudId -Site $Site
74+
}
75+
# The scoped-token gateway base is always api.atlassian.com/ex/confluence/<cloudId>.
76+
$ApiBaseUri = 'https://api.atlassian.com/ex/confluence/{0}' -f $CloudId
77+
5578
if ([string]::IsNullOrEmpty($Name)) {
5679
# api.atlassian.com is shared by every Confluence Cloud site, so the host
5780
# alone is not unique. Include the trailing base-URI path segment (the
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#SkipTest:FunctionTest:Calls the public accessible-resources endpoint with a bearer token; covered by integration tests.
2+
function Get-ConfluenceAccessibleResource {
3+
<#
4+
.SYNOPSIS
5+
List the Atlassian sites (resources) a token can reach.
6+
7+
.DESCRIPTION
8+
Calls `https://api.atlassian.com/oauth/token/accessible-resources` with the
9+
token as a bearer credential and returns one object per accessible site.
10+
Each result exposes the site `id` (which is the cloud ID used in the
11+
API-gateway base URI), the site `url`, the `name`, and the `scopes` granted
12+
to the token for that site. Use it to discover the cloud ID and confirm the
13+
granted scopes before connecting.
14+
15+
.EXAMPLE
16+
```powershell
17+
$token = Read-Host -AsSecureString
18+
Get-ConfluenceAccessibleResource -Token $token
19+
```
20+
21+
Lists every site the token can reach, with the cloud ID (`id`), URL and granted scopes.
22+
23+
.EXAMPLE
24+
```powershell
25+
(Get-ConfluenceAccessibleResource -Token $token | Where-Object url -match 'msxorg').id
26+
```
27+
28+
Returns the cloud ID for the msxorg site from the token's accessible resources.
29+
30+
.OUTPUTS
31+
System.Management.Automation.PSObject
32+
33+
.LINK
34+
https://psmodule.io/Confluence/Functions/Auth/Get-ConfluenceAccessibleResource/
35+
36+
.LINK
37+
https://developer.atlassian.com/cloud/confluence/oauth-2-3lo-apps/#3--make-calls-to-the-api-using-the-access-token
38+
#>
39+
[CmdletBinding()]
40+
[OutputType([object])]
41+
param(
42+
# The scoped Atlassian API token as a SecureString.
43+
[Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
44+
[securestring]$Token
45+
)
46+
47+
process {
48+
$bearer = Resolve-ConfluenceToken -Token $Token
49+
$headers = @{
50+
Authorization = "Bearer $bearer"
51+
Accept = 'application/json'
52+
}
53+
$uri = 'https://api.atlassian.com/oauth/token/accessible-resources'
54+
Write-Verbose "Listing accessible resources from [$uri]."
55+
56+
try {
57+
Invoke-RestMethod -Uri $uri -Headers $headers -Method Get -ErrorAction Stop
58+
} catch {
59+
throw "Failed to list accessible resources from [$uri]: $($_.Exception.Message)"
60+
}
61+
}
62+
}

‎src/variables/private/Confluence.ps1‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
$script:Confluence = [pscustomobject]@{
22
ContextVault = 'PSModule.Confluence'
3+
# The API-gateway base is https://api.atlassian.com/ex/confluence/<cloudId>. These are the two
4+
# Confluence API families that hang off it; the map keeps the version prefixes in one place.
5+
# v2 -> <base>/wiki/api/v2/... (e.g. /wiki/api/v2/spaces)
6+
# v1 -> <base>/wiki/rest/api/... (e.g. /wiki/rest/api/content)
7+
ApiPaths = @{
8+
v1 = '/wiki/rest/api'
9+
v2 = '/wiki/api/v2'
10+
}
311
DefaultConfig = @{
412
ID = 'Module'
513
DefaultContext = ''

‎tests/Confluence.Tests.ps1‎

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ Describe 'Confluence' {
4040
'Get-ConfluenceSpace'
4141
'Get-ConfluenceSiteInfo'
4242
'ConvertTo-ConfluenceCloudId'
43+
'Get-ConfluenceAccessibleResource'
4344
'New-ConfluencePage'
4445
'Get-ConfluencePage'
4546
'Set-ConfluencePage'
@@ -71,17 +72,17 @@ Describe 'Confluence' {
7172

7273
# Integration tests run only when live credentials are provided. The calling workflow supplies
7374
# them through Process-PSModule's TestSecrets (CONFLUENCE_API_TOKEN, masked) and TestVariables
74-
# (CONFLUENCE_API_BASE_URI, CONFLUENCE_USERNAME, CONFLUENCE_SPACE_KEY), which are exposed as
75-
# environment variables. They are skipped locally and wherever the API token is not available.
75+
# (CONFLUENCE_SITE, CONFLUENCE_USERNAME, CONFLUENCE_SPACE_KEY), which are exposed as environment
76+
# variables. They are skipped locally and wherever the API token is not available.
7677
Context 'Integration' -Skip:([string]::IsNullOrEmpty($env:CONFLUENCE_API_TOKEN)) {
7778
BeforeAll {
7879
$secureToken = ConvertTo-SecureString -String $env:CONFLUENCE_API_TOKEN -AsPlainText -Force
7980
$connectParams = @{
80-
ApiBaseUri = $env:CONFLUENCE_API_BASE_URI
81-
Username = $env:CONFLUENCE_USERNAME
82-
Token = $secureToken
83-
SpaceKey = $env:CONFLUENCE_SPACE_KEY
84-
Name = 'ci'
81+
Site = $env:CONFLUENCE_SITE
82+
Username = $env:CONFLUENCE_USERNAME
83+
Token = $secureToken
84+
SpaceKey = $env:CONFLUENCE_SPACE_KEY
85+
Name = 'ci'
8586
}
8687
Connect-Confluence @connectParams
8788
}

0 commit comments

Comments
 (0)