functions/Employee/Get-PERSEmployee.ps1

function Get-PERSEmployee {
    <#
    .Synopsis
        Get-PERSEmployee
 
    .DESCRIPTION
        List employee(s) from Personio
        The result can be paginated and.
 
    .PARAMETER InputObject
        Employee to call again
        It is inclusive, so the result starts from and including the provided StartDate
 
    .PARAMETER Email
        Find an employee with the given email address
 
    .PARAMETER UpdatedSince
        Find all employees that have been updated since the provided date
        NOTE: when using UpdatedSince, the Resultsize parameter is ignored
 
    .PARAMETER Attributes
        Define a list of whitelisted attributes that shall be returned for all employees
 
    .PARAMETER EmployeeId
        A list of Personio employee ID's to retrieve
 
    .PARAMETER ResultSize
        How much records will be returned from the api.
        Default is 200.
 
        Use this parameter, when function throw information about pagination
 
    .PARAMETER Token
        AccessToken object for Personio service
 
    .EXAMPLE
        PS C:\> Get-PERSEmployee
 
        Get all available company employees
        (api-side-pagination may kick in at 200)
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSPersonio
    #>

    [CmdletBinding(
        DefaultParameterSetName = "Default",
        SupportsShouldProcess = $false,
        PositionalBinding = $true,
        ConfirmImpact = 'Low'
    )]
    Param(
        [Parameter(
            ParameterSetName = "Default",
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [string]
        $Email,

        [Parameter(ParameterSetName = "Default")]
        [datetime]
        $UpdatedSince,

        [Parameter(ParameterSetName = "Default")]
        [string[]]
        $Attributes,

        [Parameter(ParameterSetName = "Default")]
        [ValidateNotNullOrEmpty()]
        [int]
        $ResultSize,

        [Parameter(
            ParameterSetName = "ByType",
            Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [Alias("Id", "EmployeeId")]
        [Personio.Employee.BasicEmployee[]]
        $InputObject,

        [ValidateNotNullOrEmpty()]
        [Personio.Core.AccessToken]
        $Token
    )

    begin {
        # define script parameters
        $queryParameter = [ordered]@{}
        $typeNameBasic = "Personio.Employee.BasicEmployee"
        $typeNameExtended = "Personio.Employee.ExtendedEmployee"
        $memberNamesBasicEmployee = Expand-MemberNamesFromBasicObject -TypeName $typeNameBasic

        # fill query parameters
        if ($ResultSize) {
            $queryParameter.Add("limit", $ResultSize)
            $queryParameter.Add("offset", 0)
        }
        if ($UpdatedSince) { $queryParameter.Add("updated_since", (Get-Date -Date $UpdatedSince -Format "yyyy-MM-ddTHH:mm:ss")) }
        if ($attributes) { $queryParameter.Add("employees[]", $attributes) }
    }

    process {
        if (-not $MyInvocation.BoundParameters['Token']) { $Token = Get-AccessToken }
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)" -Tag "Employee"

        # fill pipedin query parameters
        if ($Email) { $queryParameter.Add("email", $Email) }


        # Prepare query
        $invokeParam = @{
            "Type"    = "GET"
            "ApiPath" = "company/employees"
            "Token"   = $Token
        }
        if ($queryParameter) { $invokeParam.Add("QueryParameter", $queryParameter) }


        # Execute query
        $responseList = [System.Collections.ArrayList]@()
        if ($parameterSetName -like "Default") {
            Write-PSFMessage -Level Verbose -Message "Getting available employees" -Tag "Employee", "Query"

            $response = Invoke-PERSRequest @invokeParam

            # Check respeonse and add to responeList
            if ($response.success) {
                $null = $responseList.Add($response)
            } else {
                Write-PSFMessage -Level Warning -Message "Personio api reported no data" -Tag "Employee", "Query"
            }
        } elseif ($parameterSetName -like "ByType") {

            foreach ($inputItem in $InputObject) {
                Write-PSFMessage -Level Verbose -Message "Getting employee Id $($inputItem.Id)" -Tag "Employee", "Query"

                $invokeParam.ApiPath = "company/employees/$($inputItem.Id)"
                $response = Invoke-PERSRequest @invokeParam


                # Check respeonse and add to responeList
                if ($response.success) {
                    $null = $responseList.Add($response)
                } else {
                    Write-PSFMessage -Level Warning -Message "Personio api reported no data on employee Id $($inputItem.Id)" -Tag "Employee", "Query"
                }


                # remove token param for further api calls, due to the fact, that the passed in token, is no more valid after previous api all (api will use internal registered token)
                $invokeParam.Remove("Token")
            }
        }
        Remove-Variable -Name response -Force -WhatIf:$false -Confirm:$false -Verbose:$false -Debug:$false -ErrorAction Ignore -WarningAction Ignore -InformationAction Ignore


        foreach ($response in $responseList) {
            # Check pagination / result limitation
            if ($response.metadata) {
                Write-PSFMessage -Level Significant -Message "Pagination detected! Retrieved records: $([Array]($response.data).count) of $($response.metadata.total_elements) total records (api call hast limit of $($response.limit) records and started on record number $($response.offset))" -Tag "Employee", "Query", "WebRequest", "Pagination"
            }


            # Process result
            $output = [System.Collections.ArrayList]@()

            foreach ($record in $response.data) {
                Write-PSFMessage -Level Debug -Message "Working on record Id $($record.attributes.id.value) name: $($record.attributes.first_name.value) $($record.attributes.last_name.value)" -Tag "Employee", "ObjectCreation"

                # Create object
                $result = New-Object -TypeName $typeNameBasic -Property @{
                    BaseObject = $record.attributes
                    Id         = $record.attributes.id.value
                    Name       = "$($record.attributes.last_name.value), $($record.attributes.first_name.value)"
                }
                $result.psobject.TypeNames.Insert(1, "Personio.Employee.$($record.type)")

                #region dynamic attribute checking and typeData Format generation
                $dynamicAttributeNames = $result.BaseObject.psobject.Properties.name | Where-Object { $_ -ne "id" -and $_ -NotIn $memberNamesBasicEmployee }
                $dynamicAttributes = $result.BaseObject.psobject.Members | Where-Object name -in $dynamicAttributeNames
                if ($dynamicAttributes) {
                    # Dynamic attributes found
                    Write-PSFMessage -Level Debug -Message "Employee with dynamic attribute ('$([string]::Join("', '", $dynamicAttributeNames))') detected, create [$($typeNameExtended)] object" -Tag "Employee", "ObjectCreation", "ExtendedEmployee", "DynamicProperty"

                    # Add sythetic type on top of employee object
                    $result.psobject.TypeNames.Insert(0, $typeNameExtended)

                    # Check synthetic typeData definition & compile the gathered dynamic properties if needed
                    $typeExtendedEmployee = Get-TypeData -TypeName $typeNameExtended
                    $_modified = $false

                    foreach ($dynamicAttr in $dynamicAttributes) {
                        #$label = $dynamicAttr.Value.label.split(" ") | ConvertTo-CamelCaseString
                        # get property name
                        $memberName = $dynamicAttr.Value.label.split(" ") | ConvertTo-CamelCaseString
                        if (-not ($memberName -in $typeExtendedEmployee.Members.Keys)) {
                            # dynamic property is new and currently not in TypeData defintion

                            # check if property is a direct value or a PSObject
                            if (($dynamicAttr.Value.value.psobject.TypeNames | Select-Object -First 1) -like "System.Management.Automation.PSCustomObject") {
                                if ($dynamicAttr.Value.value.attributes.psobject.Properties.name -like "value") {
                                    [scriptblock]$value = [scriptblock]::Create( "`$this.BaseObject.$($dynamicAttr.Name).attributes.value" )
                                } elseif ($dynamicAttr.Value.value.attributes.psobject.Properties.name -like "name") {
                                    [scriptblock]$value = [scriptblock]::Create( "`$this.BaseObject.$($dynamicAttr.Name).attributes.name" )
                                }
                            } else {
                                [scriptblock]$value = [scriptblock]::Create( "`$this.BaseObject.$($dynamicAttr.Name).value" )
                            }

                            Write-PSFMessage -Level Debug -Message "Add dynamic attribute '$($dynamicAttr.Name)' as property '$($memberName)' into TypeData for [$($typeNameExtended)]" -Tag "Employee", "ObjectCreation", "ExtendedEmployee", "DynamicProperty", "TypeData"
                            Update-TypeData -TypeName $typeNameExtended -MemberType ScriptProperty -MemberName $memberName -Value $value -Force

                            $_modified = $true
                        }
                    }

                    if ($_modified -or (-not (Get-FormatData -TypeName $typeNameExtended))) {
                        Write-PSFMessage -Level Verbose -Message "New dynamic attributes within employee detected. TypeData for [Personio.Employee.ExtendedEmployee] was modified. Going to compile FormatData" -Tag "Employee", "ObjectCreation", "ExtendedEmployee", "DynamicProperty", "FormatData"
                        $typeBasicEmployee = Get-TypeData -TypeName $typeNameBasic
                        $pathExtended = Join-Path -Path $env:TEMP -ChildPath "$($typeNameExtended).Format.ps1xml"

                        $properties = @( "Id", "Name")
                        $properties += $typeBasicEmployee.Members.Keys
                        $properties += $dynamicAttributes.Value.label | ForEach-Object { $_.split(" ") | ConvertTo-CamelCaseString } #$typeExtendedEmployee.Members.Keys
                        $properties = $properties | Where-Object { $_ -notlike 'SerializationData' }

                        New-PS1XML -Path $pathExtended -TypeName $typeNameExtended -PropertyList $properties -View Table, List -Encoding UTF8

                        Write-PSFMessage -Level System -Message "Update FormatData with file '$($pathExtended)'" -Tag "Employee", "ObjectCreation", "ExtendedEmployee", "DynamicProperty", "FormatData"
                        Update-FormatData -PrependPath $pathExtended
                    }
                }
                #endregion dynamic attribute checking and typeData Format generation

                # add objects to output array
                $null = $output.Add($result)
            }

            if ($output.Count -gt 1) {
                Write-PSFMessage -Level Verbose -Message "Retrieve $(([string]::Join(" & ", ($output | ForEach-Object { $_.psobject.TypeNames[0] } | Group-Object | ForEach-Object { "$($_.count) [$($_.Name)]" })))) objects" -Tag "Employee", "Result"
            } else {
                Write-PSFMessage -Level Verbose -Message "Retrieve $($output.Count) object$(if($output) { " [" + $output[0].psobject.TypeNames[0] + "]"})" -Tag "Employee", "Result"
            }


            # Filtering
            #ToDo: Implement filtering for record output


            # output final results
            Write-PSFMessage -Level Verbose -Message "Output $($output.Count) objects" -Tag "Employee", "Result", "Output"
            foreach ($item in $output) {
                $item
            }
        }


        # Cleanup variable
        Remove-Variable -Name Token -Force -WhatIf:$false -Confirm:$false -Verbose:$false -Debug:$false -ErrorAction Ignore -WarningAction Ignore -InformationAction Ignore
        $queryParameter.remove('email')
    }

    end {
    }
}