Compare commits

..

3 Commits

Author SHA1 Message Date
jd
ed942060a2 Tidied up Bruno files. 2026-02-21 17:56:28 +00:00
jd
407043c5be Implemented PagedResponse 2026-02-21 17:51:17 +00:00
jd
afa1c13e13 Implemented the ability to add/remove games from collections. 2026-02-21 17:27:46 +00:00
38 changed files with 375 additions and 108 deletions

View File

@@ -5,7 +5,7 @@ info:
http: http:
method: POST method: POST
url: http://localhost:3000/api/auth/login url: "{{BASE_URL}}/api/auth/login"
headers: headers:
- name: Content-Type - name: Content-Type
value: application/json value: application/json
@@ -18,6 +18,16 @@ http:
} }
auth: inherit auth: inherit
runtime:
scripts:
- type: after-response
code: |-
function onResponse(res) {
console.log(res.getHeader('set-cookie'));
bru.setEnvVar("REFRESH_COOKIE", res.getHeader('set-cookie'));
}
onResponse(res);
settings: settings:
encodeUrl: true encodeUrl: true
timeout: 0 timeout: 0

View File

@@ -5,12 +5,22 @@ info:
http: http:
method: GET method: GET
url: http://localhost:3000/api/auth/token url: "{{BASE_URL}}/api/auth/token"
headers: headers:
- name: Cookie - name: Cookie
value: refresh=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1IjoiMSIsInIiOiIxIiwiaWF0IjoxNzcxNTk3NjQ2LCJleHAiOjE3NzQxODk2NDZ9.07ViS5Nie3Bi2OgnlHyybDNZ9bdXPRRiqO-RFLhjoKo; Path=/; Max-Age=2592000; Secure; HttpOnly; SameSite=Lax value: "{{REFRESH_COOKIE}}"
auth: inherit auth: inherit
runtime:
scripts:
- type: after-response
code: |-
function onResponse(res) {
let data = res.getBody();
bru.setEnvVar("BEARER_TOKEN", data.token);
}
onResponse(res);
settings: settings:
encodeUrl: true encodeUrl: true
timeout: 0 timeout: 0

View File

@@ -0,0 +1,28 @@
info:
name: Add game
type: http
seq: 6
http:
method: POST
url: "{{BASE_URL}}/api/collection/{{CollectionID}}/add"
body:
type: json
data: |-
{
"gameId": "{{GameID}}"
}
auth: inherit
runtime:
variables:
- name: CollectionID
value: ""
- name: GameID
value: ""
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5

View File

@@ -5,15 +5,20 @@ info:
http: http:
method: POST method: POST
url: http://localhost:3000/api/collection url: "{{BASE_URL}}/api/collection"
body: body:
type: json type: json
data: |- data: |-
{ {
"name": "Invited player2" "name": "{{Name}}"
} }
auth: inherit auth: inherit
runtime:
variables:
- name: Name
value: ""
settings: settings:
encodeUrl: true encodeUrl: true
timeout: 0 timeout: 0

View File

@@ -5,13 +5,14 @@ info:
http: http:
method: DELETE method: DELETE
url: http://localhost:3000/api/collection/:id url: "{{BASE_URL}}/api/collection/{{CollectionID}}"
params:
- name: id
value: bmOe
type: path
auth: inherit auth: inherit
runtime:
variables:
- name: CollectionID
value: ""
settings: settings:
encodeUrl: true encodeUrl: true
timeout: 0 timeout: 0

View File

@@ -5,13 +5,18 @@ info:
http: http:
method: GET method: GET
url: http://localhost:3000/api/collection/:id url: "{{BASE_URL}}/api/collection/{{CollectionID}}"
params: params:
- name: id - name: ""
value: ejRe value: ""
type: path type: query
auth: inherit auth: inherit
runtime:
variables:
- name: CollectionID
value: ""
settings: settings:
encodeUrl: true encodeUrl: true
timeout: 0 timeout: 0

View File

