refactor: combine v03 and v1 event interfaces, specs and schemas into single files(#270)

Signed-off-by: Grant Timmerman <timmerman+devrel@google.com>
This commit is contained in:
Grant Timmerman 2020-07-24 12:35:18 -07:00 committed by GitHub
parent 45850e329a
commit 129ec485d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 264 additions and 285 deletions

View File

@ -1,7 +1,14 @@
import { v4 as uuidv4 } from "uuid";
import { CloudEventV1, validateV1, CloudEventV1Attributes, CloudEventV1OptionalAttributes } from "./v1";
import { CloudEventV03, validateV03, CloudEventV03Attributes, CloudEventV03OptionalAttributes } from "./v03";
import {
CloudEventV03,
CloudEventV03Attributes,
CloudEventV03OptionalAttributes,
CloudEventV1,
CloudEventV1Attributes,
CloudEventV1OptionalAttributes,
} from "./interfaces";
import { validateV1, validateV03 } from "./spec";
import { ValidationError, isBinary, asBase64 } from "./validation";
import CONSTANTS from "../constants";
import { isString } from "util";

View File

@ -1,4 +0,0 @@
export * from "./cloudevent";
export * from "./validation";
export * from "./v1";
export * from "./v03";

View File

@ -1,3 +1,141 @@
/**
* The object interface for CloudEvents 1.0.
* @see https://github.com/cloudevents/spec/blob/v1.0/spec.md
*/
export interface CloudEventV1 extends CloudEventV1Attributes {
// REQUIRED Attributes
/**
* [REQUIRED] Identifies the event. Producers MUST ensure that `source` + `id`
* is unique for each distinct event. If a duplicate event is re-sent (e.g. due
* to a network error) it MAY have the same `id`. Consumers MAY assume that
* Events with identical `source` and `id` are duplicates.
* @required Non-empty string. Unique within producer.
* @example An event counter maintained by the producer
* @example A UUID
*/
id: string;
/**
* [REQUIRED] The version of the CloudEvents specification which the event
* uses. This enables the interpretation of the context. Compliant event
* producers MUST use a value of `1.0` when referring to this version of the
* specification.
* @required MUST be a non-empty string.
*/
specversion: string;
}
export interface CloudEventV1Attributes extends CloudEventV1OptionalAttributes {
/**
* [REQUIRED] Identifies the context in which an event happened. Often this
* will include information such as the type of the event source, the
* organization publishing the event or the process that produced the event. The
* exact syntax and semantics behind the data encoded in the URI is defined by
* the event producer.
* Producers MUST ensure that `source` + `id` is unique for each distinct event.
* An application MAY assign a unique `source` to each distinct producer, which
* makes it easy to produce unique IDs since no other producer will have the same
* source. The application MAY use UUIDs, URNs, DNS authorities or an
* application-specific scheme to create unique `source` identifiers.
* A source MAY include more than one producer. In that case the producers MUST
* collaborate to ensure that `source` + `id` is unique for each distinct event.
* @required Non-empty URI-reference
*/
source: string;
/**
* [REQUIRED] This attribute contains a value describing the type of event
* related to the originating occurrence. Often this attribute is used for
* routing, observability, policy enforcement, etc. The format of this is
* producer defined and might include information such as the version of the
* `type` - see
* [Versioning of Attributes in the Primer](primer.md#versioning-of-attributes)
* for more information.
* @required MUST be a non-empty string
* @should SHOULD be prefixed with a reverse-DNS name. The prefixed domain dictates the
* organization which defines the semantics of this event type.
* @example com.github.pull.create
* @example com.example.object.delete.v2
*/
type: string;
}
export interface CloudEventV1OptionalAttributes {
/**
* The following fields are optional.
*/
/**
* [OPTIONAL] Content type of `data` value. This attribute enables `data` to
* carry any type of content, whereby format and encoding might differ from that
* of the chosen event format. For example, an event rendered using the
* [JSON envelope](./json-format.md#3-envelope) format might carry an XML payload
* in `data`, and the consumer is informed by this attribute being set to
* "application/xml". The rules for how `data` content is rendered for different
* `datacontenttype` values are defined in the event format specifications; for
* example, the JSON event format defines the relationship in
* [section 3.1](./json-format.md#31-handling-of-data).
*/
datacontenttype?: string;
/**
* [OPTIONAL] Identifies the schema that `data` adheres to. Incompatible
* changes to the schema SHOULD be reflected by a different URI. See
* [Versioning of Attributes in the Primer](primer.md#versioning-of-attributes)
* for more information.
* If present, MUST be a non-empty URI.
*/
dataschema?: string;
/**
* [OPTIONAL] This describes the subject of the event in the context of the
* event producer (identified by `source`). In publish-subscribe scenarios, a
* subscriber will typically subscribe to events emitted by a `source`, but the
* `source` identifier alone might not be sufficient as a qualifier for any
* specific event if the `source` context has internal sub-structure.
*
* Identifying the subject of the event in context metadata (opposed to only in
* the `data` payload) is particularly helpful in generic subscription filtering
* scenarios where middleware is unable to interpret the `data` content. In the
* above example, the subscriber might only be interested in blobs with names
* ending with '.jpg' or '.jpeg' and the `subject` attribute allows for
* constructing a simple and efficient string-suffix filter for that subset of
* events.
*
* If present, MUST be a non-empty string.
* @example "https://example.com/storage/tenant/container"
* @example "mynewfile.jpg"
*/
subject?: string;
/**
* [OPTIONAL] Timestamp of when the occurrence happened. If the time of the
* occurrence cannot be determined then this attribute MAY be set to some other
* time (such as the current time) by the CloudEvents producer, however all
* producers for the same `source` MUST be consistent in this respect. In other
* words, either they all use the actual time of the occurrence or they all use
* the same algorithm to determine the value used.
* @example "2020-08-08T14:48:09.769Z"
*/
time?: Date | 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
* specified by the datacontenttype attribute (e.g. application/json), and adheres
* to the dataschema format when those respective attributes are present.
*/
data?: Record<string, unknown | string | number | boolean> | string | number | boolean | null | unknown;
/**
* [OPTIONAL] The event payload encoded as base64 data. This is used when the
* data is in binary form.
* @see https://github.com/cloudevents/spec/blob/v1.0/json-format.md#31-handling-of-data
*/
data_base64?: string;
/**
* [OPTIONAL] CloudEvents extension attributes.
*/
[key: string]: unknown;
}
/**
* The object interface for CloudEvents 0.3.
* @see https://github.com/cloudevents/spec/blob/v0.3/spec.md

View File

@ -1,4 +1,4 @@
const schemaV1 = {
export const schemaV1 = {
$ref: "#/definitions/event",
definitions: {
specversion: {
@ -79,4 +79,75 @@ const schemaV1 = {
type: "object",
};
export { schemaV1 };
export const schemaV03 = {
$ref: "#/definitions/event",
definitions: {
specversion: {
const: "0.3",
},
datacontenttype: {
type: "string",
},
data: {
type: ["object", "string"],
},
event: {
properties: {
specversion: {
$ref: "#/definitions/specversion",
},
datacontenttype: {
$ref: "#/definitions/datacontenttype",
},
data: {
$ref: "#/definitions/data",
},
id: {
$ref: "#/definitions/id",
},
time: {
$ref: "#/definitions/time",
},
schemaurl: {
$ref: "#/definitions/schemaurl",
},
subject: {
$ref: "#/definitions/subject",
},
type: {
$ref: "#/definitions/type",
},
source: {
$ref: "#/definitions/source",
},
},
required: ["specversion", "id", "type", "source"],
type: "object",
},
id: {
type: "string",
minLength: 1,
},
time: {
format: "date-time",
type: "string",
},
schemaurl: {
type: "string",
format: "uri-reference",
},
subject: {
type: "string",
minLength: 1,
},
type: {
type: "string",
minLength: 1,
},
source: {
format: "uri-reference",
type: "string",
},
},
type: "object",
};

View File

@ -1,17 +1,36 @@
import { v4 as uuidv4 } from "uuid";
import Ajv from "ajv";
import { ValidationError, isBase64 } from "../validation";
import { CloudEventV03, CloudEventV03Attributes } from "./cloudevent";
import { CloudEvent } from "../..";
import { schema } from "./schema";
import CONSTANTS from "../../constants";
import { v4 as uuidv4 } from "uuid";
import { ValidationError, isBase64 } from "./validation";
import { CloudEvent } from "./cloudevent";
import { CloudEventV1, CloudEventV1Attributes, CloudEventV03, CloudEventV03Attributes } from "./interfaces";
import { schemaV03, schemaV1 } from "./schemas";
import CONSTANTS from "../constants";
const ajv = new Ajv({ extendRefs: true });
const isValidAgainstSchema = ajv.compile(schema);
const isValidAgainstSchemaV1 = ajv.compile(schemaV1);
const isValidAgainstSchemaV03 = ajv.compile(schemaV03);
export function createV1(attributes: CloudEventV1Attributes): CloudEventV1 {
const event: CloudEventV1 = {
specversion: schemaV1.definitions.specversion.const,
id: uuidv4(),
time: new Date().toISOString(),
...attributes,
};
return new CloudEvent(event);
}
export function validateV1(event: CloudEventV1): boolean {
if (!isValidAgainstSchemaV1(event)) {
throw new ValidationError("invalid payload", isValidAgainstSchemaV1.errors);
}
return true;
}
export function createV03(attributes: CloudEventV03Attributes): CloudEventV03 {
const event: CloudEventV03 = {
specversion: schema.definitions.specversion.const,
specversion: schemaV03.definitions.specversion.const,
id: uuidv4(),
time: new Date().toISOString(),
...attributes,
@ -20,8 +39,8 @@ export function createV03(attributes: CloudEventV03Attributes): CloudEventV03 {
}
export function validateV03(event: CloudEventV03): boolean {
if (!isValidAgainstSchema(event)) {
throw new ValidationError("invalid payload", isValidAgainstSchema.errors);
if (!isValidAgainstSchemaV03(event)) {
throw new ValidationError("invalid payload", isValidAgainstSchemaV03.errors);
}
return checkDataContentEncoding(event);
}

View File

@ -1,3 +0,0 @@
export * from "./cloudevent";
export * from "./spec";
export * from "./schema";

View File

@ -1,74 +0,0 @@
const schemaV03 = {
$ref: "#/definitions/event",
definitions: {
specversion: {
const: "0.3",
},
datacontenttype: {
type: "string",
},
data: {
type: ["object", "string"],
},
event: {
properties: {
specversion: {
$ref: "#/definitions/specversion",
},
datacontenttype: {
$ref: "#/definitions/datacontenttype",
},
data: {
$ref: "#/definitions/data",
},
id: {
$ref: "#/definitions/id",
},
time: {
$ref: "#/definitions/time",
},
schemaurl: {
$ref: "#/definitions/schemaurl",
},
subject: {
$ref: "#/definitions/subject",
},
type: {
$ref: "#/definitions/type",
},
source: {
$ref: "#/definitions/source",
},
},
required: ["specversion", "id", "type", "source"],
type: "object",
},
id: {
type: "string",
minLength: 1,
},
time: {
format: "date-time",
type: "string",
},
schemaurl: {
type: "string",
format: "uri-reference",
},
subject: {
type: "string",
minLength: 1,
},
type: {
type: "string",
minLength: 1,
},
source: {
format: "uri-reference",
type: "string",
},
},
type: "object",
};
export { schemaV03 as schema };

View File

@ -1,137 +0,0 @@
/**
* The object interface for CloudEvents 1.0.
* @see https://github.com/cloudevents/spec/blob/v1.0/spec.md
*/
export interface CloudEventV1 extends CloudEventV1Attributes {
// REQUIRED Attributes
/**
* [REQUIRED] Identifies the event. Producers MUST ensure that `source` + `id`
* is unique for each distinct event. If a duplicate event is re-sent (e.g. due
* to a network error) it MAY have the same `id`. Consumers MAY assume that
* Events with identical `source` and `id` are duplicates.
* @required Non-empty string. Unique within producer.
* @example An event counter maintained by the producer
* @example A UUID
*/
id: string;
/**
* [REQUIRED] The version of the CloudEvents specification which the event
* uses. This enables the interpretation of the context. Compliant event
* producers MUST use a value of `1.0` when referring to this version of the
* specification.
* @required MUST be a non-empty string.
*/
specversion: string;
}
export interface CloudEventV1Attributes extends CloudEventV1OptionalAttributes {
/**
* [REQUIRED] Identifies the context in which an event happened. Often this
* will include information such as the type of the event source, the
* organization publishing the event or the process that produced the event. The
* exact syntax and semantics behind the data encoded in the URI is defined by
* the event producer.
* Producers MUST ensure that `source` + `id` is unique for each distinct event.
* An application MAY assign a unique `source` to each distinct producer, which
* makes it easy to produce unique IDs since no other producer will have the same
* source. The application MAY use UUIDs, URNs, DNS authorities or an
* application-specific scheme to create unique `source` identifiers.
* A source MAY include more than one producer. In that case the producers MUST
* collaborate to ensure that `source` + `id` is unique for each distinct event.
* @required Non-empty URI-reference
*/
source: string;
/**
* [REQUIRED] This attribute contains a value describing the type of event
* related to the originating occurrence. Often this attribute is used for
* routing, observability, policy enforcement, etc. The format of this is
* producer defined and might include information such as the version of the
* `type` - see
* [Versioning of Attributes in the Primer](primer.md#versioning-of-attributes)
* for more information.
* @required MUST be a non-empty string
* @should SHOULD be prefixed with a reverse-DNS name. The prefixed domain dictates the
* organization which defines the semantics of this event type.
* @example com.github.pull.create
* @example com.example.object.delete.v2
*/
type: string;
}
export interface CloudEventV1OptionalAttributes {
/**
* The following fields are optional.
*/
/**
* [OPTIONAL] Content type of `data` value. This attribute enables `data` to
* carry any type of content, whereby format and encoding might differ from that
* of the chosen event format. For example, an event rendered using the
* [JSON envelope](./json-format.md#3-envelope) format might carry an XML payload
* in `data`, and the consumer is informed by this attribute being set to
* "application/xml". The rules for how `data` content is rendered for different
* `datacontenttype` values are defined in the event format specifications; for
* example, the JSON event format defines the relationship in
* [section 3.1](./json-format.md#31-handling-of-data).
*/
datacontenttype?: string;
/**
* [OPTIONAL] Identifies the schema that `data` adheres to. Incompatible
* changes to the schema SHOULD be reflected by a different URI. See
* [Versioning of Attributes in the Primer](primer.md#versioning-of-attributes)
* for more information.
* If present, MUST be a non-empty URI.
*/
dataschema?: string;
/**
* [OPTIONAL] This describes the subject of the event in the context of the
* event producer (identified by `source`). In publish-subscribe scenarios, a
* subscriber will typically subscribe to events emitted by a `source`, but the
* `source` identifier alone might not be sufficient as a qualifier for any
* specific event if the `source` context has internal sub-structure.
*
* Identifying the subject of the event in context metadata (opposed to only in
* the `data` payload) is particularly helpful in generic subscription filtering
* scenarios where middleware is unable to interpret the `data` content. In the
* above example, the subscriber might only be interested in blobs with names
* ending with '.jpg' or '.jpeg' and the `subject` attribute allows for
* constructing a simple and efficient string-suffix filter for that subset of
* events.
*
* If present, MUST be a non-empty string.
* @example "https://example.com/storage/tenant/container"
* @example "mynewfile.jpg"
*/
subject?: string;
/**
* [OPTIONAL] Timestamp of when the occurrence happened. If the time of the
* occurrence cannot be determined then this attribute MAY be set to some other
* time (such as the current time) by the CloudEvents producer, however all
* producers for the same `source` MUST be consistent in this respect. In other
* words, either they all use the actual time of the occurrence or they all use
* the same algorithm to determine the value used.
* @example "2020-08-08T14:48:09.769Z"
*/
time?: Date | 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
* specified by the datacontenttype attribute (e.g. application/json), and adheres
* to the dataschema format when those respective attributes are present.
*/
data?: Record<string, unknown | string | number | boolean> | string | number | boolean | null | unknown;
/**
* [OPTIONAL] The event payload encoded as base64 data. This is used when the
* data is in binary form.
* @see https://github.com/cloudevents/spec/blob/v1.0/json-format.md#31-handling-of-data
*/
data_base64?: string;
/**
* [OPTIONAL] CloudEvents extension attributes.
*/
[key: string]: unknown;
}

View File

@ -1,3 +0,0 @@
export * from "./cloudevent";
export * from "./spec";
export * from "./schema";

View File

@ -1,28 +0,0 @@
import Ajv from "ajv";
import { v4 as uuidv4 } from "uuid";
import { CloudEvent } from "../cloudevent";
import { CloudEventV1, CloudEventV1Attributes } from "./cloudevent";
import { ValidationError } from "../validation";
import { schemaV1 } from "./schema";
const ajv = new Ajv({ extendRefs: true });
const isValidAgainstSchema = ajv.compile(schemaV1);
export function createV1(attributes: CloudEventV1Attributes): CloudEventV1 {
const event: CloudEventV1 = {
specversion: schemaV1.definitions.specversion.const,
id: uuidv4(),
time: new Date().toISOString(),
...attributes,
};
return new CloudEvent(event);
}
export function validateV1(event: CloudEventV1): boolean {
if (!isValidAgainstSchema(event)) {
throw new ValidationError("invalid payload", isValidAgainstSchema.errors);
}
return true;
}

View File

@ -1,12 +1,7 @@
import {
CloudEvent,
CloudEventV03,
CloudEventV03Attributes,
CloudEventV1,
CloudEventV1Attributes,
ValidationError,
Version,
} from "./event";
import { CloudEvent, Version } from "./event/cloudevent";
import { ValidationError } from "./event/validation";
// import {Version} from './event/'
import { CloudEventV03, CloudEventV03Attributes, CloudEventV1, CloudEventV1Attributes } from "./event/interfaces";
import { Emitter, Receiver, Mode, Protocol, TransportOptions } from "./transport";
import { Headers, headersFor } from "./transport/http/headers";

View File

@ -1,4 +1,4 @@
import { CloudEvent } from "../event";
import { CloudEvent } from "../event/cloudevent";
import { emitBinary, emitStructured } from "./http";
import { Protocol } from ".";
import { AxiosResponse } from "axios";

View File

@ -1,6 +1,6 @@
import axios, { AxiosRequestConfig, AxiosResponse } from "axios";
import { CloudEvent, Version } from "../../event";
import { CloudEvent, Version } from "../../event/cloudevent";
import { TransportOptions } from "../emitter";
import { Headers, headersFor } from "./headers";
import { asData } from "../../event/validation";

View File

@ -1,6 +1,6 @@
import { CloudEvent, Version } from "../..";
import { CloudEventV1, validateV1 } from "../../event/v1";
import { CloudEventV03, validateV03 } from "../../event/v03";
import { CloudEventV1, CloudEventV03 } from "../../event/interfaces";
import { validateV1, validateV03 } from "../../event/spec";
import { Headers, validate } from "./headers";
import { binaryParsers as v1Parsers } from "./v1";
import { binaryParsers as v03Parsers } from "./v03";

View File

@ -1,7 +1,7 @@
import { ValidationError, CloudEvent } from "../..";
import { headerMap as v1Map } from "./v1";
import { headerMap as v03Map } from "./v03";
import { Version } from "../../event";
import { Version } from "../../event/cloudevent";
import { MappedParser } from "../../parsers";
import CONSTANTS from "../../constants";

View File

@ -1,5 +1,5 @@
import axios, { AxiosRequestConfig, AxiosResponse } from "axios";
import { CloudEvent } from "../../event";
import { CloudEvent } from "../../event/cloudevent";
import { TransportOptions } from "../emitter";
import CONSTANTS from "../../constants";

View File

@ -5,8 +5,8 @@ import { parserByContentType } from "../../parsers";
import { structuredParsers as v1Parsers } from "./v1";
import { structuredParsers as v03Parsers } from "./v03";
import { isString, isBase64, ValidationError, isStringOrObjectOrThrow } from "../../event/validation";
import { CloudEventV1, validateV1 } from "../../event/v1";
import { CloudEventV03, validateV03 } from "../../event/v03";
import { CloudEventV1, CloudEventV03 } from "../../event/interfaces";
import { validateV1, validateV03 } from "../../event/spec";
import CONSTANTS from "../../constants";
/**

View File

@ -2,8 +2,7 @@ import { Headers, sanitize } from "./http/headers";
import { CloudEvent, Version, ValidationError } from "..";
import { BinaryHTTPReceiver as BinaryReceiver } from "./http/binary_receiver";
import { StructuredHTTPReceiver as StructuredReceiver } from "./http/structured_receiver";
import { CloudEventV03 } from "../event/v03";
import { CloudEventV1 } from "../event/v1";
import { CloudEventV1, CloudEventV03 } from "../event/interfaces";
import CONSTANTS from "../constants";
/**

View File

@ -1,7 +1,6 @@
import { expect } from "chai";
import { CloudEvent, Version } from "../../src";
import { CloudEventV03 } from "../../src/event/v03";
import { CloudEventV1 } from "../../src/event/v1";
import { CloudEventV03, CloudEventV1 } from "../../src/event/interfaces";
const type = "org.cncf.cloudevents.example";
const source = "http://unit.test";

View File

@ -1,7 +1,7 @@
import "mocha";
import { expect } from "chai";
import { CloudEvent, Receiver, ValidationError } from "../../src";
import { CloudEventV1 } from "../../src/event/v1";
import { CloudEventV1 } from "../../src/event/interfaces";
const id = "1234";
const type = "org.cncf.cloudevents.test";