Types/OpenPackage.Publisher/com.atproto.repo.createRecord.ps1

<#
.SYNOPSIS
    Publishes Records to At Protocol
.DESCRIPTION
    Publishes one or more records to the at protocol.
.LINK
    https://github.com/bluesky-social/atproto/blob/main/lexicons/com/atproto/repo/createRecord.json
#>

[CmdletBinding(PositionalBinding=$false,SupportsShouldProcess)]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute(
    "PSAvoidUsingPlainTextForPassword", 
    "", 
    Justification="
    SecureStrings are not actually more secure.
    Use -Credential to avoid potential information disclosure in Windows event logs.
    "

)]
param(
# Handle or other identifier supported by the server for the authenticating user.
[string]
$Identifier,

# The app password or account password.
[string]
$AppPassword,

# A credential used to connect.
# The username will be treated as the `-Identifier`.
# The password will be treated as the `-AppPassword`
[Management.Automation.PSCredential]
[Alias('PSCredential')]
$Credential,

# The personal data server used for the connection.
[Alias('PersonalDataServer')]
[string]
$PDS = "https://bsky.social/",

# The record key.
# This does not need to be provided.
# If no record key is provided,
# records will be created with a TimeStamp Identifier (`tid`)
# See https://atproto.com/specs/tid
# Individual records may also contain a `rkey`.
# If the record includes an `rkey`, this will be used.
[Alias('RecordKey')]
[string]
$RKey,

# Compare and swap with the previous commit by CID.
[string]
$SwapCommmit,

# Can be set to 'false' to skip Lexicon schema validation of record data,
# 'true' to require it, or leave unset to validate only for known Lexicons.
[switch]
$Validate,

# Any input to post.
# Only input with a `$type` property will be posted.
[Parameter(ValueFromPipeline)]
[Alias('Package')]
[PSObject[]]
$InputObject
)

# Quick collect all piped input
$allInput = @($input)

# If that had nothing, add any non-piped input objects
if (-not $allInput) {
    $allInput += $InputObject
}

# If we still have no input,
if (-not $allInput) {
    # error out.
    Write-Error "No input to publish"
    return
}

# Declare a filter to create our records.
filter createRecord {
    $NamespaceID = 'com.atproto.repo.createRecord'
    $httpMethod  = 'POST'
    $createAtRecord = $_
    # If the record we want to create has no `$type'
    if (-not $createAtRecord.'$type') {
        # warn and return
        Write-Warning 'No $type, will not createRecord'
        return
    }
    
    # Construct our create url
    $createUrl = "$(
        # If the PDS was `https://`,
        if ($pds -like 'https://*') {
            # just trim trailing slashes from https urls
            $pds -replace '/$'
        } else {
            # Otherwise prefix it by https://
            "https://$pds" -replace '/$'
    })/xrpc/$NamespaceID"


    # Prepare our parameters
    $invokeSplat = [Ordered]@{
        Uri = $createUrl
        Body = [Ordered]@{}
        Method = $httpMethod
        ContentType='application/json'
    }        

    # If there was no connection
    if (-not $atConnection) {        
        Write-Warning "Not connected!" # warn them
        return $invokeSplat # and return our parameters.
    }

    # If the connection had a did
    if ($atConnection.did) {
        # This becomes the `repo`
        $invokeSplat.Body['repo'] = $atConnection.did        
    }
    
    # The collection will always be the record `$type`
    $invokeSplat.Body.collection = $createAtRecord.'$type'

    # If we have explicitly provided an rkey
    if ($RKey) {
        $invokeSplat.Body.rkey = $RKey # use that
    }
    # Otherwise, if the record explicitly provides an rkey
    elseif ($createAtRecord.rkey) {
        # use that instead.
        $invokeSplat.Body.rkey = $createAtRecord.rkey
    }
    
    # If we want to validate, or the record indicates it should
    if ($validate -or $createAtRecord.validate) {
        # then we will validate.
        $invokeSplat.Body.validate = $true
    }

    # If we explicitly provide a swap commit
    if ($SwapCommmit) {
        # use it.
        $invokeSplat.Body.swapCommit = $SwapCommmit
    }
    # If the record provides a swap commit
    elseif ($createAtRecord.swapCommit) {
        # use that instead
        $invokeSplat.Body.swapCommit = $createAtRecord.swapCommit
    }

    # Last but not least, put our record into `.record`.
    $invokeSplat.Body.record = $createAtRecord

    # If -WhatIf was passed
    if ($WhatIfPreference) {
        # return the splat.
        return $invokeSplat
    }

    # Convert the body to json,
    $invokeSplat.Body = $invokeSplat.Body | 
        ConvertTo-Json -Depth 100

    # add our bearer token
    $invokeSplat.Headers = [Ordered]@{Authorization="Bearer $($atConnection.accessJwt)"}
    
    # and create the record.
    Invoke-RestMethod @invokeSplat
}


# Reset any potential connection.
$atConnection = $null
# and then see if we have enough data to connect.
if (-not $atConnection -and (
    ($Identifier -and $appPassword) -or 
    ($Credential)
)) {
    # If we do, prepare a splat
    $connectionSplat = [Ordered]@{}
    if ($identifier -and $AppPassword) {
        $connectionSplat.Identifier = $Identifier
        $connectionSplat.AppPassword = $AppPassword
    }
    else {
        $connectionSplat.Credential = $Credential
    }

    # and connect.
    $atConnection = Publish-OpenPackage -Publisher com.atproto.server.createSession -Option $connectionSplat
}


#region Create Records
$InputNumber = 0 
:nextInput foreach ($in in $allInput) {    
    if ($in.'$type') {
        $in | createRecord
    } else {
        Write-Warning "Input # $InputNumber is missing a '`$type'"
    }
    $InputNumber++
}
#endregion Create Records

$atConnection = $null