Skip to content

Commit 366a56d

Browse files
committed
lib: uncomment tests and move data decoding to CloudEvent object
Signed-off-by: Lance Ball <[email protected]>
1 parent 8a9c371 commit 366a56d

File tree

8 files changed

+93
-68
lines changed

8 files changed

+93
-68
lines changed

src/event/cloudevent.ts

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import { v4 as uuidv4 } from "uuid";
22

33
import { CloudEventV1, validateV1, CloudEventV1Attributes } from "./v1";
44
import { CloudEventV03, validateV03, CloudEventV03Attributes } from "./v03";
5-
import { ValidationError } from "./validation";
5+
import { ValidationError, isBinary, asBase64 } from "./validation";
6+
import CONSTANTS from "../constants";
7+
import { isString } from "util";
68

79
export const enum Version {
810
V1 = "1.0",
@@ -23,7 +25,7 @@ export class CloudEvent implements CloudEventV1, CloudEventV03 {
2325
dataschema?: string;
2426
subject?: string;
2527
#_time?: string | Date;
26-
data?: Record<string, unknown | string | number | boolean> | string | number | boolean | null | unknown;
28+
#_data?: Record<string, unknown | string | number | boolean> | string | number | boolean | null | unknown;
2729
data_base64?: string;
2830

2931
// Extensions should not exist as it's own object, but instead
@@ -60,21 +62,21 @@ export class CloudEvent implements CloudEventV1, CloudEventV03 {
6062
this.#_time = properties.time;
6163
delete properties.time;
6264

63-
this.data = properties.data;
64-
delete properties.data;
65+
this.datacontentencoding = properties.datacontentencoding as string;
66+
delete properties.datacontentencoding;
6567

6668
this.dataschema = properties.dataschema as string;
6769
delete properties.dataschema;
6870

6971
this.data_base64 = properties.data_base64 as string;
7072
delete properties.data_base64;
7173

72-
this.datacontentencoding = properties.datacontentencoding as string;
73-
delete properties.datacontentencoding;
74-
7574
this.schemaurl = properties.schemaurl as string;
7675
delete properties.schemaurl;
7776

77+
this._setData(properties.data);
78+
delete properties.data;
79+
7880
// Make sure time has a default value and whatever is provided is formatted
7981
if (!this.#_time) {
8082
this.#_time = new Date().toISOString();
@@ -103,9 +105,35 @@ export class CloudEvent implements CloudEventV1, CloudEventV03 {
103105
this.#_time = new Date(val).toISOString();
104106
}
105107

108+
get data(): unknown {
109+
if (
110+
this.datacontenttype === CONSTANTS.MIME_JSON &&
111+
!(this.datacontentencoding === CONSTANTS.ENCODING_BASE64) &&
112+
isString(this.#_data)
113+
) {
114+
return JSON.parse(this.#_data as string);
115+
} else if (isBinary(this.#_data)) {
116+
return asBase64(this.#_data as Uint32Array);
117+
}
118+
return this.#_data;
119+
}
120+
121+
set data(value: unknown) {
122+
this._setData(value);
123+
}
124+
125+
private _setData(value: unknown): void {
126+
if (isBinary(value)) {
127+
this.#_data = value;
128+
this.data_base64 = asBase64(value as Uint32Array);
129+
}
130+
this.#_data = value;
131+
}
132+
106133
toJSON(): Record<string, unknown> {
107134
const event = { ...this };
108135
event.time = this.time;
136+
event.data = this.data;
109137
return event;
110138
}
111139

@@ -119,12 +147,19 @@ export class CloudEvent implements CloudEventV1, CloudEventV03 {
119147
* @return {boolean} true if this event is valid
120148
*/
121149
validate(): boolean {
122-
if (this.specversion === Version.V1) {
123-
return validateV1(this);
124-
} else if (this.specversion === Version.V03) {
125-
return validateV03(this);
126-
} else {
150+
try {
151+
if (this.specversion === Version.V1) {
152+
return validateV1(this);
153+
} else if (this.specversion === Version.V03) {
154+
return validateV03(this);
155+
}
127156
throw new ValidationError("invalid payload");
157+
} catch (e) {
158+
if (e instanceof ValidationError) {
159+
throw e;
160+
} else {
161+
throw new ValidationError("invalid payload", e);
162+
}
128163
}
129164
}
130165

src/transport/http/binary_emitter.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,8 @@ import CONSTANTS from "../../constants";
99
export async function emitBinary(event: CloudEvent, options: TransportOptions): Promise<AxiosResponse> {
1010
if (event.specversion !== Version.V1 && event.specversion !== Version.V03) {
1111
return Promise.reject(`Unknown spec version ${event.specversion}`);
12-
} else {
13-
return emit(event, options, headersFor(event));
1412
}
13+
return emit(event, options, headersFor(event));
1514
}
1615

1716
async function emit(event: CloudEvent, options: TransportOptions, headers: Headers): Promise<AxiosResponse> {

src/transport/http/headers.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ export const requiredHeaders = [
1717
CONSTANTS.CE_HEADERS.SPEC_VERSION,
1818
];
1919

20+
/**
21+
* Validates cloud event headers and their values
22+
* @param {Headers} headers event transport headers for validation
23+
* @throws {ValidationError} if the headers are invalid
24+
* @return {boolean} true if headers are valid
25+
*/
2026
export function validate(headers: Headers): Headers {
2127
const sanitizedHeaders = sanitize(headers);
2228

@@ -75,7 +81,13 @@ export function headersFor(event: CloudEvent): Headers {
7581
return headers;
7682
}
7783

78-
export function sanitize(headers: Headers): Record<string, string> {
84+
/**
85+
* Sanitizes incoming headers by lowercasing them and potentially removing
86+
* encoding from the content-type header.
87+
* @param {Headers} headers HTTP headers as key/value pairs
88+
* @returns {Headers} the sanitized headers
89+
*/
90+
export function sanitize(headers: Headers): Headers {
7991
const sanitized: Headers = {};
8092

8193
Array.from(Object.keys(headers))
Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import axios, { AxiosRequestConfig, AxiosResponse } from "axios";
22
import { CloudEvent } from "../../event";
33
import { TransportOptions } from "../emitter";
4-
import { asBase64, isBinary } from "../../event/validation";
54
import CONSTANTS from "../../constants";
65

76
const defaults = {
@@ -15,16 +14,7 @@ export function emitStructured(event: CloudEvent, options: TransportOptions): Pr
1514
...defaults,
1615
...options,
1716
method: "POST",
18-
data: format(event),
17+
data: event,
1918
};
2019
return axios.request(config as AxiosRequestConfig);
2120
}
22-
23-
function format(event: CloudEvent): string {
24-
if (isBinary(event.data)) {
25-
event.data_base64 = asBase64(event.data as Uint32Array);
26-
}
27-
const json = JSON.stringify(event);
28-
delete event.data_base64;
29-
return json;
30-
}

src/transport/http/structured_receiver.ts

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,7 @@ import { Parser, JSONParser } from "../../parsers";
44
import { parserByContentType } from "../../parsers";
55
import { structuredParsers as v1Parsers } from "./v1/parsers";
66
import { structuredParsers as v03Parsers } from "./v03/parsers";
7-
import {
8-
isString,
9-
isBase64,
10-
ValidationError,
11-
isStringOrObjectOrThrow,
12-
isBinary,
13-
asBase64,
14-
} from "../../event/validation";
7+
import { isString, isBase64, ValidationError, isStringOrObjectOrThrow } from "../../event/validation";
158
import { CloudEventV1, validateV1 } from "../../event/v1";
169
import { CloudEventV03, validateV03 } from "../../event/v03";
1710
import CONSTANTS from "../../constants";
@@ -64,11 +57,6 @@ export class StructuredHTTPReceiver {
6457
const eventObj: { [key: string]: unknown } = {};
6558
const parserMap = this.version === Version.V1 ? v1Parsers : v03Parsers;
6659

67-
// if the incoming data is in binary form, encode to base64
68-
if (isBinary(incoming.data)) {
69-
eventObj.data_base64 = asBase64(incoming.data as Buffer);
70-
}
71-
7260
parserMap.forEach((value, key) => {
7361
if (incoming[key]) {
7462
eventObj[value.name] = value.parser.parse(incoming[key] as string);

test/receiver_structured_0_3_test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ describe("HTTP Transport Binding Structured Receiver CloudEvents v0.3", () => {
199199
const actual = receiver.parse(payload, headers);
200200

201201
// assert
202-
expect(actual.data).to.deep.equal(JSON.stringify(data));
202+
expect(actual.data).to.deep.equal(data);
203203
});
204204
});
205205
});

test/spec_03_tests.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ describe("CloudEvents Spec v0.3", () => {
167167
it("should convert data with stringified json to a json object", () => {
168168
cloudevent.datacontenttype = Constants.MIME_JSON;
169169
cloudevent.data = JSON.stringify(data);
170-
expect(cloudevent.data).to.deep.equal(JSON.stringify(data));
170+
expect(cloudevent.data).to.deep.equal(data);
171171
});
172172
});
173173

test/spec_1_tests.ts

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import "mocha";
22
import { expect } from "chai";
3-
import { CloudEvent, Version, ValidationError } from "../src";
3+
import { CloudEvent, Version, ValidationError, asBase64 } from "../src";
44
import Constants from "../src/constants";
55

66
const id = "97699ec2-a8d9-47c1-bfa0-ff7aa526f838";
@@ -168,37 +168,38 @@ describe("CloudEvents Spec v1.0", () => {
168168
});
169169
});
170170

171-
// describe("Event data constraints", () => {
172-
// it("Should have 'data'", () => {
173-
// expect(cloudevent.data).to.deep.equal(data);
174-
// });
171+
describe("Event data constraints", () => {
172+
it("Should have 'data'", () => {
173+
expect(cloudevent.data).to.deep.equal(data);
174+
});
175175

176-
// it("should maintain the type of data when no data content type", () => {
177-
// delete cloudevent.spec.payload.datacontenttype;
178-
// cloudevent.data = JSON.stringify(data);
176+
it("should maintain the type of data when no data content type", () => {
177+
const dct = cloudevent.datacontenttype;
178+
delete cloudevent.datacontenttype;
179+
cloudevent.data = JSON.stringify(data);
179180

180-
// expect(typeof cloudevent.data).to.equal("string");
181-
// cloudevent.dataContentType = dataContentType;
182-
// });
181+
expect(typeof cloudevent.data).to.equal("string");
182+
cloudevent.datacontenttype = dct;
183+
});
183184

184-
// it("should convert data with stringified json to a json object", () => {
185-
// cloudevent.dataContentType = dataContentType;
186-
// cloudevent.data = JSON.stringify(data);
187-
// expect(cloudevent.data).to.deep.equal(data);
188-
// });
185+
it("should convert data with stringified json to a json object", () => {
186+
cloudevent.datacontenttype = Constants.MIME_JSON;
187+
cloudevent.data = JSON.stringify(data);
188+
expect(cloudevent.data).to.deep.equal(data);
189+
});
189190

190-
// it("should be ok when type is 'Uint32Array' for 'Binary'", () => {
191-
// const dataString = ")(*~^my data for ce#@#$%";
191+
it("should be ok when type is 'Uint32Array' for 'Binary'", () => {
192+
const dataString = ")(*~^my data for ce#@#$%";
192193

193-
// const dataBinary = Uint32Array.from(dataString, (c) => c.codePointAt(0));
194-
// const expected = asBase64(dataBinary);
195-
// const olddct = cloudevent.dataContentType;
194+
const dataBinary = Uint32Array.from(dataString, (c) => c.codePointAt(0) as number);
195+
const expected = asBase64(dataBinary);
196+
const olddct = cloudevent.datacontenttype;
196197

197-
// cloudevent.dataContentType = "text/plain";
198-
// cloudevent.data = dataBinary;
199-
// expect(cloudevent.data).to.deep.equal(expected);
198+
cloudevent.datacontenttype = "text/plain";
199+
cloudevent.data = dataBinary;
200+
expect(cloudevent.data_base64).to.equal(expected);
200201

201-
// cloudevent.dataContentType = olddct;
202-
// });
203-
// });
202+
cloudevent.datacontenttype = olddct;
203+
});
204+
});
204205
});

0 commit comments

Comments
 (0)