@@ -5,9 +5,16 @@ info:
http: http:
method: GET method: GET
url: http://localhost:3000/api/collection/list url: "{{BASE_URL}}/api/collection/list/{{PageSize}}/{{Page}}"
auth: inherit auth: inherit
runtime:
variables:
- name: PageSize
value: "1"
- name: Page
value: "0"
settings: settings:
encodeUrl: true encodeUrl: true
timeout: 0 timeout: 0

View File

@@ -0,0 +1,28 @@
info:
name: Remove game
type: http
seq: 7
http:
method: POST
url: "{{BASE_URL}}/api/collection/{{CollectionID}}/remove"
body:
type: json
data: |-
{
"gameId": "{{GameID}}"
}
auth: inherit
runtime:
variables:
- name: CollectionID
value: ""
- name: GameID
value: ""
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5

View File

@@ -5,21 +5,22 @@ info:
http: http:
method: PATCH method: PATCH
url: http://localhost:3000/api/collection/:id url: "{{BASE_URL}}/api/collection/{{CollectionID}}"
params:
- name: id
value: bmOe
type: path
body: body:
type: json type: json
data: |- data: |-
{ {
"name": "Test Player", "name": "{{Name}}"
"isRatingLocked": true,
"canBeMultiple": false
} }
auth: inherit auth: inherit
runtime:
variables:
- name: CollectionID
value: ""
- name: Name
value: ""
settings: settings:
encodeUrl: true encodeUrl: true
timeout: 0 timeout: 0

View File

@@ -5,15 +5,20 @@ info:
http: http:
method: POST method: POST
url: http://localhost:3000/api/game url: "{{BASE_URL}}/api/game"
body: body:
type: json type: json
data: |- data: |-
{ {
"name": "Test Game3" "name": "{{Name}}"
} }
auth: inherit auth: inherit
runtime:
variables:
- name: Name
value: ""
settings: settings:
encodeUrl: true encodeUrl: true
timeout: 0 timeout: 0

View File

@@ -5,13 +5,14 @@ info:
http: http:
method: DELETE method: DELETE
url: http://localhost:3000/api/game/:id url: "{{BASE_URL}}/api/game/{{GameID}}"
params:
- name: id
value: bk5e
type: path
auth: inherit auth: inherit
runtime:
variables:
- name: GameID
value: ""
settings: settings:
encodeUrl: true encodeUrl: true
timeout: 0 timeout: 0

View File

@@ -5,13 +5,14 @@ info:
http: http:
method: GET method: GET
url: http://localhost:3000/api/game/:id url: "{{BASE_URL}}/api/game/{{GameID}}"
params:
- name: id
value: bk5e
type: path
auth: inherit auth: inherit
runtime:
variables:
- name: GameID
value: ""
settings: settings:
encodeUrl: true encodeUrl: true
timeout: 0 timeout: 0

View File

@@ -5,13 +5,18 @@ info:
http: http:
method: GET method: GET
url: http://localhost:3000/api/game/search/:query url: "{{BASE_URL}}/api/game/search/{{Query}}/{{PageSize}}/{{Page}}"
params:
- name: query
value: game
type: path
auth: inherit auth: inherit
runtime:
variables:
- name: Query
value: test
- name: PageSize
value: "2"
- name: Page
value: "2"
settings: settings:
encodeUrl: true encodeUrl: true
timeout: 0 timeout: 0

View File

@@ -5,19 +5,22 @@ info:
http: http:
method: PATCH method: PATCH
url: http://localhost:3000/api/game/:id url: "{{BASE_URL}}/api/game/{{GameID}}"
params:
- name: id
value: el5a
type: path
body: body:
type: json type: json
data: |- data: |-
{ {
"name":"Updated game" "name":"{{Name}}"
} }
auth: inherit auth: inherit
runtime:
variables:
- name: GameID
value: ""
- name: Name
value: ""
settings: settings:
encodeUrl: true encodeUrl: true
timeout: 0 timeout: 0

