fix: allow `TypedArray` for binary data (#494)

* fix: allow `Uint16|8Array` for binary data

Previously we only considered `Uint32Array` binary data. This was an
oversight. This fixes that issue.

Fixes: https://github.com/cloudevents/sdk-javascript/issues/491

Signed-off-by: Lance Ball <lball@redhat.com>
This commit is contained in:
Lance Ball 2022-06-15 00:51:36 -04:00 committed by GitHub
parent a62eb44669
commit 921e273ede
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 63 additions and 12 deletions

View File

@ -133,7 +133,7 @@ See: https://github.com/cloudevents/spec/blob/v1.0/spec.md#type-system`);
set data(value: T | undefined) {
if (isBinary(value)) {
this.data_base64 = asBase64(value);
this.data_base64 = asBase64(value as unknown as Buffer);
}
this.#_data = value;
}

View File

@ -5,6 +5,10 @@
import { ErrorObject } from "ajv";
export type TypeArray = Int8Array | Uint8Array | Int16Array | Uint16Array |
Int32Array | Uint32Array | Uint8ClampedArray | Float32Array | Float64Array;
/**
* An Error class that will be thrown when a CloudEvent
* cannot be properly validated against a specification.
@ -36,7 +40,7 @@ export const isDefined = (v: unknown): boolean => v !== null && typeof v !== "un
export const isBoolean = (v: unknown): boolean => typeof v === "boolean";
export const isInteger = (v: unknown): boolean => Number.isInteger(v as number);
export const isDate = (v: unknown): v is Date => v instanceof Date;
export const isBinary = (v: unknown): v is Uint32Array => v instanceof Uint32Array;
export const isBinary = (v: unknown): boolean => ArrayBuffer.isView(v);
export const isStringOrThrow = (v: unknown, t: Error): boolean =>
isString(v)
@ -73,7 +77,7 @@ export const isBase64 = (value: unknown): boolean =>
export const isBuffer = (value: unknown): boolean => value instanceof Buffer;
export const asBuffer = (value: string | Buffer | Uint32Array): Buffer =>
export const asBuffer = (value: string | Buffer | TypeArray): Buffer =>
isBinary(value)
? Buffer.from((value as unknown) as string)
: isBuffer(value)
@ -82,7 +86,8 @@ export const asBuffer = (value: string | Buffer | Uint32Array): Buffer =>
throw new TypeError("is not buffer or a valid binary");
})();
export const asBase64 = (value: string | Buffer | Uint32Array): string => asBuffer(value).toString("base64");
export const asBase64 =
(value: string | Buffer | TypeArray): string => asBuffer(value).toString("base64");
export const clone = (o: Record<string, unknown>): Record<string, unknown> => JSON.parse(JSON.stringify(o));
@ -97,5 +102,5 @@ export const asData = (data: unknown, contentType: string): string => {
return isBinary(maybeJson) ? asBase64(maybeJson) : maybeJson;
};
export const isValidType = (v: boolean | number | string | Date | Uint32Array | unknown): boolean =>
export const isValidType = (v: boolean | number | string | Date | TypeArray | unknown): boolean =>
isBoolean(v) || isInteger(v) || isString(v) || isDate(v) || isBinary(v) || isObject(v);

View File

@ -173,14 +173,60 @@ describe("CloudEvents Spec v1.0", () => {
expect(typeof ce.data).to.equal("string");
});
it("should be ok when type is 'Uint32Array' for 'Binary'", () => {
const dataString = ")(*~^my data for ce#@#$%";
const dataString = ")(*~^my data for ce#@#$%";
const testCases = [
{
type: Int8Array,
data: Int8Array.from(dataString, (c) => c.codePointAt(0) as number),
expected: asBase64(Int8Array.from(dataString, (c) => c.codePointAt(0) as number))
},
{
type: Uint8Array,
data: Uint8Array.from(dataString, (c) => c.codePointAt(0) as number),
expected: asBase64(Uint8Array.from(dataString, (c) => c.codePointAt(0) as number))
},
{
type: Int16Array,
data: Int16Array.from(dataString, (c) => c.codePointAt(0) as number),
expected: asBase64(Int16Array.from(dataString, (c) => c.codePointAt(0) as number))
},
{
type: Uint16Array,
data: Uint16Array.from(dataString, (c) => c.codePointAt(0) as number),
expected: asBase64(Uint16Array.from(dataString, (c) => c.codePointAt(0) as number))
},
{
type: Int32Array,
data: Int32Array.from(dataString, (c) => c.codePointAt(0) as number),
expected: asBase64(Int32Array.from(dataString, (c) => c.codePointAt(0) as number))
},
{
type: Uint32Array,
data: Uint32Array.from(dataString, (c) => c.codePointAt(0) as number),
expected: asBase64(Uint32Array.from(dataString, (c) => c.codePointAt(0) as number))
},
{
type: Uint8ClampedArray,
data: Uint8ClampedArray.from(dataString, (c) => c.codePointAt(0) as number),
expected: asBase64(Uint8ClampedArray.from(dataString, (c) => c.codePointAt(0) as number))
},
{
type: Float32Array,
data: Float32Array.from(dataString, (c) => c.codePointAt(0) as number),
expected: asBase64(Float32Array.from(dataString, (c) => c.codePointAt(0) as number))
},
{
type: Float64Array,
data: Float64Array.from(dataString, (c) => c.codePointAt(0) as number),
expected: asBase64(Float64Array.from(dataString, (c) => c.codePointAt(0) as number))
},
];
const dataBinary = Uint32Array.from(dataString, (c) => c.codePointAt(0) as number);
const expected = asBase64(dataBinary);
const ce = cloudevent.cloneWith({ datacontenttype: "text/plain", data: dataBinary });
expect(ce.data_base64).to.equal(expected);
testCases.forEach((test) => {
it(`should be ok when type is '${test.type.name}' for 'Binary'`, () => {
const ce = cloudevent.cloneWith({ datacontenttype: "text/plain", data: test.data });
expect(ce.data_base64).to.equal(test.expected);
});
});
});
});