schema.psm1

# .ExternalHelp schema.xml
class schemaString {
  [ValidateSet('string')]
  [string]$type = 'string'
  [string]$id
  [string]$ref
  [int]$minLength
  [int]$maxLength
  [string]$pattern
  [string[]]$enum
  [string]$title
  [string]$description
  [string]$default
  [string[]]$examples = @()

  #
  # Constructors
  #
  schemaString () {}
  schemaString (
    [int]$min,
    [int]$max
  ) {
    $this.minLength = $min
    $this.maxLength = $max
  }
  schemaString (
    [string]$reg
  ) {
    $this.pattern = $reg
  }

  #
  # Methods
  #
  [void]AddExample([string]$example) {
    $this.examples += $example
  }
  [void]AddEnum([string]$enum) {
    $this.enum += $enum
  }
  [object]ToString() {
    return ($this | Select-Object *, @{Name = '$id'; Exp = { $_.id } }, @{Name = '$ref'; Exp = { $_.ref } } -ExcludeProperty id, ref | ConvertTo-Json)
  }
}
class schemaInteger {
  [ValidateSet('integer')]
  [string]$type = 'integer'
  [string]$id
  [string]$ref
  [int]$minimum
  [int]$maximum
  [int]$exclusiveMinimum
  [int]$exclusiveMaximum
  [int]$multipleOf
  [int[]]$enum
  [string]$title
  [string]$description
  [int]$default
  [int[]]$examples = @()

  #
  # Constructors
  #
  schemaInteger () {}
  schemaInteger (
    [int]$min,
    [int]$max
  ) {
    $this.minimum = $min
    $this.maximum = $max
  }

  #
  # Methods
  #
  [void]AddExample([int]$example) {
    $this.examples += $example
  }
  [void]AddEnum([int]$enum) {
    $this.enum += $enum
  }
  [object]ToString() {
    return ($this | Select-Object *, @{Name = '$id'; Exp = { $_.id } }, @{Name = '$ref'; Exp = { $_.ref } } -ExcludeProperty id, ref | ConvertTo-Json)
  }
}
class schemaNumber {
  [ValidateSet('number')]
  [string]$type = 'number'
  [string]$id
  [string]$ref
  [decimal]$minimum
  [decimal]$maximum
  [decimal]$exclusiveMinimum
  [decimal]$exclusiveMaximum
  [decimal]$multipleOf
  [string]$title
  [string]$description
  [decimal]$default
  [decimal[]]$examples = @()

  #
  # Constructors
  #
  schemaNumber () {}
  schemaNumber (
    [decimal]$min,
    [decimal]$max
  ) {
    $this.minimum = $min
    $this.maximum = $max
  }

  #
  # Methods
  #
  [void]AddExample([decimal]$example) {
    $this.examples += $example
  }
  [object]ToString() {
    return ($this | Select-Object *, @{Name = '$id'; Exp = { $_.id } }, @{Name = '$ref'; Exp = { $_.ref } } -ExcludeProperty id, ref | ConvertTo-Json)
  }
}
class schemaBoolean {
  [ValidateSet('boolean')]
  [string]$type = 'boolean'
  [string]$id
  [string]$title
  [string]$description
  [bool]$default
  [bool[]]$enum
  #
  # Methods
  #
  [object]ToString() {
    return ($this | Select-Object *, @{Name = '$id'; Exp = { $_.id } }, @{Name = '$ref'; Exp = { $_.ref } } -ExcludeProperty id, ref | ConvertTo-Json)
  }
}
class schemaObject {
  [ValidateSet('object')]
  [string]$type = 'object'
  [string]$id
  [object]$properties
  [string[]]$required
  [bool]$additionalProperties = $false
  [string]$title
  [string]$description
  [object]$default
  [object[]]$enum

  #
  # Constructors
  #

  #
  # Methods
  #
  [object]AddProperty([object]$property) {
    $this.properties += $property
    return $this
  }
  [object]Find([string]$item) {
    return (Find-SchemaElement -Schema $this -ElementName $item)
  }
  [object]ToString() {
    return ($this | Select-Object *, @{Name = '$id'; Exp = { $_.id } } -ExcludeProperty id | ConvertTo-Json)
  }
  [object]ToObject() {
    return (ConvertFrom-SchemaObject -Object $this)
  }
  [object]ToObject([int]$Depth) {
    return (ConvertFrom-SchemaObject -Object $this -depth $Depth)
  }
  [object]ToObject([string]$PropertyName) {
    [object]$retVal = New-Object object;
    if ($this.properties.Contains($PropertyName)) {
      Add-Member -InputObject $retVal -MemberType NoteProperty -Name $PropertyName -Value (ConvertFrom-SchemaObject -Object ($this.GetProperty($PropertyName)))
    }
    else {
      throw "$($PropertyName) not found in collection $($this.properties.keys |Out-String)"
    }
    return $retVal
  }
  [object]ToObject([string]$PropertyName, [int]$Depth) {
    [object]$retVal = New-Object object;
    if ($this.properties.Contains($PropertyName)) {
      Add-Member -InputObject $retVal -MemberType NoteProperty -Name $PropertyName -Value (ConvertFrom-SchemaObject -Object ($this.GetProperty($PropertyName)) -depth $Depth)
    }
    else {
      throw "$($PropertyName) not found in collection $($this.properties.keys |Out-String)"
    }
    return $retVal
  }
  [object]GetProperty([string]$PropertyName) {
    if ($this.properties.Contains($PropertyName)) {
      return ($this.properties.$PropertyName)
    }
    else {
      throw "$($PropertyName) not found in collection $($this.properties.keys |Out-String)"
    }
  }
}
class schemaArray {
  [ValidateSet('array')]
  [string]$type = 'array'
  [string]$id
  [object]$items
  [bool]$additionalItems = $false
  [string]$title
  [string]$description
  [array]$default

