Skip to content

Commit

Permalink
feat(smithy-client): stricter parsing of request/response bodies (#2713)
Browse files Browse the repository at this point in the history
  • Loading branch information
adamthom-amzn committed Aug 27, 2021
1 parent f9aa15e commit 0c74892
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 1 deletion.
42 changes: 41 additions & 1 deletion packages/smithy-client/src/parse-utils.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { expectInt, limitedParseFloat, parseBoolean, strictParseFloat, strictParseInt } from "./parse-utils";
import {
expectInt,
expectNonNull,
expectObject,
limitedParseFloat,
parseBoolean,
strictParseFloat,
strictParseInt,
} from "./parse-utils";
import { expectBoolean, expectNumber, expectString } from "./parse-utils";

describe("parseBoolean", () => {
Expand Down Expand Up @@ -99,6 +107,38 @@ describe("expectInt", () => {
});
});

describe("expectNonNull", () => {
it.each([1, 1.1, "1", NaN, true, [], ["a", 123], { a: 123 }, [{ a: 123 }], "{ a : 123 }", '{"a":123}'])(
"accepts %s",
(value) => {
expect(expectNonNull(value)).toEqual(value);
}
);

it.each([null, undefined])("rejects %s", (value) => {
expect(() => expectNonNull(value)).toThrowError();
});
});

describe("expectObject", () => {
it("accepts objects", () => {
expect(expectObject({ a: 123 })).toEqual({ a: 123 });
});

it.each([null, undefined])("accepts %s", (value) => {
expect(expectObject(value)).toEqual(undefined);
});

describe("rejects non-objects", () => {
it.each([1, 1.1, "1", NaN, true, [], ["a", 123], [{ a: 123 }], "{ a : 123 }", '{"a":123}'])(
"rejects %s",
(value) => {
expect(() => expectObject(value)).toThrowError();
}
);
});
});

describe("expectString", () => {
it("accepts strings", () => {
expect(expectString("foo")).toEqual("foo");
Expand Down
35 changes: 35 additions & 0 deletions packages/smithy-client/src/parse-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,41 @@ export const expectInt = (value: any): number | undefined => {
throw new TypeError(`Expected integer, got ${typeof value}`);
};

/**
* Asserts a value is not null or undefined and returns it, or throws an error.
*
* @param value A value that is expected to be defined
* @param location The location where we're expecting to find a defined object (optional)
* @returns The value if it's not undefined, otherwise throws an error
*/
export const expectNonNull = <T>(value: T | null | undefined, location?: string): T => {
if (value === null || value === undefined) {
if (location) {
throw new TypeError(`Expected a non-null value for ${location}`);
}
throw new TypeError("Expected a non-null value");
}
return value;
};

/**
* Asserts a value is an JSON-like object and returns it. This is expected to be used
* with values parsed from JSON (arrays, objects, numbers, strings, booleans).
*
* @param value A value that is expected to be an object
* @returns The value if it's an object, undefined if it's null/undefined,
* otherwise an error is thrown.
*/
export const expectObject = (value: any): { [key: string]: any } | undefined => {
if (value === null || value === undefined) {
return undefined;
}
if (typeof value === "object" && !Array.isArray(value)) {
return value;
}
throw new TypeError(`Expected object, got ${typeof value}`);
};

/**
* Asserts a value is a string and returns it.
*
Expand Down

0 comments on commit 0c74892

Please sign in to comment.