Varibill.SourceData.psm1

class SourceDataBase
{
    hidden [string] $apiUrl
    hidden [guid] $sourceCollectorKey
    hidden [xml] $sourceXml
    hidden [xml] $resultXml
    hidden [string] $path

    SourceDataBase()
    {
    }

    SourceDataBase([string]$apiUrl, [string]$sourceCollectorName, [guid]$sourceCollectorKey) 
    {   
        $this.apiUrl = $apiUrl
        $this.sourceCollectorKey = $sourceCollectorKey

        $fileOrPath = Join-Path -Path $global:HOME -ChildPath 'collectors'
        $fileOrPath = Join-Path -Path $fileOrPath -ChildPath $sourceCollectorKey
        $this.path = $fileOrPath
    }

    SaveSourceData()
    {
        $now = Get-Date -Format "yyyyMMdd_HHmmss_fff" 
        $now += '.xml'
        $file = Join-Path -Path $this.path -ChildPath $now
        $this.sourceXml.Save($file)
    }

    SaveSourceData([string] $file)
    {
        $this.sourceXml.Save($file)
    }

    [xml] GetSourceData()
    {
        return $this.resultXml
    }

    SaveResultXml([string] $fileOrPath)
    {
        if ($this.resultXml -ne $null)
        {
            $this.resultXml.Save($fileOrPath)
        }
    }

    [xml] GetResultXml()
    {
        return $this.resultXml
    }
}

class SourceData : SourceDataBase
{
    hidden $recordsNode

    SourceData([string]$apiUrl, [string]$sourceCollectorName, [guid]$sourceCollectorKey)  : base([string]$apiUrl, [string]$sourceCollectorName, [guid]$sourceCollectorKey)
    {
        $this.sourceXml = New-Object System.Xml.XmlDocument

        $prolog = $this.sourceXml.CreateXmlDeclaration("1.0", "ISO-8859-15", $null);

        $namespaceSoapenv = "http://schemas.xmlsoap.org/soap/envelope/";
        $namespaceTem = "http://tempuri.org/";
        $namespaceVar = "http://schemas.datacontract.org/2004/07/Varibill.WebService";

        $this.sourceXml.AppendChild($prolog) | Out-Null;

        #Build the Envelope node
        $envelopeNode = $this.sourceXml.CreateNode([System.Xml.XmlNodeType]::Element, "soapenv:Envelope", $namespaceSoapenv);
        $envelopeNode.SetAttribute("xmlns:var", $namespaceVar)
        $envelopeNode.SetAttribute("xmlns:tem", $namespaceTem)
        $this.sourceXml.AppendChild($envelopeNode) | Out-Null;

        #Build the Body node
        $bodyNode = $this.sourceXml.CreateNode([System.Xml.XmlNodeType]::Element, "soapenv:Body", $namespaceSoapenv);
        $envelopeNode.AppendChild($bodyNode) | Out-Null;

        #Build the ImportSourceData node
        $importSourceDataNode = $this.sourceXml.CreateNode([System.Xml.XmlNodeType]::Element, "tem:ImportSourceData", $namespaceTem);
        $bodyNode.AppendChild($importSourceDataNode) | Out-Null;

        # Build the Source node
        $sourceNode = $this.sourceXml.CreateNode([System.Xml.XmlNodeType]::Element, "tem:source", $namespaceTem);

            $element = $this.sourceXml.CreateElement("var:Key", $namespaceVar)
            $element.InnerText = $sourceCollectorKey;
            $sourceNode.AppendChild($element) | Out-Null;

            $element = $this.sourceXml.CreateElement("var:Name", $namespaceVar)
            $element.InnerText = $sourceCollectorName;
            $sourceNode.AppendChild($element) | Out-Null;

        $importSourceDataNode.AppendChild($sourceNode) | Out-Null;
        # End the Source Node

        #Build the Records node
        $this.recordsNode = $this.sourceXml.CreateNode([System.Xml.XmlNodeType]::Element, "tem:records", $namespaceTem);
        $importSourceDataNode.AppendChild($this.recordsNode) | Out-Null;
    }

    AddSourceDataRecord([string] $clientIdentifier, [datetime] $lastSeenDateTime, [string] $productCode, [decimal] $quantity, [string] $recordGUID, [string] $recordIdentifier)
    {
        # Build the Record node
        $namespaceVar = "http://schemas.datacontract.org/2004/07/Varibill.WebService";

        $recordNode = $this.sourceXml.CreateElement("var:Record", $namespaceVar);

        $element = $this.sourceXml.CreateElement("var:ClientIdentifier", $namespaceVar)
        $element.InnerText = $clientIdentifier
        $recordNode.AppendChild($element) | Out-Null;

        $element = $this.sourceXml.CreateElement("var:LastSeenDateTime", $namespaceVar)
        $element.InnerText = $lastSeenDateTime.ToString("o")
        $recordNode.AppendChild($element) | Out-Null;

        $element = $this.sourceXml.CreateElement("var:ProductCode", $namespaceVar)
        $element.InnerText = $productCode
        $recordNode.AppendChild($element) | Out-Null;

        $element = $this.sourceXml.CreateElement("var:Quantity", $namespaceVar)
        $element.InnerText = $quantity
        $recordNode.AppendChild($element) | Out-Null;

        $element = $this.sourceXml.CreateElement("var:RecordGUID", $namespaceVar)
        $element.InnerText = $recordGUID
        $recordNode.AppendChild($element) | Out-Null;

        $element = $this.sourceXml.CreateElement("var:RecordIdentifier", $namespaceVar)
        $element.InnerText = $recordIdentifier
        $recordNode.AppendChild($element) | Out-Null;

        $this.recordsNode.AppendChild($recordNode) | Out-Null;
        # End Record Node
    }

