prisma-repo
Version:
Generating repository pattern using Prisma ORM faster and easier
349 lines (297 loc) • 9.85 kB
text/typescript
/* eslint-disable @typescript-eslint/ban-ts-comment */
// Keep in mind that this file is automatically generated.
// You can change the content of this file, but it will be overwritten.
import { Prisma } from '@prisma/client';
import _ from 'lodash';
import {
Aggregate,
AnyRecord,
BaseOption,
CountArgs,
Find,
ModelName,
models,
ModelScalarFields,
ModelStructure,
ModelTypes,
} from './prisma-repo';
export const extractCondition = <Cursor, Where>(conditions: Cursor | Where | number | string) => {
const dbCond = _.isObject(conditions) ? conditions : { id: _.toNumber(conditions) };
return dbCond;
};
/**
* @param modelName - The model name
*/
const BaseRepository = <
T extends ModelName,
Where extends ModelTypes[T]['Where'],
Select extends ModelTypes[T]['Select'],
Include extends ModelTypes[T]['Include'],
Create extends ModelTypes[T]['Create'],
Update extends ModelTypes[T]['Update'],
Cursor extends ModelTypes[T]['Cursor'],
Order extends ModelTypes[T]['Order'],
Delegate extends ModelTypes[T]['Delegate'],
Scalar extends ModelScalarFields<T>,
Model extends ModelStructure[T]
>(
modelName: T
) => {
abstract class AbstractBaseRepository {
protected static modelName: T = modelName;
/**
* Find zero or more `model` that matches the filter.\
* Note, that providing `undefined` is treated as the value not being there.
*/
public static async findAll(
conditions: Where | number | string,
filterQueryParams: AnyRecord = {},
query: AnyRecord = {},
option: Find<Select, Include, Cursor, Order, Scalar> = {}
) {
const limit = +(query.limit === 'all' ? 0 : _.get(query, 'limit', 10));
const offset = query.page && query.page > 0 ? limit * (query.page - 1) : 0;
const otherOptions = _.omit(query, ['limit', 'offset', 'page']);
const where = {
...extractCondition(conditions),
...filterQueryParams,
...otherOptions,
};
return {
// @ts-ignore
rows: (await AbstractBaseRepository.model.findMany({
// @ts-ignore
where,
...option,
skip: offset,
...(limit > 0 && { take: limit }),
})) as Model[],
/* @ts-ignore */
count: await this.count(where),
};
}
/**
* Alternative of `findAll`.\
* It works same as `findOne` but only have different names.\
* It exists for anyone who prefer to use prisma `functions` original name.
*/
public static async findMany(...params: Parameters<typeof this.findAll>) {
return AbstractBaseRepository.findAll(...params);
}
/**
* Find the first `model` that matches the filter.\
* Note, that providing `undefined` is treated as the value not being there.
*/
public static async findOne(
conditions: Where | number | string,
option: Find<Select, Include, Cursor, Order, Scalar> = {}
) {
const where = extractCondition(conditions);
// @ts-ignore
return AbstractBaseRepository.model.findFirst({
// @ts-ignore
where,
...option,
}) as Promise<Model | null>;
}
/**
* Alternative of `findOne`.\
* It works same as `findOne` but only have different names.\
* It exists for anyone who prefer to use prisma `functions` original name.
*/
public static async findFirst(...params: Parameters<typeof this.findOne>) {
return AbstractBaseRepository.findOne(...params);
}
/**
* Find zero or one `model` that matches the filter.\
* Note, that providing `undefined` is treated as the value not being there.\
* It works same as `findOne` or `findFirst` but only accept a unique column.
*/
public static async findUnique(
conditions: Cursor | number | string,
option: BaseOption<Include, Select> = {}
) {
const where = extractCondition(conditions);
// @ts-ignore
return AbstractBaseRepository.model.findUnique({
// @ts-ignore
where,
...option,
}) as Promise<Model | null>;
}
/**
* Create a `model`.
*/
public static async create(data: Create, option: BaseOption<Include, Select> = {}) {
// @ts-ignore
return AbstractBaseRepository.model.create({ data, ...option }) as Promise<Model>;
}
/**
* Update a `model`.
*/
public static async update(
conditions: Where | number | string,
data: Update | Create,
option: BaseOption<Include, Select> = {}
) {
const where = extractCondition(conditions);
// @ts-ignore
return AbstractBaseRepository.model.update({
data,
// @ts-ignore
where,
...option,
}) as Promise<Model>;
}
/**
* Delete any `model` that match with the conditions.
*/
public static async delete(conditions: Where | number | string) {
const where = extractCondition(conditions);
// @ts-ignore
return AbstractBaseRepository.model.deleteMany({
// @ts-ignore
where,
}) as Promise<Prisma.BatchPayload>;
}
/**
* Delete a `model`.
*/
public static async deleteOne(conditions: Where | number | string) {
const where = extractCondition(conditions);
// @ts-ignore
return AbstractBaseRepository.model.delete({
// @ts-ignore
where,
}) as Promise<Model>;
}
/**
* Create or update one `model`.
*/
public static async updateOrCreate(
conditions: Where | number | string,
data: Create,
option: Find<Select, Include, Cursor, Order, Scalar> = {}
) {
const obj = await AbstractBaseRepository.findOne(conditions, option);
if (obj) return AbstractBaseRepository.update(conditions, data, option);
return AbstractBaseRepository.create(data);
}
/**
* Alternative of `updateOrCreate`.\
* It works same as `updateOrCreate` but only have different names.\
* It exists for anyone who prefer to use prisma `functions` original name.
*/
public static async upsert(...params: Parameters<typeof this.updateOrCreate>) {
return AbstractBaseRepository.updateOrCreate(...params);
}
/**
* Create many `model`.
*/
public static async bulkCreate(data: Prisma.Enumerable<Create>, skipDuplicates = true) {
// @ts-ignore
return AbstractBaseRepository.model.createMany({
data,
// @ts-ignore
skipDuplicates,
}) as Promise<Prisma.BatchPayload>;
}
/**
* Alternative of `bulkCreate`.\
* It works same as `bulkCreate` but only have different names.\
* It exists for anyone who prefer to use prisma `functions` original name.
*/
public static async createMany(...params: Parameters<typeof this.bulkCreate>) {
return AbstractBaseRepository.bulkCreate(...params);
}
/**
* Update zero or more `model`.
* Note, that providing `undefined` is treated as the value not being there.
*/
public static async bulkUpdate(where: Where, data: Prisma.Enumerable<Update>) {
// @ts-ignore
return AbstractBaseRepository.model.updateMany({
data,
where,
}) as Promise<Prisma.BatchPayload>;
}
/**
* Alternative of `bulkUpdate`.\
* It works same as `bulkUpdate` but only have different names.\
* It exists for anyone who prefer to use prisma `functions` original name.
*/
public static async updateMany(...params: Parameters<typeof this.bulkUpdate>) {
return AbstractBaseRepository.bulkUpdate(...params);
}
/**
* Count the number of `model`.\
* Note, that providing `undefined` is treated as the value not being there.
*/
public static async count(
conditions: Where | number | string,
option: CountArgs<Select, Cursor, Order, Scalar> = {}
) {
const where = extractCondition(conditions);
// @ts-ignore
return AbstractBaseRepository.model.count({
// @ts-ignore
where,
...option,
}) as Promise<number>;
}
public static async groupBy(
conditions: Where | number | string,
aggregator: Omit<
// @ts-ignore
Parameters<typeof this.model.aggregate>[0],
'where' | 'cursor'
> & {
groupBy: Scalar[];
}
) {
const where = extractCondition(conditions);
// @ts-ignore
return AbstractBaseRepository.model.groupBy({
// @ts-ignore
where,
...aggregator,
});
}
/**
* Allows you to perform aggregations operations on a `model`.\
* Note, that providing `undefined` is treated as the value not being there.\
* If no any kind of aggregator provided, will use `count` by default.
*/
public static aggregate(
conditions: Where | number | string,
aggregator: Omit<
// @ts-ignore
Parameters<typeof this.model.aggregate>[0],
'cursor' | 'take' | 'skip' | 'orderBy' | 'where'
>,
option: Aggregate<Cursor, Order, Scalar> = {}
) {
// @ts-ignore
const aggregate = AbstractBaseRepository.model.aggregate as Delegate['aggregate'];
const where = extractCondition(conditions);
if (_.isEmpty(aggregator)) {
// @ts-ignore
// eslint-disable-next-line no-param-reassign, no-underscore-dangle
aggregator._count = true;
}
// @ts-ignore
return aggregate({
// @ts-ignore
where,
...aggregator,
...option,
}) as ReturnType<typeof aggregate>;
}
public static get model(): Delegate {
// @ts-ignore
return models[AbstractBaseRepository.modelName];
}
}
return AbstractBaseRepository;
};
export default BaseRepository;