Database changes. Added Snippet model and controller

This commit is contained in:
unknown
2021-09-20 17:57:32 +02:00
parent dd34f2ae02
commit 7cfe484a1d
15 changed files with 232 additions and 28 deletions

View File

@@ -0,0 +1,13 @@
import { Request, Response, NextFunction } from 'express';
import { asyncWrapper } from '../middleware';
import { SnippetModel } from '../models';
export const createSnippet = asyncWrapper(
async (req: Request, res: Response, next: NextFunction): Promise<void> => {
const snippet = await SnippetModel.create(req.body);
res.status(201).json({
data: snippet
});
}
);

View File

@@ -1,9 +1,9 @@
require('ts-node/register');
import path from 'path';
import { Sequelize } from 'sequelize';
import { Umzug, SequelizeStorage } from 'umzug';
import Umzug from 'umzug';
import { Logger } from '../utils';
const logger = new Logger();
const logger = new Logger('db');
// DB config
export const sequelize = new Sequelize({
@@ -14,15 +14,21 @@ export const sequelize = new Sequelize({
// Migrations config
const umzug = new Umzug({
migrations: { glob: '**/migrations/*.ts' },
context: sequelize.getQueryInterface(),
storage: new SequelizeStorage({ sequelize }),
logger: undefined
migrations: {
path: path.join(__dirname, './migrations'),
params: [sequelize.getQueryInterface()],
pattern: /^\d+[\w-]+\.(js|ts)$/
},
storage: 'sequelize',
storageOptions: {
sequelize
},
logging: false
});
export type Migration = typeof umzug._types.migration;
export const connectDB = async () => {
const isDev = process.env.NODE_ENV == 'development';
try {
// Create & connect db
await sequelize.authenticate();
@@ -30,15 +36,22 @@ export const connectDB = async () => {
// Check migrations
const pendingMigrations = await umzug.pending();
if (pendingMigrations.length > 0) {
logger.log(`Found pending migrations. Executing...`);
if (isDev) {
pendingMigrations.forEach(({ file }) =>
logger.log(`Executing ${file} migration`, 'DEV')
);
}
}
await umzug.up();
} catch (err) {
logger.log(`Database connection error`, 'ERROR');
if (process.env.NODE_ENV == 'development') {
if (isDev) {
console.log(err);
}

View File

@@ -1,5 +1,4 @@
import { DataTypes, Model } from 'sequelize';
import type { Migration } from '../';
import { DataTypes, Model, QueryInterface } from 'sequelize';
import {
Snippet,
SnippetCreationAttributes
@@ -7,16 +6,15 @@ import {
const { INTEGER, STRING, DATE } = DataTypes;
export const up: Migration = async ({
context: queryInterface
}): Promise<void> => {
export const up = async (queryInterface: QueryInterface): Promise<void> => {
await queryInterface.createTable<Model<Snippet, SnippetCreationAttributes>>(
'snippets',
{
id: {
type: INTEGER,
allowNull: false,
primaryKey: true
primaryKey: true,
autoIncrement: true
},
title: {
type: STRING,
@@ -38,8 +36,6 @@ export const up: Migration = async ({
);
};
export const down: Migration = async ({
context: queryInterface
}): Promise<void> => {
export const down = async (queryInterface: QueryInterface): Promise<void> => {
await queryInterface.dropTable('snippets');
};

View File

@@ -0,0 +1,8 @@
import { Request, Response, NextFunction } from 'express';
type Foo = (req: Request, res: Response, next: NextFunction) => Promise<void>;
export const asyncWrapper =
(foo: Foo) => (req: Request, res: Response, next: NextFunction) => {
return Promise.resolve(foo(req, res, next)).catch(next);
};

2
src/middleware/index.ts Normal file
View File

@@ -0,0 +1,2 @@
export * from './asyncWrapper';
export * from './requireBody';

View File

@@ -0,0 +1,16 @@
import { Request, Response, NextFunction } from 'express';
export const requireBody =
(...fields: string[]) =>
(req: Request, res: Response, next: NextFunction): void => {
const bodyKeys = Object.keys(req.body);
const missingKeys: string[] = [];
fields.forEach(field => {
if (!bodyKeys.includes(field)) {
missingKeys.push(field);
}
});
next();
};

31
src/models/Snippet.ts Normal file
View File

@@ -0,0 +1,31 @@
import { Model, DataTypes } from 'sequelize';
import { sequelize } from '../db';
import { Snippet, SnippetCreationAttributes } from '../typescript/interfaces';
const { INTEGER, STRING, DATE } = DataTypes;
interface SnippetInstance
extends Model<Snippet, SnippetCreationAttributes>,
Snippet {}
export const SnippetModel = sequelize.define<SnippetInstance>('Snippet', {
id: {
type: INTEGER,
primaryKey: true,
autoIncrement: true
},
title: {
type: STRING,
allowNull: false
},
language: {
type: STRING,
allowNull: false
},
createdAt: {
type: DATE
},
updatedAt: {
type: DATE
}
});

1
src/models/index.ts Normal file
View File

@@ -0,0 +1 @@
export * from './Snippet';

7
src/routes/snippets.ts Normal file
View File

@@ -0,0 +1,7 @@
import { Router } from 'express';
import { createSnippet } from '../controllers/snippets';
import { requireBody } from '../middleware';
export const snippetRouter = Router();
snippetRouter.route('/').post(requireBody('title', 'language'), createSnippet);

View File

@@ -3,16 +3,22 @@ import express from 'express';
import { Logger } from './utils';
import { connectDB } from './db';
// Routers
import { snippetRouter } from './routes/snippets';
// Env config
dotenv.config({ path: './src/config/.env' });
const app = express();
const logger = new Logger();
const logger = new Logger('server');
const PORT = process.env.PORT;
// App config
app.use(express.json());
// Routes
app.use('/api/snippets', snippetRouter);
(async () => {
await connectDB();

View File

@@ -1 +1 @@
export type ErrorLevel = 'INFO' | 'ERROR' | 'WARN';
export type ErrorLevel = 'INFO' | 'ERROR' | 'WARN' | 'DEV';

View File

@@ -1,8 +1,16 @@
import { ErrorLevel } from '../typescript/types/ErrorLevel';
export class Logger {
private namespace: string;
constructor(namespace: string) {
this.namespace = namespace;
}
public log(message: string, level: ErrorLevel = 'INFO'): void {
console.log(`[${this.generateTimestamp()}] [${level}] ${message}`);
console.log(
`[${this.generateTimestamp()}] [${level}] ${this.namespace}: ${message}`
);
}
private generateTimestamp(): string {