View File

@@ -5,16 +5,23 @@ info:
http: http:
method: POST method: POST
url: http://localhost:3000/api/invite/accept url: "{{BASE_URL}}/api/invite/accept"
body: body:
type: json type: json
data: |- data: |-
{ {
"inviteCode": "3ST6N8", "inviteCode": "{{InviteCode}}",
"password": "test123" "password": "{{Password}}
} }
auth: inherit auth: inherit
runtime:
variables:
- name: InviteCode
value: ""
- name: Password
value: ""
settings: settings:
encodeUrl: true encodeUrl: true
timeout: 0 timeout: 0

View File

@@ -5,16 +5,23 @@ info:
http: http:
method: POST method: POST
url: http://localhost:3000/api/invite url: "{{BASE_URL}}/api/invite"
body: body:
type: json type: json
data: |- data: |-
{ {
"email": "james+test2@dardry.com", "email": "{{Email}}",
"playerId": "boja" "playerId": "{{PlayerID}}"
} }
auth: inherit auth: inherit
runtime:
variables:
- name: Email
value: ""
- name: PlayerID
value: ""
settings: settings:
encodeUrl: true encodeUrl: true
timeout: 0 timeout: 0

View File

@@ -1,7 +1,7 @@
info: info:
name: Invites name: Invites
type: folder type: folder
seq: 5 seq: 1
request: request:
auth: inherit auth: inherit

View File

@@ -5,15 +5,20 @@ info:
http: http:
method: POST method: POST
url: http://localhost:3000/api/player url: "{{BASE_URL}}/api/player"
body: body:
type: json type: json
data: |- data: |-
{ {
"name": "Invited player2" "name": "{{Name}}"
} }
auth: inherit auth: inherit
runtime:
variables:
- name: Name
value: Test
settings: settings:
encodeUrl: true encodeUrl: true
timeout: 0 timeout: 0

View File

@@ -5,13 +5,14 @@ info:
http: http:
method: DELETE method: DELETE
url: http://localhost:3000/api/player/:id url: "{{BASE_URL}}/api/player/{{PlayerID}}"
params:
- name: id
value: bmOe
type: path
auth: inherit auth: inherit
runtime:
variables:
- name: PlayerID
value: ""
settings: settings:
encodeUrl: true encodeUrl: true
timeout: 0 timeout: 0

View File

@@ -5,13 +5,14 @@ info:
http: http:
method: GET method: GET
url: http://localhost:3000/api/player/:id url: "{{BASE_URL}}/api/player/{{PlayerID}}"
params:
- name: id
value: ejRe
type: path
auth: inherit auth: inherit
runtime:
variables:
- name: PlayerID
value: ""
settings: settings:
encodeUrl: true encodeUrl: true
timeout: 0 timeout: 0

View File

@@ -5,7 +5,7 @@ info:
http: http:
method: GET method: GET
url: http://localhost:3000/api/player/list url: "{{BASE_URL}}/api/player/list"
auth: inherit auth: inherit
settings: settings:

View File

@@ -5,21 +5,24 @@ info:
http: http:
method: PATCH method: PATCH
url: http://localhost:3000/api/player/:id url: "{{BASE_URL}}/api/player/{{PlayerID}}"
params:
- name: id
value: bmOe
type: path
body: body:
type: json type: json
data: |- data: |-
{ {
"name": "Test Player", "name": "{{Name}}",
"isRatingLocked": true, "isRatingLocked": false,
"canBeMultiple": false "canBeMultiple": false
} }
auth: inherit auth: inherit
runtime:
variables:
- name: Name
value: Foo
- name: PlayerID
value: ""
settings: settings:
encodeUrl: true encodeUrl: true
timeout: 0 timeout: 0

View File

