pudate packages
This commit is contained in:
commit
39283423a6
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
dist
|
9
.prettierrc
Normal file
9
.prettierrc
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"useTabs": true,
|
||||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"semi": false,
|
||||
"trailingComma": "none",
|
||||
"arrowParens": "avoid",
|
||||
"printWidth": 79
|
||||
}
|
2996
package-lock.json
generated
Normal file
2996
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
31
package.json
Normal file
31
package.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "axp-mongoose-helper",
|
||||
"version": "1.2.1",
|
||||
"description": "My helper library",
|
||||
"author": "AntoXa PRO <info@antoxa.pro>",
|
||||
"homepage": "https://antoxahub.ru/antoxa/axp-mongoose-helper",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://antoxahub.ru/antoxa/axp-mongoose-helper.git"
|
||||
},
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"files": [
|
||||
"dist",
|
||||
"tsconfig.json"
|
||||
],
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "rollup -c --configPlugin @rollup/plugin-typescript",
|
||||
"prepare": "npm run build"
|
||||
},
|
||||
"dependencies": {
|
||||
"axp-ts": "^1.9.6",
|
||||
"mongoose": "^6.11.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-typescript": "^11.1.2",
|
||||
"prettier": "^2.8.8",
|
||||
"rollup": "^3.26.2"
|
||||
}
|
||||
}
|
12
rollup.config.ts
Normal file
12
rollup.config.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { defineConfig } from 'rollup'
|
||||
import typescript from '@rollup/plugin-typescript'
|
||||
|
||||
export default defineConfig({
|
||||
input: 'src/index.ts',
|
||||
output: {
|
||||
dir: 'dist',
|
||||
format: 'es'
|
||||
},
|
||||
external: ['axp-ts', 'mongoose'],
|
||||
plugins: [typescript()]
|
||||
})
|
1
src/index.ts
Normal file
1
src/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './repository'
|
43
src/repository/_base-action.ts
Normal file
43
src/repository/_base-action.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import { PopulateOptions } from 'mongoose'
|
||||
import { TRepositoryOptions } from '.'
|
||||
|
||||
export abstract class _BaseAction {
|
||||
/**
|
||||
* Настройки репозитория.
|
||||
*/
|
||||
protected _options: TRepositoryOptions
|
||||
|
||||
constructor(options: TRepositoryOptions) {
|
||||
this._options = options
|
||||
}
|
||||
|
||||
/**
|
||||
* Сброс или изменение конфигурации по умолчанию.
|
||||
*/
|
||||
options(options: false | TRepositoryOptions): this {
|
||||
if (options === false) {
|
||||
this._options = {}
|
||||
} else {
|
||||
Object.assign(this._options, options)
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Выборка полей.
|
||||
*/
|
||||
select(arg: string | string[]): this {
|
||||
const items = typeof arg === 'string' ? [arg] : arg
|
||||
this._options.select = Object.assign(this._options.select || [], items)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Заполнение сущностей.
|
||||
*/
|
||||
populate(arg: PopulateOptions | Array<PopulateOptions>): this {
|
||||
this._options.populate = arg
|
||||
return this
|
||||
}
|
||||
}
|
78
src/repository/index.ts
Normal file
78
src/repository/index.ts
Normal file
@ -0,0 +1,78 @@
|
||||
export * from './_base-action'
|
||||
export * from './query-entity-action'
|
||||
export * from './query-collection-action'
|
||||
|
||||
import { Model, FilterQuery, PopulateOptions, ToObjectOptions } from 'mongoose'
|
||||
|
||||
import { QueryCollection } from './query-collection-action'
|
||||
import { QueryEntity } from './query-entity-action'
|
||||
|
||||
/**
|
||||
* Настройки репозитория по умолчанию.
|
||||
*/
|
||||
export type TRepositoryOptions = {
|
||||
select?: string[]
|
||||
populate?: PopulateOptions | Array<PopulateOptions>
|
||||
sort?: any
|
||||
toObject?: false | ToObjectOptions
|
||||
pagination?: {
|
||||
limit?: number
|
||||
maxLimit?: number
|
||||
}
|
||||
}
|
||||
|
||||
export class Repository<T extends Object> {
|
||||
/**
|
||||
* Конфигурация сервиса.
|
||||
*/
|
||||
private _options: TRepositoryOptions
|
||||
|
||||
/**
|
||||
* Модель монгуса.
|
||||
*/
|
||||
public model: Model<T>
|
||||
|
||||
constructor(model: Model<T>, options?: TRepositoryOptions) {
|
||||
this.model = model
|
||||
this._options = options || {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Копирует и возвращает клон объекта настроек для использования
|
||||
* при инициализации других классов внутри этого.
|
||||
*/
|
||||
getOptions(): TRepositoryOptions {
|
||||
return Object.assign({}, this._options)
|
||||
}
|
||||
|
||||
/**
|
||||
* Выборка из коллекции.
|
||||
*/
|
||||
find(filter: FilterQuery<T> = {}): QueryCollection<T> {
|
||||
return new QueryCollection(filter, this.model, this.getOptions())
|
||||
}
|
||||
|
||||
/**
|
||||
* Одна сущность.
|
||||
*/
|
||||
findOne(filter: FilterQuery<T>): QueryEntity<T> {
|
||||
const query = this.model.findOne(filter)
|
||||
return new QueryEntity(query, this.getOptions())
|
||||
}
|
||||
|
||||
/**
|
||||
* Одна сущность по Ид.
|
||||
*/
|
||||
findById(id: string): QueryEntity<T> {
|
||||
const query = this.model.findById(id)
|
||||
return new QueryEntity(query, this.getOptions())
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверка ObjectId
|
||||
*/
|
||||
isValidObjectId(id: string): boolean {
|
||||
if (typeof id !== 'string') return false
|
||||
return id.match(/^[a-f\d]{24}$/i) ? true : false
|
||||
}
|
||||
}
|
193
src/repository/query-collection-action.ts
Normal file
193
src/repository/query-collection-action.ts
Normal file
@ -0,0 +1,193 @@
|
||||
import { FilterQuery, Model, Query, HydratedDocument } from 'mongoose'
|
||||
import { DataResultEntity, Pagination } from 'axp-ts'
|
||||
|
||||
import { TRepositoryOptions } from '.'
|
||||
import { _BaseAction } from './_base-action'
|
||||
|
||||
type urlQueryArgs = {
|
||||
page?: number | string
|
||||
limit?: number | string
|
||||
sort?: string
|
||||
}
|
||||
|
||||
export class QueryCollection<T extends Object> extends _BaseAction {
|
||||
/**
|
||||
* Пагинация.
|
||||
*/
|
||||
public pagination: Pagination
|
||||
|
||||
/**
|
||||
* Фильтр запроса.
|
||||
*/
|
||||
private _filter: FilterQuery<T>
|
||||
|
||||
/**
|
||||
* Модель монгуса.
|
||||
*/
|
||||
private _model: Model<T>
|
||||
|
||||
/**
|
||||
* Запрос.
|
||||
*/
|
||||
private _query: Query<HydratedDocument<T>[], HydratedDocument<T>>
|
||||
|
||||
constructor(
|
||||
filter: FilterQuery<T>,
|
||||
model: Model<T>,
|
||||
options: TRepositoryOptions
|
||||
) {
|
||||
super(options)
|
||||
|
||||
this.pagination = new Pagination(
|
||||
this._options.pagination,
|
||||
this._options.pagination?.maxLimit || 100
|
||||
)
|
||||
|
||||
this._filter = filter
|
||||
this._model = model
|
||||
this._query = this._model.find(this._filter)
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает параметры фильтра (false полный сброс).
|
||||
*/
|
||||
filter(filter: FilterQuery<T> | false): this {
|
||||
if (filter === false) {
|
||||
this._filter = {}
|
||||
} else {
|
||||
Object.assign(this._filter, filter)
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Сортировка.
|
||||
*/
|
||||
sort(value: any): this {
|
||||
this._options.sort = value
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Применение параметров из URL.
|
||||
*/
|
||||
setUrlQuery(args: urlQueryArgs) {
|
||||
// Постраничное разбиение.
|
||||
const { page, limit } = args
|
||||
this.pagination.set({ page, limit })
|
||||
|
||||
// Сортировка.
|
||||
if (args.sort) {
|
||||
this.sort(args.sort)
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Перед выполнением запроса.
|
||||
*/
|
||||
private preExec(): void {
|
||||
try {
|
||||
// Сортировка.
|
||||
if (this._options.sort) {
|
||||
this._query.sort(this._options.sort)
|
||||
// console.log('Sort', this._options.sort);
|
||||
}
|
||||
|
||||
// Применение пагинации.
|
||||
this._query.skip(this.pagination.skip)
|
||||
this._query.limit(this.pagination.limit)
|
||||
|
||||
// Выборка полей в запросе.
|
||||
if (this._options.select) {
|
||||
this._query.select(this._options.select)
|
||||
// console.log('Select:', this._options.select);
|
||||
}
|
||||
|
||||
// Заполнение связей.
|
||||
if (this._options.populate) {
|
||||
this._query.populate(this._options.populate)
|
||||
// console.log('Populate:', this._options.populate);
|
||||
}
|
||||
} catch (ex: any) {
|
||||
console.log('Ex ProExec:', ex.message)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполнение запроса.
|
||||
*/
|
||||
exec(): Promise<T[]> {
|
||||
return new Promise<T[]>(async (resolve, reject) => {
|
||||
try {
|
||||
// Коллекция для возврата.
|
||||
let items: HydratedDocument<T, {}, {}>[] = []
|
||||
|
||||
// Кол-во сущностей относительно фильтра.
|
||||
const total = await this._model.countDocuments(this._filter)
|
||||
// console.log('Total:', total)
|
||||
|
||||
// Если есть данные.
|
||||
if (total > 0) {
|
||||
// Устанавливаем значение общего кол-ва.
|
||||
this.pagination.set({ total })
|
||||
|
||||
// Применяем параметры перед запросом.
|
||||
this.preExec()
|
||||
|
||||
// Выполнение запроса.
|
||||
items = await this._query.exec()
|
||||
|
||||
// Трансформация в объект.
|
||||
if (this._options.toObject !== false) {
|
||||
resolve(
|
||||
items.map(e =>
|
||||
e.toObject<T>(this._options.toObject || undefined)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
resolve(items)
|
||||
}
|
||||
} else {
|
||||
// Возвращаем результат.
|
||||
resolve([])
|
||||
}
|
||||
} catch (ex: any) {
|
||||
// Возвращаем ошибку.
|
||||
reject(ex)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает промис с моделью результата данных.
|
||||
*/
|
||||
dataResult(
|
||||
cb?: (dR: DataResultEntity<T[]>) => void
|
||||
): Promise<DataResultEntity<T[]>> {
|
||||
return new Promise<DataResultEntity<T[]>>(async resolve => {
|
||||
const dR = new DataResultEntity<T[]>()
|
||||
|
||||
try {
|
||||
const data = await this.exec()
|
||||
if (data) {
|
||||
dR.setData(data)
|
||||
dR.info.pagination = this.pagination.toObject()
|
||||
} else {
|
||||
dR.status = 404
|
||||
dR.message = 'Not found'
|
||||
dR.errors.push({ code: 'not_found', text: 'Resource not found' })
|
||||
}
|
||||
} catch (ex: any) {
|
||||
dR.status = 500
|
||||
dR.message = 'Server Error'
|
||||
dR.errors.push({ code: 'server', text: ex.message })
|
||||
}
|
||||
|
||||
if (cb) cb(dR)
|
||||
resolve(dR)
|
||||
})
|
||||
}
|
||||
}
|
73
src/repository/query-entity-action.ts
Normal file
73
src/repository/query-entity-action.ts
Normal file
@ -0,0 +1,73 @@
|
||||
import { DataResultEntity } from 'axp-ts'
|
||||
import { Query, HydratedDocument } from 'mongoose'
|
||||
|
||||
import { TRepositoryOptions } from '.'
|
||||
import { _BaseAction } from './_base-action'
|
||||
|
||||
export class QueryEntity<T extends Object> extends _BaseAction {
|
||||
private _query: Query<HydratedDocument<T> | null, HydratedDocument<T>>
|
||||
|
||||
constructor(
|
||||
query: Query<HydratedDocument<T> | null, HydratedDocument<T>>,
|
||||
options: TRepositoryOptions
|
||||
) {
|
||||
super(options)
|
||||
this._query = query
|
||||
}
|
||||
|
||||
exec(): Promise<T | null> {
|
||||
return new Promise<T | null>(async (resolve, reject) => {
|
||||
let result: HydratedDocument<T> | null = null
|
||||
|
||||
try {
|
||||
// Выборка полей в запросе.
|
||||
if (this._options.select) {
|
||||
this._query.select(this._options.select)
|
||||
}
|
||||
|
||||
// Заполнение связей.
|
||||
if (this._options.populate) {
|
||||
this._query.populate(this._options.populate)
|
||||
}
|
||||
|
||||
result = await this._query.exec()
|
||||
|
||||
if (result && this._options.toObject) {
|
||||
resolve(result.toObject<T>(this._options.toObject || undefined))
|
||||
return
|
||||
}
|
||||
} catch (ex: any) {
|
||||
reject(ex)
|
||||
}
|
||||
|
||||
resolve(result)
|
||||
})
|
||||
}
|
||||
|
||||
dataResult(
|
||||
cb?: (dR: DataResultEntity<T | null>) => void
|
||||
): Promise<DataResultEntity<T | null>> {
|
||||
return new Promise(async resolve => {
|
||||
const dR = new DataResultEntity<T | null>()
|
||||
|
||||
try {
|
||||
const result = await this.exec()
|
||||
if (result) {
|
||||
dR.setData(result)
|
||||
} else {
|
||||
dR.status = 404
|
||||
dR.message = 'Not Found'
|
||||
dR.errors.push({ code: 'not_found', text: 'Resource not found' })
|
||||
dR.setData(null)
|
||||
}
|
||||
} catch (ex: any) {
|
||||
dR.status = 500
|
||||
dR.message = 'Server Error'
|
||||
dR.errors.push({ code: 'server', text: ex.message })
|
||||
}
|
||||
|
||||
if (cb) cb(dR)
|
||||
resolve(dR)
|
||||
})
|
||||
}
|
||||
}
|
17
tsconfig.json
Normal file
17
tsconfig.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "ES6",
|
||||
|
||||
"baseUrl": ".",
|
||||
"rootDir": "src",
|
||||
"outDir": "dist",
|
||||
|
||||
"strict": true,
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
|
||||
"declaration": true
|
||||
},
|
||||
"exclude": ["rollup.config.ts"]
|
||||
}
|
Loading…
Reference in New Issue
Block a user