-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add message-queue-toolkit/schemas (#151)
- Loading branch information
Showing
18 changed files
with
459 additions
and
231 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,69 +1,12 @@ | ||
import type { ZodLiteral, ZodObject, ZodOptional, ZodString } from 'zod' | ||
import z from 'zod' | ||
import type { ZodRawShape } from 'zod/lib/types' | ||
|
||
// Base event fields that are typically autogenerated | ||
export const GENERATED_BASE_EVENT_SCHEMA = z.object({ | ||
id: z.string().describe('event unique identifier'), | ||
timestamp: z.string().datetime().describe('iso 8601 datetime'), | ||
}) | ||
|
||
// Base event fields that are typically autogenerated, marked as optional | ||
export const OPTIONAL_GENERATED_BASE_EVENT_SCHEMA = z.object({ | ||
id: z.string().describe('event unique identifier').optional(), | ||
timestamp: z.string().datetime().describe('iso 8601 datetime').optional(), | ||
}) | ||
|
||
// Base event fields that are always defined manually | ||
export const CORE_EVENT_SCHEMA = z.object({ | ||
type: z.literal<string>('<replace.me>').describe('event type name'), | ||
payload: z.optional(z.object({})).describe('event payload based on type'), | ||
}) | ||
|
||
// Core fields that describe either internal event or external message | ||
export const CONSUMER_BASE_EVENT_SCHEMA = GENERATED_BASE_EVENT_SCHEMA.extend( | ||
CORE_EVENT_SCHEMA.shape, | ||
) | ||
export const PUBLISHER_BASE_EVENT_SCHEMA = OPTIONAL_GENERATED_BASE_EVENT_SCHEMA.extend( | ||
CORE_EVENT_SCHEMA.shape, | ||
) | ||
|
||
export type ConsumerBaseEventType = z.infer<typeof CONSUMER_BASE_EVENT_SCHEMA> | ||
export type PublisherBaseEventType = z.infer<typeof PUBLISHER_BASE_EVENT_SCHEMA> | ||
export type CoreEventType = z.infer<typeof CORE_EVENT_SCHEMA> | ||
export type GeneratedBaseEventType = z.infer<typeof GENERATED_BASE_EVENT_SCHEMA> | ||
|
||
type ReturnType<T extends ZodObject<Y>, Y extends ZodRawShape, Z extends string> = { | ||
consumerSchema: ZodObject<{ | ||
id: ZodString | ||
timestamp: ZodString | ||
type: ZodLiteral<Z> | ||
payload: T | ||
}> | ||
|
||
publisherSchema: ZodObject<{ | ||
id: ZodOptional<ZodString> | ||
timestamp: ZodOptional<ZodString> | ||
type: ZodLiteral<Z> | ||
payload: T | ||
}> | ||
} | ||
|
||
export function enrichEventSchemaWithBase< | ||
T extends ZodObject<Y>, | ||
Y extends ZodRawShape, | ||
Z extends string, | ||
>(type: Z, payloadSchema: T): ReturnType<T, Y, Z> { | ||
const baseSchema = z.object({ | ||
type: z.literal(type), | ||
payload: payloadSchema, | ||
}) | ||
|
||
const consumerSchema = GENERATED_BASE_EVENT_SCHEMA.merge(baseSchema) | ||
const publisherSchema = OPTIONAL_GENERATED_BASE_EVENT_SCHEMA.merge(baseSchema) | ||
|
||
return { | ||
consumerSchema: consumerSchema, | ||
publisherSchema: publisherSchema, | ||
} | ||
} | ||
export { | ||
GENERATED_BASE_EVENT_SCHEMA, | ||
OPTIONAL_GENERATED_BASE_EVENT_SCHEMA, | ||
CORE_EVENT_SCHEMA, | ||
CONSUMER_BASE_EVENT_SCHEMA, | ||
PUBLISHER_BASE_EVENT_SCHEMA, | ||
ConsumerBaseEventType, | ||
PublisherBaseEventType, | ||
CoreEventType, | ||
GeneratedBaseEventType, | ||
enrichEventSchemaWithBase, | ||
} from '@message-queue-toolkit/schemas' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,55 +1,9 @@ | ||
import type { ZodObject, ZodTypeAny } from 'zod' | ||
import type z from 'zod' | ||
|
||
import type { MessageMetadataType } from '../messages/baseMessageSchemas' | ||
|
||
import type { CONSUMER_BASE_EVENT_SCHEMA, PUBLISHER_BASE_EVENT_SCHEMA } from './baseEventSchemas' | ||
|
||
export type EventTypeNames<EventDefinition extends CommonEventDefinition> = | ||
CommonEventDefinitionConsumerSchemaType<EventDefinition>['type'] | ||
|
||
export type CommonEventDefinition = { | ||
consumerSchema: ZodObject< | ||
Omit<(typeof CONSUMER_BASE_EVENT_SCHEMA)['shape'], 'payload'> & { payload: ZodTypeAny } | ||
> | ||
publisherSchema: ZodObject< | ||
Omit<(typeof PUBLISHER_BASE_EVENT_SCHEMA)['shape'], 'payload'> & { payload: ZodTypeAny } | ||
> | ||
schemaVersion?: string | ||
} | ||
|
||
export type CommonEventDefinitionConsumerSchemaType<T extends CommonEventDefinition> = z.infer< | ||
T['consumerSchema'] | ||
> | ||
|
||
export type CommonEventDefinitionPublisherSchemaType<T extends CommonEventDefinition> = z.infer< | ||
T['publisherSchema'] | ||
> | ||
|
||
export type EventHandler< | ||
EventDefinitionSchema extends | ||
CommonEventDefinitionConsumerSchemaType<CommonEventDefinition> = CommonEventDefinitionConsumerSchemaType<CommonEventDefinition>, | ||
MetadataDefinitionSchema extends Partial<MessageMetadataType> = Partial<MessageMetadataType>, | ||
> = { | ||
handleEvent( | ||
event: EventDefinitionSchema, | ||
metadata?: MetadataDefinitionSchema, | ||
): void | Promise<void> | ||
} | ||
|
||
export type AnyEventHandler<EventDefinitions extends CommonEventDefinition[]> = EventHandler< | ||
CommonEventDefinitionConsumerSchemaType<EventDefinitions[number]> | ||
> | ||
|
||
export type SingleEventHandler< | ||
EventDefinition extends CommonEventDefinition[], | ||
EventTypeName extends EventTypeNames<EventDefinition[number]>, | ||
> = EventHandler<EventFromArrayByTypeName<EventDefinition, EventTypeName>> | ||
|
||
type EventFromArrayByTypeName< | ||
EventDefinition extends CommonEventDefinition[], | ||
EventTypeName extends EventTypeNames<EventDefinition[number]>, | ||
> = Extract< | ||
CommonEventDefinitionConsumerSchemaType<EventDefinition[number]>, | ||
{ type: EventTypeName } | ||
> | ||
export { | ||
EventTypeNames, | ||
CommonEventDefinition, | ||
CommonEventDefinitionConsumerSchemaType, | ||
CommonEventDefinitionPublisherSchemaType, | ||
EventHandler, | ||
AnyEventHandler, | ||
SingleEventHandler, | ||
} from '@message-queue-toolkit/schemas' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,93 +1,9 @@ | ||
import z, { type ZodLiteral, type ZodObject, type ZodOptional, type ZodString } from 'zod' | ||
import type { ZodRawShape } from 'zod/lib/types' | ||
|
||
import { | ||
CONSUMER_BASE_EVENT_SCHEMA, | ||
GENERATED_BASE_EVENT_SCHEMA, | ||
OPTIONAL_GENERATED_BASE_EVENT_SCHEMA, | ||
} from '../events/baseEventSchemas' | ||
import type { CommonEventDefinition } from '../events/eventTypes' | ||
|
||
// External message metadata that describe the context in which the message was created, primarily used for debugging purposes | ||
export const MESSAGE_METADATA_SCHEMA = z | ||
.object({ | ||
schemaVersion: z.string().min(1).describe('message schema version'), | ||
// this is always set to a service that created the message | ||
producedBy: z.string().min(1).describe('app/service that produced the message'), | ||
// this is always propagated within the message chain. For the first message in the chain it is equal to "producedBy" | ||
originatedFrom: z | ||
.string() | ||
.min(1) | ||
.describe('app/service that initiated entire workflow that led to creating this message'), | ||
// this is always propagated within the message chain. | ||
correlationId: z.string().describe('unique identifier passed to all events in workflow chain'), | ||
}) | ||
.describe('external message metadata') | ||
|
||
export const MESSAGE_SCHEMA_EXTENSION = { | ||
// For internal domain events that did not originate within a message chain metadata field can be omitted, producer should then assume it is initiating a new chain | ||
metadata: MESSAGE_METADATA_SCHEMA.optional(), | ||
} | ||
|
||
export const BASE_MESSAGE_SCHEMA = CONSUMER_BASE_EVENT_SCHEMA.extend(MESSAGE_SCHEMA_EXTENSION) | ||
|
||
export type BaseMessageType = z.infer<typeof BASE_MESSAGE_SCHEMA> | ||
|
||
export type MessageMetadataType = z.infer<typeof MESSAGE_METADATA_SCHEMA> | ||
|
||
export type CommonMessageDefinitionSchemaType<T extends CommonEventDefinition> = z.infer< | ||
T['consumerSchema'] | ||
> | ||
|
||
type ReturnType<T extends ZodObject<Y>, Y extends ZodRawShape, Z extends string> = { | ||
consumerSchema: ZodObject<{ | ||
id: ZodString | ||
timestamp: ZodString | ||
type: ZodLiteral<Z> | ||
payload: T | ||
metadata: ZodOptional< | ||
ZodObject<{ | ||
schemaVersion: ZodString | ||
producedBy: ZodString | ||
originatedFrom: ZodString | ||
correlationId: ZodString | ||
}> | ||
> | ||
}> | ||
|
||
publisherSchema: ZodObject<{ | ||
id: ZodOptional<ZodString> | ||
timestamp: ZodOptional<ZodString> | ||
type: ZodLiteral<Z> | ||
payload: T | ||
metadata: ZodOptional< | ||
ZodObject<{ | ||
schemaVersion: ZodString | ||
producedBy: ZodString | ||
originatedFrom: ZodString | ||
correlationId: ZodString | ||
}> | ||
> | ||
}> | ||
} | ||
|
||
export function enrichMessageSchemaWithBase< | ||
T extends ZodObject<Y>, | ||
Y extends ZodRawShape, | ||
Z extends string, | ||
>(type: Z, payloadSchema: T): ReturnType<T, Y, Z> { | ||
const baseSchema = z.object({ | ||
type: z.literal(type), | ||
payload: payloadSchema, | ||
}) | ||
|
||
const consumerSchema = | ||
GENERATED_BASE_EVENT_SCHEMA.merge(baseSchema).extend(MESSAGE_SCHEMA_EXTENSION) | ||
const publisherSchema = | ||
OPTIONAL_GENERATED_BASE_EVENT_SCHEMA.merge(baseSchema).extend(MESSAGE_SCHEMA_EXTENSION) | ||
|
||
return { | ||
consumerSchema: consumerSchema, | ||
publisherSchema: publisherSchema, | ||
} | ||
} | ||
export { | ||
MESSAGE_METADATA_SCHEMA, | ||
MESSAGE_SCHEMA_EXTENSION, | ||
BASE_MESSAGE_SCHEMA, | ||
BaseMessageType, | ||
MessageMetadataType, | ||
CommonMessageDefinitionSchemaType, | ||
enrichMessageSchemaWithBase, | ||
} from '@message-queue-toolkit/schemas' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1 @@ | ||
export const toDatePreprocessor = (value: unknown) => { | ||
switch (typeof value) { | ||
case 'string': | ||
case 'number': | ||
return new Date(value) | ||
|
||
default: | ||
return value // could not coerce, return the original and face the consequences during validation | ||
} | ||
} | ||
export { toDatePreprocessor } from '@message-queue-toolkit/schemas' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
node_modules/ | ||
coverage/ | ||
dist/ | ||
vitest.config.mts |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
{ | ||
"parser": "@typescript-eslint/parser", | ||
"parserOptions": { | ||
"ecmaVersion": 2022, | ||
"sourceType": "module", | ||
"project": "./tsconfig.json" | ||
}, | ||
"ignorePatterns": ["node_modules"], | ||
"plugins": ["@typescript-eslint", "vitest", "import"], | ||
"extends": [ | ||
"eslint:recommended", | ||
"plugin:@typescript-eslint/recommended", | ||
"plugin:@typescript-eslint/recommended-requiring-type-checking", | ||
"plugin:import/recommended", | ||
"plugin:import/typescript", | ||
"plugin:vitest/recommended" | ||
], | ||
"rules": { | ||
"@typescript-eslint/no-empty-interface": "warn", | ||
"@typescript-eslint/ban-ts-comment": "off", | ||
"@typescript-eslint/no-use-before-define": "off", | ||
"@typescript-eslint/no-non-null-assertion": "warn", | ||
"@typescript-eslint/no-var-requires": "off", | ||
"@typescript-eslint/indent": "off", | ||
"@typescript-eslint/restrict-template-expressions": "off", | ||
"@typescript-eslint/no-explicit-any": "warn", | ||
"@typescript-eslint/no-unsafe-member-access": "off", | ||
"@typescript-eslint/no-unsafe-assignment": "off", | ||
"@typescript-eslint/explicit-function-return-type": "off", | ||
"@typescript-eslint/consistent-type-imports": "warn", | ||
"@typescript-eslint/no-unused-vars": [ | ||
"warn", | ||
{ | ||
"argsIgnorePattern": "^_", | ||
"varsIgnorePattern": "^_", | ||
"caughtErrorsIgnorePattern": "^_" | ||
} | ||
], | ||
"@typescript-eslint/member-delimiter-style": [ | ||
"error", | ||
{ | ||
"multiline": { | ||
"delimiter": "none", | ||
"requireLast": false | ||
}, | ||
"singleline": { | ||
"delimiter": "comma", | ||
"requireLast": false | ||
} | ||
} | ||
], | ||
"import/no-default-export": "error", | ||
"import/order": [ | ||
"warn", | ||
{ | ||
"alphabetize": { "order": "asc" }, | ||
"newlines-between": "always" | ||
} | ||
], | ||
"max-lines": ["error", { "max": 600 }], | ||
"max-params": ["error", { "max": 4 }], | ||
"max-statements": ["error", { "max": 15 }], | ||
"complexity": ["error", { "max": 20 }] | ||
}, | ||
"overrides": [ | ||
{ | ||
"files": ["test/**/*.ts", "*.test.ts", "*.spec.ts"], | ||
"rules": { | ||
"@typescript-eslint/require-await": "off", | ||
"@typescript-eslint/no-empty-function": "off", | ||
"@typescript-eslint/no-non-null-assertion": "off", | ||
"@typescript-eslint/no-unsafe-return": "off", | ||
"@typescript-eslint/no-unsafe-assignment": "off", | ||
"@typescript-eslint/no-unsafe-member-access": "off", | ||
"@typescript-eslint/no-unsafe-call": "off", | ||
"max-statements": ["off"] | ||
} | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"printWidth": 100, | ||
"singleQuote": true, | ||
"bracketSpacing": true, | ||
"semi": false, | ||
"arrowParens": "always", | ||
"endOfLine": "lf", | ||
"trailingComma": "all" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export { toDatePreprocessor } from './lib/utils/toDateProcessor' | ||
|
||
export * from './lib/events/eventTypes' | ||
export * from './lib/events/baseEventSchemas' | ||
export * from './lib/messages/baseMessageSchemas' |
Oops, something went wrong.