@@ -5,17 +5,26 @@ info:
http: http:
method: POST method: POST
url: http://localhost:3000/api/user url: "{{BASE_URL}}/api/user"
body: body:
type: json type: json
data: |- data: |-
{ {
"email": "Test User", "email": "{{Email}}",
"password": "Test123", "password": "{{Password}}",
"playerId": "enRe" "playerId": "{{PlayerID}}"
} }
auth: inherit auth: inherit
runtime:
variables:
- name: Email
value: ""
- name: Password
value: ""
- name: PlayerID
value: ""
settings: settings:
encodeUrl: true encodeUrl: true
timeout: 0 timeout: 0

View File

@@ -5,13 +5,14 @@ info:
http: http:
method: DELETE method: DELETE
url: http://localhost:3000/api/user/:id url: "{{BASE_URL}}/api/user/{{UserID}}"
params:
- name: id
value: ""
type: path
auth: inherit auth: inherit
runtime:
variables:
- name: UserID
value: ""
settings: settings:
encodeUrl: true encodeUrl: true
timeout: 0 timeout: 0

View File

@@ -5,13 +5,14 @@ info:
http: http:
method: GET method: GET
url: http://localhost:3000/api/user/:id url: "{{BASE_URL}}/api/user/{{UserID}}"
params:
- name: id
value: ejRe
type: path
auth: inherit auth: inherit
runtime:
variables:
- name: UserID
value: ""
settings: settings:
encodeUrl: true encodeUrl: true
timeout: 0 timeout: 0

View File

@@ -5,20 +5,28 @@ info:
http: http:
method: PATCH method: PATCH
url: http://localhost:3000/api/user/:id url: "{{BASE_URL}}/api/user/{{UserID}}"
params:
- name: id
value: ""
type: path
body: body:
type: json type: json
data: |- data: |-
{ {
"isActive": true, "isActive": {{IsActive}},
"isAdmin": false "isAdmin": {{IsAdmin}},
"email": "{{Email}}"
} }
auth: inherit auth: inherit
runtime:
variables:
- name: UserID
value: ""
- name: IsActive
value: ""
- name: IsAdmin
value: ""
- name: Email
value: ""
settings: settings:
encodeUrl: true encodeUrl: true
timeout: 0 timeout: 0

View File

@@ -0,0 +1,8 @@
name: BGApp
variables:
- name: BEARER_TOKEN
value: ""
- name: REFRESH_COOKIE
value: ""
- name: BASE_URL
value: http://localhost:3000

View File

@@ -17,7 +17,7 @@ config:
request: request:
auth: auth:
type: bearer type: bearer
token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJNUFJLTFIiLCJjbGFpbXMiOlsiQURNSU4iLCJVU0VSU19DUkVBVEUiLCJVU0VSU19TRUxGX1JFQUQiLCJVU0VSU19TRUxGX1VQREFURSIsIlVTRVJTX1NFTEZfREVMRVRFIiwiVVNFUlNfT1RIRVJfUkVBRCIsIlVTRVJTX09USEVSX1VQREFURSJdLCJpYXQiOjE3NzE2ODcxNzAsImV4cCI6MTgwMzIyMzE3MH0.inf1q3LTMuTkzLI-lEezYduPCpidJDaqsWZNNIY_doE token: "{{BEARER_TOKEN}}"
actions: actions:
- type: set-variable - type: set-variable
phase: after-response phase: after-response

View File

