Skip to content

src(event)!: make the event's time property only a string #330

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 6 additions & 20 deletions src/event/cloudevent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export class CloudEvent implements CloudEventV1, CloudEventV03 {
datacontenttype?: string;
dataschema?: string;
subject?: string;
#_time?: string | Date;
time?: string;
#_data?: Record<string, unknown | string | number | boolean> | string | number | boolean | null | unknown;
data_base64?: string;

Expand All @@ -54,6 +54,9 @@ export class CloudEvent implements CloudEventV1, CloudEventV03 {
this.id = (properties.id as string) || uuidv4();
delete properties.id;

this.time = properties.time || new Date().toISOString();
delete properties.time;

this.type = properties.type;
delete properties.type;

Expand All @@ -69,9 +72,6 @@ export class CloudEvent implements CloudEventV1, CloudEventV03 {
this.subject = properties.subject;
delete properties.subject;

this.#_time = properties.time;
delete properties.time;

this.datacontentencoding = properties.datacontentencoding as string;
delete properties.datacontentencoding;

Expand All @@ -87,13 +87,6 @@ export class CloudEvent implements CloudEventV1, CloudEventV03 {
this._setData(properties.data);
delete properties.data;

// Make sure time has a default value and whatever is provided is formatted
if (!this.#_time) {
this.#_time = new Date().toISOString();
} else if (this.#_time instanceof Date) {
this.#_time = this.#_time.toISOString();
}

// sanity checking
if (this.specversion === Version.V1 && this.schemaurl) {
throw new TypeError("cannot set schemaurl on version 1.0 event");
Expand Down Expand Up @@ -123,14 +116,6 @@ export class CloudEvent implements CloudEventV1, CloudEventV03 {
Object.freeze(this);
}

get time(): string | Date {
return this.#_time as string | Date;
}

set time(val: string | Date) {
this.#_time = new Date(val).toISOString();
}

get data(): unknown {
if (
this.datacontenttype === CONSTANTS.MIME_JSON &&
Expand Down Expand Up @@ -164,7 +149,7 @@ export class CloudEvent implements CloudEventV1, CloudEventV03 {
*/
toJSON(): Record<string, unknown> {
const event = { ...this };
event.time = this.time;
event.time = new Date(this.time as string).toISOString();
event.data = this.data;
return event;
}
Expand All @@ -182,6 +167,7 @@ export class CloudEvent implements CloudEventV1, CloudEventV03 {
try {
return validateCloudEvent(this);
} catch (e) {
console.error(e.errors);
if (e instanceof ValidationError) {
throw e;
} else {
Expand Down
4 changes: 2 additions & 2 deletions src/event/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export interface CloudEventV1OptionalAttributes {
* the same algorithm to determine the value used.
* @example "2020-08-08T14:48:09.769Z"
*/
time?: Date | string;
time?: string;
/**
* [OPTIONAL] The event payload. This specification does not place any restriction
* on the type of this information. It is encoded into a media format which is
Expand Down Expand Up @@ -258,7 +258,7 @@ export interface CloudEventV03OptionalAttributes {
* the same algorithm to determine the value used.
* @example "2020-08-08T14:48:09.769Z"
*/
time?: Date | string;
time?: string;
/**
* [OPTIONAL] The event payload. This specification does not place any restriction
* on the type of this information. It is encoded into a media format which is
Expand Down
4 changes: 2 additions & 2 deletions src/event/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export const schemaV1 = {
minLength: 1,
},
time: {
format: "date-time",
format: "js-date-time",
type: "string",
},
dataschema: {
Expand Down Expand Up @@ -129,7 +129,7 @@ export const schemaV03 = {
minLength: 1,
},
time: {
format: "date-time",
format: "js-date-time",
type: "string",
},
schemaurl: {
Expand Down
8 changes: 8 additions & 0 deletions src/event/spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ import { Version } from "./cloudevent";
import CONSTANTS from "../constants";

const ajv = new Ajv({ extendRefs: true });

// handle date-time format specially because a user could pass
// Date().toString(), which is not spec compliant date-time format
ajv.addFormat("js-date-time", function (dateTimeString) {
const date = new Date(Date.parse(dateTimeString));
return date.toString() !== "Invalid Date";
});

const isValidAgainstSchemaV1 = ajv.compile(schemaV1);
const isValidAgainstSchemaV03 = ajv.compile(schemaV03);

Expand Down
2 changes: 1 addition & 1 deletion src/message/http/headers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export function headersFor(event: CloudEvent): Headers {
});
// Treat time specially, since it's handled with getters and setters in CloudEvent
if (event.time) {
headers[CONSTANTS.CE_HEADERS.TIME] = event.time as string;
headers[CONSTANTS.CE_HEADERS.TIME] = new Date(event.time).toISOString();
}
return headers;
}
Expand Down
8 changes: 6 additions & 2 deletions src/parsers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,12 @@ export interface MappedParser {
}

export class DateParser extends Parser {
parse(payload: string): Date {
return new Date(Date.parse(payload));
parse(payload: string): string {
let date = new Date(Date.parse(payload));
if (date.toString() === "Invalid Date") {
date = new Date();
}
return date.toISOString();
}
}

Expand Down
8 changes: 5 additions & 3 deletions test/integration/cloud_event_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ describe("A CloudEvent", () => {
});

it("serializes as JSON with toString()", () => {
const ce = new CloudEvent(fixture);
const ce = new CloudEvent({ ...fixture, data: { lunch: "tacos" } });
expect(ce.toString()).to.deep.equal(JSON.stringify(ce));
expect(new CloudEvent(JSON.parse(ce.toString()))).to.deep.equal(ce);
expect(new CloudEvent(JSON.parse(JSON.stringify(ce)))).to.deep.equal(ce);
});

it("Throw a validation error for invalid extension names", () => {
Expand Down Expand Up @@ -188,9 +190,9 @@ describe("A 0.3 CloudEvent", () => {
});

it("can be constructed with a timestamp", () => {
const time = new Date();
const time = new Date().toISOString();
const ce = new CloudEvent({ time, ...v03fixture });
expect(ce.time).to.equal(time.toISOString());
expect(ce.time).to.equal(time);
});

it("can be constructed with a datacontenttype", () => {
Expand Down
2 changes: 1 addition & 1 deletion test/integration/http_binding_03.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const type = "com.github.pull.create";
const source = "urn:event:from:myapi/resourse/123";
const contentEncoding = "base64";
const contentType = "application/cloudevents+json; charset=utf-8";
const time = new Date();
const time = new Date().toISOString();
const schemaurl = "http://cloudevents.io/schema.json";

const ceContentType = "application/json";
Expand Down
2 changes: 1 addition & 1 deletion test/integration/http_binding_1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { AxiosResponse } from "axios";
const type = "com.github.pull.create";
const source = "urn:event:from:myapi/resource/123";
const contentType = "application/cloudevents+json; charset=utf-8";
const time = new Date();
const time = new Date().toISOString();
const subject = "subject.ext";
const dataschema = "http://cloudevents.io/schema.json";
const datacontenttype = "application/json";
Expand Down
4 changes: 2 additions & 2 deletions test/integration/http_emitter_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe("HTTP Transport Binding Emitter for CloudEvents", () => {
const event = new CloudEvent({
type,
source,
time: new Date(),
time: new Date().toISOString(),
data,
[ext1Name]: ext1Value,
[ext2Name]: ext2Value,
Expand Down Expand Up @@ -143,7 +143,7 @@ describe("HTTP Transport Binding Emitter for CloudEvents", () => {
specversion: Version.V03,
type,
source,
time: new Date(),
time: new Date().toISOString(),
data,
[ext1Name]: ext1Value,
[ext2Name]: ext2Value,
Expand Down
2 changes: 1 addition & 1 deletion test/integration/message_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Message, HTTP } from "../../src/message";

const type = "org.cncf.cloudevents.example";
const source = "urn:event:from:myapi/resource/123";
const time = new Date();
const time = new Date().toISOString();
const subject = "subject.ext";
const dataschema = "http://cloudevents.io/schema.json";
const datacontenttype = "application/json";
Expand Down
12 changes: 8 additions & 4 deletions test/integration/spec_03_tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Constants from "../../src/constants";
const id = "97699ec2-a8d9-47c1-bfa0-ff7aa526f838";
const type = "com.github.pull.create";
const source = "urn:event:from:myapi/resourse/123";
const time = new Date();
const time = new Date().toISOString();
const schemaurl = "http://example.com/registry/myschema.json";
const data = {
much: "wow",
Expand Down Expand Up @@ -68,7 +68,7 @@ describe("CloudEvents Spec v0.3", () => {
});

it("Should have 'time'", () => {
expect(cloudevent.time).to.equal(time.toISOString());
expect(cloudevent.time).to.equal(time);
});

it("Should have 'data'", () => {
Expand Down Expand Up @@ -186,8 +186,12 @@ describe("CloudEvents Spec v0.3", () => {

describe("'time'", () => {
it("must adhere to the format specified in RFC 3339", () => {
cloudevent = cloudevent.cloneWith({ time: time });
expect(cloudevent.time).to.equal(time.toISOString());
const d = new Date();
cloudevent = cloudevent.cloneWith({ time: d.toString() });
// ensure that we always get back the same thing we passed in
expect(cloudevent.time).to.equal(d.toString());
// ensure that when stringified, the timestamp is in RFC3339 format
expect(JSON.parse(JSON.stringify(cloudevent)).time).to.equal(new Date(d.toString()).toISOString());
});
});
});
Expand Down
12 changes: 8 additions & 4 deletions test/integration/spec_1_tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import Constants from "../../src/constants";
const id = "97699ec2-a8d9-47c1-bfa0-ff7aa526f838";
const type = "com.github.pull.create";
const source = "urn:event:from:myapi/resourse/123";
const time = new Date();
const time = new Date().toISOString();
const dataschema = "http://example.com/registry/myschema.json";
const data = {
much: "wow",
Expand Down Expand Up @@ -59,7 +59,7 @@ describe("CloudEvents Spec v1.0", () => {
});

it("Should have 'time'", () => {
expect(cloudevent.time).to.equal(time.toISOString());
expect(cloudevent.time).to.equal(time);
});
});

Expand Down Expand Up @@ -144,8 +144,12 @@ describe("CloudEvents Spec v1.0", () => {

describe("'time'", () => {
it("must adhere to the format specified in RFC 3339", () => {
cloudevent = cloudevent.cloneWith({ time: time });
expect(cloudevent.time).to.equal(time.toISOString());
const d = new Date();
cloudevent = cloudevent.cloneWith({ time: d.toString() });
// ensure that we always get back the same thing we passed in
expect(cloudevent.time).to.equal(d.toString());
// ensure that when stringified, the timestamp is in RFC3339 format
expect(JSON.parse(JSON.stringify(cloudevent)).time).to.equal(new Date(d.toString()).toISOString());
});
});
});
Expand Down