Skip to content

Commit 0c74892

Browse files
feat(smithy-client): stricter parsing of request/response bodies (#2713)
1 parent f9aa15e commit 0c74892

File tree

2 files changed

+76
-1
lines changed

2 files changed

+76
-1
lines changed

packages/smithy-client/src/parse-utils.spec.ts

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
import { expectInt, limitedParseFloat, parseBoolean, strictParseFloat, strictParseInt } from "./parse-utils";
1+
import {
2+
expectInt,
3+
expectNonNull,
4+
expectObject,
5+
limitedParseFloat,
6+
parseBoolean,
7+
strictParseFloat,
8+
strictParseInt,
9+
} from "./parse-utils";
210
import { expectBoolean, expectNumber, expectString } from "./parse-utils";
311

412
describe("parseBoolean", () => {
@@ -99,6 +107,38 @@ describe("expectInt", () => {
99107
});
100108
});
101109

110+
describe("expectNonNull", () => {
111+
it.each([1, 1.1, "1", NaN, true, [], ["a", 123], { a: 123 }, [{ a: 123 }], "{ a : 123 }", '{"a":123}'])(
112+
"accepts %s",
113+
(value) => {
114+
expect(expectNonNull(value)).toEqual(value);
115+
}
116+
);
117+
118+
it.each([null, undefined])("rejects %s", (value) => {
119+
expect(() => expectNonNull(value)).toThrowError();
120+
});
121+
});
122+
123+
describe("expectObject", () => {
124+
it("accepts objects", () => {
125+
expect(expectObject({ a: 123 })).toEqual({ a: 123 });
126+
});
127+
128+
it.each([null, undefined])("accepts %s", (value) => {
129+
expect(expectObject(value)).toEqual(undefined);
130+
});
131+
132+
describe("rejects non-objects", () => {
133+
it.each([1, 1.1, "1", NaN, true, [], ["a", 123], [{ a: 123 }], "{ a : 123 }", '{"a":123}'])(
134+
"rejects %s",
135+
(value) => {
136+
expect(() => expectObject(value)).toThrowError();
137+
}
138+
);
139+
});
140+
});
141+
102142
describe("expectString", () => {
103143
it("accepts strings", () => {
104144
expect(expectString("foo")).toEqual("foo");

packages/smithy-client/src/parse-utils.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,41 @@ export const expectInt = (value: any): number | undefined => {
6666
throw new TypeError(`Expected integer, got ${typeof value}`);
6767
};
6868

69+
/**
70+
* Asserts a value is not null or undefined and returns it, or throws an error.
71+
*
72+
* @param value A value that is expected to be defined
73+
* @param location The location where we're expecting to find a defined object (optional)
74+
* @returns The value if it's not undefined, otherwise throws an error
75+
*/
76+
export const expectNonNull = <T>(value: T | null | undefined, location?: string): T => {
77+
if (value === null || value === undefined) {
78+
if (location) {
79+
throw new TypeError(`Expected a non-null value for ${location}`);
80+
}
81+
throw new TypeError("Expected a non-null value");
82+
}
83+
return value;
84+
};
85+
86+
/**
87+
* Asserts a value is an JSON-like object and returns it. This is expected to be used
88+
* with values parsed from JSON (arrays, objects, numbers, strings, booleans).
89+
*
90+
* @param value A value that is expected to be an object
91+
* @returns The value if it's an object, undefined if it's null/undefined,
92+
* otherwise an error is thrown.
93+
*/
94+
export const expectObject = (value: any): { [key: string]: any } | undefined => {
95+
if (value === null || value === undefined) {
96+
return undefined;
97+
}
98+
if (typeof value === "object" && !Array.isArray(value)) {
99+
return value;
100+
}
101+
throw new TypeError(`Expected object, got ${typeof value}`);
102+
};
103+
69104
/**
70105
* Asserts a value is a string and returns it.
71106
*

0 commit comments

Comments
 (0)