Skip to content

Commit effd095

Browse files
committed
add WRITE_CONTINUITY_REQUESTS scope
1 parent 84ca7dc commit effd095

File tree

10 files changed

+66
-39
lines changed

10 files changed

+66
-39
lines changed

src/entities/api-key.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import Game from './game'
33
import User from './user'
44

55
export enum APIKeyScope {
6+
WRITE_CONTINUITY_REQUESTS = 'write:continuityRequests',
67
READ_GAME_FEEDBACK = 'read:gameFeedback',
78
WRITE_GAME_FEEDBACK = 'write:gameFeedback',
89
READ_GAME_CONFIG = 'read:gameConfig',

src/middlewares/continunity-middleware.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
import { isValid } from 'date-fns'
22
import { Context, Next } from 'koa'
3+
import { APIKeyScope } from '../entities/api-key'
4+
import { isAPIRoute } from './route-middleware'
5+
import checkScope from '../policies/checkScope'
36

47
export default async (ctx: Context, next: Next): Promise<void> => {
5-
const header = ctx.headers['x-talo-continuity-timestamp']
8+
if (isAPIRoute(ctx) && checkScope(ctx.state.key, APIKeyScope.WRITE_CONTINUITY_REQUESTS)) {
9+
const header = ctx.headers['x-talo-continuity-timestamp']
610

7-
if (header) {
8-
const date = new Date(Number(header))
9-
if (isValid(date)) {
10-
ctx.state.continuityDate = date
11+
if (header) {
12+
const date = new Date(Number(header))
13+
if (isValid(date)) {
14+
ctx.state.continuityDate = date
15+
}
1116
}
1217
}
1318

src/services/api/health-check-api.service.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Response, Request, Routes } from 'koa-clay'
1+
import { Response, Routes } from 'koa-clay'
22
import APIService from './api-service'
33

44
@Routes([
@@ -7,9 +7,7 @@ import APIService from './api-service'
77
}
88
])
99
export default class HealthCheckAPIService extends APIService {
10-
async index(req: Request): Promise<Response> {
11-
console.log(req.ctx.state.continuityDate)
12-
10+
async index(): Promise<Response> {
1311
return {
1412
status: 200
1513
}

src/services/player.service.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,6 @@ export default class PlayerService extends Service {
8484
const game = await em.getRepository(Game).findOne(req.ctx.state.game)
8585

8686
const player = new Player(game)
87-
if (req.ctx.state.continuityDate) {
88-
player.createdAt = req.ctx.state.continuityDate
89-
}
90-
9187
if (aliases) {
9288
for await (const alias of aliases) {
9389
if (await em.getRepository(PlayerAlias).count({ service: alias.service, identifier: alias.identifier }) > 0) {
@@ -102,10 +98,6 @@ export default class PlayerService extends Service {
10298
const alias = new PlayerAlias()
10399
alias.service = item.service
104100
alias.identifier = item.identifier
105-
if (req.ctx.state.continuityDate) {
106-
alias.createdAt = req.ctx.state.continuityDate
107-
}
108-
109101
return alias
110102
}))
111103
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { EntityManager } from '@mikro-orm/mysql'
2+
import request from 'supertest'
3+
import createAPIKeyAndToken from '../utils/createAPIKeyAndToken'
4+
import { APIKeyScope } from '../../src/entities/api-key'
5+
import GameStatFactory from '../fixtures/GameStatFactory'
6+
import PlayerFactory from '../fixtures/PlayerFactory'
7+
import { subHours } from 'date-fns'
8+
9+
describe('Continuity middleware', () => {
10+
it('should not set the createdAt of the player stat to the continuity date if the write continuity requests scope isn\'t set', async () => {
11+
const [apiKey, token] = await createAPIKeyAndToken([APIKeyScope.WRITE_GAME_STATS])
12+
const stat = await new GameStatFactory([apiKey.game]).state(() => ({ maxValue: 999, maxChange: 99 })).one()
13+
const player = await new PlayerFactory([apiKey.game]).one()
14+
await (<EntityManager>global.em).persistAndFlush([stat, player])
15+
16+
const continuityDate = subHours(new Date(), 1)
17+
18+
const res = await request(global.app)
19+
.put(`/v1/game-stats/${stat.internalName}`)
20+
.send({ change: 10 })
21+
.auth(token, { type: 'bearer' })
22+
.set('x-talo-player', player.id)
23+
.set('x-talo-continuity-timestamp', String(continuityDate.getTime()))
24+
.expect(200)
25+
26+
expect(res.body.playerStat.createdAt).not.toBe(continuityDate.toISOString())
27+
})
28+
29+
it('should not set the createdAt of the player stat to the continuity date if the timestamp is invalid', async () => {
30+
const [apiKey, token] = await createAPIKeyAndToken([APIKeyScope.WRITE_GAME_STATS, APIKeyScope.WRITE_CONTINUITY_REQUESTS])
31+
const stat = await new GameStatFactory([apiKey.game]).state(() => ({ maxValue: 999, maxChange: 99 })).one()
32+
const player = await new PlayerFactory([apiKey.game]).one()
33+
await (<EntityManager>global.em).persistAndFlush([stat, player])
34+
35+
const continuityDate = subHours(new Date(), 1)
36+
37+
const res = await request(global.app)
38+
.put(`/v1/game-stats/${stat.internalName}`)
39+
.send({ change: 10 })
40+
.auth(token, { type: 'bearer' })
41+
.set('x-talo-player', player.id)
42+
.set('x-talo-continuity-timestamp', String(Math.ceil(continuityDate.getTime() / 1000)))
43+
.expect(200)
44+
45+
expect(res.body.playerStat.createdAt).not.toBe(continuityDate.toISOString())
46+
})
47+
})

tests/middlewares/player-auth-middleware.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ describe('Player auth middleware', () => {
125125
const stat = await new GameStatFactory([apiKey.game]).one()
126126
const player = await new PlayerFactory([apiKey.game]).withTaloAlias().one()
127127

128-
player.aliases.add(await new PlayerAliasFactory().one())
128+
player.aliases.add(await new PlayerAliasFactory(player).one())
129129

130130
await em.persistAndFlush([stat, player])
131131
const sessionToken = await player.auth.createSession(player.aliases[0])

tests/services/_api/game-feedback-api/post.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ describe('Game feedback API service - post', () => {
7979
})
8080

8181
it('should set the createdAt for the feedback to the continuity date', async () => {
82-
const [apiKey, token] = await createAPIKeyAndToken([APIKeyScope.WRITE_GAME_FEEDBACK])
82+
const [apiKey, token] = await createAPIKeyAndToken([APIKeyScope.WRITE_GAME_FEEDBACK, APIKeyScope.WRITE_CONTINUITY_REQUESTS])
8383

8484
const feedbackCategory = await new GameFeedbackCategoryFactory(apiKey.game).one()
8585
const player = await new PlayerFactory([apiKey.game]).one()

tests/services/_api/game-stat-api/put.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ describe('Game stats API service - put', () => {
231231
})
232232

233233
it('should set the createdAt of the player stat to the continuity date', async () => {
234-
const [apiKey, token] = await createAPIKeyAndToken([APIKeyScope.WRITE_GAME_STATS])
234+
const [apiKey, token] = await createAPIKeyAndToken([APIKeyScope.WRITE_GAME_STATS, APIKeyScope.WRITE_CONTINUITY_REQUESTS])
235235
const stat = await createStat(apiKey.game, { maxValue: 999, maxChange: 99 })
236236
const player = await new PlayerFactory([apiKey.game]).one()
237237
await (<EntityManager>global.em).persistAndFlush(player)

tests/services/_api/leaderboard-api/post.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ describe('Leaderboard API service - post', () => {
222222
})
223223

224224
it('should set the createdAt for the entry to the continuity date', async () => {
225-
const [apiKey, token] = await createAPIKeyAndToken([APIKeyScope.WRITE_LEADERBOARDS])
225+
const [apiKey, token] = await createAPIKeyAndToken([APIKeyScope.WRITE_LEADERBOARDS, APIKeyScope.WRITE_CONTINUITY_REQUESTS])
226226
const player = await new PlayerFactory([apiKey.game]).one()
227227
const leaderboard = await new LeaderboardFactory([apiKey.game]).one()
228228
await (<EntityManager>global.em).persistAndFlush([player, leaderboard])
@@ -241,7 +241,7 @@ describe('Leaderboard API service - post', () => {
241241
})
242242

243243
it('should update an existing entry\'s created at to the continuity date for unique leaderboards', async () => {
244-
const [apiKey, token] = await createAPIKeyAndToken([APIKeyScope.WRITE_LEADERBOARDS])
244+
const [apiKey, token] = await createAPIKeyAndToken([APIKeyScope.WRITE_LEADERBOARDS, APIKeyScope.WRITE_CONTINUITY_REQUESTS])
245245
const player = await new PlayerFactory([apiKey.game]).one()
246246
const leaderboard = await new LeaderboardFactory([apiKey.game]).state(() => ({ unique: true, sortMode: LeaderboardSortMode.DESC })).one()
247247

tests/services/_api/player-api/identify.test.ts

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Collection, EntityManager } from '@mikro-orm/mysql'
22
import request from 'supertest'
33
import { APIKeyScope } from '../../../../src/entities/api-key'
44
import PlayerFactory from '../../../fixtures/PlayerFactory'
5-
import { isToday, subHours } from 'date-fns'
5+
import { isToday } from 'date-fns'
66
import PlayerGroupFactory from '../../../fixtures/PlayerGroupFactory'
77
import PlayerGroupRule, { PlayerGroupRuleName, PlayerGroupRuleCastType } from '../../../../src/entities/player-group-rule'
88
import PlayerProp from '../../../../src/entities/player-prop'
@@ -124,20 +124,4 @@ describe('Player API service - identify', () => {
124124

125125
expect(res.body).toStrictEqual({ message: 'Player not found: Talo aliases must be created using the /v1/players/auth API' })
126126
})
127-
128-
it('should set the createdAt for the player and aliases to the continuity date', async () => {
129-
const [, token] = await createAPIKeyAndToken([APIKeyScope.READ_PLAYERS, APIKeyScope.WRITE_PLAYERS])
130-
131-
const continuityDate = subHours(new Date(), 1)
132-
133-
const res = await request(global.app)
134-
.get('/v1/players/identify')
135-
.query({ service: 'steam', identifier: 'bizboz' })
136-
.auth(token, { type: 'bearer' })
137-
.set('x-talo-continuity-timestamp', String(continuityDate.getTime()))
138-
.expect(200)
139-
140-
expect(res.body.alias.createdAt).toBe(continuityDate.toISOString())
141-
expect(res.body.alias.player.createdAt).toBe(continuityDate.toISOString())
142-
})
143127
})

0 commit comments

Comments
 (0)