Public/Metaverse/Set-JIMMetaverseAttribute.ps1
|
# Copyright (c) Tetron Limited. All rights reserved. # Licensed under the Tetron Commercial License. See LICENSE file in the project root. function Set-JIMMetaverseAttribute { <# .SYNOPSIS Updates an existing custom Metaverse Attribute in JIM. .DESCRIPTION Updates a custom Metaverse Attribute. Changes are routed to the correct endpoint: - Name, RenderingHint and StandardMappings are updated together via the attribute endpoint. - Type and AttributePlurality are updated via the dedicated schema endpoint, which needs both, so supplying either sends both (the unspecified one is read from the attribute's current schema, except that -Type Boolean alone sends SingleValued, since a Boolean holds one value). While any Metaverse Object holds a value for the attribute, the only schema change JIM allows is widening it from SingleValued to MultiValued with its type unchanged; every value is kept. Any other schema change is refused until the values are cleared. Giving the type and plurality the attribute already has is no change: it always succeeds, and nothing is recorded in the attribute's change history. A rename is refused while an Expression reads the attribute by its current name (a Scoping Criterion, Attribute Flow, Object Matching Rule or Example Data Template Expression); the error names each one. Change them first. A rename that only changes the case of the name is allowed, because Expressions read attribute names ignoring case. Object Type bindings are not changed here; use Add-JIMMetaverseObjectTypeAttribute and Remove-JIMMetaverseObjectTypeAttribute. Built-in attributes cannot be modified. .PARAMETER Id The ID of the attribute to change. .PARAMETER InputObject An attribute from the pipeline, for example from Get-JIMMetaverseAttribute. .PARAMETER Name A new name for the attribute. It must be unique, ignoring case. .PARAMETER RenderingHint How the portal shows a multi-valued attribute's values. Ignored for a single-valued attribute. .PARAMETER Type A new kind of value for the attribute; Integer is the type JIM names Number. JIM refuses a type change while any Metaverse Object holds a value for the attribute. A Boolean holds one value, so Boolean without -AttributePlurality also makes the attribute single-valued. .PARAMETER AttributePlurality Whether the attribute holds one value or many. While any Metaverse Object holds a value for the attribute, JIM allows only a change from SingleValued to MultiValued, which keeps every value; it refuses MultiValued to SingleValued, and refuses MultiValued for a Boolean. Giving the plurality the attribute already has changes nothing, so stored values never block it. .PARAMETER StandardMappings The attribute's Standard Mappings, replacing any it has; @() clears them. Each is a hashtable with a Standard (Scim, Ldap or Jim), a CounterpartName (the attribute's name in that standard) and optional Notes. Standard Mappings are guidance only and never affect synchronisation. .PARAMETER ChangeReason An optional reason for the change, recorded in the attribute's Configuration Change History. .PARAMETER PassThru Returns the updated attribute. .OUTPUTS With -PassThru, the updated attribute in full; otherwise nothing. .EXAMPLE Set-JIMMetaverseAttribute -Id 1 -Name "UpdatedName" Renames the Attribute with ID 1. .EXAMPLE Set-JIMMetaverseAttribute -Id 1 -RenderingHint List -PassThru Changes the rendering hint and returns the updated object. .EXAMPLE Set-JIMMetaverseAttribute -Id 42 -StandardMappings @(@{ Standard = 'Scim'; CounterpartName = 'costCenter'; Notes = 'SCIM Enterprise User extension.' }) Records how the custom attribute corresponds to its SCIM 2.0 counterpart. .EXAMPLE Get-JIMMetaverseAttribute -Name "CustomAttr" | Set-JIMMetaverseAttribute -Type Integer Changes an attribute's data type (refused if any object holds a stored value). .EXAMPLE Set-JIMMetaverseAttribute -Id 42 -AttributePlurality MultiValued -ChangeReason "The directory now holds several values" Widens a single-valued attribute to multi-valued, keeping the values Metaverse Objects already hold. .LINK Get-JIMMetaverseAttribute New-JIMMetaverseAttribute Remove-JIMMetaverseAttribute Add-JIMMetaverseObjectTypeAttribute Remove-JIMMetaverseObjectTypeAttribute #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', DefaultParameterSetName = 'ById')] [OutputType('JIM.MetaverseAttributeDetailDto')] param( [Parameter(Mandatory, ParameterSetName = 'ById', ValueFromPipelineByPropertyName)] [int]$Id, [Parameter(Mandatory, ParameterSetName = 'ByInputObject', ValueFromPipeline)] [PSCustomObject]$InputObject, [Parameter()] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter()] [ValidateSet('Default', 'Table', 'ChipSet', 'List')] [string]$RenderingHint, [Parameter()] [ValidateSet('Text', 'Integer', 'LongNumber', 'Decimal', 'DateTime', 'Boolean', 'Reference', 'Guid', 'Binary')] [string]$Type, [Parameter()] [ValidateSet('SingleValued', 'MultiValued')] [string]$AttributePlurality, [Parameter()] [AllowEmptyCollection()] [array]$StandardMappings, [Parameter()] [ValidateNotNullOrEmpty()] [string]$ChangeReason, [switch]$PassThru ) process { # Check connection first if (-not $script:JIMConnection) { Write-Error "You are not connected to JIM. Run Connect-JIM -Url <your JIM URL> to authenticate, then try again." return } $attrId = if ($InputObject) { $InputObject.id } else { $Id } # Enum request fields are sent as their string names; the API rejects numeric ordinals # (JsonStringEnumConverter allowIntegerValues:false, PR #1060). Responses already return # enum names, so a value read back from the current schema is used as-is. -Type's # ValidateSet exposes 'Integer' as an alias for the AttributeDataType member 'Number'; # that is normalised where -Type is applied below. Other values are exact member names. $metadataChanged = $PSBoundParameters.ContainsKey('Name') -or $PSBoundParameters.ContainsKey('RenderingHint') -or $PSBoundParameters.ContainsKey('StandardMappings') $schemaChanged = $PSBoundParameters.ContainsKey('Type') -or $PSBoundParameters.ContainsKey('AttributePlurality') if (-not $metadataChanged -and -not $schemaChanged) { Write-Warning "No updates specified. Provide -Name, -RenderingHint, -StandardMappings, -Type and/or -AttributePlurality." return } # Validate and normalise Standard Mappings up front, before anything is sent to the API. A supplied # list replaces the attribute's full set, so an empty array clears them. $mappingsBody = $null if ($PSBoundParameters.ContainsKey('StandardMappings')) { $validStandards = @('Scim', 'Ldap', 'Jim') $mappingsBody = @() foreach ($mapping in $StandardMappings) { if (-not $mapping.Standard -or [string]$mapping.Standard -notin $validStandards) { Write-Error "Each Standard Mapping requires a Standard of 'Scim', 'Ldap' or 'Jim'." return } if ([string]::IsNullOrWhiteSpace([string]$mapping.CounterpartName)) { Write-Error "Each Standard Mapping requires a CounterpartName (the equivalent attribute name in the standard)." return } $entry = @{ standard = [string]$mapping.Standard counterpartName = ([string]$mapping.CounterpartName).Trim() } if (-not [string]::IsNullOrWhiteSpace([string]$mapping.Notes)) { $entry.notes = ([string]$mapping.Notes).Trim() } $mappingsBody += $entry } } $displayName = if ($Name) { $Name } elseif ($InputObject -and $InputObject.name) { $InputObject.name } else { $attrId } if (-not $PSCmdlet.ShouldProcess($displayName, "Update Metaverse Attribute")) { return } try { # Schema change (type / plurality). The endpoint requires both values, so read the # current schema to fill whichever was not supplied. if ($schemaChanged) { $current = Invoke-JIMApi -Endpoint "/api/v1/metaverse/attributes/$attrId" $typeValue = if ($PSBoundParameters.ContainsKey('Type')) { if ($Type -eq 'Integer') { 'Number' } else { $Type } } else { $current.type } # A Boolean holds one value, so a change to Boolean that names no plurality makes the attribute # single-valued, as the portal does; an explicit multi-valued Boolean is sent and JIM refuses it. $pluralityValue = if ($PSBoundParameters.ContainsKey('AttributePlurality')) { $AttributePlurality } elseif ($typeValue -eq 'Boolean') { 'SingleValued' } else { $current.attributePlurality } $schemaBody = @{ type = $typeValue attributePlurality = $pluralityValue } if ($ChangeReason) { $schemaBody.changeReason = $ChangeReason } Write-Verbose "Changing schema for Metaverse Attribute $attrId (type=$typeValue, plurality=$pluralityValue)" $null = Invoke-JIMApi -Endpoint "/api/v1/metaverse/attributes/$attrId/schema" -Method 'PATCH' -Body $schemaBody } # Name / rendering-hint change. if ($metadataChanged) { $body = @{} if ($PSBoundParameters.ContainsKey('Name')) { $body.name = $Name } if ($PSBoundParameters.ContainsKey('RenderingHint')) { $body.renderingHint = $RenderingHint } if ($null -ne $mappingsBody) { $body.standardMappings = $mappingsBody } if ($ChangeReason) { $body.changeReason = $ChangeReason } Write-Verbose "Updating name/rendering for Metaverse Attribute $attrId" $null = Invoke-JIMApi -Endpoint "/api/v1/metaverse/attributes/$attrId" -Method 'PATCH' -Body $body } if ($PassThru) { Invoke-JIMApi -Endpoint "/api/v1/metaverse/attributes/$attrId" } } catch { Write-Error "Failed to update Metaverse Attribute: $_" } } } |