Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat#75]-Gerenciar Jornada/Trilha/Conteudo #2

Merged
merged 13 commits into from
Aug 18, 2024
Merged
3 changes: 3 additions & 0 deletions .env.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
MONGODB_URI=
USER_SERVICE_URL=
AUTH_SERVICE_URL=
16 changes: 6 additions & 10 deletions jest.config.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
import type { Config } from 'jest';
import * as dotenv from 'dotenv';

dotenv.config();

dotenv.config();
const config: Config = {
moduleFileExtensions: ['js', 'json', 'ts'],
rootDir: './test',
testRegex: '.*\\.spec\\.ts$',
transform: {
'^.+\\.(t|j)s$': 'ts-jest',
},
collectCoverageFrom: ['**/*.(t|j)s'],
coverageDirectory: '../coverage',
preset: 'ts-jest',
testEnvironment: 'node',
reporters: [
'default',
Expand All @@ -23,6 +15,10 @@ const config: Config = {
},
],
],
moduleNameMapper: {
'^src/(.*)$': '<rootDir>/src/$1',
},
setupFiles: ['dotenv/config'],
};

export default config;
4 changes: 4 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { MongooseModule } from '@nestjs/mongoose';
import { HttpModule } from '@nestjs/axios';
import { ContentModule } from './content/content.module';
import * as Joi from 'joi';
import { JourneyModule } from './journey/journey.module';
import { TrailModule } from './trail/trail.module';

@Module({
imports: [
Expand All @@ -23,6 +25,8 @@ import * as Joi from 'joi';
}),
HttpModule,
ContentModule,
JourneyModule,
TrailModule,
],
controllers: [],
providers: [],
Expand Down
58 changes: 27 additions & 31 deletions src/content/content.controller.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,53 @@
import {
Controller,
Get,
Post,
Body,
Param,
Put,
Req,
UnauthorizedException,
Get,
Patch,
Delete,
Param,
Body,
NotFoundException,
} from '@nestjs/common';
import { ContentService } from './content.service';
import { CreateContentDto } from './dtos/create-content.dto';
import { Request } from 'express';
import { Content } from './content.schema';

