MemberRouter.ts 7.28 KB
Newer Older
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
1 2 3 4 5 6 7 8 9 10 11 12
import { DeleteWriteOpResultObject } from 'mongodb';
import {
  BadRequestError, BodyParam, Delete, Get, HeaderParam, InternalServerError, JsonController, Param, Post, Put, UnauthorizedError,
} from 'routing-controllers';
import MemberDAO from '../../db/MemberDAO';
import QuizDAO from '../../db/quiz/QuizDAO';
import { MemberEntity } from '../../entities/member/MemberEntity';
import { MessageProtocol, StatusProtocol } from '../../enums/Message';
import { IMemberSerialized } from '../../interfaces/entities/Member/IMemberSerialized';
import { IQuizEntity } from '../../interfaces/quizzes/IQuizEntity';
import { MemberModel } from '../../models/member/MemberModel';
import { AuthService } from '../../services/AuthService';
13
import LoggerService from '../../services/LoggerService';
14 15 16 17 18
import { AbstractRouter } from './AbstractRouter';

@JsonController('/api/v1/member')
export class MemberRouter extends AbstractRouter {

Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
  @Get('/')
  public getMembers(): Array<IMemberSerialized> {
    return MemberDAO.storage.map(member => member.serialize());
  }

  @Post('/token')
  public generateMemberToken(@BodyParam('name') name: string, @BodyParam('quizName') quizName: string): string {
    return AuthService.createToken({
      name,
      quizName,
    });
  }

