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-prisma-utils": "^1.2.0",
"@pothos/plugin-scope-auth": "^4.1.0", "@pothos/plugin-scope-auth": "^4.1.0",
"@prisma/client": "^5.20.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", "apollo-server-express": "^3.13.0",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
"class-transformer": "^0.5.1", "class-transformer": "^0.5.1",
@@ -46,6 +48,8 @@
}, },
"devDependencies": { "devDependencies": {
"@clerk/types": "^4.23.0", "@clerk/types": "^4.23.0",
"@eslint/eslintrc": "^3.1.0",
"@eslint/js": "^9.12.0",
"@graphql-codegen/cli": "5.0.2", "@graphql-codegen/cli": "5.0.2",
"@graphql-codegen/typescript": "4.0.9", "@graphql-codegen/typescript": "4.0.9",
"@graphql-codegen/typescript-resolvers": "4.2.1", "@graphql-codegen/typescript-resolvers": "4.2.1",
@@ -64,6 +68,7 @@
"eslint": "9.11.1", "eslint": "9.11.1",
"eslint-config-prettier": "^9.0.0", "eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0", "eslint-plugin-prettier": "^5.0.0",
"globals": "^15.10.0",
"jest": "^29.5.0", "jest": "^29.5.0",
"prettier": "^3.0.0", "prettier": "^3.0.0",
"prisma": "^5.20.0", "prisma": "^5.20.0",
@@ -2105,6 +2110,19 @@
"concat-map": "0.0.1" "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": { "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": {
"version": "0.4.1", "version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
@@ -2126,9 +2144,9 @@
} }
}, },
"node_modules/@eslint/js": { "node_modules/@eslint/js": {
"version": "9.11.1", "version": "9.12.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.11.1.tgz", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.12.0.tgz",
"integrity": "sha512-/qu+TWz8WwPWc7/HcIJKi+c+MOm46GdVaSlTTQcaqaL53+GsoA6MxWp5PtTx48qbSP7ylM1Kn7nhvkugfJvRSA==", "integrity": "sha512-eohesHH8WFRUprDNyEREgqP6beG6htMeUYeCpkEgBCieCMme5r9zFWjzAJp//9S+Kub4rqE+jXe9Cp1a7IYIIA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@@ -5747,6 +5765,35 @@
"@sinonjs/commons": "^3.0.0" "@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": { "node_modules/@tsconfig/node10": {
"version": "1.0.11", "version": "1.0.11",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
@@ -8968,6 +9015,16 @@
"url": "https://opencollective.com/eslint" "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": { "node_modules/eslint/node_modules/ajv": {
"version": "6.12.6", "version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -9844,9 +9901,9 @@
"license": "BSD-2-Clause" "license": "BSD-2-Clause"
}, },
"node_modules/globals": { "node_modules/globals": {
"version": "14.0.0", "version": "15.10.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", "resolved": "https://registry.npmjs.org/globals/-/globals-15.10.0.tgz",
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "integrity": "sha512-tqFIbz83w4Y5TCbtgjZjApohbuh7K9BxGYFm7ifwDR240tvdb7P9x+/9VvUKlmkPoiknoJtanI8UOrqxS3a7lQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {

View File

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

View File

@@ -7,7 +7,7 @@ import { RestfulModule } from './restful/restful.module';
imports: [ imports: [
GraphqlModule, // GraphQL setup GraphqlModule, // GraphQL setup
ClerkModule, // Clerk setup ClerkModule, // Clerk setup
RestfulModule, // RESTful API module RestfulModule,
], ],
}) })
export class AppModule {} 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, startA: number,
endA: number, endA: number,
startB: number, startB: number,
endB: number endB: number,
) { ) {
const overlapStart = Math.max(startA, startB); const overlapStart = Math.max(startA, startB);
const overlapEnd = Math.min(endA, endB); const overlapEnd = Math.min(endA, endB);
@@ -10,12 +10,7 @@ function getOverlapRange(
return overlapStart < overlapEnd ? { overlapStart, overlapEnd } : null; return overlapStart < overlapEnd ? { overlapStart, overlapEnd } : null;
} }
function isOverlap( function isOverlap(startA: number, endA: number, startB: number, endB: number) {
startA: number,
endA: number,
startB: number,
endB: number
) {
return getOverlapRange(startA, endA, startB, endB) !== null; return getOverlapRange(startA, endA, startB, endB) !== null;
} }
@@ -57,10 +52,10 @@ const endB = new Date('2024-10-06T13:00:00Z').getTime();
const overlapRange = getOverlapRange(startA, endA, startB, endB); const overlapRange = getOverlapRange(startA, endA, startB, endB);
if (overlapRange) { if (overlapRange) {
console.log( console.log(
`Overlap Start: ${new Date(overlapRange.overlapStart).toISOString()}` `Overlap Start: ${new Date(overlapRange.overlapStart).toISOString()}`,
); );
console.log( console.log(
`Overlap End: ${new Date(overlapRange.overlapEnd).toISOString()}` `Overlap End: ${new Date(overlapRange.overlapEnd).toISOString()}`,
); );
console.log('Is overlap: true'); console.log('Is overlap: true');

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

View File

@@ -1,28 +1,32 @@
import SchemaBuilder from '@pothos/core'; 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 PrismaUtils from '@pothos/plugin-prisma-utils';
import { DateTimeResolver } from 'graphql-scalars'; import { Request } from 'express';
import { GraphQLInt } from 'graphql';
import { PrismaService } from '../prisma/prisma.service';
import type PrismaTypes from '../types/pothos.generated'; import type PrismaTypes from '../types/pothos.generated';
import { getDatamodel } from '../types/pothos.generated'; import { getDatamodel } from '../types/pothos.generated';
import { DateTimeResolver } from 'graphql-scalars';
export const prisma = new PrismaService({ export interface SchemaContext {
log: ['query', 'info', 'warn', 'error'], req: Request;
errorFormat: 'pretty', }
});
export const builder = new SchemaBuilder<{ interface SchemaBuilderOption {
Context: SchemaContext;
PrismaTypes: PrismaTypes;
DataModel: PothosPrismaDatamodel;
Scalars: { Scalars: {
DateTime: { Date: {
Input: Date; Input: Date;
Output: Date; Output: Date;
}; };
}; };
PrismaTypes: PrismaTypes; }
}>({ export function createBuilder(client: PrismaClient) {
const builder = new SchemaBuilder<SchemaBuilderOption>({
plugins: [PrismaPlugin, PrismaUtils], plugins: [PrismaPlugin, PrismaUtils],
prisma: { prisma: {
client: prisma, client,
exposeDescriptions: true, exposeDescriptions: true,
filterConnectionTotalCount: true, filterConnectionTotalCount: true,
onUnusedQuery: process.env.NODE_ENV === 'production' ? null : 'warn', onUnusedQuery: process.env.NODE_ENV === 'production' ? null : 'warn',
@@ -30,7 +34,12 @@ export const builder = new SchemaBuilder<{
}, },
}); });
export const SortOrder = builder.enumType('SortOrder', { builder.queryType({});
values: ['asc', 'desc'], // builder.mutationType({});
}); // builder.subscriptionType({});
builder.addScalarType('DateTime', DateTimeResolver, {});
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 { MiddlewareConsumer, Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql'; 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 { PrismaService } from '../prisma/prisma.service';
import { schema } from './schema';
import { GraphQLValidationMiddleware } from 'src/middlewares/graphql.middleware'; 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({ @Module({
imports: [ imports: [
PrismaModule,
UserModule,
PothosModule.forRoot({
builder: {
inject: [PrismaService],
useFactory: (prisma: PrismaService) => createBuilder(prisma),
},
}),
GraphQLModule.forRoot<ApolloDriverConfig>({ GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver, driver: PothosApolloDriver,
schema: schema,
debug: true,
allowBatchedHttpRequests: true,
introspection: true,
}), }),
], ],
providers: [PrismaService], providers: [GraphqlService],
}) })
export class GraphqlModule { export class GraphqlModule {
configure(consumer: MiddlewareConsumer) { configure(consumer: MiddlewareConsumer) {

View File

@@ -1,448 +1,448 @@
import { builder, prisma, SortOrder } from './graphql.builder'; // import { builder, prisma, SortOrder } from './graphql.builder';
import { Prisma } from '@prisma/client'; // import { Prisma } from '@prisma/client';
builder.prismaObject('User', { // 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({
// fields: (t) => ({ // 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', // type: 'User',
// args: { // args: {
// data: t.arg({ // id: t.arg.string(),
// 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) => { // resolve: (query, root, args, ctx, info) => {
// if (!args.data) { // return prisma.user.findUnique({
// throw new Error('Data input is required'); // where: {
// } // id: args.id?.toString(),
// },
// return prisma.user.create({ // });
// },
// }),
// orders: t.prismaField({
// type: ['Order'],
// resolve: (query, root, args, ctx, info) => {
// return prisma.order.findMany({
// ...query, // ...query,
// data: args.data as Prisma.UserCreateInput, // Explicit type casting to match Prisma's expectation
// }); // });
// }, // },
// }), // }),
// }), // serviceFeedbacks: t.prismaField({
// type: ['ServiceFeedback'],
// resolve: (query, root, args, ctx, info) => {
// return prisma.serviceFeedback.findMany({
// ...query,
// }); // });
// },
// Subscription section // }),
// builder.subscriptionType({ // 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) => ({ // fields: (t) => ({
// userCreated: t.prismaField({ // // search by name contains
// type: 'User', // nameContain: t.string(),
// subscribe: (query, root, args, ctx, info) => { // // search by name starts with
// return prisma.$subscribe.user({ // nameStartsWith: t.string(),
// mutation_in: ['CREATED'], // // 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,
// }); // });
// }, // },
// resolve: (payload) => { // }),
// return payload; // 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,
// });
// }, // },
// }), // }),
// }), // }),
// }); // });
export const schema = builder.toSchema(); // // 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');
// // }
// // 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'] = { document.paths['/graphql'] = {
get: { get: {
summary: 'GraphQL Playground', 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: { responses: {
'200': { '200': {
description: 'GraphQL Playground', 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(); bootstrap();

View File

@@ -5,7 +5,10 @@ import { Request, Response, NextFunction } from 'express';
export class GraphQLValidationMiddleware implements NestMiddleware { export class GraphQLValidationMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) { use(req: Request, res: Response, next: NextFunction) {
// Only handle POST requests // 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; const { query, mutation, subscription } = req.body;
// If none of these are present, return a custom error response // 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({ return res.status(400).json({
errors: [ 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'; import { PrismaService } from './prisma.service';
@Global()
@Module({ @Module({
providers: [PrismaService], providers: [PrismaService],
exports: [PrismaService], // Export PrismaService so other modules can use it exports: [PrismaService],
}) })
export class PrismaModule {} 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'; import { PrismaClient } from '@prisma/client';
@Injectable() @Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy { export class PrismaService extends PrismaClient implements OnModuleInit {
async onModuleInit() { private readonly logger = new ConsoleLogger(PrismaService.name);
await this.$connect();
constructor() {
super({
log: ['query', 'info', 'warn', 'error'],
});
} }
async onModuleDestroy() { async onModuleInit() {
await this.$disconnect(); 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 { RestfulService } from './restful.service';
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger'; import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
@@ -7,40 +15,52 @@ import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
export class RestfulController { export class RestfulController {
constructor(private readonly restfulService: RestfulService) {} constructor(private readonly restfulService: RestfulService) {}
@Get() // @Get()
@ApiOperation({ summary: 'Get all items' }) // @ApiOperation({ summary: 'Get all items' })
@ApiResponse({ status: 200, description: 'Returns all items.' }) // @ApiResponse({ status: 200, description: 'Returns all items.' })
getAllItems() { // getAllItems() {
return this.restfulService.getAllItems(); // return this.restfulService.getAllItems();
} // }
@Get(':id') // @Get(':id')
@ApiOperation({ summary: 'Get an item by ID' }) // @ApiOperation({ summary: 'Get an item by ID' })
@ApiResponse({ status: 200, description: 'Returns the item with the given ID.' }) // @ApiResponse({
getItem(@Param('id') id: string) { // status: 200,
return this.restfulService.getItem(id); // description: 'Returns the item with the given ID.',
} // })
// getItem(@Param('id') id: string) {
// return this.restfulService.getItem(id);
// }
@Post() // @Post()
@ApiOperation({ summary: 'Create a new item' }) // @ApiOperation({ summary: 'Create a new item' })
@ApiResponse({ status: 201, description: 'The item has been successfully created.' }) // @ApiResponse({
createItem(@Body() createDto: any) { // status: 201,
return this.restfulService.createItem(createDto); // description: 'The item has been successfully created.',
} // })
// createItem(@Body() createDto: any) {
// return this.restfulService.createItem(createDto);
// }
@Put(':id') // @Put(':id')
@ApiOperation({ summary: 'Update an item' }) // @ApiOperation({ summary: 'Update an item' })
@ApiResponse({ status: 200, description: 'The item has been successfully updated.' }) // @ApiResponse({
updateItem(@Param('id') id: string, @Body() updateDto: any) { // status: 200,
return this.restfulService.updateItem(id, updateDto); // description: 'The item has been successfully updated.',
} // })
// updateItem(@Param('id') id: string, @Body() updateDto: any) {
// return this.restfulService.updateItem(id, updateDto);
// }
@Delete(':id') // @Delete(':id')
@ApiOperation({ summary: 'Delete an item' }) // @ApiOperation({ summary: 'Delete an item' })
@ApiResponse({ status: 200, description: 'The item has been successfully deleted.' }) // @ApiResponse({
deleteItem(@Param('id') id: string) { // status: 200,
return this.restfulService.deleteItem(id); // description: 'The item has been successfully deleted.',
} // })
// deleteItem(@Param('id') id: string) {
// return this.restfulService.deleteItem(id);
// }
} }
export default RestfulController; export default RestfulController;

View File

@@ -1,31 +1,4 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
@Injectable() @Injectable()
export class RestfulService { 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;

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 };
}
}