Create snippet with tags

This commit is contained in:
unknown
2021-09-28 12:17:59 +02:00
parent 3cd2688589
commit 86ea246e90
8 changed files with 121 additions and 76 deletions

View File

@@ -64,8 +64,8 @@ export const SnippetDetails = (props: Props): JSX.Element => {
{/* TAGS */}
<div>
{tags.map(tag => (
<span className='me-2'>
{tags.map((tag, idx) => (
<span className='me-2' key={idx}>
<Badge text={tag} color='dark' />
</span>
))}

View File

@@ -26,7 +26,7 @@ export const SnippetForm = (props: Props): JSX.Element => {
code: '',
docs: '',
isPinned: false,
tags: ''
tags: []
});
useEffect(() => {
@@ -46,6 +46,14 @@ export const SnippetForm = (props: Props): JSX.Element => {
});
};
const stringToTags = (e: ChangeEvent<HTMLInputElement>) => {
const tags = e.target.value.split(',');
setFormData({
...formData,
tags
});
};
const formHandler = (e: FormEvent) => {
e.preventDefault();
@@ -126,13 +134,13 @@ export const SnippetForm = (props: Props): JSX.Element => {
className='form-control'
id='tags'
name='tags'
value={formData.tags}
// value={formData.tags}
placeholder='automation, files, loop'
required
onChange={e => inputHandler(e)}
onChange={e => stringToTags(e)}
/>
<div className='form-text'>
Language tag will be added automatically
Tags should be separate with a comma. Language tag will be added
automatically
</div>
</div>
<hr />

View File

@@ -2,8 +2,13 @@ import { Request, Response, NextFunction } from 'express';
import { QueryTypes } from 'sequelize';
import { sequelize } from '../db';
import { asyncWrapper } from '../middleware';
import { SnippetInstance, SnippetModel } from '../models';
import { ErrorResponse, getTags, tagsParser, Logger } from '../utils';
import {
SnippetInstance,
SnippetModel,
Snippet_TagModel,
TagModel
} from '../models';
import { ErrorResponse, getTags, tagParser, Logger } from '../utils';
const logger = new Logger('snippets-controller');
@@ -14,20 +19,65 @@ const logger = new Logger('snippets-controller');
*/
export const createSnippet = asyncWrapper(
async (req: Request, res: Response, next: NextFunction): Promise<void> => {
const { language, tags } = <{ language: string; tags: string }>req.body;
const parsedTags = tagsParser(tags);
if (!parsedTags.includes(language.toLowerCase())) {
parsedTags.push(language.toLowerCase());
interface Body {
language: string;
tags: string[];
}
// Get tags from request body
const { language, tags: requestTags } = <Body>req.body;
const parsedRequestTags = tagParser([
...requestTags,
language.toLowerCase()
]);
// Create snippet
const snippet = await SnippetModel.create({
...req.body,
tags: parsedTags.join(',')
tags: [...parsedRequestTags].join(',')
});
// Get all tags
const rawAllTags = await sequelize.query<{ id: number; name: string }>(
`SELECT * FROM tags`,
{ type: QueryTypes.SELECT }
);
const parsedAllTags = rawAllTags.map(tag => tag.name);
// Create array of new tags
const newTags = [...parsedRequestTags].filter(
tag => !parsedAllTags.includes(tag)
);
// Create new tags
if (newTags.length > 0) {
for (const tag of newTags) {
const { id, name } = await TagModel.create({ name: tag });
rawAllTags.push({ id, name });
}
}
// Associate tags with snippet
for (const tag of parsedRequestTags) {
const tagObj = rawAllTags.find(t => t.name == tag);
if (tagObj) {
await Snippet_TagModel.create({
snippet_id: snippet.id,
tag_id: tagObj.id
});
}
}
// Get raw snippet values
const rawSnippet = snippet.get({ plain: true });
res.status(201).json({
data: snippet
data: {
...rawSnippet,
tags: [...parsedRequestTags]
}
});
}
);
@@ -50,7 +100,7 @@ export const getAllSnippets = asyncWrapper(
snippet.tags = tags;
}
} catch (err) {
logger.log('Error while fetching tags');
logger.log('Error while fetching tags', 'ERROR');
} finally {
resolve();
}
@@ -112,23 +162,7 @@ export const updateSnippet = asyncWrapper(
);
}
// Check if language was changed. Edit tags if so
const { language: oldLanguage } = snippet;
const { language, tags } = <{ language: string; tags: string }>req.body;
let parsedTags = tagsParser(tags);
if (oldLanguage != language) {
parsedTags = parsedTags.filter(tag => tag != oldLanguage);
if (!parsedTags.includes(language)) {
parsedTags.push(language.toLowerCase());
}
}
snippet = await snippet.update({
...req.body,
tags: parsedTags.join(',')
});
snippet = await snippet.update(req.body);
res.status(200).json({
data: snippet
@@ -156,6 +190,7 @@ export const deleteSnippet = asyncWrapper(
);
}
await Snippet_TagModel.destroy({ where: { snippet_id: req.params.id } });
await snippet.destroy();
res.status(200).json({

View File

@@ -20,7 +20,8 @@ export const up = async (queryInterface: QueryInterface): Promise<void> => {
},
name: {
type: STRING,
allowNull: false
allowNull: false,
unique: true
}
});
@@ -47,6 +48,7 @@ export const up = async (queryInterface: QueryInterface): Promise<void> => {
const uniqueLanguages = [...new Set(languages)];
const tags: TagInstance[] = [];
if (snippets.length > 0) {
await new Promise<void>(resolve => {
uniqueLanguages.forEach(async language => {
try {
@@ -81,6 +83,7 @@ export const up = async (queryInterface: QueryInterface): Promise<void> => {
}
});
});
}
};
export const down = async (queryInterface: QueryInterface): Promise<void> => {

View File

@@ -16,7 +16,8 @@ export const TagModel = sequelize.define<TagInstance>(
},
name: {
type: STRING,
allowNull: false
allowNull: false,
unique: true
}
},
{

View File

@@ -1,4 +1,4 @@
export * from './Logger';
export * from './ErrorResponse';
export * from './tagsParser';
export * from './tagParser';
export * from './getTags';

6
src/utils/tagParser.ts Normal file
View File

@@ -0,0 +1,6 @@
export const tagParser = (tags: string[]): Set<string> => {
const parsedTags = tags.map(tag => tag.trim().toLowerCase()).filter(String);
const uniqueTags = new Set([...parsedTags]);
return uniqueTags;
};

View File

@@ -1,8 +0,0 @@
export const tagsParser = (tags: string): string[] => {
const parsedTags = tags
.split(',')
.map(tag => tag.trim().toLowerCase())
.filter(String);
return parsedTags;
};