From bd7eaf3d1779c18abdfb1512c1ce6be6a5af9066 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 27 Sep 2021 12:12:19 +0200 Subject: [PATCH] Create/update snippet with tags. DB migration to support tags --- README.md | 2 +- .../src/components/Snippets/SnippetForm.tsx | 25 +++++++++++++-- client/src/typescript/interfaces/Snippet.ts | 1 + package.json | 1 + src/controllers/snippets.ts | 32 +++++++++++++++++-- src/db/migrations/02_tags.ts | 19 +++++++++++ src/models/Snippet.ts | 5 +++ src/typescript/interfaces/Snippet.ts | 1 + src/utils/index.ts | 1 + src/utils/tagsParser.ts | 8 +++++ 10 files changed, 89 insertions(+), 6 deletions(-) create mode 100644 src/db/migrations/02_tags.ts create mode 100644 src/utils/tagsParser.ts diff --git a/README.md b/README.md index 9040a3e..add45c7 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ docker run -p 5000:5000 -v /path/to/data:/app/data snippet-box ![Snippet screenshot](./.github/img/snippet.png) -- Edditor +- Editor - Create and edit your snippets from simple and easy to use editor ![Editor screenshot](./.github/img/editor.png) diff --git a/client/src/components/Snippets/SnippetForm.tsx b/client/src/components/Snippets/SnippetForm.tsx index e42da75..d839b25 100644 --- a/client/src/components/Snippets/SnippetForm.tsx +++ b/client/src/components/Snippets/SnippetForm.tsx @@ -25,7 +25,8 @@ export const SnippetForm = (props: Props): JSX.Element => { language: '', code: '', docs: '', - isPinned: false + isPinned: false, + tags: '' }); useEffect(() => { @@ -109,11 +110,31 @@ export const SnippetForm = (props: Props): JSX.Element => { id='language' name='language' value={formData.language} - placeholder='bash' + placeholder='python' required onChange={e => inputHandler(e)} /> + + {/* TAGS */} +
+ + inputHandler(e)} + /> +
+ Language tag will be added automatically +
+

{/* CODE SECTION */} diff --git a/client/src/typescript/interfaces/Snippet.ts b/client/src/typescript/interfaces/Snippet.ts index 99e517d..8e1ced5 100644 --- a/client/src/typescript/interfaces/Snippet.ts +++ b/client/src/typescript/interfaces/Snippet.ts @@ -7,6 +7,7 @@ export interface NewSnippet { code: string; docs?: string; isPinned: boolean; + tags: string; } export interface Snippet extends Model, NewSnippet {} diff --git a/package.json b/package.json index ead2fb6..62db005 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "dev:client": "npm start --prefix=client", "dev:server": "nodemon", "dev": "npm-run-all -n --parallel dev:**", + "build:client": "npm run build prefix=client", "build:clear": "rm -rf build", "build:tsc": "tsc", "build": "npm-run-all -n build:**" diff --git a/src/controllers/snippets.ts b/src/controllers/snippets.ts index 67a524b..fe6a93e 100644 --- a/src/controllers/snippets.ts +++ b/src/controllers/snippets.ts @@ -3,7 +3,7 @@ import { QueryTypes } from 'sequelize'; import { sequelize } from '../db'; import { asyncWrapper } from '../middleware'; import { SnippetModel } from '../models'; -import { ErrorResponse } from '../utils'; +import { ErrorResponse, tagsParser } from '../utils'; /** * @description Create new snippet @@ -12,7 +12,17 @@ import { ErrorResponse } from '../utils'; */ export const createSnippet = asyncWrapper( async (req: Request, res: Response, next: NextFunction): Promise => { - const snippet = await SnippetModel.create(req.body); + const { language, tags } = <{ language: string; tags: string }>req.body; + const parsedTags = tagsParser(tags); + + if (!parsedTags.includes(language.toLowerCase())) { + parsedTags.push(language.toLowerCase()); + } + + const snippet = await SnippetModel.create({ + ...req.body, + tags: parsedTags.join(',') + }); res.status(201).json({ data: snippet @@ -81,7 +91,23 @@ export const updateSnippet = asyncWrapper( ); } - snippet = await snippet.update(req.body); + // 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(',') + }); res.status(200).json({ data: snippet diff --git a/src/db/migrations/02_tags.ts b/src/db/migrations/02_tags.ts new file mode 100644 index 0000000..8797d31 --- /dev/null +++ b/src/db/migrations/02_tags.ts @@ -0,0 +1,19 @@ +import { DataTypes, QueryInterface, QueryTypes } from 'sequelize'; +import { sequelize } from '../'; +const { STRING } = DataTypes; + +export const up = async (queryInterface: QueryInterface): Promise => { + await queryInterface.addColumn('snippets', 'tags', { + type: STRING, + allowNull: true, + defaultValue: '' + }); + + await sequelize.query(`UPDATE snippets SET tags = language`, { + type: QueryTypes.UPDATE + }); +}; + +export const down = async (queryInterface: QueryInterface): Promise => { + await queryInterface.removeColumn('snippets', 'tags'); +}; diff --git a/src/models/Snippet.ts b/src/models/Snippet.ts index a435188..c7bb43a 100644 --- a/src/models/Snippet.ts +++ b/src/models/Snippet.ts @@ -41,6 +41,11 @@ export const SnippetModel = sequelize.define('Snippet', { allowNull: true, defaultValue: 0 }, + tags: { + type: STRING, + allowNull: true, + defaultValue: '' + }, createdAt: { type: DATE }, diff --git a/src/typescript/interfaces/Snippet.ts b/src/typescript/interfaces/Snippet.ts index 785ee09..3d96a2b 100644 --- a/src/typescript/interfaces/Snippet.ts +++ b/src/typescript/interfaces/Snippet.ts @@ -8,6 +8,7 @@ export interface Snippet extends Model { code: string; docs: string; isPinned: number; + tags: string; } export interface SnippetCreationAttributes diff --git a/src/utils/index.ts b/src/utils/index.ts index e59e2fd..c86e0f3 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,2 +1,3 @@ export * from './Logger'; export * from './ErrorResponse'; +export * from './tagsParser'; diff --git a/src/utils/tagsParser.ts b/src/utils/tagsParser.ts new file mode 100644 index 0000000..14fa27e --- /dev/null +++ b/src/utils/tagsParser.ts @@ -0,0 +1,8 @@ +export const tagsParser = (tags: string): string[] => { + const parsedTags = tags + .split(',') + .map(tag => tag.trim().toLowerCase()) + .filter(String); + + return parsedTags; +};