  #
  # Methods
  #
  [object]AddItem([object]$item) {
    $this.items += $item
    return $this
  }
  [object]Find([string]$item) {
    return (Find-SchemaElement -Schema $this -ElementName $item)
  }
  [object]ToString() {
    return ($this | Select-Object *, @{Name = '$id'; Exp = { $_.id } } -ExcludeProperty id | ConvertTo-Json)
  }
  [array]ToArray() {
    return (ConvertFrom-SchemaArray -Array $this)
  }
  [array]ToArray([int]$Depth) {
    return (ConvertFrom-SchemaArray -Array $this -depth $Depth)
  }
  [object]ToObject() {
    return (ConvertFrom-SchemaArray -Array $this)[0]
  }
  [object]ToObject([int]$Depth) {
    write-verbose $this |out-String
    return (ConvertFrom-SchemaArray -Array $this -depth $Depth)[0]
  }
}
class schemaDocument {
  [ValidateSet('object')]
  [string]$type = 'object'
  [ValidateSet('http://json-schema.org/draft-04/schema#', 'http://json-schema.org/draft-06/schema#', 'http://json-schema.org/draft-07/schema#')]
  [string]$schema = 'http://json-schema.org/draft-04/schema#'
  [object]$definitions
  [string]$id
  [object]$properties
  [string[]]$required
  [bool]$additionalProperties = $false
  [string]$title
  [string]$description
  [object]$default

  #
  # Constructors
  #

  #
  # Methods
  #
  [object]AddProperty([object]$property) {
    $this.properties += $property
    return $this
  }
  [object]Find([string]$item) {
    return (Find-SchemaElement -Schema $this -ElementName $item)
  }
  [object]Find([string]$item, [boolean]$IsDefinition) {
    return (Find-SchemaElement -Schema $this -ElementName $item -Isdefinition:$IsDefinition)
  }
  [object]ToString() {
    return ($this | Select-Object *, @{Name = '$id'; Exp = { $_.id } }, @{Name = '$schema'; Exp = { $_.schema } }, @{Name = '$definitions'; Exp = { $_.definitions } } -ExcludeProperty id, schema, definitions | ConvertTo-Json)
  }
  [object]ToObject() {
    return (ConvertFrom-SchemaObject -Object $this)
  }
  [object]ToObject([int]$Depth) {
    return (ConvertFrom-SchemaObject -Object $this -depth $Depth)
  }
  [object]ToObject([string]$PropertyName) {
    [object]$retVal = New-Object object;
    if ($this.properties.Contains($PropertyName)) {
      Add-Member -InputObject $retVal -MemberType NoteProperty -Name $PropertyName -Value (ConvertFrom-SchemaObject -Object ($this.GetProperty($PropertyName)))
    }
    else {
      throw "$($PropertyName) not found in collection $($this.properties.keys |Out-String)"
    }
    return $retVal
  }
  [object]ToObject([string]$PropertyName, [int]$Depth) {
    [object]$retVal = New-Object object;
    if ($this.properties.Contains($PropertyName)) {
      Add-Member -InputObject $retVal -MemberType NoteProperty -Name $PropertyName -Value (ConvertFrom-SchemaObject -Object ($this.GetProperty($PropertyName)) -depth $Depth)
    }
    else {
      throw "$($PropertyName) not found in collection $($this.properties.keys |Out-String)"
    }
    return $retVal
  }
  [object]GetProperty([string]$PropertyName) {
    if ($this.properties.Contains($PropertyName)) {
      return ($this.properties.$PropertyName)
    }
    else {
      throw "$($PropertyName) not found in collection $($this.properties.keys |Out-String)"
    }
  }
}
class schemaDefinition {
  [ValidateSet('object')]
  [string]$type = 'object'
  [object]$properties
  [string[]]$required

