This commit is contained in:
2024-10-11 17:11:47 +07:00
parent 9023725fc4
commit 85026556b2
17 changed files with 794 additions and 92 deletions

5
package-lock.json generated
View File

@@ -36,6 +36,7 @@
"bcryptjs": "^2.4.3",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"epess-web-backend": "file:",
"graphql": "^16.9.0",
"graphql-scalars": "^1.23.0",
"graphql-tools": "^9.0.1",
@@ -8812,6 +8813,10 @@
"node": ">=6"
}
},
"node_modules/epess-web-backend": {
"resolved": "",
"link": true
},
"node_modules/error-ex": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",

View File

@@ -9,7 +9,7 @@
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "npm run prisma:generate && nest start --watch",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"prisma:generate": "npx prisma generate --schema=./epess-database/prisma/schema.prisma",
@@ -50,6 +50,7 @@
"bcryptjs": "^2.4.3",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"epess-web-backend": "file:",
"graphql": "^16.9.0",
"graphql-scalars": "^1.23.0",
"graphql-tools": "^9.0.1",

View File

@@ -4,10 +4,6 @@ import { ClerkModule } from './clerk/clerk.module';
import { RestfulModule } from './restful/restful.module';
@Module({
imports: [
GraphqlModule, // GraphQL setup
ClerkModule, // Clerk setup
RestfulModule,
],
imports: [GraphqlModule, ClerkModule, RestfulModule],
})
export class AppModule {}

View File

@@ -27,11 +27,11 @@ export class CenterSchema extends PothosSchema {
description: t.exposeString('description'),
location: t.exposeString('location'),
individual: t.exposeBoolean('individual'),
createdAt: t.expose('createdAt', { type: 'Date' }),
updatedAt: t.expose('updatedAt', { type: 'Date' }),
createdAt: t.expose('createdAt', { type: 'DateTime' }),
updatedAt: t.expose('updatedAt', { type: 'DateTime' }),
services: t.relation('services'),
centerOwner: t.relation('centerOwner'),
chatRoom: t.relation('chatRoom'),
// chatRoom: t.relation('chatRoom'),
centerStaff: t.relation('CenterStaff'),
resume: t.relation('Resume'),
}),

View File

