diff --git a/src/db/migrations/02_tags.ts b/src/db/migrations/02_tags.ts index 8797d31..d15c3fd 100644 --- a/src/db/migrations/02_tags.ts +++ b/src/db/migrations/02_tags.ts @@ -1,19 +1,90 @@ -import { DataTypes, QueryInterface, QueryTypes } from 'sequelize'; -import { sequelize } from '../'; -const { STRING } = DataTypes; +import { Logger } from '../../utils'; +import { DataTypes, QueryInterface } from 'sequelize'; +import { + SnippetModel, + Snippet_TagModel, + TagInstance, + TagModel +} from '../../models'; + +const { STRING, INTEGER } = DataTypes; +const logger = new Logger('migration[02]'); export const up = async (queryInterface: QueryInterface): Promise => { - await queryInterface.addColumn('snippets', 'tags', { - type: STRING, - allowNull: true, - defaultValue: '' + await queryInterface.createTable('tags', { + id: { + type: INTEGER, + allowNull: false, + primaryKey: true, + autoIncrement: true + }, + name: { + type: STRING, + allowNull: false + } }); - await sequelize.query(`UPDATE snippets SET tags = language`, { - type: QueryTypes.UPDATE + await queryInterface.createTable('snippets_tags', { + id: { + type: INTEGER, + allowNull: false, + primaryKey: true, + autoIncrement: true + }, + snippet_id: { + type: INTEGER, + allowNull: false + }, + tag_id: { + type: INTEGER, + allowNull: false + } + }); + + // Create new tags from language column + const snippets = await SnippetModel.findAll(); + const languages = snippets.map(snippet => snippet.language); + const uniqueLanguages = [...new Set(languages)]; + const tags: TagInstance[] = []; + + await new Promise(resolve => { + uniqueLanguages.forEach(async language => { + try { + const tag = await TagModel.create({ name: language }); + tags.push(tag); + } catch (err) { + logger.log('Error while creating new tags'); + } finally { + if (uniqueLanguages.length == tags.length) { + resolve(); + } + } + }); + }); + + // Assign tag to snippet + await new Promise(resolve => { + snippets.forEach(async snippet => { + try { + const tag = tags.find(tag => tag.name == snippet.language); + + if (tag) { + await Snippet_TagModel.create({ + snippet_id: snippet.id, + tag_id: tag.id + }); + } + } catch (err) { + logger.log('Error while assigning tags to snippets'); + console.log(err); + } finally { + resolve(); + } + }); }); }; export const down = async (queryInterface: QueryInterface): Promise => { - await queryInterface.removeColumn('snippets', 'tags'); + await queryInterface.dropTable('tags'); + await queryInterface.dropTable('snippets_tags'); }; diff --git a/src/models/Snippet.ts b/src/models/Snippet.ts index c7bb43a..0b23780 100644 --- a/src/models/Snippet.ts +++ b/src/models/Snippet.ts @@ -4,52 +4,53 @@ import { Snippet, SnippetCreationAttributes } from '../typescript/interfaces'; const { INTEGER, STRING, DATE, TEXT } = DataTypes; -interface SnippetInstance +export interface SnippetInstance extends Model, Snippet {} -export const SnippetModel = sequelize.define('Snippet', { - id: { - type: INTEGER, - primaryKey: true, - autoIncrement: true +export const SnippetModel = sequelize.define( + 'Snippet', + { + id: { + type: INTEGER, + primaryKey: true, + autoIncrement: true + }, + title: { + type: STRING, + allowNull: false + }, + description: { + type: TEXT, + allowNull: true, + defaultValue: '' + }, + language: { + type: STRING, + allowNull: false + }, + code: { + type: TEXT, + allowNull: false + }, + docs: { + type: TEXT, + allowNull: true, + defaultValue: '' + }, + isPinned: { + type: INTEGER, + allowNull: true, + defaultValue: 0 + }, + createdAt: { + type: DATE + }, + updatedAt: { + type: DATE + } }, - title: { - type: STRING, - allowNull: false - }, - description: { - type: TEXT, - allowNull: true, - defaultValue: '' - }, - language: { - type: STRING, - allowNull: false - }, - code: { - type: TEXT, - allowNull: false - }, - docs: { - type: TEXT, - allowNull: true, - defaultValue: '' - }, - isPinned: { - type: INTEGER, - allowNull: true, - defaultValue: 0 - }, - tags: { - type: STRING, - allowNull: true, - defaultValue: '' - }, - createdAt: { - type: DATE - }, - updatedAt: { - type: DATE + { + tableName: 'snippets' } -}); +); diff --git a/src/models/Snippet_Tag.ts b/src/models/Snippet_Tag.ts new file mode 100644 index 0000000..0eaa25d --- /dev/null +++ b/src/models/Snippet_Tag.ts @@ -0,0 +1,35 @@ +import { Model, DataTypes } from 'sequelize'; +import { sequelize } from '../db'; +import { + Snippet_Tag, + Snippet_TagCreationAttributes +} from '../typescript/interfaces'; + +const { INTEGER } = DataTypes; + +export interface Snippet_TagInstance + extends Model, + Snippet_Tag {} + +export const Snippet_TagModel = sequelize.define( + 'Snippet_Tag', + { + id: { + type: INTEGER, + primaryKey: true, + autoIncrement: true + }, + snippet_id: { + type: INTEGER, + allowNull: false + }, + tag_id: { + type: INTEGER, + allowNull: false + } + }, + { + timestamps: false, + tableName: 'snippets_tags' + } +); diff --git a/src/models/Tag.ts b/src/models/Tag.ts new file mode 100644 index 0000000..9c2e2de --- /dev/null +++ b/src/models/Tag.ts @@ -0,0 +1,26 @@ +import { Model, DataTypes } from 'sequelize'; +import { sequelize } from '../db'; +import { Tag, TagCreationAttributes } from '../typescript/interfaces'; + +const { INTEGER, STRING } = DataTypes; + +export interface TagInstance extends Model, Tag {} + +export const TagModel = sequelize.define( + 'Tag', + { + id: { + type: INTEGER, + primaryKey: true, + autoIncrement: true + }, + name: { + type: STRING, + allowNull: false + } + }, + { + timestamps: false, + tableName: 'tags' + } +); diff --git a/src/models/index.ts b/src/models/index.ts index 02fc3fa..44b6eec 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -1 +1,3 @@ export * from './Snippet'; +export * from './Tag'; +export * from './Snippet_Tag'; diff --git a/src/typescript/interfaces/Snippet.ts b/src/typescript/interfaces/Snippet.ts index 3d96a2b..785ee09 100644 --- a/src/typescript/interfaces/Snippet.ts +++ b/src/typescript/interfaces/Snippet.ts @@ -8,7 +8,6 @@ export interface Snippet extends Model { code: string; docs: string; isPinned: number; - tags: string; } export interface SnippetCreationAttributes diff --git a/src/typescript/interfaces/Snippet_Tag.ts b/src/typescript/interfaces/Snippet_Tag.ts new file mode 100644 index 0000000..936a301 --- /dev/null +++ b/src/typescript/interfaces/Snippet_Tag.ts @@ -0,0 +1,10 @@ +import { Optional } from 'sequelize'; + +export interface Snippet_Tag { + id: number; + snippet_id: number; + tag_id: number; +} + +export interface Snippet_TagCreationAttributes + extends Optional {} diff --git a/src/typescript/interfaces/Tag.ts b/src/typescript/interfaces/Tag.ts new file mode 100644 index 0000000..923750f --- /dev/null +++ b/src/typescript/interfaces/Tag.ts @@ -0,0 +1,8 @@ +import { Optional } from 'sequelize'; + +export interface Tag { + id: number; + name: string; +} + +export interface TagCreationAttributes extends Optional {} diff --git a/src/typescript/interfaces/index.ts b/src/typescript/interfaces/index.ts index 063adcb..876ad66 100644 --- a/src/typescript/interfaces/index.ts +++ b/src/typescript/interfaces/index.ts @@ -1,2 +1,4 @@ export * from './Model'; export * from './Snippet'; +export * from './Tag'; +export * from './Snippet_Tag';