Finished implementing match endpoints

This commit is contained in:
jd
2026-02-22 12:02:05 +00:00
parent 0fa00e6759
commit 872a79663b
7 changed files with 94 additions and 31 deletions

View File

@@ -5,13 +5,13 @@ info:
http:
method: DELETE
url: "{{BASE_URL}}/{{SECTOR}}/{{UserID}}"
url: "{{BASE_URL}}/{{SECTOR}}/{{MatchID}}"
auth: inherit
runtime:
variables:
- name: UserID
value: ""
- name: MatchID
value: 846M1L
settings:
encodeUrl: true

View File

@@ -11,7 +11,7 @@ http:
runtime:
variables:
- name: MatchID
value: 9YQ84M
value: 848O12
settings:
encodeUrl: true

View File

@@ -0,0 +1,23 @@
info:
name: Leave
type: http
seq: 4
http:
method: POST
url: "{{BASE_URL}}/{{SECTOR}}/{{MatchID}}/leave"
body:
type: json
data: ""
auth: inherit
runtime:
variables:
- name: MatchID
value: 848O12
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5

View File

@@ -11,7 +11,7 @@ http:
runtime:
variables:
- name: PlayerID
value: ""
value: 539DPX
settings:
encodeUrl: true

View File

@@ -30,7 +30,17 @@ async function get(request: UnwrappedRequest): Promise<Response> {
async function drop(request: UnwrappedRequest): Promise<Response> {
try {
return new OkResponse(await orm.matches.drop(UserId.fromHash(request.params.id), request.claims));
return new OkResponse(await orm.matches.drop(MatchId.fromHash(request.params.id), request.claims));
} catch (error: any) {
return new ErrorResponse(error as Error);
}
}
async function leave(request: UnwrappedRequest): Promise<Response> {
try {
return new OkResponse(
await orm.matches.removePlayer(MatchId.fromHash(request.params.id), request.claims.userId as UserId),
);
} catch (error: any) {
return new ErrorResponse(error as Error);
}
@@ -40,4 +50,5 @@ export default {
create,
get,
drop,
leave,
};

View File

@@ -4,6 +4,7 @@ import { first, orderBy } from 'lodash';
import { BadRequestError, NotFoundError, UnauthorizedError } from '../utilities/errors';
import { GameId, MatchId, PlayerId, UserId } from '../utilities/secureIds';
import { calculateElos } from '../utilities/elo';
import { orm } from './orm';
export class MatchParticipant {
matchId?: MatchId;
@@ -125,13 +126,14 @@ export class MatchOrm {
throw new UnauthorizedError();
}
if (!dbResult) {
throw new NotFoundError('No matching user exists');
const matchData = dbResult?.find((x: any) => x.match_id);
if (!matchData?.match_id) {
throw new NotFoundError('No matching match exists');
}
return new Match(
MatchId.fromID(dbResult?.[0]?.match_id),
GameId.fromID(dbResult?.[0]?.game_id),
MatchId.fromID(matchData?.match_id),
GameId.fromID(matchData?.game_id),
orderBy(
dbResult
.filter((x: any) => x.player_id)
@@ -152,28 +154,52 @@ export class MatchOrm {
);
}
async drop(id: UserId, claims?: Claims): Promise<void> {
// if (
// !(
// Claims.test(Claims.ADMIN, claims) ||
// Claims.test(Claims.USERS.OTHER.DELETE, claims) ||
// (Claims.test(Claims.USERS.SELF.DELETE, claims) && id === claims?.userId)
// )
// ) {
// throw new UnauthorizedError();
// }
//
// // Ensure user exists before attempting to delete
// await this.get(id);
// await sql.transaction(async (tx) => {
// await tx`DELETE
// FROM user_claims
// WHERE user_id = ${id.raw}`;
// await tx`DELETE
// FROM users
// WHERE id = ${id.raw}`;
// });
async drop(id: MatchId, claims?: Claims): Promise<void> {
const match = await this.get(id);
if (
!(
Claims.test(Claims.ADMIN, claims) ||
(Claims.test(Claims.MATCHES.OWNED.DELETE, claims) && match.owner === claims?.userId)
)
) {
throw new UnauthorizedError();
}
await sql.transaction(async (tx) => {
await tx`DELETE
FROM match_players
WHERE match_id = ${id.raw}`;
await tx`DELETE
FROM matches
WHERE id = ${id.raw}`;
});
return;
}
async removePlayer(matchId: MatchId, participantId: UserId): Promise<void>;
async removePlayer(matchId: MatchId, participantId: PlayerId): Promise<void> {
let playerId: PlayerId = participantId;
if (participantId instanceof UserId) {
playerId = (await orm.users.get(participantId))?.playerId;
if (!playerId) {
throw new BadRequestError('User is not a participant');
}
}
const player = await orm.players.get(playerId);
sql.transaction(async (tx) => {
const eloRefund = parseInt(
(
await tx`SELECT elo_change FROM public.match_players WHERE match_id=${matchId.raw} AND player_id = ${playerId.raw}`
)?.[0]?.elo_change ?? 0,
);
await tx`DELETE FROM match_players WHERE match_id=${matchId.raw} AND player_id=${playerId.raw}`;
if (!player.isRatingLocked) {
await tx`UPDATE players SET elo=${player.elo - eloRefund} WHERE id=${playerId.raw}`;
}
});
return;
}
}

View File

@@ -7,5 +7,8 @@ export default {
':id': {
GET: guard(matches.get, [Claims.ADMIN, Claims.MATCHES.OWNED.READ, Claims.MATCHES.PARTICIPANT.READ]),
DELETE: guard(matches.drop, [Claims.ADMIN, Claims.MATCHES.OWNED.DELETE, Claims.USERS.SELF.UPDATE]),
leave: {
POST: guard(matches.leave, [Claims.MATCHES.PARTICIPANT.LEAVE]),
},
},
};