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

resolucao exercicio #1

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion src/controllers/PlantController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import { Request, Response, NextFunction } from 'express';
import PlantService from '../services/PlantService';

class PlantController {
public service: PlantService = new PlantService();
private readonly service: PlantService;

constructor(service: PlantService) {
this.service = service;
}

public async getAll(_req: Request, res: Response, next: NextFunction): Promise<Response | void> {
try {
Expand All @@ -21,6 +25,36 @@ class PlantController {
next(error);
}
}

public async getById(req: Request, res: Response, next: NextFunction): Promise<Response | void> {
const { id } = req.params;
try {
const plant = await this.service.getById(id);
return res.status(200).json(plant);
} catch (error) {
next(error);
}
}

public async remove(req: Request, res: Response, next: NextFunction): Promise<Response | void> {
const { id } = req.params;
try {
await this.service.removeById(id);
return res.status(204).end();
} catch (error) {
next(error);
}
}

public async update(req: Request, res: Response, next: NextFunction): Promise<Response | void> {
const { id } = req.params;
try {
const plant = await this.service.update(id, req.body);
return res.status(200).json(plant);
} catch (error) {
next(error);
}
}
}

export default PlantController;
9 changes: 9 additions & 0 deletions src/exceptions/BadRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import HttpException from './HttpException';

export default class BadRequestException extends HttpException {
private static status = 400;

constructor(message?: string) {
super(BadRequestException.status, message || 'Bad request');
}
}
2 changes: 1 addition & 1 deletion src/exceptions/HttpException.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
class HttpException extends Error {
abstract class HttpException extends Error {
status: number;

constructor(status: number, message: string) {
Expand Down
9 changes: 9 additions & 0 deletions src/exceptions/NotFound.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import HttpException from './HttpException';

export default class NotFoundException extends HttpException {
private static status = 404;

constructor(message?: string) {
super(NotFoundException.status, message || 'Not Found');
}
}
2 changes: 2 additions & 0 deletions src/exceptions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as BadRequestException } from './BadRequest';
export { default as NotFoundException } from './NotFound';
10 changes: 10 additions & 0 deletions src/interfaces/IPlant.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export interface IPlant {
id: number,
breed: string,
needsSun: boolean,
origin: string,
size: number,
waterFrequency: number,
}

export type INewPlant = Omit<IPlant, 'id' | 'waterFrequency'>;
1 change: 1 addition & 0 deletions src/interfaces/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { IPlant, INewPlant } from './IPlant';
23 changes: 23 additions & 0 deletions src/models/HandleFile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import fs from 'fs/promises';
import path from 'path';

export type FileType = 'plants' | 'opsInfo';

const PATHS = {
plants: path.join(__dirname, 'database', 'plantsData.json'),
opsInfo: path.join(__dirname, 'database', 'opsInfo.json'),
};

export class HandleFile {
private PATHS = PATHS;

public async saveFile<T>(type: FileType, data: T): Promise<void> {
await fs.writeFile(this.PATHS[`${type}`], JSON.stringify(data, null, 2));
}

public async readFile<T>(type: FileType): Promise<T> {
const dataRaw = await fs.readFile(this.PATHS[`${type}`], { encoding: 'utf8' });
const data: T = JSON.parse(dataRaw);
return data;
}
}
72 changes: 72 additions & 0 deletions src/models/PlantModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { HandleFile, FileType } from './HandleFile';
import { IModel, IOpsInfo } from './interfaces';
import { IPlant } from '../interfaces';

class PlantModel implements IModel<IPlant> {
private fileTypePlant: FileType = 'plants';

private fileTypeOpsInfo: FileType = 'opsInfo';

private handleFile = new HandleFile();

private async updateOpsInfo(incrementAmount = 1): Promise<number> {
const opsInfo = await this.handleFile.readFile<IOpsInfo>(this.fileTypeOpsInfo);
opsInfo.createdPlants += incrementAmount;

await this.handleFile.saveFile(this.fileTypeOpsInfo, opsInfo);

return opsInfo.createdPlants;
}

public async getAll(): Promise<IPlant[]> {
const plants = await this.handleFile.readFile<IPlant[]>(this.fileTypePlant);
return plants;
}

public async create(plant: Omit<IPlant, 'id'>): Promise<IPlant> {
const plants = await this.getAll();

const newPlantId = await this.updateOpsInfo(1);
const newPlant = { id: newPlantId, ...plant };
plants.push(newPlant);

await this.handleFile.saveFile(this.fileTypePlant, plants);

return newPlant;
}

public async getById(id: string): Promise<IPlant | null> {
const plants = await this.getAll();

const plantById = plants.find((plant) => plant.id === parseInt(id, 10));
if (!plantById) return null;
return plantById;
}

public async removeById(id: string): Promise<boolean> {
const plants = await this.getAll();

const removedPlant = plants.find((plant) => plant.id === parseInt(id, 10));
if (!removedPlant) return false;

const newPlants = plants.filter((plant) => plant.id !== parseInt(id, 10));
this.handleFile.saveFile(this.fileTypePlant, newPlants);

return true;
}

public async update(plant: IPlant): Promise<IPlant> {
const plants = await this.getAll();

const updatedPlants = plants.map((editPlant) => {
if (plant.id === editPlant.id) return { ...plant };
return editPlant;
});

await this.handleFile.saveFile(this.fileTypePlant, updatedPlants);

return plant;
}
}

export default PlantModel;
17 changes: 17 additions & 0 deletions src/models/interfaces/IModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export interface IModelReader<T> {
getAll(): Promise<T[]>;
getById(id: string): Promise<T | null>
}

export interface IModelWriter<T> {
create(arg: Omit<T, 'id'>): Promise<T>
update(arg: T): Promise<T>
}
export interface IModelDelete {
removeById(id: string): Promise<boolean>
}

export interface IModel<T> extends
IModelReader<T>,
IModelWriter<T>,
IModelDelete {}
3 changes: 3 additions & 0 deletions src/models/interfaces/IOpsInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default interface IOpsInfo {
createdPlants: number
}
7 changes: 7 additions & 0 deletions src/models/interfaces/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export {
IModel,
IModelReader,
IModelWriter,
IModelDelete,
} from './IModel';
export { default as IOpsInfo } from './IOpsInfo';
9 changes: 8 additions & 1 deletion src/router/PlantRouter.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import { Router } from 'express';

import PlantController from '../controllers/PlantController';
import PlantModel from '../models/PlantModel';
import PlantService from '../services/PlantService';

const plantController = new PlantController();
const plantModel = new PlantModel();
const plantService = new PlantService(plantModel);
const plantController = new PlantController(plantService);

const plantRouter = Router();

plantRouter.get('/', (req, res, next) => plantController.getAll(req, res, next));
plantRouter.post('/', (req, res, next) => plantController.create(req, res, next));
plantRouter.get('/:id', (req, res, next) => plantController.getById(req, res, next));
plantRouter.delete('/:id', (req, res, next) => plantController.remove(req, res, next));
plantRouter.put('/:id', (req, res, next) => plantController.update(req, res, next));

export default plantRouter;
94 changes: 33 additions & 61 deletions src/services/PlantService.ts
Original file line number Diff line number Diff line change
@@ -1,80 +1,52 @@
import fs from 'fs/promises';
import path from 'path';
import HttpException from '../exceptions/HttpException';
import { INewPlant, IPlant } from '../interfaces';
import { IService } from './interfaces';
import { IModel } from '../models/interfaces';
import { NotFoundException } from '../exceptions';
import PlantValidate from './validations/PlantValidate';

interface IPlant {
id: number,
breed: string,
needsSun: boolean,
origin: string,
size: number,
waterFrequency: number,
}

type INewPlant = Omit<IPlant, 'id' | 'waterFrequency'>;

interface IOpsInfo {
createdPlants: number
}

class PlantService {
private readonly plantsFile = path.join(__dirname, '..', 'models', 'database', 'plantsData.json');
class PlantService implements IService<IPlant, INewPlant> {
private readonly model: IModel<IPlant>;

private readonly opsFile = path.join(__dirname, '..', 'models', 'database', 'opsInfo.json');

private async updateOpsInfo(incrementAmount = 1): Promise<number> {
const dataRaw = await fs.readFile(this.opsFile, { encoding: 'utf8' });
const opsInfo: IOpsInfo = JSON.parse(dataRaw);
opsInfo.createdPlants += incrementAmount;

await fs.writeFile(this.opsFile, JSON.stringify(opsInfo, null, 2));

return opsInfo.createdPlants;
constructor(model: IModel<IPlant>) {
this.model = model;
}

public async getAll(): Promise<IPlant[]> {
const dataRaw = await fs.readFile(this.plantsFile, { encoding: 'utf8' });
const plants: IPlant[] = JSON.parse(dataRaw);
const plants = await this.model.getAll();
return plants;
}

public async create(plant: INewPlant): Promise<IPlant> {
const {
breed,
needsSun,
origin,
size,
} = plant;

if (typeof breed !== 'string') {
throw new HttpException(400, 'Attribute "breed" must be string.');
}

if (typeof needsSun !== 'boolean') {
throw new HttpException(400, 'Attribute "needsSun" must be boolen.');
}

if (typeof origin !== 'string') {
throw new HttpException(400, 'Attribute "origin" must be string.');
}

if (typeof size !== 'number') {
throw new HttpException(400, 'Attribute "size" must be number.');
}
PlantValidate.validateAttibutes(plant);

const { needsSun, size, origin } = plant;
const waterFrequency = needsSun
? size * 0.77 + (origin === 'Brazil' ? 8 : 7)
: (size / 2) * 1.33 + (origin === 'Brazil' ? 8 : 7);

const dataRaw = await fs.readFile(this.plantsFile, { encoding: 'utf8' });
const plants: IPlant[] = JSON.parse(dataRaw);
const newPlant = await this.model.create({ ...plant, waterFrequency });
return newPlant;
}

public async getById(id: string): Promise<IPlant> {
const plant = await this.model.getById(id);
if (!plant) throw new NotFoundException('Plant not Found!');
return plant;
}

const newPlantId = await this.updateOpsInfo(1);
const newPlant = { id: newPlantId, ...plant, waterFrequency };
plants.push(newPlant);
public async removeById(id: string): Promise<void> {
const isPlantRemoved = await this.model.removeById(id);
if (!isPlantRemoved) throw new NotFoundException('Plant not Found!');
}

await fs.writeFile(this.plantsFile, JSON.stringify(plants, null, 2));
return newPlant;
public async update(id: string, plant: Omit<IPlant, 'id'>): Promise<IPlant> {
const plantExists = await this.model.getById(id);
if (!plantExists) throw new NotFoundException('Plant not Found!');

PlantValidate.validateAttibutes(plant);

const editedPlant = await this.model.update({ id: parseInt(id, 10), ...plant });
return editedPlant;
}
}

Expand Down
17 changes: 17 additions & 0 deletions src/services/interfaces/IService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export interface IServiceReader<T> {
getAll(): Promise<T[]>;
getById(id: string): Promise<T | null>
}

export interface IServiceWriter<T, U> {
create(arg: U): Promise<T>
update(id: string, arg: Omit<T, 'id'>): Promise<T>
}
export interface IServiceDelete {
removeById(id: string): Promise<void>
}

export interface IService<T, U> extends
IServiceReader<T>,
IServiceWriter<T, U>,
IServiceDelete {}
6 changes: 6 additions & 0 deletions src/services/interfaces/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export {
IService,
IServiceReader,
IServiceWriter,
IServiceDelete,
} from './IService';
Loading