Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Cache/Add-CacheItem.ps1

<#
.SYNOPSIS
Add a cache item to the cache.
 
.DESCRIPTION
Adds a cache item to the cache with a specified key, value, and type.
 
.PARAMETER Key
The key of the cache item to add.
 
.PARAMETER Value
The value of the cache item to add.
 
.PARAMETER Type
The type of the cache item to add. Valid values are 'Project', 'Team', 'Group', 'SecurityDescriptor'.
 
.EXAMPLE
Add-CacheItem -Key 'MyKey' -Value 'MyValue' -Type 'Project'
 
.NOTES
This function is private and should not be used directly.
#>

Function Add-CacheItem
{
    [CmdletBinding()]
    param (
        # The key of the cache item to add
        [Parameter(Mandatory = $true)]
        [string]
        $Key,

        # The value of the cache item to add
        [Parameter(Mandatory = $true)]
        [object]
        $Value,

        # The type of the cache item to add
        [Parameter(Mandatory = $true)]
        [ValidateScript({$_ -in (Get-AzDoCacheObjects)})]
        [string]
        $Type,

        # Suppress warning messages
        [switch]
        $SuppressWarning
    )

    Write-Verbose "[Add-CacheItem] Retrieving the current cache."
    # Get-CacheObject returns the live list, so the changes below are made in place rather than
    # on a copy of the whole cache.
    [System.Collections.Generic.List[CacheItem]]$cache = Get-CacheObject -CacheType $Type

    # If there is no cache yet, create one. An empty list is the live cache and is filled in
    # place, so anyone already holding it sees the new item.
    if ($null -eq $cache)
    {
        Write-Verbose "[Add-CacheItem] Cache is empty. Creating new cache."
        $cache = [System.Collections.Generic.List[CacheItem]]::New()
    }

    Write-Verbose "[Add-CacheItem] Creating new cache item with key: '$Key'."
    $cacheItem = [CacheItem]::New($Key, $Value)

    Write-Verbose "[Add-CacheItem] Checking if the cache already contains the key: '$Key'."
    # A dictionary lookup instead of a Where-Object scan, which made filling a cache quadratic.
    $index = Get-CacheKeyIndex -Type $Type -Cache $cache
    if ($null -ne $index)
    {
        $existingItem = $null
        $null = $index.TryGetValue($Key, [ref]$existingItem)
    }
    else
    {
        # The cache holds a duplicate key, so it cannot be indexed. Scan it instead.
        $existingItem = $cache.Where({ $_.Key -eq $Key })
    }

    if ($existingItem)
    {
        # If the cache already contains the key, remove the existing item
        if ($SuppressWarning.IsPresent)
        {
            Write-Verbose "[Add-CacheItem] A cache item with the key '$Key' already exists. Flushing key from the cache."
        }
        else
        {
            Write-Warning "[Add-CacheItem] A cache item with the key '$Key' already exists. Flushing key from the cache."
        }

        # Remove every item with this key, walking backwards so the indexes still to be visited
        # do not shift.
        for ($i = $cache.Count - 1; $i -ge 0; $i--)
        {
            if ($cache[$i].Key -eq $Key)
            {
                $cache.RemoveAt($i)
            }
        }
        if ($null -ne $index)
        {
            $null = $index.Remove($Key)
        }
    }

    Write-Verbose "[Add-CacheItem] Adding new cache item with key: '$Key'."
    $cache.Add($cacheItem)
    if ($null -ne $index)
    {
        $index[$Key] = $cacheItem
    }

    # Update the memory cache. This is the same list in the usual case, but it is a new one when
    # there was no cache yet or the global held something other than a List[CacheItem].
    Set-Variable -Name "AzDo$Type" -Value $cache -Scope Global

    Write-Verbose "[Add-CacheItem] Cache item with key: '$Key' successfully added."
}