@@ -1,8 +1,12 @@
import { orm } from '../orm/orm'; import { orm } from '../orm/orm';
import { UnwrappedRequest } from '../utilities/guard'; import { UnwrappedRequest } from '../utilities/guard';
import { CreatedResponse, ErrorResponse, OkResponse } from '../utilities/responseHelper'; import { CreatedResponse, ErrorResponse, OkResponse, PagedResponse } from '../utilities/responseHelper';
import { CreateCollectionRequest, UpdateCollectionRequest } from '../utilities/requestModels'; import {
import { CollectionId } from '../utilities/secureIds'; GameToCollectionRequest,
CreateCollectionRequest,
UpdateCollectionRequest,
} from '../utilities/requestModels';
import { CollectionId, GameId } from '../utilities/secureIds';
async function create(request: UnwrappedRequest<CreateCollectionRequest>): Promise<Response> { async function create(request: UnwrappedRequest<CreateCollectionRequest>): Promise<Response> {
try { try {
@@ -23,7 +27,7 @@ async function get(request: UnwrappedRequest): Promise<Response> {
async function list(request: UnwrappedRequest): Promise<Response> { async function list(request: UnwrappedRequest): Promise<Response> {
try { try {
return new OkResponse(await orm.collections.list(request.claims)); return new PagedResponse(request, await orm.collections.list(request.claims));
} catch (error: any) { } catch (error: any) {
return new ErrorResponse(error as Error); return new ErrorResponse(error as Error);
} }
@@ -47,10 +51,40 @@ async function drop(request: UnwrappedRequest): Promise<Response> {
} }
} }
async function addGame(request: UnwrappedRequest<GameToCollectionRequest>): Promise<Response> {
try {
return new OkResponse(
await orm.collections.addGame(
CollectionId.fromHash(request.params.id),
GameId.fromHash(request.body.gameId),
request.claims,
),
);
} catch (error: any) {
return new ErrorResponse(error as Error);
}
}
async function removeGame(request: UnwrappedRequest<GameToCollectionRequest>): Promise<Response> {
try {
return new OkResponse(
await orm.collections.removeGame(
CollectionId.fromHash(request.params.id),
GameId.fromHash(request.body.gameId),
request.claims,
),
);
} catch (error: any) {
return new ErrorResponse(error as Error);
}
}
export default { export default {
create, create,
get, get,
list, list,
update, update,
drop, drop,
addGame,
removeGame,
}; };

View File

@@ -1,6 +1,6 @@
import { orm } from '../orm/orm'; import { orm } from '../orm/orm';
import { UnwrappedRequest } from '../utilities/guard'; import { UnwrappedRequest } from '../utilities/guard';
import { CreatedResponse, ErrorResponse, OkResponse } from '../utilities/responseHelper'; import { CreatedResponse, ErrorResponse, OkResponse, PagedResponse } from '../utilities/responseHelper';
import { import {
CreateGameRequest, CreateGameRequest,
UpdateGameRequest, UpdateGameRequest,
@@ -55,7 +55,7 @@ async function drop(request: UnwrappedRequest): Promise<Response> {
async function query(request: UnwrappedRequest): Promise<Response> { async function query(request: UnwrappedRequest): Promise<Response> {
try { try {
return new OkResponse(await orm.games.query(request.params.query)); return new PagedResponse(request, await orm.games.query(request.params.query));
} catch (error: any) { } catch (error: any) {
return new ErrorResponse(error as Error); return new ErrorResponse(error as Error);
} }

View File

@@ -1,6 +1,6 @@
import { orm } from '../orm/orm'; import { orm } from '../orm/orm';
import { UnwrappedRequest } from '../utilities/guard'; import { UnwrappedRequest } from '../utilities/guard';
import { CreatedResponse, ErrorResponse, OkResponse } from '../utilities/responseHelper'; import { CreatedResponse, ErrorResponse, OkResponse, PagedResponse } from '../utilities/responseHelper';
import { CreatePlayerRequest, UpdatePlayerRequest } from '../utilities/requestModels'; import { CreatePlayerRequest, UpdatePlayerRequest } from '../utilities/requestModels';
import { PlayerId } from '../utilities/secureIds'; import { PlayerId } from '../utilities/secureIds';
@@ -23,7 +23,7 @@ async function get(request: UnwrappedRequest): Promise<Response> {
async function list(request: UnwrappedRequest): Promise<Response> { async function list(request: UnwrappedRequest): Promise<Response> {
try { try {
return new OkResponse(await orm.players.list(request.claims)); return new PagedResponse(request, await orm.players.list(request.claims));
} catch (error: any) { } catch (error: any) {
return new ErrorResponse(error as Error); return new ErrorResponse(error as Error);
} }

View File

@@ -127,4 +127,41 @@ export class CollectionsOrm {
return; return;
} }
async addGame(id: CollectionId, gameId: GameId, claims: Claims): Promise<void> {
const collection = await this.get(id);
if (!(Claims.test(Claims.ADMIN, claims) || Claims.test(Claims.COLLECTIONS.UNOWNED.GAME.ADD, claims))) {
throw new UnauthorizedError();
} else if (
Claims.test(Claims.COLLECTIONS.OWNED.GAME.ADD, claims) &&
claims?.userId &&
collection.id !== claims.userId
) {
throw new UnauthorizedError();
}
await sql`INSERT INTO collection_games (collection_id, game_id)
VALUES (${id.raw}, ${gameId.raw})`;
return;
}
async removeGame(id: CollectionId, gameId: GameId, claims: Claims): Promise<void> {
const collection = await this.get(id);
if (!(Claims.test(Claims.ADMIN, claims) || Claims.test(Claims.COLLECTIONS.UNOWNED.GAME.REMOVE, claims))) {
throw new UnauthorizedError();
} else if (
Claims.test(Claims.COLLECTIONS.OWNED.GAME.REMOVE, claims) &&
claims?.userId &&
collection.id !== claims.userId
) {
throw new UnauthorizedError();
}
await sql`DELETE
FROM collection_games
WHERE collection_id = ${id.raw}
AND game_id = ${gameId.raw}`;
return;
}
} }

View File

@@ -11,7 +11,21 @@ export default {
// PATCH: guard(collections.update, [Claims.ADMIN, Claims.PLAYERS.OTHER.UPDATE, Claims.PLAYERS.SELF.UPDATE]), // PATCH: guard(collections.update, [Claims.ADMIN, Claims.PLAYERS.OTHER.UPDATE, Claims.PLAYERS.SELF.UPDATE]),
// DELETE: guard(collections.drop, [Claims.ADMIN, Claims.PLAYERS.OTHER.DELETE, Claims.PLAYERS.SELF.DELETE]), // DELETE: guard(collections.drop, [Claims.ADMIN, Claims.PLAYERS.OTHER.DELETE, Claims.PLAYERS.SELF.DELETE]),
}, },
'/api/collection/list': { '/api/collection/:id/add': {
POST: guard(collections.addGame, [
Claims.ADMIN,
Claims.COLLECTIONS.UNOWNED.GAME.ADD,
Claims.COLLECTIONS.OWNED.GAME.ADD,
]),
},
'/api/collection/:id/remove': {
POST: guard(collections.removeGame, [
Claims.ADMIN,
Claims.COLLECTIONS.UNOWNED.GAME.REMOVE,
Claims.COLLECTIONS.OWNED.GAME.REMOVE,
]),
},
'/api/collection/list/:pageSize/:page': {
GET: guard(collections.list, [Claims.ADMIN, Claims.COLLECTIONS.OWNED.LIST]), GET: guard(collections.list, [Claims.ADMIN, Claims.COLLECTIONS.OWNED.LIST]),
}, },
}; };

View File

@@ -11,7 +11,7 @@ export default {
PATCH: guard(games.update, [Claims.ADMIN, Claims.GAMES.UPDATE]), PATCH: guard(games.update, [Claims.ADMIN, Claims.GAMES.UPDATE]),
DELETE: guard(games.drop, [Claims.ADMIN, Claims.GAMES.DELETE]), DELETE: guard(games.drop, [Claims.ADMIN, Claims.GAMES.DELETE]),
}, },
'/api/game/search/:query': { '/api/game/search/:query/:pageSize/:page': {
GET: guard(games.query, [Claims.ADMIN, Claims.GAMES.READ]), GET: guard(games.query, [Claims.ADMIN, Claims.GAMES.READ]),
}, },
}; };

View File

@@ -11,7 +11,7 @@ export default {
PATCH: guard(player.update, [Claims.ADMIN, Claims.PLAYERS.OTHER.UPDATE, Claims.PLAYERS.SELF.UPDATE]), PATCH: guard(player.update, [Claims.ADMIN, Claims.PLAYERS.OTHER.UPDATE, Claims.PLAYERS.SELF.UPDATE]),
DELETE: guard(player.drop, [Claims.ADMIN, Claims.PLAYERS.OTHER.DELETE, Claims.PLAYERS.SELF.DELETE]), DELETE: guard(player.drop, [Claims.ADMIN, Claims.PLAYERS.OTHER.DELETE, Claims.PLAYERS.SELF.DELETE]),
}, },
'/api/player/list': { '/api/player/list/:pageSize/:page': {
GET: guard(player.list, [Claims.ADMIN, Claims.PLAYERS.OTHER.READ]), GET: guard(player.list, [Claims.ADMIN, Claims.PLAYERS.OTHER.READ]),
}, },
}; };

