-
Notifications
You must be signed in to change notification settings - Fork 105
fix(protocols): fix header serde, handle unset union payloads #1417
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a8b7807
42e3c5b
b401bd9
2ab214d
3168eda
4900529
18b5cf0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@smithy/middleware-compression": patch | ||
--- | ||
|
||
comma spacing |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@smithy/smithy-client": minor | ||
--- | ||
|
||
add quoteHeader function |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { quoteHeader } from "./quote-header"; | ||
|
||
describe(quoteHeader.name, () => { | ||
it("should not wrap header elements that don't include the delimiter or double quotes", () => { | ||
expect(quoteHeader("bc")).toBe("bc"); | ||
}); | ||
|
||
it("should wrap header elements that include the delimiter", () => { | ||
expect(quoteHeader("b,c")).toBe('"b,c"'); | ||
}); | ||
|
||
it("should wrap header elements that include double quotes", () => { | ||
expect(quoteHeader(`"bc"`)).toBe('"\\"bc\\""'); | ||
}); | ||
|
||
it("should wrap header elements that include the delimiter and double quotes", () => { | ||
expect(quoteHeader(`"b,c"`)).toBe('"\\"b,c\\""'); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
/** | ||
* @public | ||
* @param part - header list element | ||
* @returns quoted string if part contains delimiter. | ||
*/ | ||
export function quoteHeader(part: string) { | ||
if (part.includes(",") || part.includes('"')) { | ||
part = `"${part.replace(/"/g, '\\"')}"`; | ||
} | ||
return part; | ||
} | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { splitHeader } from "./split-header"; | ||
|
||
describe(splitHeader.name, () => { | ||
it("should split a string by commas and trim only the comma delimited outer values", () => { | ||
expect(splitHeader("abc")).toEqual(["abc"]); | ||
expect(splitHeader("a,b,c")).toEqual(["a", "b", "c"]); | ||
expect(splitHeader("a, b, c")).toEqual(["a", "b", "c"]); | ||
expect(splitHeader("a , b , c")).toEqual(["a", "b", "c"]); | ||
expect(splitHeader(`a , b , " c "`)).toEqual(["a", "b", " c "]); | ||
expect(splitHeader(` a , , b`)).toEqual(["a", "", "b"]); | ||
expect(splitHeader(`,,`)).toEqual(["", "", ""]); | ||
expect(splitHeader(` , , `)).toEqual(["", "", ""]); | ||
}); | ||
it("should split a string by commas that are not in quotes, and remove outer quotes", () => { | ||
expect(splitHeader('"b,c", "\\"def\\"", a')).toEqual(["b,c", '"def"', "a"]); | ||
expect(splitHeader('"a,b,c", ""def"", "a,b ,c"')).toEqual(["a,b,c", '"def"', "a,b ,c"]); | ||
expect(splitHeader(`""`)).toEqual([``]); | ||
expect(splitHeader(``)).toEqual([``]); | ||
expect(splitHeader(`\\"`)).toEqual([`"`]); | ||
expect(splitHeader(`"`)).toEqual([`"`]); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
/** | ||
* @param value - header string value. | ||
* @returns value split by commas that aren't in quotes. | ||
*/ | ||
export const splitHeader = (value: string): string[] => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This takes over Behavior specified here: https://github.com/smithy-lang/smithy/blob/main/smithy-aws-protocol-tests/model/restJson1/http-headers.smithy#L229-L232 {
id: "RestJsonInputAndOutputWithQuotedStringHeaders",
documentation: "Tests responses with string list header bindings that require quoting",
protocol: restJson1,
code: 200,
headers: {
"X-StringList": "\"b,c\", \"\\\"def\\\"\", a"
},
params: {
headerStringList: ["b,c", "\"def\"", "a"]
}
} |
||
const z = value.length; | ||
const values = []; | ||
|
||
let withinQuotes = false; | ||
let prevChar = undefined; | ||
let anchor = 0; | ||
|
||
for (let i = 0; i < z; ++i) { | ||
const char = value[i]; | ||
switch (char) { | ||
case `"`: | ||
if (prevChar !== "\\") { | ||
withinQuotes = !withinQuotes; | ||
} | ||
break; | ||
case ",": | ||
if (!withinQuotes) { | ||
values.push(value.slice(anchor, i)); | ||
anchor = i + 1; | ||
} | ||
break; | ||
default: | ||
} | ||
prevChar = char; | ||
} | ||
|
||
values.push(value.slice(anchor)); | ||
|
||
return values.map((v) => { | ||
v = v.trim(); | ||
const z = v.length; | ||
if (z < 2) { | ||
return v; | ||
} | ||
if (v[0] === `"` && v[z - 1] === `"`) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. added more test cases to describe what happens, i.e. |
||
v = v.slice(1, z - 1); | ||
} | ||
return v.replace(/\\"/g, '"'); | ||
}); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1385,6 +1385,11 @@ private String getCollectionInputParam( | |
|
||
switch (bindingType) { | ||
case HEADER: | ||
if (collectionTarget.isStringShape()) { | ||
context.getWriter().addImport( | ||
"quoteHeader", "__quoteHeader", TypeScriptDependency.AWS_SMITHY_CLIENT); | ||
return iteratedParam + ".map(__quoteHeader).join(', ')"; | ||
} | ||
return iteratedParam + ".join(', ')"; | ||
case QUERY: | ||
case QUERY_PARAMS: | ||
|
@@ -2464,18 +2469,31 @@ private HttpBinding readPayload( | |
+ "= __expectObject(await parseBody(output.body, context));"); | ||
} else if (target instanceof UnionShape) { | ||
// If payload is a Union, then we need to parse the string into JavaScript object. | ||
importUnionDeserializer(writer); | ||
writer.write("const data: Record<string, any> | undefined " | ||
+ "= __expectUnion(await parseBody(output.body, context));"); | ||
+ "= await parseBody(output.body, context);"); | ||
} else if (target instanceof StringShape || target instanceof DocumentShape) { | ||
// If payload is String or Document, we need to collect body and convert binary to string. | ||
writer.write("const data: any = await collectBodyString(output.body, context);"); | ||
} else { | ||
throw new CodegenException(String.format("Unexpected shape type bound to payload: `%s`", | ||
target.getType())); | ||
} | ||
writer.write("contents.$L = $L;", binding.getMemberName(), getOutputValue(context, | ||
|
||
if (target instanceof UnionShape) { | ||
writer.openBlock( | ||
"if (Object.keys(data ?? {}).length) {", | ||
"}", | ||
() -> { | ||
importUnionDeserializer(writer); | ||
writer.write("contents.$L = __expectUnion($L);", binding.getMemberName(), getOutputValue(context, | ||
Location.PAYLOAD, "data", binding.getMember(), target)); | ||
} | ||
); | ||
} else { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. old: const data: Record<string, any> | undefined = __expectUnion(await parseBody(output.body, context));
contents.nested = de_UnionPayload(data, context); (does not handle empty output correctly) new: const data: Record<string, any> | undefined = await parseBody(output.body, context);
if (Object.keys(data ?? {}).length) {
contents.nested = __expectUnion(de_UnionPayload(data, context));
} |
||
writer.write("contents.$L = $L;", binding.getMemberName(), getOutputValue(context, | ||
Location.PAYLOAD, "data", binding.getMember(), target)); | ||
} | ||
|
||
return binding; | ||
} | ||
|
||
|
@@ -2716,7 +2734,8 @@ private String getCollectionOutputParam( | |
case HEADER: | ||
dataSource = "(" + dataSource + " || \"\")"; | ||
// Split these values on commas. | ||
outputParam = dataSource + ".split(',')"; | ||
context.getWriter().addImport("splitHeader", "__splitHeader", TypeScriptDependency.AWS_SMITHY_CLIENT); | ||
outputParam = "__splitHeader(" + dataSource + ")"; | ||
|
||
// Headers that have HTTP_DATE formatted timestamps already contain a "," | ||
// in their formatted entry, so split on every other "," instead. | ||
|
Uh oh!
There was an error while loading. Please reload this page.