Skip to content

Events and Webhooks utilities and documentation

License

Notifications You must be signed in to change notification settings

MyUnisoft/events

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Mar 13, 2025
68f5bfa Β· Mar 13, 2025
Jan 3, 2025
Dec 10, 2024
Aug 8, 2024
Mar 13, 2025
Mar 13, 2025
Nov 23, 2023
Apr 6, 2022
Jun 5, 2023
Apr 6, 2022
Sep 5, 2022
Aug 8, 2024
Aug 8, 2024
Jan 15, 2025
Jun 26, 2023
Aug 8, 2024
Jun 22, 2023
Mar 13, 2025
Jun 5, 2023
Aug 14, 2024

Repository files navigation

Events

MyUnisoft Events validator, schemas and types (useful to work with Webhooks).

npm version license size

🚧 Requirements

  • Node.js version 18 or higher
  • Docker (for running tests).

πŸš€ Getting Started

This package is available in the Node Package Repository and can be easily installed with npm or yarn

$ npm i @myunisoft/events
# or
$ yarn add @myunisoft/events
Configure environment variables
variable description default
MYUNISOFT_EVENTS_LOGGER_MODE Set log level for the default logger info
Dispatcher
MYUNISOFT_DISPATCHER_IDLE_TIME Interval threshold when Dispatcher become idle 600_000
MYUNISOFT_DISPATCHER_CHECK_LAST_ACTIVITY_INTERVAL Dispatcher checking last activity interval 120_000
MYUNISOFT_DISPATCHER_BACKUP_TRANSACTION_STORE_NAME Default name for backup transaction store backup
MYUNISOFT_DISPATCHER_INIT_TIMEOUT Dispatcher initialisation timeout 3_500
MYUNISOFT_DISPATCHER_PING_INTERVAL Dispatcher ping interval 3_500
Incomer
MYUNISOFT_INCOMER_INIT_TIMEOUT Incomer initialisation timeout 3_500
MYUNISOFT_EVENTS_INIT_EXTERNAL Whenever Incomer should initialize an external Dispatcher false
MYUNISOFT_INCOMER_MAX_PING_INTERVAL Maximum ping interval 60_000
MYUNISOFT_INCOMER_PUBLISH_INTERVAL Publish interval 60_000
MYUNISOFT_INCOMER_IS_DISPATCHER Weither Incomer is a Dispatcher false

Some options takes the lead over environment variables. For instance with: new Incomer({ dispatcherInactivityOptions: { maxPingInterval: 900_000 }}) the max ping interval will be 900_000 even if MYUNISOFT_INCOMER_MAX_PING_INTERVAL variable is set.

πŸ“š Usage example

import * as Events, { type EventOptions } from "@myunisoft/events";

const event: EventOptions<"connector"> = {
  name: "connector",
  operation: "CREATE",
  scope: {
    schemaId: 1
  },
  metadata: {
    agent: "Node",
    origin: {
      endpoint: "http://localhost:12080/api/v1/my-custom-feature",
      method: "POST",
      requestId: crypto.randomUUID();
    },
    createdAt: Date.now()
  },
  data: {
    id: "1",
    code: "JFAC"
  }
};

Events.validate(event);

You can also use additional APIs to validate and narrow the data type depending on the operation:

if (Events.isCreateOperation(event.operation)) {
  // Do some code
}
else if (Events.isUpdateOperation(event.operation)) {
  // Do some code
}
else if (Events.isDeleteOperation(event.operation)) {
  // Do some code
}

Note

πŸ‘€ See here for the exhaustive list of Events.

πŸ’‘ What is an Event?

A fully constituted event is composed of a name, an operation, and multiple objects such as data, scope and metadata.

  • The name identifies the event.
  • The operation defines if it is a creation, update, or deletion.
  • Based on the name, we know the data and the different metadata.origin.method related to it.
  • The metadata object is used to determine various pieces of information, such as the entry point.
  • The scope defines the who.
export interface Scope {
  schemaId: number;
  firmId?: number | null;
  firmSIRET?: number | null;
  accountingFolderId?: number | null;
  accountingFolderSIRET?: number | null;
  accountingFolderRef?: string | null;
  persPhysiqueId?: number | null;
}

export interface Metadata {
  agent: string;
  origin?: {
    endpoint: string;
    method: "GET" | "POST" | "PATCH" | "PUT" | "DELETE" | "HEAD" | "OPTIONS" | (string & {});
    requestId?: string;
  };
  createdAt: number;
}

API

validate< T extends keyof Events >(options: EventOptions): void

Throw an error if a given event is not recognized internally.

isCreateOperation< T extends keyof Events >(operation: EventOptions["operation"]): operation is Operation["create"]

isUpdateOperation< T extends keyof Events >(operation: EventOptions["operation"]): operation is Operation["update"]

isDeleteOperation< T extends keyof Events >(operation: EventOptions["operation"]): operation is Operation["delete"]


EventOptions is described by the following type:

export type EventOptions<K extends keyof EventsDefinition.Events> = {
  scope: Scope;
  metadata: Metadata;
} & EventsDefinition.Events[K];

Exploiting Webhooks

πŸ‘€ See the root example/fastify for an example of utilizing webhooks with an HTTP server.

In TypeScript, webhooks can be described using the WebhookResponse type:

import type { WebhookResponse } from "@myunisoft/events";

const response: WebhooksResponse<["connector", "accountingFolder"]> = [
  {
    name: "connector",
    operation: "CREATE",
    scope: {
      schemaId: 1
    },
    data: {
      id: 1,
      code: "JFAC"
    },
    webhookId: "1",
    createdAt: Date.now()
  },
  {
    name: "accountingFolder",
    operation: "CREATE",
    scope: {
      schemaId: 1
    },
    data: {
      id: 1
    },
    webhookId: "2",
    createdAt: Date.now()
  },
];
Webhook JSON Schema
{
  "description": "Webhook",
  "type": "array",
  "items": {
    "type": "object",
    "properties": {
      "scope": {
        "$ref": "Scope"
      },
      "webhookId": {
        "type": "string"
      },
      "createdAt": {
        "type": "number"
      },
      "name": {
        "type": "string",
        "description": "event related name"
      },
      "operation": {
        "type": "string",
        "description": "event related operation",
        "enum": ["CREATE", "UPDATE", "DELETE", "VOID"]
      },
      "data": {
        "type": "object",
        "description": "event related data",
        "properties": {
          "id": {
            "type": "string"
          },
          "required": ["id"],
          "additionalProperties": true
        }
      }
    },
    "required": ["scope", "webhookId", "createdAt", "name", "operation", "data"],
    "additionalProperties": false
  }
}

Contributors ✨

All Contributors

Thanks goes to these wonderful people (emoji key):

Nicolas Hallaert
Nicolas Hallaert

πŸ’» πŸ“– ⚠️
Yefis
Yefis

πŸ’» πŸ“–
Gentilhomme
Gentilhomme

πŸ“–
PierreDemailly
PierreDemailly

πŸ’» πŸ“–

License

MIT