lambda-live-debugger
Version:
Debug Lambda functions locally like it is running in the cloud
296 lines (295 loc) • 13 kB
JavaScript
import { XmlNode, XmlText } from "@aws-sdk/xml-builder";
import { determineTimestampFormat } from "@smithy/core/protocols";
import { NormalizedSchema } from "@smithy/core/schema";
import { generateIdempotencyToken, NumericValue } from "@smithy/core/serde";
import { dateToUtcString } from "@smithy/smithy-client";
import { fromBase64, toBase64 } from "@smithy/util-base64";
import { SerdeContextConfig } from "../ConfigurableSerdeContext";
export class XmlShapeSerializer extends SerdeContextConfig {
settings;
stringBuffer;
byteBuffer;
buffer;
constructor(settings) {
super();
this.settings = settings;
}
write(schema, value) {
const ns = NormalizedSchema.of(schema);
if (ns.isStringSchema() && typeof value === "string") {
this.stringBuffer = value;
}
else if (ns.isBlobSchema()) {
this.byteBuffer =
"byteLength" in value
? value
: (this.serdeContext?.base64Decoder ?? fromBase64)(value);
}
else {
this.buffer = this.writeStruct(ns, value, undefined);
const traits = ns.getMergedTraits();
if (traits.httpPayload && !traits.xmlName) {
this.buffer.withName(ns.getName());
}
}
}
flush() {
if (this.byteBuffer !== undefined) {
const bytes = this.byteBuffer;
delete this.byteBuffer;
return bytes;
}
if (this.stringBuffer !== undefined) {
const str = this.stringBuffer;
delete this.stringBuffer;
return str;
}
const buffer = this.buffer;
if (this.settings.xmlNamespace) {
if (!buffer?.attributes?.["xmlns"]) {
buffer.addAttribute("xmlns", this.settings.xmlNamespace);
}
}
delete this.buffer;
return buffer.toString();
}
writeStruct(ns, value, parentXmlns) {
const traits = ns.getMergedTraits();
const name = ns.isMemberSchema() && !traits.httpPayload
? ns.getMemberTraits().xmlName ?? ns.getMemberName()
: traits.xmlName ?? ns.getName();
if (!name || !ns.isStructSchema()) {
throw new Error(`-sdk/core/protocols - xml serializer, cannot write struct with empty name or non-struct, schema=${ns.getName(true)}.`);
}
const structXmlNode = XmlNode.of(name);
const [xmlnsAttr, xmlns] = this.getXmlnsAttribute(ns, parentXmlns);
for (const [memberName, memberSchema] of ns.structIterator()) {
const val = value[memberName];
if (val != null || memberSchema.isIdempotencyToken()) {
if (memberSchema.getMergedTraits().xmlAttribute) {
structXmlNode.addAttribute(memberSchema.getMergedTraits().xmlName ?? memberName, this.writeSimple(memberSchema, val));
continue;
}
if (memberSchema.isListSchema()) {
this.writeList(memberSchema, val, structXmlNode, xmlns);
}
else if (memberSchema.isMapSchema()) {
this.writeMap(memberSchema, val, structXmlNode, xmlns);
}
else if (memberSchema.isStructSchema()) {
structXmlNode.addChildNode(this.writeStruct(memberSchema, val, xmlns));
}
else {
const memberNode = XmlNode.of(memberSchema.getMergedTraits().xmlName ?? memberSchema.getMemberName());
this.writeSimpleInto(memberSchema, val, memberNode, xmlns);
structXmlNode.addChildNode(memberNode);
}
}
}
const { $unknown } = value;
if ($unknown && ns.isUnionSchema() && Array.isArray($unknown) && Object.keys(value).length === 1) {
const [k, v] = $unknown;
const node = XmlNode.of(k);
if (typeof v !== "string") {
if (value instanceof XmlNode || value instanceof XmlText) {
structXmlNode.addChildNode(value);
}
else {
throw new Error(`-sdk - $unknown union member in XML requires ` +
`value of type string, -sdk/xml-builder::XmlNode or XmlText.`);
}
}
this.writeSimpleInto(0, v, node, xmlns);
structXmlNode.addChildNode(node);
}
if (xmlns) {
structXmlNode.addAttribute(xmlnsAttr, xmlns);
}
return structXmlNode;
}
writeList(listMember, array, container, parentXmlns) {
if (!listMember.isMemberSchema()) {
throw new Error(`-sdk/core/protocols - xml serializer, cannot write non-member list: ${listMember.getName(true)}`);
}
const listTraits = listMember.getMergedTraits();
const listValueSchema = listMember.getValueSchema();
const listValueTraits = listValueSchema.getMergedTraits();
const sparse = !!listValueTraits.sparse;
const flat = !!listTraits.xmlFlattened;
const [xmlnsAttr, xmlns] = this.getXmlnsAttribute(listMember, parentXmlns);
const writeItem = (container, value) => {
if (listValueSchema.isListSchema()) {
this.writeList(listValueSchema, Array.isArray(value) ? value : [value], container, xmlns);
}
else if (listValueSchema.isMapSchema()) {
this.writeMap(listValueSchema, value, container, xmlns);
}
else if (listValueSchema.isStructSchema()) {
const struct = this.writeStruct(listValueSchema, value, xmlns);
container.addChildNode(struct.withName(flat ? listTraits.xmlName ?? listMember.getMemberName() : listValueTraits.xmlName ?? "member"));
}
else {
const listItemNode = XmlNode.of(flat ? listTraits.xmlName ?? listMember.getMemberName() : listValueTraits.xmlName ?? "member");
this.writeSimpleInto(listValueSchema, value, listItemNode, xmlns);
container.addChildNode(listItemNode);
}
};
if (flat) {
for (const value of array) {
if (sparse || value != null) {
writeItem(container, value);
}
}
}
else {
const listNode = XmlNode.of(listTraits.xmlName ?? listMember.getMemberName());
if (xmlns) {
listNode.addAttribute(xmlnsAttr, xmlns);
}
for (const value of array) {
if (sparse || value != null) {
writeItem(listNode, value);
}
}
container.addChildNode(listNode);
}
}
writeMap(mapMember, map, container, parentXmlns, containerIsMap = false) {
if (!mapMember.isMemberSchema()) {
throw new Error(`-sdk/core/protocols - xml serializer, cannot write non-member map: ${mapMember.getName(true)}`);
}
const mapTraits = mapMember.getMergedTraits();
const mapKeySchema = mapMember.getKeySchema();
const mapKeyTraits = mapKeySchema.getMergedTraits();
const keyTag = mapKeyTraits.xmlName ?? "key";
const mapValueSchema = mapMember.getValueSchema();
const mapValueTraits = mapValueSchema.getMergedTraits();
const valueTag = mapValueTraits.xmlName ?? "value";
const sparse = !!mapValueTraits.sparse;
const flat = !!mapTraits.xmlFlattened;
const [xmlnsAttr, xmlns] = this.getXmlnsAttribute(mapMember, parentXmlns);
const addKeyValue = (entry, key, val) => {
const keyNode = XmlNode.of(keyTag, key);
const [keyXmlnsAttr, keyXmlns] = this.getXmlnsAttribute(mapKeySchema, xmlns);
if (keyXmlns) {
keyNode.addAttribute(keyXmlnsAttr, keyXmlns);
}
entry.addChildNode(keyNode);
let valueNode = XmlNode.of(valueTag);
if (mapValueSchema.isListSchema()) {
this.writeList(mapValueSchema, val, valueNode, xmlns);
}
else if (mapValueSchema.isMapSchema()) {
this.writeMap(mapValueSchema, val, valueNode, xmlns, true);
}
else if (mapValueSchema.isStructSchema()) {
valueNode = this.writeStruct(mapValueSchema, val, xmlns);
}
else {
this.writeSimpleInto(mapValueSchema, val, valueNode, xmlns);
}
entry.addChildNode(valueNode);
};
if (flat) {
for (const [key, val] of Object.entries(map)) {
if (sparse || val != null) {
const entry = XmlNode.of(mapTraits.xmlName ?? mapMember.getMemberName());
addKeyValue(entry, key, val);
container.addChildNode(entry);
}
}
}
else {
let mapNode;
if (!containerIsMap) {
mapNode = XmlNode.of(mapTraits.xmlName ?? mapMember.getMemberName());
if (xmlns) {
mapNode.addAttribute(xmlnsAttr, xmlns);
}
container.addChildNode(mapNode);
}
for (const [key, val] of Object.entries(map)) {
if (sparse || val != null) {
const entry = XmlNode.of("entry");
addKeyValue(entry, key, val);
(containerIsMap ? container : mapNode).addChildNode(entry);
}
}
}
}
writeSimple(_schema, value) {
if (null === value) {
throw new Error("@aws-sdk/core/protocols - (XML serializer) cannot write null value.");
}
const ns = NormalizedSchema.of(_schema);
let nodeContents = null;
if (value && typeof value === "object") {
if (ns.isBlobSchema()) {
nodeContents = (this.serdeContext?.base64Encoder ?? toBase64)(value);
}
else if (ns.isTimestampSchema() && value instanceof Date) {
const format = determineTimestampFormat(ns, this.settings);
switch (format) {
case 5:
nodeContents = value.toISOString().replace(".000Z", "Z");
break;
case 6:
nodeContents = dateToUtcString(value);
break;
case 7:
nodeContents = String(value.getTime() / 1000);
break;
default:
console.warn("Missing timestamp format, using http date", value);
nodeContents = dateToUtcString(value);
break;
}
}
else if (ns.isBigDecimalSchema() && value) {
if (value instanceof NumericValue) {
return value.string;
}
return String(value);
}
else if (ns.isMapSchema() || ns.isListSchema()) {
throw new Error("@aws-sdk/core/protocols - xml serializer, cannot call _write() on List/Map schema, call writeList or writeMap() instead.");
}
else {
throw new Error(`-sdk/core/protocols - xml serializer, unhandled schema type for object value and schema: ${ns.getName(true)}`);
}
}
if (ns.isBooleanSchema() || ns.isNumericSchema() || ns.isBigIntegerSchema() || ns.isBigDecimalSchema()) {
nodeContents = String(value);
}
if (ns.isStringSchema()) {
if (value === undefined && ns.isIdempotencyToken()) {
nodeContents = generateIdempotencyToken();
}
else {
nodeContents = String(value);
}
}
if (nodeContents === null) {
throw new Error(`Unhandled schema-value pair ${ns.getName(true)}=${value}`);
}
return nodeContents;
}
writeSimpleInto(_schema, value, into, parentXmlns) {
const nodeContents = this.writeSimple(_schema, value);
const ns = NormalizedSchema.of(_schema);
const content = new XmlText(nodeContents);
const [xmlnsAttr, xmlns] = this.getXmlnsAttribute(ns, parentXmlns);
if (xmlns) {
into.addAttribute(xmlnsAttr, xmlns);
}
into.addChildNode(content);
}
getXmlnsAttribute(ns, parentXmlns) {
const traits = ns.getMergedTraits();
const [prefix, xmlns] = traits.xmlNamespace ?? [];
if (xmlns && xmlns !== parentXmlns) {
return [prefix ? `xmlns:${prefix}` : "xmlns", xmlns];
}
return [void 0, void 0];
}
}