11import { HttpRequest , HttpResponse } from "@smithy/protocol-http" ;
2- import { StaticOperationSchema } from "@smithy/types" ;
2+ import { StaticOperationSchema , StaticStructureSchema } from "@smithy/types" ;
33import { toUtf8 } from "@smithy/util-utf8" ;
4- import { Readable } from "node:stream" ;
54import { describe , expect , test as it } from "vitest" ;
65
76import { context , deleteObjects } from "../test-schema.spec" ;
@@ -13,6 +12,11 @@ describe(AwsRestXmlProtocol.name, () => {
1312 schema : deleteObjects ,
1413 } ;
1514
15+ const protocol = new AwsRestXmlProtocol ( {
16+ xmlNamespace : "http://s3.amazonaws.com/doc/2006-03-01/" ,
17+ defaultNamespace : "com.amazonaws.s3" ,
18+ } ) ;
19+
1620 describe ( "serialization" , ( ) => {
1721 const testCases = [
1822 {
@@ -59,11 +63,6 @@ describe(AwsRestXmlProtocol.name, () => {
5963
6064 for ( const testCase of testCases ) {
6165 it ( `should serialize HTTP Requests: ${ testCase . name } ` , async ( ) => {
62- const protocol = new AwsRestXmlProtocol ( {
63- xmlNamespace : "http://s3.amazonaws.com/doc/2006-03-01/" ,
64- defaultNamespace : "com.amazonaws.s3" ,
65- } ) ;
66-
6766 const [ , namespace , name , traits , input , output ] = command . schema as StaticOperationSchema ;
6867
6968 const httpRequest = await protocol . serializeRequest (
@@ -95,74 +94,135 @@ describe(AwsRestXmlProtocol.name, () => {
9594 ) ;
9695 } ) ;
9796 }
98- } ) ;
9997
100- it ( "deserializes http responses" , async ( ) => {
101- const httpResponse = new HttpResponse ( {
102- statusCode : 200 ,
103- headers : { } ,
104- } ) ;
98+ it ( "should prepend an xml declaration only if the content type is application/xml and the input schema does not have a payload binding" , async ( ) => {
99+ const document = [
100+ 3 ,
101+ "ns" ,
102+ "Struct" ,
103+ 0 ,
104+ [ "a" , "b" , "c" , "h" ] ,
105+ [ 0 , 0 , 0 , [ 0 , { httpHeader : "content-type" } ] ] ,
106+ ] satisfies StaticStructureSchema ;
107+ const payload = [
108+ 3 ,
109+ "ns" ,
110+ "PayloadStruct" ,
111+ 0 ,
112+ [ "a" , "b" , "c" , "h" ] ,
113+ [ 0 , 0 , [ 0 , { httpPayload : 1 } ] , [ 0 , { httpHeader : "content-type" } ] ] ,
114+ ] satisfies StaticStructureSchema ;
115+
116+ const createOperation = ( input : StaticStructureSchema ) => ( {
117+ namespace : "ns" ,
118+ name : "operation" ,
119+ traits : { } ,
120+ input,
121+ output : "unit" as const ,
122+ } ) ;
105123
106- const protocol = new AwsRestXmlProtocol ( {
107- defaultNamespace : "" ,
108- xmlNamespace : "ns" ,
109- } ) ;
124+ const httpRequest1 = await protocol . serializeRequest (
125+ createOperation ( document ) ,
126+ {
127+ c : "<XML></XML>" ,
128+ h : "application/xml" ,
129+ } ,
130+ context
131+ ) ;
132+
133+ // this is not a payload binding, so although the
134+ // content and header appear to be XML,
135+ // the data is encoded within an XML container structure and the xml declaration
136+ // is prepended by the SDK.
137+ expect ( httpRequest1 . body ) . toBe (
138+ `<?xml version="1.0" encoding="UTF-8"?><Struct xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><c><XML></XML></c></Struct>`
139+ ) ;
140+
141+ const httpRequest2 = await protocol . serializeRequest (
142+ createOperation ( payload ) ,
143+ {
144+ c : "<XML></XML>" ,
145+ h : "application/xml" ,
146+ } ,
147+ context
148+ ) ;
110149
111- const output = await protocol . deserializeResponse (
112- {
113- namespace : deleteObjects [ 1 ] ,
114- name : deleteObjects [ 2 ] ,
115- traits : deleteObjects [ 3 ] ,
116- input : deleteObjects [ 4 ] ,
117- output : deleteObjects [ 5 ] ,
118- } ,
119- context ,
120- httpResponse
121- ) ;
122-
123- expect ( output ) . toEqual ( {
124- $metadata : {
125- httpStatusCode : 200 ,
126- requestId : undefined ,
127- extendedRequestId : undefined ,
128- cfId : undefined ,
129- } ,
130- } ) ;
131- } ) ;
150+ // even though this could be interpreted as XML input by the content and the header value,
151+ // because this operation is a payload binding of a string,
152+ // we simply send what the caller has provided rather than prepending the XML declaration.
153+ expect ( httpRequest2 . body ) . toBe ( `<XML></XML>` ) ;
132154
133- it ( "decorates service exceptions with unmodeled fields" , async ( ) => {
134- const httpResponse = new HttpResponse ( {
135- statusCode : 400 ,
136- headers : { } ,
137- body : Buffer . from ( `<Exception><UnmodeledField>Oh no</UnmodeledField></Exception>` ) ,
138- } ) ;
155+ const httpRequest3 = await protocol . serializeRequest (
156+ createOperation ( payload ) ,
157+ {
158+ c : "<XML></XML>" ,
159+ h : "text/xml" ,
160+ } ,
161+ context
162+ ) ;
139163
140- const protocol = new AwsRestXmlProtocol ( {
141- defaultNamespace : "" ,
142- xmlNamespace : "ns" ,
164+ expect ( httpRequest3 . body ) . toBe ( `<XML></XML>` ) ;
143165 } ) ;
166+ } ) ;
144167
145- const output = await protocol
146- . deserializeResponse (
168+ describe ( "deserialization" , ( ) => {
169+ it ( "deserializes http responses" , async ( ) => {
170+ const httpResponse = new HttpResponse ( {
171+ statusCode : 200 ,
172+ headers : { } ,
173+ } ) ;
174+
175+ const output = await protocol . deserializeResponse (
147176 {
148- namespace : "ns" ,
149- name : "Empty" ,
150- traits : 0 ,
151- input : "unit" as const ,
152- output : [ 3 , "ns" , "EmptyOutput" , 0 , [ ] , [ ] ] ,
177+ namespace : deleteObjects [ 1 ] ,
178+ name : deleteObjects [ 2 ] ,
179+ traits : deleteObjects [ 3 ] ,
180+ input : deleteObjects [ 4 ] ,
181+ output : deleteObjects [ 5 ] ,
153182 } ,
154183 context ,
155184 httpResponse
156- )
157- . catch ( ( e ) => {
158- return e ;
185+ ) ;
186+
187+ expect ( output ) . toEqual ( {
188+ $metadata : {
189+ httpStatusCode : 200 ,
190+ requestId : undefined ,
191+ extendedRequestId : undefined ,
192+ cfId : undefined ,
193+ } ,
194+ } ) ;
195+ } ) ;
196+
197+ it ( "decorates service exceptions with unmodeled fields" , async ( ) => {
198+ const httpResponse = new HttpResponse ( {
199+ statusCode : 400 ,
200+ headers : { } ,
201+ body : Buffer . from ( `<Exception><UnmodeledField>Oh no</UnmodeledField></Exception>` ) ,
159202 } ) ;
160203
161- expect ( output ) . toMatchObject ( {
162- UnmodeledField : "Oh no" ,
163- $metadata : {
164- httpStatusCode : 400 ,
165- } ,
204+ const output = await protocol
205+ . deserializeResponse (
206+ {
207+ namespace : "ns" ,
208+ name : "Empty" ,
209+ traits : 0 ,
210+ input : "unit" as const ,
211+ output : [ 3 , "ns" , "EmptyOutput" , 0 , [ ] , [ ] ] ,
212+ } ,
213+ context ,
214+ httpResponse
215+ )
216+ . catch ( ( e ) => {
217+ return e ;
218+ } ) ;
219+
220+ expect ( output ) . toMatchObject ( {
221+ UnmodeledField : "Oh no" ,
222+ $metadata : {
223+ httpStatusCode : 400 ,
224+ } ,
225+ } ) ;
166226 } ) ;
167227 } ) ;
168228} ) ;
0 commit comments