@moneypot/hub 1.20.0-dev.1 → 1.20.0-dev.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/db/util.d.ts +2 -1
- package/dist/src/liability/internal/db.d.ts +5 -0
- package/dist/src/liability/internal/db.js +14 -0
- package/dist/src/liability/internal/index.d.ts +2 -2
- package/dist/src/liability/internal/index.js +2 -2
- package/dist/src/liability/internal/processor.d.ts +7 -2
- package/dist/src/liability/internal/processor.js +70 -59
- package/dist/src/liability/public/config.d.ts +9 -4
- package/dist/src/process-transfers/polling-processor.js +3 -4
- package/dist/src/process-transfers/websocket-processor.js +2 -1
- package/dist/src/server/index.js +2 -2
- package/dist/src/test/index.d.ts +17 -10
- package/dist/src/test/index.js +18 -5
- package/dist/src/util.d.ts +4 -0
- package/dist/src/util.js +15 -0
- package/package.json +1 -1
package/dist/src/db/util.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { QueryResult, QueryResultRow } from "pg";
|
|
2
2
|
import { PgClientResult } from "postgraphile/@dataplan/pg";
|
|
3
3
|
import * as pg from "pg";
|
|
4
|
-
|
|
4
|
+
import { Prettify } from "../util.js";
|
|
5
|
+
export type QueryExecutor = Prettify<Pick<pg.Pool, "query"> | Pick<pg.ClientBase, "query">>;
|
|
5
6
|
type ResultType<T> = PgClientResult<T> | QueryResult<T extends QueryResultRow ? T : never>;
|
|
6
7
|
export declare function maybeOneRow<T>(result: ResultType<T>): T | undefined;
|
|
7
8
|
export declare function exactlyOneRow<T>(result: ResultType<T>): T;
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { PgClientInTransaction } from "../../db/transaction.js";
|
|
2
|
+
import { QueryExecutor } from "../../db/util.js";
|
|
2
3
|
import { DbLiability } from "../types.js";
|
|
3
4
|
export declare function dbLockLiability(pool: PgClientInTransaction, { id }: {
|
|
4
5
|
id: string;
|
|
5
6
|
}): Promise<DbLiability | null>;
|
|
7
|
+
export declare function dbGetLiabilityByRef(pgClient: QueryExecutor, { refType, refId }: {
|
|
8
|
+
refType: string;
|
|
9
|
+
refId: string;
|
|
10
|
+
}): Promise<DbLiability | null>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { maybeOneRow } from "../../db/util.js";
|
|
1
2
|
export async function dbLockLiability(pool, { id }) {
|
|
2
3
|
const result = await pool.query({
|
|
3
4
|
text: `
|
|
@@ -10,3 +11,16 @@ export async function dbLockLiability(pool, { id }) {
|
|
|
10
11
|
});
|
|
11
12
|
return result.rows[0] ?? null;
|
|
12
13
|
}
|
|
14
|
+
export async function dbGetLiabilityByRef(pgClient, { refType, refId }) {
|
|
15
|
+
return pgClient
|
|
16
|
+
.query({
|
|
17
|
+
text: `
|
|
18
|
+
SELECT *
|
|
19
|
+
FROM hub.liability
|
|
20
|
+
WHERE ref_type = $1 AND ref_id = $2
|
|
21
|
+
`,
|
|
22
|
+
values: [refType, refId],
|
|
23
|
+
})
|
|
24
|
+
.then(maybeOneRow)
|
|
25
|
+
.then((row) => row ?? null);
|
|
26
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { dbLockLiability } from "./db.js";
|
|
2
|
-
export {
|
|
1
|
+
export { dbLockLiability, dbGetLiabilityByRef } from "./db.js";
|
|
2
|
+
export { startLiabilityDeadlineProcessor, processDeadlinedLiabilities, processOneDeadlinedLiability, } from "./processor.js";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { dbLockLiability } from "./db.js";
|
|
2
|
-
export {
|
|
1
|
+
export { dbLockLiability, dbGetLiabilityByRef } from "./db.js";
|
|
2
|
+
export { startLiabilityDeadlineProcessor, processDeadlinedLiabilities, processOneDeadlinedLiability, } from "./processor.js";
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import * as pg from "pg";
|
|
2
|
+
import { type PgClientInTransaction } from "../../db/transaction.js";
|
|
2
3
|
import { LiabilityConfigInternal } from "../public/config.js";
|
|
3
4
|
import { QueryExecutor } from "../../db/util.js";
|
|
4
5
|
export declare function dbGetLiabilityIdsPastDeadline(pgClient: QueryExecutor, { limit }?: {
|
|
5
6
|
limit?: number;
|
|
6
7
|
}): Promise<string[]>;
|
|
7
|
-
export declare function
|
|
8
|
+
export declare function startLiabilityDeadlineProcessor<RefType extends string>({ signal, pool, liabilityConfig, }: {
|
|
8
9
|
signal: AbortSignal;
|
|
9
10
|
pool: pg.Pool;
|
|
10
11
|
liabilityConfig: LiabilityConfigInternal<RefType>;
|
|
11
12
|
}): void;
|
|
12
|
-
export declare function
|
|
13
|
+
export declare function processOneDeadlinedLiability<RefType extends string>(pgClient: PgClientInTransaction, { liabilityConfig, liabilityId, }: {
|
|
14
|
+
liabilityConfig: LiabilityConfigInternal<RefType>;
|
|
15
|
+
liabilityId: string;
|
|
16
|
+
}): Promise<void>;
|
|
17
|
+
export declare function processDeadlinedLiabilities<RefType extends string>({ pool, liabilityConfig, signal, }: {
|
|
13
18
|
pool: pg.Pool;
|
|
14
19
|
liabilityConfig: LiabilityConfigInternal<RefType>;
|
|
15
20
|
signal: AbortSignal;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { logger } from "../../logger.js";
|
|
2
2
|
import { withPgPoolTransaction, } from "../../db/transaction.js";
|
|
3
3
|
import { dbLockHouseBankroll, dbLockPlayerBalance } from "../../db/public.js";
|
|
4
|
+
import { exactlyOneRow, maybeOneRow } from "../../db/util.js";
|
|
5
|
+
import { abortableTimeout } from "../../util.js";
|
|
4
6
|
const BATCH_SIZE = 100;
|
|
5
7
|
export async function dbGetLiabilityIdsPastDeadline(pgClient, { limit = 100 } = {}) {
|
|
6
8
|
const result = await pgClient.query({
|
|
@@ -17,7 +19,8 @@ export async function dbGetLiabilityIdsPastDeadline(pgClient, { limit = 100 } =
|
|
|
17
19
|
return result.rows.map((r) => r.id);
|
|
18
20
|
}
|
|
19
21
|
async function dbTryLockLiabilityById(pgClient, id) {
|
|
20
|
-
|
|
22
|
+
return pgClient
|
|
23
|
+
.query({
|
|
21
24
|
text: `
|
|
22
25
|
SELECT *
|
|
23
26
|
FROM hub.liability
|
|
@@ -26,15 +29,16 @@ async function dbTryLockLiabilityById(pgClient, id) {
|
|
|
26
29
|
FOR UPDATE SKIP LOCKED
|
|
27
30
|
`,
|
|
28
31
|
values: [id],
|
|
29
|
-
})
|
|
30
|
-
|
|
32
|
+
})
|
|
33
|
+
.then(maybeOneRow)
|
|
34
|
+
.then((row) => row ?? null);
|
|
31
35
|
}
|
|
32
|
-
export function
|
|
36
|
+
export function startLiabilityDeadlineProcessor({ signal, pool, liabilityConfig, }) {
|
|
33
37
|
if (signal.aborted) {
|
|
34
|
-
logger.info(`[
|
|
38
|
+
logger.info(`[startLiabilityDeadlineProcessor] AbortSignal aborted. Not starting processor.`);
|
|
35
39
|
return;
|
|
36
40
|
}
|
|
37
|
-
logger.info(`Starting liability
|
|
41
|
+
logger.info(`Starting liability deadline processor`);
|
|
38
42
|
(async () => {
|
|
39
43
|
let shouldStop = false;
|
|
40
44
|
signal.addEventListener("abort", () => {
|
|
@@ -42,19 +46,71 @@ export function startLiabilityExpirationProcessor({ signal, pool, liabilityConfi
|
|
|
42
46
|
});
|
|
43
47
|
while (!shouldStop && !signal.aborted) {
|
|
44
48
|
try {
|
|
45
|
-
await
|
|
49
|
+
await processDeadlinedLiabilities({ pool, liabilityConfig, signal });
|
|
46
50
|
}
|
|
47
51
|
catch (e) {
|
|
48
|
-
logger.error(e, `Error processing
|
|
52
|
+
logger.error(e, `Error processing deadlined liabilities`);
|
|
49
53
|
}
|
|
50
54
|
if (!shouldStop && !signal.aborted) {
|
|
51
|
-
await
|
|
55
|
+
await abortableTimeout(liabilityConfig._options.pollIntervalMs, signal);
|
|
52
56
|
}
|
|
53
57
|
}
|
|
54
|
-
logger.info(`Liability
|
|
58
|
+
logger.info(`Liability deadline processor stopped`);
|
|
55
59
|
})();
|
|
56
60
|
}
|
|
57
|
-
export async function
|
|
61
|
+
export async function processOneDeadlinedLiability(pgClient, { liabilityConfig, liabilityId, }) {
|
|
62
|
+
const dbLockedLiability = await dbTryLockLiabilityById(pgClient, liabilityId);
|
|
63
|
+
if (!dbLockedLiability) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (dbLockedLiability.deadline > new Date()) {
|
|
67
|
+
throw new Error(`Cannot process liability ${dbLockedLiability.id}: deadline has not passed (deadline: ${dbLockedLiability.deadline.toISOString()})`);
|
|
68
|
+
}
|
|
69
|
+
const refTypeConfig = liabilityConfig._refTypes[dbLockedLiability.ref_type];
|
|
70
|
+
if (!refTypeConfig?.handleDeadline) {
|
|
71
|
+
logger.error(`No handler configured for liability ref_type "${dbLockedLiability.ref_type}" (id: ${dbLockedLiability.id}). Skipping.`);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const dbLockedPlayerBalance = await dbLockPlayerBalance(pgClient, {
|
|
75
|
+
userId: dbLockedLiability.user_id,
|
|
76
|
+
casinoId: dbLockedLiability.casino_id,
|
|
77
|
+
experienceId: dbLockedLiability.experience_id,
|
|
78
|
+
currencyKey: dbLockedLiability.currency_key,
|
|
79
|
+
});
|
|
80
|
+
if (!dbLockedPlayerBalance) {
|
|
81
|
+
logger.error(`No player balance found for liability ${dbLockedLiability.id}. Skipping.`);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const dbLockedHouseBankroll = await dbLockHouseBankroll(pgClient, {
|
|
85
|
+
casinoId: dbLockedLiability.casino_id,
|
|
86
|
+
currencyKey: dbLockedLiability.currency_key,
|
|
87
|
+
});
|
|
88
|
+
if (!dbLockedHouseBankroll) {
|
|
89
|
+
logger.error(`No house bankroll found for liability ${dbLockedLiability.id}.`);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
await refTypeConfig.handleDeadline({
|
|
94
|
+
pgClientInTransaction: pgClient,
|
|
95
|
+
lockedLiability: dbLockedLiability,
|
|
96
|
+
lockedPlayerBalance: dbLockedPlayerBalance,
|
|
97
|
+
lockedHouseBankroll: dbLockedHouseBankroll,
|
|
98
|
+
});
|
|
99
|
+
const { resolved_at } = await pgClient
|
|
100
|
+
.query(`SELECT resolved_at FROM hub.liability WHERE id = $1`, [dbLockedLiability.id])
|
|
101
|
+
.then(exactlyOneRow);
|
|
102
|
+
if (!resolved_at) {
|
|
103
|
+
throw new Error(`Handler returned LIABILITY_RESOLVED but did not resolve liability ${dbLockedLiability.id} (ref_type: ${dbLockedLiability.ref_type}, ref_id: ${dbLockedLiability.ref_id}). ` +
|
|
104
|
+
`Handler must call dbResolveLiabilityByRef before returning.`);
|
|
105
|
+
}
|
|
106
|
+
logger.debug(`Resolved liability ${dbLockedLiability.id}`);
|
|
107
|
+
}
|
|
108
|
+
catch (e) {
|
|
109
|
+
logger.error(e, `Error handling deadline for liability ${dbLockedLiability.id}`);
|
|
110
|
+
throw e;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
export async function processDeadlinedLiabilities({ pool, liabilityConfig, signal, }) {
|
|
58
114
|
const liabilityIds = await dbGetLiabilityIdsPastDeadline(pool, {
|
|
59
115
|
limit: BATCH_SIZE,
|
|
60
116
|
});
|
|
@@ -68,55 +124,10 @@ export async function processExpiredLiabilities({ pool, liabilityConfig, signal,
|
|
|
68
124
|
break;
|
|
69
125
|
}
|
|
70
126
|
await withPgPoolTransaction(pool, async (pgClient) => {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
const refTypeConfig = liabilityConfig._refTypes[dbLockedLiability.ref_type];
|
|
76
|
-
if (!refTypeConfig?.handleDeadline) {
|
|
77
|
-
logger.error(`No handler configured for liability ref_type "${dbLockedLiability.ref_type}" (id: ${dbLockedLiability.id}). Skipping.`);
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
const dbLockedPlayerBalance = await dbLockPlayerBalance(pgClient, {
|
|
81
|
-
userId: dbLockedLiability.user_id,
|
|
82
|
-
casinoId: dbLockedLiability.casino_id,
|
|
83
|
-
experienceId: dbLockedLiability.experience_id,
|
|
84
|
-
currencyKey: dbLockedLiability.currency_key,
|
|
127
|
+
await processOneDeadlinedLiability(pgClient, {
|
|
128
|
+
liabilityConfig,
|
|
129
|
+
liabilityId,
|
|
85
130
|
});
|
|
86
|
-
if (!dbLockedPlayerBalance) {
|
|
87
|
-
logger.error(`No player balance found for liability ${dbLockedLiability.id}. Skipping.`);
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
const dbLockedHouseBankroll = await dbLockHouseBankroll(pgClient, {
|
|
91
|
-
casinoId: dbLockedLiability.casino_id,
|
|
92
|
-
currencyKey: dbLockedLiability.currency_key,
|
|
93
|
-
});
|
|
94
|
-
if (!dbLockedHouseBankroll) {
|
|
95
|
-
logger.error(`No house bankroll found for liability ${dbLockedLiability.id}.`);
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
try {
|
|
99
|
-
await refTypeConfig.handleDeadline({
|
|
100
|
-
pgClientInTransaction: pgClient,
|
|
101
|
-
lockedLiability: dbLockedLiability,
|
|
102
|
-
lockedPlayerBalance: dbLockedPlayerBalance,
|
|
103
|
-
lockedHouseBankroll: dbLockedHouseBankroll,
|
|
104
|
-
});
|
|
105
|
-
const { resolved_at } = await pgClient
|
|
106
|
-
.query(`SELECT resolved_at FROM hub.liability WHERE id = $1`, [
|
|
107
|
-
dbLockedLiability.id,
|
|
108
|
-
])
|
|
109
|
-
.then((r) => r.rows[0]);
|
|
110
|
-
if (!resolved_at) {
|
|
111
|
-
throw new Error(`Handler returned LIABILITY_RESOLVED but did not resolve liability ${dbLockedLiability.id} (ref_type: ${dbLockedLiability.ref_type}, ref_id: ${dbLockedLiability.ref_id}). ` +
|
|
112
|
-
`Handler must call dbResolveLiabilityByRef before returning.`);
|
|
113
|
-
}
|
|
114
|
-
logger.debug(`Resolved liability ${dbLockedLiability.id}`);
|
|
115
|
-
}
|
|
116
|
-
catch (e) {
|
|
117
|
-
logger.error(e, `Error handling expiration for liability ${dbLockedLiability.id}`);
|
|
118
|
-
throw e;
|
|
119
|
-
}
|
|
120
131
|
});
|
|
121
132
|
}
|
|
122
133
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { DbLockedHouseBankrollWithAvailable, DbLockedPlayerBalance, PgClientInTransaction } from "../../db/index.js";
|
|
2
2
|
import { Branded } from "../../util.js";
|
|
3
|
-
import type { DbLiability } from "../types.js";
|
|
4
|
-
import { dbCreateLiability, dbResolveLiabilityByRef } from "./db.js";
|
|
3
|
+
import type { DbLiability, NewLiability } from "../types.js";
|
|
5
4
|
export type DbLockedLiability<RefType extends string = string> = DbLiability & {
|
|
6
5
|
ref_type: RefType;
|
|
7
6
|
};
|
|
@@ -23,8 +22,14 @@ export declare function createLiabilityConfig<RefType extends string>(refTypes:
|
|
|
23
22
|
[K in RefType]: LiabilityRefTypeConfig<K>;
|
|
24
23
|
}, options?: Partial<LiabilityConfigOptions>): LiabilityConfig<RefType>;
|
|
25
24
|
export type LiabilityConfig<RefType extends string> = {
|
|
26
|
-
dbCreateLiability:
|
|
27
|
-
|
|
25
|
+
dbCreateLiability: <T extends RefType>(pgClient: PgClientInTransaction, args: NewLiability<T>) => Promise<DbLiability & {
|
|
26
|
+
ref_type: T;
|
|
27
|
+
}>;
|
|
28
|
+
dbResolveLiabilityByRef: <T extends RefType>(pgClient: PgClientInTransaction, args: {
|
|
29
|
+
refType: T;
|
|
30
|
+
refId: string;
|
|
31
|
+
lockedBankrollId: string;
|
|
32
|
+
}) => Promise<boolean>;
|
|
28
33
|
};
|
|
29
34
|
export type LiabilityConfigOptions = {
|
|
30
35
|
pollIntervalMs: number;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as db from "../db/index.js";
|
|
2
2
|
import { logger } from "../logger.js";
|
|
3
3
|
import { assert } from "tsafe";
|
|
4
|
-
import { isUuid } from "../util.js";
|
|
4
|
+
import { isUuid, abortableTimeout } from "../util.js";
|
|
5
5
|
import { createGraphqlClient } from "../graphql-client.js";
|
|
6
6
|
import { GET_CURRENCIES } from "../graphql-queries.js";
|
|
7
7
|
import { processTakeRequests } from "../take-request/process-take-request.js";
|
|
@@ -75,7 +75,7 @@ export function startPollingProcessor({ casinoId, signal, pool, }) {
|
|
|
75
75
|
const now = Date.now();
|
|
76
76
|
const timeToWait = Math.max(0, processorState.backoffTime - (now - processorState.lastAttempt));
|
|
77
77
|
if (timeToWait > 0) {
|
|
78
|
-
await
|
|
78
|
+
await abortableTimeout(timeToWait, signal);
|
|
79
79
|
}
|
|
80
80
|
processorState.lastAttempt = Date.now();
|
|
81
81
|
const casino = await dbGetCasinoById(pool, casinoId);
|
|
@@ -227,7 +227,6 @@ async function processWithdrawals({ abortSignal, casinoId, graphqlClient, pool,
|
|
|
227
227
|
}
|
|
228
228
|
async function processTransfersUntilEmpty({ afterCursor, graphqlClient, casinoInfo, signal, pool, }) {
|
|
229
229
|
let hasNextPage = true;
|
|
230
|
-
const timeout = (ms) => new Promise((res) => setTimeout(res, ms));
|
|
231
230
|
while (hasNextPage && !signal.aborted) {
|
|
232
231
|
if (signal.aborted) {
|
|
233
232
|
logger.info(`[processTransfersUntilEmpty] Aborted by graceful shutdown.`);
|
|
@@ -276,7 +275,7 @@ async function processTransfersUntilEmpty({ afterCursor, graphqlClient, casinoIn
|
|
|
276
275
|
logger.info(`[processTransfersUntilEmpty] Aborted by graceful shutdown.`);
|
|
277
276
|
break;
|
|
278
277
|
}
|
|
279
|
-
await
|
|
278
|
+
await abortableTimeout(1000, signal);
|
|
280
279
|
}
|
|
281
280
|
return afterCursor;
|
|
282
281
|
}
|
|
@@ -7,6 +7,7 @@ import { TRANSFER_FIELDS } from "./graphql.js";
|
|
|
7
7
|
import { logger } from "../logger.js";
|
|
8
8
|
import { processTransfer } from "./process-transfer.js";
|
|
9
9
|
import assert from "assert";
|
|
10
|
+
import { abortableTimeout } from "../util.js";
|
|
10
11
|
import { mpGetTakeRequest, processSingleTakeRequest, } from "../take-request/process-take-request.js";
|
|
11
12
|
function httpToWs(url) {
|
|
12
13
|
if (url.protocol === "http:") {
|
|
@@ -64,7 +65,7 @@ export function startWebsocketProcessor({ casinoId, graphqlUrl, signal, controll
|
|
|
64
65
|
retryWait: async (retries) => {
|
|
65
66
|
const baseDelay = Math.min(1000 * Math.pow(2, retries), 30_000);
|
|
66
67
|
const jitter = Math.floor(Math.random() * 10_000);
|
|
67
|
-
await
|
|
68
|
+
await abortableTimeout(baseDelay + jitter, signal);
|
|
68
69
|
},
|
|
69
70
|
});
|
|
70
71
|
client.on("connected", () => {
|
package/dist/src/server/index.js
CHANGED
|
@@ -7,7 +7,7 @@ const postgraphile = typeof postgraphileImport === "function"
|
|
|
7
7
|
import { createPreset, defaultPlugins } from "./graphile.config.js";
|
|
8
8
|
import { createServerContext, closeServerContext, } from "../context.js";
|
|
9
9
|
import { initializeTransferProcessors } from "../process-transfers/index.js";
|
|
10
|
-
import {
|
|
10
|
+
import { startLiabilityDeadlineProcessor } from "../liability/internal/index.js";
|
|
11
11
|
import express from "express";
|
|
12
12
|
import { logger } from "../logger.js";
|
|
13
13
|
import cors from "./middleware/cors.js";
|
|
@@ -87,7 +87,7 @@ export function createHubServer({ configureApp, plugins, exportSchemaSDLPath, ex
|
|
|
87
87
|
pool: context.superuserPool,
|
|
88
88
|
});
|
|
89
89
|
if (liabilityConfig) {
|
|
90
|
-
|
|
90
|
+
startLiabilityDeadlineProcessor({
|
|
91
91
|
signal: abortController.signal,
|
|
92
92
|
pool: context.superuserPool,
|
|
93
93
|
liabilityConfig: liabilityConfig,
|
package/dist/src/test/index.d.ts
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as pg from "pg";
|
|
2
2
|
import { DbUser, DbPlayerBalance, DbHashChain, DbCasino, DbExperience, DbCurrency, DbHouseBankroll } from "../db/types.js";
|
|
3
3
|
import { GraphQLClient } from "graphql-request";
|
|
4
4
|
import { ServerOptions } from "../index.js";
|
|
5
|
+
import type { QueryExecutor } from "../db/util.js";
|
|
6
|
+
import { LiabilityConfigInternal } from "../liability/public/config.js";
|
|
7
|
+
export { type QueryExecutor };
|
|
5
8
|
export declare function uniqueTestId(): string;
|
|
6
9
|
export type HubTestServer = {
|
|
7
10
|
port: number;
|
|
8
11
|
stop: () => Promise<void>;
|
|
9
12
|
destroy: () => Promise<void>;
|
|
10
|
-
dbPool: Pool;
|
|
13
|
+
dbPool: pg.Pool;
|
|
11
14
|
playgroundCasinoId: string;
|
|
12
15
|
authenticate: (userId: DbUser["id"], experienceId: DbExperience["id"]) => Promise<{
|
|
13
16
|
sessionKey: string;
|
|
@@ -21,44 +24,48 @@ export type TestServerConfig = {
|
|
|
21
24
|
extraPgSchemas?: string[];
|
|
22
25
|
};
|
|
23
26
|
export declare function startTestServer({ plugins, userDatabaseMigrationsPath, testId, extraPgSchemas, }: TestServerConfig): Promise<HubTestServer>;
|
|
24
|
-
|
|
25
|
-
export declare function createUser(pgClient: PgClient, { casinoId, uname, }: {
|
|
27
|
+
export declare function createUser(pgClient: QueryExecutor, { casinoId, uname, }: {
|
|
26
28
|
casinoId: DbCasino["id"];
|
|
27
29
|
uname: string;
|
|
28
30
|
}): Promise<DbUser>;
|
|
29
|
-
export declare function createPlayerBalance(pgClient:
|
|
31
|
+
export declare function createPlayerBalance(pgClient: QueryExecutor, { userId, experienceId, currencyKey, amount, }: {
|
|
30
32
|
userId: DbUser["id"];
|
|
31
33
|
experienceId: DbExperience["id"];
|
|
32
34
|
currencyKey: DbCurrency["key"];
|
|
33
35
|
amount: number;
|
|
34
36
|
}): Promise<DbPlayerBalance>;
|
|
35
|
-
export declare function createHashChain(pgClient:
|
|
37
|
+
export declare function createHashChain(pgClient: QueryExecutor, { userId, experienceId, casinoId, maxIterations, }: {
|
|
36
38
|
userId: DbUser["id"];
|
|
37
39
|
experienceId: DbExperience["id"];
|
|
38
40
|
casinoId: DbCasino["id"];
|
|
39
41
|
maxIterations?: number;
|
|
40
42
|
}): Promise<DbHashChain>;
|
|
41
|
-
export declare function createExperience(pgClient:
|
|
43
|
+
export declare function createExperience(pgClient: QueryExecutor, options: {
|
|
42
44
|
casinoId: DbCasino["id"];
|
|
43
45
|
} | {
|
|
44
46
|
casinoId: DbCasino["id"];
|
|
45
47
|
mpExperienceId: string;
|
|
46
48
|
name: string;
|
|
47
49
|
}): Promise<DbExperience>;
|
|
48
|
-
export declare function createCurrency(pgClient:
|
|
50
|
+
export declare function createCurrency(pgClient: QueryExecutor, { key, casinoId, displayUnitName, displayUnitScale, }: {
|
|
49
51
|
key: DbCurrency["key"];
|
|
50
52
|
casinoId: DbCasino["id"];
|
|
51
53
|
displayUnitName: string;
|
|
52
54
|
displayUnitScale: number;
|
|
53
55
|
}): Promise<DbCurrency>;
|
|
54
|
-
export declare function createHouseBankroll(pgClient:
|
|
56
|
+
export declare function createHouseBankroll(pgClient: QueryExecutor, { casinoId, currencyKey, amount, }: {
|
|
55
57
|
casinoId: DbCasino["id"];
|
|
56
58
|
currencyKey: DbCurrency["key"];
|
|
57
59
|
amount: number;
|
|
58
60
|
}): Promise<DbHouseBankroll>;
|
|
59
61
|
export { createUser as createPlayer };
|
|
60
|
-
export declare function getPlayerBalance(pgClient:
|
|
62
|
+
export declare function getPlayerBalance(pgClient: QueryExecutor, { userId, experienceId, currencyKey, }: {
|
|
61
63
|
userId: DbUser["id"];
|
|
62
64
|
experienceId: DbExperience["id"];
|
|
63
65
|
currencyKey: DbCurrency["key"];
|
|
64
66
|
}): Promise<DbPlayerBalance | null>;
|
|
67
|
+
export declare function processOneDeadlinedLiability<RefType extends string>(dbPool: pg.Pool, { liabilityConfig, refType, refId, }: {
|
|
68
|
+
liabilityConfig: LiabilityConfigInternal<RefType>;
|
|
69
|
+
refType: RefType;
|
|
70
|
+
refId: string;
|
|
71
|
+
}): Promise<void>;
|
package/dist/src/test/index.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as pg from "pg";
|
|
2
2
|
import { randomBytes, randomUUID } from "node:crypto";
|
|
3
3
|
import { exec } from "node:child_process";
|
|
4
4
|
import { promisify } from "node:util";
|
|
5
5
|
const execAsync = promisify(exec);
|
|
6
6
|
import { DbHashKind, } from "../db/types.js";
|
|
7
|
-
import { exactlyOneRow, maybeOneRow } from "../db/index.js";
|
|
7
|
+
import { exactlyOneRow, maybeOneRow, withPgPoolTransaction, } from "../db/index.js";
|
|
8
8
|
import { GraphQLClient } from "graphql-request";
|
|
9
9
|
import { defaultPlugins, runMigrations } from "../index.js";
|
|
10
10
|
import { createHubServer } from "../server/index.js";
|
|
11
|
+
import { dbGetLiabilityByRef, processOneDeadlinedLiability as internalProcessOneDeadlinedLiability, } from "../liability/internal/index.js";
|
|
11
12
|
let testCounter = 0;
|
|
12
13
|
export function uniqueTestId() {
|
|
13
14
|
const workerId = process.env.VITEST_POOL_ID ?? "0";
|
|
@@ -20,7 +21,7 @@ export async function startTestServer({ plugins = [...defaultPlugins], userDatab
|
|
|
20
21
|
await execAsync(`psql -U postgres -c "CREATE DATABASE \\"${dbName}\\""`);
|
|
21
22
|
}
|
|
22
23
|
catch (_e) {
|
|
23
|
-
const pool = new Pool({ connectionString });
|
|
24
|
+
const pool = new pg.Pool({ connectionString });
|
|
24
25
|
try {
|
|
25
26
|
await pool.query(`
|
|
26
27
|
DROP SCHEMA IF EXISTS hub CASCADE;
|
|
@@ -38,7 +39,7 @@ export async function startTestServer({ plugins = [...defaultPlugins], userDatab
|
|
|
38
39
|
process.env.LOG_LEVEL = process.env.LOG_LEVEL ?? "error";
|
|
39
40
|
process.env.HASHCHAINSERVER_URL = "mock-server";
|
|
40
41
|
process.env.HASHCHAINSERVER_APPLICATION_SECRET = "test-secret";
|
|
41
|
-
const pgClient = new Client({ connectionString });
|
|
42
|
+
const pgClient = new pg.Client({ connectionString });
|
|
42
43
|
await pgClient.connect();
|
|
43
44
|
try {
|
|
44
45
|
await runMigrations({
|
|
@@ -61,7 +62,7 @@ export async function startTestServer({ plugins = [...defaultPlugins], userDatab
|
|
|
61
62
|
postgraphileDatabaseUrl: connectionString,
|
|
62
63
|
});
|
|
63
64
|
const { port } = await hubServer.listen();
|
|
64
|
-
const dbPool = new Pool({ connectionString });
|
|
65
|
+
const dbPool = new pg.Pool({ connectionString });
|
|
65
66
|
const url = `http://localhost:${port}`;
|
|
66
67
|
const graphqlUrl = `${url}/graphql`;
|
|
67
68
|
const { rows } = await dbPool.query("SELECT id FROM hub.casino WHERE is_playground = true LIMIT 1");
|
|
@@ -174,3 +175,15 @@ export async function getPlayerBalance(pgClient, { userId, experienceId, currenc
|
|
|
174
175
|
.then(maybeOneRow)
|
|
175
176
|
.then((row) => row ?? null);
|
|
176
177
|
}
|
|
178
|
+
export async function processOneDeadlinedLiability(dbPool, { liabilityConfig, refType, refId, }) {
|
|
179
|
+
await withPgPoolTransaction(dbPool, async (pgClient) => {
|
|
180
|
+
const dbLiability = await dbGetLiabilityByRef(pgClient, { refType, refId });
|
|
181
|
+
if (!dbLiability) {
|
|
182
|
+
throw new Error(`Liability not found: refType=${refType}, refId=${refId}`);
|
|
183
|
+
}
|
|
184
|
+
return internalProcessOneDeadlinedLiability(pgClient, {
|
|
185
|
+
liabilityConfig,
|
|
186
|
+
liabilityId: dbLiability.id,
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
}
|
package/dist/src/util.d.ts
CHANGED
|
@@ -3,6 +3,9 @@ declare const BRAND: unique symbol;
|
|
|
3
3
|
export type Branded<Brand, T> = T & {
|
|
4
4
|
readonly [BRAND]: Brand;
|
|
5
5
|
};
|
|
6
|
+
export type Prettify<T> = {
|
|
7
|
+
[K in keyof T]: T[K];
|
|
8
|
+
} & {};
|
|
6
9
|
export declare function isUuid(input: any): boolean;
|
|
7
10
|
export type Result<V, E> = {
|
|
8
11
|
ok: true;
|
|
@@ -13,4 +16,5 @@ export type Result<V, E> = {
|
|
|
13
16
|
};
|
|
14
17
|
export declare function extractFirstZodErrorMessage(e: z.ZodError): string;
|
|
15
18
|
export declare function uuidEqual(a: string, b: string): boolean;
|
|
19
|
+
export declare function abortableTimeout(delay: number, signal: AbortSignal): Promise<void>;
|
|
16
20
|
export {};
|
package/dist/src/util.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { assert } from "tsafe";
|
|
2
|
+
import { addAbortListener } from "node:events";
|
|
2
3
|
const UUID_REGEX = /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/;
|
|
3
4
|
export function isUuid(input) {
|
|
4
5
|
return typeof input === "string" && UUID_REGEX.test(input);
|
|
@@ -11,3 +12,17 @@ export function uuidEqual(a, b) {
|
|
|
11
12
|
assert(isUuid(b), `Expected valid uuid string: "${b}"`);
|
|
12
13
|
return a.toLowerCase() === b.toLowerCase();
|
|
13
14
|
}
|
|
15
|
+
export function abortableTimeout(delay, signal) {
|
|
16
|
+
if (signal.aborted)
|
|
17
|
+
return Promise.resolve();
|
|
18
|
+
return new Promise((resolve) => {
|
|
19
|
+
const disposable = addAbortListener(signal, () => {
|
|
20
|
+
clearTimeout(timeout);
|
|
21
|
+
resolve();
|
|
22
|
+
});
|
|
23
|
+
const timeout = setTimeout(() => {
|
|
24
|
+
disposable[Symbol.dispose]();
|
|
25
|
+
resolve();
|
|
26
|
+
}, delay);
|
|
27
|
+
});
|
|
28
|
+
}
|