From 9f4098259ba6fa6181bca9f0f02e8e53e764995a Mon Sep 17 00:00:00 2001 From: Rafael Esposito Date: Fri, 22 Oct 2021 15:54:06 +0200 Subject: [PATCH 1/4] Changes for use lookup and fixes --- client/src/lib/api.author.js | 31 +++++ client/src/lib/api.book.js | 31 +++++ client/src/lib/api.js | 1 + client/src/lib/requests.js | 14 -- client/src/routes/authors.svelte | 18 +-- client/src/routes/books.svelte | 219 +++++++++++++++++++----------- server/interfaces/models/IBook.ts | 2 +- server/models/author.ts | 3 +- server/models/book.ts | 25 +++- server/services/book.ts | 7 + 10 files changed, 237 insertions(+), 114 deletions(-) create mode 100644 client/src/lib/api.author.js create mode 100644 client/src/lib/api.book.js create mode 100644 client/src/lib/api.js delete mode 100644 client/src/lib/requests.js diff --git a/client/src/lib/api.author.js b/client/src/lib/api.author.js new file mode 100644 index 0000000..460f2c3 --- /dev/null +++ b/client/src/lib/api.author.js @@ -0,0 +1,31 @@ +import axios from 'axios' +import { + API_PATH +} from './api'; + +const apiAuthorPath = `${API_PATH}/author`; + +function findAuthors() { + return axios.get(apiAuthorPath); +} + +function insertAuthor(payload) { + return axios.post(apiAuthorPath, payload); +} + +function updateAuthor(id, payload) { + const path = `${apiAuthorPath}/${id}`; + return axios.put(path, payload); +} + +function removeAuthor(id) { + const path = `${apiAuthorPath}/${id}`; + return axios.delete(path); +} + +export const apiAuthor = { + findAuthors, + insertAuthor, + updateAuthor, + removeAuthor +} \ No newline at end of file diff --git a/client/src/lib/api.book.js b/client/src/lib/api.book.js new file mode 100644 index 0000000..e1b7aff --- /dev/null +++ b/client/src/lib/api.book.js @@ -0,0 +1,31 @@ +import axios from 'axios' +import { + API_PATH +} from './api'; + +const apiBookPath = `${API_PATH}/book`; + +function findBooks() { + return axios.get(apiBookPath); +} + +function insertBook(payload) { + return axios.post(apiBookPath, payload); +} + +function updateBook(id, payload) { + const path = `${apiBookPath}/${id}`; + return axios.put(path, payload); +} + +function removeBook(id) { + const path = `${apiBookPath}/${id}`; + return axios.delete(path); +} + +export const apiBook = { + findBooks, + insertBook, + updateBook, + removeBook +} \ No newline at end of file diff --git a/client/src/lib/api.js b/client/src/lib/api.js new file mode 100644 index 0000000..9a415f5 --- /dev/null +++ b/client/src/lib/api.js @@ -0,0 +1 @@ +export const API_PATH = '/api'; \ No newline at end of file diff --git a/client/src/lib/requests.js b/client/src/lib/requests.js deleted file mode 100644 index 6390828..0000000 --- a/client/src/lib/requests.js +++ /dev/null @@ -1,14 +0,0 @@ -import axios from 'axios' - -const apiPath = '/api/author' - -function getAuthors () { - axios.get(`${apiPath}`) - .then((res) => { - return (res.data) - }) -} - -export default { - getAuthors -} diff --git a/client/src/routes/authors.svelte b/client/src/routes/authors.svelte index 977db6c..e7ec02c 100644 --- a/client/src/routes/authors.svelte +++ b/client/src/routes/authors.svelte @@ -1,5 +1,4 @@ @@ -131,52 +153,78 @@
-
+
- - - - - + {#each books as book} - - - - - - - + + + + + + + {/each}
+ Name + Author + Owner + Genres -
- {book.title} - - {#each book.authors as author} -

{author.name}

- {/each} -
- {book.owner} - - {#each book.genres as genre} -
- {genre} -
- {/each} -
- - -
+ {book.title} + + {#each book.authors as author} +

{author.name}

+ {/each} +
+ {book.owner} + + {#each book.genres as genre} +
+ {genre} +
+ {/each} +
+ + +
@@ -197,23 +245,34 @@ />

- + > + {#each authors as value} + + {/each} +

- - - Owner: + + + {#if updateopen} diff --git a/server/interfaces/models/IBook.ts b/server/interfaces/models/IBook.ts index 3f61cea..43afe71 100644 --- a/server/interfaces/models/IBook.ts +++ b/server/interfaces/models/IBook.ts @@ -3,7 +3,7 @@ import { IModel } from "./IModel"; export interface IBook { _id: string; title: string; - authors: { id: number, name: string }[]; + authors: any[]; genres: string[]; location: string; owner: string; diff --git a/server/models/author.ts b/server/models/author.ts index 4def18e..256f6d3 100644 --- a/server/models/author.ts +++ b/server/models/author.ts @@ -16,11 +16,12 @@ export const AuthorSchema: Schema = { required: ["name"], additionalProperties: false, } +const validatorSchema = addFormats(new Ajv().addKeyword('example')).compile(AuthorSchema); @injectable() export class AuthorModel extends Model implements Author { - protected validator = addFormats(new Ajv().addKeyword('example')).compile(AuthorSchema); + protected validator = validatorSchema; protected collectionName: string = 'authors'; } diff --git a/server/models/book.ts b/server/models/book.ts index 0236cf7..2ea11e5 100644 --- a/server/models/book.ts +++ b/server/models/book.ts @@ -3,6 +3,7 @@ import Ajv, { Schema } from "ajv" import { IBook, Book } from '../interfaces/models/IBook'; import { Model } from './model'; +import { AggregationCursor, Collection } from 'mongodb'; export const BookSchema: Schema = { type: "object", @@ -10,10 +11,7 @@ export const BookSchema: Schema = { _id: { type: "string" }, title: { type: "string", minLength: 3, example: "The Hitchhiker's Guide to the Galaxy" }, authors: { - type: "array", items: { type: "object" }, example: [{ - "id": 1, - "name": "Douglas Adams" - }] + type: "array", items: { type: "string" } }, genres: { type: "array", items: { type: "string" }, example: [ @@ -28,11 +26,28 @@ export const BookSchema: Schema = { required: ["title", "authors", "genres"], additionalProperties: false, } +const validatorSchema = new Ajv().addKeyword('example').compile(BookSchema); @injectable() export class BookModel extends Model implements Book { - protected validator = new Ajv().addKeyword('example').compile(BookSchema); + protected validator = validatorSchema; protected collectionName: string = 'books'; + public async findAll(): Promise { + return this.aggregate((await this.collection)).toArray(); + } + + private aggregate(collection: Collection): AggregationCursor { + return collection.aggregate([{ + $lookup: + { + from: 'authors', + localField: 'authors', + foreignField: '_id', + as: 'authors' + } + }]); + } + } diff --git a/server/services/book.ts b/server/services/book.ts index be44ea4..94cc4b0 100644 --- a/server/services/book.ts +++ b/server/services/book.ts @@ -1,3 +1,4 @@ +import { ObjectID } from "bson"; import createHttpError from "http-errors"; import { inject, injectable } from "inversify"; @@ -18,6 +19,9 @@ export class BookServiceImpl implements BookService { throw createHttpError(400, 'Not valid data', { details: this.bookModel.validatorErrors }); } delete bookData._id; + if (bookData?.authors.length) { + bookData.authors = bookData.authors.map(id => new ObjectID(id)); + } return this.bookModel.insert(bookData); } @@ -29,6 +33,9 @@ export class BookServiceImpl implements BookService { if (!this.bookModel.validate(bookData)) { throw createHttpError(400, 'Not valid data', { details: this.bookModel.validatorErrors }); } + if (bookData?.authors.length) { + bookData.authors = bookData.authors.map(id => new ObjectID(id)); + } delete bookData._id; return this.bookModel.update(id, bookData).then(res => bookData); } From 3a3ed87f579c1b599a1daf38bc3f6d5e98756743 Mon Sep 17 00:00:00 2001 From: Raffone17 Date: Wed, 27 Oct 2021 11:08:11 +0200 Subject: [PATCH 2/4] Update linter configurations --- .github/linters/.eslintrc.json | 85 +++++++++++++++++++++++++++++++ .github/linters/.htmlhintrc | 4 +- .github/workflows/superlinter.yml | 4 ++ 3 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 .github/linters/.eslintrc.json diff --git a/.github/linters/.eslintrc.json b/.github/linters/.eslintrc.json new file mode 100644 index 0000000..7a94ec6 --- /dev/null +++ b/.github/linters/.eslintrc.json @@ -0,0 +1,85 @@ +{ + "root": true, + "ignorePatterns": [ + "projects/**/*" + ], + "overrides": [ + { + "files": [ + "*.ts" + ], + "parserOptions": { + "project": [ + "tsconfig.json", + "e2e/tsconfig.json" + ], + "createDefaultProgram": true + }, + "extends": [ + "plugin:@angular-eslint/ng-cli-compat", + "plugin:@angular-eslint/ng-cli-compat--formatting-add-on", + "plugin:@angular-eslint/template/process-inline-templates" + ], + "rules": { + "@angular-eslint/component-class-suffix": [ + "error", + { + "suffixes": [ + "Page", + "Component" + ] + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "app", + "style": "kebab-case" + } + ], + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "app", + "style": "camelCase" + } + ], + "@typescript-eslint/naming-convention": [ + "warn", + { + "selector": "variable", + "format": [ + "camelCase" + ] + } + ], + "no-underscore-dangle": [ + "error", + { + "allow": [ + "_id" + ] + } + ], + "@typescript-eslint/no-inferrable-types": [ + 1, + { + "ignoreParameters": true, + "ignoreProperties": true + } + ] + } + }, + { + "files": [ + "*.html" + ], + "extends": [ + "plugin:@angular-eslint/template/recommended" + ], + "rules": {} + } + ] +} diff --git a/.github/linters/.htmlhintrc b/.github/linters/.htmlhintrc index 4ddbd55..98daa2a 100644 --- a/.github/linters/.htmlhintrc +++ b/.github/linters/.htmlhintrc @@ -10,7 +10,7 @@ "spec-char-escape": true, "id-unique": false, "src-not-empty": true, - "title-require": false, + "title-require": true, "alt-require": true, "doctype-html5": true, "id-class-value": false, @@ -21,5 +21,5 @@ "id-class-ad-disabled": false, "href-abs-or-rel": false, "attr-unsafe-chars": true, - "head-script-disabled": false + "head-script-disabled": true } diff --git a/.github/workflows/superlinter.yml b/.github/workflows/superlinter.yml index 6f7b959..cb76fa8 100644 --- a/.github/workflows/superlinter.yml +++ b/.github/workflows/superlinter.yml @@ -53,3 +53,7 @@ jobs: VALIDATE_ALL_CODEBASE: false DEFAULT_BRANCH: main GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + FILTER_REGEX_EXCLUDE: .*vscode/.*.json + VALIDATE_JAVASCRIPT_STANDARD: false + TYPESCRIPT_ES_CONFIG_FILE: .eslintrc.json + VALIDATE_TYPESCRIPT_STANDARD: false From 2ab4dbe5f877fd2f2eb66ef7fd0acd3529b801ff Mon Sep 17 00:00:00 2001 From: Raffone17 Date: Wed, 27 Oct 2021 16:25:20 +0200 Subject: [PATCH 3/4] Fixes for linter --- .github/linters/.eslintrc.json | 92 +++--------------------------- .github/workflows/superlinter.yml | 1 + client/src/app.html | 1 + package.json | 4 +- server/interfaces/models/IBook.ts | 2 +- server/loaders/koa.ts | 7 +-- server/loaders/mongo-client.ts | 4 -- server/loaders/mongo-connection.ts | 2 +- server/models/author.ts | 2 +- server/models/book.ts | 2 +- server/models/reader.ts | 2 +- server/models/user.ts | 2 +- 12 files changed, 23 insertions(+), 98 deletions(-) diff --git a/.github/linters/.eslintrc.json b/.github/linters/.eslintrc.json index 7a94ec6..7a098d1 100644 --- a/.github/linters/.eslintrc.json +++ b/.github/linters/.eslintrc.json @@ -1,85 +1,11 @@ { - "root": true, - "ignorePatterns": [ - "projects/**/*" + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"], + "extends": [ + "plugin:@typescript-eslint/recommended" ], - "overrides": [ - { - "files": [ - "*.ts" - ], - "parserOptions": { - "project": [ - "tsconfig.json", - "e2e/tsconfig.json" - ], - "createDefaultProgram": true - }, - "extends": [ - "plugin:@angular-eslint/ng-cli-compat", - "plugin:@angular-eslint/ng-cli-compat--formatting-add-on", - "plugin:@angular-eslint/template/process-inline-templates" - ], - "rules": { - "@angular-eslint/component-class-suffix": [ - "error", - { - "suffixes": [ - "Page", - "Component" - ] - } - ], - "@angular-eslint/component-selector": [ - "error", - { - "type": "element", - "prefix": "app", - "style": "kebab-case" - } - ], - "@angular-eslint/directive-selector": [ - "error", - { - "type": "attribute", - "prefix": "app", - "style": "camelCase" - } - ], - "@typescript-eslint/naming-convention": [ - "warn", - { - "selector": "variable", - "format": [ - "camelCase" - ] - } - ], - "no-underscore-dangle": [ - "error", - { - "allow": [ - "_id" - ] - } - ], - "@typescript-eslint/no-inferrable-types": [ - 1, - { - "ignoreParameters": true, - "ignoreProperties": true - } - ] - } - }, - { - "files": [ - "*.html" - ], - "extends": [ - "plugin:@angular-eslint/template/recommended" - ], - "rules": {} - } - ] -} + "rules": { + "@typescript-eslint/no-var-requires": 0, + "@typescript-eslint/no-empty-interface": 0 + } +} \ No newline at end of file diff --git a/.github/workflows/superlinter.yml b/.github/workflows/superlinter.yml index cb76fa8..b9cf5fb 100644 --- a/.github/workflows/superlinter.yml +++ b/.github/workflows/superlinter.yml @@ -57,3 +57,4 @@ jobs: VALIDATE_JAVASCRIPT_STANDARD: false TYPESCRIPT_ES_CONFIG_FILE: .eslintrc.json VALIDATE_TYPESCRIPT_STANDARD: false + VALIDATE_MARKDOWN: false diff --git a/client/src/app.html b/client/src/app.html index 756626f..96a01b4 100644 --- a/client/src/app.html +++ b/client/src/app.html @@ -1,6 +1,7 @@ + Blobfishes app diff --git a/package.json b/package.json index 905f5e3..7e24a78 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,8 @@ "@types/migrate-mongo": "^8.1.1", "@types/mongodb": "^3.6.20", "@types/node": "^14.17.5", + "@typescript-eslint/eslint-plugin": "^5.2.0", + "@typescript-eslint/parser": "^5.2.0", "color-string": ">=1.5.5", "concurrently": "^6.2.0", "glob-parent": ">=5.1.2", @@ -72,4 +74,4 @@ "tslint": "^6.1.3", "typescript": "^4.3.5" } -} \ No newline at end of file +} diff --git a/server/interfaces/models/IBook.ts b/server/interfaces/models/IBook.ts index 43afe71..e54e9c2 100644 --- a/server/interfaces/models/IBook.ts +++ b/server/interfaces/models/IBook.ts @@ -3,7 +3,7 @@ import { IModel } from "./IModel"; export interface IBook { _id: string; title: string; - authors: any[]; + authors: string[]; genres: string[]; location: string; owner: string; diff --git a/server/loaders/koa.ts b/server/loaders/koa.ts index a910cb2..a16ec98 100644 --- a/server/loaders/koa.ts +++ b/server/loaders/koa.ts @@ -1,6 +1,5 @@ import Koa from "koa"; import koaBody from "koa-body"; -import jwt from "koa-jwt"; import cors from "@koa/cors"; import serve from "koa-static"; import path from "path"; @@ -26,8 +25,8 @@ export default ({ app }: { app: Koa }) => { app.use(swaggerRoute.routes()).use(swaggerRoute.allowedMethods()); } - const unlessPaths = [/^\/api\/auth/, "/", '/api/status']; - /* app.use(jwt({ + /* const unlessPaths = [/^\/api\/auth/, "/", '/api/status']; + app.use(jwt({ secret: config.jwtSecret, algorithms: [config.jwtAlgorithm] }).unless({ @@ -40,7 +39,7 @@ export default ({ app }: { app: Koa }) => { function isRouteValid(url: string) { if (!validRoutes || !url) return false - const urlStart = url.split('/').filter(item => item)[0] || '';; + const urlStart = url.split('/').filter(item => item)[0] || ''; return validRoutes.find(route => urlStart === route) } app.use(async (ctx, next) => { diff --git a/server/loaders/mongo-client.ts b/server/loaders/mongo-client.ts index 69559df..422df1a 100644 --- a/server/loaders/mongo-client.ts +++ b/server/loaders/mongo-client.ts @@ -1,13 +1,9 @@ -import { Db, ObjectID } from 'mongodb'; import { injectable } from 'inversify'; import { MongoDBConnection } from './mongo-connection'; @injectable() export class MongoDBClient { - constructor() { - } - public async getDb() { return MongoDBConnection.getConnection(); } diff --git a/server/loaders/mongo-connection.ts b/server/loaders/mongo-connection.ts index f18e2a8..4c81996 100644 --- a/server/loaders/mongo-connection.ts +++ b/server/loaders/mongo-connection.ts @@ -3,7 +3,7 @@ import { Db, MongoClient } from 'mongodb'; import config from '../config'; export class MongoDBConnection { - private static isConnected: boolean = false; + private static isConnected = false; private static db: Db; public static async getConnection(): Promise { diff --git a/server/models/author.ts b/server/models/author.ts index 256f6d3..ee3ffcb 100644 --- a/server/models/author.ts +++ b/server/models/author.ts @@ -22,6 +22,6 @@ const validatorSchema = addFormats(new Ajv().addKeyword('example')).compile(Auth export class AuthorModel extends Model implements Author { protected validator = validatorSchema; - protected collectionName: string = 'authors'; + protected collectionName = 'authors'; } diff --git a/server/models/book.ts b/server/models/book.ts index 2ea11e5..8a4587f 100644 --- a/server/models/book.ts +++ b/server/models/book.ts @@ -32,7 +32,7 @@ const validatorSchema = new Ajv().addKeyword('example').compile(BookSchema); export class BookModel extends Model implements Book { protected validator = validatorSchema; - protected collectionName: string = 'books'; + protected collectionName = 'books'; public async findAll(): Promise { return this.aggregate((await this.collection)).toArray(); diff --git a/server/models/reader.ts b/server/models/reader.ts index 8e9fa05..4256415 100644 --- a/server/models/reader.ts +++ b/server/models/reader.ts @@ -28,6 +28,6 @@ export const ReaderSchema: Schema = { export class ReaderModel extends Model implements Reader { protected validator = addFormats(new Ajv().addKeyword('example')).compile(ReaderSchema); - protected collectionName: string = 'readers'; + protected collectionName = 'readers'; } diff --git a/server/models/user.ts b/server/models/user.ts index cce4dff..88659b7 100644 --- a/server/models/user.ts +++ b/server/models/user.ts @@ -22,7 +22,7 @@ export class UserModel extends Model implements User { protected validator = new Ajv().addKeyword('example').compile(Object.assign({ required: ["password"] }, UserSchema)); protected validatorUpdate = new Ajv().addKeyword('example').compile(UserSchema); - protected collectionName: string = 'users'; + protected collectionName = 'users'; public async findByEmail(email: string): Promise { return (await this.collection).findOne({ email }); From 2a1960a4e9cca4b7bc22c09e70fc1ed406d14ded Mon Sep 17 00:00:00 2001 From: Raffone17 Date: Wed, 17 Nov 2021 09:43:25 +0100 Subject: [PATCH 4/4] Fix Book interface --- server/interfaces/models/IBook.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/interfaces/models/IBook.ts b/server/interfaces/models/IBook.ts index e54e9c2..76e858c 100644 --- a/server/interfaces/models/IBook.ts +++ b/server/interfaces/models/IBook.ts @@ -1,9 +1,10 @@ +import { ObjectID } from "bson"; import { IModel } from "./IModel"; export interface IBook { _id: string; title: string; - authors: string[]; + authors: string[] | ObjectID[]; genres: string[]; location: string; owner: string;