databases interaction fixed
This commit is contained in:
parent
af6d37c7ca
commit
aeb7a73c4c
|
|
@ -1,9 +1,11 @@
|
||||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
import { Injectable, InternalServerErrorException, NotFoundException } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
import { Attachment } from './entity/attachment.entity';
|
import { Attachment } from './entity/attachment.entity';
|
||||||
import { CreateAttachmentDto } from './dto/create_attachment.dto';
|
import { CreateAttachmentDto } from './dto/create_attachment.dto';
|
||||||
import { UpdateAttachmentDto } from './dto/update_attachment.dto';
|
import { UpdateAttachmentDto } from './dto/update_attachment.dto';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { decode } from 'jsonwebtoken';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AttachmentService {
|
export class AttachmentService {
|
||||||
|
|
@ -14,14 +16,44 @@ export class AttachmentService {
|
||||||
|
|
||||||
async create(data: CreateAttachmentDto): Promise<Attachment> {
|
async create(data: CreateAttachmentDto): Promise<Attachment> {
|
||||||
try {
|
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({
|
const attachment = this.attachmentRepository.create({
|
||||||
...data,
|
...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',
|
status: data.status || 'active',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Save the attachment to the database
|
||||||
return await this.attachmentRepository.save(attachment);
|
return await this.attachmentRepository.save(attachment);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(`Failed to create attachment: ${error.message}`);
|
throw new InternalServerErrorException(`Failed to create attachment: ${error.message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,5 +16,6 @@ export class CreateAttachmentDto extends BaseDto {
|
||||||
storageUrl: string;
|
storageUrl: string;
|
||||||
|
|
||||||
@IsUUID()
|
@IsUUID()
|
||||||
ownerId: string;
|
@IsOptional()
|
||||||
|
ownerId?: string;
|
||||||
}
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ import { Column, Entity, ManyToOne } from 'typeorm';
|
||||||
import { BaseEntity } from 'src/_core/entity/_base.entity';
|
import { BaseEntity } from 'src/_core/entity/_base.entity';
|
||||||
import { Question } from 'src/question/entity/question.entity';
|
import { Question } from 'src/question/entity/question.entity';
|
||||||
import { FormSection } from '../../formSection/entity/formSection.entity';
|
import { FormSection } from '../../formSection/entity/formSection.entity';
|
||||||
|
import { Form } from '../../form/entity/form.entity';
|
||||||
|
|
||||||
@Entity({ name: 'attachments' })
|
@Entity({ name: 'attachments' })
|
||||||
export class Attachment extends BaseEntity {
|
export class Attachment extends BaseEntity {
|
||||||
|
|
@ -30,4 +31,7 @@ export class Attachment extends BaseEntity {
|
||||||
|
|
||||||
@ManyToOne(() => FormSection, formSection => formSection.attachments)
|
@ManyToOne(() => FormSection, formSection => formSection.attachments)
|
||||||
formSection: FormSection;
|
formSection: FormSection;
|
||||||
|
|
||||||
|
@ManyToOne(() => Form, form => form.attachments)
|
||||||
|
form: Form;
|
||||||
}
|
}
|
||||||
|
|
@ -12,6 +12,7 @@ import { FormSection } from '../formSection/entity/formSection.entity';
|
||||||
import { FormPage } from '../formPage/entity/formPage.entity';
|
import { FormPage } from '../formPage/entity/formPage.entity';
|
||||||
import { FormResult } from '../formResult/entity/formResult.entity';
|
import { FormResult } from '../formResult/entity/formResult.entity';
|
||||||
import { ParticipantGroup } from '../participantGroup/entity/participantGroup.entity';
|
import { ParticipantGroup } from '../participantGroup/entity/participantGroup.entity';
|
||||||
|
import { BaseEntity } from '../_core/entity/_base.entity';
|
||||||
|
|
||||||
export const typeOrmConfig: TypeOrmModuleOptions = {
|
export const typeOrmConfig: TypeOrmModuleOptions = {
|
||||||
type: 'postgres',
|
type: 'postgres',
|
||||||
|
|
@ -19,8 +20,9 @@ export const typeOrmConfig: TypeOrmModuleOptions = {
|
||||||
port: 5433,
|
port: 5433,
|
||||||
username: 'postgres',
|
username: 'postgres',
|
||||||
password: '1111',
|
password: '1111',
|
||||||
database: 'postgres',
|
database: 'db',
|
||||||
entities: [
|
entities: [
|
||||||
|
BaseEntity,
|
||||||
Participant,
|
Participant,
|
||||||
Metadata,
|
Metadata,
|
||||||
Form,
|
Form,
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,12 @@ export class CreateFormDto extends BaseDto {
|
||||||
@IsArray()
|
@IsArray()
|
||||||
@ValidateNested({ each: true })
|
@ValidateNested({ each: true })
|
||||||
@Type(() => CreateFormPageDto)
|
@Type(() => CreateFormPageDto)
|
||||||
pages: CreateFormPageDto[];
|
@IsOptional()
|
||||||
|
pages?: CreateFormPageDto[];
|
||||||
|
|
||||||
@IsArray()
|
@IsArray()
|
||||||
@ValidateNested({ each: true })
|
@ValidateNested({ each: true })
|
||||||
@Type(() => CreateParticipantDto)
|
@Type(() => CreateParticipantDto)
|
||||||
participants: CreateParticipantDto[];
|
@IsOptional()
|
||||||
|
participants?: CreateParticipantDto[];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { BaseEntity } from '../../_core/entity/_base.entity';
|
||||||
import { FormPage } from '../../formPage/entity/formPage.entity';
|
import { FormPage } from '../../formPage/entity/formPage.entity';
|
||||||
import { Participant } from '../../participant/entity/participant.entity';
|
import { Participant } from '../../participant/entity/participant.entity';
|
||||||
import { Realm } from '../../realm/entity/realm.entity';
|
import { Realm } from '../../realm/entity/realm.entity';
|
||||||
|
import { Attachment } from '../../attachment/entity/attachment.entity';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class Form extends BaseEntity {
|
export class Form extends BaseEntity {
|
||||||
|
|
@ -20,23 +21,12 @@ export class Form extends BaseEntity {
|
||||||
@OneToMany(() => Participant, participant => participant.form, { cascade: true, eager: true })
|
@OneToMany(() => Participant, participant => participant.form, { cascade: true, eager: true })
|
||||||
participants: Participant[];
|
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;
|
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[];
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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": {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,12 +16,12 @@ export class ParticipantService {
|
||||||
|
|
||||||
async create(data: CreateParticipantDto): Promise<Participant> {
|
async create(data: CreateParticipantDto): Promise<Participant> {
|
||||||
try {
|
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(
|
const authResponse = await axios.post(
|
||||||
'https://auth.didvan.com/realms/didvan/protocol/openid-connect/token',
|
'https://auth.didvan.com/realms/didvan/protocol/openid-connect/token',
|
||||||
new URLSearchParams({
|
new URLSearchParams({
|
||||||
client_id: 'didvan-app',
|
client_id: 'didvan-app',
|
||||||
username: 'alice',
|
username: 'bob',
|
||||||
password: 'developer_password',
|
password: 'developer_password',
|
||||||
grant_type: 'password',
|
grant_type: 'password',
|
||||||
}),
|
}),
|
||||||
|
|
@ -39,16 +39,21 @@ export class ParticipantService {
|
||||||
throw new InternalServerErrorException('Failed to decode token or extract sub');
|
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({
|
const participant = this.participantRepository.create({
|
||||||
...data,
|
...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
|
userId: decodedToken.sub, // Set userId to the same sub
|
||||||
metadata: data.metadata
|
metadata: data.metadata
|
||||||
? { entries: data.metadata.entries } // Ensure metadata is properly formatted
|
? { entries: data.metadata.entries }
|
||||||
: { entries: [] },
|
: { entries: [] },
|
||||||
status: data.status || 'active',
|
status: data.status || 'active',
|
||||||
// Note: form and realm are not set as they are optional (ManyToOne)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Save the participant to the database
|
// Save the participant to the database
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,10 @@ export class CreateRealmDto extends BaseDto {
|
||||||
@IsArray()
|
@IsArray()
|
||||||
@ValidateNested({ each: true })
|
@ValidateNested({ each: true })
|
||||||
@Type(() => CreateParticipantDto)
|
@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
|
// @ValidateNested() // Not IsArray, as it's a single owner
|
||||||
@Type(() => CreateParticipantDto)
|
// @Type(() => CreateParticipantDto)
|
||||||
owner: CreateParticipantDto;
|
// owner: CreateParticipantDto;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 { BaseEntity } from '../../_core/entity/_base.entity';
|
||||||
import { Form } from '../../form/entity/form.entity';
|
import { Form } from '../../form/entity/form.entity';
|
||||||
import { Participant } from '../../participant/entity/participant.entity';
|
import { Participant } from '../../participant/entity/participant.entity';
|
||||||
|
|
@ -11,17 +11,14 @@ export class Realm extends BaseEntity {
|
||||||
@Column()
|
@Column()
|
||||||
description: string;
|
description: string;
|
||||||
|
|
||||||
// One-to-Many relationship with Form
|
|
||||||
@OneToMany(() => Form, form => form.realm, { cascade: true, eager: true })
|
@OneToMany(() => Form, form => form.realm, { cascade: true, eager: true })
|
||||||
forms: Form[];
|
forms: Form[];
|
||||||
|
|
||||||
// One-to-Many relationship with Participant (for general participants in the realm)
|
|
||||||
@OneToMany(() => Participant, participant => participant.realm, { cascade: true, eager: true })
|
@OneToMany(() => Participant, participant => participant.realm, { cascade: true, eager: true })
|
||||||
participants: Participant[];
|
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 })
|
@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;
|
owner: Participant;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,73 @@
|
||||||
// src/realm/realm.service.ts
|
import { Injectable, NotFoundException, InternalServerErrorException } from '@nestjs/common';
|
||||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
import { Realm } from './entity/realm.entity';
|
import { Realm } from './entity/realm.entity';
|
||||||
import { CreateRealmDto } from './dto/create-realm.dto';
|
import { CreateRealmDto } from './dto/create-realm.dto';
|
||||||
import { UpdateRealmDto } from './dto/update-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()
|
@Injectable()
|
||||||
export class RealmService {
|
export class RealmService {
|
||||||
constructor(
|
constructor(
|
||||||
@InjectRepository(Realm)
|
@InjectRepository(Realm)
|
||||||
private readonly realmRepo: Repository<Realm>,
|
private readonly realmRepo: Repository<Realm>,
|
||||||
|
@InjectRepository(Participant)
|
||||||
|
private readonly participantRepo: Repository<Participant>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async create(data: CreateRealmDto): Promise<Realm> {
|
async create(data: CreateRealmDto): Promise<Realm> {
|
||||||
const realm = this.realmRepo.create({ ...data });
|
try {
|
||||||
return await this.realmRepo.save(realm);
|
// 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(
|
async findAll(
|
||||||
page = 1,
|
page = 1,
|
||||||
limit = 10,
|
limit = 10,
|
||||||
|
|
@ -83,10 +133,7 @@ export class RealmService {
|
||||||
return realm;
|
return realm;
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(
|
async update(id: string, data: UpdateRealmDto): Promise<Realm | null> {
|
||||||
id: string,
|
|
||||||
data: UpdateRealmDto,
|
|
||||||
): Promise<Realm | null> {
|
|
||||||
const result = await this.realmRepo.update(
|
const result = await this.realmRepo.update(
|
||||||
{ id },
|
{ id },
|
||||||
{ ...data, updatedAt: new Date() },
|
{ ...data, updatedAt: new Date() },
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue