Updating snippets with tags

This commit is contained in:
unknown
2021-09-28 13:26:48 +02:00
parent 86ea246e90
commit 714793a407
11 changed files with 97 additions and 67 deletions

View File

@@ -1,3 +1,6 @@
### v1.2 (2021-09-28)
- Added support for tags ([#10](https://github.com/pawelmalak/snippet-box/issues/10))
### v1.1 (2021-09-24) ### v1.1 (2021-09-24)
- Added pin icon directly to snippet card ([#4](https://github.com/pawelmalak/snippet-box/issues/4)) - Added pin icon directly to snippet card ([#4](https://github.com/pawelmalak/snippet-box/issues/4))
- Fixed issue with copying snippets ([#6](https://github.com/pawelmalak/snippet-box/issues/6)) - Fixed issue with copying snippets ([#6](https://github.com/pawelmalak/snippet-box/issues/6))

View File

@@ -10,14 +10,8 @@ COPY . .
RUN mkdir -p ./public ./data RUN mkdir -p ./public ./data
# Build server code # Build
RUN npm run build RUN npm run build \
# Build client code
RUN cd ./client \
&& npm install \
&& npm run build \
&& cd .. \
&& mv ./client/build/* ./public && mv ./client/build/* ./public
# Clean up src files # Clean up src files

View File

@@ -12,14 +12,8 @@ COPY . .
RUN mkdir -p ./public ./data RUN mkdir -p ./public ./data
# Build server code # Build
RUN npm run build RUN npm run build \
# Build client code
RUN cd ./client \
&& npm install \
&& npm run build \
&& cd .. \
&& mv ./client/build/* ./public && mv ./client/build/* ./public
# Clean up src files # Clean up src files

View File

@@ -54,6 +54,10 @@ export const SnippetForm = (props: Props): JSX.Element => {
}); });
}; };
const tagsToString = (): string => {
return formData.tags.join(',');
};
const formHandler = (e: FormEvent) => { const formHandler = (e: FormEvent) => {
e.preventDefault(); e.preventDefault();
@@ -134,13 +138,13 @@ export const SnippetForm = (props: Props): JSX.Element => {
className='form-control' className='form-control'
id='tags' id='tags'
name='tags' name='tags'
// value={formData.tags} value={tagsToString()}
placeholder='automation, files, loop' placeholder='automation, files, loop'
onChange={e => stringToTags(e)} onChange={e => stringToTags(e)}
/> />
<div className='form-text'> <div className='form-text'>
Tags should be separate with a comma. Language tag will be added Tags should be separated with a comma. Language tag will be
automatically added automatically
</div> </div>
</div> </div>
<hr /> <hr />

View File

@@ -53,13 +53,13 @@ export const SnippetsContextProvider = (props: Props): JSX.Element => {
}; };
const setSnippet = (id: number): void => { const setSnippet = (id: number): void => {
getSnippetById(id);
if (id < 0) { if (id < 0) {
setCurrentSnippet(null); setCurrentSnippet(null);
return; return;
} }
getSnippetById(id);
const snippet = snippets.find(s => s.id === id); const snippet = snippets.find(s => s.id === id);
if (snippet) { if (snippet) {

View File

@@ -10,7 +10,7 @@
"dev:client": "npm start --prefix=client", "dev:client": "npm start --prefix=client",
"dev:server": "nodemon", "dev:server": "nodemon",
"dev": "npm-run-all -n --parallel dev:**", "dev": "npm-run-all -n --parallel dev:**",
"build:client": "npm run build prefix=client", "build:client": "npm run build --prefix=client",
"build:clear": "rm -rf build", "build:clear": "rm -rf build",
"build:tsc": "tsc", "build:tsc": "tsc",
"build": "npm-run-all -n build:**" "build": "npm-run-all -n build:**"

View File

@@ -2,13 +2,15 @@ import { Request, Response, NextFunction } from 'express';
import { QueryTypes } from 'sequelize'; import { QueryTypes } 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 { import {
SnippetInstance, ErrorResponse,
SnippetModel, getTags,
Snippet_TagModel, tagParser,
TagModel Logger,
} from '../models'; createTags
import { ErrorResponse, getTags, tagParser, Logger } from '../utils'; } from '../utils';
import { Body } from '../typescript/interfaces';
const logger = new Logger('snippets-controller'); const logger = new Logger('snippets-controller');
@@ -19,11 +21,6 @@ const logger = new Logger('snippets-controller');
*/ */
export const createSnippet = asyncWrapper( export const createSnippet = asyncWrapper(
async (req: Request, res: Response, next: NextFunction): Promise<void> => { async (req: Request, res: Response, next: NextFunction): Promise<void> => {
interface Body {
language: string;
tags: string[];
}
// Get tags from request body // Get tags from request body
const { language, tags: requestTags } = <Body>req.body; const { language, tags: requestTags } = <Body>req.body;
const parsedRequestTags = tagParser([ const parsedRequestTags = tagParser([
@@ -37,38 +34,8 @@ export const createSnippet = asyncWrapper(
tags: [...parsedRequestTags].join(',') tags: [...parsedRequestTags].join(',')
}); });
// Get all tags // Create tags
const rawAllTags = await sequelize.query<{ id: number; name: string }>( await createTags(parsedRequestTags, snippet.id);
`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 // Get raw snippet values
const rawSnippet = snippet.get({ plain: true }); const rawSnippet = snippet.get({ plain: true });
@@ -162,10 +129,28 @@ export const updateSnippet = asyncWrapper(
); );
} }
snippet = await snippet.update(req.body); // Get tags from request body
const { language, tags: requestTags } = <Body>req.body;
let parsedRequestTags = tagParser([...requestTags, language.toLowerCase()]);
// Update snippet
snippet = await snippet.update({
...req.body,
tags: [...parsedRequestTags].join(',')
});
// Delete old tags and create new ones
await Snippet_TagModel.destroy({ where: { snippet_id: req.params.id } });
await createTags(parsedRequestTags, snippet.id);
// Get raw snippet values
const rawSnippet = snippet.get({ plain: true });
res.status(200).json({ res.status(200).json({
data: snippet data: {
...rawSnippet,
tags: [...parsedRequestTags]
}
}); });
} }
); );

View File

@@ -0,0 +1,9 @@
export interface Body {
title: string;
description?: string;
language: string;
code: string;
docs?: string;
isPinned: boolean;
tags: string[];
}

View File

@@ -2,3 +2,4 @@ export * from './Model';
export * from './Snippet'; export * from './Snippet';
export * from './Tag'; export * from './Tag';
export * from './Snippet_Tag'; export * from './Snippet_Tag';
export * from './Body';

39
src/utils/createTags.ts Normal file
View File

@@ -0,0 +1,39 @@
import { sequelize } from '../db';
import { QueryTypes } from 'sequelize';
import { TagModel, Snippet_TagModel } from '../models';
export const createTags = async (
parsedTags: Set<string>,
snippetId: number
): Promise<void> => {
// 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 = [...parsedTags].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 parsedTags) {
const tagObj = rawAllTags.find(t => t.name == tag);
if (tagObj) {
await Snippet_TagModel.create({
snippet_id: snippetId,
tag_id: tagObj.id
});
}
}
};

View File

@@ -2,3 +2,4 @@ export * from './Logger';
export * from './ErrorResponse'; export * from './ErrorResponse';
export * from './tagParser'; export * from './tagParser';
export * from './getTags'; export * from './getTags';
export * from './createTags';