Browse Source

Utilización de Apollo Server y contrucción de Schemas de GraphQL

Pablo Barrera Yaksic 1 month ago
parent
commit
0af2e20f79
43 changed files with 6948 additions and 3361 deletions
  1. 9 0
      .env.example
  2. 1 0
      .gitignore
  3. 0 13
      conf.env.example
  4. 6 6
      gulpfile.ts
  5. 6369 2317
      package-lock.json
  6. 12 1
      package.json
  7. 36 0
      serverless.yml
  8. 0 38
      src/controllers/api/example-controller.ts
  9. 0 107
      src/controllers/api/example-resource-controller.ts
  10. 0 180
      src/controllers/api/posts-controller.ts
  11. 1 1
      src/controllers/http-controller.ts
  12. 2 2
      src/controllers/http-petition.ts
  13. 0 54
      src/controllers/http-resource-controller.ts
  14. 26 0
      src/data-storage/db/extr/generic-extr.ts
  15. 178 16
      src/data-storage/db/extr/index.ts
  16. 3 2
      src/data-storage/db/index.ts
  17. 0 102
      src/data-storage/fake-db/index.ts
  18. 0 74
      src/data-storage/fake-db/post/index.ts
  19. 14 9
      src/frameworks/express/express-app.ts
  20. 0 34
      src/frameworks/express/middlewares/verify-token.ts
  21. 0 64
      src/frameworks/express/routers/api-router.ts
  22. 30 0
      src/frameworks/express/routers/v1/api-router.ts
  23. 0 0
      src/frameworks/express/routers/v1/index.ts
  24. 23 0
      src/frameworks/express/routers/v1/web-router.ts
  25. 0 39
      src/frameworks/express/routers/web-router.ts
  26. 6 6
      src/frameworks/sequelize/index.ts
  27. 32 34
      src/frameworks/sequelize/models/generic-extr.ts
  28. 3 3
      src/index.ts
  29. 30 0
      src/lambda-apollo.ts
  30. 5 0
      src/lambda-express.ts
  31. 99 62
      src/libs/graphql/index.ts
  32. 6 30
      src/libs/log/index.ts
  33. 9 0
      src/test/.env.test
  34. 0 19
      src/test/post/add.test.ts
  35. 0 14
      src/test/post/all.test.ts
  36. 0 18
      src/test/post/delete.test.ts
  37. 0 15
      src/test/post/get.test.ts
  38. 0 26
      src/test/post/update.test.ts
  39. 0 24
      src/test/send-mail.test.ts
  40. 4 4
      src/test/server-status.test.ts
  41. 37 31
      src/use-cases/extr/all.ts
  42. 5 16
      src/use-cases/extr/get.ts
  43. 2 0
      tsconfig.json

+ 9 - 0
.env.example

@@ -0,0 +1,9 @@
+PORT = 8000
+LOG_LEVEL = debug
+
+DB_CLIENT = mysql
+DB_HOST = host
+DB_PORT = 3306
+DB_USER = user
+DB_PASS = secret
+DB_SCHEMA = example

+ 1 - 0
.gitignore

@@ -3,3 +3,4 @@ node_modules
 *.env
 coverage
 logs
+.vscode

+ 0 - 13
conf.env.example

@@ -1,13 +0,0 @@
-PORT = 8000
-LOG_LEVEL = debug
-
-DB_CLIENT = "mysql"
-DB_HOST = "host"
-DB_PORT = 3306
-DB_USER = "user"
-DB_PASS = "secret"
-DB_SCHEMA = "example"
-DB_TABLE_PATTERN = "%y%w%t%c_extr"
-
-DEFAULT_CATEGORY = "01"
-DEFAULT_COUNTRY = "01"

+ 6 - 6
gulpfile.ts

