added some token usage, has errors

This commit is contained in:
OkaykOrhmn 2025-07-23 16:45:04 +03:30
parent d2e3063094
commit f3d0659c39
4 changed files with 108 additions and 140 deletions

View File

@ -1,9 +1,11 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { Injectable, InternalServerErrorException, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Answer } from './entity/answer.entity';
import { CreateAnswerDto } from './dto/create-answer.dto';
import { UpdateAnswerDto } from './dto/update-answer.dto';
import { CreateAttachmentDto } from '../attachment/dto/create_attachment.dto';
import { Attachment } from '../attachment/entity/attachment.entity';
@Injectable()
export class AnswerService {
@ -12,13 +14,15 @@ export class AnswerService {
private readonly answerRepository: Repository<Answer>,
) {}
async create(data: CreateAnswerDto): Promise<Answer> {
try {
const answer = this.answerRepository.create({ ...data });
return await this.answerRepository.save(answer);
} catch (error) {
throw new Error(`Failed to create answer: ${error.message}`);
async create(data: CreateAnswerDto, token: any): Promise<Answer> {
if (!token || !token.sub) {
throw new InternalServerErrorException('Failed to extract sub from token');
}
const answer = this.answerRepository.create({
...data,
participantId: token.sub, // token.sub is userId, but we want participantId ?!
});
return await this.answerRepository.save(answer);
}
async findAll( page = 1, limit = 10 ): Promise<{

View File

@ -14,47 +14,17 @@ export class AttachmentService {
private readonly attachmentRepository: Repository<Attachment>,
) {}
async create(data: CreateAttachmentDto): Promise<Attachment> {
try {
// Here we send a sample request, in final implementation, there'll be no need for that, we'll get the decoded token
const authResponse = await axios.post(
'https://auth.didvan.com/realms/didvan/protocol/openid-connect/token',
new URLSearchParams({
client_id: 'didvan-app',
username: 'bob',
password: 'developer_password',
grant_type: 'password',
}),
{
headers: {
'content-type': 'application/x-www-form-urlencoded',
},
},
);
// Decode the access token to extract the 'sub' claim
const token = authResponse.data.access_token;
const decodedToken: any = decode(token);
if (!decodedToken || !decodedToken.sub) {
throw new InternalServerErrorException('Failed to decode token or extract sub');
}
// Create the attachment with ownerId set to sub
const attachment = this.attachmentRepository.create({
...data,
ownerId: decodedToken.sub, // Set ownerId to the token's sub
metadata: data.metadata
? { entries: data.metadata.entries }
: { entries: [] },
status: data.status || 'active',
});
// Save the attachment to the database
return await this.attachmentRepository.save(attachment);
} catch (error) {
throw new InternalServerErrorException(`Failed to create attachment: ${error.message}`);
async create(data: CreateAttachmentDto, token: any): Promise<Attachment> {
// Extract sub from decoded token
if (!token || !token.sub) {
throw new InternalServerErrorException('Failed to extract sub from token');
}
// Create the attachment with ownerId set to token.sub
const attachment = this.attachmentRepository.create({
...data,
ownerId: token.sub, // Set ownerId to the token's sub
});
return await this.attachmentRepository.save(attachment);
}
async findAll(

View File

@ -23,7 +23,7 @@ export class FormResultController {
}
@Get(':id')
async findOne(
async findOne( // just gets the data in database
@Param('id', new ParseUUIDPipe()) id: string,
): Promise<FormResult | null> {
return this.formResultService.findById(id);
@ -44,10 +44,10 @@ export class FormResultController {
return this.formResultService.remove(id);
}
// @Get('form/:formId/statistics')
// async getFormStatistics(
// @Param('formId', new ParseUUIDPipe()) formId: string,
// ): Promise<FormResult> {
// return this.formResultService.getFormStatistics(formId);
// }
@Get(':id/refresh')
async getFormStatistics( // updates data in database and then returns
@Param('formId', new ParseUUIDPipe()) formId: string,
): Promise<FormResult> {
return this.formResultService.getFormStatistics(formId);
}
}

View File

@ -1,12 +1,14 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { FormResult } from './entity/formResult.entity';
import { CreateFormResultDto } from './dto/create-formResult.dto';
import { UpdateFormResultDto } from './dto/update-formResult.dto';
import { Answer } from 'src/answer/entity/answer.entity';
import { Question } from '../question/entity/question.entity';
import { Form } from '../form/entity/form.entity';
import { FormSection } from '../formSection/entity/formSection.entity';
import { FormPage } from '../formPage/entity/formPage.entity';
import { Repository, In } from 'typeorm';
@Injectable()
export class FormResultService {
@ -19,6 +21,15 @@ export class FormResultService {
private readonly questionRepository: Repository<Question>,
@InjectRepository(Answer)
private readonly answerRepository: Repository<Answer>,
@InjectRepository(Form)
@InjectRepository(FormPage)
private readonly formPageRepository: Repository<FormPage>,
@InjectRepository(FormSection)
private readonly formSectionRepository: Repository<FormSection>,
@InjectRepository(Question)
@InjectRepository(Answer)
@InjectRepository(FormResult)
private readonly formResultRepository: Repository<FormResult>,
) {}
async create(data: CreateFormResultDto): Promise<FormResult> {
@ -94,90 +105,73 @@ export class FormResultService {
throw new NotFoundException(`FormResult with ID "${id}" not found`);
}
}
//
// async getFormStatistics(formId: string): Promise<FormResult> {
// // 1. Find the Form entity
// const form = await this.formRepository.findOne({ where: { id: formId } });
// if (!form) {
// throw new NotFoundException(`Form with ID "${formId}" not found`);
// }
//
// // 2. Compute numQuestions
// const numQuestions = await this.questionRepository.count({
// where: {
// formSection: {
// formPage: {
// form: { id: formId },
// },
// },
// },
// });
//
// // 3. Compute numParticipants
// const numParticipantsResult = await this.answerRepository
// .createQueryBuilder('answer')
// .select('COUNT(DISTINCT answer.participantId)', 'count')
// .innerJoin('answer.question', 'question')
// .innerJoin('question.formSection', 'formSection')
// .innerJoin('formSection.formPage', 'formPage')
// .where('formPage.formId = :formId', { formId })
// .getRawOne();
// const numParticipants = numParticipantsResult ? Number(numParticipantsResult.count) : 0;
//
// // 4. Compute numAnswers
// const numAnswers = await this.answerRepository.count({
// where: {
// question: {
// formSection: {
// formPage: {
// form: { id: formId },
// },
// },
// },
// },
// });
//
// // 5. Compute numCompleteParticipants
// const subQuery = this.answerRepository
// .createQueryBuilder('answer')
// .select('answer.participantId')
// .innerJoin('answer.question', 'question')
// .innerJoin('question.formSection', 'formSection')
// .innerJoin('formSection.formPage', 'formPage')
// .where('formPage.formId = :formId', { formId })
// .groupBy('answer.participantId')
// .having('COUNT(DISTINCT answer.questionId) = :numQuestions', { numQuestions });
//
// const numCompleteParticipantsResult = await this.answerRepository.manager
// .createQueryBuilder()
// .select('COUNT(*)', 'count')
// .from(`(${subQuery.getQuery()})`, 'subquery')
// .setParameters(subQuery.getParameters())
// .getRawOne();
// const numCompleteParticipants = numCompleteParticipantsResult ? Number(numCompleteParticipantsResult.count) : 0;
//
// // 6. Find or create FormResult for the Form
// let formResult = await this.formResultRepo.findOne({ where: { form: { id: formId } }, relations: ['form'] });
// if (!formResult) {
// formResult = this.formResultRepo.create({
// form, // Link to the Form entity
// numQuestions,
// numParticipants,
// numAnswers,
// numComplete: numCompleteParticipants,
// opinions: [], // Initialize as empty or compute if needed
// status: 'active',
// });
// } else {
// formResult.numQuestions = numQuestions;
// formResult.numParticipants = numParticipants;
// formResult.numAnswers = numAnswers;
// formResult.numComplete = numCompleteParticipants;
// // Preserve existing opinions or update if needed
// }
//
// // 7. Save and return the FormResult
// return await this.formResultRepo.save(formResult);
// }
async getFormStatistics(formId: string): Promise<FormResult> {
// 1. Find the Form entity
const form = await this.formRepository.findOne({ where: { id: formId } });
if (!form) {
throw new NotFoundException(`Form with ID "${formId}" not found`);
}
// 2. Compute numQuestions
const pageIds = form.pageIds || [];
const formPages = await this.formPageRepository.find({ where: { id: In(pageIds) } });
const formSectionIds = formPages.flatMap(page => page.formSectionIds || []);
const formSections = await this.formSectionRepository.find({ where: { id: In(formSectionIds) } });
const questionIds = formSections.flatMap(section => section.questionIds || []);
const numQuestions = questionIds.length;
// 3. Compute numParticipants (distinct participantId from answers)
const numParticipantsResult = await this.answerRepository
.createQueryBuilder('answer')
.select('COUNT(DISTINCT answer.participantId)', 'count')
.where('answer.questionId IN (:...questionIds)', { questionIds: questionIds.length ? questionIds : ['none'] }) // Handle empty questionIds
.getRawOne();
const numParticipants = numParticipantsResult ? Number(numParticipantsResult.count) : 0;
// 4. Compute numAnswers
const numAnswers = await this.answerRepository.count({
where: { questionId: In(questionIds.length ? questionIds : ['none']) }, // Handle empty questionIds
});
// 5. Compute numCompleteParticipants (participants who answered all questions)
const subQuery = this.answerRepository
.createQueryBuilder('answer')
.select('answer.participantId')
.where('answer.questionId IN (:...questionIds)', { questionIds: questionIds.length ? questionIds : ['none'] }) // Handle empty questionIds
.groupBy('answer.participantId')
.having('COUNT(DISTINCT answer.questionId) = :numQuestions', { numQuestions: numQuestions || 1 }); // Avoid division by zero
const numCompleteParticipantsResult = await this.answerRepository.manager
.createQueryBuilder()
.select('COUNT(*)', 'count')
.from(`(${subQuery.getQuery()})`, 'subquery')
.setParameters(subQuery.getParameters())
.getRawOne();
const numCompleteParticipants = numCompleteParticipantsResult ? Number(numCompleteParticipantsResult.count) : 0;
// 6. Find or create FormResult for the Form
let formResult = await this.formResultRepository.findOne({ where: { formId } });
if (!formResult) {
formResult = this.formResultRepository.create({
formId, // Link to Form via formId
numQuestions,
numParticipants,
numAnswers,
numComplete: numCompleteParticipants,
opinions: [], // Initialize as empty or compute if needed
status: 'active',
});
} else {
formResult.numQuestions = numQuestions;
formResult.numParticipants = numParticipants;
formResult.numAnswers = numAnswers;
formResult.numComplete = numCompleteParticipants;
// Preserve existing opinions or update if needed
}
// 7. Save and return the FormResult
return await this.formResultRepository.save(formResult);
}
}