Skip to content

Commit 129ec48

Browse files
authored
refactor: combine v03 and v1 event interfaces, specs and schemas into single files(cloudevents#270)
Signed-off-by: Grant Timmerman <timmerman+devrel@google.com>
1 parent 45850e3 commit 129ec48

20 files changed

+264
-285
lines changed

src/event/cloudevent.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
import { v4 as uuidv4 } from "uuid";
22

3-
import { CloudEventV1, validateV1, CloudEventV1Attributes, CloudEventV1OptionalAttributes } from "./v1";
4-
import { CloudEventV03, validateV03, CloudEventV03Attributes, CloudEventV03OptionalAttributes } from "./v03";
3+
import {
4+
CloudEventV03,
5+
CloudEventV03Attributes,
6+
CloudEventV03OptionalAttributes,
7+
CloudEventV1,
8+
CloudEventV1Attributes,
9+
CloudEventV1OptionalAttributes,
10+
} from "./interfaces";
11+
import { validateV1, validateV03 } from "./spec";
512
import { ValidationError, isBinary, asBase64 } from "./validation";
613
import CONSTANTS from "../constants";
714
import { isString } from "util";

src/event/index.ts

-4
This file was deleted.

src/event/v03/cloudevent.ts renamed to src/event/interfaces.ts

+138
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,141 @@
1+
/**
2+
* The object interface for CloudEvents 1.0.
3+
* @see https://github.com/cloudevents/spec/blob/v1.0/spec.md
4+
*/
5+
export interface CloudEventV1 extends CloudEventV1Attributes {
6+
// REQUIRED Attributes
7+
/**
8+
* [REQUIRED] Identifies the event. Producers MUST ensure that `source` + `id`
9+
* is unique for each distinct event. If a duplicate event is re-sent (e.g. due
10+
* to a network error) it MAY have the same `id`. Consumers MAY assume that
11+
* Events with identical `source` and `id` are duplicates.
12+
* @required Non-empty string. Unique within producer.
13+
* @example An event counter maintained by the producer
14+
* @example A UUID
15+
*/
16+
id: string;
17+
18+
/**
19+
* [REQUIRED] The version of the CloudEvents specification which the event
20+
* uses. This enables the interpretation of the context. Compliant event
21+
* producers MUST use a value of `1.0` when referring to this version of the
22+
* specification.
23+
* @required MUST be a non-empty string.
24+
*/
25+
specversion: string;
26+
}
27+
28+
export interface CloudEventV1Attributes extends CloudEventV1OptionalAttributes {
29+
/**
30+
* [REQUIRED] Identifies the context in which an event happened. Often this
31+
* will include information such as the type of the event source, the
32+
* organization publishing the event or the process that produced the event. The
33+
* exact syntax and semantics behind the data encoded in the URI is defined by
34+
* the event producer.
35+
* Producers MUST ensure that `source` + `id` is unique for each distinct event.
36+
* An application MAY assign a unique `source` to each distinct producer, which
37+
* makes it easy to produce unique IDs since no other producer will have the same
38+
* source. The application MAY use UUIDs, URNs, DNS authorities or an
39+
* application-specific scheme to create unique `source` identifiers.
40+
* A source MAY include more than one producer. In that case the producers MUST
41+
* collaborate to ensure that `source` + `id` is unique for each distinct event.
42+
* @required Non-empty URI-reference
43+
*/
44+
source: string;
45+
46+
/**
47+
* [REQUIRED] This attribute contains a value describing the type of event
48+
* related to the originating occurrence. Often this attribute is used for
49+
* routing, observability, policy enforcement, etc. The format of this is
50+
* producer defined and might include information such as the version of the
51+
* `type` - see
52+
* [Versioning of Attributes in the Primer](primer.md#versioning-of-attributes)
53+
* for more information.
54+
* @required MUST be a non-empty string
55+
* @should SHOULD be prefixed with a reverse-DNS name. The prefixed domain dictates the
56+
* organization which defines the semantics of this event type.
57+
* @example com.github.pull.create
58+
* @example com.example.object.delete.v2
59+
*/
60+
type: string;
61+
}
62+
63+
export interface CloudEventV1OptionalAttributes {
64+
/**
65+
* The following fields are optional.
66+
*/
67+
68+
/**
69+
* [OPTIONAL] Content type of `data` value. This attribute enables `data` to
70+
* carry any type of content, whereby format and encoding might differ from that
71+
* of the chosen event format. For example, an event rendered using the
72+
* [JSON envelope](./json-format.md#3-envelope) format might carry an XML payload
73+
* in `data`, and the consumer is informed by this attribute being set to
74+
* "application/xml". The rules for how `data` content is rendered for different
75+
* `datacontenttype` values are defined in the event format specifications; for
76+
* example, the JSON event format defines the relationship in
77+
* [section 3.1](./json-format.md#31-handling-of-data).
78+
*/
79+
datacontenttype?: string;
80+
/**
81+
* [OPTIONAL] Identifies the schema that `data` adheres to. Incompatible
82+
* changes to the schema SHOULD be reflected by a different URI. See
83+
* [Versioning of Attributes in the Primer](primer.md#versioning-of-attributes)
84+
* for more information.
85+
* If present, MUST be a non-empty URI.
86+
*/
87+
dataschema?: string;
88+
/**
89+
* [OPTIONAL] This describes the subject of the event in the context of the
90+
* event producer (identified by `source`). In publish-subscribe scenarios, a
91+
* subscriber will typically subscribe to events emitted by a `source`, but the
92+
* `source` identifier alone might not be sufficient as a qualifier for any
93+
* specific event if the `source` context has internal sub-structure.
94+
*
95+
* Identifying the subject of the event in context metadata (opposed to only in
96+
* the `data` payload) is particularly helpful in generic subscription filtering
97+
* scenarios where middleware is unable to interpret the `data` content. In the
98+
* above example, the subscriber might only be interested in blobs with names
99+
* ending with '.jpg' or '.jpeg' and the `subject` attribute allows for
100+
* constructing a simple and efficient string-suffix filter for that subset of
101+
* events.
102+
*
103+
* If present, MUST be a non-empty string.
104+
* @example "https://example.com/storage/tenant/container"
105+
* @example "mynewfile.jpg"
106+
*/
107+
subject?: string;
108+
/**
109+
* [OPTIONAL] Timestamp of when the occurrence happened. If the time of the
110+
* occurrence cannot be determined then this attribute MAY be set to some other
111+
* time (such as the current time) by the CloudEvents producer, however all
112+
* producers for the same `source` MUST be consistent in this respect. In other
113+
* words, either they all use the actual time of the occurrence or they all use
114+
* the same algorithm to determine the value used.
115+
* @example "2020-08-08T14:48:09.769Z"
116+
*/
117+
time?: Date | string;
118+
/**
119+
* [OPTIONAL] The event payload. This specification does not place any restriction
120+
* on the type of this information. It is encoded into a media format which is
121+
* specified by the datacontenttype attribute (e.g. application/json), and adheres
122+
* to the dataschema format when those respective attributes are present.
123+
*/
124+
data?: Record<string, unknown | string | number | boolean> | string | number | boolean | null | unknown;
125+
126+
/**
127+
* [OPTIONAL] The event payload encoded as base64 data. This is used when the
128+
* data is in binary form.
129+
* @see https://github.com/cloudevents/spec/blob/v1.0/json-format.md#31-handling-of-data
130+
*/
131+
data_base64?: string;
132+
133+
/**
134+
* [OPTIONAL] CloudEvents extension attributes.
135+
*/
136+
[key: string]: unknown;
137+
}
138+
1139
/**
2140
* The object interface for CloudEvents 0.3.
3141
* @see https://github.com/cloudevents/spec/blob/v0.3/spec.md

src/event/v1/schema.ts renamed to src/event/schemas.ts

+73-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const schemaV1 = {
1+
export const schemaV1 = {
22
$ref: "#/definitions/event",
33
definitions: {
44
specversion: {
@@ -79,4 +79,75 @@ const schemaV1 = {
7979
type: "object",
8080
};
8181

82-
export { schemaV1 };
82+
export const schemaV03 = {
83+
$ref: "#/definitions/event",
84+
definitions: {
85+
specversion: {
86+
const: "0.3",
87+
},
88+
datacontenttype: {
89+
type: "string",
90+
},
91+
data: {
92+
type: ["object", "string"],
93+
},
94+
event: {
95+
properties: {
96+
specversion: {
97+
$ref: "#/definitions/specversion",
98+
},
99+
datacontenttype: {
100+
$ref: "#/definitions/datacontenttype",
101+
},
102+
data: {
103+
$ref: "#/definitions/data",
104+
},
105+
id: {
106+
$ref: "#/definitions/id",
107+
},
108+
time: {
109+
$ref: "#/definitions/time",
110+
},
111+
schemaurl: {
112+
$ref: "#/definitions/schemaurl",
113+
},
114+
subject: {
115+
$ref: "#/definitions/subject",
116+
},
117+
type: {
118+
$ref: "#/definitions/type",
119+
},
120+
source: {
121+
$ref: "#/definitions/source",
122+
},
123+
},
124+
required: ["specversion", "id", "type", "source"],
125+
type: "object",
126+
},
127+
id: {
128+
type: "string",
129+
minLength: 1,
130+
},
131+
time: {
132+
format: "date-time",
133+
type: "string",
134+
},
135+
schemaurl: {
136+
type: "string",
137+
format: "uri-reference",
138+
},
139+
subject: {
140+
type: "string",
141+
minLength: 1,
142+
},
143+
type: {
144+
type: "string",
145+
minLength: 1,
146+
},
147+
source: {
148+
format: "uri-reference",
149+
type: "string",
150+
},
151+
},
152+
type: "object",
153+
};

src/event/v03/spec.ts renamed to src/event/spec.ts

+29-10
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,36 @@
1-
import { v4 as uuidv4 } from "uuid";
21
import Ajv from "ajv";
3-
import { ValidationError, isBase64 } from "../validation";
4-
import { CloudEventV03, CloudEventV03Attributes } from "./cloudevent";
5-
import { CloudEvent } from "../..";
6-
import { schema } from "./schema";
7-
import CONSTANTS from "../../constants";
2+
import { v4 as uuidv4 } from "uuid";
3+
import { ValidationError, isBase64 } from "./validation";
4+
5+
import { CloudEvent } from "./cloudevent";
6+
import { CloudEventV1, CloudEventV1Attributes, CloudEventV03, CloudEventV03Attributes } from "./interfaces";
7+
import { schemaV03, schemaV1 } from "./schemas";
8+
import CONSTANTS from "../constants";
89

910
const ajv = new Ajv({ extendRefs: true });
10-
const isValidAgainstSchema = ajv.compile(schema);
11+
const isValidAgainstSchemaV1 = ajv.compile(schemaV1);
12+
const isValidAgainstSchemaV03 = ajv.compile(schemaV03);
13+
14+
export function createV1(attributes: CloudEventV1Attributes): CloudEventV1 {
15+
const event: CloudEventV1 = {
16+
specversion: schemaV1.definitions.specversion.const,
17+
id: uuidv4(),
18+
time: new Date().toISOString(),
19+
...attributes,
20+
};
21+
return new CloudEvent(event);
22+
}
23+
24+
export function validateV1(event: CloudEventV1): boolean {
25+
if (!isValidAgainstSchemaV1(event)) {
26+
throw new ValidationError("invalid payload", isValidAgainstSchemaV1.errors);
27+
}
28+
return true;
29+
}
1130

1231
export function createV03(attributes: CloudEventV03Attributes): CloudEventV03 {
1332
const event: CloudEventV03 = {
14-
specversion: schema.definitions.specversion.const,
33+
specversion: schemaV03.definitions.specversion.const,
1534
id: uuidv4(),
1635
time: new Date().toISOString(),
1736
...attributes,
@@ -20,8 +39,8 @@ export function createV03(attributes: CloudEventV03Attributes): CloudEventV03 {
2039
}
2140

2241
export function validateV03(event: CloudEventV03): boolean {
23-
if (!isValidAgainstSchema(event)) {
24-
throw new ValidationError("invalid payload", isValidAgainstSchema.errors);
42+
if (!isValidAgainstSchemaV03(event)) {
43+
throw new ValidationError("invalid payload", isValidAgainstSchemaV03.errors);
2544
}
2645
return checkDataContentEncoding(event);
2746
}

src/event/v03/index.ts

-3
This file was deleted.

src/event/v03/schema.ts

-74
This file was deleted.

0 commit comments

Comments
 (0)