diff --git a/.github/linters/.eslintrc.json b/.github/linters/.eslintrc.json
new file mode 100644
index 0000000..7a098d1
--- /dev/null
+++ b/.github/linters/.eslintrc.json
@@ -0,0 +1,11 @@
+{
+ "parser": "@typescript-eslint/parser",
+ "plugins": ["@typescript-eslint"],
+ "extends": [
+ "plugin:@typescript-eslint/recommended"
+ ],
+ "rules": {
+ "@typescript-eslint/no-var-requires": 0,
+ "@typescript-eslint/no-empty-interface": 0
+ }
+}
\ No newline at end of file
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/linter.yml b/.github/workflows/linter.yml
index 482b880..2ad4277 100644
--- a/.github/workflows/linter.yml
+++ b/.github/workflows/linter.yml
@@ -53,3 +53,8 @@ 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
+ 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/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 @@
-
+
-
+ |
Name
|
-
+ |
Author
|
-
+ |
Owner
|
-
+ |
Genres
|
-
- |
+ |
{#each books as book}
-
-
- {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}
+ |
+
+
+
+ |
+
{/each}
@@ -197,23 +245,34 @@
/>
-
+ >
+ {#each authors as value}
+
+ {/each}
+
-
-
-
Owner:
+
+
+
+ >
+ {#each genres as genre}
+
+ {/each}
+
{#if updateopen}
diff --git a/package.json b/package.json
index 453207d..fce0f67 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",
diff --git a/server/interfaces/models/IBook.ts b/server/interfaces/models/IBook.ts
index 3f61cea..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: { id: number, name: string }[];
+ authors: string[] | ObjectID[];
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 4def18e..ee3ffcb 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 collectionName: string = 'authors';
+ protected validator = validatorSchema;
+ protected collectionName = 'authors';
}
diff --git a/server/models/book.ts b/server/models/book.ts
index 0236cf7..8a4587f 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 collectionName: string = 'books';
+ protected validator = validatorSchema;
+ protected collectionName = '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/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 });
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);
}