@r-delfino/mux-sync-engine
Version:
Mux Sync Engine to sync Mux data based on webhooks to Postgres
127 lines (120 loc) • 4.13 kB
TypeScript
import { Mux } from '@mux/mux-node';
import pg, { QueryResult } from 'pg';
import pino from 'pino';
import { HeadersLike } from '@mux/mux-node/core';
interface EntitySchema {
readonly properties: string[];
}
type PostgresConfig = {
databaseUrl: string;
schema: string;
maxConnections?: number;
};
declare class PostgresClient {
private config;
pool: pg.Pool;
constructor(config: PostgresConfig);
delete(table: string, id: string): Promise<boolean>;
query(text: string, params?: string[]): Promise<QueryResult>;
upsertMany<T extends {
[Key: string]: any;
}>(entries: T[], table: string, tableSchema: EntitySchema, options?: {
conflict?: string;
}): Promise<T[]>;
findMissingEntries(table: string, ids: string[]): Promise<string[]>;
/**
* Returns an (yesql formatted) upsert function based on the key/vals of an object.
* eg,
* insert into customers ("id", "name")
* values (:id, :name)
* on conflict (id)
* do update set (
* "id" = :id,
* "name" = :name
* )
*/
private constructUpsertSql;
/**
* For array object field like invoice.custom_fields
* ex: [{"name":"Project name","value":"Test Project"}]
*
* we need to stringify it first cos passing array object directly will end up with
* {
* invalid input syntax for type json
* detail: 'Expected ":", but found "}".',
* where: 'JSON data, line 1: ...\\":\\"Project name\\",\\"value\\":\\"Test Project\\"}"}',
* }
*/
private cleanseArrayField;
}
type MuxSyncConfig = {
/** Postgres database URL including authentication */
databaseUrl: string;
/** Database schema name. */
schema?: string;
/** Mux token ID used to authenticate requests to the Mux API. */
muxTokenId: string;
/** Mux token secret used to authenticate requests to the Mux API. */
muxTokenSecret: string;
/** Mux webhook secret used to verify the signature of webhook events. */
muxWebhookSecret: string;
/**
* If true, the sync engine will backfill related entities, i.e. when a invoice webhook comes in, it ensures that the customer is present and synced.
* This ensures foreign key integrity, but comes at the cost of additional queries to the database (and added latency for Stripe calls if the entity is actually missing).
*/
backfillRelatedEntities?: boolean;
/**
* If true, the webhook data is not used and instead the webhook is just a trigger to fetch the entity from Stripe again. This ensures that a race condition with failed webhooks can never accidentally overwrite the data with an older state.
*
* Default: false
*/
revalidateEntityViaMuxApi?: boolean;
maxPostgresConnections?: number;
logger?: pino.Logger;
};
type SyncObject = 'all' | 'mux_assets' | 'mux_live_streams';
interface Sync {
synced: number;
}
interface SyncBackfill {
muxAssets?: Sync;
muxLiveStreams?: Sync;
}
interface SyncBackfillParams {
created?: {
/**
* Minimum value to filter by (exclusive)
*/
gt?: number;
/**
* Minimum value to filter by (inclusive)
*/
gte?: number;
/**
* Maximum value to filter by (exclusive)
*/
lt?: number;
/**
* Maximum value to filter by (inclusive)
*/
lte?: number;
};
object?: SyncObject;
backfillRelatedEntities?: boolean;
}
declare class MuxSync {
private config;
mux: Mux;
postgresClient: PostgresClient;
constructor(config: MuxSyncConfig);
processWebhook(payload: string, headers: HeadersLike): Promise<void>;
private fetchOrUseWebhookData;
syncBackfill(params?: SyncBackfillParams): Promise<SyncBackfill>;
private upsertAssets;
}
type MigrationConfig = {
databaseUrl: string;
logger?: pino.Logger;
};
declare function runMigrations(config: MigrationConfig): Promise<void>;
export { MuxSync, type MuxSyncConfig, type Sync, type SyncBackfill, type SyncBackfillParams, type SyncObject, runMigrations };