Search feature. Changed how controller fetches tags

This commit is contained in:
Paweł Malak
2021-10-14 15:09:04 +02:00
parent b04d042255
commit fa7f54d0e1
5 changed files with 81 additions and 31 deletions

View File

@@ -1,3 +1,8 @@
### v1.4 (TBA)
- Added search functionality ([#18](https://github.com/pawelmalak/snippet-box/issues/18))
- Fixed date parsing bug ([#22](https://github.com/pawelmalak/snippet-box/issues/22))
- Minor UI fixes
### v1.3 (2021-09-30) ### v1.3 (2021-09-30)
- Added dark mode ([#7](https://github.com/pawelmalak/snippet-box/issues/7)) - Added dark mode ([#7](https://github.com/pawelmalak/snippet-box/issues/7))
- Added syntax highlighting ([#14](https://github.com/pawelmalak/snippet-box/issues/14)) - Added syntax highlighting ([#14](https://github.com/pawelmalak/snippet-box/issues/14))

View File

@@ -2,18 +2,10 @@ import { Request, Response, NextFunction } from 'express';
import { QueryTypes, Op } from 'sequelize'; import { QueryTypes, Op } from 'sequelize';
import { sequelize } from '../db'; import { sequelize } from '../db';
import { asyncWrapper } from '../middleware'; import { asyncWrapper } from '../middleware';
import { SnippetModel, Snippet_TagModel } from '../models'; import { SnippetModel, Snippet_TagModel, TagModel } from '../models';
import { import { ErrorResponse, tagParser, Logger, createTags } from '../utils';
ErrorResponse,
getTags,
tagParser,
Logger,
createTags
} from '../utils';
import { Body, SearchQuery } from '../typescript/interfaces'; import { Body, SearchQuery } from '../typescript/interfaces';
const logger = new Logger('snippets-controller');
/** /**
* @description Create new snippet * @description Create new snippet
* @route /api/snippets * @route /api/snippets
@@ -57,24 +49,27 @@ export const createSnippet = asyncWrapper(
export const getAllSnippets = asyncWrapper( export const getAllSnippets = asyncWrapper(
async (req: Request, res: Response, next: NextFunction): Promise<void> => { async (req: Request, res: Response, next: NextFunction): Promise<void> => {
const snippets = await SnippetModel.findAll({ const snippets = await SnippetModel.findAll({
raw: true include: {
}); model: TagModel,
as: 'tags',
await new Promise<void>(async resolve => { attributes: ['name'],
try { through: {
for await (let snippet of snippets) { attributes: []
const tags = await getTags(+snippet.id);
snippet.tags = tags;
} }
} catch (err) {
logger.log('Error while fetching tags', 'ERROR');
} finally {
resolve();
} }
}); });
const populatedSnippets = snippets.map(snippet => {
const rawSnippet = snippet.get({ plain: true });
return {
...rawSnippet,
tags: rawSnippet.tags?.map(tag => tag.name)
};
});
res.status(200).json({ res.status(200).json({
data: snippets data: populatedSnippets
}); });
} }
); );
@@ -88,7 +83,14 @@ export const getSnippet = asyncWrapper(
async (req: Request, res: Response, next: NextFunction): Promise<void> => { async (req: Request, res: Response, next: NextFunction): Promise<void> => {
const snippet = await SnippetModel.findOne({ const snippet = await SnippetModel.findOne({
where: { id: req.params.id }, where: { id: req.params.id },
raw: true include: {
model: TagModel,
as: 'tags',
attributes: ['name'],
through: {
attributes: []
}
}
}); });
if (!snippet) { if (!snippet) {
@@ -100,11 +102,14 @@ export const getSnippet = asyncWrapper(
); );
} }
const tags = await getTags(+req.params.id); const rawSnippet = snippet.get({ plain: true });
snippet.tags = tags; const populatedSnippet = {
...rawSnippet,
tags: rawSnippet.tags?.map(tag => tag.name)
};
res.status(200).json({ res.status(200).json({
data: snippet data: populatedSnippet
}); });
} }
); );
@@ -116,6 +121,8 @@ export const getSnippet = asyncWrapper(
*/ */
export const updateSnippet = asyncWrapper( export const updateSnippet = asyncWrapper(
async (req: Request, res: Response, next: NextFunction): Promise<void> => { async (req: Request, res: Response, next: NextFunction): Promise<void> => {
console.log(req.body);
let snippet = await SnippetModel.findOne({ let snippet = await SnippetModel.findOne({
where: { id: req.params.id } where: { id: req.params.id }
}); });
@@ -219,10 +226,20 @@ export const searchSnippets = asyncWrapper(
async (req: Request, res: Response, next: NextFunction): Promise<void> => { async (req: Request, res: Response, next: NextFunction): Promise<void> => {
const { query, tags, languages } = <SearchQuery>req.body; const { query, tags, languages } = <SearchQuery>req.body;
console.log(query, tags, languages); // Check if query is empty
if (query === '' && !tags.length && !languages.length) {
res.status(200).json({
data: []
});
const languageFilter = return;
languages.length > 0 ? { [Op.in]: languages } : { [Op.notIn]: languages }; }
const languageFilter = languages.length
? { [Op.in]: languages }
: { [Op.notIn]: languages };
const tagFilter = tags.length ? { [Op.in]: tags } : { [Op.notIn]: tags };
const snippets = await SnippetModel.findAll({ const snippets = await SnippetModel.findAll({
where: { where: {
@@ -237,6 +254,17 @@ export const searchSnippets = asyncWrapper(
language: languageFilter language: languageFilter
} }
] ]
},
include: {
model: TagModel,
as: 'tags',
attributes: ['name'],
where: {
name: tagFilter
},
through: {
attributes: []
}
} }
}); });

15
src/db/associateModels.ts Normal file
View File

@@ -0,0 +1,15 @@
import { TagModel, SnippetModel, Snippet_TagModel } from '../models';
export const associateModels = async () => {
TagModel.belongsToMany(SnippetModel, {
through: Snippet_TagModel,
foreignKey: 'tag_id',
as: 'snippets'
});
SnippetModel.belongsToMany(TagModel, {
through: Snippet_TagModel,
foreignKey: 'snippet_id',
as: 'tags'
});
};

View File

@@ -7,6 +7,7 @@ import { errorHandler } from './middleware';
// Routers // Routers
import { snippetRouter } from './routes/snippets'; import { snippetRouter } from './routes/snippets';
import { associateModels } from './db/associateModels';
// Env config // Env config
dotenv.config({ path: './src/config/.env' }); dotenv.config({ path: './src/config/.env' });
@@ -32,6 +33,7 @@ app.use(errorHandler);
(async () => { (async () => {
await connectDB(); await connectDB();
await associateModels();
app.listen(PORT, () => { app.listen(PORT, () => {
logger.log( logger.log(

View File

@@ -8,7 +8,7 @@ export interface Snippet extends Model {
code: string; code: string;
docs: string; docs: string;
isPinned: number; isPinned: number;
tags?: string[]; tags?: { name: string }[];
} }
export interface SnippetCreationAttributes export interface SnippetCreationAttributes