import replaceEnum from "sequelize-replace-enum-postgres" ; import * as _ from "lodash" ; import { Op , QueryInterface , Transaction } from "sequelize" ; import { PermissionName , RoleName , Permission } from "../entities" ; import { RoleDto , RolePermissionDto } from "../../auth" ; export class PermissionMigrationPayloadData { permission : PermissionName ; description ? : string ; acl : { [ P in RoleName ] : boolean ; } ; } export class PermissionMigrationPayloadOptions { trancation ? : Transaction ; transactional ? : boolean ; } export class PermissionMigrationUpPayload { data : PermissionMigrationPayloadData [ ] ; opts : PermissionMigrationPayloadOptions ; } export class PermissionMigrationDownPayload { permissions : PermissionName [ ] ; opts : PermissionMigrationPayloadOptions ; } example export class PermissionMigrator { public constructor ( protected queryInterface ) { if ( ! queryInterface ) { throw new Error ( `No query interface provided` ) ; } } public async up ( payload ) { const { data , opts } = payload ; const roles = await this . queryInterface . select ( null , "role" , { raw : true } ) as RoleDto [ ] ; const transactionOptions = opts . trancation && opts || opts . transactional ? { transaction : await this . queryInterface . sequelize . transaction ( ) } : { } ; const currentPermissionList = await this . getCurrentPermissionList ( ) ; const permissionsToAdd = _ . map ( data , permissionData => permissionData . permission ) ; const tableOptions = Permission . getTableName ( ) ; const tableName = _ . get ( tableOptions , "tableName" , tableOptions ) ; await replaceEnum ( { tableName , queryInterface : this . queryInterface , columnName : "name" , newValues : _ . uniq ( [ ... currentPermissionList , ... permissionsToAdd ] ) , } ) ; await Promise . map ( data , async ( permissionData ) => { const roleNames = _ . keys ( permissionData . acl ) as RoleName [ ] ; const [ permission ] = await this . queryInterface . bulkInsert ( "permission" , [ { name : permissionData . permission , description : permissionData . description || "" } ] , { ... transactionOptions , returning : true } ) as RolePermissionDto [ ] ; const rolePermissionRecords = _ . map ( roleNames , ( roleName ) => { const role = _ . find ( roles , { name : roleName } ) ; if ( ! role ) { transactionOptions . transaction && transactionOptions . transaction . rollback ( ) ; throw new Error ( `Incorrect role name provided: ${ roleName }` ) ; } return { roleId : role . id , permissionId : permission . id , isEnabled : permissionData . acl [ roleName ] } ; } ) ; await this . queryInterface . bulkInsert ( "role_permission" , rolePermissionRecords , { ... transactionOptions } ) ; } ) ; transactionOptions . transaction && await transactionOptions . transaction . commit ( ) ; } public async down ( payload ) { const { permissions , opts } = payload ; const transactionOptions = opts . trancation && opts || opts . transactional ? { transaction : await this . queryInterface . sequelize . transaction ( ) } : { } ; const currentPermissionList = await this . getCurrentPermissionList ( ) ; await replaceEnum ( { queryInterface : this . queryInterface , tableName : "permission" , columnName : "name" , newValues : _ . without ( currentPermissionList , ... permissions ) , } ) ; await this . queryInterface . bulkDelete ( "permission" , { name : { [ Op . in ] : permissions } } , { ... transactionOptions } ) ; transactionOptions . transaction && await transactionOptions . transaction . commit ( ) ; } returns { String [ ] } private async getCurrentPermissionList ( ) { const data = await this . queryInterface . sequelize . query ( `SELECT unnest(enum_range(NULL::enum_permission_name))` , { type : this . queryInterface . sequelize . QueryTypes . SELECT } ) ; return data . map ( d => d . unnest ) ; } }