Class/Class.ps1

class Falcon {
    [string] $Hostname
    [string] $ClientId
    [string] $ClientSecret
    [string] $MemberCid
    [string] $Token
    [datetime] $Expires
    [hashtable] $Endpoints
    [hashtable] $ItemTypes
    [hashtable] $Parameters
    [hashtable] $Patterns
    [hashtable] $Schema
    [System.Globalization.TextInfo] $Culture
    Falcon ($Data) {
        # Ingest input data generated by Falcon.psm1 and determine culture for dynamic parameter naming
        $this.Culture = (Get-Culture).TextInfo
        $this.Endpoints = $Data.Endpoints
        $this.ItemTypes = $Data.ItemTypes
        $this.Parameters = $Data.Parameters
        $this.Patterns = $Data.Patterns
        $this.Schema = $Data.Schema
        $this.PSObject.TypeNames.Insert(0,'Falcon')
    }
    [string] GetAbsolutePath([string] $Path) {
        $Output = if (-not [IO.Path]::IsPathRooted($Path)) {
            # Convert relative paths to absolute paths and respect OS platform
            $AbsolutePath = Join-Path -Path (Get-Location).Path -ChildPath $Path
            $AbsolutePath = Join-Path -Path $AbsolutePath -ChildPath '.'
            [IO.Path]::GetFullPath($AbsolutePath)
        }
        else {
            $Path
        }
        return $Output
    }
    [hashtable] GetEndpoint([string] $Endpoint) {
        $Path = $Endpoint.Split(':')[0]
        $Method = $Endpoint.Split(':')[1]
        if (-not($Path -and $Method)) {
            throw "Invalid endpoint: '$Endpoint'"
        }
        # Gather endpoint properties and begin output construction
        $Ref = $this.Endpoints.$Path.$Method
        $Output = @{
            path = $Path
            method = $Method
        }
        $Ref.GetEnumerator().Where({ $_.Key -ne 'parameters' }).foreach{
            if ($_.Key -eq 'description') {
                # Update description using ItemType
                $Output[$_.Key] = $this.GetItemType($Endpoint, $_.Value)
            }
            else {
                # Add directly to output
                $Output[$_.Key] = $_.Value
            }
        }
        if ($Ref.parameters) {
            $Output['parameters'] = $this.GetParameters($Ref.parameters, $Endpoint)
        }
        return $Output
    }
    [string] GetItemType([string] $Endpoint, [string] $Value) {
        $Output = if ($Value -match '\{0\}') {
            $ItemType = $this.ItemTypes.Clone().GetEnumerator().Where({ $Endpoint -match $_.Key }).Value
            $Value -f $ItemType
        }
        else {
            $Value
        }
        return $Output
    }
    [hashtable] GetParameters([hashtable] $Object, [string] $Endpoint) {
        $Output = $Object.Clone()
        if ($Object.schema) {
            # Collect parameters from 'Schema'
            $Ref = $this.GetSchema($Object.schema).Clone()
            $RefParameters = @( $Ref.Keys )
            foreach ($Parameter in $RefParameters) {
                if ($Output.$Parameter) {
                    # Compare existing parameters against 'Schema'
                    $Properties = @( ($Ref.$Parameter).Keys )
                    foreach ($Property in $Properties) {
                        if ($Output.$Parameter.$Property) {
                            # Remove existing properties from 'Schema' definition
                            $Ref.$Parameter.Remove($Property)
                        }
                    }
                    # Merge parameter from 'Schema'
                    $Output.$Parameter += $Ref.$Parameter
                }
                else {
                    # Add parameter from 'Schema'
                    $Output[$Parameter] = $Ref.$Parameter
                }
            }
            # Remove 'Schema' name
            $Output.Remove('schema')
        }
        foreach ($Pair in $Object.GetEnumerator()) {
            if ($this.Parameters.($Pair.Key)) {
                # Gather shared parameter properties
                $Ref = $this.Parameters.($Pair.Key).Clone()
                $Output.($Pair.Key).GetEnumerator().foreach{
                    if ($Ref.($_.Key)) {
                        # Remove existing properties from 'Shared' definition
                        $Ref.Remove($_.Key)
                    }
                }
                # Merge parameter from 'Shared'
                $Output.($Pair.Key) += $Ref
            }
        }
        ($Output.GetEnumerator()).foreach{
            if (-not $_.Value.dynamic) {
                # Generate dynamic parameter name
                $Output.($_.Key)['dynamic'] = $this.Culture.ToTitleCase($_.Key) -replace '[^a-zA-Z0-9]',''
            }
            if (($_.Key -eq 'id') -or ($_.Key -eq 'ids')) {
                # Append default RegEx pattern by endpoint
                $Pattern = $this.GetPattern($Endpoint)
                if ($Pattern) {
                    $Output.($_.Key)['pattern'] = $this.GetPattern($Endpoint) #$Pattern
                }
            }
            # Update description with ItemType
            $Output.($_.Key)['description'] = $this.GetItemType($Endpoint, $_.Value.description)
            # Add ParameterSetName
            $Output.($_.Key)['set'] = $Endpoint
        }
        return $Output
    }
    [string] GetPattern([string] $Endpoint) {
        # Match Endpoint to Patterns
        return $this.Patterns.Clone().GetEnumerator().Where({ $Endpoint -match $_.Key }).Value
    }
    [string] GetResponse([string] $Endpoint, [int] $Code) {
        # Gather Endpoint.Responses and output response type
        $Path = $Endpoint.Split(':')[0]
        $Method = $Endpoint.Split(':')[1]
        if (-not($Path -and $Method)) {
            throw "Invalid endpoint: '$Endpoint'"
        }
        $Responses = $this.Endpoints.$Path.$Method.responses
        $Output = $Responses.GetEnumerator().Where({ $_.Value -contains $Code }).Key
        if ($Output) {
            return $Output
        }
        else {
            return $Responses.default
        }
    }
    [hashtable] GetSchema([string] $Schema) {
        # Gather schema and output all references as a single parameter set
        $Output = @{}
        $this.Schema.($Schema).GetEnumerator().foreach{
            if ($_.Value.schema) {
                $Parent = $_.Key
                $this.GetSchema($_.Value.schema).GetEnumerator().foreach{
                    $Output[$_.Key] = $_.Value
                    $Output.($_.Key)['parent'] = $Parent
                }
            }
            else {
                $Output[$_.Key] = $_.Value
            }
        }
        return $Output
    }
    [string] Rfc3339($Int) {
        # Convert number of hours into an RFC-3339 string
        return "$([Xml.XmlConvert]::ToString((Get-Date).AddHours($Int),[Xml.XmlDateTimeSerializationMode]::Utc))"
    }
}