POSH-GitLab-Registry.psm1

#
## Created by: lucas.cueff[at]lucas-cueff.com
#
## released on 01/2021
#
# v1.0.1 : first public release - beta version
# v1.1.0 : beta version :
# - add encryption management to config file,
# - fix validation pattern for set-GitLabNugetRepository,
# - change location default path for import/export-GitLabNugetRepository for linux compatibility ($home instead of $env:programdata)
# - fix remove-GitLabNugetRepository
# v1.1.1 : last release - beta version :
# - add basic auth header for publish api (nuget) => gitlab.com has changed its behavior and does not accept 'PRIVATE-TOKEN' header anymore
#
#'(c) 2020-2021 lucas-cueff.com - Distributed under Artistic Licence 2.0 (https://opensource.org/licenses/artistic-license-2.0).'
<#
    .SYNOPSIS
    PowerShell Module managing package list / download from a Package Registry (NuGet format) from a Gitlab repository
 
    .DESCRIPTION
    PowerShell Module managing package list / download from a Package Registry (NuGet format) from a Gitlab repository
    Functions available :
    - Register your Package Registry info locally (ProjectID, API Key...)
    - List all packages available in your Package Registry
    - Get info from a specific package (files, metadata)
    - Download a NuGet Package from your Package Registry and extract it locally
     
    .EXAMPLE
    Set a new Package Registry source
    PS C:\> New-GitLabNugetRepository -SourceName MySource -APIKey "123456789azerty" -ProjectID "12345" -ServerName "Mygitlabci.tld" -APIKeyName "MyUserAccountORAPIKeyName"
 
    .EXAMPLE
    List all packages available from your source
    PS C:\> Get-GitLabRepositoryPackage -Action list -PackageSource MySource
 
    .EXAMPLE
    Get information for a package in a specific version
    PS C:\> Get-GitLabRepositoryPackage -Action info -PackageName MyPackage -PackageVersion 1.0.0
 
    .EXAMPLE
    Install a package from your source
    PS C:\> Get-GitLabRepositoryPackage -Action install -PackageName MyPackage -OutputDirectory $env:USERPROFILE\desktop
     
#>