  schemaDefinition () {}
}
$Global:RawSchema = $null;
function Get-Document {
  [CmdletBinding(
    HelpURI = 'https://github.com/SchemaModule/PowerShell/blob/master/docs/Get-SchemaDocument.md#get-schemadocument',
    PositionalBinding = $true)]
  [OutputType([schemaDocument])]
  param (
    [Parameter(ValueFromPipeline)]
    [string]$Path,
    [hashtable]$Headers
  )

  process {
    Write-Verbose $Path;
    try {
      Write-Verbose "Clearing Error Variables";
      $ErrorActionPreference = 'Stop';
      $Error.Clear();

      Write-Verbose "Parsing Path param";
      $Schema = [System.UriBuilder]::new($Path);
      Write-Verbose $Schema;

      switch ($Schema.Scheme) {
        'file' {
          Write-Verbose "Incoming Filepath";
          $Global:RawSchema = (Get-Content -Path $Path | ConvertFrom-Json);

        }
        'https' {
          Write-Verbose "Incoming HTTPs path";
          if ($Headers){
            $Global:RawSchema = (Invoke-WebRequest -UseBasicParsing -Uri $Path -Headers $Headers | ConvertFrom-Json | ConvertFrom-Json);
          } else {
            $Global:RawSchema = (Invoke-WebRequest -UseBasicParsing -Uri $Path | ConvertFrom-Json);
          }
        }
        'http' {
          Write-Verbose "Incoming HTTP path";
          if ($Headers){
            $Global:RawSchema = (Invoke-WebRequest -UseBasicParsing -Uri $Path -Headers $Headers | ConvertFrom-Json | ConvertFrom-Json);
          } else {
            $Global:RawSchema = (Invoke-WebRequest -UseBasicParsing -Uri $Path | ConvertFrom-Json);
          }
        }
      }
      write-verbose "converting"
      Return (ConvertTo-SchemaElement -object $Global:RawSchema -IsRootSchema);
    }
    catch {
      throw $_;
    }
  }
}
function Get-Definition {
  [CmdletBinding(
    HelpURI = 'https://github.com/SchemaModule/PowerShell/blob/master/docs/Get-SchemaDefinition.md#get-schemadefinition',
    PositionalBinding = $true)]
  [OutputType([schemaDocument], [schemaString], [schemaInteger], [schemaNumber], [schemaBoolean], [schemaObject], [schemaArray])]
  param (
    [Parameter(ValueFromPipeline)]
    [string]$Reference
  )
  process {
    Write-Verbose "Reference $($Reference)"
    if ($Reference.Contains('#')) {
      Write-Verbose "Path to definitions"
      $DefinitionName = $Reference.Split('/')[2]
      $Definition = $Global:RawSchema.definitions.($DefinitionName)
      return (ConvertTo-SchemaElement -object ($Definition))
    } elseif ($Reference.Contains('http')) {
      write-verbose "Uri to definitions"
      $Reference = [System.Uri]::new($Reference);
      $DefinitionName = $Reference.Fragment.Substring($Reference.Fragment.LastIndexOf('/') + 1, ($Reference.Fragment.Length - $Reference.Fragment.LastIndexOf('/')) - 1)
      $Definition = Get-SchemaDocument -Path $Reference.AbsoluteUri
      return (ConvertTo-SchemaElement -object ($Definition.definitions.($DefinitionName)))
    } else {
      write-verbose "Named definition"
      $DefinitionName = $Reference;
      $Definition = $Global:RawSchema.definitions.($DefinitionName)
      return (ConvertTo-SchemaElement -object ($Definition))
    }
  }
}
function ConvertTo-Definition {
  [CmdletBinding(
    HelpURI = 'https://github.com/SchemaModule/PowerShell/blob/master/docs/ConvertTo-SchemaDefinition.md#convertto-schemadefinition',
    PositionalBinding = $true)]
  [OutputType([string],[schemaDefinition])]
  param (
    [Parameter(ValueFromPipeline)]
    [object]$Definition,
    [switch]$AsJson
  )
  process {
    Write-Verbose "Creating New Definition";
    $NewDefinition = New-SchemaElement -Type definition;
    $NewDefinition.required = $Definition.required;
    $NewDefinition.properties = $Definition.properties;
    foreach ($defProperty in $Definition.properties.psobject.properties.name) {
      write-verbose "Definition Property : $($defProperty)"
      if ($NewDefinition.properties.($defProperty).type) {
        write-verbose "Found type property"
        $NewDefinition.properties.($defProperty).type = $Definition.properties.($defProperty).type.split(' ')[0]
      }
    }
    if ($AsJson) {
      return ($NewDefinition |ConvertTo-Json -Depth 99)
    } else {
      return $NewDefinition
    }
  }
}
function Get-Reference {
  [CmdletBinding(
    HelpURI = 'https://github.com/SchemaModule/PowerShell/blob/master/docs/Get-SchemaReference.md#get-schemareference',
    PositionalBinding = $true)]
  [OutputType([schemaDocument], [schemaString], [schemaInteger], [schemaNumber], [schemaBoolean], [schemaObject], [schemaArray])]
  param (
    [Parameter(ValueFromPipeline)]
    [System.Uri]$Reference
  )
  process {
    return (Get-SchemaDocument -Path $Reference.AbsoluteUri)
  }
}
function New-String {
  [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low',
    HelpURI = 'https://github.com/SchemaModule/PowerShell/blob/master/docs/New-SchemaString.md#new-schemastring',
    PositionalBinding = $true)]
  [OutputType([schemaString])]
  param (
    [parameter(Mandatory = $false, ParameterSetName = 'string')]
    [string]$id,
    [parameter(Mandatory = $false, ParameterSetName = 'string')]
    [string]$ref,
    [parameter(Mandatory = $false, ParameterSetName = 'string')]
    [int]$minLength,
    [parameter(Mandatory = $false, ParameterSetName = 'string')]
    [int]$maxLength,
    [parameter(Mandatory = $false, ParameterSetName = 'string')]
    [string]$pattern,
    [parameter(Mandatory = $false, ParameterSetName = 'string')]
    [string[]]$enum,
    [parameter(Mandatory = $false, ParameterSetName = 'string')]
    [string]$title,
    [parameter(Mandatory = $false, ParameterSetName = 'string')]
    [string]$description,
    [parameter(Mandatory = $false, ParameterSetName = 'string')]
    [string]$default,
    [parameter(Mandatory = $false, ParameterSetName = 'string')]
    [string[]]$examples = @()
  )
  if ($PSCmdlet.ShouldProcess("NewString")) {
    Write-Verbose "Creating schemaString object"
    $schemaString = New-SchemaElement -Type string
    Write-Verbose ($schemaString.GetType())

    foreach ($param in $PSBoundParameters.GetEnumerator()) {
      switch ($param) {
        { ($param.Key -eq 'Verbose' -or $param.Key -eq 'Debug' -or $param.Key -eq 'ErrorAction' -or $param.Key -eq 'WarningAction' -or $param.Key -eq 'InformationAction' -or $param.Key -eq 'ErrorVariable' -or $param.Key -eq 'WarningVariable' -or $param.Key -eq 'InformationVariable' -or $param.Key -eq 'OutVariable' -or $param.Key -eq 'OutBuffer' -or $param.Key -eq 'PipelineVariable') } {}
        default {
          Write-Verbose "Setting $($param.Value) on $($param.Key)"
          $schemaString.($param.Key) = $param.Value
        }
      }
    }

    return $schemaString
  }
}
function New-Integer {
  [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low',
    HelpURI = 'https://github.com/SchemaModule/PowerShell/blob/master/docs/New-SchemaInteger.md#new-schemainteger',
    PositionalBinding = $true)]
  [OutputType([schemaInteger])]
  param (
    [parameter(Mandatory = $false, ParameterSetName = 'integer')]
    [string]$id,
    [parameter(Mandatory = $false, ParameterSetName = 'integer')]
    [string]$ref,
    [parameter(Mandatory = $false, ParameterSetName = 'integer')]
    [int]$minimum,
    [parameter(Mandatory = $false, ParameterSetName = 'integer')]
    [int]$maximum,
    [parameter(Mandatory = $false, ParameterSetName = 'integer')]
    [int]$exclusiveMinimum,
    [parameter(Mandatory = $false, ParameterSetName = 'integer')]
    [int]$exclusiveMaximum,
    [parameter(Mandatory = $false, ParameterSetName = 'integer')]
    [int]$multipleOf,
    [parameter(Mandatory = $false, ParameterSetName = 'integer')]
    [int[]]$enum,
    [parameter(Mandatory = $false, ParameterSetName = 'integer')]
    [string]$title,
    [parameter(Mandatory = $false, ParameterSetName = 'integer')]
    [string]$description,
    [parameter(Mandatory = $false, ParameterSetName = 'integer')]
    [int]$default,
    [parameter(Mandatory = $false, ParameterSetName = 'integer')]
    [int[]]$examples = @()
  )
  if ($PSCmdlet.ShouldProcess("NewInteger")) {
    Write-Verbose "Creating schemaInteger object"
    $schemaInteger = New-SchemaElement -Type integer

    foreach ($param in $PSBoundParameters.GetEnumerator()) {
      switch ($param) {
        { ($param.Key -eq 'Verbose' -or $param.Key -eq 'Debug' -or $param.Key -eq 'ErrorAction' -or $param.Key -eq 'WarningAction' -or $param.Key -eq 'InformationAction' -or $param.Key -eq 'ErrorVariable' -or $param.Key -eq 'WarningVariable' -or $param.Key -eq 'InformationVariable' -or $param.Key -eq 'OutVariable' -or $param.Key -eq 'OutBuffer' -or $param.Key -eq 'PipelineVariable') } {}
        default {
          Write-Verbose "Setting $($param.Value) on $($param.Key)"
          $schemaInteger.($param.Key) = $param.Value
        }
      }
    }

    return $schemaInteger
  }
}
function New-Number {
  [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low',
    HelpURI = 'https://github.com/SchemaModule/PowerShell/blob/master/docs/New-SchemaNumber.md#new-schemanumber',
    PositionalBinding = $true)]
  [OutputType([schemaNumber])]
  param (
    [parameter(Mandatory = $false, ParameterSetName = 'number')]
    [string]$id,
    [parameter(Mandatory = $false, ParameterSetName = 'number')]
    [string]$ref,
    [parameter(Mandatory = $false, ParameterSetName = 'number')]
    [decimal]$minimum,
    [parameter(Mandatory = $false, ParameterSetName = 'number')]
    [decimal]$maximum,
    [parameter(Mandatory = $false, ParameterSetName = 'number')]
    [decimal]$exclusiveMinimum,
    [parameter(Mandatory = $false, ParameterSetName = 'number')]
    [decimal]$exclusiveMaximum,
    [parameter(Mandatory = $false, ParameterSetName = 'number')]
    [decimal]$multipleOf,
    [parameter(Mandatory = $false, ParameterSetName = 'number')]
    [string]$title,
    [parameter(Mandatory = $false, ParameterSetName = 'number')]
    [string]$description,
    [parameter(Mandatory = $false, ParameterSetName = 'number')]
    [decimal]$default,
    [parameter(Mandatory = $false, ParameterSetName = 'number')]
    [decimal[]]$examples = @()
  )
  if ($PSCmdlet.ShouldProcess("NewNumber")) {
    Write-Verbose "Creating schemaNumber object"
    $schemaNumber = New-SchemaElement -Type number

    foreach ($param in $PSBoundParameters.GetEnumerator()) {
      switch ($param) {
        { ($param.Key -eq 'Verbose' -or $param.Key -eq 'Debug' -or $param.Key -eq 'ErrorAction' -or $param.Key -eq 'WarningAction' -or $param.Key -eq 'InformationAction' -or $param.Key -eq 'ErrorVariable' -or $param.Key -eq 'WarningVariable' -or $param.Key -eq 'InformationVariable' -or $param.Key -eq 'OutVariable' -or $param.Key -eq 'OutBuffer' -or $param.Key -eq 'PipelineVariable') } {}
        default {
          Write-Verbose "Setting $($param.Value) on $($param.Key)"
          $schemaNumber.($param.Key) = $param.Value
        }
      }
    }

    return $schemaNumber
  }
}
function New-Boolean {
  [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low',
    HelpURI = 'https://github.com/SchemaModule/PowerShell/blob/master/docs/New-SchemaBoolean.md#new-schemaboolean',
    PositionalBinding = $true)]
  [OutputType([schemaBoolean])]
  param (
    [parameter(Mandatory = $false, ParameterSetName = 'boolean')]
    [string]$id,
    [parameter(Mandatory = $false, ParameterSetName = 'boolean')]
    [string]$ref,
    [parameter(Mandatory = $false, ParameterSetName = 'boolean')]
    [string]$title,
    [parameter(Mandatory = $false, ParameterSetName = 'boolean')]
    [string]$description,
    [parameter(Mandatory = $false, ParameterSetName = 'boolean')]
    [bool]$default
  )
  if ($PSCmdlet.ShouldProcess("NewBoolean")) {
    Write-Verbose "Creating schemaBoolean object"
    $schemaBoolean = New-SchemaElement -Type boolean

    foreach ($param in $PSBoundParameters.GetEnumerator()) {
      switch ($param) {
        { ($param.Key -eq 'Verbose' -or $param.Key -eq 'Debug' -or $param.Key -eq 'ErrorAction' -or $param.Key -eq 'WarningAction' -or $param.Key -eq 'InformationAction' -or $param.Key -eq 'ErrorVariable' -or $param.Key -eq 'WarningVariable' -or $param.Key -eq 'InformationVariable' -or $param.Key -eq 'OutVariable' -or $param.Key -eq 'OutBuffer' -or $param.Key -eq 'PipelineVariable') } {}
        default {
          Write-Verbose "Setting $($param.Value) on $($param.Key)"
          $schemaBoolean.($param.Key) = $param.Value
        }
      }
    }

    return $schemaBoolean
  }
}
function New-Property {
  [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low',
    HelpURI = 'https://github.com/SchemaModule/PowerShell/blob/master/docs/New-SchemaProperty.md#new-schemaproperty',
    PositionalBinding = $true)]
  [OutputType([hashtable])]
  param (
    [ValidateNotNullOrEmpty()]
    [string]$Name,

    [ValidateSet([schemaDocument], [schemaNumber], [schemaInteger], [schemaString], [schemaObject], [schemaArray], [schemaBoolean],[schemaDefinition])]
    $Value,

    $Array
  )
  if ($PSCmdlet.ShouldProcess("NewProperty")) {
    if ($Array) {
      $Property = (New-Object -TypeName psobject -Property @{$Array = @($value) })
    }
    else {
      $Property = (New-Object -TypeName psobject @{$Name = $value })
    }
    return $Property
  }
}
function New-Element {
  [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low',
    HelpURI = 'https://github.com/SchemaModule/PowerShell/blob/master/docs/New-SchemaElement.md#new-schemaelement',
    PositionalBinding = $true)]
  [OutputType([schemaDocument], [schemaString], [schemaInteger], [schemaNumber], [schemaBoolean], [schemaObject], [schemaArray])]
  param (
    [ValidateSet('string', 'number', 'integer', 'object', 'boolean', 'array', 'document', 'definition')]
    [string]$Type
  )
  if ($PSCmdlet.ShouldProcess("NewElement")) {
    write-verbose "Creating new schema$($Type)"
    switch ($Type) {
      'string' {
        [schemaString]::new()
      }
      'number' {
        [schemaNumber]::new()
      }
      'integer' {
        [schemaInteger]::new()
      }
      'object' {
        [schemaObject]::new()
      }
      'boolean' {
        [schemaBoolean]::new()
      }
      'array' {
        [schemaArray]::new()
      }
      'document' {
        [schemaDocument]::new()
      }
      'definition' {
        [schemaDefinition]::new()
      }
    }
  }
}
function Find-Element {
  [CmdletBinding(
    HelpURI = 'https://github.com/SchemaModule/PowerShell/blob/master/docs/Find-SchemaElement.md#find-schemaelement',
    PositionalBinding = $true)]
  [OutputType([schemaDocument], [schemaString], [schemaInteger], [schemaNumber], [schemaBoolean], [schemaObject], [schemaArray])]
  param (
    [parameter(Mandatory = $true, ParameterSetName = 'name')]
    [parameter(Mandatory = $true, ParameterSetName = 'type')]
    [parameter(Mandatory = $true, ParameterSetName = 'path')]
    [parameter(Mandatory = $true, ParameterSetName = 'definitions')]
    $Schema,
    [parameter(Mandatory = $false, ParameterSetName = 'name')]
    [parameter(Mandatory = $false, ParameterSetName = 'definitions')]
    [string]$ElementName,
    [parameter(Mandatory = $false, ParameterSetName = 'type')]
    [ValidateSet('schemaString', 'schemaNumber', 'schemaInteger', 'schemaObject', 'schemaBoolean', 'schemaArray', 'schemaDocument')]
    [string]$ElementType,
    [parameter(Mandatory = $false, ParameterSetName = 'path')]
    [string]$ElementPath,
    [parameter(Mandatory = $true, ParameterSetName = 'definitions')]
    [switch]$IsDefinition
  )
  switch ($PSCmdlet.ParameterSetName) {
    'name' {
      switch ($Schema.type) {
        'object' {
          Write-Verbose "object"
          if ($Schema.properties.keys -contains $ElementName) {
            return $Schema.properties.$ElementName
          }
          else {
            $keys = $Schema.properties.keys
            foreach ($key in $keys) {
              write-verbose $key
              Find-SchemaElement -Schema ($Schema.properties.$key) -ElementName $ElementName
            }
          }
        }
        'array' {
          write-verbose "array"
          #[array]$keys = $Schema.items.properties.keys|ForEach-Object {$_.ToLower()}
          [array]$keys = $Schema.items.psobject.Properties.Name.ToLower()
          if ($keys.Contains('oneof')) { $Selector = "OneOf" }
          if ($keys.Contains('allof')) { $Selector = "AllOf" }
          if ($keys.Contains('anyof')) { $Selector = "AnyOf" }
          if ($Selector){
            write-verbose "Found Selector : $($Selector)"
            if ($Schema.items.$Selector.properties.keys -contains $ElementName) {
              return $Schema.items.$Selector.properties.$ElementName
            }
            else {
              $keys = $Schema.items.$Selector.properties.keys
              foreach ($key in $keys) {
                write-verbose $key
                Find-SchemaElement -Schema ($Schema.items.$Selector.properties.$key) -ElementName $ElementName
              }
            }
          } else {
            write-verbose "No Selector"
            if ($Schema.items.properties.keys -contains $ElementName) {
              return $Schema.items.properties.$ElementName
            }
            else {
              $keys = $Schema.items.properties.keys
              foreach ($key in $keys) {
                write-verbose $key
                Find-SchemaElement -Schema ($Schema.items.properties.$key) -ElementName $ElementName
              }
            }
          }
        }
      }
    }
    'type' {
      if ($schema.GetType().Name -eq $ElementType) {
        return $Schema
      }
      else {
        switch ($Schema.type) {
          'object' {
            $keys = $Schema.properties.keys
            foreach ($key in $keys) {
              Find-SchemaElement -Schema ($Schema.properties.$key) -ElementType $ElementType
            }
          }
          'array' {
            $keys = $Schema.items.anyOf.properties.keys
            foreach ($key in $keys) {
              write-verbose $key
              Find-SchemaElement -Schema ($Schema.items.anyOf.properties.$key) -ElementType $ElementType
            }
          }
        }
      }
    }
    'path' {
      write-verbose "Incoming from parameter $($ElementPath)"
      [System.Collections.ArrayList]$items = $ElementPath.Split("/")
      if ($items[0] -eq "") { $items.RemoveAt(0) }
      write-verbose "Items in collection $($Items.count)"
      write-verbose "Resource $($Items[0])"
      $NewPath = ([string]::Join("/", $items[1..$items.Count]))
      write-verbose "Outgoing to parameter $($NewPath)"
      if ([string]::IsNullOrEmpty($NewPath)) {
        switch ($schema.type) {
          'object' {
            return $Schema.properties.($items[0])
          }
          'array' {
            return $Schema.items.anyOf.properties.($items[0])
          }
        }
      }
      write-verbose "Working on $($Schema.type)"
      switch ($Schema.type) {
        'object' {
          Find-SchemaElement -Schema ($Schema.properties.($items[0])) -ElementPath $NewPath
        }
        'array' {
          Find-SchemaElement -Schema ($Schema.items.anyof.properties.($items[0])) -ElementPath $NewPath
        }
      }
    }
    'definitions' {
      Write-Verbose "Definition Search"
      if ($schema.GetType().Name -eq 'schemaDocument') {
        if ($schema.definitions.psobject.properties.name.Contains($ElementName)) {
          return $Schema.definitions.$ElementName
        }
        else {
          $keys = $schema.definitions.psobject.properties.name
          foreach ($key in $keys) {
            write-verbose $key
            Find-SchemaElement -Schema ($Schema) -ElementName $key -IsDefinition
          }
        }
      }
    }
  }
}
function ConvertTo-Element {
  [CmdletBinding(
    HelpURI = 'https://github.com/SchemaModule/PowerShell/blob/master/docs/ConvertTo-SchemaElement.md#convertto-schemaelement',
    PositionalBinding = $true)]
  [OutputType([schemaDocument], [schemaString], [schemaInteger], [schemaNumber], [schemaBoolean], [schemaObject], [schemaArray])]
  param (
    [parameter(Mandatory = $true, Position = 0)]
    [object]$Object,
    [switch]$IsRootSchema
  )
  write-verbose ($Object | Out-String)
  switch ($Object.type) {
    'string' {
      write-verbose "Creating schemaString object"
      $Result = New-SchemaElement -Type string
      foreach ($prop in $Object.psobject.properties.name) {
        write-verbose $prop
        if ($prop -eq '$id') {
          $Result.id = $Object.$prop
        }
        else {
          if ($Object.$prop.GetType().IsArray)
          {
            $Result.$prop = $Object.$prop[0]
          } else {
            $Result.$prop = $Object.$prop
          }
        }
        write-verbose ($Result | out-String)
      }
    }
    'integer' {
      write-verbose "Creating schemaInteger object"
      $Result = New-SchemaElement -Type integer
      foreach ($prop in $Object.psobject.properties.name) {
        if ($prop -eq '$id') {
          $Result.id = $Object.$prop
        }
        else {
          if ($Object.$prop.GetType().IsArray)
          {
            $Result.$prop = $Object.$prop[0]
          } else {
            $Result.$prop = $Object.$prop
          }
        }
      }
    }
    'number' {
      write-verbose "Creating schemaNumber object"
      $Result = New-SchemaElement -Type number
      foreach ($prop in $Object.psobject.properties.name) {
        if ($prop -eq '$id') {
          $Result.id = $Object.$prop
        }
        else {
          if ($Object.$prop.GetType().IsArray)
          {
            $Result.$prop = $Object.$prop[0]
          } else {
            $Result.$prop = $Object.$prop
          }
        }
      }
    }
    'boolean' {
      write-verbose "Creating schemaBoolean object"
      $Result = New-SchemaElement -Type boolean
      foreach ($prop in $Object.psobject.properties.name) {
        if ($prop -eq '$id') {
          $Result.id = $Object.$prop
        }
        else {
          if ($Object.$prop.GetType().IsArray)
          {
            $Result.$prop = $Object.$prop[0]
          } else {
            $Result.$prop = $Object.$prop
          }
        }
      }
    }
    'object' {
      if ($Object.psobject.properties.name.Contains('$schema') -or $IsRootSchema) {
        write-verbose "Creating schemaDcoument object"
        $Result = New-SchemaElement -Type document
      }
      else {
        write-verbose "Creating schemaObject object"
        $Result = New-SchemaElement -Type object
      }
      foreach ($prop in $Object.psobject.properties.name) {
        if ($prop -eq '$id') {
          Write-Verbose $Object.$prop
          $Result.id = $Object.$prop
        }
        elseif ($prop -eq 'properties') {
          foreach ($oprop in $Object.properties.psobject.properties.name) {
            write-verbose $oprop
            $Result.properties += (( New-SchemaProperty -Name $oprop -Value ( ConvertTo-SchemaElement -object ($Object.properties.($oprop)) ) ))
          }
        }
        elseif ($prop -eq '$schema') {
          write-verbose "Found `$schema property"
          $Result.schema = $Object.$prop
        }
        elseif ($prop -eq 'definitions') {
          write-verbose "Found Definitions"
          write-verbose $object.$prop;
          foreach ($definition in $object.$prop.psobject.properties) {
            write-verbose "Definition Name : $($Definition.Name)"
            $Result.definitions += ((New-SchemaProperty -Name $definition.Name -Value ($definition.Value |ConvertTo-SchemaDefinition)))
          }
        }
        else {
          if ($Object.$prop.GetType().IsArray)
          {
            $Result.$prop = $Object.$prop[0]
          } else {
            $Result.$prop = $Object.$prop
          }
        }
      }
      $Result.required = $Object.required;
    }
    'array' {
      write-verbose "Creating schemaArray object"
      $Result = New-SchemaElement -Type array
      foreach ($prop in $Object.psobject.properties.name) {
        if ($prop -eq '$id') {
          Write-Verbose $Object.$prop
          $Result.id = $Object.$prop
        }
        elseif ($prop -eq 'items') {
          # if ($Object.items.psobject.properties.name.contains('properties')) {
          # Write-Verbose "Found invalid nested object"
          # $Result.items += (New-SchemaProperty -Array oneOf -Value (ConvertTo-SchemaElement -object $Object.items))
          # }
          # else {
            foreach ($oprop in $Object.items.psobject.properties.name) {
              if (!($oprop -eq '$id')) {
                write-verbose ($Object.items.psobject.properties.name | out-string)
                Write-Verbose "Found valid array object"
                Write-Verbose $oprop
                if ($oprop -eq '$ref') {
                  if ($Object.items.($oprop).contains('definitions')) {
                    $Result.items += Get-SchemaDefinition -Reference $Object.items.($oprop)
                  }
                  elseif ($Object.items.($oprop).contains('http')) {
                    $Result.items += Get-SchemaReference -Reference $Object.items.($oprop)
                  }
                  else {
                    $Result.items += Get-SchemaDefinition -Reference $Object.items.($oprop)
                  }
                } else {
                  $Result.items += ($Object.items.$oprop.GetEnumerator() | ForEach-Object { ((New-SchemaProperty -Name $oprop -Value (ConvertTo-SchemaElement -object $_) -Array $oprop)) })
                }
              }
            }
          #}
        }
        else {
          if ($Object.$prop.GetType().IsArray)
          {
            $Result.$prop = $Object.$prop[0]
          } else {
            $Result.$prop = $Object.$prop
          }
        }
      }
    }
    default {
      if ($Object.('$ref')) {
        write-verbose "ConvertTo-Element : Reference : $(($Object.('$ref'))|Out-String)"
        if ($Object.('$ref').contains('definitions')) {
          $Result = Get-SchemaDefinition -Reference $Object.('$ref')
        }
        elseif ($Object.('$ref').contains('http')) {
          $Result = Get-SchemaReference -Reference $Object.('$ref')
        }
        else {
          $Result = Get-SchemaDefinition -Reference $Object.('$ref')
        }
      }
    }
  }
  return $Result
}
function ConvertFrom-Object {
  [CmdletBinding(
    HelpURI = 'https://github.com/SchemaModule/PowerShell/blob/master/docs/ConvertFrom-SchemaObject.md#convertfrom-schemaobject',
    PositionalBinding = $true)]
  [OutputType([object])]
  param (
    [parameter(Mandatory = $true, Position = 0)]
    $Object,
    [parameter(Mandatory = $false, Position = 1)]
    [int]$Depth
  )
  if ($Object.type -eq 'object') {
    [object]$retVal = New-Object object;
    if ($Depth -ne 1) {
      Write-Verbose "ConvertFrom-Object: Incoming: $($Depth)"
      $Depth = if ($Depth -gt 1) { $Depth - 1 } else { $Depth }
      Write-Verbose "ConvertFrom-Object: Calculated: $($Depth)"
      if ($Object.schema) {
        Write-Verbose "ConvertFrom-Object: Found: $($Object.schema)"
        Add-Member -InputObject $retVal -MemberType NoteProperty -Name '$schema' -Value $Object.schema;
        if ($Object.id) {
          Write-Verbose "ConvertFrom-Object: Found: $($Object.id)"
          Add-Member -InputObject $retVal -MemberType NoteProperty -Name '$id' -Value $Object.id -force;
        }
      }
      foreach ($item in $Object.properties.keys) {
        Write-Verbose "ConvertFrom-Object: Found: $($item)"
        switch ($Object.properties.$item.type) {
          'object' {
            Add-Member -InputObject $retVal -MemberType NoteProperty -Name $item -Value (ConvertFrom-SchemaObject -Object $Object.properties.$item -depth $Depth)
          }
          'array' {
            Add-Member -InputObject $retVal -MemberType NoteProperty -Name $item -Value @(ConvertFrom-SchemaArray -Array $Object.properties.$item -depth $Depth)
          }
          'string' {
            Add-Member -InputObject $retVal -MemberType NoteProperty -Name $item -Value $Object.properties.$item.default
          }
          'number' {
            Add-Member -InputObject $retVal -MemberType NoteProperty -Name $item -Value $Object.properties.$item.default
          }
          'integer' {
            Add-Member -InputObject $retVal -MemberType NoteProperty -Name $item -Value $Object.properties.$item.default
          }
          'boolean' {
            Add-Member -InputObject $retVal -MemberType NoteProperty -Name $item -Value $Object.properties.$item.default
          }
          default {
            Add-Member -InputObject $retVal -MemberType NoteProperty -Name $item -Value $Object.properties.$item.default
          }
        }
      }
    }
    return $retVal
  }
}
function ConvertFrom-Array {
  [CmdletBinding(
    HelpURI = 'https://github.com/SchemaModule/PowerShell/blob/master/docs/ConvertFrom-SchemaArray.md#convertfrom-schemaarray',
    PositionalBinding = $true)]
  [OutputType([System.Object[]])]
  param (
    [parameter(Mandatory = $true, Position = 0)]
    $Array,
    [parameter(Mandatory = $false, Position = 1)]
    [int]$Depth
  )
  if ($Array.type -eq 'array') {
    [array]$retVal = @();
    if ($Depth -ne 1) {
      Write-Verbose "ConvertFrom-Array: Incoming: $($Depth)"
      $Depth = if ($Depth -gt 1) { $Depth - 1 } else { $Depth }
      Write-Verbose "ConvertFrom-Array: Calculated: $($Depth)"
      foreach ($item in $Array.items) {
        Write-Verbose "ConvertFrom-Array: Found: $($item |Out-string)"
        [array]$keys = $item.psobject.Properties.Name.ToLower()
        if ($keys.Contains('oneof')) { $Selector = "OneOf" }
        if ($keys.Contains('allof')) { $Selector = "AllOf" }
        if ($keys.Contains('anyof')) { $Selector = "AnyOf" }
        if ($Selector) {
          write-verbose "Selector : $($Selector)"
          $retVal += (ConvertFrom-SchemaObject -Object $item.$Selector -depth $Depth)
        } else {
          $retVal += (ConvertFrom-SchemaObject -Object $item -depth $Depth)
        }
# foreach ($key in $item.psobject.Properties.name) {
# Write-Verbose "ConvertFrom-Array: Found: $($key)"
# $retVal += (ConvertFrom-SchemaObject -Object $item.$key -depth $Depth)
# }
      }
    }
    return $retVal
  }
}
function Format-Document {
  [CmdletBinding(
    HelpURI = 'https://github.com/SchemaModule/PowerShell/blob/master/docs/Format-SchemaDocument.md#format-schemadocument',
    PositionalBinding = $true)]
  [OutputType([string])]
  param (
    [Parameter(Mandatory, ValueFromPipeline)]
    [String] $json
  )
  $indent = 0;
  ($json -Split '\n' |
    ForEach-Object {
      if ($_ -match '[\}\]]') {
        # This line contains ] or }, decrement the indentation level
        $indent--
      }
      $line = (' ' * $indent * 2) + $_.TrimStart().Replace(': ', ': ')
      if ($_ -match '[\{\[]') {
        # This line contains [ or {, increment the indentation level
        $indent++
      }
      if ($line.contains('"schema"')) {
        $line.Replace('"schema"', '"$schema"')
      }
      elseif ($line.contains('"definitions"')) {
        $line.Replace('"definitions"', '"$definitions"')
      }
      elseif ($line.contains('"id": ')) {
        $line.Replace('"id": "', '"$id": "')
      }
      elseif ($line.contains('"ref"')) {
        $line.Replace('"ref"', '"$ref"')
      }
      else {
        $line
      }
    }) -Join "`n"
}