    [bool] PostSourceData()
    {
        $headers = @{"SOAPAction" = "http://tempuri.org/ISourceData/ImportSourceData"}

        $postXml = $this.sourceXml
        if($postXml.FirstChild.NodeType -eq 'XmlDeclaration')
        {
            $postXml.FirstChild.Encoding = $null
        }

        if ($global:PSVersionTable.PSVersion.Major -ge 7)
        {
            $this.resultXml = Invoke-WebRequest $this.apiUrl -Method post -ContentType 'text/xml' -Body $postXml.OuterXML -Headers $headers -RetryIntervalSec 5 -MaximumRetryCount 5
        }
        else
        {
            $this.resultXml = Invoke-WebRequest $this.apiUrl -Method post -ContentType 'text/xml' -Body $postXml.OuterXML -Headers $headers
        }

        $this.resultXml.GetElementsByTagName("a:ExitCode").innerXML

        return ($this.resultXml.GetElementsByTagName("a:ExitCode").innerXML -eq 0)
    }
}

class RatedSourceData : SourceDataBase
{
    hidden $ratedRecordsNode

    RatedSourceData([string]$apiUrl, [string]$sourceCollectorName, [guid]$sourceCollectorKey) : base([string]$apiUrl, [string]$sourceCollectorName, [guid]$sourceCollectorKey)
    {
        $this.sourceXml = New-Object System.Xml.XmlDocument

        $prolog = $this.sourceXml.CreateXmlDeclaration("1.0", "ISO-8859-15", $null);

        $namespaceSoapenv = "http://schemas.xmlsoap.org/soap/envelope/";
        $namespaceTem = "http://tempuri.org/";
        $namespaceVar = "http://schemas.datacontract.org/2004/07/Varibill.WebService";

        $this.sourceXml.AppendChild($prolog) | Out-Null;

        #Build the Envelope node
        $envelopeNode = $this.sourceXml.CreateNode([System.Xml.XmlNodeType]::Element, "soapenv:Envelope", $namespaceSoapenv);
        $envelopeNode.SetAttribute("xmlns:var", $namespaceVar)
        $envelopeNode.SetAttribute("xmlns:tem", $namespaceTem)
        $this.sourceXml.AppendChild($envelopeNode) | Out-Null;

        #Build the Body node
        $bodyNode = $this.sourceXml.CreateNode([System.Xml.XmlNodeType]::Element, "soapenv:Body", $namespaceSoapenv);
        $envelopeNode.AppendChild($bodyNode) | Out-Null;

        #Build the ImportSourceData node
        $importSourceDataNode = $this.sourceXml.CreateNode([System.Xml.XmlNodeType]::Element, "tem:ImportRatedSourceData", $namespaceTem);
        $bodyNode.AppendChild($importSourceDataNode) | Out-Null;

        # Build the Source node
        $sourceNode = $this.sourceXml.CreateNode([System.Xml.XmlNodeType]::Element, "tem:source", $namespaceTem);

            $element = $this.sourceXml.CreateElement("var:Key", $namespaceVar)
            $element.InnerText = $sourceCollectorKey;
            $sourceNode.AppendChild($element) | Out-Null;

            $element = $this.sourceXml.CreateElement("var:Name", $namespaceVar)
            $element.InnerText = $sourceCollectorName;
            $sourceNode.AppendChild($element) | Out-Null;

        $importSourceDataNode.AppendChild($sourceNode) | Out-Null;
        # End the Source Node

        #Build the Records node
        $this.ratedRecordsNode = $this.sourceXml.CreateNode([System.Xml.XmlNodeType]::Element, "tem:ratedRecords", $namespaceTem);
        $importSourceDataNode.AppendChild($this.ratedRecordsNode) | Out-Null;
    }

