Private/Document/Resolve-OpenApiSchemaReference.ps1
|
function Resolve-OpenApiSchemaReference { <# .SYNOPSIS Resolves a schema $ref to a normalised schema, with caching, reference stubs and circular-reference detection. .DESCRIPTION - External refs (not starting with '#'): OA020 Error, the current operation is marked as using one, and a blank schema is returned. - Unresolvable local refs: OA021 Error and a blank schema. - Otherwise the target is normalised once and cached by its JSON pointer. RefName is set for '#/components/schemas/<name>' (and '#/definitions/<name>'); a schema that is only a $ref to another one (an alias) is a copy of the full target under its own name. - A ref to a named schema made while another schema is being normalised (a nested ref) returns the reference stub of the target (ConvertTo-OpenApiSchemaStub) rather than the full schema, so the model stays linear in size; the full schema is Document.Schemas[RefName]. Refs made from an operation (parameters, bodies, responses, headers), the Schemas map, or with -Full get the full, shared schema object. - A nested ref to a schema that is still being normalised is a cycle: OA022 Information and a stub with Recursive = $true (built from the target's own keywords only). #> [CmdletBinding()] [OutputType([pscustomobject])] param( [Parameter(Mandatory)] [pscustomobject]$Context, [Parameter(Mandatory)] [AllowEmptyString()] [string]$Reference, [Parameter(Mandatory)] [AllowEmptyString()] [string]$Pointer, [switch]$Full ) if (-not $Reference.StartsWith('#')) { $Context.ExternalRefHits++ Add-OpenApiFinding -Context $Context -Severity Error -Code 'OA020' -Pointer $Pointer -Message "External `$ref '$Reference' is not supported; the operations that use it are flagged Unsupported." return Get-OpenApiBlankSchema } $target = Resolve-OpenApiPointer -Root $Context.Root -Reference $Reference if (-not $target.Found) { Add-OpenApiFinding -Context $Context -Severity Error -Code 'OA021' -Pointer $Pointer -Message "`$ref '$Reference' could not be resolved." return Get-OpenApiBlankSchema } $key = $target.Pointer $refName = $null if ($key -match '^/components/schemas/([^/]+)$') { $refName = $Matches[1].Replace('~1', '/').Replace('~0', '~') } $asStub = (-not $Full) -and $null -ne $refName -and $Context.SchemaStack.Count -gt 0 if ($Context.SchemaCache.ContainsKey($key)) { if ($Context.TaintedSchemas.Contains($key)) { $Context.ExternalRefHits++ } $schema = $Context.SchemaCache[$key] } elseif ($Context.SchemaStack.Contains($key)) { $name = $refName if ($null -eq $name) { $name = $key } Add-OpenApiFinding -Context $Context -Severity Information -Code 'OA022' -Pointer $key -Operation $null -Message "Schema '$name' is circular; the recursive reference is marked Recursive and not expanded again." $stub = ConvertTo-OpenApiSchema -Context $Context -Node $target.Value -Pointer $key -Shallow if ($null -eq $stub) { $stub = Get-OpenApiBlankSchema } $stub.RefName = $refName $stub.Recursive = $true return $stub } else { [void]$Context.SchemaStack.Add($key) $hitsBefore = $Context.ExternalRefHits $raw = $target.Value try { if ($raw -is [System.Collections.IDictionary] -and $raw.Contains('$ref')) { # An alias of another schema: a copy of the full target, so the target keeps its own name $schema = Resolve-OpenApiSchemaReference -Context $Context -Reference ([string]$raw['$ref']) -Pointer $key -Full $schema = $schema.PSObject.Copy() if ($null -ne $raw['description']) { $schema.Description = [string]$raw['description'] } if ($raw['nullable'] -eq $true) { $schema.Nullable = $true } } else { $schema = ConvertTo-OpenApiSchema -Context $Context -Node $raw -Pointer $key } } finally { [void]$Context.SchemaStack.Remove($key) } if ($null -eq $schema) { $schema = Get-OpenApiBlankSchema } if ($Context.ExternalRefHits -gt $hitsBefore) { [void]$Context.TaintedSchemas.Add($key) } if ($null -ne $refName) { $schema.RefName = $refName } $Context.SchemaCache[$key] = $schema } if ($asStub) { if (-not $Context.StubCache.ContainsKey($key)) { $Context.StubCache[$key] = ConvertTo-OpenApiSchemaStub -Schema $schema } return $Context.StubCache[$key] } return $schema } |