Function Get-ScriptDirectory {
    [cmdletbinding()]
    Param ()
    if ($psISE) {
        $ScriptPath = Split-Path -Parent $psISE.CurrentFile.FullPath
    } elseif($PSVersionTable.PSVersion.Major -gt 3) {
        $ScriptPath = $PSScriptRoot
    } else {
        $ScriptPath = split-path -parent $MyInvocation.MyCommand.Path
    }
    $ScriptPath
}
Function Get-GitLabNugetRepository {
<#
    .SYNOPSIS
    Get info from an existing GitLab Package Registry Source / Repository.
 
    .DESCRIPTION
    Get info from an existing GitLab Package Registry Source / Repository.
     
    .PARAMETER SourceName
    -SourceName [string]
    SourceName of your project
         
    .OUTPUTS
       TypeName : System.Collections.Hashtable
         
    .EXAMPLE
    Get-GitLabNugetRepository -SourceName MySource
    Get repository info for MySource
     
    .EXAMPLE
    Get-GitLabNugetRepository
    Get all repositories info
#>

    [cmdletbinding()]
    param(
        [parameter(Mandatory=$false)]
        [ValidateNotNullorempty()]
            [string]$SourceName
    )
    process {
        if (!($global:GitLabNugetConfig)) {
            throw "please import or create a new source repository using Import-GitLabNugetRepository or New-GitLabNugetRepository cmdlets"
        }
        if ($SourceName) {
            if ($global:GitLabNugetConfig.RepositoryName -contains $SourceName) {
                return $global:GitLabNugetConfig | Where-Object {$_.RepositoryName -eq $SourceName}
            } else {
                throw "nuget repository $($SourceName) is not defined, please use New-GitLabNugetRepository"
            }
        } else {
            return $global:GitLabNugetConfig
        }
    }
}
Function Clear-GitLabNugetRepository {
<#
    .SYNOPSIS
    Clear all GitLab Package Registry Sources / Repositories set
 
    .DESCRIPTION
    Clear all GitLab Package Registry Sources / Repositories set
         
    .OUTPUTS
       None
         
    .EXAMPLE
    Clear-GitLabNugetRepository
    Remove all Gitlab Package Registry / repository set locally
#>

    [cmdletbinding()]
    param()
    process {
        if (!($global:GitLabNugetConfig)) {
            throw "please import or create a new source repository using Import-GitLabNugetRepository or New-GitLabNugetRepository cmdlets"
        } else {
            Remove-Variable GitLabNugetConfig -Force -Scope Global
        }
    }
}
Function New-GitLabNugetRepository {
<#
    .SYNOPSIS
    Create a new GitLab Package Registry Source / Repository
 
    .DESCRIPTION
    Create or add a new GitLab Package Registry Source / Repository. This is a prerequisite to use the other cmdlets like Get-GitLabNugetRepositoryPackage
     
    .PARAMETER SourceName
    -SourceName [string]
    SourceName of your project
 
    .PARAMETER ServerName
    -ServerName [string]
    Gitlab Server FQDN, if gitlab.com is used, use 'gitlab.com' value.
     
    .PARAMETER APIKey
    -APIKey [string]
    GitLab API Key / Access Token (mandatory to access GitLab project repository and package registry). Require API Access : read_api, read_repository, read_registry
     
    .PARAMETER APIKeyName
    -APIKeyName [string]
    GitLab API Key Name / Access Token Name. Require for Basic Auth with several nuget service (publish). if a personal access token is used, this value must be the account name.
     
    .PARAMETER ProjectID
    -ProjectID [string]
    ProjectID of your GitLab project
     
    .OUTPUTS
       TypeName : System.Collections.Hashtable
         
    .EXAMPLE
    New-GitLabNugetRepository -SourceName MySource -APIKey "123456789azerty" -ProjectID "12345" -ServerName "Mygitlabci.tld" -APIKeyName "MyUserAccountORAPIKeyName"
    Register new repository MySource
     
#>

    [cmdletbinding()]
    param(
        [parameter(Mandatory=$true)]
        [ValidateNotNullorempty()]
            [string]$SourceName,
        [parameter(Mandatory=$true)]
        [ValidateNotNullorempty()]
            [string]$ServerName,
        [parameter(Mandatory=$true)]
        [ValidateNotNullorempty()]
            [string]$APIKey,
        [parameter(Mandatory=$false)]
        [ValidateNotNullorempty()]
            [string]$APIKeyName,
        [parameter(Mandatory=$true)]
        [ValidatePattern("^\d*$")]
            [string]$ProjectID
    )
    process {
       if (!($APIKeyName)) {
            $APIKeyName = "gitlab-ci-token"
       }
       if ($global:GitLabNugetConfig) {
        if ($global:GitLabNugetConfig.RepositoryName -notcontains $SourceName) {
            $global:GitLabNugetConfig += @{"APIKey" = $APIKey; "APIKeyName" = $APIKeyName ;"ProjectID" = $ProjectID; "RepositoryName" = $SourceName; "Server" = $ServerName}
        } else {
            throw "nuget repository $($SourceName) is already defined"
        }
       } else {
        $global:GitLabNugetConfig = [System.Collections.ArrayList]@(@{"APIKey" = $APIKey ; "APIKeyName" = $APIKeyName ; "ProjectID" = $ProjectID; "RepositoryName" = $SourceName; "Server" = $ServerName})
       }
       return $global:GitLabNugetConfig
    }
}
Function Set-GitLabNugetRepository {
<#
    .SYNOPSIS
    Edit an existing GitLab Package Registry Source / Repository
 
    .DESCRIPTION
    Edit an existing GitLab Package Registry Source / Repository.
     
    .PARAMETER SourceName
    -SourceName [string]
    SourceName of your project
 
    .PARAMETER ServerName
    -ServerName [string]
    Gitlab Server FQDN, if gitlab.com is used, use 'gitlab.com' value.
     
    .PARAMETER APIKey
    -APIKey [string]
    GitLab API Key / Access Token (mandatory to access GitLab project repository and package registry). Require API Access : read_api, read_repository, read_registry
     
    .PARAMETER APIKeyName
    -APIKeyName [string]
    GitLab API Key Name / Access Token Name. Require for Basic Auth with several nuget service (publish). if a personal access token is used, this value must be the account name.
     
    .PARAMETER ProjectID
    -ProjectID [string]
    ProjectID of your GitLab project
     
    .OUTPUTS
       TypeName : System.Collections.Hashtable
         
    .EXAMPLE
    Set-GitLabNugetRepository -SourceName MySource -APIKey "123456789azerty" -ProjectID "12345" -ServerName "Mygitlabci.tld" -APIKeyName "MyUserAccountORAPIKeyName"
    Update repository MySource
     
#>

    [cmdletbinding()]
    param(
        [parameter(Mandatory=$true)]
        [ValidateNotNullorempty()]
            [string]$SourceName,
        [parameter(Mandatory=$true)]
        [ValidateNotNullorempty()]
            [string]$ServerName,
        [parameter(Mandatory=$true)]
        [ValidateNotNullorempty()]
            [string]$APIKey,
        [parameter(Mandatory=$false)]
        [ValidateNotNullorempty()]
            [string]$APIKeyName,
        [parameter(Mandatory=$true)]
        [ValidatePattern("^\d*$")]
            [string]$ProjectID
    )
    process {
        if (!($global:GitLabNugetConfig)) {
            throw "please import or create a new source repository using Import-GitLabNugetRepository or New-GitLabNugetRepository cmdlets"
        }
        if (!($APIKeyName)) {
            $APIKeyName = "gitlab-ci-token"
        }
        if ($global:GitLabNugetConfig.RepositoryName -contains $SourceName) {
            $tmpRepository = $global:GitLabNugetConfig | Where-Object {$_.RepositoryName -eq $SourceName}
            $tmpRepository.ProjectID = $ProjectID
            $tmpRepository.APIKey = $APIKey
            $tmpRepository.APIKeyName = $APIKeyName
        } else {
            throw "nuget repository $($SourceName) is not defined, please use New-GitLabNugetRepository"
        }
        return $global:GitLabNugetConfig
    }
}
Function Remove-GitLabNugetRepository {
<#
    .SYNOPSIS
    Remove an existing GitLab Package Registry Source / Repository.
 
    .DESCRIPTION
    Remove an existing GitLab Package Registry Source / Repository.
     
    .PARAMETER SourceName
    -SourceName [string]
    SourceName of your project
         
    .OUTPUTS
       TypeName : System.Collections.Hashtable
         
    .EXAMPLE
    Remove-GitLabNugetRepository -SourceName MySource
    remove repository for MySource
     
#>

    [cmdletbinding()]
    param(
        [parameter(Mandatory=$true)]
        [ValidateNotNullorempty()]
            [string]$SourceName
    )
    process {
        if (!($global:GitLabNugetConfig)) {
            throw "please import or create a new source repository using Import-GitLabNugetRepository or New-GitLabNugetRepository cmdlets"
        }
        $script:tmpGitLabNugetConfig = {$global:GitLabNugetConfig}.Invoke()
        if ($script:tmpGitLabNugetConfig.RepositoryName -contains $SourceName) {
            for ($i=0;$i -le $script:tmpGitLabNugetConfig.count;$i++) {
                if ($script:tmpGitLabNugetConfig[$i].RepositoryName -eq $SourceName) {
                    $script:tmpGitLabNugetConfig.remove($script:tmpGitLabNugetConfig[$i])
                    break
                }
            }
        } else {
            throw "nuget repository $($SourceName) is not defined, please use New-GitLabNugetRepository"
        }
        $global:GitLabNugetConfig = $script:tmpGitLabNugetConfig
        return $global:GitLabNugetConfig
    }
}
Function Import-GitLabNugetRepository {
<#
    .SYNOPSIS
    Import a GitLab Package Registry Source / Repository from a configuration file
 
    .DESCRIPTION
    Import a GitLab Package Registry Source / Repository from a configuration file. This is a prerequisite to use the other cmdlets like Get-GitLabNugetRepositoryPackage
     
    .PARAMETER XMLFolderPath
    -XMLFolderPath [string]
    Folder path where from your XML config file will be imported. If not set, $home\.PoshGitLab will be used
 
    .PARAMETER EncryptKeyInLocalFile
    -EncryptKeyInLocalFile [switch]
    encrypt apikey with password provided in MasterPassword parameter
 
    .PARAMETER MasterPassword
    -MasterPassword [securestring]
    password used to encrypt your API Keys
     
    .OUTPUTS
       TypeName : System.Collections.Hashtable
        
    .EXAMPLE
    Import-GitLabNugetRepository
    Import your configuration from default folder $home\.PoshGitLab
         
    .EXAMPLE
    Import-GitLabNugetRepository -XMLFolderPath c:\MyFolder
    import your configuration from c:\MyFolder
 
    .EXAMPLE
    Import-GitLabNugetRepository -XMLFolderPath c:\MyFolder -EncryptKeyInLocalFile -MasterPassword (ConvertTo-SecureString -String "YourP@ssw0rd" -AsPlainText -Force)
    import your encrypted configuration from c:\MyFolder
#>

    [cmdletbinding()]
    param(
        [parameter(Mandatory=$false)]
        [ValidateNotNullorempty()]
            [string]$XMLFolderPath,
        [parameter(Mandatory=$false)]
            [switch]$EncryptKeyInLocalFile,
        [parameter(Mandatory=$false)]
            [securestring]$MasterPassword
    )
    process {
        if (!$home) {
            $global:home = $env:userprofile
        }
        if (!($XMLFolderPath)) {
            $XMLFolderPath = join-path $global:home ".PoshGitLab"
        }
        $XMLFile = join-path $XMLFolderPath "GitlabRepository.xml"
        if (!(test-path $XMLFile)) {
            throw "config file $($XMLFile) does not exist"
        }
        if ($EncryptKeyInLocalFile.IsPresent) {
            If (!$MasterPassword) {
                throw "Please provide a valid Master Password to protect the API Key storage on disk and a valid API Key"
            } else {
                $script:tmpGitLabNugetConfig = Import-Clixml -Path $XMLFile
                $Credentials = New-Object System.Management.Automation.PSCredential -ArgumentList 'user', $MasterPassword
                for ($i=0;$i -le ($script:tmpGitLabNugetConfig.count -1);$i++) {
                    if ($script:tmpGitLabNugetConfig[$i].apikey -and $script:tmpGitLabNugetConfig[$i].Salt) {
                        try {
                            $Rfc2898Deriver = New-Object System.Security.Cryptography.Rfc2898DeriveBytes -ArgumentList $Credentials.GetNetworkCredential().Password, $script:tmpGitLabNugetConfig[$i].Salt
                            $KeyBytes  = $Rfc2898Deriver.GetBytes(32)
                            $SecString = ConvertTo-SecureString -Key $KeyBytes $script:tmpGitLabNugetConfig[$i].apikey
                            $SecureStringToBSTR = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecString)
                            $APIKey = [Runtime.InteropServices.Marshal]::PtrToStringAuto($SecureStringToBSTR)
                            $script:tmpGitLabNugetConfig[$i].apikey = $APIKey
                            $script:tmpGitLabNugetConfig[$i].remove('Salt')
                        } catch {
                            throw "Not able to set correctly your API Key, your passphrase my be incorrect"
                        }
                    }
                }
                $global:GitLabNugetConfig = $script:tmpGitLabNugetConfig.clone()
            }
        } else {
            $global:GitLabNugetConfig = Import-Clixml -Path $XMLFile
        }
        return $global:GitLabNugetConfig
    }
}
Function Export-GitLabNugetRepository {
<#
    .SYNOPSIS
    export a GitLab Package Registry Source / Repository to a configuration file
 
    .DESCRIPTION
    export a GitLab Package Registry Source / Repository to a configuration file
     
    .PARAMETER XMLFolderPath
    -XMLFolderPath [string]
    Folder path where from your XML config file will be imported. If not set, $home\.PoshGitLab will be used
 
    .PARAMETER EncryptKeyInLocalFile
    -EncryptKeyInLocalFile [switch]
    encrypt apikey with password provided in MasterPassword parameter
 
    .PARAMETER MasterPassword
    -MasterPassword [securestring]
    password used to encrypt your API Keys
     
    .OUTPUTS
       TypeName : System.Collections.Hashtable
        
    .EXAMPLE
    Export-GitLabNugetRepository
    Export your configuration to default folder $home\.PoshGitLab
     
    .EXAMPLE
    Export-GitLabNugetRepository -XMLFolderPath c:\MyFolder
    Export your configuration to c:\MyFolder
 
    .EXAMPLE
    Export-GitLabNugetRepository -XMLFolderPath c:\MyFolder -EncryptKeyInLocalFile -MasterPassword (ConvertTo-SecureString -String "YourP@ssw0rd" -AsPlainText -Force)
    Export and encrypt your configuration to c:\MyFolder
#>

    [cmdletbinding()]
    param(
        [parameter(Mandatory=$false)]
        [ValidateNotNullorempty()]
            [string]$XMLFolderPath,
        [parameter(Mandatory=$false)]
            [switch]$EncryptKeyInLocalFile,
        [parameter(Mandatory=$false)]
            [securestring]$MasterPassword
    )
    process {
        if (!($global:GitLabNugetConfig)) {
            throw "please import or create a new source repository using Import-GitLabNugetRepository or New-GitLabNugetRepository cmdlets"
        }
        if (!$home) {
            $global:home = $env:userprofile
        }
        if (!($XMLFolderPath)) {
            $XMLFolderPath = join-path $global:home ".PoshGitLab"
            write-verbose $XMLFolderPath
            $XMLFile = join-path $XMLFolderPath "GitlabRepository.xml"
            if (!(test-path $XMLFolderPath)) {
                New-Item -ItemType Directory -Path $XMLFolderPath -Force | out-null
            }
        } else {
            $XMLFile = join-path $XMLFolderPath "GitlabRepository.xml"
        }
        if ($EncryptKeyInLocalFile.IsPresent) {
            If (!$MasterPassword) {
                throw "Please provide a valid Master Password to protect the API Key storage on disk and a valid API Key"
            } else {
                $script:tmpGitLabNugetConfig = $global:GitLabNugetConfig.clone()
                for ($i=0;$i -le ($script:tmpGitLabNugetConfig.count -1);$i++) {
                    if ($script:tmpGitLabNugetConfig[$i].apikey) {
                        [Security.SecureString]$SecureKeyString = ConvertTo-SecureString -String $script:tmpGitLabNugetConfig[$i].apikey -AsPlainText -Force
                        $SaltBytes = New-Object byte[] 32
                        $RNG = New-Object System.Security.Cryptography.RNGCryptoServiceProvider
                        $RNG.GetBytes($SaltBytes)
                        $Credentials = New-Object System.Management.Automation.PSCredential -ArgumentList 'user', $MasterPassword
                        $Rfc2898Deriver = New-Object System.Security.Cryptography.Rfc2898DeriveBytes -ArgumentList $Credentials.GetNetworkCredential().Password, $SaltBytes
                        $KeyBytes  = $Rfc2898Deriver.GetBytes(32)
                        $EncryptedString = $SecureKeyString | ConvertFrom-SecureString -key $KeyBytes
                        $script:tmpGitLabNugetConfig[$i].add('Salt',$SaltBytes)
                        $script:tmpGitLabNugetConfig[$i].APIKey = $EncryptedString
                    }
                }
                try {
                    Export-Clixml -InputObject $tmpGitLabNugetConfig -Path $XMLFile -Force
                    Clear-GitLabNugetRepository
                    Import-GitLabNugetRepository @PSBoundParameters
                } catch {
                    throw "invalid file XML file path"
                }
            }
        } else {
            try {
                Export-Clixml -InputObject $global:GitLabNugetConfig -Path $XMLFile -Force
                return $global:GitLabNugetConfig
            } catch {
                throw "invalid file XML file path"
            }
        }
    }
}
Function Test-GitLabNugetRepository {
<#
    .SYNOPSIS
    Test if your GitLab Package Registry Source / Repository is correctly set locally
 
    .DESCRIPTION
    Test if your GitLab Package Registry Source / Repository is correctly set locally
         
    .OUTPUTS
       TypeName : System.Collections.Hashtable
         
    .EXAMPLE
    test your repository source configuration
    C:\PS> Test-GitLabNugetRepository
     
#>

    [cmdletbinding()]
    param()
    process {
        if (!($global:GitLabNugetConfig)) {
            throw "No GitLab Nuget Config loaded, please use Import-GitLabNugetRepository or New-GitLabNugetRepository first"
        } else {
            return $global:GitLabNugetConfig
        }
    }
}
Function Invoke-GitlabAPI {
    [cmdletbinding()]
    param(
        [parameter(Mandatory=$true)]
        [ValidateNotNullorempty()]
            [string]$SourceName,
        [parameter(Mandatory=$false)]
        [ValidateSet("download","metadata","query", "publish","delete")]
            [string]$NugetAPI,
        [parameter(Mandatory=$false)]
        [ValidateSet("list","files","metadata","delete")]
            [string]$PackageAPI,
        [parameter(Mandatory=$false)]
        [ValidateNotNullorempty()]
            [string]$PackageName,
        [parameter(Mandatory=$false)]
        [ValidateNotNullorempty()]
            [string]$PackageVersion,
        [parameter(Mandatory=$false)]
        [ValidateNotNullorempty()]
            [string]$OutputDirectory,
        [parameter(Mandatory=$false)]
        [ValidateNotNullorempty()]
            [string]$PackageID,
        [parameter(Mandatory=$false)]
        [ValidateNotNullorempty()]
            [string]$PackageSearch,
        [parameter(Mandatory=$false)]
        [ValidateNotNullorempty()]
            [string]$PackageFilePath
    )
    process {
        Test-GitLabNugetRepository | Out-Null
        $tempFilePath = [System.IO.Path]::GetTempFileName()
        $Repository = $global:GitLabNugetConfig | Where-Object {$_.RepositoryName -eq $SourceName}
        if (!($Repository)) {
            throw "repository not found, please use Import-GitLabNugetRepository or New-GitLabNugetRepository first"
        }
        $Headers = @{"PRIVATE-TOKEN" = $Repository.APIKey}
        $BaseGitLabURI = "https://$($Repository.server)/api/v4/projects/$($Repository.ProjectID)/packages"
        $params = @{
            "Headers" = $Headers
            "UserAgent" = "NuGet Command Line/5.8.0 (Microsoft Windows NT $([System.Environment]::OSVersion.Version.tostring()))"
            "UseBasicParsing" = $true
        }
        if ($NugetAPI) {
            switch ($NugetAPI) {
                "download" {
                    if (!($PackageName) -and !($PackageVersion) -and !($OutputDirectory)) {
                        throw "please use PackageName, PackageVersion, OutputDirectory parameters when using NugetAPI Download parameters"
                    }
                    if (!(test-path $OutputDirectory)) {
                        throw "please set an existing target folder as OutputDirectory when using NugetAPI Download parameters"
                    }
                    $URI = $BaseGitLabURI + "/nuget/download/$($PackageName)/$($PackageVersion)/$($PackageName).$($PackageVersion).nupkg"
                }
                "metadata" {
                    if (!($PackageName)) {
                        throw "please use PackageName parameter when using NugetAPI metadata parameter"
                    }
                    $URI = $BaseGitLabURI + "/nuget/metadata/$($PackageName)/index.json"
                }
                "query" {
                    if (!($PackageSearch)) {
                        throw "please use PackageSearch parameter when using NugetAPI query parameter"
                    }
                    $URI = $BaseGitLabURI + "/nuget/query?q=$([System.Web.HTTPUtility]::UrlEncode($PackageSearch))&prerelease=true"
                }
                "publish" {
                    if (!($PackageFilePath)) {
                        throw "please use PackageFilePath parameter when using NugetAPI publish parameter"
                    }
                    $URI = $BaseGitLabURI + "/nuget/"
                    $convertedinfo = Convert-GitLabNugetRepositoryPackage -PackageFilePath $PackageFilePath
                    $params.add("Infile",$convertedinfo.Infile)
                    $params.add("ContentType",$convertedinfo.contenttype)
                    $params.add("method","Put")
                    $Headers.add("Accept-Encoding","gzip, deflate")
                    $Headers.add("Authorization","Basic "+ [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes("$($Repository.APIKeyName):$($Repository.APIKey)")))
                }
                "delete" {
                    if (!($PackageID) -or !($PackageVersion)) {
                        throw "please use PackageID and PackageVersion parameters when using NugetAPI delete parameter"
                    }
                    $URI = $BaseGitLabURI + "/nuget/$($PackageID)/$($PackageVersion)"
                    $params.add("method","delete")
                }
            }
            $params.add("URI",$URI)
        } elseif ($PackageAPI) {
            switch ($PackageAPI) {
                "list" {
                    $URI = $BaseGitLabURI
                }
                "files" {
                    if (!($PackageID)) {
                        throw "please use PackageID parameter when using PackageAPI files parameter"
                    }
                    $URI = $BaseGitLabURI + "/$($PackageID)/package_files"
                }
                "metadata" {
                    if (!($PackageID)) {
                        throw "please use PackageID parameter when using PackageAPI metadata parameter"
                    }
                    $URI = $BaseGitLabURI + "/$($PackageID)"
                }
                "delete" {
                    if (!($PackageID)) {
                        throw "please use PackageID parameter when using PackageAPI metadata parameter"
                    }
                    $URI = $BaseGitLabURI + "/$($PackageID)"
                    $params.add("Method","Delete")
                }
            }
            $params.add("URI",$URI)
        }
        if ($OutputDirectory) {
            try {
                $result = Invoke-WebRequest -Uri $URI -Headers $Headers -UserAgent "NuGet Command Line/5.8.0 (Microsoft Windows NT $([System.Environment]::OSVersion.Version.tostring()))" -OutFile $tempFilePath
            } catch {
                write-error -message "Not able to connect to GitLab API"
                write-verbose -message "Error Type: $($_.Exception.GetType().FullName)"
                write-verbose -message "Error Message: $($_.Exception.Message)"
                write-verbose -message "HTTP error code:$($_.Exception.Response.StatusCode.Value__)"
                write-verbose -message "HTTP error message:$($_.Exception.Response.StatusDescription)"
            }
            if ($tempFilePath) {
                $newdirectory = "$($packagename).$($packageversion)"
                $newoutputdirectory = join-path $OutputDirectory $newdirectory
                if (!(test-path $newoutputdirectory)) {
                    new-item -ItemType Directory -Path $newoutputdirectory -Force | Out-Null
                }
                $ziptempfile = "$((get-item -path $tempFilePath).basename).zip"
                $ziptempfullpath = join-path (get-item $tempFilePath).Directory $ziptempfile
                move-item -path $tempFilePath -Destination $ziptempfullpath -Force | out-null
                expand-archive -Path $ziptempfullpath -DestinationPath $newoutputdirectory -Force | out-null
                Remove-Item -path $ziptempfullpath -Force | Out-Null
                if (test-path (join-path $newoutputdirectory "_rels")) {
                    remove-item -path (join-path $newoutputdirectory "_rels") -force -Recurse | out-null
                }
                if (test-path (join-path $newoutputdirectory "package")) {
                    remove-item -path (join-path $newoutputdirectory "package") -force -Recurse | out-null
                }
                if (test-path (join-path $newoutputdirectory "``[Content_Types``].xml")) {
                    remove-item -path (join-path $newoutputdirectory "``[Content_Types``].xml") -force | out-null
                }
            }
        } else {
            try {
                $result = Invoke-WebRequest @params
                if ($result.content) {
                    write-verbose -message $result.content
                }
            } catch {
                write-error -message "Not able to connect to GitLab API"
                write-verbose -message "Error Type: $($_.Exception.GetType().FullName)"
                write-verbose -message "Error Message: $($_.Exception.Message)"
                write-verbose -message "HTTP error code:$($_.Exception.Response.StatusCode.Value__)"
                write-verbose -message "HTTP error message:$($_.Exception.Response.StatusDescription)"
            }
            if ($result) {
                return ($result | ConvertFrom-Json)
            }
        }
    }
}
Function Get-GitLabNugetRepositoryPackage {
<#
    .SYNOPSIS
    Request package information, list packages available, download / install package from a GitLab Package Repository
 
    .DESCRIPTION
    Request package information, list packages available, download / install package from a GitLab Package Repository
     
    .PARAMETER Action
    -Action [string]
    Validate Set : "list","info","install"
    list : list all packages available from repository / package registry
    info : get information for a package and a version
    install : download / install locally a package in a specific version (last version by default)
     
    .PARAMETER PackageName
    -PackageName [string]
    name of the package
    To be used with Action parameter "info" and "install"
     
    .PARAMETER PackageSource
    -PackageSource [string]
    Name of your repository
 
    .PARAMETER PackageVersion
    -PackageVersion [switch]
    version of the package
    To be used with Action parameter "info" and "install"
 
    .PARAMETER OutputDirectory
    -OutputDirectory [string]
    install folder where package will be downloaded and extracted. If not set, current folder will be used
    To be used with Action parameter "install"
 
    .PARAMETER PackageSearchQuery
    -PackageSearchQuery [string]
    Nuget search query to find a package based on NuGet criteria.
    To be used with Action paramter "search"
    Nuget Search Query info : https://docs.microsoft.com/en-us/nuget/consume-packages/finding-and-choosing-packages#search-syntax
     
    .OUTPUTS
       TypeName : System.Collections.Hashtable
         
    .EXAMPLE
    Get-GitLabRepositoryPackage -Action list
    List all packages available from repository / package registry
     
    .EXAMPLE
    Get-GitLabRepositoryPackage -Action info -PackageName myPackage -PackageVersion 1.0.0
    Get information about a package in a specific version
     
    .EXAMPLE
    Get-GitLabRepositoryPackage -Action install -PackageName myPackage -PackageVersion 1.0.0 -OutputDirectory c:\PackageInstall
    Install a package in a specific version
 
    .EXAMPLE
    Get-GitLabRepositoryPackage -Action search -PackageSearchQuery Sysmon
    Search for a package with a name like sysmon
#>

    [cmdletbinding()]
    param(
        [parameter(Mandatory=$true,Position=0)]
        [ValidateSet("list","info","install","search")]
            [string]$Action,
        [parameter(Mandatory=$false,Position=1)]
        [ValidateNotNullOrEmpty()]
            [string]$PackageName,
        [parameter(Mandatory=$false,Position=3)]
        [ValidateNotNullOrEmpty()]
            [string]$PackageSource,
        [parameter(Mandatory=$false,Position=2)]
        [ValidateNotNullOrEmpty()]
            [string]$PackageVersion,
        [parameter(Mandatory=$false,Position=4)]
        [ValidateNotNullOrEmpty()]
            [string]$OutputDirectory,
        [parameter(Mandatory=$false,Position=5)]
        [ValidateNotNullOrEmpty()]
            [string]$PackageSearchQuery
    )
    process {
        Test-GitLabNugetRepository | out-null
        if (!($PackageSource) -and ($global:GitLabNugetConfig.count -gt 1)) {
            throw "Please provide a valid Package Source using PackageSource Parameter"
        } elseif (!($PackageSource) -and ($global:GitLabNugetConfig.count -eq 1)) {
            $PackageSource = $global:GitLabNugetConfig.repositoryname
        }
        if (!($PackageVersion) -and $PackageName) {
            $AllPackageversion = Invoke-GitlabAPI -SourceName $PackageSource -PackageAPI list | where-object {$_.name -eq $PackageName}
            $temppackageversion = $AllPackageversion[0].version
            $PackageVersion = (Invoke-GitlabAPI -SourceName $PackageSource -NugetAPI metadata -PackageName $PackageName -PackageVersion $temppackageversion).items.upper
            if (!($PackageVersion)) {
                throw "Not able to find your Package version. Please Provide a valid Package Name and Version using PackageName and PackageVersion parameters"
            }
        }
        if ($PackageName -and $PackageVersion) {
            $PackageID = (Invoke-GitlabAPI -SourceName $PackageSource -PackageAPI list | where-object {($_.name -eq $PackageName) -and ($_.version -eq $PackageVersion)}).id
            if (!($PackageID)) {
                throw "Not able to find your Package ID. Please Provide a valid Package Name and Version using PackageName and PackageVersion parameters"
            }
        }
        switch ($Action) {
            "list" {
                Invoke-GitlabAPI -SourceName $PackageSource -PackageAPI list
            }
            "info" {
                Invoke-GitlabAPI -SourceName $PackageSource -PackageAPI metadata -PackageID $PackageID -PackageVersion $PackageVersion
                Invoke-GitlabAPI -SourceName $PackageSource -PackageAPI files -PackageID $PackageID -PackageVersion $PackageVersion
            }
            "install" {
                if (!($OutputDirectory)) {
                    $OutputDirectory = Get-ScriptDirectory
                }
                Invoke-GitlabAPI -SourceName $PackageSource -NugetAPI download -PackageName $PackageName -PackageVersion $PackageVersion -OutputDirectory $OutputDirectory
            }
            "search" {
                if (!($PackageSearchQuery)) {
                    throw "Please use PackageSearchQuery parameter when using Search option of Action parameter"
                }
                $result = Invoke-GitlabAPI -SourceName $PackageSource -NugetAPI query -PackageSearch $PackageSearchQuery
                if ($result.totalHits -gt 0) {
                    $result.data
                }
            }
        }
    }
}
Function Convert-GitLabNugetRepositoryPackage {
    [cmdletbinding()]
    param(
        [parameter(Mandatory=$true)]
        [ValidateNotNullorempty()]
            [string]$PackageFilePath
    )
    process {
        if (!(test-path $PackageFilePath)) {
            throw "please provide a path to an existing nupkg file"
        }
        if ((get-item -Path $PackageFilePath).extension -ne ".nupkg") {
            throw "please provide a valid nupkg file"
        }
        $boundary = [Guid]::NewGuid().ToString()
        $bodyfileheader = @(
            "--$($boundary)",
            "Content-Type: application/octet-stream",
            "Content-Disposition: form-data; name=package; filename=package.nupkg; filename*=utf-8''package.nupkg",
            "`r`n"
        )
        $bodyfilefooter = "`r`n--$($boundary)--"
        $requestInFile = [System.IO.Path]::GetTempFileName()
        try {
            $fileStream = (New-Object -TypeName 'System.IO.FileStream' -ArgumentList ($requestInFile, [IO.FileMode]'Create', [IO.FileAccess]'Write'))
            $bytes = [Text.Encoding]::UTF8.GetBytes(($bodyfileheader -join "`r`n"))
            $fileStream.Write($bytes, 0, $bytes.Length)
            $bytes = [IO.File]::ReadAllBytes($PackageFilePath)
            $fileStream.Write($bytes, 0, $bytes.Length)
            $bytes = [Text.Encoding]::UTF8.GetBytes($bodyfilefooter)
            $fileStream.Write($bytes, 0, $bytes.Length)
        } catch {
            "not able to convert $($PackageFilePath) into a compliant 'multipart/form-data' with boundary $($boundary)"
        }
        $fileStream.Close()
        return @{
            "InFile" = $requestInFile
            "ContentType" = "multipart/form-data; boundary=`"{0}`"" -f $boundary
        }
    }
}
Function Publish-GitLabNugetRepositoryPackage {
<#
    .SYNOPSIS
    Publish a new NuGet Package to a GitLab Package Repository
 
    .DESCRIPTION
    Publish a new NuGet Package to a GitLab Package Repository
     
    .PARAMETER PackageFile
    -PackageFile [string]
    full file path of the NuGet package to publish
    File format : .nupkg
     
    .PARAMETER PackageSource
    -PackageSource [string]
    Name of your repository
     
    .OUTPUTS
       TypeName : System.Collections.Hashtable
         
    .EXAMPLE
    Publish-GitLabNugetRepositoryPackage -PackageFile C:\MyPackage\MyPackage.1.0.0.nupkg
    Publish Nuget Package C:\MyPackage\MyPackage.1.0.0.nupkg to the default Gitlab Package Registry
#>

    [cmdletbinding()]
    param(
        [parameter(Mandatory=$true,Position=1)]
        [ValidateNotNullOrEmpty()]
        [string]$PackageFile,
        [parameter(Mandatory=$false,Position=2)]
        [ValidateNotNullOrEmpty()]
        [string]$PackageSource
    )
    process {
        Test-GitLabNugetRepository | out-null
        if (!($PackageSource) -and ($global:GitLabNugetConfig.count -gt 1)) {
            throw "Please provide a valid Package Source using PackageSource Parameter"
        } elseif (!($PackageSource) -and ($global:GitLabNugetConfig.count -eq 1)) {
            $PackageSource = $global:GitLabNugetConfig.repositoryname
        }
        Invoke-GitlabAPI -SourceName $PackageSource -NugetAPI publish -PackageFilePath $PackageFile
    }
}
Function Remove-GitLabNugetRepositoryPackage {
<#
    .SYNOPSIS
    Remove a new NuGet Package from a GitLab Package Repository
 
    .DESCRIPTION
    Remove a new NuGet Package from a GitLab Package Repository
         
    .PARAMETER PackageName
    -PackageName [string]
    name of the package
     
    .PARAMETER PackageSource
    -PackageSource [string]
    Name of your repository
 
    .PARAMETER PackageVersion
    -PackageVersion [switch]
    version of the package
     
    .OUTPUTS
       TypeName : None
         
    .EXAMPLE
    Remove-GitLabNugetRepositoryPackage -PackageName MyPackage -PackageVersion 1.0.0
    Remove NuGet package MyPackage in version 1.0.0 from the default Gitlab Package Registry
#>

    [cmdletbinding()]
    param(
        [parameter(Mandatory=$true,Position=1)]
        [ValidateNotNullOrEmpty()]
        [string]$PackageName,
        [parameter(Mandatory=$true,Position=2)]
        [ValidateNotNullOrEmpty()]
        [string]$PackageVersion,
        [parameter(Mandatory=$false,Position=2)]
        [ValidateNotNullOrEmpty()]
        [string]$PackageSource
    )
    process {
        Test-GitLabNugetRepository | out-null
        if (!($PackageSource) -and ($global:GitLabNugetConfig.count -gt 1)) {
            throw "Please provide a valid Package Source using PackageSource Parameter"
        } elseif (!($PackageSource) -and ($global:GitLabNugetConfig.count -eq 1)) {
            $PackageSource = $global:GitLabNugetConfig.repositoryname
        }
        $AllPackages = Invoke-GitlabAPI -SourceName $PackageSource -PackageAPI list
        $PackagesRemoved = $AllPackages | Where-Object {($_.name -eq $PackageName) -and ($_.version -eq $PackageVersion)}
        if ($PackagesRemoved) {
            write-verbose -Message "Package Source : $($PackageSource)"
            write-verbose -Message "Package ID : $($PackagesRemoved.id)"
            Invoke-GitlabAPI -SourceName $PackageSource -PackageAPI delete -PackageID $PackagesRemoved.id
        } else {
            write-warning "No package found with Name $($PackageName) and Version $($PackageVersion)"
        }
    }
}
New-Alias -name nuget -Value Get-GitLabNugetRepositoryPackage

Export-ModuleMember -Function Import-GitLabNugetRepository, New-GitLabNugetRepository, Test-GitLabNugetRepository, Invoke-GitlabAPI, Get-GitLabNugetRepositoryPackage,
                                Convert-GitLabNugetRepositoryPackage, Publish-GitLabNugetRepositoryPackage, Remove-GitLabNugetRepositoryPackage, Export-GitLabNugetRepository, Clear-GitLabNugetRepository, 
                                Set-GitLabNugetRepository, Get-GitLabNugetRepository, Remove-GitLabNugetRepository
Export-ModuleMember -Alias nuget