@Controller('contents')
export class ContentController {
constructor(private readonly contentService: ContentService) {}

@Post()
async create(
@Body() createContentDto: CreateContentDto,
@Req() req: Request,
) {
const authHeader = req.headers.authorization as string;
const token = authHeader?.split(' ')[1];

if (!token) {
throw new UnauthorizedException('Token not found');
async createContent(
@Body() body: { title: string; content: string; trailId: string },
): Promise<Content> {
const { title, content, trailId } = body;

if (!title || !content || !trailId) {
throw new NotFoundException('Title, content, and trailId are required');
}

return this.contentService.create(createContentDto, token);
return this.contentService.createContent(title, content, trailId);
}

@Get()
async findAll() {
return this.contentService.findAll();
@Get(':id')
async findContentById(@Param('id') id: string): Promise<Content> {
return this.contentService.findContentById(id);
}

@Get(':id')
async findById(@Param('id') id: string) {
return this.contentService.findById(id);
@Get()
async findAllContents(): Promise<Content[]> {
return this.contentService.findAllContents();
}

@Put(':id')
async update(
@Patch(':id')
async updateContent(
@Param('id') id: string,
@Body() updateContentDto: CreateContentDto,
) {
return this.contentService.update(id, updateContentDto);
@Body() updateData: Partial<Content>,
): Promise<Content> {
return this.contentService.updateContent(id, updateData);
}

@Delete(':id')
async delete(@Param('id') id: string) {
return this.contentService.delete(id);
async deleteContent(@Param('id') id: string): Promise<void> {
return this.contentService.deleteContent(id);
}
}
7 changes: 4 additions & 3 deletions src/content/content.module.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { Module } from '@nestjs/common';
import { Module, forwardRef } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { ContentSchema } from './content.schema';
import { ContentService } from './content.service';
import { ContentController } from './content.controller';
import { HttpModule } from '@nestjs/axios';
import { TrailModule } from '../trail/trail.module';

@Module({
imports: [
HttpModule,
MongooseModule.forFeature([{ name: 'Content', schema: ContentSchema }]),
forwardRef(() => TrailModule),
],
providers: [ContentService],
controllers: [ContentController],
exports: [ContentService],
})
export class ContentModule {}
16 changes: 8 additions & 8 deletions src/content/content.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@ import * as mongoose from 'mongoose';
export const ContentSchema = new mongoose.Schema(
{
title: { type: String, required: true },
body: { type: String, required: true },
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
trail: { type: mongoose.Schema.Types.ObjectId, ref: 'Trail' },
journey: { type: mongoose.Schema.Types.ObjectId, ref: 'Journey' },
content: { type: String, required: true },
trail: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Trail',
required: true,
},
},
{ timestamps: true, collection: 'contents' },
);

export interface Content extends mongoose.Document {
title: string;
body: string;
user: mongoose.Schema.Types.ObjectId;
trail?: mongoose.Schema.Types.ObjectId;
journey?: mongoose.Schema.Types.ObjectId;
content: string;
trail: mongoose.Schema.Types.ObjectId;
}
83 changes: 31 additions & 52 deletions src/content/content.service.ts
Original file line number Diff line number Diff line change
@@ -1,92 +1,71 @@
import {
Injectable,
Logger,
NotFoundException,
UnauthorizedException,
} from '@nestjs/common';
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Content } from './content.schema';
import { CreateContentDto } from './dtos/create-content.dto';
import { HttpService } from '@nestjs/axios';
import { firstValueFrom } from 'rxjs';
import { Trail } from '../trail/trail.schema';
import { TrailService } from '../trail/trail.service';

@Injectable()
export class ContentService {
private readonly logger = new Logger(ContentService.name);

constructor(
@InjectModel('Content') private readonly contentModel: Model<Content>,
private readonly httpService: HttpService,
@InjectModel('Trail') private readonly trailModel: Model<Trail>,
private readonly trailService: TrailService,
) {}

async create(
createContentDto: CreateContentDto,
token: string,
async createContent(
title: string,
content: string,
trailId: string,
): Promise<Content> {
const userId = await this.validateTokenAndGetUserId(token);

this.logger.log(`User ID from token: ${userId}`);

if (!userId) {
throw new UnauthorizedException('Invalid token');
const trailExists = await this.trailModel.findById(trailId).exec();
if (!trailExists) {
throw new NotFoundException(`Trail with ID ${trailId} not found`);
}

const newContent = new this.contentModel({
...createContentDto,
user: userId,
title,
content,
trail: trailId,
});
return newContent.save();
}

async validateTokenAndGetUserId(token: string): Promise<string | null> {
try {
this.logger.log(`Validating token: ${token}`);
const response = await firstValueFrom(
this.httpService.get(`${process.env.AUTH_SERVICE_URL}/validate-token`, {
headers: { Authorization: `Bearer ${token}` },
}),
);
this.logger.log(
`Token validation response: ${JSON.stringify(response.data)}`,
);
return response.data.userPayload?.id || null;
} catch (err) {
this.logger.error(`Token validation failed: ${err.message}`);
return null;
}
}
await this.trailService.addContentToTrail(
trailId,
newContent._id.toString(),
);

async findAll(): Promise<Content[]> {
return this.contentModel.find().exec();
return newContent.save();
}

async findById(id: string): Promise<Content> {
async findContentById(id: string): Promise<Content> {
const content = await this.contentModel.findById(id).exec();
if (!content) {
throw new NotFoundException(`Content with ID ${id} not found`);
}
return content;
}

async update(
async findAllContents(): Promise<Content[]> {
return this.contentModel.find().exec();
}

async updateContent(
id: string,
updateContentDto: CreateContentDto,
updateData: Partial<Content>,
): Promise<Content> {
const content = await this.contentModel
.findByIdAndUpdate(id, updateContentDto, { new: true })
.findByIdAndUpdate(id, updateData, { new: true })
.exec();
if (!content) {
throw new NotFoundException(`Content with ID ${id} not found`);
}
return content;
}

async delete(id: string): Promise<Content> {
const content = await this.contentModel.findByIdAndDelete(id).exec();
if (!content) {
async deleteContent(id: string): Promise<void> {
const result = await this.contentModel.findByIdAndDelete(id).exec();
if (!result) {
throw new NotFoundException(`Content with ID ${id} not found`);
}
return content;
}
}
18 changes: 6 additions & 12 deletions src/content/dtos/create-content.dto.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,15 @@
import { IsString, IsOptional, IsMongoId } from 'class-validator';
import { IsString, IsNotEmpty, IsMongoId } from 'class-validator';

export class CreateContentDto {
@IsString()
@IsNotEmpty()
title: string;

@IsString()
body: string;
@IsNotEmpty()
content: string;

@IsOptional()
@IsMongoId()
user?: string;

@IsOptional()
@IsMongoId()
trail?: string;

@IsOptional()
@IsMongoId()
journey?: string;
@IsNotEmpty()
trail: string;
}
13 changes: 13 additions & 0 deletions src/journey/dtos/create-journey.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { IsString, IsOptional, IsMongoId } from 'class-validator';

export class CreateJourneyDto {
@IsString()
title: string;

@IsString()
description: string;

@IsOptional()
@IsMongoId()
user?: string;
}
Loading
Loading