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