databases interaction fixed

This commit is contained in:
OkaykOrhmn 2025-07-23 11:18:47 +03:30
parent af6d37c7ca
commit aeb7a73c4c
11 changed files with 176 additions and 49 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 { Attachment } from './entity/attachment.entity';
import { CreateAttachmentDto } from './dto/create_attachment.dto';
import { UpdateAttachmentDto } from './dto/update_attachment.dto';
import axios from 'axios';
import { decode } from 'jsonwebtoken';
@Injectable()
export class AttachmentService {
@ -14,14 +16,44 @@ export class AttachmentService {
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,
metadata: data.metadata || { entries: [] },
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 Error(`Failed to create attachment: ${error.message}`);
throw new InternalServerErrorException(`Failed to create attachment: ${error.message}`);
}
}

View File

@ -16,5 +16,6 @@ export class CreateAttachmentDto extends BaseDto {
storageUrl: string;
@IsUUID()
ownerId: string;
@IsOptional()
ownerId?: string;
}

View File

@ -2,6 +2,7 @@ import { Column, Entity, ManyToOne } from 'typeorm';
import { BaseEntity } from 'src/_core/entity/_base.entity';
import { Question } from 'src/question/entity/question.entity';
import { FormSection } from '../../formSection/entity/formSection.entity';
import { Form } from '../../form/entity/form.entity';
@Entity({ name: 'attachments' })
export class Attachment extends BaseEntity {
@ -30,4 +31,7 @@ export class Attachment extends BaseEntity {
@ManyToOne(() => FormSection, formSection => formSection.attachments)
formSection: FormSection;
@ManyToOne(() => Form, form => form.attachments)
form: Form;
}

View File

@ -12,6 +12,7 @@ import { FormSection } from '../formSection/entity/formSection.entity';
import { FormPage } from '../formPage/entity/formPage.entity';
import { FormResult } from '../formResult/entity/formResult.entity';
import { ParticipantGroup } from '../participantGroup/entity/participantGroup.entity';
import { BaseEntity } from '../_core/entity/_base.entity';
export const typeOrmConfig: TypeOrmModuleOptions = {
type: 'postgres',
@ -19,8 +20,9 @@ export const typeOrmConfig: TypeOrmModuleOptions = {
port: 5433,
username: 'postgres',
password: '1111',
database: 'postgres',
database: 'db',
entities: [
BaseEntity,
Participant,
Metadata,
Form,

View File

@ -21,10 +21,12 @@ export class CreateFormDto extends BaseDto {
@IsArray()
@ValidateNested({ each: true })
@Type(() => CreateFormPageDto)
pages: CreateFormPageDto[];
@IsOptional()
pages?: CreateFormPageDto[];
@IsArray()
@ValidateNested({ each: true })
@Type(() => CreateParticipantDto)
participants: CreateParticipantDto[];
@IsOptional()
participants?: CreateParticipantDto[];
}

View File

@ -3,6 +3,7 @@ import { BaseEntity } from '../../_core/entity/_base.entity';
import { FormPage } from '../../formPage/entity/formPage.entity';
import { Participant } from '../../participant/entity/participant.entity';
import { Realm } from '../../realm/entity/realm.entity';
import { Attachment } from '../../attachment/entity/attachment.entity';
@Entity()
export class Form extends BaseEntity {
@ -20,23 +21,12 @@ export class Form extends BaseEntity {
@OneToMany(() => Participant, participant => participant.form, { cascade: true, eager: true })
participants: Participant[];
// Many-to-One relationship with Realm (ADD THIS)
@ManyToOne(() => Realm, realm => realm.forms)
// One-to-Many relationship with Attachment
@OneToMany(() => Attachment, attachment => attachment.form, { cascade: true, eager: true })
attachments: Attachment[];
@ManyToOne(() => Realm, realm => realm.forms, { /*cascade: true, eager: true */})
realm: Realm;
// Attachments are typically stored in a separate table and linked via a many-to-many or one-to-many
// For simplicity, if attachments are embedded, they would be handled like DisplayCondition in FormSection.
// If they are separate entities, they would need a relationship defined here.
// Assuming attachments are now handled as a separate entity with a relationship if needed.
// If attachments were previously embedded, you'd need to define a class for them and use @Column(() => AttachmentClass)
// For now, removing the direct 'attachments' column from Form as it was an array of AttachmentSchema (Mongoose).
// If you need attachments directly on Form, please provide the Attachment entity structure.
// @Prop({
// type: [AttachmentSchema],
// default: [],
// })
// attachments: Attachment[];
}

46
src/mock/alice.mock.json Normal file
View File

@ -0,0 +1,46 @@
{
"token": {
"exp": 1753279286,
"iat": 1753250486,
"jti": "onrtro:4cee0913-cbe6-89c0-d4bb-dac3d8e29b04",
"iss": "https://auth.didvan.com/realms/didvan",
"aud": "account",
"sub": "010be0e5-fe3d-4686-b093-8fe37ccdc3c9",
"typ": "Bearer",
"azp": "didvan-app",
"sid": "be164904-009b-450e-98d5-553073af0fcb",
"acr": "1",
"allowed-origins": [
"https://didvan.com",
"https://web.didvan.com",
"http://localhost:3000"
],
"realm_access": {
"roles": [
"delphi-user",
"offline_access",
"uma_authorization",
"default-roles-didvan"
]
},
"resource_access": {
"account": {
"roles": [
"manage-account",
"manage-account-links",
"view-profile"
]
}
},
"scope": "email profile",
"email_verified": true,
"name": "alice alice_lastname",
"preferred_username": "alice",
"given_name": "alice",
"family_name": "alice_lastname",
"email": "alice@gmail.com"
},
"data": {
}
}

View File

@ -16,12 +16,12 @@ export class ParticipantService {
async create(data: CreateParticipantDto): Promise<Participant> {
try {
// Make the authentication request to get the token
// 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: 'alice',
username: 'bob',
password: 'developer_password',
grant_type: 'password',
}),
@ -39,16 +39,21 @@ export class ParticipantService {
throw new InternalServerErrorException('Failed to decode token or extract sub');
}
// Create the participant with the 'sub' as the ID and userId
// Check if participant with this sub already exists
const existingParticipant = await this.participantRepository.findOne({ where: { id: decodedToken.sub } });
if (existingParticipant) {
throw new InternalServerErrorException(`Participant with ID ${decodedToken.sub} already exists`);
}
// Create the participant with the 'sub' as the ID
const participant = this.participantRepository.create({
...data,
id: decodedToken.sub, // Set the ID from the token's sub
// id: , let it assign a random id
userId: decodedToken.sub, // Set userId to the same sub
metadata: data.metadata
? { entries: data.metadata.entries } // Ensure metadata is properly formatted
? { entries: data.metadata.entries }
: { entries: [] },
status: data.status || 'active',
// Note: form and realm are not set as they are optional (ManyToOne)
});
// Save the participant to the database

View File

@ -20,9 +20,10 @@ export class CreateRealmDto extends BaseDto {
@IsArray()
@ValidateNested({ each: true })
@Type(() => CreateParticipantDto)
participants: CreateParticipantDto[]; // Changed from 'participant' to 'participants' for consistency with array
@IsOptional()
participants?: CreateParticipantDto[];
@ValidateNested() // Not IsArray, as it's a single owner
@Type(() => CreateParticipantDto)
owner: CreateParticipantDto;
// @ValidateNested() // Not IsArray, as it's a single owner
// @Type(() => CreateParticipantDto)
// owner: CreateParticipantDto;
}

View File

@ -1,4 +1,4 @@
import { Entity, Column, OneToMany, OneToOne, JoinColumn } from 'typeorm';
import { Entity, Column, OneToMany, OneToOne, JoinColumn} from 'typeorm';
import { BaseEntity } from '../../_core/entity/_base.entity';
import { Form } from '../../form/entity/form.entity';
import { Participant } from '../../participant/entity/participant.entity';
@ -11,17 +11,14 @@ export class Realm extends BaseEntity {
@Column()
description: string;
// One-to-Many relationship with Form
@OneToMany(() => Form, form => form.realm, { cascade: true, eager: true })
forms: Form[];
// One-to-Many relationship with Participant (for general participants in the realm)
@OneToMany(() => Participant, participant => participant.realm, { cascade: true, eager: true })
participants: Participant[];
// One-to-One relationship with Participant (for the owner of the realm)
// Assuming 'owner' is a single Participant entity
@OneToOne(() => Participant, { cascade: true, eager: true })
@JoinColumn() // This column will be added to the Realm table to store the owner's ID
@JoinColumn({ name: 'ownerId', referencedColumnName: 'userId' }) // be explicit
owner: Participant;
}

View File

@ -1,23 +1,73 @@
// src/realm/realm.service.ts
import { Injectable, NotFoundException } from '@nestjs/common';
import { Injectable, NotFoundException, InternalServerErrorException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Realm } from './entity/realm.entity';
import { CreateRealmDto } from './dto/create-realm.dto';
import { UpdateRealmDto } from './dto/update-realm.dto';
import { Participant } from '../participant/entity/participant.entity';
import axios from 'axios';
import { decode } from 'jsonwebtoken';
@Injectable()
export class RealmService {
constructor(
@InjectRepository(Realm)
private readonly realmRepo: Repository<Realm>,
@InjectRepository(Participant)
private readonly participantRepo: Repository<Participant>,
) {}
async create(data: CreateRealmDto): Promise<Realm> {
const realm = this.realmRepo.create({ ...data });
return await this.realmRepo.save(realm);
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');
}
// Find or create a participant with userId set to sub
let participant = await this.participantRepo.findOne({ where: { userId: decodedToken.sub } });
if (!participant) {
participant = this.participantRepo.create({
displayName: data.title + ' Owner', // Default displayName
role: 'admin', // Default role for owner
userId: decodedToken.sub,
metadata: data.metadata ? { entries: data.metadata.entries } : { entries: [] },
// status: 'active',
});
participant = await this.participantRepo.save(participant);
}
// Create the realm with the participant as the owner
const realm = this.realmRepo.create({
...data,
owner: participant, // Set owner to the participant with userId = sub
});
return await this.realmRepo.save(realm);
} catch (error) {
throw new InternalServerErrorException(`Failed to create realm: ${error.message}`);
}
}
// Other methods (findAll, findById, update, remove) remain unchanged
async findAll(
page = 1,
limit = 10,
@ -83,10 +133,7 @@ export class RealmService {
return realm;
}
async update(
id: string,
data: UpdateRealmDto,
): Promise<Realm | null> {
async update(id: string, data: UpdateRealmDto): Promise<Realm | null> {
const result = await this.realmRepo.update(
{ id },
{ ...data, updatedAt: new Date() },