View File

@@ -108,6 +108,10 @@ export class ClaimDefinition {
UPDATE: 'COLLECTIONS_OWNED_UPDATE', UPDATE: 'COLLECTIONS_OWNED_UPDATE',
DELETE: 'COLLECTIONS_OWNED_DELETE', DELETE: 'COLLECTIONS_OWNED_DELETE',
LIST: 'COLLECTIONS_OWNED_LIST', LIST: 'COLLECTIONS_OWNED_LIST',
GAME: {
ADD: 'COLLECTIONS_OWNED_GAME_ADD',
REMOVE: 'COLLECTIONS_OWNED_GAME_REMOVE',
},
COMMENTS: { COMMENTS: {
DELETE: 'COLLECTIONS_OWNED_COMMENTS_DELETE', DELETE: 'COLLECTIONS_OWNED_COMMENTS_DELETE',
}, },
@@ -116,6 +120,10 @@ export class ClaimDefinition {
READ: 'COLLECTIONS_UNOWNED_READ', READ: 'COLLECTIONS_UNOWNED_READ',
UPDATE: 'COLLECTIONS_UNOWNED_UPDATE', UPDATE: 'COLLECTIONS_UNOWNED_UPDATE',
DELETE: 'COLLECTIONS_UNOWNED_DELETE', DELETE: 'COLLECTIONS_UNOWNED_DELETE',
GAME: {
ADD: 'COLLECTIONS_UNOWNED_GAME_ADD',
REMOVE: 'COLLECTIONS_UNOWNED_GAME_REMOVE',
},
}, },
}; };
public static readonly COMMENTS = { public static readonly COMMENTS = {

View File

@@ -47,3 +47,6 @@ export interface CreateCollectionRequest {
export interface UpdateCollectionRequest { export interface UpdateCollectionRequest {
name?: string; name?: string;
} }
export interface GameToCollectionRequest {
gameId: string;
}

View File

@@ -1,5 +1,6 @@
import { BadRequestError, NotFoundError, UnauthorizedError } from './errors'; import { BadRequestError, NotFoundError, UnauthorizedError } from './errors';
import { isArray } from 'lodash'; import { clamp, isArray } from 'lodash';
import { UnwrappedRequest } from './guard';
export class ErrorResponse extends Response { export class ErrorResponse extends Response {
//@ts-ignore //@ts-ignore
@@ -37,9 +38,18 @@ export class NotFoundResponse extends Response {
} }
} }
export class PagedResponse extends Response {
//@ts-ignore
constructor(request: UnwrappedRequest, body: any[]) {
const pageSize = clamp(parseInt(request.params.pageSize ?? 100), 1, 100);
const page = Math.max(0, parseInt(request.params.page ?? 1) - 1);
return new OkResponse(body.slice(page * pageSize, page * pageSize + pageSize));
}
}
export class OkResponse extends Response { export class OkResponse extends Response {
// @ts-ignore // @ts-ignore
constructor(body?: Model | null) { constructor(body?: any) {
if (body) { if (body) {
return Response.json( return Response.json(
isArray(body) isArray(body)