refactor codebase and pothos structure

This commit is contained in:
2024-10-07 10:07:08 +07:00
parent 29ec9c5360
commit dfa6b35399
31 changed files with 997 additions and 664 deletions

54
eslint.config.mjs Normal file
View File

@@ -0,0 +1,54 @@
import typescriptEslintEslintPlugin from '@typescript-eslint/eslint-plugin';
import globals from 'globals';
import tsParser from '@typescript-eslint/parser';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import js from '@eslint/js';
import { FlatCompat } from '@eslint/eslintrc';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
allConfig: js.configs.all,
});
export default [
{
ignores: ['**/.eslintrc.js'],
},
...compat.extends(
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
),
{
plugins: {
'@typescript-eslint': typescriptEslintEslintPlugin,
},
languageOptions: {
globals: {
...globals.node,
...globals.jest,
...globals.browser,
},
parser: tsParser,
ecmaVersion: 5,
sourceType: 'module',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: 'C:\\Users\\AliensVN\\EPESS\\epess-web-backend',
},
},
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
},
];

69
package-lock.json generated
View File

@@ -30,6 +30,8 @@
"@pothos/plugin-prisma-utils": "^1.2.0",
"@pothos/plugin-scope-auth": "^4.1.0",
"@prisma/client": "^5.20.0",
"@smatch-corp/nestjs-pothos": "^0.3.0",
"@smatch-corp/nestjs-pothos-apollo-driver": "^0.1.0",
"apollo-server-express": "^3.13.0",
"bcryptjs": "^2.4.3",
"class-transformer": "^0.5.1",
@@ -46,6 +48,8 @@
},
"devDependencies": {
"@clerk/types": "^4.23.0",
"@eslint/eslintrc": "^3.1.0",
"@eslint/js": "^9.12.0",
"@graphql-codegen/cli": "5.0.2",
"@graphql-codegen/typescript": "4.0.9",
"@graphql-codegen/typescript-resolvers": "4.2.1",
@@ -64,6 +68,7 @@
"eslint": "9.11.1",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"globals": "^15.10.0",
"jest": "^29.5.0",
"prettier": "^3.0.0",
"prisma": "^5.20.0",
@@ -2105,6 +2110,19 @@
"concat-map": "0.0.1"
}
},
"node_modules/@eslint/eslintrc/node_modules/globals": {
"version": "14.0.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
@@ -2126,9 +2144,9 @@
}
},
"node_modules/@eslint/js": {
"version": "9.11.1",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.11.1.tgz",
"integrity": "sha512-/qu+TWz8WwPWc7/HcIJKi+c+MOm46GdVaSlTTQcaqaL53+GsoA6MxWp5PtTx48qbSP7ylM1Kn7nhvkugfJvRSA==",
"version": "9.12.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.12.0.tgz",
"integrity": "sha512-eohesHH8WFRUprDNyEREgqP6beG6htMeUYeCpkEgBCieCMme5r9zFWjzAJp//9S+Kub4rqE+jXe9Cp1a7IYIIA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -5747,6 +5765,35 @@
"@sinonjs/commons": "^3.0.0"
}
},
"node_modules/@smatch-corp/nestjs-pothos": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@smatch-corp/nestjs-pothos/-/nestjs-pothos-0.3.0.tgz",
"integrity": "sha512-MRWLe+jCKu3q2Nr5gc2o4vU4R2LDd0Fwk5Il6HOUgMAJ7yxUjvRDf7C0tCYLme2wESjEyCPTjMyoQr7yj2Ju1g==",
"license": "MIT",
"peerDependencies": {
"@nestjs/common": "^7 || ^8 || ^9",
"@nestjs/core": "^7 || ^8 || ^9",
"@nestjs/graphql": "*",
"@pothos/core": "^3",
"rxjs": "*"
}
},
"node_modules/@smatch-corp/nestjs-pothos-apollo-driver": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@smatch-corp/nestjs-pothos-apollo-driver/-/nestjs-pothos-apollo-driver-0.1.0.tgz",
"integrity": "sha512-Xcqz6v4pOBMvJR5jm7L05EMHiOn+ZDmvKIRVfNmvK8Ttoo/fmzRIrCO3WHVJn0VdCk5nFvqgins2omHifzUwTg==",
"license": "MIT",
"dependencies": {
"@smatch-corp/nestjs-pothos": "^0.3.0"
},
"peerDependencies": {
"@nestjs/apollo": "^10",
"@nestjs/common": "^7 || ^8 || ^9",
"@nestjs/core": "^7 || ^8 || ^9",
"@nestjs/graphql": "^10",
"rxjs": "*"
}
},
"node_modules/@tsconfig/node10": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
@@ -8968,6 +9015,16 @@
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint/node_modules/@eslint/js": {
"version": "9.11.1",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.11.1.tgz",
"integrity": "sha512-/qu+TWz8WwPWc7/HcIJKi+c+MOm46GdVaSlTTQcaqaL53+GsoA6MxWp5PtTx48qbSP7ylM1Kn7nhvkugfJvRSA==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/eslint/node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -9844,9 +9901,9 @@
"license": "BSD-2-Clause"
},
"node_modules/globals": {
"version": "14.0.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
"version": "15.10.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-15.10.0.tgz",
"integrity": "sha512-tqFIbz83w4Y5TCbtgjZjApohbuh7K9BxGYFm7ifwDR240tvdb7P9x+/9VvUKlmkPoiknoJtanI8UOrqxS3a7lQ==",
"dev": true,
"license": "MIT",
"engines": {

View File

@@ -44,6 +44,8 @@
"@pothos/plugin-prisma-utils": "^1.2.0",
"@pothos/plugin-scope-auth": "^4.1.0",
"@prisma/client": "^5.20.0",
"@smatch-corp/nestjs-pothos": "^0.3.0",
"@smatch-corp/nestjs-pothos-apollo-driver": "^0.1.0",
"apollo-server-express": "^3.13.0",
"bcryptjs": "^2.4.3",
"class-transformer": "^0.5.1",
@@ -60,6 +62,8 @@
},
"devDependencies": {
"@clerk/types": "^4.23.0",
"@eslint/eslintrc": "^3.1.0",
"@eslint/js": "^9.12.0",
"@graphql-codegen/cli": "5.0.2",
"@graphql-codegen/typescript": "4.0.9",
"@graphql-codegen/typescript-resolvers": "4.2.1",
@@ -78,6 +82,7 @@
"eslint": "9.11.1",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"globals": "^15.10.0",
"jest": "^29.5.0",
"prettier": "^3.0.0",
"prisma": "^5.20.0",

View File

@@ -5,9 +5,9 @@ import { RestfulModule } from './restful/restful.module';
@Module({
imports: [
GraphqlModule, // GraphQL setup
ClerkModule, // Clerk setup
RestfulModule, // RESTful API module
GraphqlModule, // GraphQL setup
ClerkModule, // Clerk setup
RestfulModule,
],
})
export class AppModule {}

View File

@@ -1,21 +0,0 @@
export type ScheduleDateJSON = {};
export type ScheduleDate = Date | ScheduleDateJSON;
/*
[id:startTimeStamp - endTimeStamp, id:startTimeStamp - endTimeStamp, ...]
*/
1728206200 - 1728206300
1728206400 - 1728206500
1728206600 - 1728206700
1728206800 - 1728206900
1728207000 - 1728207100
1728207200 - 1728207300
1728207400 - 1728207500
1728207600 - 1728207700

View File

@@ -2,7 +2,7 @@ function getOverlapRange(
startA: number,
endA: number,
startB: number,
endB: number
endB: number,
) {
const overlapStart = Math.max(startA, startB);
const overlapEnd = Math.min(endA, endB);
@@ -10,12 +10,7 @@ function getOverlapRange(
return overlapStart < overlapEnd ? { overlapStart, overlapEnd } : null;
}
function isOverlap(
startA: number,
endA: number,
startB: number,
endB: number
) {
function isOverlap(startA: number, endA: number, startB: number, endB: number) {
return getOverlapRange(startA, endA, startB, endB) !== null;
}
@@ -57,13 +52,13 @@ const endB = new Date('2024-10-06T13:00:00Z').getTime();
const overlapRange = getOverlapRange(startA, endA, startB, endB);
if (overlapRange) {
console.log(
`Overlap Start: ${new Date(overlapRange.overlapStart).toISOString()}`
`Overlap Start: ${new Date(overlapRange.overlapStart).toISOString()}`,
);
console.log(
`Overlap End: ${new Date(overlapRange.overlapEnd).toISOString()}`
`Overlap End: ${new Date(overlapRange.overlapEnd).toISOString()}`,
);
console.log('Is overlap: true');
} else {
console.log('No overlap');
}
}

View File

@@ -1,19 +1,36 @@
import { GraphQLResolveInfo, GraphQLScalarType, GraphQLScalarTypeConfig } from 'graphql';
import {
GraphQLResolveInfo,
GraphQLScalarType,
GraphQLScalarTypeConfig,
} from 'graphql';
export type Maybe<T> = T | null;
export type InputMaybe<T> = Maybe<T>;
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
export type MakeEmpty<T extends { [key: string]: unknown }, K extends keyof T> = { [_ in K]?: never };
export type Incremental<T> = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never };
export type Exact<T extends { [key: string]: unknown }> = {
[K in keyof T]: T[K];
};
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & {
[SubKey in K]?: Maybe<T[SubKey]>;
};
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & {
[SubKey in K]: Maybe<T[SubKey]>;
};
export type MakeEmpty<
T extends { [key: string]: unknown },
K extends keyof T,
> = { [_ in K]?: never };
export type Incremental<T> =
| T
| {
[P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never;
};
/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
ID: { input: string; output: string; }
String: { input: string; output: string; }
Boolean: { input: boolean; output: boolean; }
Int: { input: number; output: number; }
Float: { input: number; output: number; }
DateTime: { input: any; output: any; }
ID: { input: string; output: string };
String: { input: string; output: string };
Boolean: { input: boolean; output: boolean };
Int: { input: number; output: number };
Float: { input: number; output: number };
DateTime: { input: any; output: any };
};
export type Center = {
@@ -103,40 +120,55 @@ export type SendingMessage = {
userId?: Maybe<Scalars['ID']['output']>;
};
export type ResolverTypeWrapper<T> = Promise<T> | T;
export type ResolverWithResolve<TResult, TParent, TContext, TArgs> = {
resolve: ResolverFn<TResult, TParent, TContext, TArgs>;
};
export type Resolver<TResult, TParent = {}, TContext = {}, TArgs = {}> = ResolverFn<TResult, TParent, TContext, TArgs> | ResolverWithResolve<TResult, TParent, TContext, TArgs>;
export type Resolver<TResult, TParent = {}, TContext = {}, TArgs = {}> =
| ResolverFn<TResult, TParent, TContext, TArgs>
| ResolverWithResolve<TResult, TParent, TContext, TArgs>;
export type ResolverFn<TResult, TParent, TContext, TArgs> = (
parent: TParent,
args: TArgs,
context: TContext,
info: GraphQLResolveInfo
info: GraphQLResolveInfo,
) => Promise<TResult> | TResult;
export type SubscriptionSubscribeFn<TResult, TParent, TContext, TArgs> = (
parent: TParent,
args: TArgs,
context: TContext,
info: GraphQLResolveInfo
info: GraphQLResolveInfo,
) => AsyncIterable<TResult> | Promise<AsyncIterable<TResult>>;
export type SubscriptionResolveFn<TResult, TParent, TContext, TArgs> = (
parent: TParent,
args: TArgs,
context: TContext,
info: GraphQLResolveInfo
info: GraphQLResolveInfo,
) => TResult | Promise<TResult>;
export interface SubscriptionSubscriberObject<TResult, TKey extends string, TParent, TContext, TArgs> {
subscribe: SubscriptionSubscribeFn<{ [key in TKey]: TResult }, TParent, TContext, TArgs>;
resolve?: SubscriptionResolveFn<TResult, { [key in TKey]: TResult }, TContext, TArgs>;
export interface SubscriptionSubscriberObject<
TResult,
TKey extends string,
TParent,
TContext,
TArgs,
> {
subscribe: SubscriptionSubscribeFn<
{ [key in TKey]: TResult },
TParent,
TContext,
TArgs
>;
resolve?: SubscriptionResolveFn<
TResult,
{ [key in TKey]: TResult },
TContext,
TArgs
>;
}
export interface SubscriptionResolverObject<TResult, TParent, TContext, TArgs> {
@@ -144,34 +176,55 @@ export interface SubscriptionResolverObject<TResult, TParent, TContext, TArgs> {
resolve: SubscriptionResolveFn<TResult, any, TContext, TArgs>;
}
export type SubscriptionObject<TResult, TKey extends string, TParent, TContext, TArgs> =
export type SubscriptionObject<
TResult,
TKey extends string,
TParent,
TContext,
TArgs,
> =
| SubscriptionSubscriberObject<TResult, TKey, TParent, TContext, TArgs>
| SubscriptionResolverObject<TResult, TParent, TContext, TArgs>;
export type SubscriptionResolver<TResult, TKey extends string, TParent = {}, TContext = {}, TArgs = {}> =
| ((...args: any[]) => SubscriptionObject<TResult, TKey, TParent, TContext, TArgs>)
export type SubscriptionResolver<
TResult,
TKey extends string,
TParent = {},
TContext = {},
TArgs = {},
> =
| ((
...args: any[]
) => SubscriptionObject<TResult, TKey, TParent, TContext, TArgs>)
| SubscriptionObject<TResult, TKey, TParent, TContext, TArgs>;
export type TypeResolveFn<TTypes, TParent = {}, TContext = {}> = (
parent: TParent,
context: TContext,
info: GraphQLResolveInfo
info: GraphQLResolveInfo,
) => Maybe<TTypes> | Promise<Maybe<TTypes>>;
export type IsTypeOfResolverFn<T = {}, TContext = {}> = (obj: T, context: TContext, info: GraphQLResolveInfo) => boolean | Promise<boolean>;
export type IsTypeOfResolverFn<T = {}, TContext = {}> = (
obj: T,
context: TContext,
info: GraphQLResolveInfo,
) => boolean | Promise<boolean>;
export type NextResolverFn<T> = () => Promise<T>;
export type DirectiveResolverFn<TResult = {}, TParent = {}, TContext = {}, TArgs = {}> = (
export type DirectiveResolverFn<
TResult = {},
TParent = {},
TContext = {},
TArgs = {},
> = (
next: NextResolverFn<TResult>,
parent: TParent,
args: TArgs,
context: TContext,
info: GraphQLResolveInfo
info: GraphQLResolveInfo,
) => TResult | Promise<TResult>;
/** Mapping between all available schema types and the resolvers types */
export type ResolversTypes = {
Boolean: ResolverTypeWrapper<Scalars['Boolean']['output']>;
@@ -216,91 +269,196 @@ export type ResolversParentTypes = {
sendingMessage: SendingMessage;
};
export type CenterResolvers<ContextType = any, ParentType extends ResolversParentTypes['Center'] = ResolversParentTypes['Center']> = {
export type CenterResolvers<
ContextType = any,
ParentType extends
ResolversParentTypes['Center'] = ResolversParentTypes['Center'],
> = {
id?: Resolver<Maybe<ResolversTypes['ID']>, ParentType, ContextType>;
userId?: Resolver<Maybe<ResolversTypes['ID']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};
export type CenterStaffResolvers<ContextType = any, ParentType extends ResolversParentTypes['CenterStaff'] = ResolversParentTypes['CenterStaff']> = {
export type CenterStaffResolvers<
ContextType = any,
ParentType extends
ResolversParentTypes['CenterStaff'] = ResolversParentTypes['CenterStaff'],
> = {
centerId?: Resolver<Maybe<ResolversTypes['ID']>, ParentType, ContextType>;
serviceId?: Resolver<Maybe<ResolversTypes['ID']>, ParentType, ContextType>;
staffId?: Resolver<Maybe<ResolversTypes['ID']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};
export type ChatRoomResolvers<ContextType = any, ParentType extends ResolversParentTypes['ChatRoom'] = ResolversParentTypes['ChatRoom']> = {
export type ChatRoomResolvers<
ContextType = any,
ParentType extends
ResolversParentTypes['ChatRoom'] = ResolversParentTypes['ChatRoom'],
> = {
id?: Resolver<Maybe<ResolversTypes['ID']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};
export interface DateTimeScalarConfig extends GraphQLScalarTypeConfig<ResolversTypes['DateTime'], any> {
export interface DateTimeScalarConfig
extends GraphQLScalarTypeConfig<ResolversTypes['DateTime'], any> {
name: 'DateTime';
}
export type OrderResolvers<ContextType = any, ParentType extends ResolversParentTypes['Order'] = ResolversParentTypes['Order']> = {
export type OrderResolvers<
ContextType = any,
ParentType extends
ResolversParentTypes['Order'] = ResolversParentTypes['Order'],
> = {
id?: Resolver<Maybe<ResolversTypes['ID']>, ParentType, ContextType>;
userId?: Resolver<Maybe<ResolversTypes['ID']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};
export type QueryResolvers<ContextType = any, ParentType extends ResolversParentTypes['Query'] = ResolversParentTypes['Query']> = {
users?: Resolver<Maybe<Array<ResolversTypes['User']>>, ParentType, ContextType>;
export type QueryResolvers<
ContextType = any,
ParentType extends
ResolversParentTypes['Query'] = ResolversParentTypes['Query'],
> = {
users?: Resolver<
Maybe<Array<ResolversTypes['User']>>,
ParentType,
ContextType
>;
};
export type ServiceResolvers<ContextType = any, ParentType extends ResolversParentTypes['Service'] = ResolversParentTypes['Service']> = {
export type ServiceResolvers<
ContextType = any,
ParentType extends
ResolversParentTypes['Service'] = ResolversParentTypes['Service'],
> = {
id?: Resolver<Maybe<ResolversTypes['ID']>, ParentType, ContextType>;
userId?: Resolver<Maybe<ResolversTypes['ID']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};
export type ServiceFeedbackResolvers<ContextType = any, ParentType extends ResolversParentTypes['ServiceFeedback'] = ResolversParentTypes['ServiceFeedback']> = {
export type ServiceFeedbackResolvers<
ContextType = any,
ParentType extends
ResolversParentTypes['ServiceFeedback'] = ResolversParentTypes['ServiceFeedback'],
> = {
id?: Resolver<Maybe<ResolversTypes['ID']>, ParentType, ContextType>;
userId?: Resolver<Maybe<ResolversTypes['ID']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};
export type UserResolvers<ContextType = any, ParentType extends ResolversParentTypes['User'] = ResolversParentTypes['User']> = {
CenterStaff?: Resolver<Maybe<ResolversTypes['CenterStaff']>, ParentType, ContextType>;
Service?: Resolver<Maybe<Array<ResolversTypes['Service']>>, ParentType, ContextType>;
WorkshopSubscription?: Resolver<Maybe<Array<ResolversTypes['WorkshopSubscription']>>, ParentType, ContextType>;
export type UserResolvers<
ContextType = any,
ParentType extends
ResolversParentTypes['User'] = ResolversParentTypes['User'],
> = {
CenterStaff?: Resolver<
Maybe<ResolversTypes['CenterStaff']>,
ParentType,
ContextType
>;
Service?: Resolver<
Maybe<Array<ResolversTypes['Service']>>,
ParentType,
ContextType
>;
WorkshopSubscription?: Resolver<
Maybe<Array<ResolversTypes['WorkshopSubscription']>>,
ParentType,
ContextType
>;
center?: Resolver<Maybe<ResolversTypes['Center']>, ParentType, ContextType>;
centerStaffChatRoom?: Resolver<Maybe<Array<ResolversTypes['ChatRoom']>>, ParentType, ContextType>;
createdAt?: Resolver<Maybe<ResolversTypes['DateTime']>, ParentType, ContextType>;
customerChatRoom?: Resolver<Maybe<Array<ResolversTypes['ChatRoom']>>, ParentType, ContextType>;
documents?: Resolver<Maybe<Array<ResolversTypes['documents']>>, ParentType, ContextType>;
centerStaffChatRoom?: Resolver<
Maybe<Array<ResolversTypes['ChatRoom']>>,
ParentType,
ContextType
>;
createdAt?: Resolver<
Maybe<ResolversTypes['DateTime']>,
ParentType,
ContextType
>;
customerChatRoom?: Resolver<
Maybe<Array<ResolversTypes['ChatRoom']>>,
ParentType,
ContextType
>;
documents?: Resolver<
Maybe<Array<ResolversTypes['documents']>>,
ParentType,
ContextType
>;
email?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
id?: Resolver<Maybe<ResolversTypes['ID']>, ParentType, ContextType>;
name?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
order?: Resolver<Maybe<Array<ResolversTypes['Order']>>, ParentType, ContextType>;
phoneNumber?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
order?: Resolver<
Maybe<Array<ResolversTypes['Order']>>,
ParentType,
ContextType
>;
phoneNumber?: Resolver<
Maybe<ResolversTypes['String']>,
ParentType,
ContextType
>;
role?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
sendingMessage?: Resolver<Maybe<Array<ResolversTypes['sendingMessage']>>, ParentType, ContextType>;
serviceFeedbacks?: Resolver<Maybe<Array<ResolversTypes['ServiceFeedback']>>, ParentType, ContextType>;
updatedAt?: Resolver<Maybe<ResolversTypes['DateTime']>, ParentType, ContextType>;
sendingMessage?: Resolver<
Maybe<Array<ResolversTypes['sendingMessage']>>,
ParentType,
ContextType
>;
serviceFeedbacks?: Resolver<
Maybe<Array<ResolversTypes['ServiceFeedback']>>,
ParentType,
ContextType
>;
updatedAt?: Resolver<
Maybe<ResolversTypes['DateTime']>,
ParentType,
ContextType
>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};
export type WorkshopResolvers<ContextType = any, ParentType extends ResolversParentTypes['Workshop'] = ResolversParentTypes['Workshop']> = {
export type WorkshopResolvers<
ContextType = any,
ParentType extends
ResolversParentTypes['Workshop'] = ResolversParentTypes['Workshop'],
> = {
id?: Resolver<Maybe<ResolversTypes['ID']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};
export type WorkshopSubscriptionResolvers<ContextType = any, ParentType extends ResolversParentTypes['WorkshopSubscription'] = ResolversParentTypes['WorkshopSubscription']> = {
export type WorkshopSubscriptionResolvers<
ContextType = any,
ParentType extends
ResolversParentTypes['WorkshopSubscription'] = ResolversParentTypes['WorkshopSubscription'],
> = {
user?: Resolver<Maybe<ResolversTypes['User']>, ParentType, ContextType>;
userId?: Resolver<Maybe<ResolversTypes['ID']>, ParentType, ContextType>;
workshop?: Resolver<Maybe<ResolversTypes['Workshop']>, ParentType, ContextType>;
workshop?: Resolver<
Maybe<ResolversTypes['Workshop']>,
ParentType,
ContextType
>;
workshopId?: Resolver<Maybe<ResolversTypes['ID']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};
export type DocumentsResolvers<ContextType = any, ParentType extends ResolversParentTypes['documents'] = ResolversParentTypes['documents']> = {
export type DocumentsResolvers<
ContextType = any,
ParentType extends
ResolversParentTypes['documents'] = ResolversParentTypes['documents'],
> = {
id?: Resolver<Maybe<ResolversTypes['ID']>, ParentType, ContextType>;
userId?: Resolver<Maybe<ResolversTypes['ID']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};
export type SendingMessageResolvers<ContextType = any, ParentType extends ResolversParentTypes['sendingMessage'] = ResolversParentTypes['sendingMessage']> = {
export type SendingMessageResolvers<
ContextType = any,
ParentType extends
ResolversParentTypes['sendingMessage'] = ResolversParentTypes['sendingMessage'],
> = {
id?: Resolver<Maybe<ResolversTypes['ID']>, ParentType, ContextType>;
userId?: Resolver<Maybe<ResolversTypes['ID']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
@@ -321,4 +479,3 @@ export type Resolvers<ContextType = any> = {
documents?: DocumentsResolvers<ContextType>;
sendingMessage?: SendingMessageResolvers<ContextType>;
};

View File

@@ -1,36 +1,45 @@
import SchemaBuilder from '@pothos/core';
import PrismaPlugin from '@pothos/plugin-prisma';
import PrismaPlugin, { PothosPrismaDatamodel } from '@pothos/plugin-prisma';
import { PrismaClient } from '@prisma/client';
import PrismaUtils from '@pothos/plugin-prisma-utils';
import { DateTimeResolver } from 'graphql-scalars';
import { GraphQLInt } from 'graphql';
import { PrismaService } from '../prisma/prisma.service';
import { Request } from 'express';
import type PrismaTypes from '../types/pothos.generated';
import { getDatamodel } from '../types/pothos.generated';
import { DateTimeResolver } from 'graphql-scalars';
export const prisma = new PrismaService({
log: ['query', 'info', 'warn', 'error'],
errorFormat: 'pretty',
});
export const builder = new SchemaBuilder<{
export interface SchemaContext {
req: Request;
}
interface SchemaBuilderOption {
Context: SchemaContext;
PrismaTypes: PrismaTypes;
DataModel: PothosPrismaDatamodel;
Scalars: {
DateTime: {
Date: {
Input: Date;
Output: Date;
};
};
PrismaTypes: PrismaTypes;
}>({
plugins: [PrismaPlugin, PrismaUtils],
prisma: {
client: prisma,
exposeDescriptions: true,
filterConnectionTotalCount: true,
onUnusedQuery: process.env.NODE_ENV === 'production' ? null : 'warn',
dmmf: getDatamodel(),
},
});
}
export function createBuilder(client: PrismaClient) {
const builder = new SchemaBuilder<SchemaBuilderOption>({
plugins: [PrismaPlugin, PrismaUtils],
prisma: {
client,
exposeDescriptions: true,
filterConnectionTotalCount: true,
onUnusedQuery: process.env.NODE_ENV === 'production' ? null : 'warn',
dmmf: getDatamodel(),
},
});
export const SortOrder = builder.enumType('SortOrder', {
values: ['asc', 'desc'],
});
builder.addScalarType('DateTime', DateTimeResolver, {});
builder.queryType({});
// builder.mutationType({});
// builder.subscriptionType({});
builder.addScalarType('Date', DateTimeResolver, {});
return builder;
}
export type Builder = ReturnType<typeof createBuilder>;

View File

@@ -1,20 +1,29 @@
import { ApolloDriverConfig } from '@nestjs/apollo';
import { MiddlewareConsumer, Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { PothosModule } from '@smatch-corp/nestjs-pothos';
import { PothosApolloDriver } from '@smatch-corp/nestjs-pothos-apollo-driver';
import { createBuilder } from './graphql.builder';
import { PrismaService } from '../prisma/prisma.service';
import { schema } from './schema';
import { GraphQLValidationMiddleware } from 'src/middlewares/graphql.middleware';
import { PrismaModule } from 'src/prisma/prisma.module';
import { UserModule } from 'src/user/user.module';
import { GraphqlService } from './graphql.service';
@Module({
imports: [
PrismaModule,
UserModule,
PothosModule.forRoot({
builder: {
inject: [PrismaService],
useFactory: (prisma: PrismaService) => createBuilder(prisma),
},
}),
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
schema: schema,
debug: true,
allowBatchedHttpRequests: true,
introspection: true,
driver: PothosApolloDriver,
}),
],
providers: [PrismaService],
providers: [GraphqlService],
})
export class GraphqlModule {
configure(consumer: MiddlewareConsumer) {

View File

@@ -1,448 +1,448 @@
import { builder, prisma, SortOrder } from './graphql.builder';
import { Prisma } from '@prisma/client';
// import { builder, prisma, SortOrder } from './graphql.builder';
// import { Prisma } from '@prisma/client';
builder.prismaObject('User', {
fields: (t) => ({
id: t.exposeID('id'),
name: t.exposeString('name'),
email: t.exposeString('email'),
phoneNumber: t.exposeString('phoneNumber'),
role: t.exposeString('role'),
createdAt: t.expose('createdAt', {
type: 'DateTime',
nullable: true,
}),
updatedAt: t.expose('updatedAt', {
type: 'DateTime',
nullable: true,
}),
order: t.relation('orders'),
serviceFeedbacks: t.relation('serviceFeedbacks'),
documents: t.relation('documents'),
sendingMessage: t.relation('sendingMessage'),
Service: t.relation('Service'),
center: t.relation('center'),
customerChatRoom: t.relation('customerChatRoom'),
centerStaffChatRoom: t.relation('centerStaffChatRoom'),
CenterStaff: t.relation('CenterStaff'),
WorkshopSubscription: t.relation('WorkshopSubscription'),
}),
});
builder.prismaObject('Order', {
fields: (t) => ({
id: t.exposeID('id'),
paymentId: t.exposeString('paymentId'),
userId: t.exposeID('userId'),
serviceId: t.exposeID('serviceId'),
status: t.exposeString('status'),
total: t.expose('total', {
type: 'Int',
}),
createdAt: t.expose('createdAt', {
type: 'DateTime',
nullable: true,
}),
updatedAt: t.expose('updatedAt', {
type: 'DateTime',
nullable: true,
}),
user: t.relation('user'),
payment: t.relation('payment'),
service: t.relation('service'),
refundTicket: t.relation('refundTicket'),
}),
});
builder.prismaObject('ServiceFeedback', {
fields: (t) => ({
id: t.exposeID('id'),
userId: t.exposeID('userId'),
}),
});
builder.prismaObject('UploadedDocument', {
name: 'documents',
fields: (t) => ({
id: t.exposeID('id'),
userId: t.exposeID('userId'),
}),
});
builder.prismaObject('Message', {
name: 'sendingMessage',
fields: (t) => ({
id: t.exposeID('id'),
userId: t.exposeID('senderId'),
}),
});
builder.prismaObject('Payment', {
fields: (t) => ({
id: t.exposeID('id'),
amount: t.expose('amount', {
type: 'Float',
}),
status: t.exposeString('status'),
createdAt: t.expose('createdAt', {
type: 'DateTime',
nullable: true,
}),
updatedAt: t.expose('updatedAt', {
type: 'DateTime',
nullable: true,
}),
order: t.relation('Order'),
}),
});
builder.prismaObject('RefundTicket', {
fields: (t) => ({
id: t.exposeID('id'),
orderId: t.exposeID('orderId'),
amount: t.expose('amount', {
type: 'Float',
}),
status: t.exposeString('status'),
createdAt: t.expose('createdAt', {
type: 'DateTime',
nullable: true,
}),
updatedAt: t.expose('updatedAt', {
type: 'DateTime',
nullable: true,
}),
order: t.relation('order'),
}),
});
builder.prismaObject('Service', {
fields: (t) => ({
id: t.exposeID('id'),
name: t.exposeString('name'),
description: t.exposeString('description'),
centerId: t.exposeID('centerId'),
price: t.expose('price', {
type: 'Float',
}),
rating: t.expose('rating', {
type: 'Float',
}),
createdAt: t.expose('createdAt', {
type: 'DateTime',
nullable: true,
}),
updatedAt: t.expose('updatedAt', {
type: 'DateTime',
nullable: true,
}),
managedBy: t.relation('managedBy'),
feedbacks: t.relation('feedbacks'),
order: t.relation('order'),
workshop: t.relation('workshop'),
milestone: t.relation('milestone'),
schedule: t.relation('schedule'),
serviceAndCategory: t.relation('serviceAndCategory'),
workshopOrganization: t.relation('workshopOrganization'),
user: t.relation('user'),
userId: t.exposeID('userId'),
}),
});
builder.prismaObject('Milestone', {
fields: (t) => ({
id: t.exposeID('id'),
serviceId: t.exposeID('serviceId'),
}),
});
builder.prismaObject('Schedule', {
fields: (t) => ({
id: t.exposeID('id'),
serviceId: t.exposeID('serviceId'),
}),
});
builder.prismaObject('ServiceAndCategory', {
fields: (t) => ({
serviceId: t.exposeID('serviceId'),
categoryId: t.exposeID('categoryId'),
service: t.relation('service'),
category: t.relation('category'),
}),
});
builder.prismaObject('WorkshopOrganization', {
fields: (t) => ({
serviceId: t.exposeID('serviceId'),
workshopId: t.exposeID('workshopId'),
service: t.relation('service'),
workshop: t.relation('workshop'),
}),
});
builder.prismaObject('Category', {
fields: (t) => ({
id: t.exposeID('id'),
}),
});
builder.prismaObject('Center', {
fields: (t) => ({
id: t.exposeID('id'),
userId: t.exposeID('centerOwnerId'),
}),
});
builder.prismaObject('ChatRoom', {
fields: (t) => ({
id: t.exposeID('id'),
}),
});
builder.prismaObject('CenterStaff', {
fields: (t) => ({
staffId: t.exposeID('staffId'),
staff: t.relation('staff'),
centerId: t.exposeID('centerId'),
serviceId: t.exposeID('serviceId'),
}),
});
builder.prismaObject('WorkshopSubscription', {
fields: (t) => ({
userId: t.exposeID('userId'),
workshopId: t.exposeID('workshopId'),
user: t.relation('user'),
workshop: t.relation('workshop'),
}),
});
builder.prismaObject('Workshop', {
fields: (t) => ({
id: t.exposeID('id'),
}),
});
// Query section
builder.queryType({
fields: (t) => ({
users: t.prismaField({
type: ['User'], // Return type is a list of 'User'
resolve: (query, root, args, ctx, info) => {
return prisma.user.findMany({
...query,
// Include related posts in the query
});
},
}),
user: t.prismaField({
type: 'User',
args: {
id: t.arg.string(),
},
resolve: (query, root, args, ctx, info) => {
return prisma.user.findUnique({
where: {
id: args.id?.toString(),
},
});
},
}),
orders: t.prismaField({
type: ['Order'],
resolve: (query, root, args, ctx, info) => {
return prisma.order.findMany({
...query,
});
},
}),
serviceFeedbacks: t.prismaField({
type: ['ServiceFeedback'],
resolve: (query, root, args, ctx, info) => {
return prisma.serviceFeedback.findMany({
...query,
});
},
}),
documents: t.prismaField({
type: ['UploadedDocument'],
resolve: (query, root, args, ctx, info) => {
return prisma.uploadedDocument.findMany({
...query,
});
},
}),
messages: t.prismaField({
type: ['Message'],
resolve: (query, root, args, ctx, info) => {
return prisma.message.findMany({
...query,
});
},
}),
services: t.prismaField({
type: ['Service'],
args: {
where: t.arg({
type: builder.inputType('ServiceWhereInput', {
fields: (t) => ({
// search by name contains
nameContain: t.string(),
// search by name starts with
nameStartsWith: t.string(),
// search by name ends with
nameEndsWith: t.string(),
}),
}),
}),
orderBy: t.arg({
type: builder.inputType('ServiceOrderByInput', {
fields: (t) => ({
rating: t.field({
type: SortOrder,
}),
price: t.field({
type: SortOrder,
}),
}),
}),
}),
cursor: t.arg({
type: builder.inputType('ServiceWhereUniqueInput', {
fields: (t) => ({
// Define fields to match your `ServiceWhereUniqueInput` structure.
name: t.string(),
}),
}),
}),
take: t.arg.int(),
skip: t.arg.int(),
},
resolve: async (query, root, args, ctx, info) => {
return prisma.service.findMany({
// handle where condition
where: {
name: {
contains: args.where?.nameContain as string | undefined,
startsWith: args.where?.nameStartsWith as string | undefined,
endsWith: args.where?.nameEndsWith as string | undefined,
},
},
// handle orderBy condition
orderBy: {
rating: args.orderBy?.rating as Prisma.SortOrder,
price: args.orderBy?.price as Prisma.SortOrder,
},
// handle pagination
cursor: args.cursor as Prisma.ServiceWhereUniqueInput | undefined,
take: args.take as number | undefined,
skip: args.skip as number | undefined,
});
},
}),
service: t.prismaField({
type: 'Service',
args: {
id: t.arg.string(),
},
resolve: (query, root, args, ctx, info) => {
return prisma.service.findUnique({
where: {
id: args.id?.toString(),
},
});
},
}),
centers: t.prismaField({
type: ['Center'],
resolve: (query, root, args, ctx, info) => {
return prisma.center.findMany({
...query,
});
},
}),
chatRooms: t.prismaField({
type: ['ChatRoom'],
resolve: (query, root, args, ctx, info) => {
return prisma.chatRoom.findMany({
...query,
});
},
}),
centerStaffs: t.prismaField({
type: ['CenterStaff'],
resolve: (query, root, args, ctx, info) => {
return prisma.centerStaff.findMany({
...query,
});
},
}),
workshopSubscriptions: t.prismaField({
type: ['WorkshopSubscription'],
resolve: (query, root, args, ctx, info) => {
return prisma.workshopSubscription.findMany({
...query,
});
},
}),
workshops: t.prismaField({
type: ['Workshop'],
resolve: (query, root, args, ctx, info) => {
return prisma.workshop.findMany({
...query,
});
},
}),
}),
});
// Mutation section
// builder.mutationType({
// builder.prismaObject('User', {
// fields: (t) => ({
// createUser: t.prismaField({
// id: t.exposeID('id'),
// name: t.exposeString('name'),
// email: t.exposeString('email'),
// phoneNumber: t.exposeString('phoneNumber'),
// role: t.exposeString('role'),
// createdAt: t.expose('createdAt', {
// type: 'DateTime',
// nullable: true,
// }),
// updatedAt: t.expose('updatedAt', {
// type: 'DateTime',
// nullable: true,
// }),
// order: t.relation('orders'),
// serviceFeedbacks: t.relation('serviceFeedbacks'),
// documents: t.relation('documents'),
// sendingMessage: t.relation('sendingMessage'),
// Service: t.relation('Service'),
// center: t.relation('center'),
// customerChatRoom: t.relation('customerChatRoom'),
// centerStaffChatRoom: t.relation('centerStaffChatRoom'),
// CenterStaff: t.relation('CenterStaff'),
// WorkshopSubscription: t.relation('WorkshopSubscription'),
// }),
// });
// builder.prismaObject('Order', {
// fields: (t) => ({
// id: t.exposeID('id'),
// paymentId: t.exposeString('paymentId'),
// userId: t.exposeID('userId'),
// serviceId: t.exposeID('serviceId'),
// status: t.exposeString('status'),
// total: t.expose('total', {
// type: 'Int',
// }),
// createdAt: t.expose('createdAt', {
// type: 'DateTime',
// nullable: true,
// }),
// updatedAt: t.expose('updatedAt', {
// type: 'DateTime',
// nullable: true,
// }),
// user: t.relation('user'),
// payment: t.relation('payment'),
// service: t.relation('service'),
// refundTicket: t.relation('refundTicket'),
// }),
// });
// builder.prismaObject('ServiceFeedback', {
// fields: (t) => ({
// id: t.exposeID('id'),
// userId: t.exposeID('userId'),
// }),
// });
// builder.prismaObject('UploadedDocument', {
// name: 'documents',
// fields: (t) => ({
// id: t.exposeID('id'),
// userId: t.exposeID('userId'),
// }),
// });
// builder.prismaObject('Message', {
// name: 'sendingMessage',
// fields: (t) => ({
// id: t.exposeID('id'),
// userId: t.exposeID('senderId'),
// }),
// });
// builder.prismaObject('Payment', {
// fields: (t) => ({
// id: t.exposeID('id'),
// amount: t.expose('amount', {
// type: 'Float',
// }),
// status: t.exposeString('status'),
// createdAt: t.expose('createdAt', {
// type: 'DateTime',
// nullable: true,
// }),
// updatedAt: t.expose('updatedAt', {
// type: 'DateTime',
// nullable: true,
// }),
// order: t.relation('Order'),
// }),
// });
// builder.prismaObject('RefundTicket', {
// fields: (t) => ({
// id: t.exposeID('id'),
// orderId: t.exposeID('orderId'),
// amount: t.expose('amount', {
// type: 'Float',
// }),
// status: t.exposeString('status'),
// createdAt: t.expose('createdAt', {
// type: 'DateTime',
// nullable: true,
// }),
// updatedAt: t.expose('updatedAt', {
// type: 'DateTime',
// nullable: true,
// }),
// order: t.relation('order'),
// }),
// });
// builder.prismaObject('Service', {
// fields: (t) => ({
// id: t.exposeID('id'),
// name: t.exposeString('name'),
// description: t.exposeString('description'),
// centerId: t.exposeID('centerId'),
// price: t.expose('price', {
// type: 'Float',
// }),
// rating: t.expose('rating', {
// type: 'Float',
// }),
// createdAt: t.expose('createdAt', {
// type: 'DateTime',
// nullable: true,
// }),
// updatedAt: t.expose('updatedAt', {
// type: 'DateTime',
// nullable: true,
// }),
// managedBy: t.relation('managedBy'),
// feedbacks: t.relation('feedbacks'),
// order: t.relation('order'),
// workshop: t.relation('workshop'),
// milestone: t.relation('milestone'),
// schedule: t.relation('schedule'),
// serviceAndCategory: t.relation('serviceAndCategory'),
// workshopOrganization: t.relation('workshopOrganization'),
// user: t.relation('user'),
// userId: t.exposeID('userId'),
// }),
// });
// builder.prismaObject('Milestone', {
// fields: (t) => ({
// id: t.exposeID('id'),
// serviceId: t.exposeID('serviceId'),
// }),
// });
// builder.prismaObject('Schedule', {
// fields: (t) => ({
// id: t.exposeID('id'),
// serviceId: t.exposeID('serviceId'),
// }),
// });
// builder.prismaObject('ServiceAndCategory', {
// fields: (t) => ({
// serviceId: t.exposeID('serviceId'),
// categoryId: t.exposeID('categoryId'),
// service: t.relation('service'),
// category: t.relation('category'),
// }),
// });
// builder.prismaObject('WorkshopOrganization', {
// fields: (t) => ({
// serviceId: t.exposeID('serviceId'),
// workshopId: t.exposeID('workshopId'),
// service: t.relation('service'),
// workshop: t.relation('workshop'),
// }),
// });
// builder.prismaObject('Category', {
// fields: (t) => ({
// id: t.exposeID('id'),
// }),
// });
// builder.prismaObject('Center', {
// fields: (t) => ({
// id: t.exposeID('id'),
// userId: t.exposeID('centerOwnerId'),
// }),
// });
// builder.prismaObject('ChatRoom', {
// fields: (t) => ({
// id: t.exposeID('id'),
// }),
// });
// builder.prismaObject('CenterStaff', {
// fields: (t) => ({
// staffId: t.exposeID('staffId'),
// staff: t.relation('staff'),
// centerId: t.exposeID('centerId'),
// serviceId: t.exposeID('serviceId'),
// }),
// });
// builder.prismaObject('WorkshopSubscription', {
// fields: (t) => ({
// userId: t.exposeID('userId'),
// workshopId: t.exposeID('workshopId'),
// user: t.relation('user'),
// workshop: t.relation('workshop'),
// }),
// });
// builder.prismaObject('Workshop', {
// fields: (t) => ({
// id: t.exposeID('id'),
// }),
// });
// // Query section
// builder.queryType({
// fields: (t) => ({
// users: t.prismaField({
// type: ['User'], // Return type is a list of 'User'
// resolve: (query, root, args, ctx, info) => {
// return prisma.user.findMany({
// ...query,
// // Include related posts in the query
// });
// },
// }),
// user: t.prismaField({
// type: 'User',
// args: {
// data: t.arg({
// type: builder.inputType('UserCreateInput', {
// fields: (t) => ({
// email: t.string({ required: true }),
// name: t.string(),
// // Include other fields as per your schema
// }),
// }),
// required: true, // Make the data argument required
// }),
// id: t.arg.string(),
// },
// resolve: (query, root, args, ctx, info) => {
// if (!args.data) {
// throw new Error('Data input is required');
// }
// return prisma.user.create({
// return prisma.user.findUnique({
// where: {
// id: args.id?.toString(),
// },
// });
// },
// }),
// orders: t.prismaField({
// type: ['Order'],
// resolve: (query, root, args, ctx, info) => {
// return prisma.order.findMany({
// ...query,
// });
// },
// }),
// serviceFeedbacks: t.prismaField({
// type: ['ServiceFeedback'],
// resolve: (query, root, args, ctx, info) => {
// return prisma.serviceFeedback.findMany({
// ...query,
// });
// },
// }),
// documents: t.prismaField({
// type: ['UploadedDocument'],
// resolve: (query, root, args, ctx, info) => {
// return prisma.uploadedDocument.findMany({
// ...query,
// });
// },
// }),
// messages: t.prismaField({
// type: ['Message'],
// resolve: (query, root, args, ctx, info) => {
// return prisma.message.findMany({
// ...query,
// });
// },
// }),
// services: t.prismaField({
// type: ['Service'],
// args: {
// where: t.arg({
// type: builder.inputType('ServiceWhereInput', {
// fields: (t) => ({
// // search by name contains
// nameContain: t.string(),
// // search by name starts with
// nameStartsWith: t.string(),
// // search by name ends with
// nameEndsWith: t.string(),
// }),
// }),
// }),
// orderBy: t.arg({
// type: builder.inputType('ServiceOrderByInput', {
// fields: (t) => ({
// rating: t.field({
// type: SortOrder,
// }),
// price: t.field({
// type: SortOrder,
// }),
// }),
// }),
// }),
// cursor: t.arg({
// type: builder.inputType('ServiceWhereUniqueInput', {
// fields: (t) => ({
// // Define fields to match your `ServiceWhereUniqueInput` structure.
// name: t.string(),
// }),
// }),
// }),
// take: t.arg.int(),
// skip: t.arg.int(),
// },
// resolve: async (query, root, args, ctx, info) => {
// return prisma.service.findMany({
// // handle where condition
// where: {
// name: {
// contains: args.where?.nameContain as string | undefined,
// startsWith: args.where?.nameStartsWith as string | undefined,
// endsWith: args.where?.nameEndsWith as string | undefined,
// },
// },
// // handle orderBy condition
// orderBy: {
// rating: args.orderBy?.rating as Prisma.SortOrder,
// price: args.orderBy?.price as Prisma.SortOrder,
// },
// // handle pagination
// cursor: args.cursor as Prisma.ServiceWhereUniqueInput | undefined,
// take: args.take as number | undefined,
// skip: args.skip as number | undefined,
// });
// },
// }),
// service: t.prismaField({
// type: 'Service',
// args: {
// id: t.arg.string(),
// },
// resolve: (query, root, args, ctx, info) => {
// return prisma.service.findUnique({
// where: {
// id: args.id?.toString(),
// },
// });
// },
// }),
// centers: t.prismaField({
// type: ['Center'],
// resolve: (query, root, args, ctx, info) => {
// return prisma.center.findMany({
// ...query,
// });
// },
// }),
// chatRooms: t.prismaField({
// type: ['ChatRoom'],
// resolve: (query, root, args, ctx, info) => {
// return prisma.chatRoom.findMany({
// ...query,
// });
// },
// }),
// centerStaffs: t.prismaField({
// type: ['CenterStaff'],
// resolve: (query, root, args, ctx, info) => {
// return prisma.centerStaff.findMany({
// ...query,
// });
// },
// }),
// workshopSubscriptions: t.prismaField({
// type: ['WorkshopSubscription'],
// resolve: (query, root, args, ctx, info) => {
// return prisma.workshopSubscription.findMany({
// ...query,
// });
// },
// }),
// workshops: t.prismaField({
// type: ['Workshop'],
// resolve: (query, root, args, ctx, info) => {
// return prisma.workshop.findMany({
// ...query,
// data: args.data as Prisma.UserCreateInput, // Explicit type casting to match Prisma's expectation
// });
// },
// }),
// }),
// });
// Subscription section
// builder.subscriptionType({
// fields: (t) => ({
// userCreated: t.prismaField({
// type: 'User',
// subscribe: (query, root, args, ctx, info) => {
// return prisma.$subscribe.user({
// mutation_in: ['CREATED'],
// });
// },
// resolve: (payload) => {
// return payload;
// },
// }),
// }),
// });
// // Mutation section
// // builder.mutationType({
// // fields: (t) => ({
// // createUser: t.prismaField({
// // type: 'User',
// // args: {
// // data: t.arg({
// // type: builder.inputType('UserCreateInput', {
// // fields: (t) => ({
// // email: t.string({ required: true }),
// // name: t.string(),
// // // Include other fields as per your schema
// // }),
// // }),
// // required: true, // Make the data argument required
// // }),
// // },
// // resolve: (query, root, args, ctx, info) => {
// // if (!args.data) {
// // throw new Error('Data input is required');
// // }
export const schema = builder.toSchema();
// // return prisma.user.create({
// // ...query,
// // data: args.data as Prisma.UserCreateInput, // Explicit type casting to match Prisma's expectation
// // });
// // },
// // }),
// // }),
// // });
// // Subscription section
// // builder.subscriptionType({
// // fields: (t) => ({
// // userCreated: t.prismaField({
// // type: 'User',
// // subscribe: (query, root, args, ctx, info) => {
// // return prisma.$subscribe.user({
// // mutation_in: ['CREATED'],
// // });
// // },
// // resolve: (payload) => {
// // return payload;
// // },
// // }),
// // }),
// // });
// export const schema = builder.toSchema();

View File

@@ -22,7 +22,8 @@ async function bootstrap() {
document.paths['/graphql'] = {
get: {
summary: 'GraphQL Playground',
description: 'Access the GraphQL Playground to interact with the GraphQL API.',
description:
'Access the GraphQL Playground to interact with the GraphQL API.',
responses: {
'200': {
description: 'GraphQL Playground',
@@ -31,6 +32,7 @@ async function bootstrap() {
},
};
await app.listen(3000);
const port = process.env.LISTEN_PORT ?? 3000; // Default to 3000 if LISTEN_PORT is not set
await app.listen(port);
}
bootstrap();

View File

@@ -5,7 +5,10 @@ import { Request, Response, NextFunction } from 'express';
export class GraphQLValidationMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
// Only handle POST requests
if (req.method === 'POST' && req.headers['content-type'] === 'application/json') {
if (
req.method === 'POST' &&
req.headers['content-type'] === 'application/json'
) {
const { query, mutation, subscription } = req.body;
// If none of these are present, return a custom error response
@@ -13,7 +16,8 @@ export class GraphQLValidationMiddleware implements NestMiddleware {
return res.status(400).json({
errors: [
{
message: 'Must provide a valid GraphQL query, mutation, or subscription.',
message:
'Must provide a valid GraphQL query, mutation, or subscription.',
},
],
});

View File

@@ -1,8 +1,9 @@
import { Module } from '@nestjs/common';
import { Global, Module } from '@nestjs/common';
import { PrismaService } from './prisma.service';
@Global()
@Module({
providers: [PrismaService],
exports: [PrismaService], // Export PrismaService so other modules can use it
exports: [PrismaService],
})
export class PrismaModule {}

View File

@@ -1,13 +1,32 @@
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import {
ConsoleLogger,
INestApplication,
Injectable,
OnModuleInit,
} from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
async onModuleInit() {
await this.$connect();
export class PrismaService extends PrismaClient implements OnModuleInit {
private readonly logger = new ConsoleLogger(PrismaService.name);
constructor() {
super({
log: ['query', 'info', 'warn', 'error'],
});
}
async onModuleDestroy() {
await this.$disconnect();
async onModuleInit() {
this.logger.log('Try to connect database...');
await this.$connect();
this.logger.log('Connected.');
}
async enableShutdownHooks(app: INestApplication) {
this.$on('beforeExit' as never, async () => {
this.logger.log('Wait for application closing...');
await app.close();
});
}
}

View File

@@ -1,4 +1,12 @@
import { Controller, Get, Post, Put, Delete, Param, Body } from '@nestjs/common';
import {
Controller,
Get,
Post,
Put,
Delete,
Param,
Body,
} from '@nestjs/common';
import { RestfulService } from './restful.service';
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
@@ -7,40 +15,52 @@ import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
export class RestfulController {
constructor(private readonly restfulService: RestfulService) {}
@Get()
@ApiOperation({ summary: 'Get all items' })
@ApiResponse({ status: 200, description: 'Returns all items.' })
getAllItems() {
return this.restfulService.getAllItems();
}
// @Get()
// @ApiOperation({ summary: 'Get all items' })
// @ApiResponse({ status: 200, description: 'Returns all items.' })
// getAllItems() {
// return this.restfulService.getAllItems();
// }
@Get(':id')
@ApiOperation({ summary: 'Get an item by ID' })
@ApiResponse({ status: 200, description: 'Returns the item with the given ID.' })
getItem(@Param('id') id: string) {
return this.restfulService.getItem(id);
}
// @Get(':id')
// @ApiOperation({ summary: 'Get an item by ID' })
// @ApiResponse({
// status: 200,
// description: 'Returns the item with the given ID.',
// })
// getItem(@Param('id') id: string) {
// return this.restfulService.getItem(id);
// }
@Post()
@ApiOperation({ summary: 'Create a new item' })
@ApiResponse({ status: 201, description: 'The item has been successfully created.' })
createItem(@Body() createDto: any) {
return this.restfulService.createItem(createDto);
}
// @Post()
// @ApiOperation({ summary: 'Create a new item' })
// @ApiResponse({
// status: 201,
// description: 'The item has been successfully created.',
// })
// createItem(@Body() createDto: any) {
// return this.restfulService.createItem(createDto);
// }
@Put(':id')
@ApiOperation({ summary: 'Update an item' })
@ApiResponse({ status: 200, description: 'The item has been successfully updated.' })
updateItem(@Param('id') id: string, @Body() updateDto: any) {
return this.restfulService.updateItem(id, updateDto);
}
// @Put(':id')
// @ApiOperation({ summary: 'Update an item' })
// @ApiResponse({
// status: 200,
// description: 'The item has been successfully updated.',
// })
// updateItem(@Param('id') id: string, @Body() updateDto: any) {
// return this.restfulService.updateItem(id, updateDto);
// }
@Delete(':id')
@ApiOperation({ summary: 'Delete an item' })
@ApiResponse({ status: 200, description: 'The item has been successfully deleted.' })
deleteItem(@Param('id') id: string) {
return this.restfulService.deleteItem(id);
}
// @Delete(':id')
// @ApiOperation({ summary: 'Delete an item' })
// @ApiResponse({
// status: 200,
// description: 'The item has been successfully deleted.',
// })
// deleteItem(@Param('id') id: string) {
// return this.restfulService.deleteItem(id);
// }
}
export default RestfulController;

View File

@@ -6,4 +6,4 @@ import { RestfulService } from './restful.service';
controllers: [RestfulController],
providers: [RestfulService],
})
export class RestfulModule {}
export class RestfulModule {}

View File

@@ -1,31 +1,4 @@
import { Injectable } from '@nestjs/common';
@Injectable()
export class RestfulService {
getAllItems() {
// Implementation to get all items
return []; // Replace with actual logic
}
getItem(id: string) {
// Implementation to get a single item by id
return {}; // Replace with actual logic
}
createItem(createDto: any) {
// Implementation to create a new item
return {}; // Replace with actual logic
}
updateItem(id: string, updateDto: any) {
// Implementation to update an item by id
return {}; // Replace with actual logic
}
deleteItem(id: string) {
// Implementation to delete an item by id
return {}; // Replace with actual logic
}
}
export default RestfulService;
export class RestfulService {}

View File

@@ -0,0 +1,4 @@
import { Module } from '@nestjs/common';
@Module({})
export class ServiceModule {}

View File

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

9
src/user/user.module.ts Normal file
View File

@@ -0,0 +1,9 @@
import { Global, Module } from '@nestjs/common';
import { UserSchema } from './user.schema';
@Global()
@Module({
providers: [UserSchema],
exports: [UserSchema],
})
export class UserModule {}

59
src/user/user.schema.ts Normal file
View File

@@ -0,0 +1,59 @@
import { Inject, Injectable } from '@nestjs/common';
import {
Pothos,
PothosRef,
PothosSchema,
SchemaBuilderToken,
} from '@smatch-corp/nestjs-pothos';
import { Builder } from '../graphql/graphql.builder';
import { PrismaService } from 'src/prisma/prisma.service';
@Injectable()
export class UserSchema extends PothosSchema {
constructor(
@Inject(SchemaBuilderToken) private readonly builder: Builder,
private readonly prisma: PrismaService,
) {
super();
}
// Types section
@PothosRef()
user(): any {
return this.builder.prismaObject('User', {
fields: (t) => ({
id: t.exposeID('id'),
name: t.exposeString('name'),
email: t.exposeString('email'),
phoneNumber: t.exposeString('phoneNumber'),
oauthToken: t.exposeString('oauthToken'),
role: t.exposeString('role'),
createdAt: t.expose('createdAt', {
type: 'Date',
nullable: true,
}),
updatedAt: t.expose('updatedAt', {
type: 'Date',
nullable: true,
}),
}),
});
}
// Query section
@Pothos()
init(): void {
this.builder.queryFields((t) => ({
users: t.prismaField({
type: 'User',
resolve: async (query) => this.prisma.user.findMany() as any,
}),
}));
}
// Mutation section
@Pothos()
createUser() {
return 'createUser';
}
}

View File

@@ -1,10 +0,0 @@
import { Module } from '@nestjs/common';
import { UsersService } from './users.service';
import { UsersResolver } from './users.resolver';
import { PrismaModule } from '../prisma/prisma.module'; // PrismaModule to access database
@Module({
imports: [PrismaModule],
providers: [UsersService, UsersResolver],
})
export class UsersModule {}

View File

@@ -1,7 +0,0 @@
import { Resolver, Query } from '@nestjs/graphql';
import { UsersService } from './users.service';
@Resolver('User')
export class UsersResolver {
constructor(private readonly usersService: UsersService) {}
}

View File

@@ -1,7 +0,0 @@
import { Injectable } from '@nestjs/common';
@Injectable()
export class UsersService {
async findOneById(id: string) {
return { id };
}
}