mirror of
https://github.com/pawelmalak/snippet-box.git
synced 2025-12-21 13:23:05 +01:00
Search feature. Changed how controller fetches tags
This commit is contained in:
@@ -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))
|
||||||
|
|||||||
@@ -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
15
src/db/associateModels.ts
Normal 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'
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -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(
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user