@@ -1,14 +1,20 @@
import SchemaBuilder from '@pothos/core';
import PrismaPlugin, { PothosPrismaDatamodel } from '@pothos/plugin-prisma';
import { PrismaClient } from '@prisma/client';
import PrismaPlugin, {
PothosPrismaDatamodel,
PrismaClient,
} from '@pothos/plugin-prisma';
import PrismaUtils from '@pothos/plugin-prisma-utils';
import { Request } from 'express';
import { Request, Response } from 'express';
import type PrismaTypes from '../types/pothos.generated';
import { getDatamodel } from '../types/pothos.generated';
import { DateTimeResolver } from 'graphql-scalars';
import { DateTimeResolver, JSONResolver } from 'graphql-scalars';
import { Injectable } from '@nestjs/common';
import { PrismaCrudGenerator } from './graphql.generator';
export interface SchemaContext {
req: Request;
res: Response;
generator: PrismaCrudGenerator<BuilderTypes>;
}
export interface SchemaBuilderOption {
@@ -16,30 +22,38 @@ export interface SchemaBuilderOption {
PrismaTypes: PrismaTypes;
DataModel: PothosPrismaDatamodel;
Scalars: {
Date: {
DateTime: {
Input: Date;
Output: Date;
};
Json: {
Input: JSON;
Output: JSON;
};
};
}
export function createBuilder(client: PrismaClient) {
const builder = new SchemaBuilder<SchemaBuilderOption>({
@Injectable()
export class Builder extends SchemaBuilder<SchemaBuilderOption> {
public generator: PrismaCrudGenerator<BuilderTypes>;
constructor(private readonly prisma: PrismaClient) {
super({
plugins: [PrismaPlugin, PrismaUtils],
prisma: {
client,
client: prisma,
exposeDescriptions: true,
filterConnectionTotalCount: true,
onUnusedQuery: process.env.NODE_ENV === 'production' ? null : 'warn',
dmmf: getDatamodel(),
},
});
builder.queryType({});
// builder.mutationType({});
// builder.subscriptionType({});
builder.addScalarType('Date', DateTimeResolver, {});
return builder;
this.generator = new PrismaCrudGenerator<BuilderTypes>(this);
this.addScalarType('DateTime', DateTimeResolver);
this.addScalarType('Json', JSONResolver);
this.queryType({});
// this.mutationType({});
}
export type Builder = ReturnType<typeof createBuilder>;
}
export type BuilderTypes =
PothosSchemaTypes.ExtendDefaultTypes<SchemaBuilderOption>;

View File

@@ -0,0 +1,637 @@
import { Inject, Injectable } from '@nestjs/common';
import {
type BaseEnum,
type EnumRef,
InputObjectRef,
type InputType,
type InputTypeParam,
type SchemaTypes,
} from '@pothos/core';
import { type PrismaModelTypes, getModel } from '@pothos/plugin-prisma';
import type { FilterOps } from '@pothos/plugin-prisma-utils';
import * as Prisma from '@prisma/client';
import { SchemaBuilderToken } from '@smatch-corp/nestjs-pothos';
//
const filterOps = ['equals', 'in', 'notIn', 'not', 'is', 'isNot'] as const;
const sortableFilterProps = ['lt', 'lte', 'gt', 'gte'] as const;
const stringFilterOps = [
...filterOps,
'contains',
'startsWith',
'endsWith',
] as const;
const sortableTypes = ['String', 'Int', 'Float', 'DateTime', 'BigInt'] as const;
const listOps = ['every', 'some', 'none'] as const;
const scalarListOps = [
'has',
'hasSome',
'hasEvery',
'isEmpty',
'equals',
] as const;
const JsonFilterOps = ['equals', 'in', 'notIn', 'not', 'is', 'isNot'] as const;
const EnumFilterOps = ['equals', 'not'] as const;
@Injectable()
export class PrismaCrudGenerator<Types extends SchemaTypes> {
private refCache = new Map<
InputType<Types> | string,
Map<string, InputObjectRef<Types, unknown>>
>();
private enumRefs = new Map<string, EnumRef<Types, unknown>>();
constructor(
@Inject(SchemaBuilderToken)
private builder: PothosSchemaTypes.SchemaBuilder<Types>,
) {}
findManyArgs<Name extends string & keyof Types['PrismaTypes']>(
modelName: Name,
) {
return this.builder.args((t) => ({
filter: t.field({
type: this.getWhere(modelName),
required: false,
}),
orderBy: t.field({
type: this.getOrderBy(modelName),
required: false,
}),
cursor: t.field({
type: this.getWhereUnique(modelName),
required: false,
}),
take: t.field({
type: 'Int',
required: false,
}),
skip: t.field({
type: 'Int',
required: false,
}),
}));
}
getWhere<Name extends string & keyof Types['PrismaTypes']>(
modelName: Name,
without?: string[],
) {
const withoutName = (without ?? [])
.map((name) => `Without${capitalize(name)}`)
.join('');
const fullName = `${modelName}${withoutName}Filter`;
return this.getRef(modelName, fullName, () => {
const model = getModel(modelName, this.builder);
return this.builder.prismaWhere(modelName, {
name: fullName,
fields: (() => {
const fields: Record<string, InputType<Types>> = {};
const withoutFields = model.fields.filter((field) =>
without?.includes(field.name),
);
model.fields
.filter(
(field) =>
!withoutFields.some(
(f) =>
f.name === field.name ||
f.relationFromFields?.includes(field.name),
),
)
.forEach((field) => {
// biome-ignore lint/suspicious/noImplicitAnyLet: <explanation>
let type;
switch (field.kind) {
case 'scalar':
type = field.isList
? this.getScalarListFilter(
this.mapScalarType(field.type) as InputType<Types>,
)
: this.getFilter(
this.mapScalarType(field.type) as InputType<Types>,
);
break;
case 'enum':
type = field.isList
? this.getScalarListFilter(this.getEnum(field.type))
: this.getFilter(this.getEnum(field.type));
break;
case 'object':
type = field.isList
? this.getListFilter(this.getWhere(field.type as Name))
: this.getWhere(field.type as Name);
break;
case 'unsupported':
break;
default:
throw new Error(`Unknown field kind ${field.kind}`);
}
if (!type) {
return;
}
fields[field.name] = type;
});
return fields;
}) as never,
}) as InputObjectRef<
Types,
(PrismaModelTypes & Types['PrismaTypes'][Name])['Where']
>;
});
}
getWhereUnique<Name extends string & keyof Types['PrismaTypes']>(
modelName: Name,
) {
const name = `${modelName}UniqueFilter`;
return this.getRef(modelName, name, () => {
const model = getModel(modelName, this.builder);
return this.builder.prismaWhereUnique(modelName, {
name,
fields: (() => {
const fields: Record<string, InputType<Types>> = {};
model.fields
.filter(
(field) =>
field.isUnique ||
field.isId ||
model.uniqueIndexes.some((index) =>
index.fields.includes(field.name),
) ||
model.primaryKey?.fields.includes(field.name),
)
.forEach((field) => {
// biome-ignore lint/suspicious/noImplicitAnyLet: <explanation>
let type;
switch (field.kind) {
case 'scalar':
type = this.mapScalarType(field.type) as InputType<Types>;
break;
case 'enum':
type = this.getEnum(field.type);
break;
case 'object':
case 'unsupported':
break;
default:
throw new Error(`Unknown field kind ${field.kind}`);
}
if (!type) {
return;
}
fields[field.name] = type;
});
return fields;
}) as never,
}) as InputObjectRef<
Types,
(PrismaModelTypes & Types['PrismaTypes'][Name])['WhereUnique']
>;
});
}
getOrderBy<Name extends string & keyof Types['PrismaTypes']>(
modelName: Name,
) {
const name = `${modelName}OrderBy`;
return this.getRef(modelName, name, () => {
const model = getModel(modelName, this.builder);
return this.builder.prismaOrderBy(modelName, {
name,
fields: () => {
const fields: Record<string, InputType<Types> | boolean> = {};
model.fields.forEach((field) => {
// biome-ignore lint/suspicious/noImplicitAnyLet: <explanation>
let type;
switch (field.kind) {
case 'scalar':
case 'enum':
type = true;
break;
case 'object':
type = this.getOrderBy(field.type as Name);
break;
case 'unsupported':
break;
default:
throw new Error(`Unknown field kind ${field.kind}`);
}
if (type) {
fields[field.name] = type;
}
});
return fields as {};
},
});
});
}
getCreateInput<Name extends string & keyof Types['PrismaTypes']>(
modelName: Name,
without?: string[],
) {
const withoutName = (without ?? [])
.map((name) => `Without${capitalize(name)}`)
.join('');
const fullName = `${modelName}Create${withoutName}Input`;
return this.getRef(modelName, fullName, () => {
const model = getModel(modelName, this.builder);
return this.builder.prismaCreate(modelName, {
name: fullName,
fields: (() => {
const fields: Record<string, InputTypeParam<Types>> = {};
const withoutFields = model.fields.filter((field) =>
without?.includes(field.name),
);
const relationIds = model.fields.flatMap(
(field) => field.relationFromFields ?? [],
);
model.fields
.filter(
(field) =>
!withoutFields.some(
(f) =>
f.name === field.name ||
f.relationFromFields?.includes(field.name),
) && !relationIds.includes(field.name),
)
.forEach((field) => {
// biome-ignore lint/suspicious/noImplicitAnyLet: <explanation>
let type;
switch (field.kind) {
case 'scalar':
type = this.mapScalarType(field.type) as InputType<Types>;
break;
case 'enum':
type = this.getEnum(field.type);
break;
case 'object':
type = this.getCreateRelationInput(modelName, field.name);
break;
case 'unsupported':
break;
default:
throw new Error(`Unknown field kind ${field.kind}`);
}
if (type) {
fields[field.name] = type;
}
});
return fields;
}) as never,
}) as InputObjectRef<
Types,
(PrismaModelTypes & Types['PrismaTypes'][Name])['Create']
>;
});
}
getCreateRelationInput<
Name extends string & keyof Types['PrismaTypes'],
Relation extends Model['RelationName'],
Model extends
PrismaModelTypes = Types['PrismaTypes'][Name] extends PrismaModelTypes
? Types['PrismaTypes'][Name]
: never,
>(modelName: Name, relation: Relation) {
return this.getRef(
`${modelName}${capitalize(relation)}`,
'CreateRelationInput',
() => {
const model = getModel(modelName, this.builder);
return this.builder.prismaCreateRelation(modelName, relation, {
fields: () => {
const relationField = model.fields.find(
(field) => field.name === relation,
)!;
const relatedModel = getModel(relationField.type, this.builder);
const relatedFieldName = relatedModel.fields.find(
(field) => field.relationName === relationField.relationName,
)!;
return {
create: this.getCreateInput(relationField.type as Name, [
relatedFieldName.name,
]),
connect: this.getWhereUnique(relationField.type as Name),
};
},
} as never) as InputObjectRef<
Types,
NonNullable<Model['Create'][Relation & keyof Model['Update']]>
>;
},
);
}
getCreateManyInput<Name extends string & keyof Types['PrismaTypes']>(
modelName: Name,
without?: string[],
) {
const withoutName = (without ?? [])
.map((name) => `Without${capitalize(name)}`)
.join('');
const fullName = `${modelName}Create${withoutName}Input`;
return this.getRef(modelName, fullName, () => {
const model = getModel(modelName, this.builder);
return this.builder.prismaCreateMany(modelName, {
name: fullName,
fields: (() => {
const fields: Record<string, InputTypeParam<Types>> = {};
const withoutFields = model.fields.filter((field) =>
without?.includes(field.name),
);
const relationIds = model.fields.flatMap(
(field) => field.relationFromFields ?? [],
);
model.fields
.filter(
(field) =>
!withoutFields.some(
(f) =>
f.name === field.name ||
f.relationFromFields?.includes(field.name),
) && !relationIds.includes(field.name),
)
.forEach((field) => {
// biome-ignore lint/suspicious/noImplicitAnyLet: <explanation>
let type;
switch (field.kind) {
case 'scalar':
type = this.mapScalarType(field.type) as InputType<Types>;
break;
case 'enum':
type = this.getEnum(field.type);
break;
case 'unsupported':
break;
default:
throw new Error(`Unknown field kind ${field.kind}`);
}
if (type) {
fields[field.name] = type;
}
});
return fields;
}) as never,
}) as InputObjectRef<
Types,
(PrismaModelTypes & Types['PrismaTypes'][Name])['Create']
>;
});
}
getUpdateInput<Name extends string & keyof Types['PrismaTypes']>(
modelName: Name,
without?: string[],
) {
const withoutName = (without ?? [])
.map((name) => `Without${capitalize(name)}`)
.join('');
const fullName = `${modelName}Update${withoutName}Input`;
return this.getRef(modelName, fullName, () => {
const model = getModel(modelName, this.builder);
return this.builder.prismaUpdate(modelName, {
name: fullName,
fields: (() => {
const fields: Record<string, InputTypeParam<Types>> = {};
const withoutFields = model.fields.filter((field) =>
without?.includes(field.name),
);
const relationIds = model.fields.flatMap(
(field) => field.relationFromFields ?? [],
);
model.fields
.filter(
(field) =>
!withoutFields.some(
(f) =>
f.name === field.name ||
f.relationFromFields?.includes(field.name),
) && !relationIds.includes(field.name),
)
.forEach((field) => {
// biome-ignore lint/suspicious/noImplicitAnyLet: <explanation>
let type;
switch (field.kind) {
case 'scalar':
type = this.mapScalarType(field.type) as InputType<Types>;
break;
case 'enum':
type = this.getEnum(field.type);
break;
case 'object':
type = this.getUpdateRelationInput(modelName, field.name);
break;
case 'unsupported':
break;
default:
throw new Error(`Unknown field kind ${field.kind}`);
}
if (type) {
fields[field.name] = type;
}
});
return fields;
}) as never,
}) as InputObjectRef<
Types,
(PrismaModelTypes & Types['PrismaTypes'][Name])['Update']
>;
});
}
getUpdateRelationInput<
Name extends string & keyof Types['PrismaTypes'],
Relation extends Model['RelationName'],
Model extends
PrismaModelTypes = Types['PrismaTypes'][Name] extends PrismaModelTypes
? Types['PrismaTypes'][Name]
: never,
>(modelName: Name, relation: Relation) {
return this.getRef(
`${modelName}${capitalize(relation)}`,
'UpdateRelationInput',
() => {
const model = getModel(modelName, this.builder);
return this.builder.prismaUpdateRelation(modelName, relation, {
fields: () => {
const relationField = model.fields.find(
(field) => field.name === relation,
)!;
const relatedModel = getModel(relationField.type, this.builder);
const relatedFieldName = relatedModel.fields.find(
(field) => field.relationName === relationField.relationName,
)!.name;
if (relationField.isList) {
return {
create: this.getCreateInput(relationField.type as Name, [
relatedFieldName,
]),
createMany: {
skipDuplicates: 'Boolean',
data: this.getCreateInput(relationField.type as Name, [
relatedFieldName,
]),
},
set: this.getWhereUnique(relationField.type as Name),
disconnect: this.getWhereUnique(relationField.type as Name),
delete: this.getWhereUnique(relationField.type as Name),
connect: this.getWhereUnique(relationField.type as Name),
update: {
where: this.getWhereUnique(relationField.type as Name),
data: this.getUpdateInput(relationField.type as Name, [
relatedFieldName,
]),
},
updateMany: {
where: this.getWhere(relationField.type as Name, [
relatedFieldName,
]),
data: this.getUpdateInput(relationField.type as Name, [
relatedFieldName,
]),
},
deleteMany: this.getWhere(relationField.type as Name, [
relatedFieldName,
]),
};
}
return {
create: this.getCreateInput(relationField.type as Name, [
relatedFieldName,
]),
update: this.getUpdateInput(relationField.type as Name, [
relatedFieldName,
]),
connect: this.getWhereUnique(relationField.type as Name),
disconnect: relationField.isRequired ? undefined : 'Boolean',
delete: relationField.isRequired ? undefined : 'Boolean',
};
},
} as never) as InputObjectRef<
Types,
NonNullable<Model['Update'][Relation & keyof Model['Update']]>
>;
},
);
}
private getFilter(type: InputType<Types>) {
return this.getRef(type, `${String(type)}Filter`, () => {
const ops: FilterOps[] = [...filterOps];
if (type === 'String') {
ops.push(...stringFilterOps);
}
if (sortableTypes.includes(type as never)) {
ops.push(...sortableFilterProps);
}
return this.builder.prismaFilter(type, {
ops,
});
});
}
private getScalarListFilter(type: InputType<Types>) {
return this.getRef(type, `${String(type)}ListFilter`, () =>
this.builder.prismaScalarListFilter(type, {
ops: scalarListOps,
}),
);
}
private getListFilter(type: InputType<Types>) {
return this.getRef(type, `${String(type)}ListFilter`, () =>
this.builder.prismaListFilter(type, {
ops: listOps,
}),
);
}
private getEnum(name: string) {
if (!this.enumRefs.has(name)) {
const enumRef = this.builder.enumType(
(Prisma as unknown as Record<string, BaseEnum>)[name],
{
name,
},
);
this.enumRefs.set(name, enumRef);
}
return this.enumRefs.get(name)!;
}
private mapScalarType(type: string) {
switch (type) {
case 'String':
case 'Boolean':
case 'Int':
case 'Float':
case 'DateTime':
case 'Json':
return type;
default:
return null;
}
}
private getRef<T extends InputObjectRef<Types, unknown>>(
key: InputType<Types> | string,
name: string,
create: () => T,
): T {
if (!this.refCache.has(key)) {
this.refCache.set(key, new Map());
}
const cache = this.refCache.get(key)!;
if (cache.has(name)) {
return cache.get(name)! as T;
}
const ref = new InputObjectRef<Types, unknown>(name);
cache.set(name, ref);
this.builder.configStore.associateParamWithRef(ref, create());
return ref as T;
}
}
function capitalize(str: string) {
return str[0].toUpperCase() + str.slice(1);
}

View File

@@ -1,15 +1,14 @@
import { ApolloDriverConfig } from '@nestjs/apollo';
import { MiddlewareConsumer, Module } from '@nestjs/common';
import { Global, MiddlewareConsumer, Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { PothosModule } from '@smatch-corp/nestjs-pothos';
import { PothosApolloDriver } from '@smatch-corp/nestjs-pothos-apollo-driver';
import { createBuilder } from './graphql.builder';
import { Builder } from './graphql.builder';
import { PrismaService } from '../prisma/prisma.service';
import { GraphQLValidationMiddleware } from 'src/middlewares/graphql.middleware';
import { PrismaModule } from 'src/prisma/prisma.module';
import { UserModule } from 'src/user/user.module';
import { CenterModule } from 'src/center/center.module';
import { GraphqlService } from './graphql.service';
import { ServiceModule } from 'src/service/service.module';
import { ChatroomModule } from 'src/chatroom/chatroom.module';
import { CenterStaffModule } from 'src/centerstaff/centerstaff.module';
@@ -17,6 +16,9 @@ import { ResumeModule } from 'src/resume/resume.module';
import { WorkshopModule } from 'src/workshop/workshop.module';
import { WorkshopOrganizationModule } from 'src/workshoporganization/workshoporganization.module';
import { WorkshopSubscriptionModule } from 'src/workshopsubscription/workshopsubscription.module';
import { PrismaCrudGenerator } from './graphql.generator';
@Global()
@Module({
imports: [
PrismaModule,
@@ -32,14 +34,26 @@ import { WorkshopSubscriptionModule } from 'src/workshopsubscription/workshopsub
PothosModule.forRoot({
builder: {
inject: [PrismaService],
useFactory: (prisma: PrismaService) => createBuilder(prisma),
useFactory: (prisma: PrismaService) => new Builder(prisma),
},
}),
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: PothosApolloDriver,
}),
],
providers: [GraphqlService],
providers: [
{
provide: Builder,
useFactory: (prisma: PrismaService) => new Builder(prisma),
inject: [PrismaService],
},
{
provide: PrismaCrudGenerator,
useFactory: (builder: Builder) => new PrismaCrudGenerator(builder),
inject: [Builder],
},
],
exports: [Builder, PrismaCrudGenerator],
})
export class GraphqlModule {
configure(consumer: MiddlewareConsumer) {

View File

@@ -2,8 +2,6 @@ import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { cors } from './common/utils/cors.utils';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
// Import DateTime scalar if necessary
require('./common/utils/datetime.utils');
async function bootstrap() {
const app = await NestFactory.create(AppModule);

View File

@@ -4,7 +4,7 @@ import { Request, Response, NextFunction } from 'express';
@Injectable()
export class GraphQLValidationMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
// Only handle POST requests
// handle post request
if (
req.method === 'POST' &&
req.headers['content-type'] === 'application/json'
@@ -12,18 +12,14 @@ export class GraphQLValidationMiddleware implements NestMiddleware {
const { query, mutation, subscription } = req.body;
// If none of these are present, return a custom error response
if (!query && !mutation && !subscription) {
return res.status(400).json({
errors: [
{
message:
'Must provide a valid GraphQL query, mutation, or subscription.',
},
],
});
}
// handle query only contain \n
if (query.trim() === '') {
if (
!query &&
!mutation &&
!subscription &&
query.trim() === '' &&
mutation.trim() === '' &&
subscription.trim() === ''
) {
return res.status(400).json({
errors: [
{

View File

@@ -12,7 +12,24 @@ export class PrismaService extends PrismaClient implements OnModuleInit {
constructor() {
super({
log: ['query', 'info', 'warn', 'error'],
log: [
{
emit: 'event',
level: 'query',
},
{
emit: 'stdout',
level: 'error',
},
{
emit: 'event',
level: 'info',
},
{
emit: 'stdout',
level: 'warn',
},
],
});
}

View File

@@ -26,11 +26,11 @@ export class ResumeSchema extends PothosSchema {
centerId: t.exposeID('centerId'),
status: t.exposeString('status'),
createdAt: t.expose('createdAt', {
type: 'Date',
type: 'DateTime',
nullable: true,
}),
updatedAt: t.expose('updatedAt', {
type: 'Date',
type: 'DateTime',
nullable: true,
}),
center: t.relation('center'),
@@ -38,13 +38,6 @@ export class ResumeSchema extends PothosSchema {
});
}
@PothosRef()
resumeStatus() {
return this.builder.enumType('ResumeStatus', {
values: ['PENDING', 'APPROVED', 'REJECTED'],
});
}
@Pothos()
init() {
this.builder.queryField('resumes', (t) =>

View File

@@ -1,9 +1,10 @@
import { Global, Module } from '@nestjs/common';
import { ServiceSchema } from './service.schema';
import { PrismaCrudGenerator } from 'src/graphql/graphql.generator';
@Global()
@Module({
providers: [ServiceSchema],
providers: [ServiceSchema, PrismaCrudGenerator],
exports: [ServiceSchema],
})
export class ServiceModule {}

View File

@@ -5,8 +5,9 @@ import {
PothosSchema,
SchemaBuilderToken,
} from '@smatch-corp/nestjs-pothos';
import { Builder } from '../graphql/graphql.builder';
import { Builder, BuilderTypes } from '../graphql/graphql.builder';
import { PrismaService } from 'src/prisma/prisma.service';
import { PrismaCrudGenerator } from 'src/graphql/graphql.generator';
@Injectable()
export class ServiceSchema extends PothosSchema {
@@ -27,11 +28,11 @@ export class ServiceSchema extends PothosSchema {
price: t.exposeFloat('price'),
rating: t.exposeFloat('rating'),
createdAt: t.expose('createdAt', {
type: 'Date',
type: 'DateTime',
nullable: true,
}),
updatedAt: t.expose('updatedAt', {
type: 'Date',
type: 'DateTime',
nullable: true,
}),
}),
@@ -79,4 +80,26 @@ export class ServiceSchema extends PothosSchema {
}),
}));
}
// @Pothos()
// initMutation() {
// this.builder.mutationFields((t) => ({
// createService: t.prismaField({
// type: this.service(),
// args: {
// input: t.arg({
// type: this.generator.getCreateInput('Service'),
// required: true,
// }),
// },
// resolve: async (query, root, args, ctx, info) => {
// const { input } = args;
// const service = await this.prisma.service.create({
// data: input,
// });
// return service;
// },
// }),
// }));
// }
}

View File

@@ -1 +0,0 @@
import { DateTimeResolver } from 'graphql-scalars';

View File

@@ -1,9 +1,30 @@
import { Global, Module } from '@nestjs/common';
import { UserSchema } from './user.schema';
import { Builder, BuilderTypes } from '../graphql/graphql.builder';
import { PrismaCrudGenerator } from 'src/graphql/graphql.generator';
import { PrismaService } from 'src/prisma/prisma.service';
@Global()
@Module({
providers: [UserSchema],
providers: [
PrismaService,
UserSchema,
{
provide: Builder,
useFactory: (prisma: PrismaService) => new Builder(prisma),
inject: [PrismaService],
},
{
provide: PrismaCrudGenerator,
inject: [
{
token: Builder,
optional: false,
},
],
useFactory: (builder: Builder) =>
new PrismaCrudGenerator<BuilderTypes>(builder),
},
],
exports: [UserSchema],
})
export class UserModule {}

View File

@@ -27,9 +27,9 @@ export class UserSchema extends PothosSchema {
email: t.exposeString('email'),
phoneNumber: t.exposeString('phoneNumber'),
oauthToken: t.exposeString('oauthToken', { nullable: true }),
role: t.exposeString('role'),
createdAt: t.expose('createdAt', { type: 'Date' }),
updatedAt: t.expose('updatedAt', { type: 'Date' }),
// role: t.exposeString('role'),
createdAt: t.expose('createdAt', { type: 'DateTime' }),
updatedAt: t.expose('updatedAt', { type: 'DateTime' }),
center: t.relation('center'),
}),
});
@@ -41,25 +41,12 @@ export class UserSchema extends PothosSchema {
this.builder.queryFields((t) => ({
users: t.prismaField({
type: [this.user()],
args: {
skip: t.arg.int(),
take: t.arg.int(),
cursor: t.arg.string(),
where: t.arg.string(),
orderBy: t.arg.string(),
},
args: this.builder.generator.findManyArgs('User'),
resolve: async (query, root, args, ctx, info) => {
const { skip, take, cursor, where, orderBy } = args;
const users = await this.prisma.user.findMany({
skip: skip || 0,
take: take || 10,
cursor: cursor ? { id: cursor } : undefined,
where: where ? JSON.parse(where) : undefined,
orderBy: orderBy ? JSON.parse(orderBy) : undefined,
return await this.prisma.user.findMany({
...query,
take: args.take ?? 10,
});
return users;
},
}),

View File

@@ -27,13 +27,13 @@ export class WorkshopSchema extends PothosSchema {
staffId: t.exposeID('staffId'),
serviceId: t.exposeID('serviceId'),
date: t.expose('date', {
type: 'Date',
type: 'DateTime',
}),
createdAt: t.expose('createdAt', {
type: 'Date',
type: 'DateTime',
}),
updatedAt: t.expose('updatedAt', {
type: 'Date',
type: 'DateTime',
}),
service: t.relation('service'),
workshopOrganization: t.relation('workshopOrganization'),