fix(game-api): separate engine message responses
This commit is contained in:
@@ -1,6 +1,9 @@
|
|||||||
|
import { randomUUID } from 'node:crypto';
|
||||||
|
|
||||||
import { TRPCError } from '@trpc/server';
|
import { TRPCError } from '@trpc/server';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { asRecord } from '@sammo-ts/common';
|
import { asRecord, ChangeJournal } from '@sammo-ts/common';
|
||||||
|
import { writeReadModelChangeJournal } from '@sammo-ts/infra';
|
||||||
import type { UserSanctions } from '@sammo-ts/common/auth/gameToken';
|
import type { UserSanctions } from '@sammo-ts/common/auth/gameToken';
|
||||||
import { isMessageAccessBlocked } from '@sammo-ts/common/auth/sanctions';
|
import { isMessageAccessBlocked } from '@sammo-ts/common/auth/sanctions';
|
||||||
|
|
||||||
@@ -9,7 +12,9 @@ import {
|
|||||||
accessLimitAuthedInputProcedure,
|
accessLimitAuthedInputProcedure,
|
||||||
accessWallAuthedInputProcedure,
|
accessWallAuthedInputProcedure,
|
||||||
authedProcedure,
|
authedProcedure,
|
||||||
|
engineAuthedProcedure,
|
||||||
router,
|
router,
|
||||||
|
scopeApiInputEventRequestId,
|
||||||
wallAuthedProcedure,
|
wallAuthedProcedure,
|
||||||
} from '../../trpc.js';
|
} from '../../trpc.js';
|
||||||
import {
|
import {
|
||||||
@@ -32,6 +37,7 @@ import {
|
|||||||
import { getOwnedGeneral } from '../shared/general.js';
|
import { getOwnedGeneral } from '../shared/general.js';
|
||||||
import { resolveNationPermission } from '../nation/shared.js';
|
import { resolveNationPermission } from '../nation/shared.js';
|
||||||
import { respondToDiplomaticMessage } from '../../messages/diplomaticResponse.js';
|
import { respondToDiplomaticMessage } from '../../messages/diplomaticResponse.js';
|
||||||
|
import { executeInputEvent } from '../../inputEventBoundary.js';
|
||||||
|
|
||||||
const zMessageType = z.enum(['private', 'public', 'national', 'diplomacy']);
|
const zMessageType = z.enum(['private', 'public', 'national', 'diplomacy']);
|
||||||
|
|
||||||
@@ -315,7 +321,7 @@ export const messagesRouter = router({
|
|||||||
markMessageMailboxes(ctx, [message.mailbox, ...(receiverMailbox === null ? [] : [receiverMailbox])]);
|
markMessageMailboxes(ctx, [message.mailbox, ...(receiverMailbox === null ? [] : [receiverMailbox])]);
|
||||||
return { ok: true, deletedIds };
|
return { ok: true, deletedIds };
|
||||||
}),
|
}),
|
||||||
respond: authedProcedure
|
respond: engineAuthedProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
generalId: z.number().int().positive(),
|
generalId: z.number().int().positive(),
|
||||||
@@ -346,29 +352,54 @@ export const messagesRouter = router({
|
|||||||
}
|
}
|
||||||
return { result: commandResult.ok, reason: commandResult.reason };
|
return { result: commandResult.ok, reason: commandResult.reason };
|
||||||
}
|
}
|
||||||
const result = await respondToDiplomaticMessage({
|
const ownsChangeJournal = !ctx.changeJournal;
|
||||||
|
const changeJournal = ctx.changeJournal ?? new ChangeJournal();
|
||||||
|
let journalPersisted = false;
|
||||||
|
const response = await executeInputEvent({
|
||||||
db: ctx.db,
|
db: ctx.db,
|
||||||
actor: general,
|
requestId: scopeApiInputEventRequestId(ctx.requestId ?? randomUUID(), 'messages.respond.diplomatic', 0),
|
||||||
messageId: input.messageId,
|
eventType: 'messages.respond.diplomatic',
|
||||||
response: input.response,
|
payload: input,
|
||||||
|
actorUserId: ctx.auth?.user.id,
|
||||||
|
execute: async (transaction) => {
|
||||||
|
const transactionContext = { ...ctx, db: transaction, changeJournal };
|
||||||
|
const transactionGeneral = ownsChangeJournal
|
||||||
|
? await getOwnedGeneral(transactionContext, input.generalId)
|
||||||
|
: general;
|
||||||
|
const result = await respondToDiplomaticMessage({
|
||||||
|
db: transaction,
|
||||||
|
actor: transactionGeneral,
|
||||||
|
messageId: input.messageId,
|
||||||
|
response: input.response,
|
||||||
|
});
|
||||||
|
markMessageMailboxes(transactionContext, result.affectedMailboxes);
|
||||||
|
for (const generalId of result.affectedGeneralRecordIds) {
|
||||||
|
changeJournal.mark('records.general', generalId);
|
||||||
|
}
|
||||||
|
for (const nationId of result.affectedNationIds) {
|
||||||
|
changeJournal.mark('nation.content', nationId);
|
||||||
|
}
|
||||||
|
for (const cityId of result.affectedCityIds) {
|
||||||
|
changeJournal.mark('city.content', cityId);
|
||||||
|
}
|
||||||
|
if (result.affectedCityIds.length > 0) {
|
||||||
|
changeJournal.mark('map.world');
|
||||||
|
}
|
||||||
|
if (result.affectedNationIds.length > 0 || result.affectedCityIds.length > 0) {
|
||||||
|
changeJournal.mark('dashboard.global');
|
||||||
|
}
|
||||||
|
if (ownsChangeJournal) {
|
||||||
|
journalPersisted = Boolean(
|
||||||
|
await writeReadModelChangeJournal(transaction, changeJournal.snapshot())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return { result: result.result, reason: result.reason };
|
||||||
|
},
|
||||||
});
|
});
|
||||||
markMessageMailboxes(ctx, result.affectedMailboxes);
|
if (journalPersisted) {
|
||||||
for (const generalId of result.affectedGeneralRecordIds) {
|
ctx.readModelOutbox?.wake();
|
||||||
ctx.changeJournal?.mark('records.general', generalId);
|
|
||||||
}
|
}
|
||||||
for (const nationId of result.affectedNationIds) {
|
return response;
|
||||||
ctx.changeJournal?.mark('nation.content', nationId);
|
|
||||||
}
|
|
||||||
for (const cityId of result.affectedCityIds) {
|
|
||||||
ctx.changeJournal?.mark('city.content', cityId);
|
|
||||||
}
|
|
||||||
if (result.affectedCityIds.length > 0) {
|
|
||||||
ctx.changeJournal?.mark('map.world');
|
|
||||||
}
|
|
||||||
if (result.affectedNationIds.length > 0 || result.affectedCityIds.length > 0) {
|
|
||||||
ctx.changeJournal?.mark('dashboard.global');
|
|
||||||
}
|
|
||||||
return { result: result.result, reason: result.reason };
|
|
||||||
}),
|
}),
|
||||||
getOld: authedProcedure
|
getOld: authedProcedure
|
||||||
.input(
|
.input(
|
||||||
|
|||||||
@@ -801,8 +801,11 @@ describe('messages router missing-flow compatibility', () => {
|
|||||||
action,
|
action,
|
||||||
reason: 'success',
|
reason: 'success',
|
||||||
}));
|
}));
|
||||||
|
const transaction = vi.fn(async () => {
|
||||||
|
throw new Error('ENGINE message response must not enter the API input-event transaction.');
|
||||||
|
});
|
||||||
const { caller } = buildContext(
|
const { caller } = buildContext(
|
||||||
{ $queryRaw: vi.fn(async () => [messageRow]) },
|
{ $queryRaw: vi.fn(async () => [messageRow]), $transaction: transaction },
|
||||||
{ turnDaemon: { requestCommand } }
|
{ turnDaemon: { requestCommand } }
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -822,6 +825,7 @@ describe('messages router missing-flow compatibility', () => {
|
|||||||
response: true,
|
response: true,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
expect(transaction).not.toHaveBeenCalled();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user