Initial commit
This commit is contained in:
commit
b2afcf89bd
|
@ -0,0 +1,5 @@
|
|||
**/.vs
|
||||
**/bin
|
||||
**/obj
|
||||
/CloudEvents
|
||||
/CloudEvents.Sdk
|
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2021-Present The CloudEvents Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,213 @@
|
|||
# PowerShell 7.0 SDK for CloudEvents based on [.NET SDK for CloudEvents](https://github.com/cloudevents/sdk-csharp)
|
||||
|
||||
## Status
|
||||
|
||||
Supported CloudEvents versions:
|
||||
- v1.0
|
||||
|
||||
Supported Protocols:
|
||||
- HTTP
|
||||
|
||||
# **CloudEvents.Sdk** Module
|
||||
The module contains functions to
|
||||
- Create CloudEvent objects
|
||||
- Add data to a CloudEvent object
|
||||
- Read data from a CloudEvent object
|
||||
- Convert an CloudEvent object to an HTTP Message
|
||||
- Convert an HTTP Message to an CloudEvent object
|
||||
|
||||
## Producer
|
||||
### Create a CloudEvent object
|
||||
```powershell
|
||||
$cloudEvent = New-CloudEvent -Type 'com.example.object.deleted.v2' -Source 'mailto:cncf-wg-serverless@lists.cncf.io' -Id '6e8bc430-9c3a-11d9-9669-0800200c9a66' -Time (Get-Date)
|
||||
```
|
||||
|
||||
### Add **JSON Data** to a CloudEvent object
|
||||
```powershell
|
||||
$cloudEvent | Add-CloudEventJsonData -Data @{
|
||||
'key1' = 'value1'
|
||||
'key2' = @{
|
||||
'key3' = 'value3'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Add **XML Data** to a CloudEvent object
|
||||
```powershell
|
||||
$cloudEvent | Add-CloudEventXmlData -Data @{
|
||||
'key1' = @{
|
||||
'key2' = 'value'
|
||||
}
|
||||
} `
|
||||
-AttributesKeysInElementAttributes $true
|
||||
```
|
||||
`AttributesKeysInElementAttributes` specifies how to format the XML. If `true` and the input Data hashtable has pairs of 'Attributes', 'Value' keys creates XML element with attributes, otherwise each key is formatted as XML element.<br/>
|
||||
If `true`
|
||||
```powershell
|
||||
@{'root' = @{'Attributes' = @{'att1' = 'true'}; 'Value' = 'val-1'}}
|
||||
```
|
||||
is formatted as
|
||||
```xml
|
||||
<root att1="true">val-1</root>
|
||||
```
|
||||
If `false`
|
||||
```powershell
|
||||
@{'root' = @{'Attributes' = @{'att1' = 'true'}; 'Value' = 'val-1'}}
|
||||
```
|
||||
is formatted as
|
||||
```xml
|
||||
<root><Attributes><att1>true</att1></Attributes><Value>val-1</Value></root>
|
||||
```
|
||||
|
||||
#### Add Custom Format Data to a CloudEvent object
|
||||
```powershell
|
||||
$cloudEvent | Add-CloudEventData -DataContentType 'application/text' -Data 'wow'
|
||||
```
|
||||
|
||||
### Convert a CloudEvent object to an HTTP message in **Binary** or **Structured** content mode
|
||||
```powershell
|
||||
$cloudEventBinaryHttpMessage = $cloudEvent | ConvertTo-HttpMessage -ContentMode Binary
|
||||
$cloudEventStructuredHttpMessage = $cloudEvent | ConvertTo-HttpMessage -ContentMode Structured
|
||||
```
|
||||
|
||||
### Send CloudEvent object to HTTP server
|
||||
```powershell
|
||||
Invoke-WebRequest -Method POST -Uri 'http://my.cloudevents.server/' -Headers $cloudEventBinaryHttpMessage.Headers -Body $cloudEventBinaryHttpMessage.Body
|
||||
```
|
||||
|
||||
## Consumer
|
||||
### Convert an HTTP message to a CloudEvent object
|
||||
```powershell
|
||||
$cloudEvent = ConvertFrom-HttpMessage -Headers <headers> -Body <body>
|
||||
```
|
||||
|
||||
### Read CloudEvent **JSON Data** as a **PowerShell Hashtable**
|
||||
```powershell
|
||||
$hashtableData = Read-CloudEventJsonData -CloudEvent $cloudEvent
|
||||
```
|
||||
|
||||
### Read CloudEvent **XML Data** as a **PowerShell Hashtable**
|
||||
```powershell
|
||||
$hashtableData = Read-CloudEventXmlData -CloudEvent $cloudEvent -ConvertMode SkipAttributes
|
||||
```
|
||||
The `ConvertMode` parameter specifies how the XML to be represented in the result hashtable<br/>
|
||||
`SkipAttributes` - Skips attributes of the XML elements. XmlElement is represented as a Key-Value pair where key is the xml element name, and the value is the xml element inner text<br/>
|
||||
Example:
|
||||
```xml
|
||||
<key att='true'>value1</key>
|
||||
```
|
||||
is converted to
|
||||
```powershell
|
||||
@{'key' = 'value-1'}
|
||||
```
|
||||
`AlwaysAttrValue` - Each element is represented as a hashtable with two keys<br/>
|
||||
'Attributes' - key-value pair of the cml element attributes if any, otherwise null<br/>
|
||||
'Value' - string value represinting the xml element inner text<br/>
|
||||
Example:
|
||||
```xml
|
||||
```
|
||||
<key1 att='true'>value1</key1><key2>value2</key2>
|
||||
is converted to
|
||||
```powershell
|
||||
@{
|
||||
'key1' = @{
|
||||
'Attributes' = @{
|
||||
'att' = 'true'
|
||||
}
|
||||
'Value' = 'value1'
|
||||
}
|
||||
'key2' = @{
|
||||
'Attributes' = $null
|
||||
'Value' = 'value2'
|
||||
}
|
||||
}
|
||||
```
|
||||
`AttrValueWhenAttributes` - Uses `SkipAttributes` for xml elements without attributes and `AlwaysAttrValue` for xml elements with attributes<br/>
|
||||
Example:
|
||||
```xml
|
||||
<key1 att='true'>value1</key1><key2>value2</key2>
|
||||
```
|
||||
is converted to
|
||||
```powershell
|
||||
@{
|
||||
'key1' = @{
|
||||
'Attributes' = @{
|
||||
'att' = 'true'
|
||||
}
|
||||
'Value' = 'value1'
|
||||
}
|
||||
'key2' = 'value2'
|
||||
}
|
||||
```
|
||||
|
||||
### Read CloudEvent Custom Format **Data** as a **byte[]**
|
||||
```powershell
|
||||
$bytes = Read-CloudEventData -CloudEvent $cloudEvent
|
||||
```
|
||||
|
||||
# Build the **CloudEvents.Sdk** Module
|
||||
|
||||
The `build.ps1` script
|
||||
- Creates the CloudEvents PowerShell Module in a `CloudEvents` directory.
|
||||
- Runs functions unit tests
|
||||
- Runs local integrations tests
|
||||
- Creates a catalog file for the CloudEvents Module
|
||||
|
||||
### Prerequisites
|
||||
- [PowerShell 7.0](https://github.com/PowerShell/PowerShell/releases/tag/v7.0.4)
|
||||
- [Pester 5.1.1](https://www.powershellgallery.com/packages/Pester/5.1.1)
|
||||
- [dotnet SDK](https://dotnet.microsoft.com/download/dotnet/5.0)
|
||||
|
||||
```powershell
|
||||
> ./build.ps1
|
||||
[9:52:42 AM] INFO: Publish CloudEvents.Sdk Module to 'C:\git-repos\cloudevents\cloudevents-sdk-powershell\CloudEvents.Sdk'
|
||||
Microsoft (R) Build Engine version 16.8.3+39993bd9d for .NET
|
||||
Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
|
||||
Determining projects to restore...
|
||||
All projects are up-to-date for restore.
|
||||
CloudEventsPowerShell -> C:\git-repos\cloudevents\cloudevents-sdk-powershell\src\CloudEventsPowerShell\bin\Release\netstandard2.0\CloudEventsPowerShell.dll
|
||||
CloudEventsPowerShell -> C:\git-repos\cloudevents\cloudevents-sdk-powershell\CloudEvents.Sdk\
|
||||
[9:52:44 AM] INFO: Run unit tests
|
||||
|
||||
Starting discovery in 9 files.
|
||||
Discovery finished in 294ms.
|
||||
[+] C:\git-repos\cloudevents\cloudevents-sdk-powershell\test\unit\Add-CloudEventData.Tests.ps1 1.01s (184ms|656ms)
|
||||
[+] C:\git-repos\cloudevents\cloudevents-sdk-powershell\test\unit\Add-CloudEventJsonData.Tests.ps1 329ms (39ms|279ms) [+] C:\git-repos\cloudevents\cloudevents-sdk-powershell\test\unit\Add-CloudEventXmlData.Tests.ps1 336ms (58ms|267ms) [+] C:\git-repos\cloudevents\cloudevents-sdk-powershell\test\unit\ConvertFrom-HttpMessage.Tests.ps1 557ms (203ms|337ms) [+] C:\git-repos\cloudevents\cloudevents-sdk-powershell\test\unit\ConvertTo-HttpMessage.Tests.ps1 508ms (132ms|361ms) [+] C:\git-repos\cloudevents\cloudevents-sdk-powershell\test\unit\New-CloudEvent.Tests.ps1 275ms (22ms|243ms)
|
||||
[+] C:\git-repos\cloudevents\cloudevents-sdk-powershell\test\unit\Read-CloudEventData.Tests.ps1 257ms (10ms|236ms)
|
||||
[+] C:\git-repos\cloudevents\cloudevents-sdk-powershell\test\unit\Read-CloudEventJsonData.Tests.ps1 308ms (40ms|257ms)
|
||||
[+] C:\git-repos\cloudevents\cloudevents-sdk-powershell\test\unit\Read-CloudEventXmlData.Tests.ps1 310ms (53ms|246ms)
|
||||
Tests completed in 3.94s
|
||||
Tests Passed: 28, Failed: 0, Skipped: 0 NotRun: 0
|
||||
[9:52:49 AM] INFO: Run integration tests
|
||||
|
||||
Starting discovery in 1 files.
|
||||
Discovery finished in 176ms.
|
||||
[+] C:\git-repos\cloudevents\cloudevents-sdk-powershell\test\integration\HttpIntegration.Tests.ps1 2.54s (1.77s|617ms)
|
||||
Tests completed in 2.56s
|
||||
Tests Passed: 5, Failed: 0, Skipped: 0 NotRun: 0
|
||||
```
|
||||
|
||||
# Install **CloudEvents.Sdk** Module
|
||||
|
||||
```powershell
|
||||
$vmwareArtifactoryRepo = 'https://build-artifactory.eng.vmware.com/artifactory/api/nuget/powercli-nuget-local/'
|
||||
|
||||
Register-PSRepository -Name 'Artifactory' -SourceLocation $vmwareArtifactoryRepo -PublishLocation $vmwareArtifactoryRepo -InstallationPolicy Trusted
|
||||
|
||||
Install-Module CloudEvents.Sdk -Repository Artifactory
|
||||
Import-Module CloudEvents.Sdk
|
||||
Get-Command -Module CloudEvents.Sdk
|
||||
|
||||
ommandType Name Version Source
|
||||
----------- ---- ------- ------
|
||||
Function Add-CloudEventData 0.1.2 CloudEvents.Sdk
|
||||
Function Add-CloudEventJsonData 0.1.2 CloudEvents.Sdk
|
||||
Function Add-CloudEventXmlData 0.1.2 CloudEvents.Sdk
|
||||
Function ConvertFrom-HttpMessage 0.1.2 CloudEvents.Sdk
|
||||
Function ConvertTo-HttpMessage 0.1.2 CloudEvents.Sdk
|
||||
Function New-CloudEvent 0.1.2 CloudEvents.Sdk
|
||||
Function Read-CloudEventData 0.1.2 CloudEvents.Sdk
|
||||
Function Read-CloudEventJsonData 0.1.2 CloudEvents.Sdk
|
||||
Function Read-CloudEventXmlData 0.1.2 CloudEvents.Sdk
|
||||
```
|
|
@ -0,0 +1,96 @@
|
|||
param(
|
||||
[Parameter()]
|
||||
[string]
|
||||
$OutputDir
|
||||
)
|
||||
|
||||
$moduleName = 'CloudEvents.Sdk'
|
||||
|
||||
#region Input
|
||||
if (-not $OutputDir) {
|
||||
$OutputDir = $PSScriptRoot
|
||||
}
|
||||
|
||||
$OutputDir = Join-Path $OutputDir $moduleName
|
||||
|
||||
if (-not (Test-Path $OutputDir)) {
|
||||
New-Item -ItemType Directory -Path $OutputDir | Out-Null
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Helper Funcitons
|
||||
function Write-InfoLog($message) {
|
||||
$dt = (Get-Date).ToLongTimeString()
|
||||
Write-Host "[$dt] INFO: $message" -ForegroundColor Green
|
||||
}
|
||||
|
||||
function Write-ErrorLog($message) {
|
||||
$dt = (Get-Date).ToLongTimeString()
|
||||
Write-Host "[$dt] ERROR: $message" -ForegroundColor Red
|
||||
}
|
||||
|
||||
function Test-BuildToolsAreAvailable {
|
||||
$dotnetSdk = Get-Command 'dotnet'
|
||||
if (-not $dotnetSdk) {
|
||||
throw "'dotnet' sdk is not available"
|
||||
}
|
||||
}
|
||||
|
||||
function Start-Tests {
|
||||
param(
|
||||
[Parameter()]
|
||||
[ValidateSet('unit', 'integration', 'all')]
|
||||
[string]
|
||||
$TestsType
|
||||
)
|
||||
$pesterModule = Get-Module Pester -List
|
||||
if ($pesterModule -eq $null) {
|
||||
Write-ErrorLog "Pester Module is not available"
|
||||
} else {
|
||||
# Run Tests in external process because it will load build output binaries
|
||||
Write-InfoLog "Run $TestsType tests"
|
||||
$usePowerShell = (Get-Process -Id $pid).ProcessName
|
||||
|
||||
$testLauncherScript = Join-Path (Join-Path $PSScriptRoot 'test') 'RunTests.ps1'
|
||||
$CloudEventsModulePath = Join-Path $OutputDir "$moduleName.psd1"
|
||||
$testProcessArguments = "-Command $testLauncherScript -CloudEventsModulePath '$CloudEventsModulePath' -TestsType '$TestsType' -EnableProcessExit"
|
||||
|
||||
# Process Exit Code is 0 if all tests pass, otherwise it equals the number of failed tests
|
||||
$testProcess = Start-Process `
|
||||
-FilePath $usePowerShell `
|
||||
-ArgumentList $testProcessArguments `
|
||||
-PassThru `
|
||||
-NoNewWindow
|
||||
|
||||
$testProcess | Wait-Process
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
$dotnetProjectName = 'CloudEventsPowerShell'
|
||||
$dotnetProjectPath = Join-Path (Join-Path (Join-Path $PSScriptRoot 'src') $dotnetProjectName) "$dotnetProjectName.csproj"
|
||||
|
||||
# 1. Test dotnet command is available
|
||||
Test-BuildToolsAreAvailable
|
||||
|
||||
# 2. Publish CloudEvents Module
|
||||
Write-InfoLog "Publish CloudEvents.Sdk Module to '$OutputDir'"
|
||||
dotnet publish -c Release -o $OutputDir $dotnetProjectPath
|
||||
|
||||
# 3. Cleanup Unnecessary Outputs
|
||||
Get-ChildItem "$dotnetProjectName*" -Path $OutputDir | Remove-Item -Confirm:$false
|
||||
|
||||
# 4. Run Unit Tests
|
||||
Start-Tests -TestsType 'unit'
|
||||
|
||||
# 5. Run Integration Tests
|
||||
Start-Tests -TestsType 'integration'
|
||||
|
||||
# 6. Prepare Module for Publishing
|
||||
$dirItem = Get-Item $OutputDir
|
||||
$catalogFilePath = Join-path $OutputDir ($dirItem.Name + ".cat")
|
||||
if (Test-Path $catalogFilePath) {
|
||||
# Delete previous catalog file
|
||||
Remove-Item $catalogFilePath -Confirm:$false
|
||||
}
|
||||
New-FileCatalog -Path $OutputDir -CatalogFilePath $catalogFilePath | Out-Null
|
|
@ -0,0 +1,80 @@
|
|||
|
||||
@{
|
||||
|
||||
# Script module or binary module file associated with this manifest.
|
||||
RootModule = 'CloudEvents.Sdk.psm1'
|
||||
|
||||
# Version number of this module.
|
||||
ModuleVersion = '0.1.4'
|
||||
|
||||
# Supported PSEditions
|
||||
CompatiblePSEditions = @('Core')
|
||||
|
||||
# ID used to uniquely identify this module
|
||||
GUID = 'd0d7d392-0eab-40a8-8a3f-78ba41ef2f02'
|
||||
|
||||
# Author of this module
|
||||
Author = 'dmilov'
|
||||
|
||||
# Company or vendor of this module
|
||||
CompanyName = 'The CloudEvents Authors'
|
||||
|
||||
# Copyright statement for this module
|
||||
Copyright = '(c) The CloudEvents Authors
|
||||
|
||||
# Description of the functionality provided by this module
|
||||
Description = 'PowerShell CloudEvents SDK'
|
||||
|
||||
# Minimum version of the PowerShell engine required by this module
|
||||
PowerShellVersion = '7.0'
|
||||
|
||||
# Name of the PowerShell host required by this module
|
||||
# PowerShellHostName = ''
|
||||
|
||||
# Minimum version of the PowerShell host required by this module
|
||||
# PowerShellHostVersion = ''
|
||||
|
||||
# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
|
||||
# DotNetFrameworkVersion = ''
|
||||
|
||||
# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
|
||||
# ClrVersion = ''
|
||||
|
||||
# Processor architecture (None, X86, Amd64) required by this module
|
||||
# ProcessorArchitecture = ''
|
||||
|
||||
# Modules that must be imported into the global environment prior to importing this module
|
||||
# RequiredModules = @()
|
||||
|
||||
# Assemblies that must be loaded prior to importing this module
|
||||
RequiredAssemblies = @('CloudNative.CloudEvents.dll')
|
||||
|
||||
# Script files (.ps1) that are run in the caller's environment prior to importing this module.
|
||||
# ScriptsToProcess = @()
|
||||
|
||||
# Type files (.ps1xml) to be loaded when importing this module
|
||||
# TypesToProcess = @()
|
||||
|
||||
# Format files (.ps1xml) to be loaded when importing this module
|
||||
# FormatsToProcess = @()
|
||||
|
||||
# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
|
||||
# NestedModules = @()
|
||||
|
||||
# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
|
||||
FunctionsToExport = @(
|
||||
'New-CloudEvent', 'Add-CloudEventData', 'Add-CloudEventJsonData', 'Add-CloudEventXmlData', 'Read-CloudEventData', 'Read-CloudEventJsonData', 'Read-CloudEventXmlData', # CloudEvent Object Functions
|
||||
'ConvertTo-HttpMessage', 'ConvertFrom-HttpMessage' # Http Binding Functions
|
||||
)
|
||||
|
||||
# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
|
||||
CmdletsToExport = @()
|
||||
|
||||
# Variables to export from this module
|
||||
VariablesToExport = '*'
|
||||
|
||||
# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
|
||||
AliasesToExport = @()
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,780 @@
|
|||
$xmlDataSerilizationLibPath = Join-Path (Join-Path $PSScriptRoot 'dataserialization') 'xml.ps1'
|
||||
. $xmlDataSerilizationLibPath
|
||||
|
||||
function New-CloudEvent {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
This function creates a new cloud event.
|
||||
|
||||
|
||||
.DESCRIPTION
|
||||
This function creates a new cloud event object with the provided parameters.
|
||||
The result cloud event object has no data. Use Add-CloudEvent* functions to
|
||||
add data to the cloud event object.
|
||||
|
||||
.PARAMETER Type
|
||||
Specifies the 'type' attribute of the cloud event.
|
||||
|
||||
.PARAMETER Source
|
||||
Specifies the 'source' attribute of the cloud event.
|
||||
|
||||
.PARAMETER Id
|
||||
Specifies the 'id' attribute of the cloud event.
|
||||
|
||||
.PARAMETER Time
|
||||
Specifies the 'time' attribute of the cloud event.
|
||||
|
||||
.EXAMPLE
|
||||
New-CloudEvent -Type 'com.example.object.deleted.v2' -Source 'mailto:cncf-wg-serverless@lists.cncf.io' -Id '6e8bc430-9c3a-11d9-9669-0800200c9a66' -Time (Get-Date)
|
||||
|
||||
Creates a cloud event with Type, Source, Id, and Time
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]
|
||||
$Type,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[System.Uri]
|
||||
$Source,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]
|
||||
$Id,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[DateTime]
|
||||
$Time
|
||||
)
|
||||
|
||||
PROCESS {
|
||||
$cloudEvent = New-Object `
|
||||
-TypeName 'CloudNative.CloudEvents.CloudEvent' `
|
||||
-ArgumentList @(
|
||||
$Type,
|
||||
$Source,
|
||||
$Id,
|
||||
$Time,
|
||||
@())
|
||||
|
||||
Write-Output $cloudEvent
|
||||
}
|
||||
}
|
||||
|
||||
#region Add Data Functions
|
||||
function Add-CloudEventData {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
This function adds data to a cloud event.
|
||||
|
||||
.DESCRIPTION
|
||||
This function adds data to a cloud event object with the provided parameters.
|
||||
|
||||
.PARAMETER CloudEvent
|
||||
Specifies the cloud event object to add data to.
|
||||
|
||||
.PARAMETER Data
|
||||
Specifies the data object that is added to the cloud event 'data' attribute.
|
||||
|
||||
.PARAMETER DataContentType
|
||||
Specifies the 'datacontenttype' attribute of the cloud event.
|
||||
|
||||
|
||||
.EXAMPLE
|
||||
$cloudEvent = New-CloudEvent -Type 'com.example.object.deleted.v2' -Source 'mailto:cncf-wg-serverless@lists.cncf.io' -Id '6e8bc430-9c3a-11d9-9669-0800200c9a66' -Time (Get-Date)
|
||||
$cloudEvent | Add-CloudEventData -Data '<much wow="xml"/>' -DataContentType 'application/xml'
|
||||
|
||||
Adds xml data to the cloud event
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true,
|
||||
ValueFromPipeline = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[CloudNative.CloudEvents.CloudEvent]
|
||||
$CloudEvent,
|
||||
|
||||
[Parameter(Mandatory = $true,
|
||||
ValueFromPipeline = $false)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[object]
|
||||
$Data,
|
||||
|
||||
# CloudEvent 'datacontenttype' attribute. Content type of the 'data' attribute value.
|
||||
# This attribute enables the data attribute to carry any type of content, whereby
|
||||
# format and encoding might differ from that of the chosen event format.
|
||||
[Parameter(Mandatory = $false,
|
||||
ValueFromPipeline = $false)]
|
||||
[string]
|
||||
$DataContentType)
|
||||
|
||||
PROCESS {
|
||||
|
||||
# https://github.com/cloudevents/spec/blob/master/spec.md#datacontenttype
|
||||
$contentType = New-Object `
|
||||
-TypeName 'System.Net.Mime.ContentType' `
|
||||
-ArgumentList ($DataContentType)
|
||||
|
||||
$cloudEvent.Data = $Data
|
||||
$cloudEvent.DataContentType = $dataContentType
|
||||
|
||||
Write-Output $CloudEvent
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function Add-CloudEventJsonData {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
This function adds JSON format data to a cloud event.
|
||||
|
||||
.DESCRIPTION
|
||||
This function converts a PowerShell hashtable to JSON format data and adds it to a cloud event.
|
||||
|
||||
.PARAMETER CloudEvent
|
||||
Specifies the cloud event object to add data to.
|
||||
|
||||
.PARAMETER Data
|
||||
Specifies the PowerShell hashtable object that is added as JSON to the cloud event 'data' attribute.
|
||||
The 'datacontenttype' attribute is set to 'applicaiton/json'
|
||||
|
||||
|
||||
.EXAMPLE
|
||||
$cloudEvent = New-CloudEvent -Type 'com.example.object.deleted.v2' -Source 'mailto:cncf-wg-serverless@lists.cncf.io' -Id '6e8bc430-9c3a-11d9-9669-0800200c9a66' -Time (Get-Date)
|
||||
$cloudEvent | Add-CloudEventJsonData -Data @{ 'key1' = 'value1'; 'key2' = 'value2'; }
|
||||
|
||||
Adds JSON data to the cloud event
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true,
|
||||
ValueFromPipeline = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[CloudNative.CloudEvents.CloudEvent]
|
||||
$CloudEvent,
|
||||
|
||||
[Parameter(Mandatory = $true,
|
||||
ValueFromPipeline = $false)]
|
||||
[ValidateNotNull()]
|
||||
[Hashtable]
|
||||
$Data,
|
||||
|
||||
[Parameter(Mandatory = $false,
|
||||
ValueFromPipeline = $false)]
|
||||
[int]
|
||||
$Depth = 3)
|
||||
|
||||
PROCESS {
|
||||
|
||||
# DataContentType is set to 'application/json'
|
||||
# https://github.com/cloudevents/spec/blob/master/spec.md#datacontenttype
|
||||
$dataContentType = New-Object `
|
||||
-TypeName 'System.Net.Mime.ContentType' `
|
||||
-ArgumentList ([System.Net.Mime.MediaTypeNames+Application]::Json)
|
||||
|
||||
$cloudEvent.DataContentType = $dataContentType
|
||||
$cloudEvent.Data = ConvertTo-Json -InputObject $Data -Depth $Depth
|
||||
|
||||
Write-Output $CloudEvent
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function Add-CloudEventXmlData {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
This function adds XML format data to a cloud event.
|
||||
|
||||
.DESCRIPTION
|
||||
This function converts a PowerShell hashtable to XML format data and adds it to a cloud event.
|
||||
|
||||
.PARAMETER CloudEvent
|
||||
Specifies the cloud event object to add data to.
|
||||
|
||||
.PARAMETER Data
|
||||
Specifies the PowerShell hashtable object that is added as XML to the cloud event 'data' attribute.
|
||||
The 'datacontenttype' attribute is set to 'applicaiton/xml'
|
||||
|
||||
.PARAMETER AttributesKeysInElementAttributes
|
||||
Specifies how to format the XML. If specified and the input Data hashtable has pairs of 'Attributes', 'Value' keys
|
||||
creates XML element with attributes, otherwise each key is formatted as XML element.
|
||||
If true
|
||||
@{'root' = @{'Attributes' = @{'att1' = 'true'}; 'Value' = 'val-1'}} would be '<root att1="true">val-1</root>'
|
||||
Otherwise
|
||||
@{'root' = @{'Attributes' = @{'att1' = 'true'}; 'Value' = 'val-1'}} would be '<root><Attributes><att1>true</att1></Attributes><Value>val-1</Value></root>'
|
||||
|
||||
|
||||
.EXAMPLE
|
||||
$cloudEvent = New-CloudEvent -Type 'com.example.object.deleted.v2' -Source 'mailto:cncf-wg-serverless@lists.cncf.io' -Id '6e8bc430-9c3a-11d9-9669-0800200c9a66' -Time (Get-Date)
|
||||
$cloudEvent | Add-CloudEventXmlData -Data @{ 'key1' = 'value1'; 'key2' = 'value2'; } -AttributesKeysInElementAttributes $true
|
||||
|
||||
Adds XML data to the cloud event
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true,
|
||||
ValueFromPipeline = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[CloudNative.CloudEvents.CloudEvent]
|
||||
$CloudEvent,
|
||||
|
||||
[Parameter(Mandatory = $true,
|
||||
ValueFromPipeline = $false)]
|
||||
[ValidateNotNull()]
|
||||
[Hashtable]
|
||||
$Data,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[bool]
|
||||
$AttributesKeysInElementAttributes)
|
||||
|
||||
PROCESS {
|
||||
|
||||
# DataContentType is set to 'application/xml'
|
||||
$dataContentType = New-Object `
|
||||
-TypeName 'System.Net.Mime.ContentType' `
|
||||
-ArgumentList ([System.Net.Mime.MediaTypeNames+Application]::Xml)
|
||||
|
||||
$cloudEvent.DataContentType = $dataContentType
|
||||
$cloudEvent.Data = ConvertTo-CEDataXml -InputObject $Data -AttributesKeysInElementAttributes $AttributesKeysInElementAttributes
|
||||
|
||||
Write-Output $CloudEvent
|
||||
}
|
||||
|
||||
}
|
||||
#endregion Add Data Functions
|
||||
|
||||
#region Read Data Functions
|
||||
function Read-CloudEventData {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
This function gets the data from a cloud event.
|
||||
|
||||
.DESCRIPTION
|
||||
This function gets the data as-is from a cloud event. It is equiualent of accessing the Data property of a CloudEvent object
|
||||
|
||||
.PARAMETER CloudEvent
|
||||
Specifies the cloud event object to get data from.
|
||||
|
||||
.EXAMPLE
|
||||
$cloudEvent = ConvertFrom-HttpMessage -Headers $httpResponse.Headers -Body $httpResponse.Content
|
||||
$cloudEvent | Read-CloudEventData
|
||||
|
||||
Reads data from a cloud event received on the http response
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true,
|
||||
ValueFromPipeline = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[CloudNative.CloudEvents.CloudEvent]
|
||||
$CloudEvent
|
||||
)
|
||||
|
||||
PROCESS {
|
||||
Write-Output $CloudEvent.Data
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function Read-CloudEventJsonData {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
This function gets JSON fromat data from a cloud event as a PowerShell hashtable.
|
||||
|
||||
.DESCRIPTION
|
||||
This function gets the data from a cloud event and converts it to a PowerShell hashtable.
|
||||
If the cloud event datacontenttype is not 'application/json' nothing is returned.
|
||||
|
||||
.PARAMETER CloudEvent
|
||||
Specifies the cloud event object to get data from.
|
||||
|
||||
.PARAMETER Depth
|
||||
Specifies how many levels of contained objects are included in the JSON representation. The default value is 3.
|
||||
|
||||
.EXAMPLE
|
||||
$cloudEvent = ConvertFrom-HttpMessage -Headers $httpResponse.Headers -Body $httpResponse.Content
|
||||
$hashtable = $cloudEvent | Read-CloudEventJsonData
|
||||
|
||||
Reads JSON data as a hashtable from a cloud event received on the http response
|
||||
#>
|
||||
|
||||
|
||||
<#
|
||||
.DESCRIPTION
|
||||
Returns PowerShell hashtable that represents the CloudEvent Json Data
|
||||
if the data content type is 'application/json', otherwise otherwise non-terminating error and no result
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true,
|
||||
ValueFromPipeline = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[CloudNative.CloudEvents.CloudEvent]
|
||||
$CloudEvent,
|
||||
|
||||
[Parameter(Mandatory = $false,
|
||||
ValueFromPipeline = $false)]
|
||||
[int]
|
||||
$Depth = 3
|
||||
)
|
||||
|
||||
PROCESS {
|
||||
|
||||
# DataContentType is expected to be 'application/json'
|
||||
# https://github.com/cloudevents/spec/blob/master/spec.md#datacontenttype
|
||||
$dataContentType = New-Object `
|
||||
-TypeName 'System.Net.Mime.ContentType' `
|
||||
-ArgumentList ([System.Net.Mime.MediaTypeNames+Application]::Json)
|
||||
|
||||
if ($CloudEvent.DataContentType -eq $dataContentType -or `
|
||||
($CloudEvent.DataContentType -eq $null -and ` # Datacontent Type is Optional, if it is not specified we assume it is JSON as per https://github.com/cloudevents/spec/blob/v1.0.1/spec.md#datacontenttype
|
||||
$cloudEvent.Data -is [Newtonsoft.Json.Linq.JObject])) {
|
||||
|
||||
$data = $cloudEvent.Data
|
||||
|
||||
if ($cloudEvent.Data -is [byte[]]) {
|
||||
$data = [System.Text.Encoding]::UTF8.GetString($data)
|
||||
}
|
||||
|
||||
$result = $data.ToString() | ConvertFrom-Json -AsHashtable -Depth $Depth
|
||||
|
||||
Write-Output $result
|
||||
} else {
|
||||
Write-Error "Cloud Event '$($cloudEvent.Id)' has no json data"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function Read-CloudEventXmlData {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
This function gets XML fromat data from a cloud event as a PowerShell hashtable.
|
||||
|
||||
.DESCRIPTION
|
||||
This function gets the data from a cloud event and converts it to a PowerShell hashtable.
|
||||
If the cloud event datacontenttype is not 'application/xml' nothing is returned.
|
||||
|
||||
.PARAMETER CloudEvent
|
||||
Specifies the cloud event object to get data from.
|
||||
|
||||
.PARAMETER ConvertMode
|
||||
Specifies the how to convert the xml data to a hashtable
|
||||
'SkipAttributes' - Skips attributes of the XML elements. XmlElement is represented as a
|
||||
Key-Value pair where key is the xml element name, and the value is the xml element inner text
|
||||
|
||||
Example:
|
||||
"<key att='true'>value1</key>" is converted to
|
||||
@{'key' = 'value-1'}
|
||||
|
||||
'AlwaysAttrValue' - Each element is represented as a hashtable with two keys
|
||||
'Attributes' - key-value pair of the cml element attributes if any, otherwise null
|
||||
'Value' - string value represinting the xml element inner text
|
||||
|
||||
Example:
|
||||
"<key1 att='true'>value1</key1><key2>value2</key2>" is converted to
|
||||
@{
|
||||
'key1' = @{
|
||||
'Attributes' = @{
|
||||
'att' = 'true'
|
||||
}
|
||||
'Value' = 'value1'
|
||||
}
|
||||
'key2' = @{
|
||||
'Attributes' = $null
|
||||
'Value' = 'value2'
|
||||
}
|
||||
|
||||
}
|
||||
'AttrValueWhenAttributes' - Uses 'SkipAttributes' for xml elements without attributes and
|
||||
'AlwaysAttrValue' for xml elements with attributes
|
||||
Example:
|
||||
"<key1 att='true'>value1</key1><key2>value2</key2>" is converted to
|
||||
@{
|
||||
'key1' = @{
|
||||
'Attributes' = @{
|
||||
'att' = 'true'
|
||||
}
|
||||
'Value' = 'value1'
|
||||
}
|
||||
'key2' = 'value2'
|
||||
}
|
||||
|
||||
.EXAMPLE
|
||||
$cloudEvent = ConvertFrom-HttpMessage -Headers $httpResponse.Headers -Body $httpResponse.Content
|
||||
$hashtable = $cloudEvent | Read-CloudEventXmlData -ConvertMode AttrValueWhenAttributes
|
||||
|
||||
Reads XML data as a hashtable from a cloud event received on the http response
|
||||
#>
|
||||
|
||||
|
||||
<#
|
||||
.DESCRIPTION
|
||||
Returns PowerShell hashtable that represents the CloudEvent Xml Data
|
||||
if the data content type is 'application/xml', otherwise non-terminating error and no result
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true,
|
||||
ValueFromPipeline = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[CloudNative.CloudEvents.CloudEvent]
|
||||
$CloudEvent,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateSet("SkipAttributes", "AlwaysAttrValue", "AttrValueWhenAttributes")]
|
||||
[string]
|
||||
$ConvertMode
|
||||
)
|
||||
|
||||
PROCESS {
|
||||
|
||||
# DataContentType is expected to be 'application/xml'
|
||||
$dataContentType = New-Object `
|
||||
-TypeName 'System.Net.Mime.ContentType' `
|
||||
-ArgumentList ([System.Net.Mime.MediaTypeNames+Application]::Xml)
|
||||
|
||||
if ($CloudEvent.DataContentType -eq $dataContentType) {
|
||||
|
||||
$data = $cloudEvent.Data
|
||||
|
||||
if ($cloudEvent.Data -is [byte[]]) {
|
||||
$data = [System.Text.Encoding]::UTF8.GetString($data)
|
||||
}
|
||||
|
||||
$result = $data.ToString() | ConvertFrom-CEDataXml -ConvertMode $ConvertMode
|
||||
|
||||
Write-Output $result
|
||||
} else {
|
||||
Write-Error "Cloud Event '$($cloudEvent.Id)' has no xml data"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endregion Read Data Functions
|
||||
|
||||
#region HTTP Protocol Binding Conversion Functions
|
||||
function ConvertTo-HttpMessage {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
This function converts a cloud event object to a Http Message.
|
||||
|
||||
.DESCRIPTION
|
||||
This function converts a cloud event object to a PSObject with Headers and Body properties.
|
||||
The 'Headers' propery is a hashtable that can pe provided to the 'Headers' parameter of the Inveok-WebRequest cmdlet.
|
||||
The 'Body' propery is byte[] that can pe provided to the 'Body' parameter of the Inveok-WebRequest cmdlet.
|
||||
|
||||
.PARAMETER CloudEvent
|
||||
Specifies the cloud event object to convert.
|
||||
|
||||
.PARAMETER ContentMode
|
||||
Specifies the cloud event content mode. Structured and Binary content modes are supporterd.
|
||||
|
||||
.EXAMPLE
|
||||
$cloudEvent = New-CloudEvent -Type 'com.example.object.deleted.v2' -Source 'mailto:cncf-wg-serverless@lists.cncf.io' -Id '6e8bc430-9c3a-11d9-9669-0800200c9a66' -Time (Get-Date)
|
||||
$cloudEvent | Add-CloudEventJsonData -Data @{ 'key1' = 'value1'; 'key2' = 'value2'; }
|
||||
|
||||
$cloudEvent | ConvertTo-HttpMessage -ContentMode Binary
|
||||
|
||||
Converts a cloud event object to Headers and Body formatted in Binary content mode.
|
||||
|
||||
.EXAMPLE
|
||||
$cloudEvent = New-CloudEvent -Type 'com.example.object.deleted.v2' -Source 'mailto:cncf-wg-serverless@lists.cncf.io' -Id '6e8bc430-9c3a-11d9-9669-0800200c9a66' -Time (Get-Date)
|
||||
$cloudEvent | Add-CloudEventJsonData -Data @{ 'key1' = 'value1'; 'key2' = 'value2'; }
|
||||
|
||||
$cloudEvent | ConvertTo-HttpMessage -ContentMode Structured
|
||||
|
||||
Converts a cloud event object to Headers and Body formatted in Structured content mode.
|
||||
|
||||
.EXAMPLE
|
||||
$httpMessage = New-CloudEvent -Type 'com.example.object.deleted.v2' -Source 'mailto:cncf-wg-serverless@lists.cncf.io' -Id '6e8bc430-9c3a-11d9-9669-0800200c9a66' -Time (Get-Date) | `
|
||||
Add-CloudEventJsonData -Data @{ 'key1' = 'value1'; 'key2' = 'value2'; } | `
|
||||
ConvertTo-HttpMessage -ContentMode Structured
|
||||
|
||||
Invoke-WebRequest -Uri 'http://localhost:52673/' -Headers $httpMessage.Headers -Body $httpMessage.Body
|
||||
|
||||
Sends a cloud event http requests to a server
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(
|
||||
Mandatory = $true,
|
||||
ValueFromPipeline = $true,
|
||||
ValueFromPipelineByPropertyName = $false)]
|
||||
[ValidateNotNull()]
|
||||
[CloudNative.CloudEvents.CloudEvent]
|
||||
$CloudEvent,
|
||||
|
||||
[Parameter(
|
||||
Mandatory = $true,
|
||||
ValueFromPipeline = $false,
|
||||
ValueFromPipelineByPropertyName = $false)]
|
||||
[CloudNative.CloudEvents.ContentMode]
|
||||
$ContentMode)
|
||||
|
||||
PROCESS {
|
||||
# Output Object
|
||||
$result = New-Object -TypeName PSCustomObject
|
||||
|
||||
$cloudEventFormatter = New-Object 'CloudNative.CloudEvents.JsonEventFormatter'
|
||||
|
||||
$HttpHeaderPrefix = "ce-";
|
||||
$SpecVersionHttpHeader1 = $HttpHeaderPrefix + "cloudEventsVersion";
|
||||
$SpecVersionHttpHeader2 = $HttpHeaderPrefix + "specversion";
|
||||
|
||||
$headers = @{}
|
||||
|
||||
# Build HTTP headers
|
||||
foreach ($attribute in $cloudEvent.GetAttributes()) {
|
||||
if (-not $attribute.Key.Equals([CloudNative.CloudEvents.CloudEventAttributes]::DataAttributeName($cloudEvent.SpecVersion)) -and `
|
||||
-not $attribute.Key.Equals([CloudNative.CloudEvents.CloudEventAttributes]::DataContentTypeAttributeName($cloudEvent.SpecVersion))) {
|
||||
if ($attribute.Value -is [string]) {
|
||||
$headers.Add(($HttpHeaderPrefix + $attribute.Key), $attribute.Value.ToString())
|
||||
}
|
||||
elseif ($attribute.Value -is [DateTime]) {
|
||||
$headers.Add(($HttpHeaderPrefix + $attribute.Key), $attribute.Value.ToString("u"))
|
||||
}
|
||||
elseif ($attribute.Value -is [Uri] -or $attribute.Value -is [int]) {
|
||||
$headers.Add(($HttpHeaderPrefix + $attribute.Key), $attribute.Value.ToString())
|
||||
}
|
||||
else
|
||||
{
|
||||
$headers.Add(($HttpHeaderPrefix + $attribute.Key),
|
||||
[System.Text.Encoding]::UTF8.GetString($cloudEventFormatter.EncodeAttribute($cloudEvent.SpecVersion, $attribute.Key,
|
||||
$attribute.Value,
|
||||
$cloudEvent.Extensions.Values)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Add Headers property to the output object
|
||||
$result | Add-Member -MemberType NoteProperty -Name 'Headers' -Value $headers
|
||||
|
||||
# Process Structured Mode
|
||||
# Structured Mode supports non-batching JSON format only
|
||||
# https://github.com/cloudevents/spec/blob/v1.0.1/http-protocol-binding.md#14-event-formats
|
||||
if ($ContentMode -eq [CloudNative.CloudEvents.ContentMode]::Structured) {
|
||||
# Format Body as byte[]
|
||||
$contentType = $null
|
||||
|
||||
# CloudEventFormatter is instance of 'CloudNative.CloudEvents.JsonEventFormatter' from the
|
||||
# .NET CloudEvents SDK for the purpose of fomatting structured mode
|
||||
$buffer = $cloudEventFormatter.EncodeStructuredEvent($cloudEvent, [ref] $contentType)
|
||||
$result | Add-Member -MemberType NoteProperty -Name 'Body' -Value $buffer
|
||||
$result.Headers.Add('Content-Type', $contentType)
|
||||
}
|
||||
|
||||
# Process Binary Mode
|
||||
if ($ContentMode -eq [CloudNative.CloudEvents.ContentMode]::Binary) {
|
||||
$bodyData = $null
|
||||
|
||||
if ($cloudEvent.DataContentType -ne $null) {
|
||||
$result.Headers.Add('Content-Type', $cloudEvent.DataContentType)
|
||||
}
|
||||
|
||||
if ($cloudEvent.Data -is [byte[]]) {
|
||||
$bodyData = $cloudEvent.Data
|
||||
}
|
||||
elseif ($cloudEvent.Data -is [string]) {
|
||||
$bodyData = [System.Text.Encoding]::UTF8.GetBytes($cloudEvent.Data.ToString())
|
||||
}
|
||||
elseif ($cloudEvent.Data -is [IO.Stream]) {
|
||||
$buffer = New-Object 'byte[]' -ArgumentList 1024
|
||||
|
||||
$ms = New-Object 'IO.MemoryStream'
|
||||
|
||||
try {
|
||||
$read = 0
|
||||
while (($read = $cloudEvent.Data.Read($buffer, 0, 1024)) -gt 0)
|
||||
{
|
||||
$ms.Write($buffer, 0, $read);
|
||||
}
|
||||
$bodyData = $ms.ToArray()
|
||||
} finally {
|
||||
$ms.Dispose()
|
||||
}
|
||||
|
||||
} else {
|
||||
$bodyData = $cloudEventFormatter.EncodeAttribute($cloudEvent.SpecVersion,
|
||||
[CloudNative.CloudEvents.CloudEventAttributes]::DataAttributeName($cloudEvent.SpecVersion),
|
||||
$cloudEvent.Data, $cloudEvent.Extensions.Values)
|
||||
}
|
||||
|
||||
# Add Body property to the output object
|
||||
$result | Add-Member -MemberType NoteProperty -Name 'Body' -Value $bodyData
|
||||
}
|
||||
|
||||
Write-Output $result
|
||||
}
|
||||
}
|
||||
|
||||
function ConvertFrom-HttpMessage {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
This function converts a Http Message to a cloud event object
|
||||
|
||||
.DESCRIPTION
|
||||
This function converts a Http Message (Headers and Body) to a cloud event object.
|
||||
Result of Invoke-WebRequest that contains a cloud event can be passed as input to this
|
||||
function binding the the 'Headers' and 'Content' properties to the 'Headers' and 'Body' paramters.
|
||||
|
||||
.PARAMETER Headers
|
||||
Specifies the Http Headers as a PowerShell hashtable.
|
||||
|
||||
.PARAMETER Body
|
||||
Specifies the Http body as string or byte[].
|
||||
|
||||
.EXAMPLE
|
||||
$httpReponse = Invoke-WebRequest -Uri 'http://localhost:52673/' -Headers $httpMessage.Headers -Body $httpMessage.Body
|
||||
$cloudEvent = ConvertFrom-HttpMessage -Headers $httpResponse.Headers -Body $httpResponse.Content
|
||||
|
||||
Converts a http response to a cloud event object
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(
|
||||
Mandatory = $true,
|
||||
ValueFromPipeline = $false,
|
||||
ValueFromPipelineByPropertyName = $false)]
|
||||
[ValidateNotNull()]
|
||||
[hashtable]
|
||||
$Headers,
|
||||
|
||||
[Parameter(
|
||||
Mandatory = $false,
|
||||
ValueFromPipeline = $false,
|
||||
ValueFromPipelineByPropertyName = $false)]
|
||||
[ValidateNotNull()]
|
||||
$Body)
|
||||
|
||||
PROCESS {
|
||||
$HttpHeaderPrefix = "ce-";
|
||||
$SpecVersionHttpHeader1 = $HttpHeaderPrefix + "cloudEventsVersion";
|
||||
$SpecVersionHttpHeader2 = $HttpHeaderPrefix + "specversion";
|
||||
|
||||
$result = $null
|
||||
|
||||
# Always Convert Body to byte[]
|
||||
# Conversion works with byte[] while
|
||||
# body can be string in HTTP responses
|
||||
# for text content type
|
||||
if ($Body -is [string]) {
|
||||
$Body = [System.Text.Encoding]::UTF8.GetBytes($Body)
|
||||
}
|
||||
|
||||
if ($Headers['Content-Type'] -ne $null) {
|
||||
$ContentType = $Headers['Content-Type']
|
||||
if ($ContentType -is [array]) {
|
||||
# Get the first content-type value
|
||||
$ContentType = $ContentType[0]
|
||||
}
|
||||
|
||||
if ($ContentType.StartsWith([CloudNative.CloudEvents.CloudEvent]::MediaType,
|
||||
[StringComparison]::InvariantCultureIgnoreCase)) {
|
||||
|
||||
# Handle Structured Mode
|
||||
$ctParts = $ContentType.Split(';')
|
||||
if ($ctParts[0].Trim().StartsWith(([CloudNative.CloudEvents.CloudEvent]::MediaType) + ([CloudNative.CloudEvents.JsonEventFormatter]::MediaTypeSuffix),
|
||||
[StringComparison]::InvariantCultureIgnoreCase)) {
|
||||
|
||||
# Structured Mode supports non-batching JSON format only
|
||||
# https://github.com/cloudevents/spec/blob/v1.0.1/http-protocol-binding.md#14-event-formats
|
||||
|
||||
# .NET SDK 'CloudNative.CloudEvents.JsonEventFormatter' type is used
|
||||
# to decode the Structured Mode CloudEvents
|
||||
|
||||
$json = [System.Text.Encoding]::UTF8.GetString($Body)
|
||||
$jObject = [Newtonsoft.Json.Linq.JObject]::Parse($json)
|
||||
$formatter = New-Object 'CloudNative.CloudEvents.JsonEventFormatter'
|
||||
$result = $formatter.DecodeJObject($jObject, $null)
|
||||
|
||||
$result.Data = $result.Data
|
||||
} else {
|
||||
# Throw error for unsupported encoding
|
||||
throw "Unsupported CloudEvents encoding"
|
||||
}
|
||||
} else {
|
||||
# Handle Binary Mode
|
||||
$version = [CloudNative.CloudEvents.CloudEventsSpecVersion]::Default
|
||||
if ($Headers.Contains($SpecVersionHttpHeader1)) {
|
||||
$version = [CloudNative.CloudEvents.CloudEventsSpecVersion]::V0_1
|
||||
}
|
||||
|
||||
if ($Headers.Contains($SpecVersionHttpHeader2)) {
|
||||
if ($Headers[$SpecVersionHttpHeader2][0] -eq "0.2") {
|
||||
$version = [CloudNative.CloudEvents.CloudEventsSpecVersion]::V0_2
|
||||
} elseif ($Headers[$SpecVersionHttpHeader2][0] -eq "0.3") {
|
||||
$version = [CloudNative.CloudEvents.CloudEventsSpecVersion]::V0_3
|
||||
}
|
||||
}
|
||||
|
||||
$cloudEvent = New-Object `
|
||||
-TypeName 'CloudNative.CloudEvents.CloudEvent' `
|
||||
-ArgumentList @($version, $null);
|
||||
|
||||
$attributes = $cloudEvent.GetAttributes();
|
||||
|
||||
# Get attributes from HTTP Headers
|
||||
foreach ($httpHeader in $Headers.GetEnumerator()) {
|
||||
if ($httpHeader.Key.Equals($SpecVersionHttpHeader1, [StringComparison]::InvariantCultureIgnoreCase) -or `
|
||||
$httpHeader.Key.Equals($SpecVersionHttpHeader2, [StringComparison]::InvariantCultureIgnoreCase)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if ($httpHeader.Key.StartsWith($HttpHeaderPrefix, [StringComparison]::InvariantCultureIgnoreCase)) {
|
||||
$headerValue = $httpHeader.Value
|
||||
if ($headerValue -is [array]) {
|
||||
# Get the first object
|
||||
$headerValue = $headerValue[0]
|
||||
}
|
||||
$name = $httpHeader.Key.Substring(3);
|
||||
|
||||
# Abolished structures in headers in 1.0
|
||||
if ($version -ne [CloudNative.CloudEvents.CloudEventsSpecVersion]::V0_1 -and `
|
||||
$headerValue -ne $null -and `
|
||||
$headerValue.StartsWith('"') -and `
|
||||
$headerValue.EndsWith('"') -or `
|
||||
$headerValue.StartsWith("'") -and $headerValue.EndsWith("'") -or `
|
||||
$headerValue.StartsWith("{") -and $headerValue.EndsWith("}") -or `
|
||||
$headerValue.StartsWith("[") -and $headerValue.EndsWith("]")) {
|
||||
|
||||
$jsonFormatter = New-Object 'CloudNative.CloudEvents.JsonEventFormatter'
|
||||
|
||||
$attributes[$name] = $jsonFormatter.DecodeAttribute($version, $name,
|
||||
[System.Text.Encoding]::UTF8.GetBytes($headerValue), $null);
|
||||
} else {
|
||||
$attributes[$name] = $headerValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($Headers['Content-Type'] -ne $null -and $Headers['Content-Type'][0] -is [string]) {
|
||||
$cloudEvent.DataContentType = New-Object 'System.Net.Mime.ContentType' -ArgumentList @($Headers['Content-Type'][0])
|
||||
}
|
||||
|
||||
# Get Data from HTTP Body
|
||||
$cloudEvent.Data = $Body
|
||||
|
||||
$result = $cloudEvent
|
||||
}
|
||||
}
|
||||
|
||||
Write-Output $result
|
||||
}
|
||||
}
|
||||
#endregion HTTP Protocol Binding Conversion Functions
|
|
@ -0,0 +1,26 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<RootNamespace>CloudEventsPowerShell</RootNamespace>
|
||||
<AssemblyName>CloudEventsPowerShell</AssemblyName>
|
||||
<Description>CloudEvents PowerShell SDK.</Description>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CloudNative.CloudEvents" Version="1.3.80" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="CloudEvents.Sdk.psd1">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="CloudEvents.Sdk.psm1">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="dataserialization/xml.ps1">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,363 @@
|
|||
BeforeAll {
|
||||
. $PSCommandPath.Replace('.Tests.ps1', '.ps1')
|
||||
}
|
||||
|
||||
Describe "CloudEvent Xml Serializers Unit Tests" {
|
||||
Context "ConvertFrom-CEDataXml" {
|
||||
It "Should convert single element XML text to a hashtable" {
|
||||
# Arrange
|
||||
$inputXml = "<key>value</key>"
|
||||
$expected = @{'key' = 'value'}
|
||||
|
||||
# Act
|
||||
$actual = ConvertFrom-CEDataXml -InputString $inputXml -ConvertMode SkipAttributes
|
||||
|
||||
# Assert
|
||||
$actual | Should -Not -Be $null
|
||||
$actual -is [hashtable] | Should -Be $true
|
||||
$actual.Keys.Count | Should -Be $expected.Keys.Count
|
||||
$actual.key | Should -Be $expected.key
|
||||
}
|
||||
|
||||
It "Should convert XML with array of nodes to a hashtable" {
|
||||
# Arrange
|
||||
$inputXml = "<keys><key>value1</key><key>value2</key><key>value3</key></keys>"
|
||||
$expected = @{'keys' = @{'key' = @('value1', 'value2', 'value3')}}
|
||||
|
||||
# Act
|
||||
$actual = ConvertFrom-CEDataXml -InputString $inputXml -ConvertMode SkipAttributes
|
||||
|
||||
# Assert
|
||||
$actual | Should -Not -Be $null
|
||||
$actual -is [hashtable] | Should -Be $true
|
||||
$actual.keys.key[0] | Should -Be $expected.keys.key[0]
|
||||
$actual.keys.key[1] | Should -Be $expected.keys.key[1]
|
||||
$actual.keys.key[2] | Should -Be $expected.keys.key[2]
|
||||
}
|
||||
|
||||
It "Should convert XML with array of nodes to a hashtable and ConvertMode 'AlwaysAttrValue'" {
|
||||
# Arrange
|
||||
$inputXml = "<keys><key>value1</key><key>value2</key><key>value3</key></keys>"
|
||||
$expected = @{
|
||||
'keys' = @{
|
||||
'Attributes' = $null
|
||||
'Value' = @{
|
||||
'key' = @{
|
||||
'Attributes' = $null
|
||||
'Value' = @(
|
||||
@{
|
||||
'Attributes' = $null
|
||||
'Value' = 'value1'
|
||||
},
|
||||
@{
|
||||
'Attributes' = $null
|
||||
'Value' = 'value2'
|
||||
},
|
||||
@{
|
||||
'Attributes' = $null
|
||||
'Value' = 'value3'
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Act
|
||||
$actual = ConvertFrom-CEDataXml -InputString $inputXml -ConvertMode AlwaysAttrValue
|
||||
|
||||
# Assert
|
||||
$actual | Should -Not -Be $null
|
||||
$actual -is [hashtable] | Should -Be $true
|
||||
$actual.keys.Attributes | Should -Be $null
|
||||
$actual.keys.Value.key.Value[0].Value | Should -Be $expected.keys.Value.key.Value[0].Value
|
||||
$actual.keys.Value.key.Attributes | Should -Be $null
|
||||
$actual.keys.Value.key.Value[0].Attributes | Should -Be $null
|
||||
$actual.keys.Value.key.Value[1].Value | Should -Be $expected.keys.Value.key.Value[1].Value
|
||||
$actual.keys.Value.key.Value[2].Value | Should -Be $expected.keys.Value.key.Value[2].Value
|
||||
}
|
||||
|
||||
It "Should convert single element XML with ConvertMode = 'AlwaysAttrValue'" {
|
||||
# Arrange
|
||||
$inputXml = "<root><key1 att1='true'>value-1</key1><key2>value-2</key2></root>"
|
||||
$expected = @{
|
||||
'root' = @{
|
||||
'Attributes' = $null
|
||||
'Value' = @{
|
||||
'key1' = @{
|
||||
'Attributes' = @{
|
||||
'att1' = 'true'
|
||||
}
|
||||
'Value' = 'value-1'
|
||||
}
|
||||
'key2' = @{
|
||||
'Attributes' = $null
|
||||
'Value' = 'value-2'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Act
|
||||
$actual = ConvertFrom-CEDataXml -InputString $inputXml -ConvertMode AlwaysAttrValue
|
||||
|
||||
# Assert
|
||||
$actual | Should -Not -Be $null
|
||||
$actual -is [hashtable] | Should -Be $true
|
||||
$actual.root.Value.key1.Attributes.att1 | Should -Be $expected.root.Value.key1.Attributes.att1
|
||||
$actual.root.Value.key1.Value | Should -Be $expected.root.Value.key1.Value
|
||||
$actual.root.Value.key2.Attributes | Should -Be $expected.root.Value.key2.Attributes
|
||||
$actual.root.Value.key2.Value | Should -Be $expected.root.Value.key2.Value
|
||||
}
|
||||
|
||||
It "Should convert elements with attributes with ConvertMode = 'AttrValueWhenAttributes'" {
|
||||
# Arrange
|
||||
$inputXml = "<root><key>value</key><withattr att1='true' att2='false'>value-1</withattr></root>"
|
||||
$expected = @{
|
||||
'root' = @{
|
||||
'key' = 'value'
|
||||
'withattr' = @{
|
||||
'Attributes' = @{
|
||||
'att1' = 'true'
|
||||
'att2' = 'false'
|
||||
}
|
||||
'Value' = 'value-1'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Act
|
||||
$actual = ConvertFrom-CEDataXml -InputString $inputXml -ConvertMode AttrValueWhenAttributes
|
||||
|
||||
# Assert
|
||||
$actual | Should -Not -Be $null
|
||||
$actual -is [hashtable] | Should -Be $true
|
||||
$actual.root.key | Should -Be $expected.root.key
|
||||
$actual.root.withattr.Attributes.att1 | Should -Be $expected.root.withattr.Attributes.att1
|
||||
$actual.root.withattr.Attributes.att2 | Should -Be $expected.root.withattr.Attributes.att2
|
||||
$actual.root.withattr.Value | Should -Be $expected.root.withattr.Value
|
||||
}
|
||||
|
||||
It "Should convert XML with nested elements to a hashtable skipping attribute properties" {
|
||||
# Arrange
|
||||
$inputXml = '<UserLoginSessionEvent><key>8570</key><chainId>8570</chainId><createdTime>2021-02-04T08:51:53.723999Z</createdTime><userName>dcui</userName><datacenter><name>vcqaDC</name><datacenter type="Datacenter">datacenter-2</datacenter></datacenter><computeResource><name>cls</name><computeResource type="ClusterComputeResource">domain-c7</computeResource></computeResource><host><name>10.161.140.163</name><host type="HostSystem">host-21</host></host><fullFormattedMessage>User dcui@127.0.0.1 logged in as VMware-client/6.5.0</fullFormattedMessage><ipAddress>127.0.0.1</ipAddress><userAgent>VMware-client/6.4.0</userAgent><locale>en</locale><sessionId>52b910cf-661f-f72d-9f86-fb82113404b7</sessionId></UserLoginSessionEvent>'
|
||||
$expected = @{
|
||||
'UserLoginSessionEvent' = @{
|
||||
'key' = '8570'
|
||||
'createdTime' = '2021-02-04T08:51:53.723999Z'
|
||||
'userName' = 'dcui'
|
||||
'datacenter' = @{
|
||||
'name' = 'vcqaDC'
|
||||
'datacenter' = 'datacenter-2'
|
||||
}
|
||||
'computeResource' = @{
|
||||
'name' = 'cls'
|
||||
'computeResource' = 'domain-c7'
|
||||
}
|
||||
'host' = @{
|
||||
'name' = '10.161.140.163'
|
||||
'host' = 'host-21'
|
||||
}
|
||||
'fullFormattedMessage' = 'User dcui@127.0.0.1 logged in as VMware-client/6.5.0'
|
||||
'ipAddress' = '127.0.0.1'
|
||||
'userAgent' = 'VMware-client/6.4.0'
|
||||
'locale' = 'en'
|
||||
'sessionId' = '52b910cf-661f-f72d-9f86-fb82113404b7'
|
||||
}
|
||||
}
|
||||
|
||||
# Act
|
||||
$actual = ConvertFrom-CEDataXml -InputString $inputXml -ConvertMode SkipAttributes
|
||||
|
||||
# Assert
|
||||
$actual | Should -Not -Be $null
|
||||
$actual -is [hashtable] | Should -Be $true
|
||||
$actual.UserLoginSessionEvent -is [hashtable] | Should -Be $true
|
||||
$actual.UserLoginSessionEvent.key | Should -Be $expected.UserLoginSessionEvent.key
|
||||
$actual.UserLoginSessionEvent.createdTime | Should -Be $expected.UserLoginSessionEvent.createdTime
|
||||
$actual.UserLoginSessionEvent.userName | Should -Be $expected.UserLoginSessionEvent.userName
|
||||
$actual.UserLoginSessionEvent.datacenter -is [hashtable] | Should -Be $true
|
||||
$actual.UserLoginSessionEvent.datacenter.name | Should -Be $expected.UserLoginSessionEvent.datacenter.name
|
||||
$actual.UserLoginSessionEvent.datacenter.datacenter | Should -Be $expected.UserLoginSessionEvent.datacenter.datacenter
|
||||
$actual.UserLoginSessionEvent.host -is [hashtable] | Should -Be $true
|
||||
$actual.UserLoginSessionEvent.host.name | Should -Be $expected.UserLoginSessionEvent.host.name
|
||||
$actual.UserLoginSessionEvent.host.host | Should -Be $expected.UserLoginSessionEvent.host.host
|
||||
$actual.UserLoginSessionEvent.fullFormattedMessage | Should -Be $expected.UserLoginSessionEvent.fullFormattedMessage
|
||||
}
|
||||
|
||||
It "Should convert XML with nested elements to a hashtable with ConvertMode = 'AttrValueWhenAttributes'" {
|
||||
# Arrange
|
||||
$inputXml = '<UserLoginSessionEvent><key>8570</key><chainId>8570</chainId><createdTime>2021-02-04T08:51:53.723999Z</createdTime><userName>dcui</userName><datacenter><name>vcqaDC</name><datacenter type="Datacenter">datacenter-2</datacenter></datacenter><computeResource><name>cls</name><computeResource type="ClusterComputeResource">domain-c7</computeResource></computeResource><host><name>10.161.140.163</name><host type="HostSystem">host-21</host></host><fullFormattedMessage>User dcui@127.0.0.1 logged in as VMware-client/6.5.0</fullFormattedMessage><ipAddress>127.0.0.1</ipAddress><userAgent>VMware-client/6.4.0</userAgent><locale>en</locale><sessionId>52b910cf-661f-f72d-9f86-fb82113404b7</sessionId></UserLoginSessionEvent>'
|
||||
$expected = @{
|
||||
'UserLoginSessionEvent' = @{
|
||||
'key' = '8570'
|
||||
'createdTime' = '2021-02-04T08:51:53.723999Z'
|
||||
'userName' = 'dcui'
|
||||
'datacenter' = @{
|
||||
'name' = 'vcqaDC'
|
||||
'datacenter' = @{
|
||||
'Attributes' = @{
|
||||
'type' = 'Datacenter'
|
||||
}
|
||||
'Value' = 'datacenter-2'
|
||||
}
|
||||
}
|
||||
'computeResource' = @{
|
||||
'name' = 'cls'
|
||||
'computeResource' = @{
|
||||
'Attributes' = @{
|
||||
'type' = 'ClusterComputeResource'
|
||||
}
|
||||
'Value' = 'domain-c7'
|
||||
}
|
||||
}
|
||||
'host' = @{
|
||||
'name' = '10.161.140.163'
|
||||
'host' = @{
|
||||
'Attributes' = @{
|
||||
'type' = 'HostSystem'
|
||||
}
|
||||
'Value' = 'host-21'
|
||||
}
|
||||
}
|
||||
'fullFormattedMessage' = 'User dcui@127.0.0.1 logged in as VMware-client/6.5.0'
|
||||
'ipAddress' = '127.0.0.1'
|
||||
'userAgent' = 'VMware-client/6.4.0'
|
||||
'locale' = 'en'
|
||||
'sessionId' = '52b910cf-661f-f72d-9f86-fb82113404b7'
|
||||
}
|
||||
}
|
||||
|
||||
# Act
|
||||
$actual = ConvertFrom-CEDataXml -InputString $inputXml -ConvertMode AttrValueWhenAttributes
|
||||
|
||||
# Assert
|
||||
$actual | Should -Not -Be $null
|
||||
$actual -is [hashtable] | Should -Be $true
|
||||
$actual.UserLoginSessionEvent -is [hashtable] | Should -Be $true
|
||||
$actual.UserLoginSessionEvent.key | Should -Be $expected.UserLoginSessionEvent.key
|
||||
$actual.UserLoginSessionEvent.createdTime | Should -Be $expected.UserLoginSessionEvent.createdTime
|
||||
$actual.UserLoginSessionEvent.userName | Should -Be $expected.UserLoginSessionEvent.userName
|
||||
$actual.UserLoginSessionEvent.datacenter -is [hashtable] | Should -Be $true
|
||||
$actual.UserLoginSessionEvent.datacenter.name | Should -Be $expected.UserLoginSessionEvent.datacenter.name
|
||||
$actual.UserLoginSessionEvent.datacenter.datacenter.Attributes.type | Should -Be $expected.UserLoginSessionEvent.datacenter.datacenter.Attributes.type
|
||||
$actual.UserLoginSessionEvent.datacenter.datacenter.Value | Should -Be $expected.UserLoginSessionEvent.datacenter.datacenter.Value
|
||||
$actual.UserLoginSessionEvent.computeResource.computeResource.Attributes.type | Should -Be $expected.UserLoginSessionEvent.computeResource.computeResource.Attributes.type
|
||||
$actual.UserLoginSessionEvent.computeResource.computeResource.Value | Should -Be $expected.UserLoginSessionEvent.computeResource.computeResource.Value
|
||||
$actual.UserLoginSessionEvent.host.host.Attributes.type | Should -Be $expected.UserLoginSessionEvent.host.host.Attributes.type
|
||||
$actual.UserLoginSessionEvent.host.host.Value | Should -Be $expected.UserLoginSessionEvent.host.host.Value
|
||||
}
|
||||
}
|
||||
|
||||
Context "ConvertTo-CEDataXml" {
|
||||
It "Should convert single hashtable to XML" {
|
||||
# Arrange
|
||||
$inputHashtable = @{'key' = 'value'}
|
||||
$expected = "<key>value</key>"
|
||||
|
||||
# Act
|
||||
$actual = ConvertTo-CEDataXml -InputObject $inputHashtable -AttributesKeysInElementAttributes $false
|
||||
|
||||
# Assert
|
||||
$actual | Should -Be $expected
|
||||
}
|
||||
|
||||
It "Should convert hashtable with array values to XML " {
|
||||
# Arrange
|
||||
$inputHashtable = @{'keys' = @{'key' = @('value1', 'value2', 'value3')}}
|
||||
$expected = "<keys><key>value1</key><key>value2</key><key>value3</key></keys>"
|
||||
|
||||
# Act
|
||||
$actual = ConvertTo-CEDataXml -InputObject $inputHashtable -AttributesKeysInElementAttributes $false
|
||||
|
||||
# Assert
|
||||
$actual | Should -Be $expected
|
||||
}
|
||||
|
||||
It "Should convert hashtable with hashtable values to XML " {
|
||||
# Arrange
|
||||
$inputHashtable = @{
|
||||
'UserLoginSessionEvent' = @{
|
||||
'datacenter' = @{
|
||||
'datacenter' = 'datacenter-2'
|
||||
}
|
||||
}
|
||||
}
|
||||
$expected = '<UserLoginSessionEvent><datacenter><datacenter>datacenter-2</datacenter></datacenter></UserLoginSessionEvent>'
|
||||
|
||||
# Act
|
||||
$actual = ConvertTo-CEDataXml -InputObject $inputHashtable -AttributesKeysInElementAttributes $false
|
||||
|
||||
# Assert
|
||||
$actual | Should -Be $expected
|
||||
}
|
||||
|
||||
It "Should convert hashtable with Attributes keys to XML elements without attributes" {
|
||||
# Arrange
|
||||
$inputHashtable = @{
|
||||
'UserLoginSessionEvent' = @{
|
||||
'computeResource' = @{
|
||||
'name' = 'cls'
|
||||
'computeResource' = @{
|
||||
'Attributes' = @{
|
||||
'type' = 'ClusterComputeResource'
|
||||
}
|
||||
'Value' = 'domain-c7'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$expected = '<UserLoginSessionEvent><computeResource><computeResource><Attributes><type>ClusterComputeResource</type></Attributes><Value>domain-c7</Value></computeResource><name>cls</name></computeResource></UserLoginSessionEvent>'
|
||||
|
||||
# Act
|
||||
$actual = ConvertTo-CEDataXml -InputObject $inputHashtable -AttributesKeysInElementAttributes $false
|
||||
|
||||
# Assert
|
||||
## We can not expected the xml elements to be ordered as in the expected string,
|
||||
## so test Xml ignoring the elements order
|
||||
$actual.Contains('<Attributes><type>ClusterComputeResource</type>') | Should -Be $true
|
||||
$actual.Contains('<Value>domain-c7</Value>') | Should -Be $true
|
||||
$actual.Contains('<name>cls</name>') | Should -Be $true
|
||||
$actual.IndexOf('<Attributes>') | Should -BeGreaterThan $actual.IndexOf('<computeResource><computeResource>')
|
||||
$actual.IndexOf('<Value>') | Should -BeLessThan $actual.IndexOf('</computeResource>')
|
||||
}
|
||||
|
||||
|
||||
It "Should convert hashtable with Attributes keys to XML elements with attributes" {
|
||||
# Arrange
|
||||
$inputHashtable = @{
|
||||
'UserLoginSessionEvent' = @{
|
||||
'computeResource' = @{
|
||||
'name' = 'cls'
|
||||
'computeResource' = @{
|
||||
'Attributes' = @{
|
||||
'type' = 'ClusterComputeResource'
|
||||
}
|
||||
'Value' = 'domain-c7'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$expected = '<UserLoginSessionEvent><computeResource><computeResource type="ClusterComputeResource">domain-c7</computeResource><name>cls</name></computeResource></UserLoginSessionEvent>'
|
||||
|
||||
# Act
|
||||
$actual = ConvertTo-CEDataXml -InputObject $inputHashtable -AttributesKeysInElementAttributes $true
|
||||
|
||||
# Assert
|
||||
## We can not expected the xml elements to be ordered as in the expected string,
|
||||
## so test Xml ignoring the elements order
|
||||
$actual.StartsWith('<UserLoginSessionEvent>') | Should -Be $true
|
||||
$actual.Contains('<computeResource type="ClusterComputeResource">domain-c7</computeResource>') | Should -Be $true
|
||||
$actual.Contains('<name>cls</name>') | Should -Be $true
|
||||
}
|
||||
|
||||
It "Should throw when input hashtable has more than one root" {
|
||||
# Arrange
|
||||
$inputHashtable = @{'key1' = 'val1'; 'key2' = 'val2'}
|
||||
|
||||
# Act & Assert
|
||||
{ ConvertTo-CEDataXml -InputObject $inputHashtable -AttributesKeysInElementAttributes $false } | Should -Throw 'Input Hashtable must have single root key'
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,187 @@
|
|||
$SKIPATTR = "SkipAttributes"
|
||||
$ALWAYSATTRVALUE = "AlwaysAttrValue"
|
||||
$ATTRVALUEFORELEMENTSWITHATTR = "AttrValueWhenAttributes"
|
||||
function ConvertFrom-XmlPropertyValue {
|
||||
param(
|
||||
[Parameter(Mandatory = $true,
|
||||
ValueFromPipeline = $false)]
|
||||
$InputObject,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateSet("SkipAttributes", "AlwaysAttrValue", "AttrValueWhenAttributes")]
|
||||
[string]
|
||||
$ConvertMode
|
||||
)
|
||||
|
||||
$value = $InputObject
|
||||
$Attributes = $null
|
||||
|
||||
if ($InputObject -is [Xml.XmlElement]) {
|
||||
$hasAttributes = (($InputObject | Get-Member -MemberType Properties) | Where-Object {$_.Name -eq '#text'}) -ne $null
|
||||
if ($hasAttributes) {
|
||||
$Attributes = @{}
|
||||
$arrProperties = $InputObject | Get-Member -MemberType Properties
|
||||
foreach ($p in $arrProperties) {
|
||||
if ($p.Name -eq '#text') {
|
||||
$value = $InputObject.'#text'
|
||||
} else {
|
||||
$Attributes[$p.Name] = $InputObject.$($p.Name)
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
$value = ConvertFrom-CEDataXml -InputXmlElement $InputObject -ConvertMode $ConvertMode
|
||||
}
|
||||
}
|
||||
|
||||
if ($InputObject -is [array]) {
|
||||
$value = @()
|
||||
foreach ($obj in $InputObject) {
|
||||
$value += ConvertFrom-XmlPropertyValue -InputObject $obj -ConvertMode $ConvertMode
|
||||
}
|
||||
}
|
||||
|
||||
if (($ConvertMode -eq $SKIPATTR) -or
|
||||
($Attributes -eq $null -and $ConvertMode -eq $ATTRVALUEFORELEMENTSWITHATTR)) {
|
||||
|
||||
$value
|
||||
}
|
||||
|
||||
if (($ConvertMode -eq $ALWAYSATTRVALUE) -or
|
||||
($Attributes -ne $null -and $ConvertMode -eq $ATTRVALUEFORELEMENTSWITHATTR)) {
|
||||
@{
|
||||
"Attributes" = $Attributes
|
||||
"Value" = $value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function ConvertFrom-CEDataXml {
|
||||
param(
|
||||
[Parameter(Mandatory = $true,
|
||||
ValueFromPipeline = $true,
|
||||
ParameterSetName="Text")]
|
||||
[ValidateNotNull()]
|
||||
[string]
|
||||
$InputString,
|
||||
|
||||
[Parameter(Mandatory = $true,
|
||||
ValueFromPipeline = $false,
|
||||
ParameterSetName="XmlElement")]
|
||||
[ValidateNotNull()]
|
||||
[Xml.XmlElement]
|
||||
$InputXmlElement,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateSet("SkipAttributes", "AlwaysAttrValue", "AttrValueWhenAttributes")]
|
||||
[string]
|
||||
$ConvertMode
|
||||
)
|
||||
$result = $null
|
||||
if ($InputString -ne $null) {
|
||||
$xmlDocument = [xml]$InputString
|
||||
}
|
||||
if ($InputXmlElement -ne $null) {
|
||||
$xmlDocument = $InputXmlElement
|
||||
}
|
||||
|
||||
if ($xmlDocument -ne $null) {
|
||||
$xmlProperties = $xmlDocument | Get-Member -MemberType Properties
|
||||
|
||||
$result = @{}
|
||||
|
||||
|
||||
foreach ($property in $xmlProperties) {
|
||||
$propertyName = $property.Name
|
||||
$value = ConvertFrom-XmlPropertyValue -InputObject $xmlDocument.$propertyName -ConvertMode $ConvertMode
|
||||
$result[$propertyName] = $value
|
||||
}
|
||||
}
|
||||
|
||||
$result
|
||||
}
|
||||
|
||||
function New-XmlElement {
|
||||
param(
|
||||
[Parameter(Mandatory = $true,
|
||||
ValueFromPipeline = $false)]
|
||||
[ValidateNotNull()]
|
||||
$DictionaryEntry,
|
||||
|
||||
[Parameter(Mandatory = $true,
|
||||
ValueFromPipeline = $false)]
|
||||
$XmlDocument,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[Switch]
|
||||
$AttributesKeysInElementAttributes
|
||||
|
||||
)
|
||||
$result = $XmlDocument.CreateElement($DictionaryEntry.Name)
|
||||
|
||||
$value = ""
|
||||
|
||||
if ($DictionaryEntry.Value -is [hashtable]) {
|
||||
if($DictionaryEntry.Value.Keys.Count -eq 2 -and `
|
||||
$DictionaryEntry.Value['Attributes'] -is [hashtable] -and `
|
||||
$DictionaryEntry.Value['Value'] -ne $null -and `
|
||||
$AttributesKeysInElementAttributes) {
|
||||
foreach ($attKv in $DictionaryEntry.Value['Attributes'].GetEnumerator()) {
|
||||
$result.SetAttribute($attKv.Name, $attKv.Value)
|
||||
}
|
||||
$result.InnerText = $DictionaryEntry.Value['Value'].ToString()
|
||||
|
||||
} else {
|
||||
foreach ($nameValue in $DictionaryEntry.Value.GetEnumerator()) {
|
||||
$xmlElement = New-XmlElement -DictionaryEntry $nameValue -XmlDocument $XmlDocument -AttributesKeysInElementAttributes:$AttributesKeysInElementAttributes
|
||||
$xmlElement | Foreach-Object {
|
||||
$result.AppendChild($_) | Out-Null
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif ($DictionaryEntry.Value -is [array]) {
|
||||
$result = @()
|
||||
foreach ($item in $DictionaryEntry.Value) {
|
||||
$result += (New-XmlElement `
|
||||
-DictionaryEntry `
|
||||
(New-Object System.Collections.DictionaryEntry `
|
||||
-ArgumentList @($DictionaryEntry.Name, $item)) `
|
||||
-XmlDocument $XmlDocument `
|
||||
-AttributesKeysInElementAttributes:$AttributesKeysInElementAttributes)
|
||||
}
|
||||
} else {
|
||||
$value = $DictionaryEntry.Value.ToString()
|
||||
$result.InnerText = $value
|
||||
}
|
||||
|
||||
$result
|
||||
}
|
||||
|
||||
function ConvertTo-CEDataXml {
|
||||
param(
|
||||
[Parameter(Mandatory = $true,
|
||||
ValueFromPipeline = $true)]
|
||||
[ValidateNotNull()]
|
||||
[Hashtable]
|
||||
$InputObject,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[bool]
|
||||
$AttributesKeysInElementAttributes
|
||||
)
|
||||
if ($InputObject.Keys.Count -ne 1) {
|
||||
throw "Input Hashtable must have single root key"
|
||||
}
|
||||
|
||||
[xml]$resultDocument = New-Object System.Xml.XmlDocument
|
||||
|
||||
foreach ($nameValue in $InputObject.GetEnumerator()) {
|
||||
$element = New-XmlElement -DictionaryEntry $nameValue -XmlDocument $resultDocument -AttributesKeysInElementAttributes:$AttributesKeysInElementAttributes
|
||||
|
||||
$element | Foreach-Object {
|
||||
$resultDocument.AppendChild($_) | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
$resultDocument.OuterXml
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
param(
|
||||
[Parameter()]
|
||||
[ValidateScript({Test-Path $_})]
|
||||
[string]
|
||||
$CloudEventsModulePath,
|
||||
|
||||
[Parameter()]
|
||||
[ValidateSet('unit', 'integration', 'all')]
|
||||
[string]
|
||||
$TestsType,
|
||||
|
||||
[Parameter()]
|
||||
[Switch]
|
||||
$EnableProcessExit
|
||||
)
|
||||
|
||||
Import-Module $CloudEventsModulePath
|
||||
|
||||
if ($TestsType -eq 'unit' -or $TestsType -eq 'all') {
|
||||
$pesterContainer = New-PesterContainer -Path (Join-Path $PSScriptRoot 'unit')
|
||||
$pesterConfiguration = [PesterConfiguration]::Default
|
||||
|
||||
$pesterConfiguration.Run.Path = (Join-Path $PSScriptRoot 'unit')
|
||||
$pesterConfiguration.Run.Container = $pesterContainer
|
||||
|
||||
Invoke-Pester -Configuration $pesterConfiguration
|
||||
}
|
||||
|
||||
if ($TestsType -eq 'integration' -or $TestsType -eq 'all') {
|
||||
|
||||
$testsData = @{
|
||||
CloudEventsModulePath = $CloudEventsModulePath
|
||||
}
|
||||
|
||||
$pesterContainer = New-PesterContainer -Path (Join-Path $PSScriptRoot 'integration') -Data $testsData
|
||||
$pesterConfiguration = [PesterConfiguration]::Default
|
||||
|
||||
$pesterConfiguration.Run.Path = (Join-Path $PSScriptRoot 'integration')
|
||||
$pesterConfiguration.Run.Container = $pesterContainer
|
||||
|
||||
Invoke-Pester -Configuration $pesterConfiguration
|
||||
}
|
|
@ -0,0 +1,275 @@
|
|||
param(
|
||||
[Parameter()]
|
||||
[ValidateScript({Test-Path $_})]
|
||||
[string]
|
||||
$CloudEventsModulePath)
|
||||
|
||||
Describe "Client-Server Integration Tests" {
|
||||
Context "Send And Receive CloudEvents over Http" {
|
||||
BeforeAll {
|
||||
$testServerUrl = 'http://localhost:52673/'
|
||||
|
||||
$serverProcess = $null
|
||||
|
||||
. (Join-Path $PSScriptRoot 'ProtocolConstants.ps1')
|
||||
|
||||
# Starts CloudEvent Test Server
|
||||
$usePowerShell = (Get-Process -Id $pid).ProcessName
|
||||
$serverScript = Join-Path $PSScriptRoot 'HttpServer.ps1'
|
||||
$serverProcessArguments = "-Command $serverScript -CloudEventsModulePath '$CloudEventsModulePath' -ServerUrl '$testServerUrl'"
|
||||
|
||||
$serverProcess = Start-Process `
|
||||
-FilePath $usePowerShell `
|
||||
-ArgumentList $serverProcessArguments `
|
||||
-PassThru `
|
||||
-NoNewWindow
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
# Requests Stop CloudEvent Test Server
|
||||
$serverStopRequest = `
|
||||
New-CloudEvent `
|
||||
-Id ([Guid]::NewGuid()) `
|
||||
-Type $script:ServerStopType `
|
||||
-Source $script:ClientSource | `
|
||||
ConvertTo-HttpMessage `
|
||||
-ContentMode Structured
|
||||
|
||||
Invoke-WebRequest `
|
||||
-Uri $testServerUrl `
|
||||
-Headers $serverStopRequest.Headers `
|
||||
-Body $serverStopRequest.Body | Out-Null
|
||||
|
||||
if ($serverProcess -ne $null -and `
|
||||
-not $serverProcess.HasExited) {
|
||||
$serverProcess | Wait-Process
|
||||
}
|
||||
}
|
||||
|
||||
It 'Echo binary content mode cloud events' {
|
||||
# Arrange
|
||||
$cloudEvent = New-CloudEvent `
|
||||
-Type $script:EchoBinaryType `
|
||||
-Source $script:ClientSource `
|
||||
-Id 'integration-test-1' `
|
||||
-Time (Get-Date) | `
|
||||
Add-CloudEventJsonData -Data @{
|
||||
'a1' = 'b'
|
||||
'a2' = 'c'
|
||||
'a3' = 'd'
|
||||
}
|
||||
|
||||
# Act
|
||||
|
||||
## Convert CloudEvent to HTTP Message
|
||||
$httpRequest = ConvertTo-HttpMessage `
|
||||
-CloudEvent $cloudEvent `
|
||||
-ContentMode Binary
|
||||
|
||||
## Invoke WebRequest with the HTTP Message
|
||||
$httpResponse = Invoke-WebRequest `
|
||||
-Uri $testServerUrl `
|
||||
-Headers $httpRequest.Headers `
|
||||
-Body $httpRequest.Body
|
||||
|
||||
## Convert HTTP Response to CloudEvent
|
||||
$resultCloudEvent = ConvertFrom-HttpMessage `
|
||||
-Headers $httpResponse.Headers `
|
||||
-Body $httpResponse.Content
|
||||
|
||||
# Assert
|
||||
|
||||
## Assert echoed CloudEvent
|
||||
$resultCloudEvent | Should -Not -Be $null
|
||||
$resultCloudEvent.Source | Should -Be $script:ServerSource
|
||||
$resultCloudEvent.Type | Should -Be $script:EchoBinaryType
|
||||
$resultCloudEvent.Id | Should -Be $cloudEvent.Id
|
||||
$resultCloudEvent.Time | Should -BeGreaterThan $cloudEvent.Time
|
||||
|
||||
## Assert Result CloudEvent Data
|
||||
## Read Data as Json
|
||||
$resultData = $resultCloudEvent | Read-CloudEventJsonData
|
||||
$resultData.a1 | Should -Be 'b'
|
||||
$resultData.a2 | Should -Be 'c'
|
||||
$resultData.a3 | Should -Be 'd'
|
||||
}
|
||||
|
||||
It 'Echo binary content mode cloud events with XML data' {
|
||||
# Arrange
|
||||
$cloudEvent = New-CloudEvent `
|
||||
-Type $script:EchoBinaryType `
|
||||
-Source $script:ClientSource `
|
||||
-Id 'integration-test-2' `
|
||||
-Time (Get-Date) | `
|
||||
Add-CloudEventXmlData -Data @{
|
||||
'a1' = @{
|
||||
'a2' = 'c'
|
||||
'a3' = 'd'
|
||||
}
|
||||
} `
|
||||
-AttributesKeysInElementAttributes $false
|
||||
|
||||
# Act
|
||||
|
||||
## Convert CloudEvent to HTTP Message
|
||||
$httpRequest = ConvertTo-HttpMessage `
|
||||
-CloudEvent $cloudEvent `
|
||||
-ContentMode Binary
|
||||
|
||||
## Invoke WebRequest with the HTTP Message
|
||||
$httpResponse = Invoke-WebRequest `
|
||||
-Uri $testServerUrl `
|
||||
-Headers $httpRequest.Headers `
|
||||
-Body $httpRequest.Body
|
||||
|
||||
## Convert HTTP Response to CloudEvent
|
||||
$resultCloudEvent = ConvertFrom-HttpMessage `
|
||||
-Headers $httpResponse.Headers `
|
||||
-Body $httpResponse.Content
|
||||
|
||||
# Assert
|
||||
|
||||
## Assert echoed CloudEvent
|
||||
$resultCloudEvent | Should -Not -Be $null
|
||||
$resultCloudEvent.Source | Should -Be $script:ServerSource
|
||||
$resultCloudEvent.Type | Should -Be $script:EchoBinaryType
|
||||
$resultCloudEvent.Id | Should -Be $cloudEvent.Id
|
||||
$resultCloudEvent.Time | Should -BeGreaterThan $cloudEvent.Time
|
||||
|
||||
## Assert Result CloudEvent Data
|
||||
## Read Data as Xml
|
||||
$resultData = $resultCloudEvent | Read-CloudEventXmlData -ConvertMode 'SkipAttributes'
|
||||
$resultData -is [hashtable] | Should -Be $true
|
||||
$resultData.a1 -is [hashtable] | Should -Be $true
|
||||
$resultData.a1.a2 | Should -Be 'c'
|
||||
$resultData.a1.a3 | Should -Be 'd'
|
||||
}
|
||||
|
||||
It 'Echo structured content mode cloud events' {
|
||||
# Arrange
|
||||
$cloudEvent = New-CloudEvent `
|
||||
-Type $script:EchoStructuredType `
|
||||
-Source $script:ClientSource `
|
||||
-Id 'integration-test-3' `
|
||||
-Time (Get-Date) | `
|
||||
Add-CloudEventJsonData -Data @{
|
||||
'b1' = 'd'
|
||||
'b2' = 'e'
|
||||
'b3' = 'f'
|
||||
}
|
||||
|
||||
# Act
|
||||
|
||||
## Convert CloudEvent to HTTP Message
|
||||
$httpRequest = ConvertTo-HttpMessage `
|
||||
-CloudEvent $cloudEvent `
|
||||
-ContentMode Structured
|
||||
|
||||
## Invoke WebRequest with the HTTP Message
|
||||
$httpResponse = Invoke-WebRequest `
|
||||
-Uri $testServerUrl `
|
||||
-Headers $httpRequest.Headers `
|
||||
-Body $httpRequest.Body
|
||||
|
||||
## Convert HTTP Response to CloudEvent
|
||||
$resultCloudEvent = ConvertFrom-HttpMessage `
|
||||
-Headers $httpResponse.Headers `
|
||||
-Body $httpResponse.Content
|
||||
|
||||
# Assert
|
||||
|
||||
## Assert echoed CloudEvent
|
||||
$resultCloudEvent | Should -Not -Be $null
|
||||
$resultCloudEvent.Source | Should -Be $script:ServerSource
|
||||
$resultCloudEvent.Type | Should -Be $script:EchoStructuredType
|
||||
$resultCloudEvent.Id | Should -Be $cloudEvent.Id
|
||||
$resultCloudEvent.Time | Should -BeGreaterThan $cloudEvent.Time
|
||||
|
||||
## Assert Result CloudEvent Data
|
||||
## Read Data as Json
|
||||
$resultData = $resultCloudEvent | Read-CloudEventJsonData
|
||||
$resultData.b1 | Should -Be 'd'
|
||||
$resultData.b2 | Should -Be 'e'
|
||||
$resultData.b3 | Should -Be 'f'
|
||||
}
|
||||
|
||||
It 'Echo structured content mode cloud events with XML data' {
|
||||
# Arrange
|
||||
$cloudEvent = New-CloudEvent `
|
||||
-Type $script:EchoStructuredType `
|
||||
-Source $script:ClientSource `
|
||||
-Id 'integration-test-4' `
|
||||
-Time (Get-Date) | `
|
||||
Add-CloudEventXmlData -Data @{
|
||||
'b1' = @{
|
||||
'b2' = 'e'
|
||||
'b3' = 'f'
|
||||
}
|
||||
} `
|
||||
-AttributesKeysInElementAttributes $false
|
||||
|
||||
# Act
|
||||
|
||||
## Convert CloudEvent to HTTP Message
|
||||
$httpRequest = ConvertTo-HttpMessage `
|
||||
-CloudEvent $cloudEvent `
|
||||
-ContentMode Structured
|
||||
|
||||
## Invoke WebRequest with the HTTP Message
|
||||
$httpResponse = Invoke-WebRequest `
|
||||
-Uri $testServerUrl `
|
||||
-Headers $httpRequest.Headers `
|
||||
-Body $httpRequest.Body
|
||||
|
||||
## Convert HTTP Response to CloudEvent
|
||||
$resultCloudEvent = ConvertFrom-HttpMessage `
|
||||
-Headers $httpResponse.Headers `
|
||||
-Body $httpResponse.Content
|
||||
|
||||
# Assert
|
||||
|
||||
## Assert echoed CloudEvent
|
||||
$resultCloudEvent | Should -Not -Be $null
|
||||
$resultCloudEvent.Source | Should -Be $script:ServerSource
|
||||
$resultCloudEvent.Type | Should -Be $script:EchoStructuredType
|
||||
$resultCloudEvent.Id | Should -Be $cloudEvent.Id
|
||||
$resultCloudEvent.Time | Should -BeGreaterThan $cloudEvent.Time
|
||||
|
||||
## Assert Result CloudEvent Data
|
||||
## Read Data as Xml
|
||||
$resultData = $resultCloudEvent | Read-CloudEventXmlData -ConvertMode 'SkipAttributes'
|
||||
$resultData -is [hashtable] | Should -Be $true
|
||||
$resultData.b1 -is [hashtable] | Should -Be $true
|
||||
$resultData.b1.b2 | Should -Be 'e'
|
||||
$resultData.b1.b3 | Should -Be 'f'
|
||||
}
|
||||
|
||||
It 'Send cloud event expecting no result' {
|
||||
# Arrange
|
||||
$cloudEvent = New-CloudEvent `
|
||||
-Type 'no-content' `
|
||||
-Source $script:ClientSource `
|
||||
-Id 'integration-test-5' `
|
||||
-Time (Get-Date) | `
|
||||
Add-CloudEventData `
|
||||
-Data 'This is text data' `
|
||||
-DataContentType 'application/text'
|
||||
|
||||
# Act
|
||||
|
||||
## Convert CloudEvent to HTTP Message
|
||||
$httpRequest = ConvertTo-HttpMessage `
|
||||
-CloudEvent $cloudEvent `
|
||||
-ContentMode Structured
|
||||
|
||||
## Invoke WebRequest with the HTTP Message
|
||||
$httpResponse = Invoke-WebRequest `
|
||||
-Uri $testServerUrl `
|
||||
-Headers $httpRequest.Headers `
|
||||
-Body $httpRequest.Body
|
||||
|
||||
# Assert
|
||||
$httpResponse.StatusCode | Should -Be ([int]([System.Net.HttpStatusCode]::NoContent))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateScript({Test-Path $_})]
|
||||
[string]
|
||||
$CloudEventsModulePath,
|
||||
|
||||
[Parameter(
|
||||
Mandatory = $true,
|
||||
ValueFromPipeline = $false,
|
||||
ValueFromPipelineByPropertyName = $false)]
|
||||
[ValidateNotNull()]
|
||||
[string]
|
||||
$ServerUrl
|
||||
)
|
||||
|
||||
. (Join-Path $PSScriptRoot 'ProtocolConstants.ps1')
|
||||
|
||||
# Import SDK Module
|
||||
Import-Module $CloudEventsModulePath
|
||||
|
||||
function Start-HttpCloudEventListener {
|
||||
<#
|
||||
.DESCRIPTION
|
||||
Starts a HTTP CloudEvent Listener on specified Url
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(
|
||||
Mandatory = $true,
|
||||
ValueFromPipeline = $false,
|
||||
ValueFromPipelineByPropertyName = $false)]
|
||||
[ValidateNotNull()]
|
||||
[string]
|
||||
$Url,
|
||||
|
||||
[Parameter(
|
||||
Mandatory = $false,
|
||||
ValueFromPipeline = $false,
|
||||
ValueFromPipelineByPropertyName = $false)]
|
||||
[scriptblock]
|
||||
$Handler
|
||||
)
|
||||
|
||||
$listener = New-Object -Type 'System.Net.HttpListener'
|
||||
$listener.AuthenticationSchemes = [System.Net.AuthenticationSchemes]::Anonymous
|
||||
$listener.Prefixes.Add($Url)
|
||||
|
||||
try {
|
||||
$listener.Start()
|
||||
|
||||
$context = $listener.GetContext()
|
||||
|
||||
# Read Input Stream
|
||||
$buffer = New-Object 'byte[]' -ArgumentList 1024
|
||||
$ms = New-Object 'IO.MemoryStream'
|
||||
$read = 0
|
||||
while (($read = $context.Request.InputStream.Read($buffer, 0, 1024)) -gt 0) {
|
||||
$ms.Write($buffer, 0, $read);
|
||||
}
|
||||
$bodyData = $ms.ToArray()
|
||||
$ms.Dispose()
|
||||
|
||||
# Read Headers
|
||||
$headers = @{}
|
||||
for($i =0; $i -lt $context.Request.Headers.Count; $i++) {
|
||||
$headers[$context.Request.Headers.GetKey($i)] = $context.Request.Headers.GetValues($i)
|
||||
}
|
||||
|
||||
$cloudEvent = ConvertFrom-HttpMessage -Headers $headers -Body $bodyData
|
||||
|
||||
if ( $cloudEvent -ne $null ) {
|
||||
$Handler.Invoke($cloudEvent, $context.Response)
|
||||
$context.Response.Close();
|
||||
} else {
|
||||
$context.Response.StatusCode = [int]([System.Net.HttpStatusCode]::BadRequest)
|
||||
$context.Response.Close();
|
||||
}
|
||||
|
||||
} catch {
|
||||
Write-Error $_
|
||||
$context.Response.StatusCode = [int]([System.Net.HttpStatusCode]::InternalServerError)
|
||||
$context.Response.Close();
|
||||
} finally {
|
||||
$listener.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
$global:serverStopRequested = $false
|
||||
while ( -not $global:serverStopRequested ) {
|
||||
Start-HttpCloudEventListener -Url $ServerUrl -Handler {
|
||||
$requestCloudEvent = $args[0]
|
||||
$response = $args[1]
|
||||
|
||||
# When CloudEvent Type is 'echo-structured' or 'echo-binary' the Server responds
|
||||
# with CloudEvent in corresponding content mode
|
||||
if ( $requestCloudEvent.Type -eq $script:EchoBinaryType -or `
|
||||
$requestCloudEvent.Type -eq $script:EchoStructuredType ) {
|
||||
|
||||
# Create Cloud Event for the response
|
||||
$cloudEvent = New-CloudEvent `
|
||||
-Type $requestCloudEvent.Type `
|
||||
-Source $script:ServerSource `
|
||||
-Time (Get-Date) `
|
||||
-Id $requestCloudEvent.Id
|
||||
|
||||
# Add Data to the new Cloud Event
|
||||
$requestCloudEventJsonData = $requestCloudEvent | Read-CloudEventJsonData
|
||||
$requestCloudEventXmlData = $requestCloudEvent | Read-CloudEventXmlData -ConvertMode 'SkipAttributes'
|
||||
if ($requestCloudEventJsonData) {
|
||||
$cloudEvent = $cloudEvent | Add-CloudEventJsonData `
|
||||
-Data $requestCloudEventJsonData
|
||||
} elseif ($requestCloudEventXmlData) {
|
||||
$cloudEvent = $cloudEvent | Add-CloudEventXmlData `
|
||||
-Data $requestCloudEventXmlData `
|
||||
-AttributesKeysInElementAttributes $false
|
||||
} else {
|
||||
$requestCloudEventData = $requestCloudEvent | Read-CloudEventData
|
||||
$cloudEvent = $cloudEvent | Add-CloudEventData `
|
||||
-Data $requestCloudEventData `
|
||||
-DataContentType $requestCloudEvent.DataContentType
|
||||
}
|
||||
|
||||
# Convert Cloud Event to HTTP Response
|
||||
$contentMode = $requestCloudEvent.Type.TrimStart('echo-')
|
||||
$httpMessage = $cloudEvent | ConvertTo-HttpMessage -ContentMode $contentMode
|
||||
|
||||
$response.Headers = New-Object 'System.Net.WebHeaderCollection'
|
||||
foreach ($keyValue in $httpMessage.Headers.GetEnumerator()) {
|
||||
$response.Headers.Add($keyValue.Key, $keyValue.Value)
|
||||
}
|
||||
$response.ContentLength64 = $httpMessage.Body.Length
|
||||
$response.OutputStream.Write($httpMessage.Body, 0, $httpMessage.Body.Length)
|
||||
$response.StatusCode = [int]([System.Net.HttpStatusCode]::OK)
|
||||
|
||||
} else {
|
||||
# No Content in all other cases
|
||||
$response.StatusCode = [int]([System.Net.HttpStatusCode]::NoContent)
|
||||
}
|
||||
|
||||
if ( $requestCloudEvent.Type -eq $script:ServerStopType ) {
|
||||
# Server Stop is requested
|
||||
$global:serverStopRequested = $true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
New-Variable -Option Constant -Scope 'script' -Name 'ClientSource' -Value 'ps:test:client'
|
||||
New-Variable -Option Constant -Scope 'script' -Name 'ServerSource' -Value 'ps:test:server'
|
||||
New-Variable -Option Constant -Scope 'script' -Name 'EchoBinaryType' -Value 'echo-binary'
|
||||
New-Variable -Option Constant -Scope 'script' -Name 'EchoStructuredType' -Value 'echo-structured'
|
||||
New-Variable -Option Constant -Scope 'script' -Name 'ServerStopType' -Value 'server-stop'
|
|
@ -0,0 +1,103 @@
|
|||
Describe "Add-CloudEventData Function Tests" {
|
||||
Context "Adds Data" {
|
||||
It 'Adds byte[] data' {
|
||||
# Arrange
|
||||
$cloudEvent = New-CloudEvent `
|
||||
-Id ([Guid]::NewGuid()) `
|
||||
-Type test `
|
||||
-Source 'urn:test'
|
||||
|
||||
$expectedData = [Text.Encoding]::UTF8.GetBytes("test")
|
||||
$expectedDataContentType = 'application/octet-stream'
|
||||
|
||||
|
||||
# Act
|
||||
$actual = $cloudEvent | `
|
||||
Add-CloudEventData `
|
||||
-Data $expectedData `
|
||||
-DataContentType $expectedDataContentType
|
||||
|
||||
# Assert
|
||||
$actual | Should -Not -Be $null
|
||||
$actual.DataContentType.ToString() | Should -Be $expectedDataContentType
|
||||
$actual.Data | Should -Be $expectedData
|
||||
}
|
||||
|
||||
It 'Adds xml text data' {
|
||||
# Arrange
|
||||
$cloudEvent = New-CloudEvent `
|
||||
-Id ([Guid]::NewGuid()) `
|
||||
-Type test `
|
||||
-Source 'urn:test'
|
||||
|
||||
$expectedData = '<much wow="xml"/>'
|
||||
$expectedDataContentType = 'application/xml'
|
||||
|
||||
|
||||
# Act
|
||||
$actual = $cloudEvent | `
|
||||
Add-CloudEventData `
|
||||
-Data $expectedData `
|
||||
-DataContentType $expectedDataContentType
|
||||
|
||||
# Assert
|
||||
$actual | Should -Not -Be $null
|
||||
$actual.DataContentType.ToString() | Should -Be $expectedDataContentType
|
||||
$actual.Data | Should -Be $expectedData
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Context "Errors on invalid data content type" {
|
||||
It 'Throws error on invalid MIME content type' {
|
||||
# Arrange
|
||||
$invalidContentType = 'invalid'
|
||||
|
||||
$cloudEvent = New-CloudEvent `
|
||||
-Id ([Guid]::NewGuid()) `
|
||||
-Type test `
|
||||
-Source 'urn:test'
|
||||
|
||||
# Act & Assert
|
||||
{ Add-CloudEventData `
|
||||
-CloudEvent $cloudEvent `
|
||||
-Data '1' `
|
||||
-DataContentType $invalidContentType } | `
|
||||
Should -Throw "*The specified content type is invalid*"
|
||||
}
|
||||
|
||||
It 'Throws error on empty content type' {
|
||||
# Arrange
|
||||
$invalidContentType = [string]::Empty
|
||||
|
||||
$cloudEvent = New-CloudEvent `
|
||||
-Id ([Guid]::NewGuid()) `
|
||||
-Type test `
|
||||
-Source 'urn:test'
|
||||
|
||||
# Act & Assert
|
||||
{ Add-CloudEventData `
|
||||
-CloudEvent $cloudEvent `
|
||||
-Data '1' `
|
||||
-DataContentType $invalidContentType } | `
|
||||
Should -Throw "*The parameter 'contentType' cannot be an empty string*"
|
||||
}
|
||||
|
||||
It 'Throws error on null content type' {
|
||||
# Arrange
|
||||
$invalidContentType = $null
|
||||
|
||||
$cloudEvent = New-CloudEvent `
|
||||
-Id ([Guid]::NewGuid()) `
|
||||
-Type test `
|
||||
-Source 'urn:test'
|
||||
|
||||
# Act & Assert
|
||||
{ Add-CloudEventData `
|
||||
-CloudEvent $cloudEvent `
|
||||
-Data '1' `
|
||||
-DataContentType $invalidContentType } | `
|
||||
Should -Throw "*The parameter 'contentType' cannot be an empty string*"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
Describe "Add-CloudEventJsonData Function Tests" {
|
||||
Context "Adds Json Data" {
|
||||
It 'Adds json data with depth 1' {
|
||||
# Arrange
|
||||
$cloudEvent = New-CloudEvent `
|
||||
-Id ([Guid]::NewGuid()) `
|
||||
-Type test `
|
||||
-Source 'urn:test'
|
||||
|
||||
$expectedJson = '{
|
||||
"a": "b"
|
||||
}'
|
||||
|
||||
$htData = @{'a' = 'b'}
|
||||
|
||||
# Act
|
||||
$actual = $cloudEvent | Add-CloudEventJsonData -Data $htData
|
||||
|
||||
# Assert
|
||||
$actual | Should -Not -Be $null
|
||||
$actual.DataContentType.ToString() | Should -Be 'application/json'
|
||||
$actual.Data | Should -Be $expectedJson
|
||||
}
|
||||
|
||||
It 'Adds json data with depth 4' {
|
||||
# Arrange
|
||||
$cloudEvent = New-CloudEvent `
|
||||
-Id ([Guid]::NewGuid()) `
|
||||
-Type test `
|
||||
-Source 'urn:test'
|
||||
|
||||
$expectedJson = '{
|
||||
"1": {
|
||||
"2": {
|
||||
"3": {
|
||||
"4": "wow"
|
||||
}
|
||||
}
|
||||
}
|
||||
}'
|
||||
|
||||
$htData = @{
|
||||
'1' = @{
|
||||
'2' = @{
|
||||
'3' = @{
|
||||
'4' = 'wow'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Act
|
||||
$actual = $cloudEvent | Add-CloudEventJsonData -Data $htData -Depth 4
|
||||
|
||||
# Assert
|
||||
$actual | Should -Not -Be $null
|
||||
$actual.DataContentType.ToString() | Should -Be 'application/json'
|
||||
$actual.Data | Should -Be $expectedJson
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
Describe "Add-CloudEventXmlData Function Tests" {
|
||||
Context "Adds Xml Data" {
|
||||
It 'Adds xml data with depth 1' {
|
||||
# Arrange
|
||||
$cloudEvent = New-CloudEvent `
|
||||
-Id ([Guid]::NewGuid()) `
|
||||
-Type test `
|
||||
-Source 'urn:test'
|
||||
|
||||
$expectedXml = '<a>b</a>'
|
||||
|
||||
$htData = @{'a' = 'b'}
|
||||
|
||||
# Act
|
||||
$actual = $cloudEvent | Add-CloudEventXmlData -Data $htData -AttributesKeysInElementAttributes $false
|
||||
|
||||
# Assert
|
||||
$actual | Should -Not -Be $null
|
||||
$actual.DataContentType.ToString() | Should -Be 'application/xml'
|
||||
$actual.Data | Should -Be $expectedXml
|
||||
}
|
||||
|
||||
It 'Adds xml data with depth 4' {
|
||||
# Arrange
|
||||
$cloudEvent = New-CloudEvent `
|
||||
-Id ([Guid]::NewGuid()) `
|
||||
-Type test `
|
||||
-Source 'urn:test'
|
||||
|
||||
$expectedXml = '<1><2><3><4>wow</4></3></2></1>'
|
||||
|
||||
$htData = @{
|
||||
'1' = @{
|
||||
'2' = @{
|
||||
'3' = @{
|
||||
'4' = 'wow'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Act
|
||||
$actual = $cloudEvent | Add-CloudEventXmlData -Data $htData -AttributesKeysInElementAttributes $false
|
||||
|
||||
# Assert
|
||||
$actual | Should -Not -Be $null
|
||||
$actual.DataContentType.ToString() | Should -Be 'application/xml'
|
||||
$actual.Data | Should -Be $expectedXml
|
||||
}
|
||||
|
||||
It 'Should throw when no single root key hashtable is passed to the Add-CloudEventXmlData Data parameter' {
|
||||
# Arrange
|
||||
$cloudEvent = New-CloudEvent `
|
||||
-Id ([Guid]::NewGuid()) `
|
||||
-Type test `
|
||||
-Source 'urn:test'
|
||||
|
||||
$htData = @{
|
||||
'1' = '2'
|
||||
'3' = '4'
|
||||
}
|
||||
|
||||
# Act & Assert
|
||||
{ $cloudEvent | Add-CloudEventXmlData -Data $htData -AttributesKeysInElementAttributes $false} | `
|
||||
Should -Throw '*Input Hashtable must have single root key*'
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,244 @@
|
|||
Describe "ConvertFrom-HttpMessage Function Tests" {
|
||||
BeforeAll {
|
||||
$expectedSpecVersion = '1.0'
|
||||
$expectedStructuredContentType = 'application/cloudevents+json'
|
||||
}
|
||||
|
||||
Context "Converts CloudEvent in Binary Content Mode" {
|
||||
It 'Converts a CloudEvent with all properties and json format data' {
|
||||
|
||||
# Arrange
|
||||
$expectedType = 'test'
|
||||
$expectedSource = 'urn:test'
|
||||
$expectedId = 'test-id-1'
|
||||
$expectedTime = Get-Date `
|
||||
-Year 2021 `
|
||||
-Month 1 `
|
||||
-Day 18 `
|
||||
-Hour 12 `
|
||||
-Minute 30 `
|
||||
-Second 23 `
|
||||
-MilliSecond 134
|
||||
$expectedDataContentType = 'application/json'
|
||||
|
||||
$headers = @{
|
||||
'Content-Type' = @($expectedDataContentType, 'charset=utf-8')
|
||||
'ce-specversion' = $expectedSpecVersion
|
||||
'ce-type' = $expectedType
|
||||
'ce-time' = $expectedTime.ToString("u")
|
||||
'ce-id' = $expectedId
|
||||
'ce-source' = $expectedSource
|
||||
}
|
||||
|
||||
$body =[Text.Encoding]::UTF8.GetBytes('{
|
||||
"l10": {
|
||||
"l2": {
|
||||
"l3": "wow"
|
||||
}
|
||||
},
|
||||
"l11": "mhm"
|
||||
}')
|
||||
|
||||
# Act
|
||||
$actual = ConvertFrom-HttpMessage `
|
||||
-Headers $headers `
|
||||
-Body $body
|
||||
|
||||
# Assert
|
||||
$actual | Should -Not -Be $null
|
||||
$actual.Type | Should -Be $expectedType
|
||||
$actual.Source | Should -Be $expectedSource
|
||||
$actual.Id | Should -Be $expectedId
|
||||
$actual.Time.Year | Should -Be $expectedTime.Year
|
||||
$actual.Time.Month | Should -Be $expectedTime.Month
|
||||
$actual.Time.Day | Should -Be $expectedTime.Day
|
||||
$actual.Time.Hours | Should -Be $expectedTime.Hours
|
||||
$actual.Time.Minutes | Should -Be $expectedTime.Minutes
|
||||
$actual.Time.Seconds | Should -Be $expectedTime.Seconds
|
||||
$actual.Time.MilliSeconds | Should -Be $expectedTime.MilliSeconds
|
||||
$actual.DataContentType | Should -Be $expectedDataContentType
|
||||
|
||||
## Assert Data
|
||||
$actualHTData = $actual | Read-CloudEventJsonData -Depth 3
|
||||
|
||||
$actualHTData | Should -Not -Be $null
|
||||
$actualHTData.l10.l2.l3 | Should -Be 'wow'
|
||||
$actualHTData.l11 | Should -Be 'mhm'
|
||||
|
||||
}
|
||||
|
||||
It 'Converts a CloudEvent with required properties and application/xml format data' {
|
||||
# Arrange
|
||||
$expectedType = 'test'
|
||||
$expectedSource = 'urn:test'
|
||||
$expectedDataContentType = 'application/xml'
|
||||
$expectedData = [Text.Encoding]::UTF8.GetBytes('<much wow="xml"/>')
|
||||
|
||||
$headers = @{
|
||||
'Content-Type' = @($expectedDataContentType, 'charset=utf-8')
|
||||
'ce-specversion' = $expectedSpecVersion
|
||||
'ce-type' = $expectedType
|
||||
'ce-source' = $expectedSource
|
||||
}
|
||||
|
||||
$body = $expectedData
|
||||
|
||||
# Act
|
||||
$actual = ConvertFrom-HttpMessage `
|
||||
-Headers $headers `
|
||||
-Body $body
|
||||
|
||||
# Assert
|
||||
$actual | Should -Not -Be $null
|
||||
$actual.Type | Should -Be $expectedType
|
||||
$actual.Source | Should -Be $expectedSource
|
||||
$actual.DataContentType | Should -Be $expectedDataContentType
|
||||
$actual.Data | Should -Be $expectedData
|
||||
|
||||
## Assert Data obtained by Read-CloudEventData
|
||||
$actualData = $actual | Read-CloudEventData
|
||||
|
||||
$actualData | Should -Be $expectedData
|
||||
}
|
||||
}
|
||||
|
||||
Context "Converts CloudEvent in Structured Content Mode" {
|
||||
It 'Converts a CloudEvent with all properties and json format data' {
|
||||
# Arrange
|
||||
$expectedType = 'test'
|
||||
$expectedSource = 'urn:test'
|
||||
$expectedId = 'test-id-1'
|
||||
$expectedTime = Get-Date `
|
||||
-Year 2021 `
|
||||
-Month 1 `
|
||||
-Day 18 `
|
||||
-Hour 12 `
|
||||
-Minute 30 `
|
||||
-Second 23 `
|
||||
-MilliSecond 134
|
||||
$expectedDataContentType = 'application/json'
|
||||
|
||||
$headers = @{
|
||||
'Content-Type' = $expectedStructuredContentType
|
||||
}
|
||||
|
||||
$eventData = @{
|
||||
'l10' = @{
|
||||
'l2' = @{
|
||||
'l3' = 'wow'
|
||||
}
|
||||
}
|
||||
'l11' = 'mhm'
|
||||
}
|
||||
|
||||
$structuredJsonBody = @{
|
||||
'specversion' = $expectedSpecVersion
|
||||
'type' = $expectedType
|
||||
'time' = $expectedTime.ToString("u")
|
||||
'id' = $expectedId
|
||||
'source' = $expectedSource
|
||||
'datacontenttype' = $expectedDataContentType
|
||||
}
|
||||
|
||||
$structuredJsonBody['data_base64'] = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($eventData | ConvertTo-Json -Depth 3)))
|
||||
|
||||
$body = [Text.Encoding]::UTF8.GetBytes(($structuredJsonBody | ConvertTo-Json))
|
||||
|
||||
# Act
|
||||
$actual = ConvertFrom-HttpMessage `
|
||||
-Headers $headers `
|
||||
-Body $body
|
||||
|
||||
# Assert
|
||||
$actual | Should -Not -Be $null
|
||||
$actual.Type | Should -Be $expectedType
|
||||
$actual.Source | Should -Be $expectedSource
|
||||
$actual.Id | Should -Be $expectedId
|
||||
$actual.Time.Year | Should -Be $expectedTime.Year
|
||||
$actual.Time.Month | Should -Be $expectedTime.Month
|
||||
$actual.Time.Day | Should -Be $expectedTime.Day
|
||||
$actual.Time.Hours | Should -Be $expectedTime.Hours
|
||||
$actual.Time.Minutes | Should -Be $expectedTime.Minutes
|
||||
$actual.Time.Seconds | Should -Be $expectedTime.Seconds
|
||||
$actual.Time.MilliSeconds | Should -Be $expectedTime.MilliSeconds
|
||||
$actual.DataContentType | Should -Be $expectedDataContentType
|
||||
|
||||
## Assert Data
|
||||
$actualHTData = $actual | Read-CloudEventJsonData -Depth 3
|
||||
|
||||
$actualHTData | Should -Not -Be $null
|
||||
$actualHTData -is [hashtable] | Should -Be $true
|
||||
$actualHTData.l10.l2.l3 | Should -Be 'wow'
|
||||
$actualHTData.l11 | Should -Be 'mhm'
|
||||
}
|
||||
|
||||
It 'Converts a CloudEvent with required properties and application/xml format data' {
|
||||
# Arrange
|
||||
$expectedType = 'test'
|
||||
$expectedSource = 'urn:test'
|
||||
$expectedDataContentType = 'application/xml'
|
||||
$expectedData = [Text.Encoding]::UTF8.GetBytes('<much wow="xml"/>')
|
||||
|
||||
$headers = @{
|
||||
'Content-Type' = $expectedStructuredContentType
|
||||
}
|
||||
|
||||
$structuredJsonBody = @{
|
||||
'specversion' = $expectedSpecVersion
|
||||
'type' = $expectedType
|
||||
'source' = $expectedSource
|
||||
'datacontenttype' = $expectedDataContentType
|
||||
'data' = $expectedData
|
||||
}
|
||||
|
||||
$body = [Text.Encoding]::UTF8.GetBytes(($structuredJsonBody | ConvertTo-Json))
|
||||
|
||||
# Act
|
||||
$actual = ConvertFrom-HttpMessage `
|
||||
-Headers $headers `
|
||||
-Body $body
|
||||
|
||||
# Assert
|
||||
$actual | Should -Not -Be $null
|
||||
$actual.Type | Should -Be $expectedType
|
||||
$actual.Source | Should -Be $expectedSource
|
||||
$actual.DataContentType | Should -Be $expectedDataContentType
|
||||
$actual.Data | Should -Be $expectedData
|
||||
|
||||
## Assert Data obtained by Read-CloudEventData
|
||||
$actualData = $actual | Read-CloudEventData
|
||||
|
||||
$actualData | Should -Be $expectedData
|
||||
}
|
||||
|
||||
It 'Throws error when CloudEvent encoding is not non-batching JSON' {
|
||||
# Arrange
|
||||
$unsupportedContentFormat = 'application/cloudevents-batch+json'
|
||||
|
||||
$expectedType = 'test'
|
||||
$expectedSource = 'urn:test'
|
||||
$expectedDataContentType = 'application/xml'
|
||||
$expectedData = [Text.Encoding]::UTF8.GetBytes('<much wow="xml"/>')
|
||||
|
||||
$headers = @{
|
||||
'Content-Type' = $unsupportedContentFormat
|
||||
}
|
||||
|
||||
$structuredJsonBody = @{
|
||||
'specversion' = $expectedSpecVersion
|
||||
'type' = $expectedType
|
||||
'source' = $expectedSource
|
||||
'datacontenttype' = $expectedDataContentType
|
||||
'data' = $expectedData
|
||||
}
|
||||
|
||||
$body = [Text.Encoding]::UTF8.GetBytes(($structuredJsonBody | ConvertTo-Json))
|
||||
|
||||
# Act & Assert
|
||||
{ConvertFrom-HttpMessage `
|
||||
-Headers $headers `
|
||||
-Body $body } | `
|
||||
Should -Throw "*Unsupported CloudEvents encoding*"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,223 @@
|
|||
Describe "ConvertTo-HttpMessage Function Tests" {
|
||||
BeforeAll {
|
||||
$expectedSpecVersion = '1.0'
|
||||
$expectedStructuredContentType = 'application/cloudevents+json'
|
||||
}
|
||||
|
||||
Context "Converts CloudEvent in Binary Content Mode" {
|
||||
It 'Converts a CloudEvent with all properties and json format data' {
|
||||
|
||||
# Arrange
|
||||
$expectedType = 'test'
|
||||
$expectedSource = 'urn:test'
|
||||
$expectedId = 'test-id-1'
|
||||
$expectedTime = Get-Date -Year 2021 -Month 1 -Day 18 -Hour 12 -Minute 30 -Second 0
|
||||
$expectedDataContentType = 'application/json'
|
||||
|
||||
$cloudEvent = New-CloudEvent `
|
||||
-Type $expectedType `
|
||||
-Source $expectedSource `
|
||||
-Id $expectedId `
|
||||
-Time $expectedTime
|
||||
|
||||
$expectedData = @{ 'key1' = 'value2'; 'key3' = 'value4' }
|
||||
$cloudEvent = Add-CloudEventJsonData `
|
||||
-CloudEvent $cloudEvent `
|
||||
-Data $expectedData
|
||||
|
||||
|
||||
# Act
|
||||
$actual = $cloudEvent | ConvertTo-HttpMessage -ContentMode Binary
|
||||
|
||||
# Assert
|
||||
$actual | Should -Not -Be $null
|
||||
$actual.Headers | Should -Not -Be $null
|
||||
$actual.Body | Should -Not -Be $null
|
||||
|
||||
## Assert Headers
|
||||
$actual.Headers['Content-Type'] | Should -Be $expectedDataContentType
|
||||
$actual.Headers['ce-source'] | Should -Be $expectedSource
|
||||
$actual.Headers['ce-specversion'] | Should -Be $expectedSpecVersion
|
||||
$actual.Headers['ce-type'] | Should -Be $expectedType
|
||||
$actual.Headers['ce-time'] | Should -Be '2021-01-18 12:30:00Z'
|
||||
$actual.Headers['ce-id'] | Should -Be $expectedId
|
||||
|
||||
## Assert Body
|
||||
|
||||
## Expected Body is
|
||||
## {
|
||||
## "key1": "value2",
|
||||
## "key3": "value4"
|
||||
## }
|
||||
|
||||
|
||||
$actualDecodedBody = [Text.Encoding]::UTF8.GetString($actual.Body) | ConvertFrom-Json -AsHashtable
|
||||
$actualDecodedBody.Keys.Count | Should -Be 2
|
||||
$actualDecodedBody.key1 | Should -Be $expectedData.key1
|
||||
$actualDecodedBody.key3 | Should -Be $expectedData.key3
|
||||
}
|
||||
|
||||
It 'Converts a CloudEvent with required properties and application/xml format data' {
|
||||
# Arrange
|
||||
$expectedType = 'test'
|
||||
$expectedId = 'test-id-1'
|
||||
$expectedSource = 'urn:test'
|
||||
$expectedDataContentType = 'application/xml'
|
||||
|
||||
$cloudEvent = New-CloudEvent `
|
||||
-Id $expectedId `
|
||||
-Type $expectedType `
|
||||
-Source $expectedSource
|
||||
|
||||
$expectedData = '<much wow="xml"/>'
|
||||
$cloudEvent = Add-CloudEventData `
|
||||
-CloudEvent $cloudEvent `
|
||||
-Data $expectedData `
|
||||
-DataContentType $expectedDataContentType
|
||||
|
||||
# Act
|
||||
$actual = $cloudEvent | ConvertTo-HttpMessage -ContentMode Binary
|
||||
|
||||
# Assert
|
||||
$actual | Should -Not -Be $null
|
||||
$actual.Headers | Should -Not -Be $null
|
||||
$actual.Body | Should -Not -Be $null
|
||||
|
||||
## Assert Headers
|
||||
$actual.Headers['Content-Type'] | Should -Be $expectedDataContentType
|
||||
$actual.Headers['ce-source'] | Should -Be $expectedSource
|
||||
$actual.Headers['ce-specversion'] | Should -Be $expectedSpecVersion
|
||||
$actual.Headers['ce-type'] | Should -Be $expectedType
|
||||
$actual.Headers['ce-id'] | Should -Be $expectedId
|
||||
|
||||
## Assert Body
|
||||
|
||||
## Expected Body is
|
||||
## <much wow="xml"/>
|
||||
$actualDecodedBody =[Text.Encoding]::UTF8.GetString($actual.Body)
|
||||
$actualDecodedBody | Should -Be $expectedData
|
||||
}
|
||||
}
|
||||
|
||||
Context "Converts CloudEvent in Structured Content Mode" {
|
||||
It 'Converts a CloudEvent with all properties and json format data' {
|
||||
# Arrange
|
||||
$expectedType = 'test'
|
||||
$expectedSource = 'urn:test'
|
||||
$expectedId = 'test-id-1'
|
||||
$expectedTime = Get-Date -Year 2021 -Month 1 -Day 18 -Hour 12 -Minute 30 -Second 0
|
||||
$expectedDataContentType = 'application/json'
|
||||
|
||||
$cloudEvent = New-CloudEvent `
|
||||
-Type $expectedType `
|
||||
-Source $expectedSource `
|
||||
-Id $expectedId `
|
||||
-Time $expectedTime
|
||||
|
||||
$expectedData = @{ 'key1' = 'value2'; 'key3' = 'value4' }
|
||||
$cloudEvent = Add-CloudEventJsonData `
|
||||
-CloudEvent $cloudEvent `
|
||||
-Data $expectedData
|
||||
|
||||
|
||||
# Act
|
||||
$actual = $cloudEvent | ConvertTo-HttpMessage -ContentMode Structured
|
||||
|
||||
# Assert
|
||||
$actual | Should -Not -Be $null
|
||||
$actual.Headers | Should -Not -Be $null
|
||||
$actual.Body | Should -Not -Be $null
|
||||
|
||||
## Assert Headers
|
||||
$headerContentTypes = $actual.Headers['Content-Type'].ToString().Split(';')
|
||||
$headerContentTypes[0].Trim() | Should -Be $expectedStructuredContentType
|
||||
$headerContentTypes[1].Trim() | Should -Be 'charset=utf-8'
|
||||
|
||||
$actual.Headers['ce-source'] | Should -Be $expectedSource
|
||||
$actual.Headers['ce-specversion'] | Should -Be $expectedSpecVersion
|
||||
$actual.Headers['ce-type'] | Should -Be $expectedType
|
||||
$actual.Headers['ce-time'] | Should -Be '2021-01-18 12:30:00Z'
|
||||
$actual.Headers['ce-id'] | Should -Be $expectedId
|
||||
|
||||
## Assert Body
|
||||
|
||||
## Expected Body is
|
||||
## {
|
||||
## "specversion": "1.0",
|
||||
## "type": "test",
|
||||
## "source": "urn:test",
|
||||
## "id": "test-id-1",
|
||||
## "time": "2021-01-18T12:30:00.9785466+02:00",
|
||||
## "datacontenttype": "application/json",
|
||||
## "data": "{
|
||||
## "key1": "value2",
|
||||
## "key3": "value4"
|
||||
## }"
|
||||
## }
|
||||
$actualDecodedBody = [Text.Encoding]::UTF8.GetString($actual.Body) | ConvertFrom-Json -Depth 1
|
||||
|
||||
$actualDecodedBody.specversion | Should -Be $expectedSpecVersion
|
||||
$actualDecodedBody.type | Should -Be $expectedType
|
||||
$actualDecodedBody.source | Should -Be $expectedSource
|
||||
Get-Date $actualDecodedBody.time | Should -Be $expectedTime
|
||||
$actualDecodedBody.datacontenttype | Should -Be $expectedDataContentType
|
||||
$actualDecodedData = $actualDecodedBody.data | ConvertFrom-Json -AsHashtable
|
||||
$actualDecodedData.Keys.Count | Should -Be 2
|
||||
$actualDecodedData.key1 | Should -Be $expectedData.key1
|
||||
$actualDecodedData.key3 | Should -Be $expectedData.key3
|
||||
}
|
||||
|
||||
It 'Converts a CloudEvent with required properties and application/xml format data' {
|
||||
# Arrange
|
||||
$expectedId = ([Guid]::NewGuid().ToString())
|
||||
$expectedType = 'test'
|
||||
$expectedSource = 'urn:test'
|
||||
$expectedDataContentType = 'application/xml'
|
||||
|
||||
$cloudEvent = New-CloudEvent `
|
||||
-Id $expectedId `
|
||||
-Type $expectedType `
|
||||
-Source $expectedSource
|
||||
|
||||
$expectedData = '<much wow="xml"/>'
|
||||
$cloudEvent = Add-CloudEventData `
|
||||
-CloudEvent $cloudEvent `
|
||||
-Data $expectedData `
|
||||
-DataContentType $expectedDataContentType
|
||||
|
||||
# Act
|
||||
$actual = $cloudEvent | ConvertTo-HttpMessage -ContentMode Structured
|
||||
|
||||
# Assert
|
||||
$actual | Should -Not -Be $null
|
||||
$actual.Headers | Should -Not -Be $null
|
||||
$actual.Body | Should -Not -Be $null
|
||||
|
||||
## Assert Headers
|
||||
$headerContentTypes = $actual.Headers['Content-Type'].ToString().Split(';')
|
||||
$headerContentTypes[0].Trim() | Should -Be $expectedStructuredContentType
|
||||
$headerContentTypes[1].Trim() | Should -Be 'charset=utf-8'
|
||||
$actual.Headers['ce-source'] | Should -Be $expectedSource
|
||||
$actual.Headers['ce-specversion'] | Should -Be $expectedSpecVersion
|
||||
$actual.Headers['ce-type'] | Should -Be $expectedType
|
||||
$actual.Headers['ce-id'] | Should -Be $expectedId
|
||||
|
||||
## Assert Body
|
||||
|
||||
## Expected Body is
|
||||
## {
|
||||
## "specversion": "1.0",
|
||||
## "type": "test",
|
||||
## "source": "urn:test",
|
||||
## "datacontenttype": "application/xml",
|
||||
## "data": "<much wow=/"xml/"/>"
|
||||
## }
|
||||
$actualDecodedBody = [Text.Encoding]::UTF8.GetString($actual.Body) | ConvertFrom-Json -Depth 1
|
||||
$actualDecodedBody.specversion | Should -Be $expectedSpecVersion
|
||||
$actualDecodedBody.type | Should -Be $expectedType
|
||||
$actualDecodedBody.source | Should -Be $expectedSource
|
||||
$actualDecodedBody.datacontenttype | Should -Be $expectedDataContentType
|
||||
$actualDecodedBody.data | Should -Be $expectedData
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
Describe "New-CloudEvent Function Tests" {
|
||||
Context "Create CloudEvent Object" {
|
||||
It 'Create CloudEvent with required parameters only' {
|
||||
# Arrange
|
||||
$expectedType = 'test'
|
||||
$expectedSource = 'urn:test'
|
||||
$expectedId = ([Guid]::NewGuid().ToString())
|
||||
|
||||
# Act
|
||||
$actual = New-CloudEvent `
|
||||
-Id $expectedId `
|
||||
-Type $expectedType `
|
||||
-Source $expectedSource
|
||||
|
||||
# Assert
|
||||
$actual | Should -Not -Be $null
|
||||
$actual.Type | Should -Be $expectedType
|
||||
$actual.Source | Should -Be $expectedSource
|
||||
$actual.Id | Should -Be $expectedId
|
||||
}
|
||||
|
||||
It 'Create CloudEvent with all possible parameters' {
|
||||
# Arrange
|
||||
$expectedType = 'test'
|
||||
$expectedSource = 'urn:test'
|
||||
$expectedId = 'test-id-1'
|
||||
$expectedTime = Get-Date -Year 2021 -Month 1 -Day 18 -Hour 12 -Minute 30 -Second 0
|
||||
|
||||
# Act
|
||||
$actual = New-CloudEvent `
|
||||
-Type $expectedType `
|
||||
-Source $expectedSource `
|
||||
-Id $expectedId `
|
||||
-Time $expectedTime
|
||||
|
||||
# Assert
|
||||
$actual | Should -Not -Be $null
|
||||
$actual.Type | Should -Be $expectedType
|
||||
$actual.Source | Should -Be $expectedSource
|
||||
$actual.Id | Should -Be $expectedId
|
||||
$actual.Time | Should -Be $expectedTime
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
Describe "Read-CloudEventData Function Tests" {
|
||||
Context "Extracts Data from CloudEvent" {
|
||||
It 'Reads xml text data' {
|
||||
# Arrange
|
||||
$cloudEvent = New-CloudEvent `
|
||||
-Id ([Guid]::NewGuid()) `
|
||||
-Type test `
|
||||
-Source 'urn:test'
|
||||
|
||||
|
||||
$expectedData = '<much wow="xml"/>'
|
||||
$expectedDataContentType = 'text/xml'
|
||||
|
||||
$cloudEvent = $cloudEvent | Add-CloudEventData -Data $expectedData -DataContentType $expectedDataContentType
|
||||
|
||||
# Act
|
||||
$actual = $cloudEvent | Read-CloudEventData
|
||||
|
||||
# Assert
|
||||
$actual | Should -Not -Be $null
|
||||
$actual | Should -Be $expectedData
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
Describe "Read-CloudEventJsonData Function Tests" {
|
||||
Context "Extracts Json Data from CloudEvent" {
|
||||
It 'Extracts hashtable from CloudEvent with json data' {
|
||||
# Arrange
|
||||
$cloudEvent = New-CloudEvent `
|
||||
-Id ([Guid]::NewGuid()) `
|
||||
-Type test `
|
||||
-Source 'urn:test'
|
||||
|
||||
$expectedHtData = @{'a' = 'b'}
|
||||
|
||||
$cloudEvent = $cloudEvent | Add-CloudEventJsonData -Data $expectedHtData
|
||||
|
||||
# Act
|
||||
$actual = $cloudEvent | Read-CloudEventJsonData
|
||||
|
||||
# Assert
|
||||
$actual | Should -Not -Be $null
|
||||
$actual -is [hashtable] | Should -Be $true
|
||||
$actual.a | Should -Be 'b'
|
||||
}
|
||||
|
||||
It 'Expects error when CloudEvent data is not json' {
|
||||
# Arrange
|
||||
$cloudEvent = New-CloudEvent `
|
||||
-Id ([Guid]::NewGuid()) `
|
||||
-Type test `
|
||||
-Source 'urn:test'
|
||||
|
||||
$cloudEvent = $cloudEvent | Add-CloudEventData -Data "test" -DataContentType 'application/text'
|
||||
$pre
|
||||
|
||||
# Act
|
||||
{ $cloudEvent | Read-CloudEventJsonData -ErrorAction Stop } | `
|
||||
Should -Throw "*Cloud Event '$($cloudEvent.Id)' has no json data*"
|
||||
|
||||
}
|
||||
|
||||
It 'Extracts hashtable from CloudEvent with json data with depth 4' {
|
||||
# Arrange
|
||||
$cloudEvent = New-CloudEvent `
|
||||
-Id ([Guid]::NewGuid()) `
|
||||
-Type test `
|
||||
-Source 'urn:test'
|
||||
|
||||
$expectedHtData = @{
|
||||
'l1' = @{
|
||||
'l2' = @{
|
||||
'l3' = @{
|
||||
'l4' = 'wow'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$cloudEvent = $cloudEvent | Add-CloudEventJsonData -Data $expectedHtData -Depth 4
|
||||
|
||||
# Act
|
||||
$actual = $cloudEvent | Read-CloudEventJsonData -Depth 4
|
||||
|
||||
# Assert
|
||||
$actual | Should -Not -Be $null
|
||||
$actual -is [hashtable] | Should -Be $true
|
||||
$actual.l1.l2.l3.l4 | Should -Be 'wow'
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
Describe "Read-CloudEventXmlData Function Tests" {
|
||||
Context "Extracts Xml Data from CloudEvent" {
|
||||
It 'Extracts hashtable from CloudEvent with xml data' {
|
||||
# Arrange
|
||||
$cloudEvent = New-CloudEvent `
|
||||
-Id ([Guid]::NewGuid()) `
|
||||
-Type test `
|
||||
-Source 'urn:test'
|
||||
|
||||
$xmlData = "<a>b</a>"
|
||||
$expectedHtData = @{'a' = 'b'}
|
||||
|
||||
$cloudEvent = $cloudEvent | Add-CloudEventData -Data $xmlData -DataContentType 'application/xml'
|
||||
|
||||
# Act
|
||||
$actual = $cloudEvent | Read-CloudEventXmlData -ConvertMode 'SkipAttributes'
|
||||
|
||||
# Assert
|
||||
$actual | Should -Not -Be $null
|
||||
$actual -is [hashtable] | Should -Be $true
|
||||
$actual.a | Should -Be 'b'
|
||||
}
|
||||
|
||||
It 'Expects error when CloudEvent data is not xml' {
|
||||
# Arrange
|
||||
$cloudEvent = New-CloudEvent `
|
||||
-Id ([Guid]::NewGuid()) `
|
||||
-Type test `
|
||||
-Source 'urn:test'
|
||||
|
||||
$cloudEvent = $cloudEvent | Add-CloudEventData -Data "test" -DataContentType 'application/text'
|
||||
$pre
|
||||
|
||||
# Act
|
||||
{ $cloudEvent | Read-CloudEventXmlData -ConvertMode 'SkipAttributes' -ErrorAction Stop } | `
|
||||
Should -Throw "*Cloud Event '$($cloudEvent.Id)' has no xml data*"
|
||||
|
||||
}
|
||||
|
||||
It 'Extracts hashtable from CloudEvent with xml data with depth 4' {
|
||||
# Arrange
|
||||
$cloudEvent = New-CloudEvent `
|
||||
-Id ([Guid]::NewGuid()) `
|
||||
-Type test `
|
||||
-Source 'urn:test'
|
||||
|
||||
$xmlData = '<l1><l2><l3><l4>wow</l4></l3></l2></l1>'
|
||||
$expectedHtData = @{
|
||||
'l1' = @{
|
||||
'l2' = @{
|
||||
'l3' = @{
|
||||
'l4' = 'wow'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$cloudEvent = $cloudEvent | Add-CloudEventData -Data $xmlData -DataContentType 'application/xml'
|
||||
|
||||
# Act
|
||||
$actual = $cloudEvent | Read-CloudEventXmlData -ConvertMode 'SkipAttributes'
|
||||
|
||||
# Assert
|
||||
$actual | Should -Not -Be $null
|
||||
$actual -is [hashtable] | Should -Be $true
|
||||
$actual.l1.l2.l3.l4 | Should -Be 'wow'
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue