Private/Objects.ps1
|
# What the cmdlets emit. Everything that mirrors an API payload is emitted as the API answers it (the same # field names in the REST API, Cloud Shell and here), with a PSTypeName that picks its view: catalog types get # their jsonFields parsed and their flatten objects' keys copied onto the item; deployments, their operations, # what-if changes, diagnostics and tenants are typed for the views in VirtualMetric.Custom.Format.ps1xml, which # show dates and positions readably without changing the data. The context is the one object the module makes # itself (VirtualMetric.Context, see Private/Session.ps1). # The API's timestamps are Unix seconds. Each such field gets a companion ScriptProperty with the local # DateTime (createdAt -> CreatedAtTime), so a pipeline can compare with Get-Date. The getters are built unbound # and self-contained: an object made in a thread job is read in another runspace. $script:VMetricTimeGetters = [ordered]@{} foreach ($timeField in @('createdAt', 'updatedAt', 'startedAt', 'completedAt')) { $script:VMetricTimeGetters[$timeField] = [scriptblock]::Create((@' $value = $this.{0} $seconds = 0L if ($null -eq $value -or -not [long]::TryParse([string]$value, [ref] $seconds) -or $seconds -le 0) {{ return $null }} if ($seconds -gt 100000000000) {{ return [DateTimeOffset]::FromUnixTimeMilliseconds($seconds).LocalDateTime }} [DateTimeOffset]::FromUnixTimeSeconds($seconds).LocalDateTime '@ -f $timeField)) } function Add-VMetricTimeProperty { [CmdletBinding()] param( [AllowNull()] [object] $InputObject ) if (-not (Test-VMetricObjectLike -InputObject $InputObject) -or $InputObject -is [System.Collections.IDictionary]) { return } foreach ($field in $script:VMetricTimeGetters.Keys) { $property = $InputObject.psobject.Properties[$field] if (-not $property -or $property.MemberType -ne 'NoteProperty' -or $property.Value -is [string]) { continue } $name = $field.Substring(0, 1).ToUpperInvariant() + $field.Substring(1) + 'Time' if (-not $InputObject.psobject.Properties[$name]) { $InputObject.psobject.Properties.Add([System.Management.Automation.PSScriptProperty]::new($name, $script:VMetricTimeGetters[$field])) } } } function Add-VMetricTypeName { [CmdletBinding()] param( [AllowNull()] [object] $InputObject, [Parameter(Mandatory)] [string] $TypeName ) if ($null -ne $InputObject -and $InputObject -is [System.Management.Automation.PSObject] -and $InputObject.psobject.TypeNames[0] -ne $TypeName) { $InputObject.psobject.TypeNames.Insert(0, $TypeName) } } function ConvertTo-VMetricOutputObject { [CmdletBinding()] param( [AllowNull()] [object] $InputObject, [AllowNull()] [AllowEmptyString()] [string] $TypeName ) if ($null -eq $InputObject) { return } $object = $InputObject if ($object -is [System.Collections.IDictionary]) { $object = ConvertTo-VMetricPSObject -InputObject $object } if (-not (Test-VMetricObjectLike -InputObject $object)) { return $object } $type = Get-VMetricCatalogType -Name $TypeName if ($type) { foreach ($field in (ConvertTo-VMetricArray -InputObject (Get-VMetricMember -InputObject $type -Name 'jsonFields'))) { $property = $object.psobject.Properties[[string]$field] if ($property -and $property.Value -is [string]) { $text = $property.Value.Trim() if ($text.StartsWith('{') -or $text.StartsWith('[')) { try { $property.Value = ConvertFrom-VMetricJson -Json $text } catch { Write-Verbose "The $field field is not valid JSON; left as text." } } } } foreach ($field in (ConvertTo-VMetricArray -InputObject (Get-VMetricMember -InputObject $type -Name 'flatten'))) { $property = $object.psobject.Properties[[string]$field] if (-not $property -or -not (Test-VMetricObjectLike -InputObject $property.Value)) { continue } foreach ($entry in (Get-VMetricEntryList -InputObject $property.Value)) { if (-not $object.psobject.Properties[$entry.Name]) { $object.psobject.Properties.Add([System.Management.Automation.PSNoteProperty]::new($entry.Name, $entry.Value)) } } } } Add-VMetricTimeProperty -InputObject $object if ($TypeName) { Add-VMetricTypeName -InputObject $object -TypeName $TypeName } if ($TypeName -eq 'VirtualMetric.Deployment') { # Whoever emits a deployment (Get-VMetricDeployment, New-VMetricDeployment, a write's read back), its # operations and their property changes are typed for their views, with their timestamps. foreach ($operation in (ConvertTo-VMetricArray -InputObject (Get-VMetricMember -InputObject $object -Name 'operations'))) { Add-VMetricTypeName -InputObject $operation -TypeName 'VirtualMetric.DeploymentOperation' Add-VMetricTimeProperty -InputObject $operation Add-VMetricDeltaTypeName -InputObject $operation } } $object } function Get-VMetricDeploymentTypeName { # The type the catalog gives deployments (Get-VMetricDeployment's output), so that New-VMetricDeployment and # Get-VMetricDeployment emit one shape. [CmdletBinding()] [OutputType([string])] param() try { $entry = Get-VMetricCatalogCmdlet -Name 'Get-VMetricDeployment' $output = [string](Get-VMetricMember -InputObject $entry -Name 'output') if ($output) { return $output } } catch { if (Test-VMetricFlowControl -ErrorRecord $_) { throw } Write-Verbose "No catalog entry for Get-VMetricDeployment: $($_.Exception.Message)" } return 'VirtualMetric.Deployment' } function Add-VMetricDeltaTypeName { [CmdletBinding()] param( [AllowNull()] [object] $InputObject ) foreach ($delta in (ConvertTo-VMetricArray -InputObject (Get-VMetricMember -InputObject $InputObject -Name 'delta'))) { Add-VMetricTypeName -InputObject $delta -TypeName 'VirtualMetric.WhatIfPropertyChange' } } function ConvertTo-VMetricDeploymentObject { # A deployment as the API answers it, typed: the deployment, each operation # (VirtualMetric.DeploymentOperation) and each property change (VirtualMetric.WhatIfPropertyChange). [CmdletBinding()] param( [Parameter(Mandatory)] [object] $InputObject ) $typeName = Get-VMetricDeploymentTypeName $object = ConvertTo-VMetricOutputObject -InputObject $InputObject -TypeName $typeName if ($typeName -ne 'VirtualMetric.Deployment') { foreach ($operation in (ConvertTo-VMetricArray -InputObject (Get-VMetricMember -InputObject $object -Name 'operations'))) { Add-VMetricTypeName -InputObject $operation -TypeName 'VirtualMetric.DeploymentOperation' Add-VMetricTimeProperty -InputObject $operation Add-VMetricDeltaTypeName -InputObject $operation } } $object } function ConvertTo-VMetricWhatIfChange { # One change of a what-if as the API answers it, typed VirtualMetric.WhatIfChange. [CmdletBinding()] param( [Parameter(Mandatory)] [object] $InputObject ) Add-VMetricTypeName -InputObject $InputObject -TypeName 'VirtualMetric.WhatIfChange' Add-VMetricDeltaTypeName -InputObject $InputObject $InputObject } function ConvertTo-VMetricDiagnostic { # One diagnostic as the API answers it ({code, message, severity, range}), typed VirtualMetric.Diagnostic. [CmdletBinding()] param( [Parameter(Mandatory)] [object] $InputObject ) Add-VMetricTypeName -InputObject $InputObject -TypeName 'VirtualMetric.Diagnostic' $InputObject } function ConvertTo-VMetricTenantObject { # A tenant of GET /cli/tenants ({id, name, isMain, delegated}), typed VirtualMetric.Tenant, with isCurrent. [CmdletBinding()] param( [Parameter(Mandatory)] [object] $InputObject, [AllowNull()] [AllowEmptyString()] [string] $CurrentTenantId ) $object = ConvertTo-VMetricOutputObject -InputObject $InputObject -TypeName 'VirtualMetric.Tenant' $id = [string](Get-VMetricMember -InputObject $object -Name 'id') $object | Add-Member -NotePropertyName 'isCurrent' -NotePropertyValue ([bool]($CurrentTenantId -and $id -eq $CurrentTenantId)) -Force $object } |