@@ -12,9 +12,9 @@ export function test() {
   // Unlike the jest CLI tool, gulp-jest does not automatically set process.env.NODE_ENV to be test
   process.env.NODE_ENV = "test";
 
-  if (!fs.existsSync("src/test/conf.test.env")) {
-    src("conf.env.example")
-      .pipe(rename("conf.test.env"))
+  if (!fs.existsSync("src/test/.env.test")) {
+    src(".env.example")
+      .pipe(rename(".env.test"))
       .pipe(dest("src/test"));
   }
 
@@ -27,9 +27,9 @@ export function test() {
 }
 
 function createConf(cb: any) {
-  if (!fs.existsSync("dist/conf.env")) {
-    return src("conf.env.example")
-      .pipe(rename("conf.env"))
+  if (!fs.existsSync("dist/.env")) {
+    return src(".env.example")
+      .pipe(rename(".env"))
       .pipe(dest("dist"));
   }
   cb();

File diff suppressed because it is too large
+ 6369 - 2317
package-lock.json


+ 12 - 1
package.json

@@ -24,11 +24,13 @@
   "author": "Pablo Barrera Yaksic",
   "license": "ISC",
   "dependencies": {
+    "@apollo/server": "^4.11.0",
+    "@as-integrations/aws-lambda": "^3.1.0",
+    "@codegenie/serverless-express": "^4.15.0",
     "cors": "^2.8.5",
     "dotenv": "^16.4.5",
     "express": "^4.21.1",
     "graphql": "^16.9.0",
-    "graphql-http": "1.22.1",
     "helmet": "^8.0.0",
     "log4js": "^6.9.1",
     "morgan": "^1.10.0",
@@ -42,9 +44,15 @@
     "@types/cors": "^2.8.17",
     "@types/express": "^5.0.0",
     "@types/graphql": "^14.5.0",
+    "@types/gulp-nodemon": "^0.0.37",
+    "@types/gulp-rename": "^2.0.6",
+    "@types/jest": "^29.5.13",
     "@types/morgan": "^1.9.9",
     "@types/node": "^22.7.6",
     "@types/sequelize": "^4.28.20",
+    "@types/supertest": "^6.0.2",
+    "@types/swagger-jsdoc": "^6.0.4",
+    "@types/swagger-ui-express": "^4.1.6",
     "eslint": "^9.12.0",
     "eslint-config-prettier": "^9.1.0",
     "eslint-plugin-prettier": "^5.2.1",
@@ -56,6 +64,9 @@
     "jest": "^29.7.0",
     "nodemon": "^3.1.7",
     "prettier": "^3.3.3",
+    "serverless": "^3.34.0",
+    "serverless-offline": "^13.6.0",
+    "serverless-plugin-typescript": "^2.1.5",
     "supertest": "^7.0.0",
     "ts-jest": "^29.2.5",
     "ts-node": "^10.9.2",

+ 36 - 0
serverless.yml

@@ -0,0 +1,36 @@
+service: operacion-precio-api-extr
+
+provider:
+  name: aws
+  region: us-west-1
+  runtime: nodejs20.x
+  vpc:
+    securityGroupIds:
+      - sg-0dcf8347897f9ba0b
+    subnetIds:
+      - subnet-0b5fa80770cebed1f
+      - subnet-0515ee9bbcabead4c
+      - subnet-080018f99a775b068
+
+plugins:
+  - serverless-offline
+  - serverless-plugin-typescript
+
+functions:
+  api:
+    handler: src/lambda-express
+    events:
+      - httpApi:
+          method: GET
+          path: /api/v1/ping
+
+      - httpApi:
+          method: ANY
+          path: /{proxy+}
+
+  graphql:
+    handler: src/lambda-apollo
+    events:
+      - http:
+          method: ANY
+          path: /graphql

+ 0 - 38
src/controllers/api/example-controller.ts

@@ -1,38 +0,0 @@
-import * as jwt from "jsonwebtoken";
-
-import { HttpController } from "../http-controller";
-
-import Log from "../../libs/log";
-
-export class ExampleController extends HttpController {
-  private _log: Log;
-
-  constructor() {
-    super();
-    this._log = Log.instance;
-  }
-
-  public index = async () => {
-    try {
-      this.success({ status: "OK", statusCode: 200, payload: {} });
-    } catch (err) {
-      this._log.logger.error("Ocurrió un error", err.message);
-      this.error({ error: err.message, status: "NO_OK", statusCode: 422 });
-    }
-  };
-
-  public getToken = () => {
-    const token = jwt.sign({ data: "example data" }, process.env.SECRET, {
-      expiresIn: "24h"
-    });
-    this.success({ status: "OK", statusCode: 200, payload: { token } });
-  };
-
-  public protectedMethod = () => {
-    this.success({
-      status: "OK",
-      statusCode: 200,
-      payload: { message: "This is a protected method" }
-    });
-  };
-}

+ 0 - 107
src/controllers/api/example-resource-controller.ts

@@ -1,107 +0,0 @@
-import { Response } from "../http-petition";
-import { HttpResourceController } from "../http-resource-controller";
-
-import Log from "../../libs/log";
-
-export class ExampleResourceController extends HttpResourceController {
-  private _log: Log;
-
-  constructor() {
-    super();
-    this._log = Log.instance;
-  }
-
-  public async index(): Promise<Response> {
-    try {
-      const items = new Array<any>(); // await this.modules.database("table").select("*");
-
-      return this.success({ status: "OK", statusCode: 200, payload: items });
-    } catch (err) {
-      this._log.logger.error("Ocurrió un error", err.message);
-      return this.error({
-        error: err.message,
-        status: "NO_OK",
-        statusCode: 422
-      });
-    }
-  }
-
-  public async store(): Promise<Response> {
-    try {
-      const item = this.petition.response.payload;
-      const id = 0; // await this.modules.database("table").insert(item);
-
-      return this.success({
-        status: "OK",
-        statusCode: 200,
-        payload: { id, message: "inserted" }
-      });
-    } catch (err) {
-      this._log.logger.error("Ocurrió un error", err.message);
-      return this.error({
-        error: err.message,
-        status: "NO_OK",
-        statusCode: 422
-      });
-    }
-  }
-
-  public async show(): Promise<Response> {
-    try {
-      const id = this.petition.request.params.id;
-      const item = { id };
-      return this.success({
-        status: "OK",
-        statusCode: 200,
-        payload: item || {}
-      });
-    } catch (err) {
-      this._log.logger.error("Ocurrió un error", err.message);
-      return this.error({
-        error: err.message,
-        status: "NO_OK",
-        statusCode: 422
-      });
-    }
-  }
-
-  public async update(): Promise<Response> {
-    try {
-      const item = this.petition.request.payload;
-      const result = 0;
-
-      return this.success({
-        status: "OK",
-        statusCode: 200,
-        payload: { rowsAffected: result }
-      });
-    } catch (err) {
-      this._log.logger.error("Ocurrió un error", err.message);
-      return this.error({
-        error: err.message,
-        status: "NO_OK",
-        statusCode: 422
-      });
-    }
-  }
-
-  public async destroy(): Promise<Response> {
-    try {
-      const id = this.petition.request.params.id;
-      const result = 0;
-
-      return this.success({
-        status: "OK",
-        statusCode: 200,
-        payload: { rowsAffected: result }
-      });
-    } catch (err) {
-      this._log.logger.error("Ocurrió un error", err.message);
-      return this.error({
-        error: err.message,
-        status: "NO_OK",
-        statusCode: 422
-      });
-    }
-  }
-}

+ 0 - 180
src/controllers/api/posts-controller.ts

@@ -1,180 +0,0 @@
-import { HttpResourceController } from "../http-resource-controller";
-import { Response } from "../http-petition";
-
-import Log from "../../libs/log";
-
-import Db from "../../data-storage/db";
-
-import { getAuthor } from "../../use-cases/author";
-import {
-  getPost,
-  allPosts,
-  addPost,
-  updatePost,
-  deletePost
-} from "../../use-cases/post";
-import AuthorDb from "../../data-storage/db/author";
-import PostDb from "../../data-storage/db/post";
-
-export default class PostsController extends HttpResourceController {
-  private _db: Db;
-  private _log: Log;
-
-  constructor() {
-    super();
-    this._db = new Db();
-    this._log = Log.instance;
-  }
-
-  public async index(): Promise<Response> {
-    try {
-      const postDb = new PostDb(this._db);
-      const posts = await allPosts.get(postDb).catch(err => {
-        throw err;
-      });
-
-      return this.success({
-        status: "OK",
-        statusCode: 200,
-        payload: posts
-      });
-    } catch (err) {
-      this._log.logger.error("Ocurrió un error", err);
-      return this.error({
-        error: err.message,
-        status: "NO_OK",
-        statusCode: 422
-      });
-    }
-  }
-
-  public async store(): Promise<Response> {
-    try {
-      const authorDb = new AuthorDb(this._db);
-      const author = await getAuthor
-        .retrieve(this.request.payload.authorId, authorDb)
-        .catch(err => {
-          throw err;
-        });
-
-      const postDb = new PostDb(this._db);
-      const post = await addPost
-        .save({ ...this.request.payload, author }, postDb)
-        .catch(err => {
-          throw err;
-        });
-
-      return this.success({
-        status: "OK",
-        statusCode: 200,
-        payload: post
-      });
-    } catch (err) {
-      this._log.logger.error("An error has occurred:", err.message);
-      return this.error({
-        error: err.message,
-        status: "NO_OK",
-        statusCode: 422
-      });
-    }
-  }
-
-  public async show(): Promise<Response> {
-    try {
-      const postDb = new PostDb(this._db);
-      const post = await getPost
-        .byId(Number(this.request.params.id), postDb)
-        .catch(err => {
-          throw err;
-        });
-
-      const authorDb = new AuthorDb(this._db);
-      const author = await getAuthor
-        .retrieve(post.authorId, authorDb)
-        .catch(err => {
-          throw err;
-        });
-      post.author = author;
-
-      return this.success({
-        status: "OK",
-        statusCode: 200,
-        payload: post
-      });
-    } catch (err) {
-      this._log.logger.error("An error has occurred", err);
-      return this.error({
-        error: err.message,
-        status: "NO_OK",
-        statusCode: 422
-      });
-    }
-  }
-
-  public async update(): Promise<Response> {
-    try {
-      const postDb = new PostDb(this._db);
-      const post = await getPost
-        .byId(Number(this.request.params.id), postDb)
-        .catch(err => {
-          throw err;
-        });
-
-      const authorDb = new AuthorDb(this._db);
-      const author = await getAuthor
-        .retrieve(this.request.payload.authorId, authorDb)
-        .catch(err => {
-          throw err;
-        });
-
-      post.id = this.request.params.id;
-      post.author = author;
-      await updatePost
-        .save(post, { ...this.request.payload }, postDb)
-        .catch(err => {
-          throw err;
-        });
-
-      return this.success({
-        status: "OK",
-        statusCode: 200,
-        payload: post
-      });
-    } catch (err) {
-      this._log.logger.error("An error has occurred", err);
-      return this.error({
-        error: err.message,
-        status: "NO_OK",
-        statusCode: 422
-      });
-    }
-  }
-
-  public async destroy(): Promise<Response> {
-    try {
-      const postDb = new PostDb(this._db);
-      const post = await getPost
-        .byId(Number(this.request.params.id), postDb)
-        .catch(err => {
-          throw err;
-        });
-
-      await deletePost.save(post, postDb).catch(err => {
-        throw err;
-      });
-
-      return this.success({
-        status: "OK",
-        statusCode: 200,
-        payload: post.id
-      });
-    } catch (err) {
-      this._log.logger.error("An error has occurred", err);
-      return this.error({
-        error: err.message,
-        status: "NO_OK",
-        statusCode: 422
-      });
-    }
-  }
-}

+ 1 - 1
src/controllers/http-controller.ts

@@ -3,7 +3,7 @@ import { Petition, Request, Response } from "./http-petition";
 export class HttpController {
   private _petition: Petition;
 
-  constructor(petition?: Petition) {
+  constructor(petition: Petition) {
     this._petition = petition;
   }
 

+ 2 - 2
src/controllers/http-petition.ts

@@ -13,6 +13,6 @@ export interface Response {
 }
 
 export interface Petition {
-  request?: Request;
-  response?: Response;
+  request: Request;
+  response: Response;
 }

+ 0 - 54
src/controllers/http-resource-controller.ts

@@ -1,54 +0,0 @@
-import { Router, Request, Response } from "express";
-
-import { Response as HttpResponse } from "./http-petition";
-import { HttpController } from "./http-controller";
-
-export abstract class HttpResourceController extends HttpController {
-  constructor() {
-    super();
-  }
-
-  public abstract index(): HttpResponse | Promise<HttpResponse>;
-  public abstract store(): HttpResponse | Promise<HttpResponse>;
-  public abstract show(): HttpResponse | Promise<HttpResponse>;
-  public abstract update(): HttpResponse | Promise<HttpResponse>;
-  public abstract destroy(): HttpResponse | Promise<HttpResponse>;
-
-  public generateRoutes(router: Router, path: string) {
-    router.get(path, (req: Request, res: Response) =>
-      this.getResponse(req, res, "index")
-    );
-    router.post(path, (req: Request, res: Response) =>
-      this.getResponse(req, res, "store")
-    );
-    router.get(`${path}/:id`, (req: Request, res: Response) =>
-      this.getResponse(req, res, "show")
-    );
-    router.put(`${path}/:id`, (req: Request, res: Response) =>
-      this.getResponse(req, res, "update")
-    );
-    router.delete(`${path}/:id`, (req: Request, res: Response) =>
-      this.getResponse(req, res, "destroy")
-    );
-  }
-
-  private getResponse(req: Request, res: Response, method: string) {
-    this.petition = {
-      request: { ...req, payload: req.body },
-      response: { ...res, status: "", headers: null, payload: {} }
-    };
-
-    switch (method) {
-      case "index":
-        return this.index();
-      case "store":
-        return this.store();
-      case "show":
-        return this.show();
-      case "update":
-        return this.update();
-      case "destroy":
-        return this.destroy();
-    }
-  }
-}

+ 26 - 0
src/data-storage/db/extr/generic-extr.ts

@@ -0,0 +1,26 @@
+export default interface GenericExtr {
+  year: number;
+  maker: string;
+  model: string;
+  version: string;
+  color: string;
+  price: number;
+  title: string;
+  description: string;
+  plate: string;
+  mileage: number;
+  gearbox: string;
+  engine: string;
+  fuel: string;
+  imageUrl: string;
+  code: string;
+  url: string;
+  region: string;
+  city: string;
+  sellerType: string;
+  sellerName: string;
+  publicationCreationDate: Date;
+  publicationCreationRawDate: Date;
+  publicationUpdateDate: Date;
+  publicationCreatedAt: Date;
+}

+ 178 - 16
src/data-storage/db/extr/index.ts

@@ -1,7 +1,7 @@
-import { Op } from "sequelize";
+import { Op, Sequelize } from "sequelize";
 
 import Db from "..";
-import { Chileautos } from "../../../entities";
+import GenericExtr from "../../db/extr/generic-extr";
 
 export default class ExtrDb {
   private _db: Db;
@@ -14,8 +14,16 @@ export default class ExtrDb {
     this._db.defineModel(this._tableName);
   }
 
-  public async getByCode(code: string): Promise<Chileautos> {
-    const result = await this._db.models().extrs.findOne({ where: { code } });
+  private static getStartEndDay(date: string): { start: Date, end: Date } {
+    const parsedDate = new Date(date);
+    const start = new Date(Date.UTC(parsedDate.getUTCFullYear(), parsedDate.getUTCMonth(), parsedDate.getUTCDate(), 0, 0, 0, 0));
+    const end = new Date(Date.UTC(parsedDate.getUTCFullYear(), parsedDate.getUTCMonth(), parsedDate.getUTCDate(), 23, 59, 59, 999));
+
+    return { start, end };
+  }
+
+  public async getByCode(code: string, requestedFields: Array<string>): Promise<GenericExtr | null> {
+    const result = await this._db.models()[this._tableName].findOne({ attributes: requestedFields, where: { code } });
     if (!result) {
       return null;
     }
@@ -23,33 +31,187 @@ export default class ExtrDb {
     return { code, ...result.get() };
   }
 
-  public async getAll(): Promise<Array<any>> {
-    const result = await this._db.models().extrs.findAll();
+  public async getAll(requestedFields: Array<string>): Promise<Array<any>> {
+    const result = await this._db.models()[this._tableName].findAll({ attributes: requestedFields });
     if (!result) {
-      return null;
+      return [];
     }
 
     return result;
   }
 
-  public async getFilteredBy({id = 0, authorId = 0}: any): Promise<Array<any>> {
+  public async getFilteredBy(filters: any, requestedFields: Array<string>): Promise<Array<any>> {
     const where: any = { };
 
-    if (id !== 0) {
-      where.id = {
-        [Op.eq]: id
+    if (filters.year && filters.year >= 1900) {
+      where.year = {
+        [Op.eq]: filters.year
+      };
+    }
+
+    if (filters.maker) {
+      where.maker = {
+        [Op.like]: `%${filters.maker}%`
+      };
+    }
+
+    if (filters.model) {
+      where.model = {
+        [Op.like]: `%${filters.model}%`
+      };
+    }
+
+    if (filters.version) {
+      where.version = {
+        [Op.like]: `%${filters.version}%`
+      };
+    }
+
+    if (filters.color) {
+      where.color = {
+        [Op.like]: `%${filters.color}%`
+      };
+    }
+
+    if (filters.price && filters.price >= 0) {
+      where.price = {
+        [Op.eq]: filters.price
+      };
+    }
+
+    if (filters.code) {
+      where.code = {
+        [Op.eq]: filters.code
+      };
+    }
+
+    if (filters.title) {
+      where.title = {
+        [Op.like]: `%${filters.title}%`
+      };
+    }
+    
+    if (filters.description) {
+      where.description = {
+        [Op.like]: `%${filters.description}%`
+      };
+    }
+    
+    if (filters.plate) {
+      where.plate = {
+        [Op.eq]: filters.plate
+      };
+    }
+    
+    if (filters.mileage && filters.mileage >= 0) {
+      where.mileage = {
+        [Op.eq]: filters.mileage
+      };
+    }
+    
+    if (filters.gearbox) {
+      where.gearbox = {
+        [Op.like]: `%${filters.gearbox}%`
+      };
+    }
+    
+    if (filters.engine) {
+      where.engine = {
+        [Op.like]: `%${filters.engine}%`
+      };
+    }
+    
+    if (filters.fuel) {
+      where.fuel = {
+        [Op.like]: `%${filters.fuel}%`
+      };
+    }
+    
+    if (filters.imageUrl) {
+      where.imageUrl = {
+        [Op.eq]: filters.imageUrl
+      };
+    }
+    
+    if (filters.code) {
+      where.code = {
+        [Op.eq]: filters.code
+      };
+    }
+    
+    if (filters.url) {
+      where.url = {
+        [Op.eq]: filters.url
+      };
+    }
+    
+    if (filters.region) {
+      where.region = {
+        [Op.like]: `%${filters.region}%`
+      };
+    }
+    
+    if (filters.city) {
+      where.city = {
+        [Op.like]: `%${filters.city}%`
+      };
+    }
+    
+    if (filters.sellerType) {
+      where.sellerType = {
+        [Op.like]: `%${filters.sellerType}%`
+      };
+    }
+    
+    if (filters.sellerName) {
+      where.sellerName = {
+        [Op.like]: `%${filters.sellerName}%`
+      };
+    }
+    
+    if (filters.publicationCreationDate) {
+      const { start, end } = ExtrDb.getStartEndDay(filters.publicationCreationDate);
+      where.publicationCreationDate = {
+        [Op.between]: [
+          Sequelize.fn('STR_TO_DATE', start, '%Y-%m-%d %H:%i:%s'),  // Ajusta el formato según sea necesario
+          Sequelize.fn('STR_TO_DATE', end, '%Y-%m-%d %H:%i:%s')
+        ]
       };
     }
 
-    if (authorId !== 0) {
-      where.authorId = {
-        [Op.eq]: authorId
+    if (filters.publicationCreationRawDate) {
+      const { start, end } = ExtrDb.getStartEndDay(filters.publicationCreationRawDate);
+      where.publicationCreationRawDate = {
+        [Op.between]: [
+          Sequelize.fn('STR_TO_DATE', start, '%Y-%m-%d %H:%i:%s'),  // Ajusta el formato según sea necesario
+          Sequelize.fn('STR_TO_DATE', end, '%Y-%m-%d %H:%i:%s')
+        ]
+      };
+    }
+    
+    if (filters.publicationUpdateDate) {
+      const { start, end } = ExtrDb.getStartEndDay(filters.publicationUpdateDate);
+      where.publicationUpdateDate = {
+        [Op.between]: [
+          Sequelize.fn('STR_TO_DATE', start, '%Y-%m-%d %H:%i:%s'),  // Ajusta el formato según sea necesario
+          Sequelize.fn('STR_TO_DATE', end, '%Y-%m-%d %H:%i:%s')
+        ]
+      };
+    }
+    
+    if (filters.publicationCreatedAt) {
+      const { start, end } = ExtrDb.getStartEndDay(filters.publicationCreatedAt);
+      where.publicationCreatedAt = {
+        [Op.between]: [
+          Sequelize.fn('STR_TO_DATE', start, '%Y-%m-%d %H:%i:%s'),  // Ajusta el formato según sea necesario
+          Sequelize.fn('STR_TO_DATE', end, '%Y-%m-%d %H:%i:%s')
+        ]
       };
     }
 
-    const result = await this._db.models().extrs.findAll({ where });
+    const result = await this._db.models()[this._tableName].findAll({ attributes: requestedFields, where });
     if (!result) {
-      return null;
+      return [];
     }
 
     return result;

+ 3 - 2
src/data-storage/db/index.ts

@@ -1,4 +1,5 @@
 import SequelizeDbConnector from "../../frameworks/sequelize";
+import createGenericExtrModel from "../../frameworks/sequelize/models/generic-extr";
 
 export default class Db {
   private _sequelizeDbConnector: SequelizeDbConnector;
@@ -27,7 +28,7 @@ export default class Db {
     return this._sequelizeDbConnector.sequelize.models;
   }
 
-  public defineModel(modelName: string) {
-    
+  public async defineModel(modelName: string) {
+    await createGenericExtrModel(this._sequelizeDbConnector.sequelize, modelName);
   }
 }

+ 0 - 102
src/data-storage/fake-db/index.ts

@@ -1,102 +0,0 @@
-import { randomInt } from "crypto";
-import Db from "../db";
-
-class FakeModel {
-  private _fakeData: any;
-  private _name: string;
-
-  constructor(fakeData?: any) {
-    this._fakeData = fakeData;
-  }
-
-  public get fakeData() {
-    return this._fakeData;
-  }
-
-  public get name() {
-    return this._name;
-  }
-
-  public set name(name: string) {
-    this._name = name;
-  }
-
-  findByPk(id: number) {
-    return new FakeModel({ id, title: "Test title", body: "Test body" });
-  }
-
-  findAll() {
-    return Array<FakeModel>();
-  }
-
-  build(modelData: any) {
-    modelData._id = randomInt(9999);
-    modelData.save = () => {
-      return modelData;
-    };
-
-    modelData.get = () => {
-      return modelData;
-    };
-
-    return modelData;
-  }
-
-  update(values: any, options: any) {
-    return [1];
-  }
-
-  destroy(options: any) {
-    return true;
-  }
-
-  get() {
-    return this._fakeData;
-  }
-}
-
-class FakeDbConnector {
-  private static _instance: FakeDbConnector;
-
-  _models: {
-    [key: string]: FakeModel;
-  };
-
-  private constructor() {
-    this._models = { fake: new FakeModel() };
-  }
-
-  public static get instance() {
-    return this._instance || (this._instance = new this());
-  }
-}
-
-export default class FakeDb extends Db {
-  private _fakeDbConnector: FakeDbConnector;
-
-  constructor() {
-    const noInit = true;
-    super(noInit);
-    this.initFakeDbConnector();
-  }
-
-  private async initFakeDbConnector() {
-    this._fakeDbConnector = FakeDbConnector.instance;
-
-    const models = ["posts"];
-    const fakeModel = new FakeModel();
-
-    models.forEach(model => {
-      fakeModel.name = model;
-      this._fakeDbConnector._models[fakeModel.name] = fakeModel;
-    });
-  }
-
-  public fakeModels() {
-    return this._fakeDbConnector._models;
-  }
-
-  public add(): any {
-    return null;
-  }
-}

+ 0 - 74
src/data-storage/fake-db/post/index.ts

@@ -1,74 +0,0 @@
-import FakeDb from "../";
-import Post from "../../../entities/post";
-import PostDb from "../../db/post";
-
-export default class FakePostDb extends PostDb {
-  private _fakeDb: FakeDb;
-
-  constructor(_db: FakeDb) {
-    super(_db);
-    this._fakeDb = _db;
-  }
-
-  public async getById(id: number): Promise<any> {
-    const result = await this._fakeDb.fakeModels().posts.findByPk(id);
-    if (!result) {
-      return null;
-    }
-
-    return { id, ...result.get() };
-  }
-
-  public async getAll(): Promise<Array<any>> {
-    const result = await this._fakeDb.fakeModels().posts.findAll();
-    if (!result) {
-      return null;
-    }
-
-    return result;
-  }
-
-  public async add(post: Post): Promise<any> {
-    const result = await this._fakeDb
-      .fakeModels()
-      .posts.build({ ...post })
-      .save();
-
-    if (!result) {
-      return null;
-    }
-
-    return result.get();
-  }
-
-  public async update(postData: any): Promise<number> {
-    const result = await this._fakeDb.fakeModels().posts.update(
-      { ...postData },
-      {
-        where: {
-          id: Number(postData.id)
-        }
-      }
-    );
-
-    if (!result[0]) {
-      return null;
-    }
-
-    return result[0];
-  }
-
-  public async delete(post: Post): Promise<any> {
-    const result = await this._fakeDb.fakeModels().posts.destroy({
-      where: {
-        id: post.id
-      }
-    });
-
-    if (!result) {
-      return null;
-    }
-
-    return result;
-  }
-}

+ 14 - 9
src/frameworks/express/express-app.ts

@@ -1,14 +1,14 @@
 import * as cors from "cors";
 import * as express from "express";
 import * as morgan from "morgan";
-import { createHandler } from "graphql-http/lib/use/express";
+import * as helmet from "helmet";
+import { ApolloServer } from "@apollo/server";
+import { expressMiddleware } from "@apollo/server/express4";
 
-import { apiRouter, webRouter } from "./routers";
+import { apiRouter, webRouter } from "./routers/v1";
 import { swaggerUIServe, swaggerUISetup } from "../../libs/swagger";
 import { schema, root } from "../../libs/graphql";
 
-const helmet = require("helmet");
-
 export default class ExpressApp {
   private static _instance: express.Express;
 
@@ -16,13 +16,18 @@ export default class ExpressApp {
   private constructor() {}
 
   private static initRouters() {
-    this._instance.use("/api", apiRouter);
+    this._instance.use("/api/v1", apiRouter);
     this._instance.use("/", webRouter);
     this._instance.use("/swagger", swaggerUIServe, swaggerUISetup);
-    this._instance.use("/graphql", createHandler({
-      schema: schema,
+  }
+
+  private static async initApolloServer() {
+    const apolloServer = new ApolloServer({
+      typeDefs: schema,
       rootValue: root
-    }));
+    });
+    await apolloServer.start();
+    this._instance.use("/graphql", cors<cors.CorsRequest>(), express.json(), expressMiddleware(apolloServer));
   }
 
   public static get instance(): express.Express {
@@ -38,7 +43,6 @@ export default class ExpressApp {
       this._instance.use(helmet.crossOriginOpenerPolicy());
       this._instance.use(helmet.crossOriginResourcePolicy());
       this._instance.use(helmet.dnsPrefetchControl());
-      this._instance.use(helmet.expectCt());
       this._instance.use(helmet.frameguard());
       this._instance.use(helmet.hidePoweredBy());
       this._instance.use(helmet.hsts());
@@ -50,6 +54,7 @@ export default class ExpressApp {
       this._instance.use(helmet.xssFilter());
 
       this.initRouters();
+      this.initApolloServer();
     }
     return this._instance;
   }

+ 0 - 34
src/frameworks/express/middlewares/verify-token.ts

@@ -1,34 +0,0 @@
-import { Request, Response } from "express";
-import * as jwt from "jsonwebtoken";
-
-export default (req: Request, res: Response, next: Function) => {
-  const token: string = req.headers.authorization;
-  if (!token) {
-    return res.status(403).json({
-      auth: false,
-      status: "NO_OK",
-      code: 407,
-      message: "No token provided."
-    });
-  }
-
-  try {
-    const secret = process.env.SECRET;
-
-    jwt.verify(token, secret, (err: any, decoded: any) => {
-      if (err) {
-        throw new Error(err.name);
-      }
-      // if everything good, save to request for use in other routes
-      const data = decoded.data;
-      req.headers["x-data"] = data;
-      next();
-    });
-  } catch (err) {
-    return res.status(503).send({
-      auth: false,
-      status: "NO_OK",
-      message: err.message
-    });
-  }
-};

+ 0 - 64
src/frameworks/express/routers/api-router.ts

@@ -1,64 +0,0 @@
-import { Request, Response, Router } from "express";
-
-import PostController from "../../../controllers/api/posts-controller";
-
-export default class ApiRouter {
-  private static _instance: Router;
-
-  private constructor() {
-    return this;
-  }
-
-  private static initRoutes() {
-    this._instance.get("/status", (req: Request, res: Response) => {
-      res.json("OK").status(200);
-    });
-
-    const postController = new PostController();
-
-    this._instance.get("/posts", async (req: Request, res: Response) => {
-      postController.petition = this.buildPetition(req, res);
-      const response = await postController.index();
-      return res.status(response.statusCode).json(response);
-    });
-
-    this._instance.post("/posts", async (req: Request, res: Response) => {
-      postController.petition = this.buildPetition(req, res);
-      const response = await postController.store();
-      return res.status(response.statusCode).json(response);
-    });
-
-    this._instance.get("/posts/:id", async (req: Request, res: Response) => {
-      postController.petition = this.buildPetition(req, res);
-      const response = await postController.show();
-      return res.status(response.statusCode).json(response);
-    });
-
-    this._instance.put("/posts/:id", async (req: Request, res: Response) => {
-      postController.petition = this.buildPetition(req, res);
-      const response = await postController.update();
-      return res.status(response.statusCode).json(response);
-    });
-
-    this._instance.delete("/posts/:id", async (req: Request, res: Response) => {
-      postController.petition = this.buildPetition(req, res);
-      const response = await postController.destroy();
-      return res.status(response.statusCode).json(response);
-    });
-  }
-
-  private static buildPetition(req: Request, res: Response) {
-    return {
-      request: { ...req, payload: req.body },
-      response: { ...res, status: "OK" }
-    };
-  }
-
-  public static get instance(): Router {
-    if (!this._instance) {
-      this._instance = Router();
-      this.initRoutes();
-    }
-    return this._instance;
-  }
-}

+ 30 - 0
src/frameworks/express/routers/v1/api-router.ts

@@ -0,0 +1,30 @@
+import { Request, Response, Router } from "express";
+
+export default class ApiRouter {
+  private static _instance: Router;
+
+  private constructor() {
+    return this;
+  }
+
+  private static initRoutes() {
+    this._instance.get("/ping", (req: Request, res: Response) => {
+      res.json("pong").status(200);
+    });
+  }
+
+  private static buildPetition(req: Request, res: Response) {
+    return {
+      request: { ...req, payload: req.body },
+      response: { ...res, status: "OK" }
+    };
+  }
+
+  public static get instance(): Router {
+    if (!this._instance) {
+      this._instance = Router();
+      this.initRoutes();
+    }
+    return this._instance;
+  }
+}

+ 0 - 0
src/frameworks/express/routers/index.ts → src/frameworks/express/routers/v1/index.ts


+ 23 - 0
src/frameworks/express/routers/v1/web-router.ts

@@ -0,0 +1,23 @@
+import { Request, Response, Router } from "express";
+
+export default class WebRouter {
+  private static _instance: Router;
+
+  private constructor() {
+    return this;
+  }
+
+  private static initRoutes() {
+    this._instance.get("/status", (req: Request, res: Response) => {
+      res.send("OK").status(200);
+    });
+  }
+
+  public static get instance() {
+    if (!this._instance) {
+      this._instance = Router();
+      this.initRoutes();
+    }
+    return this._instance;
+  }
+}

+ 0 - 39
src/frameworks/express/routers/web-router.ts

@@ -1,39 +0,0 @@
-import { Request, Response, Router } from "express";
-
-import { Petition } from "../../../controllers/http-petition";
-import { ExampleController } from "../../../controllers/api/example-controller";
-
-export default class WebRouter {
-  private static _instance: Router;
-
-  private constructor() {
-    return this;
-  }
-
-  private static initRoutes() {
-    this._instance.get("/status", (req: Request, res: Response) => {
-      res.send("OK").status(200);
-    });
-
-    const exampleController = new ExampleController();
-    this._instance.get("/example", (req: Request, res: Response) => {
-      exampleController.petition = this.getControllerPetition(req, res);
-      return exampleController.index();
-    });
-  }
-
-  private static getControllerPetition(req: Request, res: Response): Petition {
-    return {
-      request: { ...req, payload: req.body },
-      response: { ...res, status: "", headers: null, payload: {} }
-    };
-  }
-
-  public static get instance() {
-    if (!this._instance) {
-      this._instance = Router();
-      this.initRoutes();
-    }
-    return this._instance;
-  }
-}

+ 6 - 6
src/frameworks/sequelize/index.ts

@@ -25,8 +25,8 @@ export default class SequelizeDbConnector {
 
     const basePath = path.join(__dirname, "../../");
     const confPath = this._isTestingEnv
-      ? `${basePath}test/conf.test.env`
-      : `${basePath}conf.env`;
+      ? `${basePath}test/.env.test`
+      : `${basePath}.env`;
 
     const result = dotenv.config({
       path: confPath
@@ -37,11 +37,11 @@ export default class SequelizeDbConnector {
     }
 
     const client = process.env.DB_CLIENT || "mysql";
-    const host = process.env.DB_HOST;
-    const port = Number(process.env.DB_PORT);
-    const user = process.env.DB_USER;
+    const host = process.env.DB_HOST || "localhost";
+    const port = Number(process.env.DB_PORT) || 3306;
+    const user = process.env.DB_USER || "";
     const password = process.env.DB_PASS;
-    const schema = process.env.DB_SCHEMA;
+    const schema = process.env.DB_SCHEMA || "";
 
     this.sequelize = new Sequelize(schema, user, password, {
       host,

+ 32 - 34
src/frameworks/sequelize/models/generic_extr.ts → src/frameworks/sequelize/models/generic-extr.ts

@@ -1,30 +1,5 @@
 import { Sequelize, Optional, Model, DataTypes } from "sequelize";
-
-interface GenericExtr {
-  year: number;
-  maker: string;
-  model: string;
-  version: string;
-  color: string;
-  price: number;
-  title: string;
-  description: string;
-  plate: string;
-  mileage: number;
-  gearbox: string;
-  engine: string;
-  fuel: string;
-  imageUrl: string;
-  code: string;
-  url: string;
-  region: string;
-  city: string;
-  sellerType: string;
-  sellerName: string;
-  publicationCreationDate: Date;
-  publicationUpdateDate: Date;
-  publicationCreatedAt: Date;
-}
+import GenericExtr from "../../../data-storage/db/extr/generic-extr";
 
 interface GenericExtrCreationAttributes extends Optional<GenericExtr, 'publicationCreationDate' | 'publicationUpdateDate'> { }
 
@@ -55,28 +30,51 @@ async function createGenericExtrModel(sequelize: Sequelize, tableName: string) {
     gearbox: DataTypes.STRING,
     engine: DataTypes.STRING,
     fuel: DataTypes.STRING,
-    imageUrl: DataTypes.STRING,
-    code: DataTypes.STRING,
+    imageUrl: {
+      type: DataTypes.STRING,
+      field: "image_url"
+    },
+    code: {
+      type: DataTypes.STRING,
+      primaryKey: true
+    },
     url: DataTypes.STRING,
     region: DataTypes.STRING,
     city: DataTypes.STRING,
-    sellerType: DataTypes.STRING,
-    sellerName: DataTypes.STRING,
+    sellerType: {
+      type: DataTypes.STRING,
+      field: "seller_type",
+    },
+    sellerName: {
+      type: DataTypes.STRING,
+      field: "seller_name",
+    },
     publicationCreationDate: {
       type: DataTypes.DATE,
       allowNull: false,
-      defaultValue: new Date()
+      defaultValue: new Date(),
+      field: "publication_creation"
+    },
+    publicationCreationRawDate: {
+      type: DataTypes.DATE,
+      allowNull: false,
+      defaultValue: new Date(),
+      field: "publication_creation_raw"
     },
     publicationUpdateDate: {
       type: DataTypes.DATE,
       allowNull: true,
-      defaultValue: null
+      defaultValue: null,
+      field: "publication_update"
     },
     publicationCreatedAt: {
       type: DataTypes.DATE,
       allowNull: false,
-      defaultValue: new Date()
-    }
+      defaultValue: new Date(),
+      field: "createdAt"
+    },
+  }, {
+    timestamps: false
   })
 }
 

+ 3 - 3
src/index.ts

@@ -34,8 +34,8 @@ class HttpServer {
   private initConfig() {
     this._basePath = path.join(__dirname, "./");
     const confPath = this._isTestingEnv
-      ? `${this._basePath}test/conf.test.env`
-      : `${this._basePath}conf.env`;
+      ? `${this._basePath}test/.env.test`
+      : `${this._basePath}.env`;
 
     const result = dotenv.config({
       path: confPath
@@ -53,7 +53,7 @@ class HttpServer {
 
   private initLog() {
     this._log = Log.instance;
-    this._log.logger.level = process.env.LOG_LEVEL;
+    this._log.logger.level = process.env.LOG_LEVEL || "info";
   }
 
   private initExpressApp() {

+ 30 - 0
src/lambda-apollo.ts

@@ -0,0 +1,30 @@
+import { ApolloServer } from '@apollo/server';
+import {
+  startServerAndCreateLambdaHandler,
+  handlers,
+} from '@as-integrations/aws-lambda';
+
+// The GraphQL schema
+const typeDefs = `#graphql
+  type Query {
+    hello: String!
+  }
+`;
+
+// A map of functions which return data for the schema.
+const resolvers = {
+  Query: {
+    hello: () => 'world',
+  },
+};
+
+// Set up Apollo Server
+const server = new ApolloServer({
+  typeDefs,
+  resolvers,
+});
+
+export default startServerAndCreateLambdaHandler(
+  server,
+  handlers.createAPIGatewayProxyEventV2RequestHandler(),
+);

+ 5 - 0
src/lambda-express.ts

@@ -0,0 +1,5 @@
+import ExpressApp from "./frameworks/express/express-app";
+
+// eslint-disable-next-line @typescript-eslint/no-var-requires
+const serverlessExpress = require("@codegenie/serverless-express");
+export default serverlessExpress({ app: ExpressApp.instance });

+ 99 - 62
src/libs/graphql/index.ts

@@ -1,84 +1,121 @@
-import { GraphQLSchema, GraphQLObjectType, GraphQLString } from "graphql";
+import { GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLInputObjectType, GraphQLInt, GraphQLFloat, GraphQLID, GraphQLList } from "graphql";
 import Db from "../../data-storage/db";
 import ExtrDb from "../../data-storage/db/extr";
 
-import { Chileautos } from "../../entities";
-
 import { allExtr, getExtr } from "../../use-cases/extr";
 
+const ExtrSchema = new GraphQLObjectType({
+  name: "Extr",
+  fields: {
+    year: { type: GraphQLInt },
+    maker: { type: GraphQLString },
+    model: { type: GraphQLString },
+    version: { type: GraphQLString },
+    color: { type: GraphQLString },
+    price: { type: GraphQLFloat },
+    title: { type: GraphQLString },
+    description: { type: GraphQLString },
+    plate: { type: GraphQLString },
+    mileage: { type: GraphQLFloat },
+    gearbox: { type: GraphQLString },
+    engine: { type: GraphQLString },
+    fuel: { type: GraphQLString },
+    imageUrl: { type: GraphQLString },
+    code: { type: GraphQLID },
+    url: { type: GraphQLString },
+    region: { type: GraphQLString },
+    city: { type: GraphQLString },
+    sellerType: { type: GraphQLString },
+    sellerName: { type: GraphQLString },
+    publicationCreationDate: { type: GraphQLString },
+    publicationCreationRawDate: { type: GraphQLString },
+    publicationUpdateDate: { type: GraphQLString },
+    publicationCreatedAt: { type: GraphQLString },
+  }
+});
+
+const ExtrExtSchema = new GraphQLObjectType({
+  name: "ExtrExt",
+  fields: {
+    Extrs: { type: new GraphQLList(ExtrSchema) },
+    Total: { type: GraphQLInt }
+  }
+});
+
+
+const Filter = new GraphQLInputObjectType({
+  name: "Filter",
+  fields: {
+    year: { type: GraphQLInt },
+    maker: { type: GraphQLString },
+    model: { type: GraphQLString },
+    version: { type: GraphQLString },
+    color: { type: GraphQLString },
+    price: { type: GraphQLFloat },
+    title: { type: GraphQLString },
+    description: { type: GraphQLString },
+    plate: { type: GraphQLString },
+    mileage: { type: GraphQLFloat },
+    gearbox: { type: GraphQLString },
+    engine: { type: GraphQLString },
+    fuel: { type: GraphQLString },
+    imageUrl: { type: GraphQLString },
+    code: { type: GraphQLID },
+    url: { type: GraphQLString },
+    region: { type: GraphQLString },
+    city: { type: GraphQLString },
+    sellerType: { type: GraphQLString },
+    sellerName: { type: GraphQLString },
+    publicationCreationDate: { type: GraphQLString },
+    publicationCreationRawDate: { type: GraphQLString },
+    publicationUpdateDate: { type: GraphQLString },
+    publicationCreatedAt: { type: GraphQLString },
+  }
+});
+
 const schema = new GraphQLSchema({
   query: new GraphQLObjectType({
-    name: "Query",
+    name: "Extrs",
     fields: {
-      code: {
-        type: GraphQLString,
-        resolve(rootValue) {
-          return "Hello World";
+      getExtr: {
+        type: ExtrSchema,
+        args: {
+          code: { type: GraphQLID },
         }
-      }
+      },
+      allExtrs: {
+        type: ExtrExtSchema,
+        args: {
+          filters: {
+            type: Filter,
+            defaultValue: {}
+          }
+        }
+      },
     }
   })
-})
-// (`
-//   type RandomDie {
-//     numSides: Int!
-//     rollOnce: Int!
-//     roll(numRolls: Int!): [Int]
-//   }
-
-//   type Extr {
-//     year: Int!
-//     maker: String!
-//     model: String!
-//     version: String
-//     color: String
-//     price: Float!
-//     title: String!
-//     description: String
-//     plate: String
-//     mileage: Float
-//     gearbox: String
-//     engine: String
-//     fuel: String
-//     imageUrl: String
-//     code: String!
-//     url: String!
-//     region: String
-//     city: String
-//     sellerType: String
-//     sellerName: String
-//     publicationCreationDate: String!
-//     publicationUpdateDate: String!
-//     publicationCreatedAt: String!
-//   }
-
-//   type Query {
-//     hello: String
-//     rollDice(numDice: Int!, numSides: Int): [Int]
-//     getExtrs(code: String): [Extr]
-//     getExtr(code: String): Extr
-//   }
-// `);
+});
 
 const tblName = "chileautos";
 
 const db = new Db();
 const extrDb = new ExtrDb(db, tblName);
 
+
 const root = {
-  hello: () => "hello",
-  rollDice: (args: any) => {
-    const output = [];
-    for (let i = 0; i < args.numDice; i++) {
-      output.push(1 + Math.floor(Math.random() * (args.numSides || 6)));
-    }
-    return output;
-  },
-  getExtrs: ({ code }: any) => {
-    return code ? allExtr.filteredBy({ code }, extrDb) : allExtr.get(extrDb);
+  getExtr: ({ code }: any, __: any, info: any) => {
+    const requestedFields = info.fieldNodes[0].selectionSet.selections.map((field:any) => field.name.value);
+    return getExtr.byCode(code, requestedFields, extrDb);
   },
-  getExtr: ({ id }: any) => {
-    return getExtr.byCode(id, extrDb);
+  allExtrs: async (inputs: any, _: any, info: any) => {
+    console.log("Inputs", inputs);
+
+    const filteredFields = info.fieldNodes[0].selectionSet.selections.filter((field: any) => field.kind === "Field" && field.name.value === "Extrs")[0]
+    const requestedFields = filteredFields.selectionSet.selections.map((field:any) => field.name.value);
+    console.log("Requested Fields", requestedFields);
+
+    const results = inputs.filters ? await allExtr.filteredBy(inputs.filters, requestedFields, extrDb) : await allExtr.get(requestedFields, extrDb);
+    return { Extrs: results, Total: results.length };
   }
 };
 

+ 6 - 30
src/libs/log/index.ts

@@ -1,6 +1,5 @@
 import * as dotenv from "dotenv";
 import * as path from "path";
-import * as fs from "fs";
 import * as log4js from "log4js";
 
 export default class Log {
@@ -9,14 +8,8 @@ export default class Log {
   private _logger: log4js.Logger;
 
   private constructor() {
-    const logsDirectory = path.join(__dirname, "../../logs");
-
-    if (!fs.existsSync(logsDirectory)) {
-      fs.mkdirSync(logsDirectory);
-    }
-
     this.initConfig();
-    this.configureLog(logsDirectory);
+    this.configureLog();
   }
 
   private initConfig() {
@@ -24,8 +17,8 @@ export default class Log {
 
     const basePath = path.join(__dirname, "../../");
     const confPath = this._isTestingEnv
-      ? `${basePath}test/conf.test.env`
-      : `${basePath}conf.env`;
+      ? `${basePath}test/.env.test`
+      : `${basePath}.env`;
 
     const result = dotenv.config({
       path: confPath
@@ -36,35 +29,18 @@ export default class Log {
     }
   }
 
-  private configureLog(logsDirectory: string) {
-    const today = new Date();
-    const month = (today.getMonth() + 1).toString().padStart(2, "0");
-    const logFileName = `/logs-${today.getFullYear()}${month}${today.getDate()}.log`;
-
+  private configureLog (): void {
     log4js.configure({
       appenders: {
-        app: {
-          backups: 5,
-          compress: true,
-          filename: logsDirectory.concat(logFileName),
-          layout: {
-            pattern: "[%d{dd/MM/yyyy hh:mm:ss,SSS}] %p [%z] %m%n",
-            type: "pattern"
-          },
-          maxLogSize: 204857600, // 200MB
-          type: "file"
-        },
         out: { type: "stdout", layout: { type: "coloured" } }
       },
       categories: {
-        default: { appenders: ["out", "app"], level: "debug" }
+        default: { appenders: ["out"], level: "debug" }
       }
     });
 
     this._logger = log4js.getLogger();
-
-    // console.debug("LogLevel", process.env.LOG_LEVEL);
-    this._logger.level = process.env.LOG_LEVEL;
+    this._logger.level = process.env.LOG_LEVEL || "info";
   }
 
   public static get instance() {

+ 9 - 0
src/test/.env.test

@@ -0,0 +1,9 @@
+PORT = 8000
+LOG_LEVEL = debug
+
+DB_CLIENT = mysql
+DB_HOST = localhost
+DB_PORT = 3306
+DB_USER = pablo
+DB_PASS = VidaNueva02.
+DB_SCHEMA = pablo_api_extr

+ 0 - 19
src/test/post/add.test.ts

@@ -1,19 +0,0 @@
-import { Post } from "../../entities";
-import Add from "../../use-cases/post/add";
-import FakeDb from "../../data-storage/fake-db";
-import FakePostDb from "../../data-storage/fake-db/post";
-
-describe("Se intentará agregar un nuevo post", () => {
-  test("Agregar un nuevo post", () => {
-    const add = new Add();
-    const fakeDb = new FakeDb();
-    const fakePostDb = new FakePostDb(fakeDb);
-    const newPost = new Post();
-    newPost.authorId = 1;
-    newPost.title = "Test title";
-    newPost.body = "Test body";
-    return add.save(newPost, fakePostDb).then(data => {
-      expect(data).toHaveProperty("_id");
-    });
-  });
-});

+ 0 - 14
src/test/post/all.test.ts

@@ -1,14 +0,0 @@
-import All from "../../use-cases/post/all";
-import FakeDb from "../../data-storage/fake-db";
-import FakePostDb from "../../data-storage/fake-db/post";
-
-describe("Se intentará obtener todos los posts", () => {
-  test("Obtener todos los posts", () => {
-    const all = new All();
-    const fakeDb = new FakeDb();
-    const fakePostDb = new FakePostDb(fakeDb);
-    return all.retrieve(fakePostDb).then(data => {
-      expect(data).toBeInstanceOf(Array);
-    });
-  });
-});

+ 0 - 18
src/test/post/delete.test.ts

@@ -1,18 +0,0 @@
-import Delete from "../../use-cases/post/delete";
-import FakeDb from "../../data-storage/fake-db";
-import FakePostDb from "../../data-storage/fake-db/post";
-import { randomInt } from "crypto";
-import { Post } from "../../entities";
-
-describe("Se intentará eliminar un nuevo post", () => {
-  test("Eliminar un post", () => {
-    const deletePost = new Delete();
-    const fakeDb = new FakeDb();
-    const fakePostDb = new FakePostDb(fakeDb);
-    const post = new Post();
-    post.id = randomInt(9999);
-    return deletePost.save(post, fakePostDb).then(data => {
-      expect(data).toBeTruthy();
-    });
-  });
-});

+ 0 - 15
src/test/post/get.test.ts

@@ -1,15 +0,0 @@
-import Get from "../../use-cases/post/get";
-import FakeDb from "../../data-storage/fake-db";
-import FakePostDb from "../../data-storage/fake-db/post";
-import { randomInt } from "crypto";
-
-describe("Se intentará obtener un post por su Id", () => {
-  test("Obtener un post", () => {
-    const get = new Get();
-    const fakeDb = new FakeDb();
-    const fakePostDb = new FakePostDb(fakeDb);
-    return get.byId(randomInt(9999), fakePostDb).then(data => {
-      expect(data).toHaveProperty("id");
-    });
-  });
-});

+ 0 - 26
src/test/post/update.test.ts

@@ -1,26 +0,0 @@
-import { Post } from "../../entities";
-import Update from "../../use-cases/post/update";
-import FakeDb from "../../data-storage/fake-db";
-import FakePostDb from "../../data-storage/fake-db/post";
-import { randomInt } from "crypto";
-
-describe("Se intentará actualizar un nuevo post", () => {
-  test("Actualizar un nuevo post", () => {
-    const update = new Update();
-    const fakeDb = new FakeDb();
-    const fakePostDb = new FakePostDb(fakeDb);
-    const postData = {
-      id: randomInt(9999),
-      title: "Test title",
-      body: "Test body",
-      authorId: 1,
-      published: false
-    };
-    const newPost = new Post();
-    newPost.title = postData.title;
-    newPost.body = postData.body;
-    return update.save(newPost, postData, fakePostDb).then(data => {
-      expect(data).toBeTruthy();
-    });
-  });
-});

+ 0 - 24
src/test/send-mail.test.ts

@@ -1,24 +0,0 @@
-import Mailer from "../libs/mailer";
-import { EmailParams } from "../libs/mailer/params-interface";
-
-describe("Se intenta enviar un email", () => {
-  test("Se enviará un email", async () => {
-    const mailer = new Mailer();
-    const emailParms: EmailParams = {
-      from: "contacto@pablo.by",
-      html: "<p>Email de prueba</p>",
-      subject: "Email de prueba",
-      to: ["contacto@pablo.by"]
-    };
-    const result = await mailer.sendEmail(emailParms).catch(err => {
-      console.error(err.message);
-    });
-    expect(result.response.toString()).toMatch(/Ok/);
-  });
-
-  // Hack
-  // https://github.com/visionmedia/supertest/issues/520#issuecomment-469044925
-  afterAll(async () => {
-    await new Promise(resolve => setTimeout(() => resolve(null), 2000)); // avoid jest open handle error
-  });
-});

+ 4 - 4
src/test/server-status.test.ts

@@ -1,7 +1,7 @@
 import * as request from "supertest";
 import ExpressApp from "../frameworks/express/express-app";
 
-describe("Comprueba si las rutas sin accesibles a través de las interfaces Web y API", () => {
+describe("Comprueba si las rutas son accesibles a través de las interfaces Web y API", () => {
   const expressApp = ExpressApp.instance;
 
   test("Obtiene el estado del Servidor vía interfaz Web", async () => {
@@ -10,12 +10,12 @@ describe("Comprueba si las rutas sin accesibles a través de las interfaces Web
     expect(response.text).toBe("OK");
   });
 
-  test("Obtiene el estado del Servidor via interfaz API", async () => {
-    const response = await request(expressApp).get("/api/status");
+  test("Hace un ping al Servidor via interfaz API", async () => {
+    const response = await request(expressApp).get("/api/v1/ping");
 
     expect(response.headers["content-type"]).toMatch(/json/);
     expect(response.status).toEqual(200);
-    expect(response.body).toBe("OK");
+    expect(response.body).toBe("pong");
   });
 
   // Hack

+ 37 - 31
src/use-cases/extr/all.ts

@@ -1,47 +1,53 @@
-import { Chileautos } from "../../entities/extr";
-
 import ExtrDb from "../../data-storage/db/extr";
+import GenericExtr from "../../data-storage/db/extr/generic-extr";
 
 export default class All {
   private _db: ExtrDb;
 
-  public async get(db: ExtrDb): Promise<Array<Chileautos>> {
+  public async get(requestedFields: Array<string>, db: ExtrDb): Promise<Array<GenericExtr>> {
     this._db = db;
-    return this.build(await this._db.getAll());
+    return this.build(await this._db.getAll(requestedFields));
   }
 
-  public async filteredBy({code = ''}, db: ExtrDb): Promise<Array<Chileautos>> {
-    return this.build(await this._db.getFilteredBy({code}));
+  public async filteredBy(filters: any, requestedFields: Array<string>, db: ExtrDb): Promise<Array<GenericExtr>> {
+    this._db = db;
+    return this.build(await this._db.getFilteredBy(filters, requestedFields));
   }
 
   private build(
-    postsData: Array<{
-      id?: number;
-      title: string;
-      body: string;
-      author?: Author;
-      authorId: number;
-      published: boolean;
-      createdAt: number;
-      updatedAt: number;
-    }>
-  ): Array<Chileautos> {
-    if (!postsData) {
-      throw new Error("No posts found");
+    extrsData: Array<GenericExtr>
+  ): Array<GenericExtr> {
+    if (!extrsData) {
+      throw new Error("No extrs found");
     }
 
-    return postsData.map(postData => {
-      const post = new Chileautos();
-      post.id = postData.id || 0;
-      post.title = postData.title || "";
-      post.body = postData.body || "";
-      post.author = postData.author || null;
-      post.authorId = postData.authorId || 0;
-      post.published = postData.published || false;
-      post.createdAt = postData.createdAt;
-      post.updatedAt = postData.updatedAt;
-
-      return post;
+    return extrsData.map(extrData => {
+      return {
+        year: extrData.year,
+        maker: extrData.maker,
+        model: extrData.model,
+        version: extrData.version,
+        color: extrData.color,
+        price: extrData.price,
+        title: extrData.title,
+        description: extrData.description,
+        plate: extrData.plate,
+        mileage: extrData.mileage,
+        gearbox: extrData.gearbox,
+        engine: extrData.engine,
+        fuel: extrData.fuel,
+        imageUrl: extrData.imageUrl,
+        code: extrData.code,
+        url: extrData.url,
+        region: extrData.region,
+        city: extrData.city,
+        sellerType: extrData.sellerType,
+        sellerName: extrData.sellerName,
+        publicationCreationDate: extrData.publicationCreationDate,
+        publicationCreationRawDate: extrData.publicationCreationRawDate,
+        publicationUpdateDate: extrData.publicationUpdateDate,
+        publicationCreatedAt: extrData.publicationCreatedAt,
+      };
     });
   }
 }

+ 5 - 16
src/use-cases/extr/get.ts

@@ -1,30 +1,19 @@
-import { Chileautos } from "../../entities/";
-
 import ExtrDb from "../../data-storage/db/extr";
+import GenericExtr from "../../data-storage/db/extr/generic-extr";
 
 export default class Get {
   private _db: ExtrDb;
 
-  public async byCode(code: string, db: ExtrDb) {
+  public async byCode(code: string, requestedFields: Array<string>, db: ExtrDb) {
     this._db = db;
-    return this.build(await this._db.getByCode(code));
+    return this.build(await this._db.getByCode(code, requestedFields));
   }
 
-  private build(itemData: Chileautos): Chileautos {
+  private build(itemData: GenericExtr | null): GenericExtr {
     if (!itemData) {
       throw new Error("Register not found");
     }
 
-    // const post = new Chileautos();
-    // post.id = postData.id || 0;
-    // post.title = postData.title || "";
-    // post.body = postData.body || "";
-    // post.author = postData.author || null;
-    // post.authorId = postData.authorId || 0;
-    // post.published = postData.published || false;
-    // post.createdAt = postData.createdAt;
-    // post.updatedAt = postData.updatedAt;
-
-    return itemData as Chileautos;
+    return itemData;
   }
 }

+ 2 - 0
tsconfig.json

@@ -1,5 +1,7 @@
 {
   "compilerOptions": {
+      "preserveConstEnums": true,
+      "strictNullChecks": true,
       "outDir": "dist",
       "sourceMap": false,
       "noImplicitAny": true,

Some files were not shown because too many files changed in this diff