chore: initial commit

This commit is contained in:
Divlo
2021-10-24 04:06:16 +02:00
commit 714cc643ba
260 changed files with 40783 additions and 0 deletions

View File

@ -0,0 +1,110 @@
import { Sequelize } from 'sequelize-typescript'
import sqlite3 from 'sqlite3'
import { open, Database } from 'sqlite'
import { paginateModel } from '../paginateModel'
import { BadRequestError } from '../../errors/BadRequestError'
import PostTest from './utils/PostTest'
import { createPosts } from './utils/createPosts'
let sqlite: Database | undefined
let sequelize: Sequelize | undefined
describe('/tools/database/paginateModel', () => {
beforeAll(async () => {
sqlite = await open({
filename: ':memory:',
driver: sqlite3.Database
})
sequelize = new Sequelize({
dialect: 'sqlite',
storage: ':memory:',
logging: false,
models: [PostTest]
})
})
beforeEach(async () => {
await sequelize?.sync({ force: true })
})
afterAll(async () => {
await sqlite?.close()
await sequelize?.close()
})
it('fetch a certain amount of rows', async () => {
const numberOfPosts = 21
await createPosts(numberOfPosts)
const result = await paginateModel({ Model: PostTest })
expect(result.hasMore).toBeTruthy()
expect(result.rows.length).toEqual(20)
expect(result.totalItems).toEqual(numberOfPosts)
})
it('fetch less than 20 itemsPerPage', async () => {
const numberOfPosts = 15
await createPosts(numberOfPosts)
const result = await paginateModel({ Model: PostTest })
expect(result.hasMore).toBeFalsy()
expect(result.rows.length).toEqual(numberOfPosts)
expect(result.totalItems).toEqual(numberOfPosts)
})
it('fetch more than 20 itemsPerPage', async () => {
const numberOfPosts = 30
const itemsPerPage = '25'
await createPosts(numberOfPosts)
const result = await paginateModel({
Model: PostTest,
queryOptions: { itemsPerPage }
})
expect(result.hasMore).toBeTruthy()
expect(result.rows.length).toEqual(parseInt(itemsPerPage))
expect(result.totalItems).toEqual(numberOfPosts)
expect(result.itemsPerPage).toEqual(Number(itemsPerPage))
})
it('throws "BadRequestError" if "itemsPerPage" is more than 100', async () => {
const numberOfPosts = 10
const itemsPerPage = '101'
await createPosts(numberOfPosts)
await expect(
paginateModel({ Model: PostTest, queryOptions: { itemsPerPage } })
).rejects.toThrow(BadRequestError)
})
it('goes to the next page', async () => {
let page = 1
const numberOfPosts = 100
const itemsPerPage = '30'
const itemsPerPageInt = parseInt(itemsPerPage)
await createPosts(numberOfPosts)
const result1 = await paginateModel({
Model: PostTest,
queryOptions: { itemsPerPage, page: page.toString() },
findOptions: {
order: [['id', 'ASC']]
}
})
page += 1
expect(result1.hasMore).toBeTruthy()
expect(result1.rows[itemsPerPageInt - 1].title).toEqual(
`title-${itemsPerPage}`
)
expect(result1.totalItems).toEqual(numberOfPosts)
const result2 = await paginateModel({
Model: PostTest,
queryOptions: { itemsPerPage, page: page.toString() },
findOptions: {
order: [['id', 'ASC']]
}
})
expect(result2.page).toEqual(page)
expect(result2.hasMore).toBeTruthy()
expect(result2.rows[itemsPerPageInt - 1].title).toEqual(
`title-${itemsPerPageInt * 2}`
)
expect(result2.totalItems).toEqual(numberOfPosts)
})
})

View File

@ -0,0 +1,10 @@
import { Table, Model, Column, DataType } from 'sequelize-typescript'
@Table
export default class PostTest extends Model {
@Column({
type: DataType.STRING,
allowNull: false
})
title!: string
}

View File

@ -0,0 +1,9 @@
import PostTest from './PostTest'
export const createPosts = async (
numberOfPostsToCreate: number
): Promise<void> => {
for (let index = 1; index <= numberOfPostsToCreate; index++) {
await PostTest.create({ title: `title-${index}` })
}
}

View File

@ -0,0 +1,46 @@
import { FindOptions, Model } from 'sequelize/types'
import { BadRequestError } from '../errors/BadRequestError'
import { parseIntOrDefaultValue } from '../utils/parseIntOrDefaultValue'
interface PaginateModelOptions<M extends Model> {
findOptions?: FindOptions
queryOptions?: {
page?: string
itemsPerPage?: string
}
Model: typeof Model & (new () => M)
}
/** Allows to make a pagination system on a Sequelize model instance */
export const paginateModel = async <M extends Model<any, any>>(
options: PaginateModelOptions<M>
): Promise<{
totalItems: number
hasMore: boolean
page: number
itemsPerPage: number
rows: M[]
}> => {
const {
findOptions = {
order: [['createdAt', 'DESC']]
},
queryOptions,
Model
} = options
const page = parseIntOrDefaultValue(queryOptions?.page, 1)
const itemsPerPage = parseIntOrDefaultValue(queryOptions?.itemsPerPage, 20)
if (itemsPerPage > 100) {
throw new BadRequestError('"itemsPerPage" should be less than 100')
}
const offset = (page - 1) * itemsPerPage
const result = await Model.findAndCountAll<M>({
limit: itemsPerPage,
offset,
...findOptions
})
const { count, rows } = result
const hasMore = page * itemsPerPage < count
return { page, itemsPerPage, totalItems: count, hasMore, rows }
}

View File

@ -0,0 +1,29 @@
import path from 'path'
import { Sequelize } from 'sequelize-typescript'
const sequelize = new Sequelize({
host: process.env.DATABASE_HOST,
database: process.env.DATABASE_NAME,
dialect: process.env.DATABASE_DIALECT,
storage: process.env.DATABASE_DIALECT === 'sqlite' ? ':memory:' : undefined,
username: process.env.DATABASE_USER,
password: process.env.DATABASE_PASSWORD,
port: parseInt(process.env.DATABASE_PORT ?? '3306', 10),
models: [path.join(__dirname, '..', '..', 'models')],
retry: {
max: 10,
match: [
/ConnectionError/,
/SequelizeConnectionError/,
/SequelizeConnectionRefusedError/,
/SequelizeHostNotFoundError/,
/SequelizeHostNotReachableError/,
/SequelizeInvalidConnectionError/,
/SequelizeConnectionTimedOutError/,
/SequelizeConnectionAcquireTimeoutError/,
/Connection terminated unexpectedly/
]
}
})
export { sequelize }