From bbc80cd5437f9d1bf0027ec3ed5c7cb0fb69b237 Mon Sep 17 00:00:00 2001 From: Charles Bochet Date: Sun, 4 Jun 2023 00:21:36 +0200 Subject: [PATCH] Refactor backend and add exception handlers (#189) --- front/src/generated/graphql.tsx | 589 +++++------------- ...-create-nested-one-without-people.input.ts | 9 +- ...-update-one-without-people-nested.input.ts | 19 +- ...reate-nested-many-without-company.input.ts | 12 +- ...pdate-many-without-company-nested.input.ts | 39 +- ...eate-nested-one-without-companies.input.ts | 9 +- ...date-one-without-companies-nested.input.ts | 19 +- server/src/api/api.module.ts | 5 + server/src/api/resolvers/company.resolver.ts | 9 +- .../exception-filters/exception.filter.ts | 18 + .../api/resolvers/guards/create-one.guard.ts | 12 + .../api/resolvers/guards/delete-many.guard.ts | 12 + .../api/resolvers/guards/update-one.guard.ts | 50 ++ server/src/api/resolvers/person.resolver.ts | 9 +- server/src/api/resolvers/user.resolver.ts | 15 +- server/src/app.controller.spec.ts | 20 - server/src/app.controller.ts | 7 - server/src/app.module.ts | 3 +- server/src/auth/auth.module.ts | 17 +- server/src/auth/google.auth.controller.ts | 2 +- .../guards/check-workspace-ownership.guard.ts | 85 --- server/src/auth/services/auth.service.ts | 60 +- ...auth.controller.ts => token.controller.ts} | 2 +- server/src/database/schema.prisma | 93 ++- server/src/entities/company/company.module.ts | 10 - .../entities/company/company.repository.ts | 25 - server/src/entities/person/person.module.ts | 10 - .../src/entities/person/person.repository.ts | 19 - .../refresh-token/refresh-token.repository.ts | 32 - server/src/entities/user/user.module.ts | 10 - server/src/entities/user/user.repository.ts | 67 -- .../entities/workspace/workspace.module.ts | 10 - .../workspace/workspace.repository.ts | 37 -- server/src/health.controller.spec.ts | 22 + server/src/main.ts | 1 + 35 files changed, 459 insertions(+), 899 deletions(-) create mode 100644 server/src/api/resolvers/exception-filters/exception.filter.ts create mode 100644 server/src/api/resolvers/guards/create-one.guard.ts create mode 100644 server/src/api/resolvers/guards/delete-many.guard.ts create mode 100644 server/src/api/resolvers/guards/update-one.guard.ts delete mode 100644 server/src/app.controller.spec.ts delete mode 100644 server/src/app.controller.ts delete mode 100644 server/src/auth/guards/check-workspace-ownership.guard.ts rename server/src/auth/{auth.controller.ts => token.controller.ts} (95%) delete mode 100644 server/src/entities/company/company.module.ts delete mode 100644 server/src/entities/company/company.repository.ts delete mode 100644 server/src/entities/person/person.module.ts delete mode 100644 server/src/entities/person/person.repository.ts delete mode 100644 server/src/entities/refresh-token/refresh-token.repository.ts delete mode 100644 server/src/entities/user/user.module.ts delete mode 100644 server/src/entities/user/user.repository.ts delete mode 100644 server/src/entities/workspace/workspace.module.ts delete mode 100644 server/src/entities/workspace/workspace.repository.ts create mode 100644 server/src/health.controller.spec.ts diff --git a/front/src/generated/graphql.tsx b/front/src/generated/graphql.tsx index 1b8a4d7914..8d2b4c8a9e 100644 --- a/front/src/generated/graphql.tsx +++ b/front/src/generated/graphql.tsx @@ -22,10 +22,6 @@ export type AffectedRows = { count: Scalars['Int']; }; -export type BoolFieldUpdateOperationsInput = { - set?: InputMaybe; -}; - export type BoolFilter = { equals?: InputMaybe; not?: InputMaybe; @@ -54,20 +50,6 @@ export type CommentCreateInput = { updatedAt?: InputMaybe; }; -export type CommentCreateManyAuthorInput = { - body: Scalars['String']; - commentThreadId: Scalars['String']; - createdAt?: InputMaybe; - deletedAt?: InputMaybe; - id: Scalars['String']; - updatedAt?: InputMaybe; -}; - -export type CommentCreateManyAuthorInputEnvelope = { - data: Array; - skipDuplicates?: InputMaybe; -}; - export type CommentCreateManyCommentThreadInput = { authorId: Scalars['String']; body: Scalars['String']; @@ -82,31 +64,10 @@ export type CommentCreateManyCommentThreadInputEnvelope = { skipDuplicates?: InputMaybe; }; -export type CommentCreateNestedManyWithoutAuthorInput = { - connect?: InputMaybe>; - connectOrCreate?: InputMaybe>; - create?: InputMaybe>; - createMany?: InputMaybe; -}; - export type CommentCreateNestedManyWithoutCommentThreadInput = { createMany?: InputMaybe; }; -export type CommentCreateOrConnectWithoutAuthorInput = { - create: CommentCreateWithoutAuthorInput; - where: CommentWhereUniqueInput; -}; - -export type CommentCreateWithoutAuthorInput = { - body: Scalars['String']; - commentThread: CommentThreadCreateNestedOneWithoutCommentsInput; - createdAt?: InputMaybe; - deletedAt?: InputMaybe; - id: Scalars['String']; - updatedAt?: InputMaybe; -}; - export type CommentListRelationFilter = { every?: InputMaybe; none?: InputMaybe; @@ -117,19 +78,6 @@ export type CommentOrderByRelationAggregateInput = { _count?: InputMaybe; }; -export type CommentScalarWhereInput = { - AND?: InputMaybe>; - NOT?: InputMaybe>; - OR?: InputMaybe>; - authorId?: InputMaybe; - body?: InputMaybe; - commentThreadId?: InputMaybe; - createdAt?: InputMaybe; - deletedAt?: InputMaybe; - id?: InputMaybe; - updatedAt?: InputMaybe; -}; - export type CommentThread = { __typename?: 'CommentThread'; commentThreadTargets?: Maybe>; @@ -153,19 +101,6 @@ export type CommentThreadCreateNestedOneWithoutCommentsInput = { connect?: InputMaybe; }; -export type CommentThreadCreateOrConnectWithoutCommentsInput = { - create: CommentThreadCreateWithoutCommentsInput; - where: CommentThreadWhereUniqueInput; -}; - -export type CommentThreadCreateWithoutCommentsInput = { - commentThreadTargets?: InputMaybe; - createdAt?: InputMaybe; - deletedAt?: InputMaybe; - id: Scalars['String']; - updatedAt?: InputMaybe; -}; - export type CommentThreadRelationFilter = { is?: InputMaybe; isNot?: InputMaybe; @@ -201,87 +136,12 @@ export type CommentThreadTargetCreateNestedManyWithoutCommentThreadInput = { createMany?: InputMaybe; }; -export type CommentThreadTargetCreateOrConnectWithoutCommentThreadInput = { - create: CommentThreadTargetCreateWithoutCommentThreadInput; - where: CommentThreadTargetWhereUniqueInput; -}; - -export type CommentThreadTargetCreateWithoutCommentThreadInput = { - commentableId: Scalars['String']; - commentableType: CommentableType; - createdAt?: InputMaybe; - deletedAt?: InputMaybe; - id: Scalars['String']; - updatedAt?: InputMaybe; -}; - export type CommentThreadTargetListRelationFilter = { every?: InputMaybe; none?: InputMaybe; some?: InputMaybe; }; -export type CommentThreadTargetScalarWhereInput = { - AND?: InputMaybe>; - NOT?: InputMaybe>; - OR?: InputMaybe>; - commentThreadId?: InputMaybe; - commentableId?: InputMaybe; - commentableType?: InputMaybe; - createdAt?: InputMaybe; - deletedAt?: InputMaybe; - id?: InputMaybe; - updatedAt?: InputMaybe; -}; - -export type CommentThreadTargetUpdateManyMutationInput = { - commentableId?: InputMaybe; - commentableType?: InputMaybe; - createdAt?: InputMaybe; - deletedAt?: InputMaybe; - id?: InputMaybe; - updatedAt?: InputMaybe; -}; - -export type CommentThreadTargetUpdateManyWithWhereWithoutCommentThreadInput = { - data: CommentThreadTargetUpdateManyMutationInput; - where: CommentThreadTargetScalarWhereInput; -}; - -export type CommentThreadTargetUpdateManyWithoutCommentThreadNestedInput = { - connect?: InputMaybe>; - connectOrCreate?: InputMaybe>; - create?: InputMaybe>; - createMany?: InputMaybe; - delete?: InputMaybe>; - deleteMany?: InputMaybe>; - disconnect?: InputMaybe>; - set?: InputMaybe>; - update?: InputMaybe>; - updateMany?: InputMaybe>; - upsert?: InputMaybe>; -}; - -export type CommentThreadTargetUpdateWithWhereUniqueWithoutCommentThreadInput = { - data: CommentThreadTargetUpdateWithoutCommentThreadInput; - where: CommentThreadTargetWhereUniqueInput; -}; - -export type CommentThreadTargetUpdateWithoutCommentThreadInput = { - commentableId?: InputMaybe; - commentableType?: InputMaybe; - createdAt?: InputMaybe; - deletedAt?: InputMaybe; - id?: InputMaybe; - updatedAt?: InputMaybe; -}; - -export type CommentThreadTargetUpsertWithWhereUniqueWithoutCommentThreadInput = { - create: CommentThreadTargetCreateWithoutCommentThreadInput; - update: CommentThreadTargetUpdateWithoutCommentThreadInput; - where: CommentThreadTargetWhereUniqueInput; -}; - export type CommentThreadTargetWhereInput = { AND?: InputMaybe>; NOT?: InputMaybe>; @@ -296,31 +156,6 @@ export type CommentThreadTargetWhereInput = { updatedAt?: InputMaybe; }; -export type CommentThreadTargetWhereUniqueInput = { - id?: InputMaybe; -}; - -export type CommentThreadUpdateOneRequiredWithoutCommentsNestedInput = { - connect?: InputMaybe; - connectOrCreate?: InputMaybe; - create?: InputMaybe; - update?: InputMaybe; - upsert?: InputMaybe; -}; - -export type CommentThreadUpdateWithoutCommentsInput = { - commentThreadTargets?: InputMaybe; - createdAt?: InputMaybe; - deletedAt?: InputMaybe; - id?: InputMaybe; - updatedAt?: InputMaybe; -}; - -export type CommentThreadUpsertWithoutCommentsInput = { - create: CommentThreadCreateWithoutCommentsInput; - update: CommentThreadUpdateWithoutCommentsInput; -}; - export type CommentThreadWhereInput = { AND?: InputMaybe>; NOT?: InputMaybe>; @@ -337,53 +172,6 @@ export type CommentThreadWhereUniqueInput = { id?: InputMaybe; }; -export type CommentUpdateManyMutationInput = { - body?: InputMaybe; - createdAt?: InputMaybe; - deletedAt?: InputMaybe; - id?: InputMaybe; - updatedAt?: InputMaybe; -}; - -export type CommentUpdateManyWithWhereWithoutAuthorInput = { - data: CommentUpdateManyMutationInput; - where: CommentScalarWhereInput; -}; - -export type CommentUpdateManyWithoutAuthorNestedInput = { - connect?: InputMaybe>; - connectOrCreate?: InputMaybe>; - create?: InputMaybe>; - createMany?: InputMaybe; - delete?: InputMaybe>; - deleteMany?: InputMaybe>; - disconnect?: InputMaybe>; - set?: InputMaybe>; - update?: InputMaybe>; - updateMany?: InputMaybe>; - upsert?: InputMaybe>; -}; - -export type CommentUpdateWithWhereUniqueWithoutAuthorInput = { - data: CommentUpdateWithoutAuthorInput; - where: CommentWhereUniqueInput; -}; - -export type CommentUpdateWithoutAuthorInput = { - body?: InputMaybe; - commentThread?: InputMaybe; - createdAt?: InputMaybe; - deletedAt?: InputMaybe; - id?: InputMaybe; - updatedAt?: InputMaybe; -}; - -export type CommentUpsertWithWhereUniqueWithoutAuthorInput = { - create: CommentCreateWithoutAuthorInput; - update: CommentUpdateWithoutAuthorInput; - where: CommentWhereUniqueInput; -}; - export type CommentWhereInput = { AND?: InputMaybe>; NOT?: InputMaybe>; @@ -399,10 +187,6 @@ export type CommentWhereInput = { updatedAt?: InputMaybe; }; -export type CommentWhereUniqueInput = { - id?: InputMaybe; -}; - export enum CommentableType { Company = 'Company', Person = 'Person' @@ -441,25 +225,6 @@ export type CompanyCreateInput = { export type CompanyCreateNestedOneWithoutPeopleInput = { connect?: InputMaybe; - connectOrCreate?: InputMaybe; - create?: InputMaybe; -}; - -export type CompanyCreateOrConnectWithoutPeopleInput = { - create: CompanyCreateWithoutPeopleInput; - where: CompanyWhereUniqueInput; -}; - -export type CompanyCreateWithoutPeopleInput = { - accountOwner?: InputMaybe; - address: Scalars['String']; - createdAt?: InputMaybe; - deletedAt?: InputMaybe; - domainName: Scalars['String']; - employees?: InputMaybe; - id: Scalars['String']; - name: Scalars['String']; - updatedAt?: InputMaybe; }; export type CompanyListRelationFilter = { @@ -519,29 +284,6 @@ export type CompanyUpdateInput = { export type CompanyUpdateOneWithoutPeopleNestedInput = { connect?: InputMaybe; - connectOrCreate?: InputMaybe; - create?: InputMaybe; - delete?: InputMaybe; - disconnect?: InputMaybe; - update?: InputMaybe; - upsert?: InputMaybe; -}; - -export type CompanyUpdateWithoutPeopleInput = { - accountOwner?: InputMaybe; - address?: InputMaybe; - createdAt?: InputMaybe; - deletedAt?: InputMaybe; - domainName?: InputMaybe; - employees?: InputMaybe; - id?: InputMaybe; - name?: InputMaybe; - updatedAt?: InputMaybe; -}; - -export type CompanyUpsertWithoutPeopleInput = { - create: CompanyCreateWithoutPeopleInput; - update: CompanyUpdateWithoutPeopleInput; }; export type CompanyWhereInput = { @@ -591,10 +333,6 @@ export type DateTimeNullableFilter = { notIn?: InputMaybe>; }; -export type EnumCommentableTypeFieldUpdateOperationsInput = { - set?: InputMaybe; -}; - export type EnumCommentableTypeFilter = { equals?: InputMaybe; in?: InputMaybe>; @@ -602,6 +340,13 @@ export type EnumCommentableTypeFilter = { notIn?: InputMaybe>; }; +export type EnumPipelineProgressableTypeFilter = { + equals?: InputMaybe; + in?: InputMaybe>; + not?: InputMaybe; + notIn?: InputMaybe>; +}; + export type IntNullableFilter = { equals?: InputMaybe; gt?: InputMaybe; @@ -717,6 +462,13 @@ export type NestedEnumCommentableTypeFilter = { notIn?: InputMaybe>; }; +export type NestedEnumPipelineProgressableTypeFilter = { + equals?: InputMaybe; + in?: InputMaybe>; + not?: InputMaybe; + notIn?: InputMaybe>; +}; + export type NestedIntNullableFilter = { equals?: InputMaybe; gt?: InputMaybe; @@ -768,10 +520,6 @@ export type NullableIntFieldUpdateOperationsInput = { set?: InputMaybe; }; -export type NullableStringFieldUpdateOperationsInput = { - set?: InputMaybe; -}; - export type Person = { __typename?: 'Person'; _commentCount: Scalars['Int']; @@ -803,45 +551,8 @@ export type PersonCreateInput = { updatedAt?: InputMaybe; }; -export type PersonCreateManyCompanyInput = { - city: Scalars['String']; - createdAt?: InputMaybe; - deletedAt?: InputMaybe; - email: Scalars['String']; - firstname: Scalars['String']; - id: Scalars['String']; - lastname: Scalars['String']; - phone: Scalars['String']; - updatedAt?: InputMaybe; -}; - -export type PersonCreateManyCompanyInputEnvelope = { - data: Array; - skipDuplicates?: InputMaybe; -}; - export type PersonCreateNestedManyWithoutCompanyInput = { connect?: InputMaybe>; - connectOrCreate?: InputMaybe>; - create?: InputMaybe>; - createMany?: InputMaybe; -}; - -export type PersonCreateOrConnectWithoutCompanyInput = { - create: PersonCreateWithoutCompanyInput; - where: PersonWhereUniqueInput; -}; - -export type PersonCreateWithoutCompanyInput = { - city: Scalars['String']; - createdAt?: InputMaybe; - deletedAt?: InputMaybe; - email: Scalars['String']; - firstname: Scalars['String']; - id: Scalars['String']; - lastname: Scalars['String']; - phone: Scalars['String']; - updatedAt?: InputMaybe; }; export type PersonListRelationFilter = { @@ -882,22 +593,6 @@ export enum PersonScalarFieldEnum { WorkspaceId = 'workspaceId' } -export type PersonScalarWhereInput = { - AND?: InputMaybe>; - NOT?: InputMaybe>; - OR?: InputMaybe>; - city?: InputMaybe; - companyId?: InputMaybe; - createdAt?: InputMaybe; - deletedAt?: InputMaybe; - email?: InputMaybe; - firstname?: InputMaybe; - id?: InputMaybe; - lastname?: InputMaybe; - phone?: InputMaybe; - updatedAt?: InputMaybe; -}; - export type PersonUpdateInput = { city?: InputMaybe; company?: InputMaybe; @@ -911,58 +606,8 @@ export type PersonUpdateInput = { updatedAt?: InputMaybe; }; -export type PersonUpdateManyMutationInput = { - city?: InputMaybe; - createdAt?: InputMaybe; - deletedAt?: InputMaybe; - email?: InputMaybe; - firstname?: InputMaybe; - id?: InputMaybe; - lastname?: InputMaybe; - phone?: InputMaybe; - updatedAt?: InputMaybe; -}; - -export type PersonUpdateManyWithWhereWithoutCompanyInput = { - data: PersonUpdateManyMutationInput; - where: PersonScalarWhereInput; -}; - export type PersonUpdateManyWithoutCompanyNestedInput = { connect?: InputMaybe>; - connectOrCreate?: InputMaybe>; - create?: InputMaybe>; - createMany?: InputMaybe; - delete?: InputMaybe>; - deleteMany?: InputMaybe>; - disconnect?: InputMaybe>; - set?: InputMaybe>; - update?: InputMaybe>; - updateMany?: InputMaybe>; - upsert?: InputMaybe>; -}; - -export type PersonUpdateWithWhereUniqueWithoutCompanyInput = { - data: PersonUpdateWithoutCompanyInput; - where: PersonWhereUniqueInput; -}; - -export type PersonUpdateWithoutCompanyInput = { - city?: InputMaybe; - createdAt?: InputMaybe; - deletedAt?: InputMaybe; - email?: InputMaybe; - firstname?: InputMaybe; - id?: InputMaybe; - lastname?: InputMaybe; - phone?: InputMaybe; - updatedAt?: InputMaybe; -}; - -export type PersonUpsertWithWhereUniqueWithoutCompanyInput = { - create: PersonCreateWithoutCompanyInput; - update: PersonUpdateWithoutCompanyInput; - where: PersonWhereUniqueInput; }; export type PersonWhereInput = { @@ -998,6 +643,17 @@ export type Pipeline = { updatedAt: Scalars['DateTime']; }; +export type PipelineOrderByWithRelationInput = { + createdAt?: InputMaybe; + deletedAt?: InputMaybe; + icon?: InputMaybe; + id?: InputMaybe; + name?: InputMaybe; + pipelineProgresses?: InputMaybe; + pipelineStages?: InputMaybe; + updatedAt?: InputMaybe; +}; + export type PipelineProgress = { __typename?: 'PipelineProgress'; associableId: Scalars['String']; @@ -1012,11 +668,52 @@ export type PipelineProgress = { updatedAt: Scalars['DateTime']; }; +export type PipelineProgressListRelationFilter = { + every?: InputMaybe; + none?: InputMaybe; + some?: InputMaybe; +}; + +export type PipelineProgressOrderByRelationAggregateInput = { + _count?: InputMaybe; +}; + +export type PipelineProgressWhereInput = { + AND?: InputMaybe>; + NOT?: InputMaybe>; + OR?: InputMaybe>; + associableId?: InputMaybe; + associableType?: InputMaybe; + createdAt?: InputMaybe; + deletedAt?: InputMaybe; + id?: InputMaybe; + pipeline?: InputMaybe; + pipelineId?: InputMaybe; + pipelineStage?: InputMaybe; + pipelineStageId?: InputMaybe; + updatedAt?: InputMaybe; +}; + export enum PipelineProgressableType { Company = 'Company', Person = 'Person' } +export type PipelineRelationFilter = { + is?: InputMaybe; + isNot?: InputMaybe; +}; + +export enum PipelineScalarFieldEnum { + CreatedAt = 'createdAt', + DeletedAt = 'deletedAt', + Icon = 'icon', + Id = 'id', + Name = 'name', + UpdatedAt = 'updatedAt', + WorkspaceId = 'workspaceId' +} + export type PipelineStage = { __typename?: 'PipelineStage'; color: Scalars['String']; @@ -1031,10 +728,90 @@ export type PipelineStage = { updatedAt: Scalars['DateTime']; }; +export type PipelineStageListRelationFilter = { + every?: InputMaybe; + none?: InputMaybe; + some?: InputMaybe; +}; + +export type PipelineStageOrderByRelationAggregateInput = { + _count?: InputMaybe; +}; + +export type PipelineStageOrderByWithRelationInput = { + color?: InputMaybe; + createdAt?: InputMaybe; + deletedAt?: InputMaybe; + id?: InputMaybe; + name?: InputMaybe; + pipeline?: InputMaybe; + pipelineId?: InputMaybe; + pipelineProgresses?: InputMaybe; + type?: InputMaybe; + updatedAt?: InputMaybe; +}; + +export type PipelineStageRelationFilter = { + is?: InputMaybe; + isNot?: InputMaybe; +}; + +export enum PipelineStageScalarFieldEnum { + Color = 'color', + CreatedAt = 'createdAt', + DeletedAt = 'deletedAt', + Id = 'id', + Name = 'name', + PipelineId = 'pipelineId', + Type = 'type', + UpdatedAt = 'updatedAt', + WorkspaceId = 'workspaceId' +} + +export type PipelineStageWhereInput = { + AND?: InputMaybe>; + NOT?: InputMaybe>; + OR?: InputMaybe>; + color?: InputMaybe; + createdAt?: InputMaybe; + deletedAt?: InputMaybe; + id?: InputMaybe; + name?: InputMaybe; + pipeline?: InputMaybe; + pipelineId?: InputMaybe; + pipelineProgresses?: InputMaybe; + type?: InputMaybe; + updatedAt?: InputMaybe; +}; + +export type PipelineStageWhereUniqueInput = { + id?: InputMaybe; +}; + +export type PipelineWhereInput = { + AND?: InputMaybe>; + NOT?: InputMaybe>; + OR?: InputMaybe>; + createdAt?: InputMaybe; + deletedAt?: InputMaybe; + icon?: InputMaybe; + id?: InputMaybe; + name?: InputMaybe; + pipelineProgresses?: InputMaybe; + pipelineStages?: InputMaybe; + updatedAt?: InputMaybe; +}; + +export type PipelineWhereUniqueInput = { + id?: InputMaybe; +}; + export type Query = { __typename?: 'Query'; findManyCompany: Array; findManyPerson: Array; + findManyPipeline: Array; + findManyPipelineStage: Array; findManyUser: Array; }; @@ -1059,6 +836,26 @@ export type QueryFindManyPersonArgs = { }; +export type QueryFindManyPipelineArgs = { + cursor?: InputMaybe; + distinct?: InputMaybe>; + orderBy?: InputMaybe>; + skip?: InputMaybe; + take?: InputMaybe; + where?: InputMaybe; +}; + + +export type QueryFindManyPipelineStageArgs = { + cursor?: InputMaybe; + distinct?: InputMaybe>; + orderBy?: InputMaybe>; + skip?: InputMaybe; + take?: InputMaybe; + where?: InputMaybe; +}; + + export type QueryFindManyUserArgs = { cursor?: InputMaybe; distinct?: InputMaybe>; @@ -1149,31 +946,6 @@ export type UserCreateNestedOneWithoutCommentsInput = { export type UserCreateNestedOneWithoutCompaniesInput = { connect?: InputMaybe; - connectOrCreate?: InputMaybe; - create?: InputMaybe; -}; - -export type UserCreateOrConnectWithoutCompaniesInput = { - create: UserCreateWithoutCompaniesInput; - where: UserWhereUniqueInput; -}; - -export type UserCreateWithoutCompaniesInput = { - avatarUrl?: InputMaybe; - comments?: InputMaybe; - createdAt?: InputMaybe; - deletedAt?: InputMaybe; - disabled?: InputMaybe; - displayName: Scalars['String']; - email: Scalars['String']; - emailVerified?: InputMaybe; - id: Scalars['String']; - lastSeen?: InputMaybe; - locale: Scalars['String']; - metadata?: InputMaybe; - passwordHash?: InputMaybe; - phoneNumber?: InputMaybe; - updatedAt?: InputMaybe; }; export type UserOrderByWithRelationInput = { @@ -1219,35 +991,6 @@ export enum UserScalarFieldEnum { export type UserUpdateOneWithoutCompaniesNestedInput = { connect?: InputMaybe; - connectOrCreate?: InputMaybe; - create?: InputMaybe; - delete?: InputMaybe; - disconnect?: InputMaybe; - update?: InputMaybe; - upsert?: InputMaybe; -}; - -export type UserUpdateWithoutCompaniesInput = { - avatarUrl?: InputMaybe; - comments?: InputMaybe; - createdAt?: InputMaybe; - deletedAt?: InputMaybe; - disabled?: InputMaybe; - displayName?: InputMaybe; - email?: InputMaybe; - emailVerified?: InputMaybe; - id?: InputMaybe; - lastSeen?: InputMaybe; - locale?: InputMaybe; - metadata?: InputMaybe; - passwordHash?: InputMaybe; - phoneNumber?: InputMaybe; - updatedAt?: InputMaybe; -}; - -export type UserUpsertWithoutCompaniesInput = { - create: UserCreateWithoutCompaniesInput; - update: UserUpdateWithoutCompaniesInput; }; export type UserWhereInput = { diff --git a/server/src/api/@generated/company/company-create-nested-one-without-people.input.ts b/server/src/api/@generated/company/company-create-nested-one-without-people.input.ts index a4edb9de7d..ddf7c3c47c 100644 --- a/server/src/api/@generated/company/company-create-nested-one-without-people.input.ts +++ b/server/src/api/@generated/company/company-create-nested-one-without-people.input.ts @@ -1,18 +1,17 @@ import { Field } from '@nestjs/graphql'; import { InputType } from '@nestjs/graphql'; import { CompanyCreateWithoutPeopleInput } from './company-create-without-people.input'; -import { Type } from 'class-transformer'; +import { HideField } from '@nestjs/graphql'; import { CompanyCreateOrConnectWithoutPeopleInput } from './company-create-or-connect-without-people.input'; import { CompanyWhereUniqueInput } from './company-where-unique.input'; +import { Type } from 'class-transformer'; @InputType() export class CompanyCreateNestedOneWithoutPeopleInput { - @Field(() => CompanyCreateWithoutPeopleInput, { nullable: true }) - @Type(() => CompanyCreateWithoutPeopleInput) + @HideField() create?: CompanyCreateWithoutPeopleInput; - @Field(() => CompanyCreateOrConnectWithoutPeopleInput, { nullable: true }) - @Type(() => CompanyCreateOrConnectWithoutPeopleInput) + @HideField() connectOrCreate?: CompanyCreateOrConnectWithoutPeopleInput; @Field(() => CompanyWhereUniqueInput, { nullable: true }) diff --git a/server/src/api/@generated/company/company-update-one-without-people-nested.input.ts b/server/src/api/@generated/company/company-update-one-without-people-nested.input.ts index d94d96db97..c7286d4b3e 100644 --- a/server/src/api/@generated/company/company-update-one-without-people-nested.input.ts +++ b/server/src/api/@generated/company/company-update-one-without-people-nested.input.ts @@ -1,37 +1,34 @@ import { Field } from '@nestjs/graphql'; import { InputType } from '@nestjs/graphql'; import { CompanyCreateWithoutPeopleInput } from './company-create-without-people.input'; -import { Type } from 'class-transformer'; +import { HideField } from '@nestjs/graphql'; import { CompanyCreateOrConnectWithoutPeopleInput } from './company-create-or-connect-without-people.input'; import { CompanyUpsertWithoutPeopleInput } from './company-upsert-without-people.input'; import { CompanyWhereUniqueInput } from './company-where-unique.input'; +import { Type } from 'class-transformer'; import { CompanyUpdateWithoutPeopleInput } from './company-update-without-people.input'; @InputType() export class CompanyUpdateOneWithoutPeopleNestedInput { - @Field(() => CompanyCreateWithoutPeopleInput, { nullable: true }) - @Type(() => CompanyCreateWithoutPeopleInput) + @HideField() create?: CompanyCreateWithoutPeopleInput; - @Field(() => CompanyCreateOrConnectWithoutPeopleInput, { nullable: true }) - @Type(() => CompanyCreateOrConnectWithoutPeopleInput) + @HideField() connectOrCreate?: CompanyCreateOrConnectWithoutPeopleInput; - @Field(() => CompanyUpsertWithoutPeopleInput, { nullable: true }) - @Type(() => CompanyUpsertWithoutPeopleInput) + @HideField() upsert?: CompanyUpsertWithoutPeopleInput; - @Field(() => Boolean, { nullable: true }) + @HideField() disconnect?: boolean; - @Field(() => Boolean, { nullable: true }) + @HideField() delete?: boolean; @Field(() => CompanyWhereUniqueInput, { nullable: true }) @Type(() => CompanyWhereUniqueInput) connect?: CompanyWhereUniqueInput; - @Field(() => CompanyUpdateWithoutPeopleInput, { nullable: true }) - @Type(() => CompanyUpdateWithoutPeopleInput) + @HideField() update?: CompanyUpdateWithoutPeopleInput; } diff --git a/server/src/api/@generated/person/person-create-nested-many-without-company.input.ts b/server/src/api/@generated/person/person-create-nested-many-without-company.input.ts index c864b511de..d460f20233 100644 --- a/server/src/api/@generated/person/person-create-nested-many-without-company.input.ts +++ b/server/src/api/@generated/person/person-create-nested-many-without-company.input.ts @@ -1,23 +1,21 @@ import { Field } from '@nestjs/graphql'; import { InputType } from '@nestjs/graphql'; import { PersonCreateWithoutCompanyInput } from './person-create-without-company.input'; -import { Type } from 'class-transformer'; +import { HideField } from '@nestjs/graphql'; import { PersonCreateOrConnectWithoutCompanyInput } from './person-create-or-connect-without-company.input'; import { PersonCreateManyCompanyInputEnvelope } from './person-create-many-company-input-envelope.input'; import { PersonWhereUniqueInput } from './person-where-unique.input'; +import { Type } from 'class-transformer'; @InputType() export class PersonCreateNestedManyWithoutCompanyInput { - @Field(() => [PersonCreateWithoutCompanyInput], { nullable: true }) - @Type(() => PersonCreateWithoutCompanyInput) + @HideField() create?: Array; - @Field(() => [PersonCreateOrConnectWithoutCompanyInput], { nullable: true }) - @Type(() => PersonCreateOrConnectWithoutCompanyInput) + @HideField() connectOrCreate?: Array; - @Field(() => PersonCreateManyCompanyInputEnvelope, { nullable: true }) - @Type(() => PersonCreateManyCompanyInputEnvelope) + @HideField() createMany?: PersonCreateManyCompanyInputEnvelope; @Field(() => [PersonWhereUniqueInput], { nullable: true }) diff --git a/server/src/api/@generated/person/person-update-many-without-company-nested.input.ts b/server/src/api/@generated/person/person-update-many-without-company-nested.input.ts index bcf823e85e..8cb3630c37 100644 --- a/server/src/api/@generated/person/person-update-many-without-company-nested.input.ts +++ b/server/src/api/@generated/person/person-update-many-without-company-nested.input.ts @@ -1,64 +1,49 @@ import { Field } from '@nestjs/graphql'; import { InputType } from '@nestjs/graphql'; import { PersonCreateWithoutCompanyInput } from './person-create-without-company.input'; -import { Type } from 'class-transformer'; +import { HideField } from '@nestjs/graphql'; import { PersonCreateOrConnectWithoutCompanyInput } from './person-create-or-connect-without-company.input'; import { PersonUpsertWithWhereUniqueWithoutCompanyInput } from './person-upsert-with-where-unique-without-company.input'; import { PersonCreateManyCompanyInputEnvelope } from './person-create-many-company-input-envelope.input'; import { PersonWhereUniqueInput } from './person-where-unique.input'; +import { Type } from 'class-transformer'; import { PersonUpdateWithWhereUniqueWithoutCompanyInput } from './person-update-with-where-unique-without-company.input'; import { PersonUpdateManyWithWhereWithoutCompanyInput } from './person-update-many-with-where-without-company.input'; import { PersonScalarWhereInput } from './person-scalar-where.input'; @InputType() export class PersonUpdateManyWithoutCompanyNestedInput { - @Field(() => [PersonCreateWithoutCompanyInput], { nullable: true }) - @Type(() => PersonCreateWithoutCompanyInput) + @HideField() create?: Array; - @Field(() => [PersonCreateOrConnectWithoutCompanyInput], { nullable: true }) - @Type(() => PersonCreateOrConnectWithoutCompanyInput) + @HideField() connectOrCreate?: Array; - @Field(() => [PersonUpsertWithWhereUniqueWithoutCompanyInput], { - nullable: true, - }) - @Type(() => PersonUpsertWithWhereUniqueWithoutCompanyInput) + @HideField() upsert?: Array; - @Field(() => PersonCreateManyCompanyInputEnvelope, { nullable: true }) - @Type(() => PersonCreateManyCompanyInputEnvelope) + @HideField() createMany?: PersonCreateManyCompanyInputEnvelope; - @Field(() => [PersonWhereUniqueInput], { nullable: true }) - @Type(() => PersonWhereUniqueInput) + @HideField() set?: Array; - @Field(() => [PersonWhereUniqueInput], { nullable: true }) - @Type(() => PersonWhereUniqueInput) + @HideField() disconnect?: Array; - @Field(() => [PersonWhereUniqueInput], { nullable: true }) - @Type(() => PersonWhereUniqueInput) + @HideField() delete?: Array; @Field(() => [PersonWhereUniqueInput], { nullable: true }) @Type(() => PersonWhereUniqueInput) connect?: Array; - @Field(() => [PersonUpdateWithWhereUniqueWithoutCompanyInput], { - nullable: true, - }) - @Type(() => PersonUpdateWithWhereUniqueWithoutCompanyInput) + @HideField() update?: Array; - @Field(() => [PersonUpdateManyWithWhereWithoutCompanyInput], { - nullable: true, - }) - @Type(() => PersonUpdateManyWithWhereWithoutCompanyInput) + @HideField() updateMany?: Array; - @Field(() => [PersonScalarWhereInput], { nullable: true }) - @Type(() => PersonScalarWhereInput) + @HideField() deleteMany?: Array; } diff --git a/server/src/api/@generated/user/user-create-nested-one-without-companies.input.ts b/server/src/api/@generated/user/user-create-nested-one-without-companies.input.ts index 4b756fcb9c..413d1b36cc 100644 --- a/server/src/api/@generated/user/user-create-nested-one-without-companies.input.ts +++ b/server/src/api/@generated/user/user-create-nested-one-without-companies.input.ts @@ -1,18 +1,17 @@ import { Field } from '@nestjs/graphql'; import { InputType } from '@nestjs/graphql'; import { UserCreateWithoutCompaniesInput } from './user-create-without-companies.input'; -import { Type } from 'class-transformer'; +import { HideField } from '@nestjs/graphql'; import { UserCreateOrConnectWithoutCompaniesInput } from './user-create-or-connect-without-companies.input'; import { UserWhereUniqueInput } from './user-where-unique.input'; +import { Type } from 'class-transformer'; @InputType() export class UserCreateNestedOneWithoutCompaniesInput { - @Field(() => UserCreateWithoutCompaniesInput, { nullable: true }) - @Type(() => UserCreateWithoutCompaniesInput) + @HideField() create?: UserCreateWithoutCompaniesInput; - @Field(() => UserCreateOrConnectWithoutCompaniesInput, { nullable: true }) - @Type(() => UserCreateOrConnectWithoutCompaniesInput) + @HideField() connectOrCreate?: UserCreateOrConnectWithoutCompaniesInput; @Field(() => UserWhereUniqueInput, { nullable: true }) diff --git a/server/src/api/@generated/user/user-update-one-without-companies-nested.input.ts b/server/src/api/@generated/user/user-update-one-without-companies-nested.input.ts index 0b142e1f87..9b0d06cad4 100644 --- a/server/src/api/@generated/user/user-update-one-without-companies-nested.input.ts +++ b/server/src/api/@generated/user/user-update-one-without-companies-nested.input.ts @@ -1,37 +1,34 @@ import { Field } from '@nestjs/graphql'; import { InputType } from '@nestjs/graphql'; import { UserCreateWithoutCompaniesInput } from './user-create-without-companies.input'; -import { Type } from 'class-transformer'; +import { HideField } from '@nestjs/graphql'; import { UserCreateOrConnectWithoutCompaniesInput } from './user-create-or-connect-without-companies.input'; import { UserUpsertWithoutCompaniesInput } from './user-upsert-without-companies.input'; import { UserWhereUniqueInput } from './user-where-unique.input'; +import { Type } from 'class-transformer'; import { UserUpdateWithoutCompaniesInput } from './user-update-without-companies.input'; @InputType() export class UserUpdateOneWithoutCompaniesNestedInput { - @Field(() => UserCreateWithoutCompaniesInput, { nullable: true }) - @Type(() => UserCreateWithoutCompaniesInput) + @HideField() create?: UserCreateWithoutCompaniesInput; - @Field(() => UserCreateOrConnectWithoutCompaniesInput, { nullable: true }) - @Type(() => UserCreateOrConnectWithoutCompaniesInput) + @HideField() connectOrCreate?: UserCreateOrConnectWithoutCompaniesInput; - @Field(() => UserUpsertWithoutCompaniesInput, { nullable: true }) - @Type(() => UserUpsertWithoutCompaniesInput) + @HideField() upsert?: UserUpsertWithoutCompaniesInput; - @Field(() => Boolean, { nullable: true }) + @HideField() disconnect?: boolean; - @Field(() => Boolean, { nullable: true }) + @HideField() delete?: boolean; @Field(() => UserWhereUniqueInput, { nullable: true }) @Type(() => UserWhereUniqueInput) connect?: UserWhereUniqueInput; - @Field(() => UserUpdateWithoutCompaniesInput, { nullable: true }) - @Type(() => UserUpdateWithoutCompaniesInput) + @HideField() update?: UserUpdateWithoutCompaniesInput; } diff --git a/server/src/api/api.module.ts b/server/src/api/api.module.ts index c702b93c9e..a451aff9e6 100644 --- a/server/src/api/api.module.ts +++ b/server/src/api/api.module.ts @@ -22,6 +22,7 @@ import { CompanyRelationsResolver } from './resolvers/relations/company-relation import { CommentThreadRelationsResolver } from './resolvers/relations/comment-thread-relations.resolver'; import { PipelineRelationsResolver } from './resolvers/relations/pipeline-relations.resolver'; import { PipelineStageRelationsResolver } from './resolvers/relations/pipeline-stage-relations.resolver'; +import { GraphQLError } from 'graphql'; @Module({ imports: [ @@ -29,6 +30,10 @@ import { PipelineStageRelationsResolver } from './resolvers/relations/pipeline-s context: ({ req }) => ({ req }), driver: ApolloDriver, autoSchemaFile: true, + formatError: (error: GraphQLError) => { + error.extensions.stacktrace = undefined; + return error; + }, }), AuthModule, PrismaModule, diff --git a/server/src/api/resolvers/company.resolver.ts b/server/src/api/resolvers/company.resolver.ts index c00bb8fd22..8d93e82bad 100644 --- a/server/src/api/resolvers/company.resolver.ts +++ b/server/src/api/resolvers/company.resolver.ts @@ -11,10 +11,12 @@ import { AffectedRows } from '../@generated/prisma/affected-rows.output'; import { DeleteManyCompanyArgs } from '../@generated/company/delete-many-company.args'; import { Workspace } from '@prisma/client'; import { ArgsService } from './services/args.service'; -import { CheckWorkspaceOwnership } from 'src/auth/guards/check-workspace-ownership.guard'; import { Prisma } from '@prisma/client'; +import { UpdateOneGuard } from './guards/update-one.guard'; +import { DeleteManyGuard } from './guards/delete-many.guard'; +import { CreateOneGuard } from './guards/create-one.guard'; -@UseGuards(JwtAuthGuard, CheckWorkspaceOwnership) +@UseGuards(JwtAuthGuard) @Resolver(() => Company) export class CompanyResolver { constructor( @@ -35,6 +37,7 @@ export class CompanyResolver { return this.prismaService.company.findMany(preparedArgs); } + @UseGuards(UpdateOneGuard) @Mutation(() => Company, { nullable: true, }) @@ -50,6 +53,7 @@ export class CompanyResolver { } satisfies UpdateOneCompanyArgs as Prisma.CompanyUpdateArgs); } + @UseGuards(DeleteManyGuard) @Mutation(() => AffectedRows, { nullable: false, }) @@ -61,6 +65,7 @@ export class CompanyResolver { }); } + @UseGuards(CreateOneGuard) @Mutation(() => Company, { nullable: false, }) diff --git a/server/src/api/resolvers/exception-filters/exception.filter.ts b/server/src/api/resolvers/exception-filters/exception.filter.ts new file mode 100644 index 0000000000..71a7e77650 --- /dev/null +++ b/server/src/api/resolvers/exception-filters/exception.filter.ts @@ -0,0 +1,18 @@ +import { Catch, HttpException } from '@nestjs/common'; +import { GqlExceptionFilter } from '@nestjs/graphql'; +import { Prisma } from '@prisma/client'; +import { GraphQLError } from 'graphql'; + +@Catch() +export class ExceptionFilter implements GqlExceptionFilter { + catch(exception: HttpException) { + if (exception instanceof Prisma.PrismaClientValidationError) { + throw new GraphQLError('Invalid request', { + extensions: { + code: 'INVALID_REQUEST', + }, + }); + } + return exception; + } +} diff --git a/server/src/api/resolvers/guards/create-one.guard.ts b/server/src/api/resolvers/guards/create-one.guard.ts new file mode 100644 index 0000000000..5be45e7a9e --- /dev/null +++ b/server/src/api/resolvers/guards/create-one.guard.ts @@ -0,0 +1,12 @@ +import { CanActivate, Injectable } from '@nestjs/common'; +import { PrismaService } from 'src/database/prisma.service'; + +@Injectable() +export class CreateOneGuard implements CanActivate { + constructor(private prismaService: PrismaService) {} + + async canActivate(): Promise { + // TODO + return true; + } +} diff --git a/server/src/api/resolvers/guards/delete-many.guard.ts b/server/src/api/resolvers/guards/delete-many.guard.ts new file mode 100644 index 0000000000..8beeb3f7a8 --- /dev/null +++ b/server/src/api/resolvers/guards/delete-many.guard.ts @@ -0,0 +1,12 @@ +import { CanActivate, Injectable } from '@nestjs/common'; +import { PrismaService } from 'src/database/prisma.service'; + +@Injectable() +export class DeleteManyGuard implements CanActivate { + constructor(private prismaService: PrismaService) {} + + async canActivate(): Promise { + // TODO + return true; + } +} diff --git a/server/src/api/resolvers/guards/update-one.guard.ts b/server/src/api/resolvers/guards/update-one.guard.ts new file mode 100644 index 0000000000..3ec28e8e17 --- /dev/null +++ b/server/src/api/resolvers/guards/update-one.guard.ts @@ -0,0 +1,50 @@ +import { + CanActivate, + ExecutionContext, + HttpException, + HttpStatus, + Injectable, +} from '@nestjs/common'; +import { GqlExecutionContext } from '@nestjs/graphql'; +import { PrismaService } from 'src/database/prisma.service'; + +@Injectable() +export class UpdateOneGuard implements CanActivate { + constructor(private prismaService: PrismaService) {} + + async canActivate(context: ExecutionContext): Promise { + const gqlContext = GqlExecutionContext.create(context); + const request = gqlContext.getContext().req; + const entity = gqlContext.getArgByIndex(3).returnType?.name; + const args = gqlContext.getArgs(); + + console.log(args.data); + if (!entity || !args.where?.id) { + throw new HttpException( + { reason: 'Invalid Request' }, + HttpStatus.BAD_REQUEST, + ); + } + + const object = await this.prismaService[entity].findUniqueOrThrow({ + where: { id: args.where.id }, + }); + + if (!object) { + throw new HttpException( + { reason: 'Record not found' }, + HttpStatus.NOT_FOUND, + ); + } + + const workspace = await request.workspace; + + if (object.workspaceId !== workspace.id) { + throw new HttpException( + { reason: 'Record not found' }, + HttpStatus.NOT_FOUND, + ); + } + return true; + } +} diff --git a/server/src/api/resolvers/person.resolver.ts b/server/src/api/resolvers/person.resolver.ts index a94173594a..aa0087413f 100644 --- a/server/src/api/resolvers/person.resolver.ts +++ b/server/src/api/resolvers/person.resolver.ts @@ -11,10 +11,12 @@ import { DeleteManyPersonArgs } from '../@generated/person/delete-many-person.ar import { Workspace } from '../@generated/workspace/workspace.model'; import { AuthWorkspace } from './decorators/auth-workspace.decorator'; import { ArgsService } from './services/args.service'; -import { CheckWorkspaceOwnership } from 'src/auth/guards/check-workspace-ownership.guard'; import { Prisma } from '@prisma/client'; +import { UpdateOneGuard } from './guards/update-one.guard'; +import { DeleteManyGuard } from './guards/delete-many.guard'; +import { CreateOneGuard } from './guards/create-one.guard'; -@UseGuards(JwtAuthGuard, CheckWorkspaceOwnership) +@UseGuards(JwtAuthGuard) @Resolver(() => Person) export class PersonResolver { constructor( @@ -39,6 +41,7 @@ export class PersonResolver { }); } + @UseGuards(UpdateOneGuard) @Mutation(() => Person, { nullable: true, }) @@ -54,6 +57,7 @@ export class PersonResolver { } satisfies UpdateOnePersonArgs as Prisma.PersonUpdateArgs); } + @UseGuards(DeleteManyGuard) @Mutation(() => AffectedRows, { nullable: false, }) @@ -65,6 +69,7 @@ export class PersonResolver { }); } + @UseGuards(CreateOneGuard) @Mutation(() => Person, { nullable: false, }) diff --git a/server/src/api/resolvers/user.resolver.ts b/server/src/api/resolvers/user.resolver.ts index cfb7d44628..b2daa9c560 100644 --- a/server/src/api/resolvers/user.resolver.ts +++ b/server/src/api/resolvers/user.resolver.ts @@ -1,23 +1,20 @@ import { Resolver, Query, Args } from '@nestjs/graphql'; import { PrismaService } from 'src/database/prisma.service'; -import { UseGuards } from '@nestjs/common'; -import { JwtAuthGuard } from 'src/auth/guards/jwt.auth.guard'; +import { UseFilters, UseGuards } from '@nestjs/common'; import { User } from '../@generated/user/user.model'; import { FindManyUserArgs } from '../@generated/user/find-many-user.args'; import { Workspace } from '@prisma/client'; import { AuthWorkspace } from './decorators/auth-workspace.decorator'; -import { ArgsService } from './services/args.service'; -import { CheckWorkspaceOwnership } from 'src/auth/guards/check-workspace-ownership.guard'; +import { ExceptionFilter } from './exception-filters/exception.filter'; +import { JwtAuthGuard } from 'src/auth/guards/jwt.auth.guard'; -@UseGuards(JwtAuthGuard, CheckWorkspaceOwnership) +@UseGuards(JwtAuthGuard) @Resolver(() => User) export class UserResolver { - constructor( - private readonly prismaService: PrismaService, - private readonly argsService: ArgsService, - ) {} + constructor(private readonly prismaService: PrismaService) {} + @UseFilters(ExceptionFilter) @Query(() => [User], { nullable: false, }) diff --git a/server/src/app.controller.spec.ts b/server/src/app.controller.spec.ts deleted file mode 100644 index 894698b7d4..0000000000 --- a/server/src/app.controller.spec.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { AppController } from './app.controller'; -import { AppService } from './app.service'; - -describe('AppController', () => { - let appController: AppController; - - beforeEach(async () => { - const app: TestingModule = await Test.createTestingModule({ - controllers: [AppController], - providers: [AppService], - }).compile(); - - appController = app.get(AppController); - }); - - it('should be defined', () => { - expect(appController).toBeDefined(); - }); -}); diff --git a/server/src/app.controller.ts b/server/src/app.controller.ts deleted file mode 100644 index c73d6d8de8..0000000000 --- a/server/src/app.controller.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Controller } from '@nestjs/common'; -import { AppService } from './app.service'; - -@Controller() -export class AppController { - constructor(private readonly appService: AppService) {} -} diff --git a/server/src/app.module.ts b/server/src/app.module.ts index f9b091f4d9..2fbd1fcbc7 100644 --- a/server/src/app.module.ts +++ b/server/src/app.module.ts @@ -1,5 +1,4 @@ import { Module } from '@nestjs/common'; -import { AppController } from './app.controller'; import { AppService } from './app.service'; import { HealthController } from './health.controller'; import { TerminusModule } from '@nestjs/terminus'; @@ -9,7 +8,7 @@ import { ConfigModule } from '@nestjs/config'; import { ApiModule } from './api/api.module'; @Module({ imports: [ConfigModule.forRoot({}), TerminusModule, AuthModule, ApiModule], - controllers: [AppController, HealthController], + controllers: [HealthController], providers: [AppService], }) export class AppModule {} diff --git a/server/src/auth/auth.module.ts b/server/src/auth/auth.module.ts index 9bdead790a..644da53c2e 100644 --- a/server/src/auth/auth.module.ts +++ b/server/src/auth/auth.module.ts @@ -5,10 +5,7 @@ import { JwtAuthStrategy } from './strategies/jwt.auth.strategy'; import { AuthService } from './services/auth.service'; import { GoogleAuthController } from './google.auth.controller'; import { GoogleStrategy } from './strategies/google.auth.strategy'; -import { AuthController } from './auth.controller'; -import { UserRepository } from 'src/entities/user/user.repository'; -import { WorkspaceRepository } from 'src/entities/workspace/workspace.repository'; -import { RefreshTokenRepository } from 'src/entities/refresh-token/refresh-token.repository'; +import { TokenController } from './token.controller'; import { PrismaService } from 'src/database/prisma.service'; const jwtModule = JwtModule.registerAsync({ @@ -26,16 +23,8 @@ const jwtModule = JwtModule.registerAsync({ @Module({ imports: [jwtModule, ConfigModule.forRoot({})], - controllers: [GoogleAuthController, AuthController], - providers: [ - AuthService, - JwtAuthStrategy, - GoogleStrategy, - UserRepository, - WorkspaceRepository, - RefreshTokenRepository, - PrismaService, - ], + controllers: [GoogleAuthController, TokenController], + providers: [AuthService, JwtAuthStrategy, GoogleStrategy, PrismaService], exports: [jwtModule], }) export class AuthModule {} diff --git a/server/src/auth/google.auth.controller.ts b/server/src/auth/google.auth.controller.ts index a813bd9f4f..5ab5c79563 100644 --- a/server/src/auth/google.auth.controller.ts +++ b/server/src/auth/google.auth.controller.ts @@ -26,7 +26,7 @@ export class GoogleAuthController { @Get('redirect') @UseGuards(AuthGuard('google')) async googleAuthRedirect(@Req() req: GoogleRequest, @Res() res: Response) { - const user = await this.authService.upsertUser(req.user); + const user = await this.authService.createUser(req.user); if (!user) { throw new HttpException( diff --git a/server/src/auth/guards/check-workspace-ownership.guard.ts b/server/src/auth/guards/check-workspace-ownership.guard.ts deleted file mode 100644 index a762bbd024..0000000000 --- a/server/src/auth/guards/check-workspace-ownership.guard.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { - CanActivate, - ExecutionContext, - HttpException, - HttpStatus, - Injectable, -} from '@nestjs/common'; -import { GqlExecutionContext } from '@nestjs/graphql'; -import { Request } from 'express'; -import { PrismaService } from 'src/database/prisma.service'; - -type OperationEntity = { - operation?: string; - entity?: string; -}; - -@Injectable() -export class CheckWorkspaceOwnership implements CanActivate { - constructor(private prismaService: PrismaService) {} - - async canActivate(context: ExecutionContext): Promise { - const gqlContext = GqlExecutionContext.create(context); - const request = gqlContext.getContext().req; - - const { operation, entity } = this.fetchOperationAndEntity(request); - const variables = request.body.variables; - const workspace = await request.workspace; - - if (!entity || !operation) { - return false; - } - - if (operation === 'updateOne') { - const object = await this.prismaService[entity].findUniqueOrThrow({ - where: { id: variables.id }, - }); - - if (!object) { - throw new HttpException( - { reason: 'Record not found' }, - HttpStatus.NOT_FOUND, - ); - } - if (object.workspaceId !== workspace.id) { - throw new HttpException( - { reason: 'Record not found' }, - HttpStatus.NOT_FOUND, - ); - } - return true; - } - - if (operation === 'deleteMany') { - // TODO: write this logic - return true; - } - - if (operation === 'findMany') { - return true; - } - - if (operation === 'createOne') { - return true; - } - - return false; - } - - private fetchOperationAndEntity(request: Request): OperationEntity { - if (!request.body.operationName) { - return { operation: undefined, entity: undefined }; - } - - const regex = - /(updateOne|deleteMany|createOne|findMany)(Person|Company|User)/i; - const match = request.body.query.match(regex); - if (match) { - return { - operation: match[1], - entity: match[2].toLowerCase(), - }; - } - return { operation: undefined, entity: undefined }; - } -} diff --git a/server/src/auth/services/auth.service.ts b/server/src/auth/services/auth.service.ts index a232385d3b..76acedae3f 100644 --- a/server/src/auth/services/auth.service.ts +++ b/server/src/auth/services/auth.service.ts @@ -2,11 +2,9 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { JwtPayload } from '../strategies/jwt.auth.strategy'; import { ConfigService } from '@nestjs/config'; -import { UserRepository } from 'src/entities/user/user.repository'; -import { WorkspaceRepository } from 'src/entities/workspace/workspace.repository'; -import { RefreshTokenRepository } from 'src/entities/refresh-token/refresh-token.repository'; import { v4 } from 'uuid'; import { RefreshToken, User } from '@prisma/client'; +import { PrismaService } from 'src/database/prisma.service'; export type UserPayload = { firstName: string; @@ -19,12 +17,10 @@ export class AuthService { constructor( private jwtService: JwtService, private configService: ConfigService, - private userRepository: UserRepository, - private workspaceRepository: WorkspaceRepository, - private refreshTokenRepository: RefreshTokenRepository, + private prismaService: PrismaService, ) {} - async upsertUser(rawUser: UserPayload) { + async createUser(rawUser: UserPayload) { if (!rawUser.email) { throw new HttpException( { reason: 'Email is missing' }, @@ -48,7 +44,7 @@ export class AuthService { ); } - const workspace = await this.workspaceRepository.findUnique({ + const workspace = await this.prismaService.workspace.findUnique({ where: { domainName: emailDomain }, }); @@ -59,50 +55,66 @@ export class AuthService { ); } - const user = await this.userRepository.upsertUser({ - data: { - id: v4(), + const user = await this.prismaService.user.upsert({ + where: { email: rawUser.email, + }, + create: { + id: v4(), displayName: rawUser.firstName + ' ' + rawUser.lastName, + email: rawUser.email, locale: 'en', }, - workspaceId: workspace.id, + update: {}, }); - await this.userRepository.upsertWorkspaceMember({ - data: { + await this.prismaService.workspaceMember.upsert({ + where: { + userId: user.id, + }, + create: { id: v4(), userId: user.id, workspaceId: workspace.id, }, + update: {}, }); return user; } async generateAccessToken(refreshToken: string): Promise { - const refreshTokenObject = await this.refreshTokenRepository.findFirst({ + const refreshTokenObject = await this.prismaService.refreshToken.findFirst({ where: { refreshToken: refreshToken }, }); if (!refreshTokenObject) { - return; + throw new HttpException( + { reason: 'Invalid Refresh token' }, + HttpStatus.FORBIDDEN, + ); } - const user = await this.userRepository.findUnique({ + const user = await this.prismaService.user.findUnique({ where: { id: refreshTokenObject.userId }, }); if (!user) { - return; + throw new HttpException( + { reason: 'Refresh token is not associated to a valid user' }, + HttpStatus.FORBIDDEN, + ); } - const workspace = await this.workspaceRepository.findFirst({ + const workspace = await this.prismaService.workspace.findFirst({ where: { workspaceMember: { some: { userId: user.id } } }, }); if (!workspace) { - return; + throw new HttpException( + { reason: 'Refresh token is not associated to a valid workspace' }, + HttpStatus.FORBIDDEN, + ); } const payload: JwtPayload = { @@ -113,12 +125,16 @@ export class AuthService { } async registerRefreshToken(user: User): Promise { - const refreshToken = await this.refreshTokenRepository.upsertRefreshToken({ - data: { + const refreshToken = await this.prismaService.refreshToken.upsert({ + where: { + id: user.id, + }, + create: { id: v4(), userId: user.id, refreshToken: v4(), }, + update: {}, }); return refreshToken; diff --git a/server/src/auth/auth.controller.ts b/server/src/auth/token.controller.ts similarity index 95% rename from server/src/auth/auth.controller.ts rename to server/src/auth/token.controller.ts index 8e2c2f894c..b06cc9f82b 100644 --- a/server/src/auth/auth.controller.ts +++ b/server/src/auth/token.controller.ts @@ -3,7 +3,7 @@ import { Request, Response } from 'express'; import { AuthService } from './services/auth.service'; @Controller('auth/token') -export class AuthController { +export class TokenController { constructor(private authService: AuthService) {} @Post() diff --git a/server/src/database/schema.prisma b/server/src/database/schema.prisma index 438c524a54..2196d60718 100644 --- a/server/src/database/schema.prisma +++ b/server/src/database/schema.prisma @@ -12,35 +12,78 @@ generator nestgraphql { provider = "node node_modules/prisma-nestjs-graphql" output = "../../src/api/@generated" - decorate_1_type = "*CommentThreadTargetCreateNestedManyWithoutCommentThreadInput" - decorate_1_field = "!(createMany)" - decorate_1_name = "HideField" - decorate_1_from = "@nestjs/graphql" - decorate_1_arguments = "[]" + // CommentThread create: Only Allow targets createMany and comments createMany + decorate_createCommentThreadTargets_type = "*CommentThreadTargetCreateNestedManyWithoutCommentThreadInput" + decorate_createCommentThreadTargets_field = "!(createMany)" + decorate_createCommentThreadTargets_name = "HideField" + decorate_createCommentThreadTargets_from = "@nestjs/graphql" + decorate_createCommentThreadTargets_arguments = "[]" - decorate_2_type = "*CommentCreateNestedManyWithoutCommentThreadInput" - decorate_2_field = "!(createMany)" - decorate_2_name = "HideField" - decorate_2_from = "@nestjs/graphql" - decorate_2_arguments = "[]" + decorate_createCommentThreadComments_type = "*CommentCreateNestedManyWithoutCommentThreadInput" + decorate_createCommentThreadComments_field = "!(createMany)" + decorate_createCommentThreadComments_name = "HideField" + decorate_createCommentThreadComments_from = "@nestjs/graphql" + decorate_createCommentThreadComments_arguments = "[]" - decorate_3_type = "*UserCreateNestedOneWithoutCommentsInput" - decorate_3_field = "!(connect)" - decorate_3_name = "HideField" - decorate_3_from = "@nestjs/graphql" - decorate_3_arguments = "[]" + // Comment create: Only Allow author connect and commentThread connect + decorate_createCommentUser_type = "*UserCreateNestedOneWithoutCommentsInput" + decorate_createCommentUser_field = "!(connect)" + decorate_createCommentUser_name = "HideField" + decorate_createCommentUser_from = "@nestjs/graphql" + decorate_createCommentUser_arguments = "[]" - decorate_4_type = "*CommentThreadCreateNestedOneWithoutCommentsInput" - decorate_4_field = "!(connect)" - decorate_4_name = "HideField" - decorate_4_from = "@nestjs/graphql" - decorate_4_arguments = "[]" + decorate_createCommentCommentThread_type = "*CommentThreadCreateNestedOneWithoutCommentsInput" + decorate_createCommentCommentThread_field = "!(connect)" + decorate_createCommentCommentThread_name = "HideField" + decorate_createCommentCommentThread_from = "@nestjs/graphql" + decorate_createCommentCommentThread_arguments = "[]" - decorate_5_type = "!(*Aggregate*|*GroupBy*|*OrderBy*)" - decorate_5_field = "_count" - decorate_5_name = "HideField" - decorate_5_from = "@nestjs/graphql" - decorate_5_arguments = "[]" + // Person create: Only Allow company connect + decorate_createPersonCompany_type = "*CompanyCreateNestedOneWithoutPeopleInput" + decorate_createPersonCompany_field = "!(connect)" + decorate_createPersonCompany_name = "HideField" + decorate_createPersonCompany_from = "@nestjs/graphql" + decorate_createPersonCompany_arguments = "[]" + + // Person update: Only Allow company connect + decorate_updatePersonCompany_type = "*CompanyUpdateOneWithoutPeopleNestedInput" + decorate_updatePersonCompany_field = "!(connect)" + decorate_updatePersonCompany_name = "HideField" + decorate_updatePersonCompany_from = "@nestjs/graphql" + decorate_updatePersonCompany_arguments = "[]" + + // Company create: Only Allow people and accountOwner connect + decorate_createCompanyUser_type = "*UserCreateNestedOneWithoutCompaniesInput" + decorate_createCompanyUser_field = "!(connect)" + decorate_createCompanyUser_name = "HideField" + decorate_createCompanyUser_from = "@nestjs/graphql" + decorate_createCompanyUser_arguments = "[]" + + decorate_createCompanyPerson_type = "*PersonCreateNestedManyWithoutCompanyInput" + decorate_createCompanyPerson_field = "!(connect)" + decorate_createCompanyPerson_name = "HideField" + decorate_createCompanyPerson_from = "@nestjs/graphql" + decorate_createCompanyPerson_arguments = "[]" + + // Company update: Only Allow action on people and accountOwner + decorate_updateCompanyUser_type = "*UserUpdateOneWithoutCompaniesNestedInput" + decorate_updateCompanyUser_field = "!(connect)" + decorate_updateCompanyUser_name = "HideField" + decorate_updateCompanyUser_from = "@nestjs/graphql" + decorate_updateCompanyUser_arguments = "[]" + + decorate_updateCompanyPerson_type = "*PersonUpdateManyWithoutCompanyNestedInput" + decorate_updateCompanyPerson_field = "!(connect)" + decorate_updateCompanyPerson_name = "HideField" + decorate_updateCompanyPerson_from = "@nestjs/graphql" + decorate_updateCompanyPerson_arguments = "[]" + + // Disable _count on all models except Aggregation use case + decorate_count_type = "!(*Aggregate*|*GroupBy*|*OrderBy*)" + decorate_count_field = "_count" + decorate_count_name = "HideField" + decorate_count_from = "@nestjs/graphql" + decorate_count_arguments = "[]" } model User { diff --git a/server/src/entities/company/company.module.ts b/server/src/entities/company/company.module.ts deleted file mode 100644 index f943820c3a..0000000000 --- a/server/src/entities/company/company.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Module } from '@nestjs/common'; -import { CompanyRepository } from './company.repository'; -import { PrismaModule } from 'src/database/prisma.module'; - -@Module({ - imports: [PrismaModule], - providers: [CompanyRepository], - exports: [CompanyRepository], -}) -export class CompanyModule {} diff --git a/server/src/entities/company/company.repository.ts b/server/src/entities/company/company.repository.ts deleted file mode 100644 index 60973db81d..0000000000 --- a/server/src/entities/company/company.repository.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { Company, Prisma } from '@prisma/client'; -import { PrismaService } from 'src/database/prisma.service'; - -@Injectable() -export class CompanyRepository { - constructor(private prisma: PrismaService) {} - - async findMany(params: { - skip?: number; - take?: number; - cursor?: Prisma.CompanyWhereUniqueInput; - where?: Prisma.CompanyWhereInput; - orderBy?: Prisma.CompanyOrderByWithRelationInput; - }): Promise { - const { skip, take, cursor, where, orderBy } = params; - return this.prisma.company.findMany({ skip, take, cursor, where, orderBy }); - } - - async findOne(id: string | null) { - if (id === null) return null; - const company = await this.prisma.company.findUnique({ where: { id } }); - return company; - } -} diff --git a/server/src/entities/person/person.module.ts b/server/src/entities/person/person.module.ts deleted file mode 100644 index 379b8877ac..0000000000 --- a/server/src/entities/person/person.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Module } from '@nestjs/common'; -import { PersonRepository } from './person.repository'; -import { PrismaModule } from 'src/database/prisma.module'; - -@Module({ - imports: [PrismaModule], - providers: [PersonRepository], - exports: [PersonRepository], -}) -export class PersonModule {} diff --git a/server/src/entities/person/person.repository.ts b/server/src/entities/person/person.repository.ts deleted file mode 100644 index 8e0beed50d..0000000000 --- a/server/src/entities/person/person.repository.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { Person, Prisma } from '@prisma/client'; -import { PrismaService } from 'src/database/prisma.service'; - -@Injectable() -export class PersonRepository { - constructor(private prisma: PrismaService) {} - - async findMany(params: { - skip?: number; - take?: number; - cursor?: Prisma.PersonWhereUniqueInput; - where?: Prisma.PersonWhereInput; - orderBy?: Prisma.PersonOrderByWithRelationInput; - }): Promise { - const { skip, take, cursor, where, orderBy } = params; - return this.prisma.person.findMany({ skip, take, cursor, where, orderBy }); - } -} diff --git a/server/src/entities/refresh-token/refresh-token.repository.ts b/server/src/entities/refresh-token/refresh-token.repository.ts deleted file mode 100644 index 76f8803803..0000000000 --- a/server/src/entities/refresh-token/refresh-token.repository.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { Prisma, RefreshToken } from '@prisma/client'; -import { PrismaService } from 'src/database/prisma.service'; - -@Injectable() -export class RefreshTokenRepository { - constructor(private prisma: PrismaService) {} - - async upsertRefreshToken(params: { - data: Prisma.RefreshTokenUncheckedCreateInput; - }): Promise { - const { data } = params; - - return await this.prisma.refreshToken.upsert({ - where: { - id: data.id, - }, - create: { - id: data.id, - userId: data.userId, - refreshToken: data.refreshToken, - }, - update: {}, - }); - } - - async findFirst( - data: Prisma.RefreshTokenFindFirstArgs, - ): Promise { - return await this.prisma.refreshToken.findFirst(data); - } -} diff --git a/server/src/entities/user/user.module.ts b/server/src/entities/user/user.module.ts deleted file mode 100644 index 1e14ec4ada..0000000000 --- a/server/src/entities/user/user.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Module } from '@nestjs/common'; -import { UserRepository } from './user.repository'; -import { PrismaModule } from 'src/database/prisma.module'; - -@Module({ - imports: [PrismaModule], - providers: [UserRepository], - exports: [UserRepository], -}) -export class UserModule {} diff --git a/server/src/entities/user/user.repository.ts b/server/src/entities/user/user.repository.ts deleted file mode 100644 index 3d8d2548a3..0000000000 --- a/server/src/entities/user/user.repository.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { User, Prisma, WorkspaceMember } from '@prisma/client'; -import { PrismaService } from 'src/database/prisma.service'; - -@Injectable() -export class UserRepository { - constructor(private prisma: PrismaService) {} - - async findMany(params: { - skip?: number; - take?: number; - cursor?: Prisma.UserWhereUniqueInput; - where?: Prisma.UserWhereInput; - orderBy?: Prisma.UserOrderByWithRelationInput; - }): Promise { - const { skip, take, cursor, where, orderBy } = params; - return this.prisma.user.findMany({ skip, take, cursor, where, orderBy }); - } - - async findUnique(params: { - where?: Prisma.UserWhereInput; - }): Promise { - const { where } = params; - - return this.prisma.user.findFirst({ - where, - }); - } - - async upsertUser(params: { - data: Prisma.UserCreateInput; - workspaceId: string; - }): Promise { - const { data } = params; - - return await this.prisma.user.upsert({ - where: { - email: data.email, - }, - create: { - id: data.id, - displayName: data.displayName, - email: data.email, - locale: data.locale, - }, - update: {}, - }); - } - - async upsertWorkspaceMember(params: { - data: Prisma.WorkspaceMemberUncheckedCreateInput; - }): Promise { - const { data } = params; - - return await this.prisma.workspaceMember.upsert({ - where: { - userId: data.userId, - }, - create: { - id: data.id, - userId: data.userId, - workspaceId: data.workspaceId, - }, - update: {}, - }); - } -} diff --git a/server/src/entities/workspace/workspace.module.ts b/server/src/entities/workspace/workspace.module.ts deleted file mode 100644 index ed54e0f09c..0000000000 --- a/server/src/entities/workspace/workspace.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Module } from '@nestjs/common'; -import { WorkspaceRepository } from './workspace.repository'; -import { PrismaModule } from 'src/database/prisma.module'; - -@Module({ - imports: [PrismaModule], - providers: [WorkspaceRepository], - exports: [WorkspaceRepository], -}) -export class WorkspaceModule {} diff --git a/server/src/entities/workspace/workspace.repository.ts b/server/src/entities/workspace/workspace.repository.ts deleted file mode 100644 index 3b5e8c2a7d..0000000000 --- a/server/src/entities/workspace/workspace.repository.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { Workspace, Prisma } from '@prisma/client'; -import { PrismaService } from 'src/database/prisma.service'; - -@Injectable() -export class WorkspaceRepository { - constructor(private prisma: PrismaService) {} - - async findMany(params: { - skip?: number; - take?: number; - cursor?: Prisma.WorkspaceWhereUniqueInput; - where?: Prisma.WorkspaceWhereInput; - orderBy?: Prisma.WorkspaceOrderByWithRelationInput; - }): Promise { - const { skip, take, cursor, where, orderBy } = params; - return this.prisma.workspace.findMany({ - skip, - take, - cursor, - where, - orderBy, - }); - } - - async findUnique( - params: Prisma.WorkspaceFindUniqueArgs, - ): Promise { - return await this.prisma.workspace.findUnique(params); - } - - async findFirst( - params: Prisma.WorkspaceFindFirstArgs, - ): Promise { - return await this.prisma.workspace.findFirst(params); - } -} diff --git a/server/src/health.controller.spec.ts b/server/src/health.controller.spec.ts new file mode 100644 index 0000000000..01eb5c2e73 --- /dev/null +++ b/server/src/health.controller.spec.ts @@ -0,0 +1,22 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { HealthController } from './health.controller'; +import { AppService } from './app.service'; +import { TerminusModule } from '@nestjs/terminus'; + +describe('HealthController', () => { + let healthController: HealthController; + + beforeEach(async () => { + const app: TestingModule = await Test.createTestingModule({ + controllers: [HealthController], + imports: [TerminusModule], + providers: [AppService], + }).compile(); + + healthController = app.get(HealthController); + }); + + it('should be defined', () => { + expect(healthController).toBeDefined(); + }); +}); diff --git a/server/src/main.ts b/server/src/main.ts index 8d9718c074..1e2e66b8a1 100644 --- a/server/src/main.ts +++ b/server/src/main.ts @@ -5,4 +5,5 @@ async function bootstrap() { const app = await NestFactory.create(AppModule, { cors: true }); await app.listen(3000); } + bootstrap();