Skip to content

Commit e8f165a

Browse files
francoijsnicolodavis
authored andcommitted
server: add new API endpoints 'list' and 'leave' (#276)
* server: add new API endpoints 'list' and 'leave' * server: add implementation for new API 'list' and 'remove' in all supported DB
1 parent d1a1a8a commit e8f165a

8 files changed

Lines changed: 472 additions & 57 deletions

File tree

src/server/api.js

Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ import { CreateGameReducer } from '../core/reducer';
1717

1818
const createCredentials = () => uuid();
1919
const getGameMetadataKey = gameID => `${gameID}:metadata`;
20+
const isGameMetadataKey = (key, gameName) =>
21+
key.match(gameName + ':.*:metadata');
22+
const getNamespacedGameID = (gameID, gameName) => `${gameName}:${gameID}`;
2023
const getNewGameInstanceID = () => uuid();
2124
const createGameMetadata = () => ({
2225
players: {},
@@ -90,7 +93,7 @@ export const createApiServer = ({ db, games }) => {
9093
}
9194

9295
const gameID = getNewGameInstanceID();
93-
const namespacedGameID = `${gameName}:${gameID}`;
96+
const namespacedGameID = getNamespacedGameID(gameID, gameName);
9497

9598
await db.set(getGameMetadataKey(namespacedGameID), gameMetadata);
9699
await db.set(namespacedGameID, state);
@@ -100,17 +103,47 @@ export const createApiServer = ({ db, games }) => {
100103
};
101104
});
102105

106+
router.get('/games/:name', async ctx => {
107+
const gameName = ctx.params.name;
108+
const gameList = await db.list();
109+
let gameInstances = [];
110+
for (let key of Array.from(gameList)) {
111+
if (isGameMetadataKey(key, gameName)) {
112+
const gameID = key.slice(
113+
gameName.length + 1,
114+
key.lastIndexOf(':metadata')
115+
);
116+
const metadata = await db.get(key);
117+
gameInstances.push({
118+
gameID: gameID,
119+
players: Object.values(metadata.players).map(player => {
120+
// strip away credentials
121+
return { id: player.id, name: player.name };
122+
}),
123+
});
124+
}
125+
}
126+
ctx.body = {
127+
gameInstances: gameInstances,
128+
};
129+
});
130+
103131
router.post('/games/:name/:id/join', koaBody(), async ctx => {
104132
const gameName = ctx.params.name;
105133
const gameID = ctx.params.id;
106134
const playerID = ctx.request.body.playerID;
107135
const playerName = ctx.request.body.playerName;
108-
109-
const namespacedGameID = `${gameName}:${gameID}`;
136+
const namespacedGameID = getNamespacedGameID(gameID, gameName);
110137
const gameMetadata = await db.get(getGameMetadataKey(namespacedGameID));
111138

112-
if (gameMetadata === null) {
113-
ctx.throw(404, 'Game not found');
139+
if (!gameMetadata) {
140+
ctx.throw(404, 'Game ' + gameID + ' not found');
141+
}
142+
if (!gameMetadata.players[playerID]) {
143+
ctx.throw(404, 'Player ' + playerID + ' not found');
144+
}
145+
if (gameMetadata.players[playerID].name) {
146+
ctx.throw(409, 'Player ' + playerID + ' not available');
114147
}
115148

116149
gameMetadata.players[playerID].name = playerName;
@@ -123,6 +156,37 @@ export const createApiServer = ({ db, games }) => {
123156
};
124157
});
125158

159+
router.post('/games/:name/:id/leave', koaBody(), async ctx => {
160+
const gameName = ctx.params.name;
161+
const gameID = ctx.params.id;
162+
const playerID = ctx.request.body.playerID;
163+
const playerCredentials = ctx.request.body.playerCredentials;
164+
const namespacedGameID = getNamespacedGameID(gameID, gameName);
165+
const gameMetadata = await db.get(getGameMetadataKey(namespacedGameID));
166+
167+
if (!gameMetadata) {
168+
ctx.throw(404, 'Game ' + gameID + ' not found');
169+
}
170+
if (!gameMetadata.players[playerID]) {
171+
ctx.throw(404, 'Player ' + playerID + ' not found');
172+
}
173+
if (
174+
playerCredentials !== gameMetadata.players[playerID].playerCredentials
175+
) {
176+
ctx.throw(403, 'Invalid credentials ' + playerCredentials);
177+
}
178+
179+
delete gameMetadata.players[playerID].name;
180+
if (Object.values(gameMetadata.players).some(val => val.name)) {
181+
await db.set(getGameMetadataKey(namespacedGameID), gameMetadata);
182+
} else {
183+
// remove game
184+
await db.remove(gameID);
185+
await db.remove(getGameMetadataKey(namespacedGameID));
186+
}
187+
ctx.body = {};
188+
});
189+
126190
app.use(cors());
127191

128192
// If API_SECRET is set, then require that requests set an

0 commit comments

Comments
 (0)