  @Get('/available/:quizName')
  public getAvailableMemberNames(@Param('quizName') quizName: string): Array<string> {
    const quiz = QuizDAO.getQuizByName(quizName);
    const nicks = JSON.parse(JSON.stringify(quiz.sessionConfig.nicks.selectedNicks));

    MemberDAO.getMembersOfQuiz(quizName).forEach(val => nicks.splice(nicks.indexOf(val.name), 1));

    return nicks;
  }

42
  @Put('/')
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
43 44 45 46 47
  public async addMember(
    @BodyParam('member') member: IMemberSerialized, //
    @HeaderParam('authorization') token: string, //
  ): Promise<object> {
    const activeQuiz: IQuizEntity = QuizDAO.getActiveQuizByName(member.currentQuizName);
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
48

Fullarton's avatar
Fullarton committed
49
    if (!activeQuiz) {
50
      throw new InternalServerError(JSON.stringify({
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
51 52
        status: StatusProtocol.Failed,
        step: MessageProtocol.IsInactive,
53
        payload: {},
Fullarton's avatar
Fullarton committed
54 55
      }));
    }
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
56

Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
57
    if (!member.name || (activeQuiz.sessionConfig.nicks.restrictToCasLogin && !member.ticket)) {
58
      throw new InternalServerError(JSON.stringify({
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
59 60
        status: StatusProtocol.Failed,
        step: MessageProtocol.InvalidParameters,
61
        payload: {},
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
62 63
      }));
    }
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
64

Fullarton's avatar
Fullarton committed
65
    try {
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
66 67
      if (!member.groupName) {
        member.groupName = 'Default';
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
68 69
      }

Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
70 71 72 73 74 75 76 77 78
      member.token = token;

      const memberValidator = new MemberModel(new MemberEntity(member).serialize());

      const results = memberValidator.validateSync();

      if (results) {
        throw results;
      }
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
79

Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
80
      await memberValidator.save();
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
81

82
      return {
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
83 84
        status: StatusProtocol.Success,
        step: MessageProtocol.Added,
85
      };
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
86

Fullarton's avatar
Fullarton committed
87
    } catch (ex) {
88
      LoggerService.error('Cannot add member', ex.message);
89
      throw new InternalServerError(JSON.stringify({
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
90 91
        status: StatusProtocol.Failed,
        step: MessageProtocol.Added,
92
        payload: { message: ex.message },
Fullarton's avatar
Fullarton committed
93 94 95 96
      }));
    }
  }

97
  @Put('/reading-confirmation')
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
98
  public addReadingConfirmation(@HeaderParam('authorization') token: string, //
99 100
  ): object {

Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
101 102 103 104
    const member = MemberDAO.getMemberByToken(token);
    const quiz = QuizDAO.getActiveQuizByName(member.currentQuizName);
    if (!member || !quiz) {
      throw new UnauthorizedError(MessageProtocol.InsufficientPermissions);
Fullarton's avatar
Fullarton committed
105
    }
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
106 107 108

    member.setReadingConfirmation();

109
    return {
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
110 111
      status: StatusProtocol.Success,
      step: MessageProtocol.ReadingConfirmationRequested,
112
      payload: {},
113
    };
Fullarton's avatar
Fullarton committed
114 115
  }

116 117
  @Put('/confidence-value')
  public addConfidenceValue(
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
118
    @HeaderParam('authorization') token: string, //
119 120 121
    @BodyParam('confidenceValue') confidenceValue: number, //
  ): object {

Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
122 123 124 125
    const member = MemberDAO.getMemberByToken(token);
    const quiz = QuizDAO.getActiveQuizByName(member.currentQuizName);
    if (!member || !quiz) {
      throw new UnauthorizedError(MessageProtocol.InsufficientPermissions);
Fullarton's avatar
Fullarton committed
126
    }
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
127 128 129

    member.setConfidenceValue(confidenceValue);

130
    return {
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
131 132
      status: StatusProtocol.Success,
      step: MessageProtocol.ConfidenceValueRequested,
133
      payload: {},
134
    };
Fullarton's avatar
Fullarton committed
135 136
  }

137 138 139
  @Put('/response')
  public addResponse(
    @HeaderParam('authorization') token: string, //
140
    @BodyParam('response', { required: false }) value: string, //
141 142 143 144 145 146 147 148 149 150 151 152
  ): object {

    if (!Array.isArray(value) && !['string', 'number'].includes(typeof value)) {
      throw new BadRequestError(MessageProtocol.InvalidData);
    }

    const member = MemberDAO.getMemberByToken(token);
    const quiz = QuizDAO.getActiveQuizByName(member.currentQuizName);
    if (!member || !quiz) {
      throw new UnauthorizedError(MessageProtocol.InsufficientPermissions);
    }

153 154 155 156 157
    if (!member.responses[quiz.currentQuestionIndex]) {
      console.error(`No responses for questionIndex ${quiz.currentQuestionIndex} found for member ${member.name} of quiz ${quiz.name}.
        Quiz has ${quiz.questionList.length} questions.`);
      throw new BadRequestError(MessageProtocol.InvalidData);
    }
158 159 160 161 162 163 164 165 166 167 168 169 170
    if (member.responses[quiz.currentQuestionIndex].responseTime > 0) {
      throw new BadRequestError(MessageProtocol.DuplicateResponse);
    }

    member.addResponseValue(value);

    return {
      status: StatusProtocol.Success,
      step: MessageProtocol.UpdatedResponse,
      payload: {},
    };
  }

171
  @Delete('/:quizName/:nickname')
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
172
  public async deleteMember(
173 174
    @Param('quizName') quizName: string, //
    @Param('nickname') nickname: string, //
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
175
  ): Promise<object> {
176

Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
177
    const activeQuiz: IQuizEntity = QuizDAO.getActiveQuizByName(quizName);
178
    if (!activeQuiz || !nickname) {
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
179
      return;
Fullarton's avatar
Fullarton committed
180
    }
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
181
    const result: DeleteWriteOpResultObject = await activeQuiz.removeMember(nickname);
182
    const response: Object = { status: result.deletedCount ? StatusProtocol.Success : StatusProtocol.Failed };
183 184 185 186 187 188 189 190 191

    Object.assign(response, {
      step: MessageProtocol.Removed,
      payload: {
        ok: result.result.ok,
        deletedCount: result.deletedCount,
      },
    });

192
    return response;
Fullarton's avatar
Fullarton committed
193 194
  }

195
  @Get('/:quizName')
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
196
  public getAllMembers(@Param('quizName') quizName: string, //
197 198
  ): object {

Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
199
    const activeQuiz: IQuizEntity = QuizDAO.getActiveQuizByName(quizName);
Fullarton's avatar
Fullarton committed
200
    if (!activeQuiz) {
201
      throw new InternalServerError(JSON.stringify({
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
202 203
        status: StatusProtocol.Failed,
        step: MessageProtocol.IsInactive,
204
        payload: {},
Fullarton's avatar
Fullarton committed
205 206
      }));
    }
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
207

208
    return {
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
209 210
      status: StatusProtocol.Success,
      step: MessageProtocol.GetPlayers,
211
      payload: {
212
        members: MemberDAO.getMembersOfQuiz(quizName).map(member => member.serialize()),
213
      },
214
    };
Fullarton's avatar
Fullarton committed
215 216
  }

Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
217 218
  @Get('/:quizName/available')
  public getRemainingNicks(@Param('quizName') quizName: string, //
219 220
  ): object {

Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
221
    const activeQuiz: IQuizEntity = QuizDAO.getActiveQuizByName(quizName);
Fullarton's avatar
Fullarton committed
222
    if (!activeQuiz) {
223
      throw new InternalServerError(JSON.stringify({
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
224 225
        status: StatusProtocol.Failed,
        step: MessageProtocol.IsInactive,
226
        payload: {},
Fullarton's avatar
Fullarton committed
227 228
      }));
    }
229 230
    const names: Array<string> = activeQuiz.sessionConfig.nicks.selectedNicks.filter((nick) => {
      return !MemberDAO.getMembersOfQuiz(activeQuiz.name).find(member => member.name === nick);
Fullarton's avatar
Fullarton committed
231
    });
232
    return {
Christopher Mark Fullarton's avatar
Christopher Mark Fullarton committed
233 234
      status: StatusProtocol.Success,
      step: MessageProtocol.GetRemainingNicks,
235
      payload: { nicknames: names },
236
    };
Fullarton's avatar
Fullarton committed
237 238
  }
}