    AddSourceDataRecord([string] $clientIdentifier, [string] $productCode, [string] $recordGUID, [string] $recordIdentifier, [datetime] $lastSeenDateTime, [decimal] $quantity, [decimal] $total)
    {
        # Build the Record node
        $namespaceVar = "http://schemas.datacontract.org/2004/07/Varibill.WebService";

        $ratedRecordNode = $this.sourceXml.CreateElement("var:RatedRecord", $namespaceVar);

        $element = $this.sourceXml.CreateElement("var:ClientIdentifier", $namespaceVar)
        $element.InnerText = $clientIdentifier
        $ratedRecordNode.AppendChild($element) | Out-Null;

        $element = $this.sourceXml.CreateElement("var:LastSeenDateTime", $namespaceVar)
        $element.InnerText = $lastSeenDateTime.ToString("o")
        $ratedRecordNode.AppendChild($element) | Out-Null;

        $element = $this.sourceXml.CreateElement("var:ProductCode", $namespaceVar)
        $element.InnerText = $productCode
        $ratedRecordNode.AppendChild($element) | Out-Null;

         $element = $this.sourceXml.CreateElement("var:Quantity", $namespaceVar)
        $element.InnerText = $quantity
        $ratedRecordNode.AppendChild($element) | Out-Null;

        $element = $this.sourceXml.CreateElement("var:RecordGUID", $namespaceVar)
        $element.InnerText = $recordGUID
        $ratedRecordNode.AppendChild($element) | Out-Null;

        $element = $this.sourceXml.CreateElement("var:RecordIdentifier", $namespaceVar)
        $element.InnerText = $recordIdentifier
        $ratedRecordNode.AppendChild($element) | Out-Null;

        $element = $this.sourceXml.CreateElement("var:Total", $namespaceVar)
        $element.InnerText = $total
        $ratedRecordNode.AppendChild($element) | Out-Null;

        $this.ratedRecordsNode.AppendChild($ratedRecordNode) | Out-Null;
        # End Record Node
    }

    [bool] PostSourceData()
    {
        $headers = @{"SOAPAction" = "http://tempuri.org/IRatedSourceData/ImportRatedSourceData"}

        $postXml = $this.sourceXml
        if($postXml.FirstChild.NodeType -eq 'XmlDeclaration')
        {
            $postXml.FirstChild.Encoding = $null
        }

        if ($global:PSVersionTable.PSVersion.Major -ge 7)
        {
            $this.resultXml = Invoke-WebRequest $this.apiUrl -Method post -ContentType 'text/xml' -Body $postXml.OuterXML -Headers $headers -RetryIntervalSec 5 -MaximumRetryCount 5
        }
        else
        {
            $this.resultXml = Invoke-WebRequest $this.apiUrl -Method post -ContentType 'text/xml' -Body $postXml.OuterXML -Headers $headers
        }

        $this.resultXml.GetElementsByTagName("a:ExitCode").innerXML

        return ($this.resultXml.GetElementsByTagName("a:ExitCode").innerXML -eq 0)
    }
}

<#
.SYNOPSIS
The Varibill.SourceCollector module provides the framework so that unrated base data can be easily post to the Varibill web service for consumption.
 
.DESCRIPTION
Varibill collects data from various source systems for billing purposes. Some of this data is collected via Powershell, and needs to be posted to the
Varibill web service for billing purposes.
 
This module absctracts the creation and posting of the XML data so that the users can focus on the business requirements.
 
More information can be found at https://docs.varibill.com
 
.PARAMETER ApiUrl
The URL to the tenant specific API. The URL would follow the pattern of https://api-<COUNTRY>-<LOCATION>_<ID>.varibill.com/SourceData.svc
 
.PARAMETER SourceCollectorName
The name of the Source Collector as configured in Varibill.
 
.PARAMETER sourceCollectorKey
The key of the Source Collector as configured in Varibill.
 
.EXAMPLE
$sourceData = New-SourceData -ApiUrl 'https://api-<COUNTRY>-<LOCATION>_<ID>.varibill.com/SourceData.svc' -SourceCollectorName 'Source Collector Name' -sourceCollectorKey '00000000-0000-0000-0000-000000000000'
$sourceData.AddSourceDataRecord('Client Identifier', '2020-01-01 00:00:00', 'Product Code', 1.234, 'Unique Identifier', 'Record Identifier')
$sourceData.PostSourceData()
 
$ratedSourceData = New-RatedSourceData -ApiUrl 'https://api-<COUNTRY>-<LOCATION>_<ID>.varibill.com/SourceData.svc' -SourceCollectorName 'Source Collector Name' -sourceCollectorKey '00000000-0000-0000-0000-000000000000'
$ratedSourceData.AddSourceDataRecord('Client Identifier', 'Product Code', 'Unique Identifier', 'Record Identifier', '2020-01-01 00:00:00', 1.234, 9.876)
$ratedSourceData.PostSourceData()
 
.NOTES
General notes
#>

Function New-SourceData {
    [CmdletBinding(SupportsShouldProcess)]
    Param(
        [string] $ApiUrl,
        [string] $SourceCollectorName,
        [guid] $SourceCollectorKey
    )

    if ($PSCmdlet.ShouldProcess($SourceCollectorName))
    {
        [SourceData]::New($ApiUrl, $SourceCollectorName, $SourceCollectorKey)
    }
}

Function New-RatedSourceData {
    [CmdletBinding(SupportsShouldProcess)]
    Param(
        [string] $ApiUrl,
        [string] $SourceCollectorName,
        [guid] $SourceCollectorKey
    )

    if ($PSCmdlet.ShouldProcess($SourceCollectorName))
    {
        [RatedSourceData]::New($ApiUrl, $SourceCollectorName, $SourceCollectorKey)
    }
}

Export-ModuleMember -Function New-SourceData
Export-ModuleMember -Function New-RatedSourceData