bastienrobert/la-ferme

View on GitHub
packages/server/app/graphql/resolvers/game.ts

Summary

Maintainability
A
3 hrs
Test Coverage
import { Collection } from 'bookshelf'
import { withFilter } from 'apollo-server'
import { GAME, PLAYER, ROUND } from '@la-ferme/shared/constants'
import { NOT_ALLOWED } from '@la-ferme/shared/errors'
import { characters, goals, skills } from '@la-ferme/shared/data'
import {
  GameStatusType,
  GameGlobalStatisticsName
} from '@la-ferme/shared/typings'

import pubsub from '@/app/pubsub'
import Room from '@/app/models/Room'
import Game from '@/app/models/Game'
import User from '@/app/models/User'
import Skill from '@/app/models/Skill'
import Player from '@/app/models/Player'

import { connections } from '@/app/stores'
import getStatistics from '@/app/engine/getStatistics'

import getAndSplice from '@/app/helpers/getAndSplice'
import formatPlayers from '@/app/helpers/formatPlayers'

const resolvers = {
  GameStatusType: {
    START: GameStatusType.Start,
    END: GameStatusType.End,
    READY: GameStatusType.Ready,
    ROUND: GameStatusType.Round
  },
  GameGlobalStatisticsName: {
    CIVIL: GameGlobalStatisticsName.Civil,
    UNCIVIL: GameGlobalStatisticsName.Uncivil
  },
  GameStatus: {
    __resolveType({ type }) {
      switch (type) {
        case GameStatusType.End:
          return 'GameStatusWon'
        case GameStatusType.Ready:
          return 'GameStatusReady'
        case GameStatusType.Round:
          return 'GameStatusRound'
        default:
          return 'GameStatusDefault'
      }
    }
  },
  Query: {
    async getReadyPlayers(_, { gameUUID }) {
      const game = await Game.findByUUID(gameUUID, {
        withRelated: ['players']
      })

      const players = game.related<Player>('players') as Collection<Player>
      const formattedPlayers = await formatPlayers(players)

      return formattedPlayers
    }
  },
  Mutation: {
    // set game as ready
    async startGame(_, { playerUUID }) {
      const player = await Player.findByUUID(playerUUID, {
        withRelated: [
          'user',
          'game',
          'game.creator',
          'game.players',
          'game.players.user'
        ]
      })

      const game = player.related('game') as Game
      const user = player.related('user') as User
      const creator = game.related('creator') as User

      if (creator.uuid !== user.uuid) throw new Error(NOT_ALLOWED)
      game.start()
      await game.save()

      const players = game.related('players') as Collection<Player>

      const getCharacter = getAndSplice(characters)
      const getSkill = getAndSplice(skills)
      const getGoal = getAndSplice(goals)

      await Promise.all(
        players.map(async p => {
          const skill = new Skill({
            player_id: p.id
          })
          skill.name = getSkill()
          await skill.save()

          p.character = getCharacter()
          p.goal = getGoal()

          return p.save()
        })
      )

      const formattedPlayer = await formatPlayers(players)

      pubsub.publish(GAME.START, {
        gameUpdated: {
          gameUUID: game.uuid,
          type: GameStatusType.Start,
          players: formattedPlayer
        }
      })

      return true
    },
    async stopGame(_, { winnerUUID }) {
      const winner = await Player.findByUUID(winnerUUID, {
        withRelated: ['game', 'game.room', 'game.players', 'game.players.user']
      })
      const game = winner.related('game') as Game
      const room = game.related('room') as Room

      game.winner = winner.id
      await game.save()

      const players = game.related('players') as Collection<Player>
      const statistics = await getStatistics(game, { players, winner })

      const [numberOfRounds, formattedPlayer] = await Promise.all([
        game.numberOfRounds(),
        formatPlayers(players)
      ])

      connections.getByBoxID(room.boxID).forEach((__, key) => {
        connections.reset(key)
      })

      pubsub.publish(GAME.STOP, {
        gameUpdated: {
          gameUUID: game.uuid,
          type: GameStatusType.End,
          numberOfRounds,
          winnerUUID,
          statistics,
          players: formattedPlayer
        }
      })

      return true
    }
  },
  Subscription: {
    gameUpdated: {
      subscribe: withFilter(
        () =>
          pubsub.asyncIterator([
            GAME.START,
            GAME.STOP,
            PLAYER.READY,
            ROUND.UPDATE
          ]),
        ({ gameUpdated }, variables) => {
          return gameUpdated.gameUUID === variables.gameUUID
        }
      )
    }
  }
}

export default resolvers