Merge pull request #43 from dmilov/topic/no-content-type
fix: Incorrect parsing of Binary Content Mode cloud events
This commit is contained in:
commit
c715c5c97e
|
@ -9,7 +9,7 @@
|
||||||
RootModule = 'CloudEvents.Sdk.psm1'
|
RootModule = 'CloudEvents.Sdk.psm1'
|
||||||
|
|
||||||
# Version number of this module.
|
# Version number of this module.
|
||||||
ModuleVersion = '0.3.0'
|
ModuleVersion = '0.3.1'
|
||||||
|
|
||||||
# Supported PSEditions
|
# Supported PSEditions
|
||||||
CompatiblePSEditions = @('Core')
|
CompatiblePSEditions = @('Core')
|
||||||
|
|
|
@ -349,7 +349,7 @@ PROCESS {
|
||||||
-ArgumentList ([System.Net.Mime.MediaTypeNames+Application]::Json)
|
-ArgumentList ([System.Net.Mime.MediaTypeNames+Application]::Json)
|
||||||
|
|
||||||
if ($CloudEvent.DataContentType -eq $dataContentType -or `
|
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.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])) {
|
$cloudEvent.Data -is [Newtonsoft.Json.Linq.JObject])) {
|
||||||
|
|
||||||
$data = $cloudEvent.Data
|
$data = $cloudEvent.Data
|
||||||
|
@ -361,7 +361,8 @@ PROCESS {
|
||||||
$result = $data.ToString() | ConvertFrom-Json -AsHashtable -Depth $Depth
|
$result = $data.ToString() | ConvertFrom-Json -AsHashtable -Depth $Depth
|
||||||
|
|
||||||
Write-Output $result
|
Write-Output $result
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
Write-Error "Cloud Event '$($cloudEvent.Id)' has no json data"
|
Write-Error "Cloud Event '$($cloudEvent.Id)' has no json data"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -468,7 +469,8 @@ PROCESS {
|
||||||
$result = $data.ToString() | ConvertFrom-CEDataXml -ConvertMode $ConvertMode
|
$result = $data.ToString() | ConvertFrom-CEDataXml -ConvertMode $ConvertMode
|
||||||
|
|
||||||
Write-Output $result
|
Write-Output $result
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
Write-Error "Cloud Event '$($cloudEvent.Id)' has no xml data"
|
Write-Error "Cloud Event '$($cloudEvent.Id)' has no xml data"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -561,8 +563,7 @@ PROCESS {
|
||||||
elseif ($attribute.Value -is [Uri] -or $attribute.Value -is [int]) {
|
elseif ($attribute.Value -is [Uri] -or $attribute.Value -is [int]) {
|
||||||
$headers.Add(($HttpHeaderPrefix + $attribute.Key), $attribute.Value.ToString())
|
$headers.Add(($HttpHeaderPrefix + $attribute.Key), $attribute.Value.ToString())
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
$headers.Add(($HttpHeaderPrefix + $attribute.Key),
|
$headers.Add(($HttpHeaderPrefix + $attribute.Key),
|
||||||
[System.Text.Encoding]::UTF8.GetString($cloudEventFormatter.EncodeAttribute($cloudEvent.SpecVersion, $attribute.Key,
|
[System.Text.Encoding]::UTF8.GetString($cloudEventFormatter.EncodeAttribute($cloudEvent.SpecVersion, $attribute.Key,
|
||||||
$attribute.Value,
|
$attribute.Value,
|
||||||
|
@ -609,16 +610,17 @@ PROCESS {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$read = 0
|
$read = 0
|
||||||
while (($read = $cloudEvent.Data.Read($buffer, 0, 1024)) -gt 0)
|
while (($read = $cloudEvent.Data.Read($buffer, 0, 1024)) -gt 0) {
|
||||||
{
|
|
||||||
$ms.Write($buffer, 0, $read);
|
$ms.Write($buffer, 0, $read);
|
||||||
}
|
}
|
||||||
$bodyData = $ms.ToArray()
|
$bodyData = $ms.ToArray()
|
||||||
} finally {
|
}
|
||||||
|
finally {
|
||||||
$ms.Dispose()
|
$ms.Dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
$bodyData = $cloudEventFormatter.EncodeAttribute($cloudEvent.SpecVersion,
|
$bodyData = $cloudEventFormatter.EncodeAttribute($cloudEvent.SpecVersion,
|
||||||
[CloudNative.CloudEvents.CloudEventAttributes]::DataAttributeName($cloudEvent.SpecVersion),
|
[CloudNative.CloudEvents.CloudEventAttributes]::DataAttributeName($cloudEvent.SpecVersion),
|
||||||
$cloudEvent.Data, $cloudEvent.Extensions.Values)
|
$cloudEvent.Data, $cloudEvent.Extensions.Values)
|
||||||
|
@ -674,8 +676,7 @@ param(
|
||||||
|
|
||||||
PROCESS {
|
PROCESS {
|
||||||
$HttpHeaderPrefix = "ce-";
|
$HttpHeaderPrefix = "ce-";
|
||||||
$SpecVersionHttpHeader1 = $HttpHeaderPrefix + "cloudEventsVersion";
|
$SpecVersionHttpHeader = $HttpHeaderPrefix + "specversion";
|
||||||
$SpecVersionHttpHeader2 = $HttpHeaderPrefix + "specversion";
|
|
||||||
|
|
||||||
$result = $null
|
$result = $null
|
||||||
|
|
||||||
|
@ -687,7 +688,7 @@ PROCESS {
|
||||||
$Body = [System.Text.Encoding]::UTF8.GetBytes($Body)
|
$Body = [System.Text.Encoding]::UTF8.GetBytes($Body)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($Headers['Content-Type'] -ne $null) {
|
if ($null -ne $Headers['Content-Type']) {
|
||||||
$ContentType = $Headers['Content-Type']
|
$ContentType = $Headers['Content-Type']
|
||||||
if ($ContentType -is [array]) {
|
if ($ContentType -is [array]) {
|
||||||
# Get the first content-type value
|
# Get the first content-type value
|
||||||
|
@ -714,25 +715,25 @@ PROCESS {
|
||||||
$result = $formatter.DecodeJObject($jObject, $null)
|
$result = $formatter.DecodeJObject($jObject, $null)
|
||||||
|
|
||||||
$result.Data = $result.Data
|
$result.Data = $result.Data
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
# Throw error for unsupported encoding
|
# Throw error for unsupported encoding
|
||||||
throw "Unsupported CloudEvents encoding"
|
throw "Unsupported CloudEvents encoding"
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
# Handle Binary Mode
|
# Handle Binary Mode
|
||||||
$version = [CloudNative.CloudEvents.CloudEventsSpecVersion]::Default
|
$version = $null
|
||||||
if ($Headers.Contains($SpecVersionHttpHeader1)) {
|
if ($Headers.Contains($SpecVersionHttpHeader) -and `
|
||||||
$version = [CloudNative.CloudEvents.CloudEventsSpecVersion]::V0_1
|
$null -ne $Headers[$SpecVersionHttpHeader] -and `
|
||||||
}
|
($Headers[$SpecVersionHttpHeader] | Select-Object -First 1).StartsWith('1.0')) {
|
||||||
|
# We do support the 1.0 cloud event version
|
||||||
if ($Headers.Contains($SpecVersionHttpHeader2)) {
|
$version = [CloudNative.CloudEvents.CloudEventsSpecVersion]::V1_0
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($null -ne $version) {
|
||||||
|
# SpecVersion is REQUIRED attribute, it it is not specified this is not a CloudEvent
|
||||||
|
# https://github.com/cloudevents/spec/blob/v1.0.1/spec.md#specversion
|
||||||
$cloudEvent = New-Object `
|
$cloudEvent = New-Object `
|
||||||
-TypeName 'CloudNative.CloudEvents.CloudEvent' `
|
-TypeName 'CloudNative.CloudEvents.CloudEvent' `
|
||||||
-ArgumentList @($version, $null);
|
-ArgumentList @($version, $null);
|
||||||
|
@ -741,8 +742,7 @@ PROCESS {
|
||||||
|
|
||||||
# Get attributes from HTTP Headers
|
# Get attributes from HTTP Headers
|
||||||
foreach ($httpHeader in $Headers.GetEnumerator()) {
|
foreach ($httpHeader in $Headers.GetEnumerator()) {
|
||||||
if ($httpHeader.Key.Equals($SpecVersionHttpHeader1, [StringComparison]::InvariantCultureIgnoreCase) -or `
|
if ($httpHeader.Key.Equals($SpecVersionHttpHeader, [StringComparison]::InvariantCultureIgnoreCase)) {
|
||||||
$httpHeader.Key.Equals($SpecVersionHttpHeader2, [StringComparison]::InvariantCultureIgnoreCase)) {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -755,8 +755,7 @@ PROCESS {
|
||||||
$name = $httpHeader.Key.Substring(3);
|
$name = $httpHeader.Key.Substring(3);
|
||||||
|
|
||||||
# Abolished structures in headers in 1.0
|
# Abolished structures in headers in 1.0
|
||||||
if ($version -ne [CloudNative.CloudEvents.CloudEventsSpecVersion]::V0_1 -and `
|
if ( $null -ne $headerValue -and `
|
||||||
$headerValue -ne $null -and `
|
|
||||||
$headerValue.StartsWith('"') -and `
|
$headerValue.StartsWith('"') -and `
|
||||||
$headerValue.EndsWith('"') -or `
|
$headerValue.EndsWith('"') -or `
|
||||||
$headerValue.StartsWith("'") -and $headerValue.EndsWith("'") -or `
|
$headerValue.StartsWith("'") -and $headerValue.EndsWith("'") -or `
|
||||||
|
@ -767,22 +766,39 @@ PROCESS {
|
||||||
|
|
||||||
$attributes[$name] = $jsonFormatter.DecodeAttribute($version, $name,
|
$attributes[$name] = $jsonFormatter.DecodeAttribute($version, $name,
|
||||||
[System.Text.Encoding]::UTF8.GetBytes($headerValue), $null);
|
[System.Text.Encoding]::UTF8.GetBytes($headerValue), $null);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
$attributes[$name] = $headerValue
|
$attributes[$name] = $headerValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($Headers['Content-Type'] -ne $null -and $Headers['Content-Type'][0] -is [string]) {
|
# Verify parsed attributes from HTTP Headers
|
||||||
|
if ($null -ne $attributes['datacontenttype']) {
|
||||||
|
# ce-datatype is prohibitted by the protocol -> throw error
|
||||||
|
# https://github.com/cloudevents/spec/blob/v1.0.1/http-protocol-binding.md#311-http-content-type
|
||||||
|
throw "'ce-datacontenttype' HTTP header is prohibited for Binary ContentMode CloudEvent"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($Headers['Content-Type'] -is [string]) {
|
||||||
|
$cloudEvent.DataContentType = New-Object 'System.Net.Mime.ContentType' -ArgumentList @($Headers['Content-Type'])
|
||||||
|
} elseif ($Headers['Content-Type'][0] -is [string]) {
|
||||||
$cloudEvent.DataContentType = New-Object 'System.Net.Mime.ContentType' -ArgumentList @($Headers['Content-Type'][0])
|
$cloudEvent.DataContentType = New-Object 'System.Net.Mime.ContentType' -ArgumentList @($Headers['Content-Type'][0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Id, Type, and Source are reuiqred to be non-empty strings otherwise consider this is not a CloudEvent
|
||||||
|
# https://github.com/cloudevents/spec/blob/v1.0.1/spec.md#required-attributes
|
||||||
|
if ( -not [string]::IsNullOrEmpty($cloudEvent.Id) -and `
|
||||||
|
-not [string]::IsNullOrEmpty($cloudEvent.Source) -and `
|
||||||
|
-not [string]::IsNullOrEmpty($cloudEvent.Type)) {
|
||||||
# Get Data from HTTP Body
|
# Get Data from HTTP Body
|
||||||
$cloudEvent.Data = $Body
|
$cloudEvent.Data = $Body
|
||||||
|
|
||||||
$result = $cloudEvent
|
$result = $cloudEvent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Write-Output $result
|
Write-Output $result
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
# **************************************************************************
|
# **************************************************************************
|
||||||
|
|
||||||
Describe "ConvertFrom-HttpMessage Function Tests" {
|
Describe "ConvertFrom-HttpMessage Function Tests" {
|
||||||
BeforeAll {
|
BeforeEach {
|
||||||
$expectedSpecVersion = '1.0'
|
$script:expectedSpecVersion = '1.0'
|
||||||
$expectedStructuredContentType = 'application/cloudevents+json'
|
$script:expectedStructuredContentType = 'application/cloudevents+json'
|
||||||
}
|
}
|
||||||
|
|
||||||
Context "Converts CloudEvent in Binary Content Mode" {
|
Context "Converts CloudEvent in Binary Content Mode" {
|
||||||
|
@ -73,6 +73,43 @@ Describe "ConvertFrom-HttpMessage Function Tests" {
|
||||||
}
|
}
|
||||||
|
|
||||||
It 'Converts a CloudEvent with required properties and application/xml format data' {
|
It 'Converts a CloudEvent with required properties and application/xml format data' {
|
||||||
|
# Arrange
|
||||||
|
$expectedType = 'test'
|
||||||
|
$expectedSource = 'urn:test'
|
||||||
|
$expectedDataContentType = 'application/xml'
|
||||||
|
$expectedId = 'test-id-2'
|
||||||
|
$expectedData = [Text.Encoding]::UTF8.GetBytes('<much wow="xml"/>')
|
||||||
|
|
||||||
|
$headers = @{
|
||||||
|
'Content-Type' = @($expectedDataContentType, 'charset=utf-8')
|
||||||
|
'ce-specversion' = $expectedSpecVersion
|
||||||
|
'ce-type' = $expectedType
|
||||||
|
'ce-source' = $expectedSource
|
||||||
|
'ce-id' = $expectedId
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = $expectedData
|
||||||
|
|
||||||
|
# Act
|
||||||
|
$actual = ConvertFrom-HttpMessage `
|
||||||
|
-Headers $headers `
|
||||||
|
-Body $body
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
$actual | Should -Not -Be $null
|
||||||
|
$actual.Id | Should -Be $expectedId
|
||||||
|
$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 'Returns null when ce-id is not specified' {
|
||||||
# Arrange
|
# Arrange
|
||||||
$expectedType = 'test'
|
$expectedType = 'test'
|
||||||
$expectedSource = 'urn:test'
|
$expectedSource = 'urn:test'
|
||||||
|
@ -94,16 +131,57 @@ Describe "ConvertFrom-HttpMessage Function Tests" {
|
||||||
-Body $body
|
-Body $body
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
$actual | Should -Not -Be $null
|
$actual | Should -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
|
It 'Returns null when ce-source is not specified' {
|
||||||
$actualData = $actual | Read-CloudEventData
|
# Arrange
|
||||||
|
$expectedType = 'test'
|
||||||
|
$expectedDataContentType = 'application/xml'
|
||||||
|
$expectedId = 'test-id-3'
|
||||||
|
$expectedData = [Text.Encoding]::UTF8.GetBytes('<much wow="xml"/>')
|
||||||
|
|
||||||
$actualData | Should -Be $expectedData
|
$headers = @{
|
||||||
|
'Content-Type' = @($expectedDataContentType, 'charset=utf-8')
|
||||||
|
'ce-specversion' = $expectedSpecVersion
|
||||||
|
'ce-type' = $expectedType
|
||||||
|
'ce-id' = $expectedId
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = $expectedData
|
||||||
|
|
||||||
|
# Act
|
||||||
|
$actual = ConvertFrom-HttpMessage `
|
||||||
|
-Headers $headers `
|
||||||
|
-Body $body
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
$actual | Should -Be $null
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns null when ce-type is not specified' {
|
||||||
|
# Arrange
|
||||||
|
$expectedSource = 'urn:test'
|
||||||
|
$expectedDataContentType = 'application/xml'
|
||||||
|
$expectedId = 'test-id-4'
|
||||||
|
$expectedData = [Text.Encoding]::UTF8.GetBytes('<much wow="xml"/>')
|
||||||
|
|
||||||
|
$headers = @{
|
||||||
|
'Content-Type' = @($expectedDataContentType, 'charset=utf-8')
|
||||||
|
'ce-specversion' = $expectedSpecVersion
|
||||||
|
'ce-source' = $expectedSource
|
||||||
|
'ce-id' = $expectedId
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = $expectedData
|
||||||
|
|
||||||
|
# Act
|
||||||
|
$actual = ConvertFrom-HttpMessage `
|
||||||
|
-Headers $headers `
|
||||||
|
-Body $body
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
$actual | Should -Be $null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,5 +323,29 @@ Describe "ConvertFrom-HttpMessage Function Tests" {
|
||||||
-Body $body } | `
|
-Body $body } | `
|
||||||
Should -Throw "*Unsupported CloudEvents encoding*"
|
Should -Throw "*Unsupported CloudEvents encoding*"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
It 'Returns null when no Content-Type header' {
|
||||||
|
# Arrange
|
||||||
|
|
||||||
|
$expectedType = 'test'
|
||||||
|
$expectedSource = 'urn:test'
|
||||||
|
$expectedDataContentType = 'application/xml'
|
||||||
|
$expectedData = [Text.Encoding]::UTF8.GetBytes('<much wow="xml"/>')
|
||||||
|
$structuredJsonBody = @{
|
||||||
|
'specversion' = $expectedSpecVersion
|
||||||
|
'type' = $expectedType
|
||||||
|
'source' = $expectedSource
|
||||||
|
'datacontenttype' = $expectedDataContentType
|
||||||
|
'data' = $expectedData
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = [Text.Encoding]::UTF8.GetBytes(($structuredJsonBody | ConvertTo-Json))
|
||||||
|
|
||||||
|
# Act & Assert
|
||||||
|
$ce = ConvertFrom-HttpMessage `
|
||||||
|
-Headers @{} `
|
||||||
|
-Body $body
|
||||||
|
$ce | Should -Be $null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue