Removing Prisma and Grapql-nestjs-prisma resolvers (#2574)
* Some cleaning * Fix seeds * Fix all sign in, sign up flow and apiKey optimistic rendering * Fix
This commit is contained in:
@ -1,7 +0,0 @@
|
||||
export enum AbilityAction {
|
||||
Manage = 'manage',
|
||||
Create = 'create',
|
||||
Read = 'read',
|
||||
Update = 'update',
|
||||
Delete = 'delete',
|
||||
}
|
||||
@ -1,176 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { PureAbility, AbilityBuilder } from '@casl/ability';
|
||||
import { createPrismaAbility, PrismaQuery, Subjects } from '@casl/prisma';
|
||||
import {
|
||||
Activity,
|
||||
ActivityTarget,
|
||||
Attachment,
|
||||
ApiKey,
|
||||
Comment,
|
||||
Company,
|
||||
Favorite,
|
||||
Person,
|
||||
Pipeline,
|
||||
PipelineProgress,
|
||||
PipelineStage,
|
||||
RefreshToken,
|
||||
User,
|
||||
UserSettings,
|
||||
WebHook,
|
||||
Workspace,
|
||||
WorkspaceMember,
|
||||
} from '@prisma/client';
|
||||
|
||||
import { AbilityAction } from './ability.action';
|
||||
|
||||
type SubjectsAbility = Subjects<{
|
||||
Activity: Activity;
|
||||
ActivityTarget: ActivityTarget;
|
||||
Attachment: Attachment;
|
||||
ApiKey: ApiKey;
|
||||
Comment: Comment;
|
||||
Company: Company;
|
||||
Favorite: Favorite;
|
||||
WebHook: WebHook;
|
||||
Person: Person;
|
||||
Pipeline: Pipeline;
|
||||
PipelineProgress: PipelineProgress;
|
||||
PipelineStage: PipelineStage;
|
||||
RefreshToken: RefreshToken;
|
||||
User: User;
|
||||
UserSettings: UserSettings;
|
||||
|
||||
Workspace: Workspace;
|
||||
WorkspaceMember: WorkspaceMember;
|
||||
}>;
|
||||
|
||||
export type AppAbility = PureAbility<
|
||||
[string, SubjectsAbility | 'all'],
|
||||
PrismaQuery
|
||||
>;
|
||||
|
||||
@Injectable()
|
||||
export class AbilityFactory {
|
||||
defineAbility(workspace: Workspace, user?: User) {
|
||||
const { can, cannot, build } = new AbilityBuilder<AppAbility>(
|
||||
createPrismaAbility,
|
||||
);
|
||||
|
||||
// User
|
||||
|
||||
if (user) {
|
||||
can(AbilityAction.Read, 'User', { id: user.id });
|
||||
can(AbilityAction.Update, 'User', { id: user.id });
|
||||
can(AbilityAction.Delete, 'User', { id: user.id });
|
||||
} else {
|
||||
cannot(AbilityAction.Update, 'User');
|
||||
cannot(AbilityAction.Delete, 'User');
|
||||
}
|
||||
|
||||
// ApiKey
|
||||
can(AbilityAction.Read, 'ApiKey', { workspaceId: workspace.id });
|
||||
can(AbilityAction.Create, 'ApiKey');
|
||||
can(AbilityAction.Update, 'ApiKey', { workspaceId: workspace.id });
|
||||
|
||||
// WebHook
|
||||
can(AbilityAction.Read, 'WebHook', { workspaceId: workspace.id });
|
||||
can(AbilityAction.Create, 'WebHook');
|
||||
can(AbilityAction.Delete, 'WebHook', { workspaceId: workspace.id });
|
||||
|
||||
// Workspace
|
||||
can(AbilityAction.Read, 'Workspace');
|
||||
can(AbilityAction.Update, 'Workspace');
|
||||
can(AbilityAction.Delete, 'Workspace');
|
||||
|
||||
// Workspace Member
|
||||
can(AbilityAction.Read, 'WorkspaceMember', { workspaceId: workspace.id });
|
||||
if (user) {
|
||||
can(AbilityAction.Delete, 'WorkspaceMember', {
|
||||
workspaceId: workspace.id,
|
||||
});
|
||||
cannot(AbilityAction.Delete, 'WorkspaceMember', { userId: user.id });
|
||||
can(AbilityAction.Update, 'WorkspaceMember', {
|
||||
userId: user.id,
|
||||
workspaceId: workspace.id,
|
||||
});
|
||||
} else {
|
||||
cannot(AbilityAction.Delete, 'WorkspaceMember');
|
||||
cannot(AbilityAction.Update, 'WorkspaceMember');
|
||||
}
|
||||
|
||||
// Company
|
||||
can(AbilityAction.Read, 'Company', { workspaceId: workspace.id });
|
||||
can(AbilityAction.Create, 'Company');
|
||||
can(AbilityAction.Update, 'Company', { workspaceId: workspace.id });
|
||||
can(AbilityAction.Delete, 'Company', { workspaceId: workspace.id });
|
||||
|
||||
// Person
|
||||
can(AbilityAction.Read, 'Person', { workspaceId: workspace.id });
|
||||
can(AbilityAction.Create, 'Person');
|
||||
can(AbilityAction.Update, 'Person', { workspaceId: workspace.id });
|
||||
can(AbilityAction.Delete, 'Person', { workspaceId: workspace.id });
|
||||
|
||||
// RefreshToken
|
||||
cannot(AbilityAction.Manage, 'RefreshToken');
|
||||
|
||||
// Activity
|
||||
can(AbilityAction.Read, 'Activity', { workspaceId: workspace.id });
|
||||
can(AbilityAction.Create, 'Activity');
|
||||
can(AbilityAction.Update, 'Activity', { workspaceId: workspace.id });
|
||||
can(AbilityAction.Delete, 'Activity', { workspaceId: workspace.id });
|
||||
|
||||
// Comment
|
||||
can(AbilityAction.Read, 'Comment', { workspaceId: workspace.id });
|
||||
can(AbilityAction.Create, 'Comment');
|
||||
if (user) {
|
||||
can(AbilityAction.Update, 'Comment', {
|
||||
workspaceId: workspace.id,
|
||||
authorId: user.id,
|
||||
});
|
||||
can(AbilityAction.Delete, 'Comment', {
|
||||
workspaceId: workspace.id,
|
||||
authorId: user.id,
|
||||
});
|
||||
} else {
|
||||
cannot(AbilityAction.Update, 'Comment');
|
||||
cannot(AbilityAction.Delete, 'Comment');
|
||||
}
|
||||
|
||||
// ActivityTarget
|
||||
can(AbilityAction.Read, 'ActivityTarget');
|
||||
can(AbilityAction.Create, 'ActivityTarget');
|
||||
|
||||
// Attachment
|
||||
can(AbilityAction.Read, 'Attachment', { workspaceId: workspace.id });
|
||||
can(AbilityAction.Update, 'Attachment', { workspaceId: workspace.id });
|
||||
can(AbilityAction.Create, 'Attachment', { workspaceId: workspace.id });
|
||||
|
||||
// Pipeline
|
||||
can(AbilityAction.Read, 'Pipeline', { workspaceId: workspace.id });
|
||||
|
||||
// PipelineStage
|
||||
can(AbilityAction.Read, 'PipelineStage', { workspaceId: workspace.id });
|
||||
can(AbilityAction.Create, 'PipelineStage', { workspaceId: workspace.id });
|
||||
can(AbilityAction.Update, 'PipelineStage', { workspaceId: workspace.id });
|
||||
can(AbilityAction.Delete, 'PipelineStage', { workspaceId: workspace.id });
|
||||
|
||||
// PipelineProgress
|
||||
can(AbilityAction.Read, 'PipelineProgress', { workspaceId: workspace.id });
|
||||
can(AbilityAction.Create, 'PipelineProgress');
|
||||
can(AbilityAction.Update, 'PipelineProgress', {
|
||||
workspaceId: workspace.id,
|
||||
});
|
||||
can(AbilityAction.Delete, 'PipelineProgress', {
|
||||
workspaceId: workspace.id,
|
||||
});
|
||||
|
||||
// Favorite
|
||||
can(AbilityAction.Read, 'Favorite', { workspaceId: workspace.id });
|
||||
can(AbilityAction.Create, 'Favorite');
|
||||
can(AbilityAction.Update, 'Favorite', { workspaceId: workspace.id });
|
||||
can(AbilityAction.Delete, 'Favorite', { workspaceId: workspace.id });
|
||||
|
||||
return build();
|
||||
}
|
||||
}
|
||||
@ -1,309 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { AbilityFactory } from 'src/ability/ability.factory';
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import {
|
||||
CreateWebHookAbilityHandler,
|
||||
DeleteWebHookAbilityHandler,
|
||||
ReadWebHookAbilityHandler,
|
||||
} from 'src/ability/handlers/web-hook.ability-handler';
|
||||
|
||||
import {
|
||||
CreateUserAbilityHandler,
|
||||
DeleteUserAbilityHandler,
|
||||
ManageUserAbilityHandler,
|
||||
ReadUserAbilityHandler,
|
||||
UpdateUserAbilityHandler,
|
||||
} from './handlers/user.ability-handler';
|
||||
import {
|
||||
CreateWorkspaceAbilityHandler,
|
||||
DeleteWorkspaceAbilityHandler,
|
||||
ManageWorkspaceAbilityHandler,
|
||||
ReadWorkspaceAbilityHandler,
|
||||
UpdateWorkspaceAbilityHandler,
|
||||
} from './handlers/workspace.ability-handler';
|
||||
import {
|
||||
CreateWorkspaceMemberAbilityHandler,
|
||||
DeleteWorkspaceMemberAbilityHandler,
|
||||
ManageWorkspaceMemberAbilityHandler,
|
||||
ReadWorkspaceMemberAbilityHandler,
|
||||
UpdateWorkspaceMemberAbilityHandler,
|
||||
} from './handlers/workspace-member.ability-handler';
|
||||
import {
|
||||
ManageCompanyAbilityHandler,
|
||||
ReadOneCompanyAbilityHandler,
|
||||
CreateCompanyAbilityHandler,
|
||||
UpdateCompanyAbilityHandler,
|
||||
DeleteCompanyAbilityHandler,
|
||||
} from './handlers/company.ability-handler';
|
||||
import {
|
||||
CreatePersonAbilityHandler,
|
||||
DeletePersonAbilityHandler,
|
||||
ManagePersonAbilityHandler,
|
||||
ReadPersonAbilityHandler,
|
||||
UpdatePersonAbilityHandler,
|
||||
} from './handlers/person.ability-handler';
|
||||
import {
|
||||
ManageRefreshTokenAbilityHandler,
|
||||
ReadRefreshTokenAbilityHandler,
|
||||
CreateRefreshTokenAbilityHandler,
|
||||
UpdateRefreshTokenAbilityHandler,
|
||||
DeleteRefreshTokenAbilityHandler,
|
||||
} from './handlers/refresh-token.ability-handler';
|
||||
import {
|
||||
ManageActivityAbilityHandler,
|
||||
ReadActivityAbilityHandler,
|
||||
CreateActivityAbilityHandler,
|
||||
UpdateActivityAbilityHandler,
|
||||
DeleteActivityAbilityHandler,
|
||||
} from './handlers/activity.ability-handler';
|
||||
import {
|
||||
ManageCommentAbilityHandler,
|
||||
ReadCommentAbilityHandler,
|
||||
CreateCommentAbilityHandler,
|
||||
UpdateCommentAbilityHandler,
|
||||
DeleteCommentAbilityHandler,
|
||||
} from './handlers/comment.ability-handler';
|
||||
import {
|
||||
ManageActivityTargetAbilityHandler,
|
||||
ReadActivityTargetAbilityHandler,
|
||||
CreateActivityTargetAbilityHandler,
|
||||
UpdateActivityTargetAbilityHandler,
|
||||
DeleteActivityTargetAbilityHandler,
|
||||
} from './handlers/activity-target.ability-handler';
|
||||
import {
|
||||
ManagePipelineAbilityHandler,
|
||||
ReadPipelineAbilityHandler,
|
||||
CreatePipelineAbilityHandler,
|
||||
UpdatePipelineAbilityHandler,
|
||||
DeletePipelineAbilityHandler,
|
||||
} from './handlers/pipeline.ability-handler';
|
||||
import {
|
||||
ManagePipelineStageAbilityHandler,
|
||||
ReadPipelineStageAbilityHandler,
|
||||
CreatePipelineStageAbilityHandler,
|
||||
UpdatePipelineStageAbilityHandler,
|
||||
DeletePipelineStageAbilityHandler,
|
||||
} from './handlers/pipeline-stage.ability-handler';
|
||||
import {
|
||||
ManagePipelineProgressAbilityHandler,
|
||||
ReadPipelineProgressAbilityHandler,
|
||||
CreatePipelineProgressAbilityHandler,
|
||||
UpdatePipelineProgressAbilityHandler,
|
||||
DeletePipelineProgressAbilityHandler,
|
||||
} from './handlers/pipeline-progress.ability-handler';
|
||||
import {
|
||||
CreateAttachmentAbilityHandler,
|
||||
DeleteAttachmentAbilityHandler,
|
||||
ManageAttachmentAbilityHandler,
|
||||
ReadAttachmentAbilityHandler,
|
||||
UpdateAttachmentAbilityHandler,
|
||||
} from './handlers/attachment.ability-handler';
|
||||
import {
|
||||
CreateFavoriteAbilityHandler,
|
||||
ReadFavoriteAbilityHandler,
|
||||
DeleteFavoriteAbilityHandler,
|
||||
UpdateFavoriteAbilityHandler,
|
||||
} from './handlers/favorite.ability-handler';
|
||||
import {
|
||||
CreateApiKeyAbilityHandler,
|
||||
UpdateApiKeyAbilityHandler,
|
||||
ManageApiKeyAbilityHandler,
|
||||
ReadApiKeyAbilityHandler,
|
||||
} from './handlers/api-key.ability-handler';
|
||||
|
||||
@Module({
|
||||
providers: [
|
||||
AbilityFactory,
|
||||
PrismaService,
|
||||
// User
|
||||
ManageUserAbilityHandler,
|
||||
ReadUserAbilityHandler,
|
||||
CreateUserAbilityHandler,
|
||||
UpdateUserAbilityHandler,
|
||||
DeleteUserAbilityHandler,
|
||||
// Workspace
|
||||
ManageWorkspaceAbilityHandler,
|
||||
ReadWorkspaceAbilityHandler,
|
||||
CreateWorkspaceAbilityHandler,
|
||||
UpdateWorkspaceAbilityHandler,
|
||||
DeleteWorkspaceAbilityHandler,
|
||||
// Workspace Member
|
||||
ManageWorkspaceMemberAbilityHandler,
|
||||
ReadWorkspaceMemberAbilityHandler,
|
||||
CreateWorkspaceMemberAbilityHandler,
|
||||
UpdateWorkspaceMemberAbilityHandler,
|
||||
DeleteWorkspaceMemberAbilityHandler,
|
||||
// Company
|
||||
ManageCompanyAbilityHandler,
|
||||
ReadOneCompanyAbilityHandler,
|
||||
CreateCompanyAbilityHandler,
|
||||
UpdateCompanyAbilityHandler,
|
||||
DeleteCompanyAbilityHandler,
|
||||
// Person
|
||||
ManagePersonAbilityHandler,
|
||||
ReadPersonAbilityHandler,
|
||||
CreatePersonAbilityHandler,
|
||||
UpdatePersonAbilityHandler,
|
||||
DeletePersonAbilityHandler,
|
||||
// RefreshToken
|
||||
ManageRefreshTokenAbilityHandler,
|
||||
ReadRefreshTokenAbilityHandler,
|
||||
CreateRefreshTokenAbilityHandler,
|
||||
UpdateRefreshTokenAbilityHandler,
|
||||
DeleteRefreshTokenAbilityHandler,
|
||||
// Activity
|
||||
ManageActivityAbilityHandler,
|
||||
ReadActivityAbilityHandler,
|
||||
CreateActivityAbilityHandler,
|
||||
UpdateActivityAbilityHandler,
|
||||
DeleteActivityAbilityHandler,
|
||||
// Comment
|
||||
ManageCommentAbilityHandler,
|
||||
ReadCommentAbilityHandler,
|
||||
CreateCommentAbilityHandler,
|
||||
UpdateCommentAbilityHandler,
|
||||
DeleteCommentAbilityHandler,
|
||||
// ActivityTarget
|
||||
ManageActivityTargetAbilityHandler,
|
||||
ReadActivityTargetAbilityHandler,
|
||||
CreateActivityTargetAbilityHandler,
|
||||
UpdateActivityTargetAbilityHandler,
|
||||
DeleteActivityTargetAbilityHandler,
|
||||
//Attachment
|
||||
ManageAttachmentAbilityHandler,
|
||||
ReadAttachmentAbilityHandler,
|
||||
CreateAttachmentAbilityHandler,
|
||||
UpdateAttachmentAbilityHandler,
|
||||
DeleteAttachmentAbilityHandler,
|
||||
// Pipeline
|
||||
ManagePipelineAbilityHandler,
|
||||
ReadPipelineAbilityHandler,
|
||||
CreatePipelineAbilityHandler,
|
||||
UpdatePipelineAbilityHandler,
|
||||
DeletePipelineAbilityHandler,
|
||||
// PipelineStage
|
||||
ManagePipelineStageAbilityHandler,
|
||||
ReadPipelineStageAbilityHandler,
|
||||
CreatePipelineStageAbilityHandler,
|
||||
UpdatePipelineStageAbilityHandler,
|
||||
DeletePipelineStageAbilityHandler,
|
||||
// PipelineProgress
|
||||
ManagePipelineProgressAbilityHandler,
|
||||
ReadPipelineProgressAbilityHandler,
|
||||
CreatePipelineProgressAbilityHandler,
|
||||
UpdatePipelineProgressAbilityHandler,
|
||||
DeletePipelineProgressAbilityHandler,
|
||||
//Favorite
|
||||
ReadFavoriteAbilityHandler,
|
||||
CreateFavoriteAbilityHandler,
|
||||
UpdateFavoriteAbilityHandler,
|
||||
DeleteFavoriteAbilityHandler,
|
||||
|
||||
// ApiKey
|
||||
ReadApiKeyAbilityHandler,
|
||||
ManageApiKeyAbilityHandler,
|
||||
CreateApiKeyAbilityHandler,
|
||||
UpdateApiKeyAbilityHandler,
|
||||
// Hook
|
||||
CreateWebHookAbilityHandler,
|
||||
DeleteWebHookAbilityHandler,
|
||||
ReadWebHookAbilityHandler,
|
||||
],
|
||||
exports: [
|
||||
AbilityFactory,
|
||||
// User
|
||||
ManageUserAbilityHandler,
|
||||
ReadUserAbilityHandler,
|
||||
CreateUserAbilityHandler,
|
||||
UpdateUserAbilityHandler,
|
||||
DeleteUserAbilityHandler,
|
||||
// Workspace
|
||||
ManageWorkspaceAbilityHandler,
|
||||
ReadWorkspaceAbilityHandler,
|
||||
CreateWorkspaceAbilityHandler,
|
||||
UpdateWorkspaceAbilityHandler,
|
||||
DeleteWorkspaceAbilityHandler,
|
||||
// Workspace Member
|
||||
ManageWorkspaceMemberAbilityHandler,
|
||||
ReadWorkspaceMemberAbilityHandler,
|
||||
CreateWorkspaceMemberAbilityHandler,
|
||||
UpdateWorkspaceMemberAbilityHandler,
|
||||
DeleteWorkspaceMemberAbilityHandler,
|
||||
// Company
|
||||
ManageCompanyAbilityHandler,
|
||||
ReadOneCompanyAbilityHandler,
|
||||
CreateCompanyAbilityHandler,
|
||||
UpdateCompanyAbilityHandler,
|
||||
DeleteCompanyAbilityHandler,
|
||||
// Person
|
||||
ManagePersonAbilityHandler,
|
||||
ReadPersonAbilityHandler,
|
||||
CreatePersonAbilityHandler,
|
||||
UpdatePersonAbilityHandler,
|
||||
DeletePersonAbilityHandler,
|
||||
// RefreshToken
|
||||
ManageRefreshTokenAbilityHandler,
|
||||
ReadRefreshTokenAbilityHandler,
|
||||
CreateRefreshTokenAbilityHandler,
|
||||
UpdateRefreshTokenAbilityHandler,
|
||||
DeleteRefreshTokenAbilityHandler,
|
||||
// Activity
|
||||
ManageActivityAbilityHandler,
|
||||
ReadActivityAbilityHandler,
|
||||
CreateActivityAbilityHandler,
|
||||
UpdateActivityAbilityHandler,
|
||||
DeleteActivityAbilityHandler,
|
||||
// Comment
|
||||
ManageCommentAbilityHandler,
|
||||
ReadCommentAbilityHandler,
|
||||
CreateCommentAbilityHandler,
|
||||
UpdateCommentAbilityHandler,
|
||||
DeleteCommentAbilityHandler,
|
||||
// ActivityTarget
|
||||
ManageActivityTargetAbilityHandler,
|
||||
ReadActivityTargetAbilityHandler,
|
||||
CreateActivityTargetAbilityHandler,
|
||||
UpdateActivityTargetAbilityHandler,
|
||||
DeleteActivityTargetAbilityHandler,
|
||||
//Attachment
|
||||
ManageAttachmentAbilityHandler,
|
||||
ReadAttachmentAbilityHandler,
|
||||
CreateAttachmentAbilityHandler,
|
||||
UpdateAttachmentAbilityHandler,
|
||||
DeleteAttachmentAbilityHandler,
|
||||
// Pipeline
|
||||
ManagePipelineAbilityHandler,
|
||||
ReadPipelineAbilityHandler,
|
||||
CreatePipelineAbilityHandler,
|
||||
UpdatePipelineAbilityHandler,
|
||||
DeletePipelineAbilityHandler,
|
||||
// PipelineStage
|
||||
ManagePipelineStageAbilityHandler,
|
||||
ReadPipelineStageAbilityHandler,
|
||||
CreatePipelineStageAbilityHandler,
|
||||
UpdatePipelineStageAbilityHandler,
|
||||
DeletePipelineStageAbilityHandler,
|
||||
// PipelineProgress
|
||||
ManagePipelineProgressAbilityHandler,
|
||||
ReadPipelineProgressAbilityHandler,
|
||||
CreatePipelineProgressAbilityHandler,
|
||||
UpdatePipelineProgressAbilityHandler,
|
||||
DeletePipelineProgressAbilityHandler,
|
||||
//Favorite
|
||||
ReadFavoriteAbilityHandler,
|
||||
CreateFavoriteAbilityHandler,
|
||||
DeleteFavoriteAbilityHandler,
|
||||
|
||||
// ApiKey
|
||||
ReadApiKeyAbilityHandler,
|
||||
ManageApiKeyAbilityHandler,
|
||||
CreateApiKeyAbilityHandler,
|
||||
UpdateApiKeyAbilityHandler,
|
||||
// Hook
|
||||
CreateWebHookAbilityHandler,
|
||||
DeleteWebHookAbilityHandler,
|
||||
ReadWebHookAbilityHandler,
|
||||
],
|
||||
})
|
||||
export class AbilityModule {}
|
||||
@ -1,253 +0,0 @@
|
||||
import { Prisma, PrismaClient } from '@prisma/client';
|
||||
import { subject } from '@casl/ability';
|
||||
|
||||
import { camelCase } from 'src/utils/camel-case';
|
||||
|
||||
import { AppAbility } from './ability.factory';
|
||||
import { AbilityAction } from './ability.action';
|
||||
|
||||
type OperationType =
|
||||
| 'create'
|
||||
| 'connectOrCreate'
|
||||
| 'upsert'
|
||||
| 'createMany'
|
||||
| 'set'
|
||||
| 'disconnect'
|
||||
| 'delete'
|
||||
| 'connect'
|
||||
| 'update'
|
||||
| 'updateMany'
|
||||
| 'deleteMany';
|
||||
|
||||
// in most case unique identifier is the id, but it can be something else...
|
||||
|
||||
type OperationAbilityChecker = (
|
||||
modelName: Prisma.ModelName,
|
||||
ability: AppAbility,
|
||||
prisma: PrismaClient,
|
||||
data: any,
|
||||
) => Promise<boolean>;
|
||||
|
||||
const createAbilityCheck: OperationAbilityChecker = async (
|
||||
modelName,
|
||||
ability,
|
||||
prisma,
|
||||
data,
|
||||
) => {
|
||||
// Handle all operations cases
|
||||
const items = data?.data
|
||||
? !Array.isArray(data.data)
|
||||
? [data.data]
|
||||
: data.data
|
||||
: !Array.isArray(data)
|
||||
? [data]
|
||||
: data;
|
||||
|
||||
// Check if user try to create an element that is not allowed to create
|
||||
for (const {} of items) {
|
||||
if (!ability.can(AbilityAction.Create, modelName)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const simpleAbilityCheck: OperationAbilityChecker = async (
|
||||
modelName,
|
||||
ability,
|
||||
prisma,
|
||||
data,
|
||||
) => {
|
||||
// TODO: Replace user by workspaceMember and remove this check
|
||||
if (
|
||||
modelName === 'User' ||
|
||||
modelName === 'UserSettings' ||
|
||||
modelName === 'Workspace'
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (typeof data === 'boolean') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Extract entity name from model name
|
||||
const entity = camelCase(modelName);
|
||||
//TODO: Fix boolean data types so that disconnects are possible
|
||||
if (typeof data === 'boolean') {
|
||||
return true;
|
||||
}
|
||||
// Handle all operations cases
|
||||
const operations = !Array.isArray(data) ? [data] : data;
|
||||
// Handle where case
|
||||
const normalizedOperations = operations.map((op) =>
|
||||
op.where ? op.where : op,
|
||||
);
|
||||
// Force entity type because of Prisma typing
|
||||
const items = await prisma[entity as string].findMany({
|
||||
where: {
|
||||
OR: normalizedOperations,
|
||||
},
|
||||
});
|
||||
|
||||
// Check if user try to connect an element that is not allowed to read
|
||||
for (const item of items) {
|
||||
if (!ability.can(AbilityAction.Read, subject(modelName, item))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const operationAbilityCheckers: Record<OperationType, OperationAbilityChecker> =
|
||||
{
|
||||
create: createAbilityCheck,
|
||||
createMany: createAbilityCheck,
|
||||
upsert: simpleAbilityCheck,
|
||||
update: simpleAbilityCheck,
|
||||
updateMany: simpleAbilityCheck,
|
||||
delete: simpleAbilityCheck,
|
||||
deleteMany: simpleAbilityCheck,
|
||||
connectOrCreate: simpleAbilityCheck,
|
||||
connect: simpleAbilityCheck,
|
||||
disconnect: simpleAbilityCheck,
|
||||
set: simpleAbilityCheck,
|
||||
};
|
||||
|
||||
// Check relation nested abilities
|
||||
export const relationAbilityChecker = async (
|
||||
modelName: Prisma.ModelName,
|
||||
ability: AppAbility,
|
||||
prisma: PrismaClient,
|
||||
args: any,
|
||||
) => {
|
||||
// Extract models from Prisma
|
||||
const models = Prisma.dmmf.datamodel.models;
|
||||
// Find main model from options
|
||||
const mainModel = models.find((item) => item.name === modelName);
|
||||
|
||||
if (!mainModel) {
|
||||
throw new Error('Main model not found');
|
||||
}
|
||||
|
||||
// Loop over fields
|
||||
for (const field of mainModel.fields) {
|
||||
// Check if field is a relation
|
||||
if (field.relationName) {
|
||||
// Check if field is in args
|
||||
const operation = args.data?.[field.name] ?? args?.[field.name];
|
||||
|
||||
if (operation) {
|
||||
// Extract operation name and value
|
||||
const operationType = Object.keys(operation)[0] as OperationType;
|
||||
const operationValue = operation[operationType];
|
||||
// Get operation checker for the operation type
|
||||
const operationChecker = operationAbilityCheckers[operationType];
|
||||
|
||||
if (!operationChecker) {
|
||||
throw new Error('Operation not found');
|
||||
}
|
||||
|
||||
// Check if operation is allowed
|
||||
const allowed = await operationChecker(
|
||||
field.type as Prisma.ModelName,
|
||||
ability,
|
||||
prisma,
|
||||
operationValue,
|
||||
);
|
||||
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// For the 'create', 'connectOrCreate', 'upsert', 'update', and 'updateMany' operations,
|
||||
// we should also check the nested operations.
|
||||
if (
|
||||
[
|
||||
'create',
|
||||
'connectOrCreate',
|
||||
'upsert',
|
||||
'update',
|
||||
'updateMany',
|
||||
].includes(operationType)
|
||||
) {
|
||||
// Handle nested operations all cases
|
||||
|
||||
const operationValues = !Array.isArray(operationValue)
|
||||
? [operationValue]
|
||||
: operationValue;
|
||||
|
||||
// Loop over nested args
|
||||
for (const nestedArgs of operationValues) {
|
||||
const nestedCreateAllowed = await relationAbilityChecker(
|
||||
field.type as Prisma.ModelName,
|
||||
ability,
|
||||
prisma,
|
||||
nestedArgs.create ?? nestedArgs.data ?? nestedArgs,
|
||||
);
|
||||
|
||||
if (!nestedCreateAllowed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nestedArgs.update) {
|
||||
const nestedUpdateAllowed = await relationAbilityChecker(
|
||||
field.type as Prisma.ModelName,
|
||||
ability,
|
||||
prisma,
|
||||
nestedArgs.update,
|
||||
);
|
||||
|
||||
if (!nestedUpdateAllowed) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const isWhereInput = (input: any): boolean => {
|
||||
return Object.values(input).some((value) => typeof value === 'object');
|
||||
};
|
||||
|
||||
type ExcludeUnique<T> = T extends infer U
|
||||
? 'AND' extends keyof U
|
||||
? U
|
||||
: never
|
||||
: never;
|
||||
|
||||
/**
|
||||
* Convert a where unique input to a where input prisma
|
||||
* @param args Can be a where unique input or a where input
|
||||
* @returns whare input
|
||||
*/
|
||||
export const convertToWhereInput = <T>(
|
||||
where: T | undefined,
|
||||
): ExcludeUnique<T> | undefined => {
|
||||
const input = where as any;
|
||||
|
||||
if (!input) {
|
||||
return input;
|
||||
}
|
||||
|
||||
// If it's already a WhereInput, return it directly
|
||||
if (isWhereInput(input)) {
|
||||
return input;
|
||||
}
|
||||
|
||||
// If not convert it to a WhereInput
|
||||
const whereInput = {};
|
||||
|
||||
for (const key in input) {
|
||||
whereInput[key] = { equals: input[key] };
|
||||
}
|
||||
|
||||
return whereInput as ExcludeUnique<T>;
|
||||
};
|
||||
@ -1,81 +0,0 @@
|
||||
import {
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { subject } from '@casl/ability';
|
||||
|
||||
import { IAbilityHandler } from 'src/ability/interfaces/ability-handler.interface';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { AbilityAction } from 'src/ability/ability.action';
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
import { assert } from 'src/utils/assert';
|
||||
import { ActivityTargetWhereInput } from 'src/core/@generated/activity-target/activity-target-where.input';
|
||||
|
||||
class ActivityTargetArgs {
|
||||
where?: ActivityTargetWhereInput;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ManageActivityTargetAbilityHandler implements IAbilityHandler {
|
||||
async handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Manage, 'ActivityTarget');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ReadActivityTargetAbilityHandler implements IAbilityHandler {
|
||||
handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Read, 'ActivityTarget');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class CreateActivityTargetAbilityHandler implements IAbilityHandler {
|
||||
handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Create, 'ActivityTarget');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class UpdateActivityTargetAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<ActivityTargetArgs>();
|
||||
const ActivityTarget =
|
||||
await this.prismaService.client.activityTarget.findFirst({
|
||||
where: args.where,
|
||||
});
|
||||
assert(ActivityTarget, '', NotFoundException);
|
||||
|
||||
return ability.can(
|
||||
AbilityAction.Update,
|
||||
subject('ActivityTarget', ActivityTarget),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DeleteActivityTargetAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<ActivityTargetArgs>();
|
||||
const ActivityTarget =
|
||||
await this.prismaService.client.activityTarget.findFirst({
|
||||
where: args.where,
|
||||
});
|
||||
assert(ActivityTarget, '', NotFoundException);
|
||||
|
||||
return ability.can(
|
||||
AbilityAction.Delete,
|
||||
subject('ActivityTarget', ActivityTarget),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,73 +0,0 @@
|
||||
import {
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { subject } from '@casl/ability';
|
||||
|
||||
import { IAbilityHandler } from 'src/ability/interfaces/ability-handler.interface';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { AbilityAction } from 'src/ability/ability.action';
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
import { assert } from 'src/utils/assert';
|
||||
import { ActivityWhereInput } from 'src/core/@generated/activity/activity-where.input';
|
||||
|
||||
class ActivityArgs {
|
||||
where?: ActivityWhereInput;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ManageActivityAbilityHandler implements IAbilityHandler {
|
||||
async handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Manage, 'Activity');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ReadActivityAbilityHandler implements IAbilityHandler {
|
||||
handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Read, 'Activity');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class CreateActivityAbilityHandler implements IAbilityHandler {
|
||||
handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Create, 'Activity');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class UpdateActivityAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<ActivityArgs>();
|
||||
const Activity = await this.prismaService.client.activity.findFirst({
|
||||
where: args.where,
|
||||
});
|
||||
assert(Activity, '', NotFoundException);
|
||||
|
||||
return ability.can(AbilityAction.Update, subject('Activity', Activity));
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DeleteActivityAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<ActivityArgs>();
|
||||
const Activity = await this.prismaService.client.activity.findFirst({
|
||||
where: args.where,
|
||||
});
|
||||
assert(Activity, '', NotFoundException);
|
||||
|
||||
return ability.can(AbilityAction.Delete, subject('Activity', Activity));
|
||||
}
|
||||
}
|
||||
@ -1,85 +0,0 @@
|
||||
import {
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { subject } from '@casl/ability';
|
||||
|
||||
import { IAbilityHandler } from 'src/ability/interfaces/ability-handler.interface';
|
||||
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
import { AbilityAction } from 'src/ability/ability.action';
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { ApiKeyWhereUniqueInput } from 'src/core/@generated/api-key/api-key-where-unique.input';
|
||||
import { ApiKeyWhereInput } from 'src/core/@generated/api-key/api-key-where.input';
|
||||
import { assert } from 'src/utils/assert';
|
||||
import {
|
||||
convertToWhereInput,
|
||||
relationAbilityChecker,
|
||||
} from 'src/ability/ability.util';
|
||||
|
||||
class ApiKeyArgs {
|
||||
where?: ApiKeyWhereUniqueInput | ApiKeyWhereInput;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ManageApiKeyAbilityHandler implements IAbilityHandler {
|
||||
async handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Manage, 'ApiKey');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ReadApiKeyAbilityHandler implements IAbilityHandler {
|
||||
async handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Read, 'ApiKey');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class CreateApiKeyAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs();
|
||||
const allowed = await relationAbilityChecker(
|
||||
'ApiKey',
|
||||
ability,
|
||||
this.prismaService.client,
|
||||
args,
|
||||
);
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
return ability.can(AbilityAction.Create, 'ApiKey');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class UpdateApiKeyAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<ApiKeyArgs>();
|
||||
const where = convertToWhereInput(args.where);
|
||||
const apiKey = await this.prismaService.client.apiKey.findFirst({
|
||||
where,
|
||||
});
|
||||
assert(apiKey, '', NotFoundException);
|
||||
const allowed = await relationAbilityChecker(
|
||||
'ApiKey',
|
||||
ability,
|
||||
this.prismaService.client,
|
||||
args,
|
||||
);
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
return ability.can(AbilityAction.Update, subject('ApiKey', apiKey));
|
||||
}
|
||||
}
|
||||
@ -1,66 +0,0 @@
|
||||
import {
|
||||
ExecutionContext,
|
||||
ForbiddenException,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { subject } from '@casl/ability';
|
||||
|
||||
import { IAbilityHandler } from 'src/ability/interfaces/ability-handler.interface';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { AbilityAction } from 'src/ability/ability.action';
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
import { assert } from 'src/utils/assert';
|
||||
|
||||
class AttachmentArgs {
|
||||
activityId?: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ManageAttachmentAbilityHandler implements IAbilityHandler {
|
||||
async handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Manage, 'Attachment');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ReadAttachmentAbilityHandler implements IAbilityHandler {
|
||||
handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Read, 'Attachment');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class CreateAttachmentAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<AttachmentArgs>();
|
||||
assert(args.activityId, '', ForbiddenException);
|
||||
|
||||
const activity = await this.prismaService.client.activity.findUnique({
|
||||
where: { id: args.activityId },
|
||||
});
|
||||
assert(activity, '', NotFoundException);
|
||||
|
||||
return ability.can(AbilityAction.Update, subject('Activity', activity));
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class UpdateAttachmentAbilityHandler implements IAbilityHandler {
|
||||
async handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Update, 'Attachment');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DeleteAttachmentAbilityHandler implements IAbilityHandler {
|
||||
async handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Delete, 'Attachment');
|
||||
}
|
||||
}
|
||||
@ -1,102 +0,0 @@
|
||||
import {
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { subject } from '@casl/ability';
|
||||
|
||||
import { IAbilityHandler } from 'src/ability/interfaces/ability-handler.interface';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { AbilityAction } from 'src/ability/ability.action';
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
import { CommentWhereInput } from 'src/core/@generated/comment/comment-where.input';
|
||||
import { relationAbilityChecker } from 'src/ability/ability.util';
|
||||
import { assert } from 'src/utils/assert';
|
||||
|
||||
class CommentArgs {
|
||||
where?: CommentWhereInput;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ManageCommentAbilityHandler implements IAbilityHandler {
|
||||
async handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Manage, 'Comment');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ReadCommentAbilityHandler implements IAbilityHandler {
|
||||
handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Read, 'Comment');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class CreateCommentAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
const allowed = await relationAbilityChecker(
|
||||
'Comment',
|
||||
ability,
|
||||
this.prismaService.client,
|
||||
args,
|
||||
);
|
||||
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ability.can(AbilityAction.Create, 'Comment');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class UpdateCommentAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<CommentArgs>();
|
||||
const comment = await this.prismaService.client.comment.findFirst({
|
||||
where: args.where,
|
||||
});
|
||||
assert(comment, '', NotFoundException);
|
||||
|
||||
const allowed = await relationAbilityChecker(
|
||||
'Comment',
|
||||
ability,
|
||||
this.prismaService.client,
|
||||
args,
|
||||
);
|
||||
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ability.can(AbilityAction.Update, subject('Comment', comment));
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DeleteCommentAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<CommentArgs>();
|
||||
const comment = await this.prismaService.client.comment.findFirst({
|
||||
where: args.where,
|
||||
});
|
||||
assert(comment, '', NotFoundException);
|
||||
|
||||
return ability.can(AbilityAction.Delete, subject('Comment', comment));
|
||||
}
|
||||
}
|
||||
@ -1,139 +0,0 @@
|
||||
import {
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { subject } from '@casl/ability';
|
||||
|
||||
import { IAbilityHandler } from 'src/ability/interfaces/ability-handler.interface';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { AbilityAction } from 'src/ability/ability.action';
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
import { CompanyWhereInput } from 'src/core/@generated/company/company-where.input';
|
||||
import { CompanyWhereUniqueInput } from 'src/core/@generated/company/company-where-unique.input';
|
||||
import {
|
||||
convertToWhereInput,
|
||||
relationAbilityChecker,
|
||||
} from 'src/ability/ability.util';
|
||||
import { assert } from 'src/utils/assert';
|
||||
|
||||
class CompanyArgs {
|
||||
where?: CompanyWhereUniqueInput | CompanyWhereInput;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ManageCompanyAbilityHandler implements IAbilityHandler {
|
||||
async handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Manage, 'Company');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ReadOneCompanyAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<CompanyArgs>();
|
||||
const company = await this.prismaService.client.company.findFirst({
|
||||
where: args.where,
|
||||
});
|
||||
assert(company, '', NotFoundException);
|
||||
|
||||
return ability.can(AbilityAction.Read, subject('Company', company));
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class CreateCompanyAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
const allowed = await relationAbilityChecker(
|
||||
'Company',
|
||||
ability,
|
||||
this.prismaService.client,
|
||||
args,
|
||||
);
|
||||
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ability.can(AbilityAction.Create, 'Company');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class UpdateCompanyAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<CompanyArgs>();
|
||||
const where = convertToWhereInput(args.where);
|
||||
const companies = await this.prismaService.client.company.findMany({
|
||||
where,
|
||||
});
|
||||
assert(companies.length, '', NotFoundException);
|
||||
|
||||
const allowed = await relationAbilityChecker(
|
||||
'Company',
|
||||
ability,
|
||||
this.prismaService.client,
|
||||
args,
|
||||
);
|
||||
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const company of companies) {
|
||||
const allowed = ability.can(
|
||||
AbilityAction.Delete,
|
||||
subject('Company', company),
|
||||
);
|
||||
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DeleteCompanyAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<CompanyArgs>();
|
||||
const where = convertToWhereInput(args.where);
|
||||
const companies = await this.prismaService.client.company.findMany({
|
||||
where,
|
||||
});
|
||||
assert(companies.length, '', NotFoundException);
|
||||
|
||||
for (const company of companies) {
|
||||
const allowed = ability.can(
|
||||
AbilityAction.Delete,
|
||||
subject('Company', company),
|
||||
);
|
||||
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -1,102 +0,0 @@
|
||||
import {
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { subject } from '@casl/ability';
|
||||
|
||||
import { IAbilityHandler } from 'src/ability/interfaces/ability-handler.interface';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { AbilityAction } from 'src/ability/ability.action';
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
import { relationAbilityChecker } from 'src/ability/ability.util';
|
||||
import { FavoriteWhereInput } from 'src/core/@generated/favorite/favorite-where.input';
|
||||
import { assert } from 'src/utils/assert';
|
||||
|
||||
class FavoriteArgs {
|
||||
where?: FavoriteWhereInput;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ManageFavoriteAbilityHandler implements IAbilityHandler {
|
||||
async handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Manage, 'Favorite');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ReadFavoriteAbilityHandler implements IAbilityHandler {
|
||||
handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Read, 'Favorite');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class CreateFavoriteAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
const allowed = await relationAbilityChecker(
|
||||
'Favorite',
|
||||
ability,
|
||||
this.prismaService.client,
|
||||
args,
|
||||
);
|
||||
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ability.can(AbilityAction.Create, 'Favorite');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class UpdateFavoriteAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<FavoriteArgs>();
|
||||
|
||||
const favorite = await this.prismaService.client.favorite.findFirst({
|
||||
where: args.where,
|
||||
});
|
||||
assert(favorite, '', NotFoundException);
|
||||
|
||||
const allowed = await relationAbilityChecker(
|
||||
'Favorite',
|
||||
ability,
|
||||
this.prismaService.client,
|
||||
args,
|
||||
);
|
||||
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ability.can(AbilityAction.Update, 'Favorite');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DeleteFavoriteAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<FavoriteArgs>();
|
||||
const favorite = await this.prismaService.client.favorite.findFirst({
|
||||
where: args.where,
|
||||
});
|
||||
assert(favorite, '', NotFoundException);
|
||||
|
||||
return ability.can(AbilityAction.Delete, subject('Favorite', favorite));
|
||||
}
|
||||
}
|
||||
@ -1,102 +0,0 @@
|
||||
import {
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { subject } from '@casl/ability';
|
||||
|
||||
import { IAbilityHandler } from 'src/ability/interfaces/ability-handler.interface';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { AbilityAction } from 'src/ability/ability.action';
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
import { PersonWhereInput } from 'src/core/@generated/person/person-where.input';
|
||||
import { relationAbilityChecker } from 'src/ability/ability.util';
|
||||
import { assert } from 'src/utils/assert';
|
||||
|
||||
class PersonArgs {
|
||||
where?: PersonWhereInput;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ManagePersonAbilityHandler implements IAbilityHandler {
|
||||
async handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Manage, 'Person');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ReadPersonAbilityHandler implements IAbilityHandler {
|
||||
handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Read, 'Person');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class CreatePersonAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
const allowed = await relationAbilityChecker(
|
||||
'Person',
|
||||
ability,
|
||||
this.prismaService.client,
|
||||
args,
|
||||
);
|
||||
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ability.can(AbilityAction.Create, 'Person');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class UpdatePersonAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<PersonArgs>();
|
||||
const person = await this.prismaService.client.person.findFirst({
|
||||
where: args.where,
|
||||
});
|
||||
assert(person, '', NotFoundException);
|
||||
|
||||
const allowed = await relationAbilityChecker(
|
||||
'Person',
|
||||
ability,
|
||||
this.prismaService.client,
|
||||
args,
|
||||
);
|
||||
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ability.can(AbilityAction.Update, subject('Person', person));
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DeletePersonAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<PersonArgs>();
|
||||
const person = await this.prismaService.client.person.findFirst({
|
||||
where: args.where,
|
||||
});
|
||||
assert(person, '', NotFoundException);
|
||||
|
||||
return ability.can(AbilityAction.Delete, subject('Person', person));
|
||||
}
|
||||
}
|
||||
@ -1,110 +0,0 @@
|
||||
import {
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { subject } from '@casl/ability';
|
||||
|
||||
import { IAbilityHandler } from 'src/ability/interfaces/ability-handler.interface';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { AbilityAction } from 'src/ability/ability.action';
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
import { PipelineProgressWhereInput } from 'src/core/@generated/pipeline-progress/pipeline-progress-where.input';
|
||||
import { relationAbilityChecker } from 'src/ability/ability.util';
|
||||
import { assert } from 'src/utils/assert';
|
||||
|
||||
class PipelineProgressArgs {
|
||||
where?: PipelineProgressWhereInput;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ManagePipelineProgressAbilityHandler implements IAbilityHandler {
|
||||
async handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Manage, 'PipelineProgress');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ReadPipelineProgressAbilityHandler implements IAbilityHandler {
|
||||
handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Read, 'PipelineProgress');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class CreatePipelineProgressAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
const allowed = await relationAbilityChecker(
|
||||
'PipelineProgress',
|
||||
ability,
|
||||
this.prismaService.client,
|
||||
args,
|
||||
);
|
||||
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ability.can(AbilityAction.Create, 'PipelineProgress');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class UpdatePipelineProgressAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<PipelineProgressArgs>();
|
||||
const pipelineProgress =
|
||||
await this.prismaService.client.pipelineProgress.findFirst({
|
||||
where: args.where,
|
||||
});
|
||||
assert(pipelineProgress, '', NotFoundException);
|
||||
|
||||
const allowed = await relationAbilityChecker(
|
||||
'PipelineProgress',
|
||||
ability,
|
||||
this.prismaService.client,
|
||||
args,
|
||||
);
|
||||
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ability.can(
|
||||
AbilityAction.Update,
|
||||
subject('PipelineProgress', pipelineProgress),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DeletePipelineProgressAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<PipelineProgressArgs>();
|
||||
const pipelineProgress =
|
||||
await this.prismaService.client.pipelineProgress.findFirst({
|
||||
where: args.where,
|
||||
});
|
||||
assert(pipelineProgress, '', NotFoundException);
|
||||
|
||||
return ability.can(
|
||||
AbilityAction.Delete,
|
||||
subject('PipelineProgress', pipelineProgress),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,110 +0,0 @@
|
||||
import {
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { subject } from '@casl/ability';
|
||||
|
||||
import { IAbilityHandler } from 'src/ability/interfaces/ability-handler.interface';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { AbilityAction } from 'src/ability/ability.action';
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
import { PipelineStageWhereInput } from 'src/core/@generated/pipeline-stage/pipeline-stage-where.input';
|
||||
import { relationAbilityChecker } from 'src/ability/ability.util';
|
||||
import { assert } from 'src/utils/assert';
|
||||
|
||||
class PipelineStageArgs {
|
||||
where?: PipelineStageWhereInput;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ManagePipelineStageAbilityHandler implements IAbilityHandler {
|
||||
async handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Manage, 'PipelineStage');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ReadPipelineStageAbilityHandler implements IAbilityHandler {
|
||||
handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Read, 'PipelineStage');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class CreatePipelineStageAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
const allowed = await relationAbilityChecker(
|
||||
'PipelineStage',
|
||||
ability,
|
||||
this.prismaService.client,
|
||||
args,
|
||||
);
|
||||
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ability.can(AbilityAction.Create, 'PipelineStage');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class UpdatePipelineStageAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<PipelineStageArgs>();
|
||||
const pipelineStage =
|
||||
await this.prismaService.client.pipelineStage.findFirst({
|
||||
where: args.where,
|
||||
});
|
||||
assert(pipelineStage, '', NotFoundException);
|
||||
|
||||
const allowed = await relationAbilityChecker(
|
||||
'PipelineStage',
|
||||
ability,
|
||||
this.prismaService.client,
|
||||
args,
|
||||
);
|
||||
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ability.can(
|
||||
AbilityAction.Update,
|
||||
subject('PipelineStage', pipelineStage),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DeletePipelineStageAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<PipelineStageArgs>();
|
||||
const pipelineStage =
|
||||
await this.prismaService.client.pipelineStage.findFirst({
|
||||
where: args.where,
|
||||
});
|
||||
assert(pipelineStage, '', NotFoundException);
|
||||
|
||||
return ability.can(
|
||||
AbilityAction.Delete,
|
||||
subject('PipelineStage', pipelineStage),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,102 +0,0 @@
|
||||
import {
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { subject } from '@casl/ability';
|
||||
|
||||
import { IAbilityHandler } from 'src/ability/interfaces/ability-handler.interface';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { AbilityAction } from 'src/ability/ability.action';
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
import { PipelineWhereInput } from 'src/core/@generated/pipeline/pipeline-where.input';
|
||||
import { relationAbilityChecker } from 'src/ability/ability.util';
|
||||
import { assert } from 'src/utils/assert';
|
||||
|
||||
class PipelineArgs {
|
||||
where?: PipelineWhereInput;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ManagePipelineAbilityHandler implements IAbilityHandler {
|
||||
async handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Manage, 'Pipeline');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ReadPipelineAbilityHandler implements IAbilityHandler {
|
||||
handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Read, 'Pipeline');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class CreatePipelineAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
const allowed = await relationAbilityChecker(
|
||||
'Pipeline',
|
||||
ability,
|
||||
this.prismaService.client,
|
||||
args,
|
||||
);
|
||||
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ability.can(AbilityAction.Create, 'Pipeline');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class UpdatePipelineAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<PipelineArgs>();
|
||||
const pipeline = await this.prismaService.client.pipeline.findFirst({
|
||||
where: args.where,
|
||||
});
|
||||
assert(pipeline, '', NotFoundException);
|
||||
|
||||
const allowed = await relationAbilityChecker(
|
||||
'Pipeline',
|
||||
ability,
|
||||
this.prismaService.client,
|
||||
args,
|
||||
);
|
||||
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ability.can(AbilityAction.Update, subject('Pipeline', pipeline));
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DeletePipelineAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<PipelineArgs>();
|
||||
const pipeline = await this.prismaService.client.pipeline.findFirst({
|
||||
where: args.where,
|
||||
});
|
||||
assert(pipeline, '', NotFoundException);
|
||||
|
||||
return ability.can(AbilityAction.Delete, subject('Pipeline', pipeline));
|
||||
}
|
||||
}
|
||||
@ -1,112 +0,0 @@
|
||||
import {
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { subject } from '@casl/ability';
|
||||
|
||||
import { IAbilityHandler } from 'src/ability/interfaces/ability-handler.interface';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { AbilityAction } from 'src/ability/ability.action';
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
import { RefreshTokenWhereInput } from 'src/core/@generated/refresh-token/refresh-token-where.input';
|
||||
import { relationAbilityChecker } from 'src/ability/ability.util';
|
||||
import { assert } from 'src/utils/assert';
|
||||
|
||||
class RefreshTokenArgs {
|
||||
where?: RefreshTokenWhereInput;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ManageRefreshTokenAbilityHandler implements IAbilityHandler {
|
||||
async handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Manage, 'RefreshToken');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ReadRefreshTokenAbilityHandler implements IAbilityHandler {
|
||||
handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Read, 'RefreshToken');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class CreateRefreshTokenAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
const allowed = await relationAbilityChecker(
|
||||
'RefreshToken',
|
||||
ability,
|
||||
this.prismaService.client,
|
||||
args,
|
||||
);
|
||||
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ability.can(AbilityAction.Create, 'RefreshToken');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class UpdateRefreshTokenAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<RefreshTokenArgs>();
|
||||
const refreshToken = await this.prismaService.client.refreshToken.findFirst(
|
||||
{
|
||||
where: args.where,
|
||||
},
|
||||
);
|
||||
assert(refreshToken, '', NotFoundException);
|
||||
|
||||
const allowed = await relationAbilityChecker(
|
||||
'RefreshToken',
|
||||
ability,
|
||||
this.prismaService.client,
|
||||
args,
|
||||
);
|
||||
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ability.can(
|
||||
AbilityAction.Update,
|
||||
subject('RefreshToken', refreshToken),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DeleteRefreshTokenAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<RefreshTokenArgs>();
|
||||
const refreshToken = await this.prismaService.client.refreshToken.findFirst(
|
||||
{
|
||||
where: args.where,
|
||||
},
|
||||
);
|
||||
assert(refreshToken, '', NotFoundException);
|
||||
|
||||
return ability.can(
|
||||
AbilityAction.Delete,
|
||||
subject('RefreshToken', refreshToken),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,109 +0,0 @@
|
||||
import {
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { subject } from '@casl/ability';
|
||||
|
||||
import { IAbilityHandler } from 'src/ability/interfaces/ability-handler.interface';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { AbilityAction } from 'src/ability/ability.action';
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
import { UserWhereInput } from 'src/core/@generated/user/user-where.input';
|
||||
import { relationAbilityChecker } from 'src/ability/ability.util';
|
||||
import { assert } from 'src/utils/assert';
|
||||
|
||||
class UserArgs {
|
||||
where?: UserWhereInput;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ManageUserAbilityHandler implements IAbilityHandler {
|
||||
async handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Manage, 'User');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ReadUserAbilityHandler implements IAbilityHandler {
|
||||
handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Read, 'User');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class CreateUserAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
const allowed = await relationAbilityChecker(
|
||||
'User',
|
||||
ability,
|
||||
this.prismaService.client,
|
||||
args,
|
||||
);
|
||||
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ability.can(AbilityAction.Create, 'User');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class UpdateUserAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<UserArgs>();
|
||||
// TODO: Confirm if this is correct
|
||||
const user = await this.prismaService.client.user.findFirst({
|
||||
where: args.where,
|
||||
});
|
||||
assert(user, '', NotFoundException);
|
||||
|
||||
const allowed = await relationAbilityChecker(
|
||||
'User',
|
||||
ability,
|
||||
this.prismaService.client,
|
||||
args,
|
||||
);
|
||||
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ability.can(AbilityAction.Update, subject('User', user));
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DeleteUserAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<UserArgs>();
|
||||
|
||||
// obtain the auth user from the context
|
||||
const reqUser = gqlContext.getContext().req.user;
|
||||
|
||||
// FIXME: When `args.where` is undefined(which it is in almost all the cases I've tested),
|
||||
// this query will return the first user entry in the DB, which is most likely not the current user
|
||||
const user = await this.prismaService.client.user.findFirst({
|
||||
where: { ...args.where, id: reqUser.user.id },
|
||||
});
|
||||
assert(user, '', NotFoundException);
|
||||
|
||||
return ability.can(AbilityAction.Delete, subject('User', user));
|
||||
}
|
||||
}
|
||||
@ -1,57 +0,0 @@
|
||||
import {
|
||||
Injectable,
|
||||
ExecutionContext,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { subject } from '@casl/ability';
|
||||
|
||||
import { IAbilityHandler } from 'src/ability/interfaces/ability-handler.interface';
|
||||
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
import { relationAbilityChecker } from 'src/ability/ability.util';
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { AbilityAction } from 'src/ability/ability.action';
|
||||
import { assert } from 'src/utils/assert';
|
||||
|
||||
@Injectable()
|
||||
export class CreateWebHookAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs();
|
||||
const allowed = await relationAbilityChecker(
|
||||
'WebHook',
|
||||
ability,
|
||||
this.prismaService.client,
|
||||
args,
|
||||
);
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
return ability.can(AbilityAction.Create, 'WebHook');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DeleteWebHookAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs();
|
||||
const hook = await this.prismaService.client.webHook.findFirst({
|
||||
where: args.where,
|
||||
});
|
||||
assert(hook, '', NotFoundException);
|
||||
return ability.can(AbilityAction.Delete, subject('WebHook', hook));
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ReadWebHookAbilityHandler implements IAbilityHandler {
|
||||
async handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Read, 'WebHook');
|
||||
}
|
||||
}
|
||||
@ -1,110 +0,0 @@
|
||||
import {
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { subject } from '@casl/ability';
|
||||
|
||||
import { IAbilityHandler } from 'src/ability/interfaces/ability-handler.interface';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { AbilityAction } from 'src/ability/ability.action';
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
import { WorkspaceMemberWhereInput } from 'src/core/@generated/workspace-member/workspace-member-where.input';
|
||||
import { relationAbilityChecker } from 'src/ability/ability.util';
|
||||
import { assert } from 'src/utils/assert';
|
||||
|
||||
class WorkspaceMemberArgs {
|
||||
where?: WorkspaceMemberWhereInput;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ManageWorkspaceMemberAbilityHandler implements IAbilityHandler {
|
||||
async handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Manage, 'WorkspaceMember');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ReadWorkspaceMemberAbilityHandler implements IAbilityHandler {
|
||||
handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Read, 'WorkspaceMember');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class CreateWorkspaceMemberAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
const allowed = await relationAbilityChecker(
|
||||
'WorkspaceMember',
|
||||
ability,
|
||||
this.prismaService.client,
|
||||
args,
|
||||
);
|
||||
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ability.can(AbilityAction.Create, 'WorkspaceMember');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class UpdateWorkspaceMemberAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<WorkspaceMemberArgs>();
|
||||
const workspaceMember =
|
||||
await this.prismaService.client.workspaceMember.findFirst({
|
||||
where: args.where,
|
||||
});
|
||||
assert(workspaceMember, '', NotFoundException);
|
||||
|
||||
const allowed = await relationAbilityChecker(
|
||||
'WorkspaceMember',
|
||||
ability,
|
||||
this.prismaService.client,
|
||||
args,
|
||||
);
|
||||
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ability.can(
|
||||
AbilityAction.Update,
|
||||
subject('WorkspaceMember', workspaceMember),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DeleteWorkspaceMemberAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<WorkspaceMemberArgs>();
|
||||
const workspaceMember =
|
||||
await this.prismaService.client.workspaceMember.findFirst({
|
||||
where: args.where,
|
||||
});
|
||||
assert(workspaceMember, '', NotFoundException);
|
||||
|
||||
return ability.can(
|
||||
AbilityAction.Delete,
|
||||
subject('WorkspaceMember', workspaceMember),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,100 +0,0 @@
|
||||
import {
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { IAbilityHandler } from 'src/ability/interfaces/ability-handler.interface';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { AbilityAction } from 'src/ability/ability.action';
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
import { WorkspaceWhereInput } from 'src/core/@generated/workspace/workspace-where.input';
|
||||
import { relationAbilityChecker } from 'src/ability/ability.util';
|
||||
import { assert } from 'src/utils/assert';
|
||||
|
||||
class WorkspaceArgs {
|
||||
where?: WorkspaceWhereInput;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ManageWorkspaceAbilityHandler implements IAbilityHandler {
|
||||
async handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Manage, 'Workspace');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ReadWorkspaceAbilityHandler implements IAbilityHandler {
|
||||
handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Read, 'Workspace');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class CreateWorkspaceAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
const allowed = await relationAbilityChecker(
|
||||
'Workspace',
|
||||
ability,
|
||||
this.prismaService.client,
|
||||
args,
|
||||
);
|
||||
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ability.can(AbilityAction.Create, 'Workspace');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class UpdateWorkspaceAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<WorkspaceArgs>();
|
||||
const workspace = await this.prismaService.client.workspace.findFirst({
|
||||
where: args.where,
|
||||
});
|
||||
assert(workspace, '', NotFoundException);
|
||||
|
||||
const allowed = await relationAbilityChecker(
|
||||
'Workspace',
|
||||
ability,
|
||||
this.prismaService.client,
|
||||
args,
|
||||
);
|
||||
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ability.can(AbilityAction.Update, 'Workspace');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DeleteWorkspaceAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<WorkspaceArgs>();
|
||||
const workspace = await this.prismaService.client.workspace.findFirst({
|
||||
where: args.where,
|
||||
});
|
||||
assert(workspace, '', NotFoundException);
|
||||
|
||||
return ability.can(AbilityAction.Delete, 'Workspace');
|
||||
}
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
import { ExecutionContext, Type } from '@nestjs/common';
|
||||
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
|
||||
export interface IAbilityHandler {
|
||||
handle(
|
||||
ability: AppAbility,
|
||||
executionContext: ExecutionContext,
|
||||
): Promise<boolean> | boolean;
|
||||
}
|
||||
|
||||
export type AbilityHandler = Type<IAbilityHandler>;
|
||||
@ -13,9 +13,7 @@ import { AppService } from './app.service';
|
||||
|
||||
import { CoreModule } from './core/core.module';
|
||||
import { IntegrationsModule } from './integrations/integrations.module';
|
||||
import { PrismaModule } from './database/prisma.module';
|
||||
import { HealthModule } from './health/health.module';
|
||||
import { AbilityModule } from './ability/ability.module';
|
||||
import { WorkspaceModule } from './workspace/workspace.module';
|
||||
import { EnvironmentService } from './integrations/environment/environment.service';
|
||||
import {
|
||||
@ -104,9 +102,7 @@ import { ExceptionFilter } from './filters/exception.filter';
|
||||
resolvers: { JSON: GraphQLJSON },
|
||||
plugins: [],
|
||||
}),
|
||||
PrismaModule,
|
||||
HealthModule,
|
||||
AbilityModule,
|
||||
IntegrationsModule,
|
||||
CoreModule,
|
||||
WorkspaceModule,
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { AbilityModule } from 'src/ability/ability.module';
|
||||
import { PrismaModule } from 'src/database/prisma.module';
|
||||
|
||||
import { ActivityResolver } from './resolvers/activity.resolver';
|
||||
import { ActivityService } from './services/activity.service';
|
||||
import { ActivityTargetService } from './services/activity-target.service';
|
||||
|
||||
@Module({
|
||||
imports: [AbilityModule, PrismaModule],
|
||||
providers: [ActivityResolver, ActivityService, ActivityTargetService],
|
||||
exports: [ActivityService, ActivityTargetService],
|
||||
})
|
||||
export class ActivityModule {}
|
||||
@ -1,32 +0,0 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { ActivityService } from 'src/core/activity/services/activity.service';
|
||||
import { AbilityFactory } from 'src/ability/ability.factory';
|
||||
|
||||
import { ActivityResolver } from './activity.resolver';
|
||||
|
||||
describe('ActivityResolver', () => {
|
||||
let resolver: ActivityResolver;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
ActivityResolver,
|
||||
{
|
||||
provide: ActivityService,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: AbilityFactory,
|
||||
useValue: {},
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
resolver = module.get<ActivityResolver>(ActivityResolver);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(resolver).toBeDefined();
|
||||
});
|
||||
});
|
||||
@ -1,156 +0,0 @@
|
||||
import { Resolver, Args, Mutation, Query } from '@nestjs/graphql';
|
||||
import { UseGuards } from '@nestjs/common';
|
||||
|
||||
import { accessibleBy } from '@casl/prisma';
|
||||
import { Prisma } from '@prisma/client';
|
||||
|
||||
import { JwtAuthGuard } from 'src/guards/jwt.auth.guard';
|
||||
import { AuthWorkspace } from 'src/decorators/auth-workspace.decorator';
|
||||
import {
|
||||
PrismaSelector,
|
||||
PrismaSelect,
|
||||
} from 'src/decorators/prisma-select.decorator';
|
||||
import { AbilityGuard } from 'src/guards/ability.guard';
|
||||
import { CheckAbilities } from 'src/decorators/check-abilities.decorator';
|
||||
import {
|
||||
CreateActivityAbilityHandler,
|
||||
DeleteActivityAbilityHandler,
|
||||
ReadActivityAbilityHandler,
|
||||
UpdateActivityAbilityHandler,
|
||||
} from 'src/ability/handlers/activity.ability-handler';
|
||||
import { UserAbility } from 'src/decorators/user-ability.decorator';
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
import { AffectedRows } from 'src/core/@generated/prisma/affected-rows.output';
|
||||
import { Activity } from 'src/core/@generated/activity/activity.model';
|
||||
import { ActivityService } from 'src/core/activity/services/activity.service';
|
||||
import { CreateOneActivityArgs } from 'src/core/@generated/activity/create-one-activity.args';
|
||||
import { Workspace } from 'src/core/@generated/workspace/workspace.model';
|
||||
import { UpdateOneActivityArgs } from 'src/core/@generated/activity/update-one-activity.args';
|
||||
import { FindManyActivityArgs } from 'src/core/@generated/activity/find-many-activity.args';
|
||||
import { DeleteManyActivityArgs } from 'src/core/@generated/activity/delete-many-activity.args';
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Resolver(() => Activity)
|
||||
export class ActivityResolver {
|
||||
constructor(private readonly activityService: ActivityService) {}
|
||||
|
||||
@Mutation(() => Activity, {
|
||||
nullable: false,
|
||||
})
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(CreateActivityAbilityHandler)
|
||||
async createOneActivity(
|
||||
@Args() args: CreateOneActivityArgs,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
@PrismaSelector({ modelName: 'Activity' })
|
||||
prismaSelect: PrismaSelect<'Activity'>,
|
||||
): Promise<Partial<Activity>> {
|
||||
const createdActivity = await this.activityService.create({
|
||||
data: {
|
||||
...args.data,
|
||||
...{ workspace: { connect: { id: workspace.id } } },
|
||||
activityTargets: args.data?.activityTargets?.createMany
|
||||
? {
|
||||
createMany: {
|
||||
data: args.data.activityTargets.createMany.data.map(
|
||||
(target) => ({ ...target, workspaceId: workspace.id }),
|
||||
),
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
select: prismaSelect.value,
|
||||
} as Prisma.ActivityCreateArgs);
|
||||
|
||||
return createdActivity;
|
||||
}
|
||||
|
||||
@Mutation(() => Activity, {
|
||||
nullable: false,
|
||||
})
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(UpdateActivityAbilityHandler)
|
||||
async updateOneActivity(
|
||||
@Args() args: UpdateOneActivityArgs,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
@PrismaSelector({ modelName: 'Activity' })
|
||||
prismaSelect: PrismaSelect<'Activity'>,
|
||||
): Promise<Partial<Activity>> {
|
||||
// TODO: Do a proper check with recursion testing on args in a more generic place
|
||||
for (const key in args.data) {
|
||||
if (args.data[key]) {
|
||||
for (const subKey in args.data[key]) {
|
||||
if (JSON.stringify(args.data[key][subKey]) === '{}') {
|
||||
delete args.data[key][subKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (JSON.stringify(args.data[key]) === '{}') {
|
||||
delete args.data[key];
|
||||
}
|
||||
}
|
||||
const updatedActivity = await this.activityService.update({
|
||||
where: args.where,
|
||||
data: {
|
||||
...args.data,
|
||||
activityTargets: args.data?.activityTargets
|
||||
? {
|
||||
createMany: args.data.activityTargets.createMany
|
||||
? {
|
||||
data: args.data.activityTargets.createMany.data.map(
|
||||
(target) => ({
|
||||
...target,
|
||||
workspaceId: workspace.id,
|
||||
}),
|
||||
),
|
||||
}
|
||||
: undefined,
|
||||
deleteMany: args.data.activityTargets.deleteMany ?? undefined,
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
select: prismaSelect.value,
|
||||
} as Prisma.ActivityUpdateArgs);
|
||||
|
||||
return updatedActivity;
|
||||
}
|
||||
|
||||
@Query(() => [Activity])
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(ReadActivityAbilityHandler)
|
||||
async findManyActivities(
|
||||
@Args() args: FindManyActivityArgs,
|
||||
@UserAbility() ability: AppAbility,
|
||||
@PrismaSelector({ modelName: 'Activity' })
|
||||
prismaSelect: PrismaSelect<'Activity'>,
|
||||
): Promise<Partial<Activity>[]> {
|
||||
const result = await this.activityService.findMany({
|
||||
where: {
|
||||
...args.where,
|
||||
AND: [accessibleBy(ability).Activity],
|
||||
},
|
||||
orderBy: args.orderBy,
|
||||
cursor: args.cursor,
|
||||
take: args.take,
|
||||
skip: args.skip,
|
||||
distinct: args.distinct,
|
||||
select: prismaSelect.value,
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Mutation(() => AffectedRows, {
|
||||
nullable: false,
|
||||
})
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(DeleteActivityAbilityHandler)
|
||||
async deleteManyActivities(
|
||||
@Args() args: DeleteManyActivityArgs,
|
||||
): Promise<AffectedRows> {
|
||||
return this.activityService.deleteMany({
|
||||
where: args.where,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { prismaMock } from 'src/database/client-mock/jest-prisma-singleton';
|
||||
|
||||
import { ActivityTargetService } from './activity-target.service';
|
||||
|
||||
describe('ActivityTargetService', () => {
|
||||
let service: ActivityTargetService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
ActivityTargetService,
|
||||
{
|
||||
provide: PrismaService,
|
||||
useValue: prismaMock,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get<ActivityTargetService>(ActivityTargetService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
@ -1,40 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class ActivityTargetService {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
// Find
|
||||
findFirst = this.prismaService.client.activityTarget.findFirst;
|
||||
findFirstOrThrow = this.prismaService.client.activityTarget.findFirstOrThrow;
|
||||
|
||||
findUnique = this.prismaService.client.activityTarget.findUnique;
|
||||
findUniqueOrThrow =
|
||||
this.prismaService.client.activityTarget.findUniqueOrThrow;
|
||||
|
||||
findMany = this.prismaService.client.activityTarget.findMany;
|
||||
|
||||
// Create
|
||||
create = this.prismaService.client.activityTarget.create;
|
||||
createMany = this.prismaService.client.activityTarget.createMany;
|
||||
|
||||
// Update
|
||||
update = this.prismaService.client.activityTarget.update;
|
||||
upsert = this.prismaService.client.activityTarget.upsert;
|
||||
updateMany = this.prismaService.client.activityTarget.updateMany;
|
||||
|
||||
// Delete
|
||||
delete = this.prismaService.client.activityTarget.delete;
|
||||
deleteMany = this.prismaService.client.activityTarget.deleteMany;
|
||||
|
||||
// Aggregate
|
||||
aggregate = this.prismaService.client.activityTarget.aggregate;
|
||||
|
||||
// Count
|
||||
count = this.prismaService.client.activityTarget.count;
|
||||
|
||||
// GroupBy
|
||||
groupBy = this.prismaService.client.activityTarget.groupBy;
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { prismaMock } from 'src/database/client-mock/jest-prisma-singleton';
|
||||
|
||||
import { ActivityService } from './activity.service';
|
||||
|
||||
describe('ActivityService', () => {
|
||||
let service: ActivityService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
ActivityService,
|
||||
{
|
||||
provide: PrismaService,
|
||||
useValue: prismaMock,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get<ActivityService>(ActivityService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
@ -1,39 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class ActivityService {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
// Find
|
||||
findFirst = this.prismaService.client.activity.findFirst;
|
||||
findFirstOrThrow = this.prismaService.client.activity.findFirstOrThrow;
|
||||
|
||||
findUnique = this.prismaService.client.activity.findUnique;
|
||||
findUniqueOrThrow = this.prismaService.client.activity.findUniqueOrThrow;
|
||||
|
||||
findMany = this.prismaService.client.activity.findMany;
|
||||
|
||||
// Create
|
||||
create = this.prismaService.client.activity.create;
|
||||
createMany = this.prismaService.client.activity.createMany;
|
||||
|
||||
// Update
|
||||
update = this.prismaService.client.activity.update;
|
||||
upsert = this.prismaService.client.activity.upsert;
|
||||
updateMany = this.prismaService.client.activity.updateMany;
|
||||
|
||||
// Delete
|
||||
delete = this.prismaService.client.activity.delete;
|
||||
deleteMany = this.prismaService.client.activity.deleteMany;
|
||||
|
||||
// Aggregate
|
||||
aggregate = this.prismaService.client.activity.aggregate;
|
||||
|
||||
// Count
|
||||
count = this.prismaService.client.activity.count;
|
||||
|
||||
// GroupBy
|
||||
groupBy = this.prismaService.client.activity.groupBy;
|
||||
}
|
||||
@ -1,11 +1,11 @@
|
||||
import { Resolver, Mutation, Args } from '@nestjs/graphql';
|
||||
import { UseGuards } from '@nestjs/common';
|
||||
|
||||
import { User, Workspace } from '@prisma/client';
|
||||
|
||||
import { OptionalJwtAuthGuard } from 'src/guards/optional-jwt.auth.guard';
|
||||
import { AuthWorkspace } from 'src/decorators/auth-workspace.decorator';
|
||||
import { AuthUser } from 'src/decorators/auth-user.decorator';
|
||||
import { Workspace } from 'src/core/workspace/workspace.entity';
|
||||
import { User } from 'src/core/user/user.entity';
|
||||
|
||||
import { AnalyticsService } from './analytics.service';
|
||||
import { Analytics } from './analytics.entity';
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { User, Workspace } from '@prisma/client';
|
||||
import axios, { AxiosInstance } from 'axios';
|
||||
|
||||
import { anonymize } from 'src/utils/anonymize';
|
||||
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
||||
import { User } from 'src/core/user/user.entity';
|
||||
import { Workspace } from 'src/core/workspace/workspace.entity';
|
||||
|
||||
import { CreateAnalyticsInput } from './dto/create-analytics.input';
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { ArgsType, Field } from '@nestjs/graphql';
|
||||
|
||||
import GraphQLJSON from 'graphql-type-json';
|
||||
import graphqlTypeJson from 'graphql-type-json';
|
||||
import { IsNotEmpty, IsString, IsObject } from 'class-validator';
|
||||
|
||||
@ArgsType()
|
||||
@ -10,7 +10,7 @@ export class CreateAnalyticsInput {
|
||||
@IsString()
|
||||
type: string;
|
||||
|
||||
@Field(() => GraphQLJSON, { description: 'Event data in JSON format' })
|
||||
@Field(() => graphqlTypeJson, { description: 'Event data in JSON format' })
|
||||
@IsObject()
|
||||
data: JSON;
|
||||
}
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
|
||||
import { TokenService } from 'src/core/auth/services/token.service';
|
||||
import { AbilityModule } from 'src/ability/ability.module';
|
||||
import { PrismaModule } from 'src/database/prisma.module';
|
||||
|
||||
import { ApiKeyResolver } from './api-key.resolver';
|
||||
import { ApiKeyService } from './api-key.service';
|
||||
|
||||
@Module({
|
||||
imports: [AbilityModule, PrismaModule],
|
||||
providers: [ApiKeyResolver, ApiKeyService, TokenService, JwtService],
|
||||
})
|
||||
export class ApiKeyModule {}
|
||||
@ -1,28 +0,0 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
|
||||
import { AbilityFactory } from 'src/ability/ability.factory';
|
||||
import { TokenService } from 'src/core/auth/services/token.service';
|
||||
|
||||
import { ApiKeyResolver } from './api-key.resolver';
|
||||
import { ApiKeyService } from './api-key.service';
|
||||
|
||||
describe('ApiKeyResolver', () => {
|
||||
let resolver: ApiKeyResolver;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
ApiKeyResolver,
|
||||
{ provide: ApiKeyService, useValue: {} },
|
||||
{ provide: TokenService, useValue: {} },
|
||||
{ provide: JwtService, useValue: {} },
|
||||
{ provide: AbilityFactory, useValue: {} },
|
||||
],
|
||||
}).compile();
|
||||
resolver = module.get<ApiKeyResolver>(ApiKeyResolver);
|
||||
});
|
||||
it('should be defined', () => {
|
||||
expect(resolver).toBeDefined();
|
||||
});
|
||||
});
|
||||
@ -1,97 +0,0 @@
|
||||
import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
|
||||
import { NotFoundException, UseGuards } from '@nestjs/common';
|
||||
|
||||
import { accessibleBy } from '@casl/prisma';
|
||||
|
||||
import { AbilityGuard } from 'src/guards/ability.guard';
|
||||
import { AuthWorkspace } from 'src/decorators/auth-workspace.decorator';
|
||||
import { Workspace } from 'src/core/@generated/workspace/workspace.model';
|
||||
import { CreateOneApiKeyArgs } from 'src/core/@generated/api-key/create-one-api-key.args';
|
||||
import { ApiKey } from 'src/core/@generated/api-key/api-key.model';
|
||||
import { FindManyApiKeyArgs } from 'src/core/@generated/api-key/find-many-api-key.args';
|
||||
import { DeleteOneApiKeyArgs } from 'src/core/@generated/api-key/delete-one-api-key.args';
|
||||
import { CheckAbilities } from 'src/decorators/check-abilities.decorator';
|
||||
import {
|
||||
CreateApiKeyAbilityHandler,
|
||||
UpdateApiKeyAbilityHandler,
|
||||
ReadApiKeyAbilityHandler,
|
||||
} from 'src/ability/handlers/api-key.ability-handler';
|
||||
import { JwtAuthGuard } from 'src/guards/jwt.auth.guard';
|
||||
import { UserAbility } from 'src/decorators/user-ability.decorator';
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
import { ApiKeyToken } from 'src/core/auth/dto/token.entity';
|
||||
|
||||
import { ApiKeyService } from './api-key.service';
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Resolver(() => ApiKey)
|
||||
export class ApiKeyResolver {
|
||||
constructor(private readonly apiKeyService: ApiKeyService) {}
|
||||
|
||||
@Mutation(() => ApiKeyToken)
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(CreateApiKeyAbilityHandler)
|
||||
async createOneApiKey(
|
||||
@Args() args: CreateOneApiKeyArgs,
|
||||
@AuthWorkspace() { id: workspaceId }: Workspace,
|
||||
): Promise<ApiKeyToken> {
|
||||
return await this.apiKeyService.generateApiKeyToken(
|
||||
workspaceId,
|
||||
args.data.name,
|
||||
args.data.expiresAt,
|
||||
);
|
||||
}
|
||||
|
||||
@Mutation(() => ApiKeyToken)
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(CreateApiKeyAbilityHandler)
|
||||
async generateApiKeyV2Token(
|
||||
@Args()
|
||||
args: CreateOneApiKeyArgs,
|
||||
@AuthWorkspace() { id: workspaceId }: Workspace,
|
||||
): Promise<Pick<ApiKeyToken, 'token'> | undefined> {
|
||||
return await this.apiKeyService.generateApiKeyV2Token(
|
||||
workspaceId,
|
||||
args.data.id,
|
||||
args.data.expiresAt,
|
||||
);
|
||||
}
|
||||
|
||||
@Mutation(() => ApiKey)
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(UpdateApiKeyAbilityHandler)
|
||||
async revokeOneApiKey(
|
||||
@Args() args: DeleteOneApiKeyArgs,
|
||||
): Promise<Partial<ApiKey>> {
|
||||
const apiKeyToDelete = await this.apiKeyService.findFirst({
|
||||
where: { ...args.where },
|
||||
});
|
||||
if (!apiKeyToDelete) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
return this.apiKeyService.update({
|
||||
where: args.where,
|
||||
data: {
|
||||
revokedAt: new Date(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@Query(() => [ApiKey])
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(ReadApiKeyAbilityHandler)
|
||||
async findManyApiKey(
|
||||
@Args() args: FindManyApiKeyArgs,
|
||||
@UserAbility() ability: AppAbility,
|
||||
) {
|
||||
const filterOptions = [
|
||||
accessibleBy(ability).WorkspaceMember,
|
||||
{ revokedAt: null },
|
||||
];
|
||||
if (args.where) filterOptions.push(args.where);
|
||||
return this.apiKeyService.findMany({
|
||||
...args,
|
||||
where: { AND: filterOptions },
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,85 +0,0 @@
|
||||
import { Injectable, InternalServerErrorException } from '@nestjs/common';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { ApiKeyToken } from 'src/core/auth/dto/token.entity';
|
||||
import { assert } from 'src/utils/assert';
|
||||
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
||||
|
||||
@Injectable()
|
||||
export class ApiKeyService {
|
||||
constructor(
|
||||
private readonly prismaService: PrismaService,
|
||||
private readonly environmentService: EnvironmentService,
|
||||
private readonly jwtService: JwtService,
|
||||
) {}
|
||||
|
||||
findFirst = this.prismaService.client.apiKey.findFirst;
|
||||
findUniqueOrThrow = this.prismaService.client.apiKey.findUniqueOrThrow;
|
||||
findMany = this.prismaService.client.apiKey.findMany;
|
||||
create = this.prismaService.client.apiKey.create;
|
||||
update = this.prismaService.client.apiKey.update;
|
||||
delete = this.prismaService.client.apiKey.delete;
|
||||
|
||||
async generateApiKeyV2Token(
|
||||
workspaceId: string,
|
||||
apiKeyId?: string,
|
||||
expiresAt?: Date | string,
|
||||
): Promise<Pick<ApiKeyToken, 'token'> | undefined> {
|
||||
if (!apiKeyId) {
|
||||
return;
|
||||
}
|
||||
const jwtPayload = {
|
||||
sub: workspaceId,
|
||||
};
|
||||
const secret = this.environmentService.getAccessTokenSecret();
|
||||
let expiresIn: string | number;
|
||||
if (expiresAt) {
|
||||
expiresIn = Math.floor(
|
||||
(new Date(expiresAt).getTime() - new Date().getTime()) / 1000,
|
||||
);
|
||||
} else {
|
||||
expiresIn = this.environmentService.getApiTokenExpiresIn();
|
||||
}
|
||||
const token = this.jwtService.sign(jwtPayload, {
|
||||
secret,
|
||||
expiresIn,
|
||||
jwtid: apiKeyId,
|
||||
});
|
||||
return { token };
|
||||
}
|
||||
|
||||
async generateApiKeyToken(
|
||||
workspaceId: string,
|
||||
name: string,
|
||||
expiresAt?: Date | string,
|
||||
): Promise<ApiKeyToken> {
|
||||
const secret = this.environmentService.getAccessTokenSecret();
|
||||
let expiresIn: string | number;
|
||||
const now = new Date().getTime();
|
||||
if (expiresAt) {
|
||||
expiresIn = Math.floor((new Date(expiresAt).getTime() - now) / 1000);
|
||||
} else {
|
||||
expiresIn = this.environmentService.getApiTokenExpiresIn();
|
||||
}
|
||||
assert(expiresIn, '', InternalServerErrorException);
|
||||
const jwtPayload = {
|
||||
sub: workspaceId,
|
||||
};
|
||||
const newApiKey = await this.prismaService.client.apiKey.create({
|
||||
data: {
|
||||
expiresAt: expiresAt,
|
||||
name: name,
|
||||
workspaceId: workspaceId,
|
||||
},
|
||||
});
|
||||
return {
|
||||
...newApiKey,
|
||||
token: this.jwtService.sign(jwtPayload, {
|
||||
secret,
|
||||
expiresIn,
|
||||
jwtid: newApiKey.id,
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { FileUploadService } from 'src/core/file/services/file-upload.service';
|
||||
import { AbilityModule } from 'src/ability/ability.module';
|
||||
import { PrismaModule } from 'src/database/prisma.module';
|
||||
|
||||
import { AttachmentResolver } from './resolvers/attachment.resolver';
|
||||
import { AttachmentService } from './services/attachment.service';
|
||||
|
||||
@Module({
|
||||
imports: [AbilityModule, PrismaModule],
|
||||
providers: [AttachmentService, AttachmentResolver, FileUploadService],
|
||||
exports: [AttachmentService],
|
||||
})
|
||||
export class AttachmentModule {}
|
||||
@ -1,37 +0,0 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { FileUploadService } from 'src/core/file/services/file-upload.service';
|
||||
import { AttachmentService } from 'src/core/attachment/services/attachment.service';
|
||||
import { AbilityFactory } from 'src/ability/ability.factory';
|
||||
|
||||
import { AttachmentResolver } from './attachment.resolver';
|
||||
|
||||
describe('AttachmentResolver', () => {
|
||||
let resolver: AttachmentResolver;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
AttachmentResolver,
|
||||
{
|
||||
provide: FileUploadService,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: AttachmentService,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: AbilityFactory,
|
||||
useValue: {},
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
resolver = module.get<AttachmentResolver>(AttachmentResolver);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(resolver).toBeDefined();
|
||||
});
|
||||
});
|
||||
@ -1,72 +0,0 @@
|
||||
import { Resolver, Args, Mutation } from '@nestjs/graphql';
|
||||
import { UseGuards } from '@nestjs/common';
|
||||
|
||||
import { User, Workspace } from '@prisma/client';
|
||||
import { GraphQLUpload, FileUpload } from 'graphql-upload';
|
||||
import { v4 as uuidV4 } from 'uuid';
|
||||
|
||||
import { FileFolder } from 'src/core/file/interfaces/file-folder.interface';
|
||||
|
||||
import { AuthUser } from 'src/decorators/auth-user.decorator';
|
||||
import { AuthWorkspace } from 'src/decorators/auth-workspace.decorator';
|
||||
import { streamToBuffer } from 'src/utils/stream-to-buffer';
|
||||
import { AttachmentService } from 'src/core/attachment/services/attachment.service';
|
||||
import { FileUploadService } from 'src/core/file/services/file-upload.service';
|
||||
import { JwtAuthGuard } from 'src/guards/jwt.auth.guard';
|
||||
import { Attachment } from 'src/core/@generated/attachment/attachment.model';
|
||||
import { AbilityGuard } from 'src/guards/ability.guard';
|
||||
import { CreateAttachmentAbilityHandler } from 'src/ability/handlers/attachment.ability-handler';
|
||||
import { CheckAbilities } from 'src/decorators/check-abilities.decorator';
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Resolver(() => Attachment)
|
||||
@Resolver()
|
||||
export class AttachmentResolver {
|
||||
constructor(
|
||||
private readonly fileUploadService: FileUploadService,
|
||||
private readonly attachmentService: AttachmentService,
|
||||
) {}
|
||||
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(CreateAttachmentAbilityHandler)
|
||||
@Mutation(() => String)
|
||||
async uploadAttachment(
|
||||
@AuthUser() user: User,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
@Args({ name: 'file', type: () => GraphQLUpload })
|
||||
{ createReadStream, filename, mimetype }: FileUpload,
|
||||
@Args('activityId') activityId?: string,
|
||||
@Args('companyId') companyId?: string,
|
||||
@Args('personId') personId?: string,
|
||||
): Promise<string> {
|
||||
const stream = createReadStream();
|
||||
const buffer = await streamToBuffer(stream);
|
||||
|
||||
const { path } = await this.fileUploadService.uploadFile({
|
||||
file: buffer,
|
||||
filename,
|
||||
mimeType: mimetype,
|
||||
fileFolder: FileFolder.Attachment,
|
||||
});
|
||||
|
||||
await this.attachmentService.create({
|
||||
data: {
|
||||
id: uuidV4(),
|
||||
fullPath: path,
|
||||
type: this.attachmentService.getFileTypeFromFileName(filename),
|
||||
name: filename,
|
||||
activityId,
|
||||
companyId,
|
||||
personId,
|
||||
authorId: user.id,
|
||||
workspaceId: workspace.id,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
fullPath: true,
|
||||
},
|
||||
});
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { prismaMock } from 'src/database/client-mock/jest-prisma-singleton';
|
||||
|
||||
import { AttachmentService } from './attachment.service';
|
||||
|
||||
describe('AttachmentService', () => {
|
||||
let service: AttachmentService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
AttachmentService,
|
||||
{
|
||||
provide: PrismaService,
|
||||
useValue: prismaMock,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get<AttachmentService>(AttachmentService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
@ -1,83 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { AttachmentType } from '@prisma/client';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class AttachmentService {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
// Find
|
||||
findFirst = this.prismaService.client.attachment.findFirst;
|
||||
findFirstOrThrow = this.prismaService.client.attachment.findFirstOrThrow;
|
||||
|
||||
findUnique = this.prismaService.client.attachment.findUnique;
|
||||
findUniqueOrThrow = this.prismaService.client.attachment.findUniqueOrThrow;
|
||||
|
||||
findMany = this.prismaService.client.attachment.findMany;
|
||||
|
||||
// Create
|
||||
create = this.prismaService.client.attachment.create;
|
||||
createMany = this.prismaService.client.attachment.createMany;
|
||||
|
||||
// Update
|
||||
update = this.prismaService.client.attachment.update;
|
||||
upsert = this.prismaService.client.attachment.upsert;
|
||||
updateMany = this.prismaService.client.attachment.updateMany;
|
||||
|
||||
// Delete
|
||||
delete = this.prismaService.client.attachment.delete;
|
||||
deleteMany = this.prismaService.client.attachment.deleteMany;
|
||||
|
||||
// Aggregate
|
||||
aggregate = this.prismaService.client.attachment.aggregate;
|
||||
|
||||
// Count
|
||||
count = this.prismaService.client.attachment.count;
|
||||
|
||||
// GroupBy
|
||||
groupBy = this.prismaService.client.attachment.groupBy;
|
||||
|
||||
getFileTypeFromFileName(fileName: string): AttachmentType {
|
||||
const extension = fileName.split('.').pop()?.toLowerCase();
|
||||
|
||||
switch (extension) {
|
||||
case 'mp4':
|
||||
case 'avi':
|
||||
case 'mov':
|
||||
return AttachmentType.Video;
|
||||
|
||||
case 'mp3':
|
||||
case 'wav':
|
||||
case 'ogg':
|
||||
return AttachmentType.Audio;
|
||||
|
||||
case 'jpg':
|
||||
case 'jpeg':
|
||||
case 'png':
|
||||
case 'gif':
|
||||
return AttachmentType.Image;
|
||||
|
||||
case 'txt':
|
||||
case 'doc':
|
||||
case 'docx':
|
||||
case 'pdf':
|
||||
return AttachmentType.TextDocument;
|
||||
|
||||
case 'xls':
|
||||
case 'xlsx':
|
||||
case 'csv':
|
||||
return AttachmentType.Spreadsheet;
|
||||
|
||||
case 'zip':
|
||||
case 'rar':
|
||||
case 'tar':
|
||||
case '7z':
|
||||
return AttachmentType.Archive;
|
||||
|
||||
default:
|
||||
return AttachmentType.Other;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,21 @@
|
||||
/* eslint-disable no-restricted-imports */
|
||||
import { Module } from '@nestjs/common';
|
||||
import { JwtModule } from '@nestjs/jwt';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { UserModule } from 'src/core/user/user.module';
|
||||
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
||||
import { WorkspaceModule } from 'src/core/workspace/workspace.module';
|
||||
import { FileModule } from 'src/core/file/file.module';
|
||||
import { Workspace } from 'src/core/workspace/workspace.entity';
|
||||
import { User } from 'src/core/user/user.entity';
|
||||
import { RefreshToken } from 'src/core/refresh-token/refresh-token.entity';
|
||||
import { DataSourceModule } from 'src/metadata/data-source/data-source.module';
|
||||
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
|
||||
import { UserModule } from 'src/core/user/user.module';
|
||||
import { WorkspaceManagerModule } from 'src/workspace/workspace-manager/workspace-manager.module';
|
||||
|
||||
import config from '../../../ormconfig';
|
||||
|
||||
import { AuthResolver } from './auth.resolver';
|
||||
|
||||
@ -28,15 +38,22 @@ const jwtModule = JwtModule.registerAsync({
|
||||
});
|
||||
|
||||
@Module({
|
||||
imports: [jwtModule, UserModule, WorkspaceModule, FileModule],
|
||||
controllers: [GoogleAuthController, VerifyAuthController],
|
||||
providers: [
|
||||
AuthService,
|
||||
TokenService,
|
||||
JwtAuthStrategy,
|
||||
PrismaService,
|
||||
AuthResolver,
|
||||
imports: [
|
||||
jwtModule,
|
||||
FileModule,
|
||||
DataSourceModule,
|
||||
UserModule,
|
||||
WorkspaceManagerModule,
|
||||
TypeOrmModule.forRoot(config),
|
||||
NestjsQueryGraphQLModule.forFeature({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([Workspace, User, RefreshToken]),
|
||||
TypeORMModule,
|
||||
],
|
||||
}),
|
||||
],
|
||||
controllers: [GoogleAuthController, VerifyAuthController],
|
||||
providers: [AuthService, TokenService, JwtAuthStrategy, AuthResolver],
|
||||
exports: [jwtModule],
|
||||
})
|
||||
export class AuthModule {}
|
||||
|
||||
@ -4,21 +4,19 @@ import {
|
||||
ForbiddenException,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import {
|
||||
PrismaSelect,
|
||||
PrismaSelector,
|
||||
} from 'src/decorators/prisma-select.decorator';
|
||||
import { JwtAuthGuard } from 'src/guards/jwt.auth.guard';
|
||||
import { AuthUser } from 'src/decorators/auth-user.decorator';
|
||||
import { assert } from 'src/utils/assert';
|
||||
import { User } from 'src/core/@generated/user/user.model';
|
||||
import { Workspace } from 'src/core/@generated/workspace/workspace.model';
|
||||
import { WorkspaceService } from 'src/core/workspace/services/workspace.service';
|
||||
import { Workspace } from 'src/core/workspace/workspace.entity';
|
||||
import { AuthWorkspace } from 'src/decorators/auth-workspace.decorator';
|
||||
import { User } from 'src/core/user/user.entity';
|
||||
import { ApiKeyTokenInput } from 'src/core/auth/dto/api-key-token.input';
|
||||
|
||||
import { AuthTokens } from './dto/token.entity';
|
||||
import { ApiKeyToken, AuthTokens } from './dto/token.entity';
|
||||
import { TokenService } from './services/token.service';
|
||||
import { RefreshTokenInput } from './dto/refresh-token.input';
|
||||
import { Verify } from './dto/verify.entity';
|
||||
@ -36,7 +34,8 @@ import { ImpersonateInput } from './dto/impersonate.input';
|
||||
@Resolver()
|
||||
export class AuthResolver {
|
||||
constructor(
|
||||
private workspaceService: WorkspaceService,
|
||||
@InjectRepository(Workspace)
|
||||
private readonly workspaceRepository: Repository<Workspace>,
|
||||
private authService: AuthService,
|
||||
private tokenService: TokenService,
|
||||
) {}
|
||||
@ -64,10 +63,8 @@ export class AuthResolver {
|
||||
async findWorkspaceFromInviteHash(
|
||||
@Args() workspaceInviteHashValidInput: WorkspaceInviteHashValidInput,
|
||||
) {
|
||||
return await this.workspaceService.findFirst({
|
||||
where: {
|
||||
inviteHash: workspaceInviteHashValidInput.inviteHash,
|
||||
},
|
||||
return await this.workspaceRepository.findOneBy({
|
||||
inviteHash: workspaceInviteHashValidInput.inviteHash,
|
||||
});
|
||||
}
|
||||
|
||||
@ -88,21 +85,12 @@ export class AuthResolver {
|
||||
}
|
||||
|
||||
@Mutation(() => Verify)
|
||||
async verify(
|
||||
@Args() verifyInput: VerifyInput,
|
||||
@PrismaSelector({
|
||||
modelName: 'User',
|
||||
defaultFields: { User: { id: true } },
|
||||
})
|
||||
prismaSelect: PrismaSelect<'User'>,
|
||||
): Promise<Verify> {
|
||||
async verify(@Args() verifyInput: VerifyInput): Promise<Verify> {
|
||||
const email = await this.tokenService.verifyLoginToken(
|
||||
verifyInput.loginToken,
|
||||
);
|
||||
const select = prismaSelect.valueOf('user') as Prisma.UserSelect & {
|
||||
id: true;
|
||||
};
|
||||
const result = await this.authService.verify(email, select);
|
||||
|
||||
const result = await this.authService.verify(email);
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -125,22 +113,24 @@ export class AuthResolver {
|
||||
async impersonate(
|
||||
@Args() impersonateInput: ImpersonateInput,
|
||||
@AuthUser() user: User,
|
||||
@PrismaSelector({
|
||||
modelName: 'User',
|
||||
defaultFields: {
|
||||
User: {
|
||||
id: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
prismaSelect: PrismaSelect<'User'>,
|
||||
): Promise<Verify> {
|
||||
// Check if user can impersonate
|
||||
assert(user.canImpersonate, 'User cannot impersonate', ForbiddenException);
|
||||
const select = prismaSelect.valueOf('user') as Prisma.UserSelect & {
|
||||
id: true;
|
||||
};
|
||||
|
||||
return this.authService.impersonate(impersonateInput.userId, select);
|
||||
return this.authService.impersonate(impersonateInput.userId);
|
||||
}
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Mutation(() => ApiKeyToken)
|
||||
async generateApiKeyToken(
|
||||
@Args() args: ApiKeyTokenInput,
|
||||
@AuthWorkspace() { id: workspaceId }: Workspace,
|
||||
): Promise<ApiKeyToken | undefined> {
|
||||
console.log('toto');
|
||||
return await this.tokenService.generateApiKeyToken(
|
||||
workspaceId,
|
||||
args.apiKeyId,
|
||||
args.expiresAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,29 +1,29 @@
|
||||
import { Controller, Get, Req, Res, UseGuards } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Response } from 'express';
|
||||
import FileType from 'file-type';
|
||||
import { v4 as uuidV4 } from 'uuid';
|
||||
|
||||
import { FileFolder } from 'src/core/file/interfaces/file-folder.interface';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { GoogleRequest } from 'src/core/auth/strategies/google.auth.strategy';
|
||||
import { UserService } from 'src/core/user/user.service';
|
||||
import { TokenService } from 'src/core/auth/services/token.service';
|
||||
import { GoogleProviderEnabledGuard } from 'src/core/auth/guards/google-provider-enabled.guard';
|
||||
import { GoogleOauthGuard } from 'src/core/auth/guards/google-oauth.guard';
|
||||
import { WorkspaceService } from 'src/core/workspace/services/workspace.service';
|
||||
import { User } from 'src/core/user/user.entity';
|
||||
import { Workspace } from 'src/core/workspace/workspace.entity';
|
||||
import { AuthService } from 'src/core/auth/services/auth.service';
|
||||
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
||||
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
||||
import { getImageBufferFromUrl } from 'src/utils/image';
|
||||
import { FileUploadService } from 'src/core/file/services/file-upload.service';
|
||||
|
||||
@Controller('auth/google')
|
||||
export class GoogleAuthController {
|
||||
constructor(
|
||||
private readonly tokenService: TokenService,
|
||||
private readonly userService: UserService,
|
||||
private readonly workspaceService: WorkspaceService,
|
||||
private readonly environmentService: EnvironmentService,
|
||||
private readonly fileUploadService: FileUploadService,
|
||||
private readonly typeORMService: TypeORMService,
|
||||
private readonly authService: AuthService,
|
||||
@InjectRepository(Workspace)
|
||||
@InjectRepository(User, 'metadata')
|
||||
private readonly userRepository: Repository<User>,
|
||||
) {}
|
||||
|
||||
@Get()
|
||||
@ -39,65 +39,29 @@ export class GoogleAuthController {
|
||||
const { firstName, lastName, email, picture, workspaceInviteHash } =
|
||||
req.user;
|
||||
|
||||
let workspaceId: string | undefined = undefined;
|
||||
if (workspaceInviteHash) {
|
||||
const workspace = await this.workspaceService.findFirst({
|
||||
where: {
|
||||
inviteHash: workspaceInviteHash,
|
||||
},
|
||||
});
|
||||
const mainDataSource = await this.typeORMService.getMainDataSource();
|
||||
|
||||
if (!workspace) {
|
||||
return res.redirect(
|
||||
`${this.environmentService.getFrontAuthCallbackUrl()}`,
|
||||
);
|
||||
}
|
||||
const existingUser = await mainDataSource
|
||||
.getRepository(User)
|
||||
.findOneBy({ email: email });
|
||||
|
||||
workspaceId = workspace.id;
|
||||
if (existingUser) {
|
||||
const loginToken = await this.tokenService.generateLoginToken(
|
||||
existingUser.email,
|
||||
);
|
||||
|
||||
return res.redirect(
|
||||
this.tokenService.computeRedirectURI(loginToken.token),
|
||||
);
|
||||
}
|
||||
|
||||
let user = await this.userService.createUser(
|
||||
{
|
||||
data: {
|
||||
email,
|
||||
firstName: firstName ?? '',
|
||||
lastName: lastName ?? '',
|
||||
locale: 'en',
|
||||
},
|
||||
},
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (!user.avatarUrl) {
|
||||
let imagePath: string | undefined = undefined;
|
||||
|
||||
if (picture) {
|
||||
// Get image buffer from url
|
||||
const buffer = await getImageBufferFromUrl(picture);
|
||||
|
||||
// Extract mimetype and extension from buffer
|
||||
const type = await FileType.fromBuffer(buffer);
|
||||
|
||||
// Upload image
|
||||
const { paths } = await this.fileUploadService.uploadImage({
|
||||
file: buffer,
|
||||
filename: `${uuidV4()}.${type?.ext}`,
|
||||
mimeType: type?.mime,
|
||||
fileFolder: FileFolder.ProfilePicture,
|
||||
});
|
||||
|
||||
imagePath = paths[0];
|
||||
}
|
||||
|
||||
user = await this.userService.update({
|
||||
where: {
|
||||
id: user.id,
|
||||
},
|
||||
data: {
|
||||
avatarUrl: imagePath,
|
||||
},
|
||||
});
|
||||
}
|
||||
const user = await this.authService.signUp({
|
||||
email,
|
||||
firstName,
|
||||
lastName,
|
||||
picture,
|
||||
workspaceInviteHash,
|
||||
});
|
||||
|
||||
const loginToken = await this.tokenService.generateLoginToken(user.email);
|
||||
|
||||
|
||||
@ -17,13 +17,7 @@ export class VerifyAuthController {
|
||||
const email = await this.tokenService.verifyLoginToken(
|
||||
verifyInput.loginToken,
|
||||
);
|
||||
const result = await this.authService.verify(email, {
|
||||
id: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
email: true,
|
||||
emailVerified: true,
|
||||
});
|
||||
const result = await this.authService.verify(email);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
15
server/src/core/auth/dto/api-key-token.input.ts
Normal file
15
server/src/core/auth/dto/api-key-token.input.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { ArgsType, Field } from '@nestjs/graphql';
|
||||
|
||||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
@ArgsType()
|
||||
export class ApiKeyTokenInput {
|
||||
@Field(() => String)
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
apiKeyId: string;
|
||||
|
||||
@Field(() => String)
|
||||
@IsNotEmpty()
|
||||
expiresAt: string;
|
||||
}
|
||||
@ -1,7 +1,5 @@
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import { ApiKey } from 'src/core/@generated/api-key/api-key.model';
|
||||
|
||||
@ObjectType()
|
||||
export class AuthToken {
|
||||
@Field(() => String)
|
||||
@ -12,7 +10,7 @@ export class AuthToken {
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class ApiKeyToken extends ApiKey {
|
||||
export class ApiKeyToken {
|
||||
@Field(() => String)
|
||||
token: string;
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import { User } from 'src/core/@generated/user/user.model';
|
||||
import { User } from 'src/core/user/user.entity';
|
||||
|
||||
import { AuthTokens } from './token.entity';
|
||||
|
||||
|
||||
@ -4,11 +4,15 @@ import {
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Prisma } from '@prisma/client';
|
||||
import FileType from 'file-type';
|
||||
import { Repository } from 'typeorm';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { FileFolder } from 'src/core/file/interfaces/file-folder.interface';
|
||||
|
||||
import { ChallengeInput } from 'src/core/auth/dto/challenge.input';
|
||||
import { UserService } from 'src/core/user/user.service';
|
||||
import { assert } from 'src/utils/assert';
|
||||
import {
|
||||
PASSWORD_REGEX,
|
||||
@ -17,9 +21,13 @@ import {
|
||||
} from 'src/core/auth/auth.util';
|
||||
import { Verify } from 'src/core/auth/dto/verify.entity';
|
||||
import { UserExists } from 'src/core/auth/dto/user-exists.entity';
|
||||
import { WorkspaceService } from 'src/core/workspace/services/workspace.service';
|
||||
import { WorkspaceInviteHashValid } from 'src/core/auth/dto/workspace-invite-hash-valid.entity';
|
||||
import { SignUpInput } from 'src/core/auth/dto/sign-up.input';
|
||||
import { User } from 'src/core/user/user.entity';
|
||||
import { Workspace } from 'src/core/workspace/workspace.entity';
|
||||
import { UserService } from 'src/core/user/services/user.service';
|
||||
import { WorkspaceManagerService } from 'src/workspace/workspace-manager/workspace-manager.service';
|
||||
import { getImageBufferFromUrl } from 'src/utils/image';
|
||||
import { FileUploadService } from 'src/core/file/services/file-upload.service';
|
||||
|
||||
import { TokenService } from './token.service';
|
||||
|
||||
@ -34,14 +42,17 @@ export class AuthService {
|
||||
constructor(
|
||||
private readonly tokenService: TokenService,
|
||||
private readonly userService: UserService,
|
||||
private readonly workspaceService: WorkspaceService,
|
||||
private readonly workspaceManagerService: WorkspaceManagerService,
|
||||
private readonly fileUploadService: FileUploadService,
|
||||
@InjectRepository(Workspace)
|
||||
private readonly workspaceRepository: Repository<Workspace>,
|
||||
@InjectRepository(User)
|
||||
private readonly userRepository: Repository<User>,
|
||||
) {}
|
||||
|
||||
async challenge(challengeInput: ChallengeInput) {
|
||||
const user = await this.userService.findUnique({
|
||||
where: {
|
||||
email: challengeInput.email,
|
||||
},
|
||||
const user = await this.userRepository.findOneBy({
|
||||
email: challengeInput.email,
|
||||
});
|
||||
|
||||
assert(user, "This user doesn't exist", NotFoundException);
|
||||
@ -57,24 +68,40 @@ export class AuthService {
|
||||
return user;
|
||||
}
|
||||
|
||||
async signUp(signUpInput: SignUpInput) {
|
||||
const existingUser = await this.userService.findUnique({
|
||||
where: {
|
||||
email: signUpInput.email,
|
||||
},
|
||||
async signUp({
|
||||
email,
|
||||
password,
|
||||
workspaceInviteHash,
|
||||
firstName,
|
||||
lastName,
|
||||
picture,
|
||||
}: {
|
||||
email: string;
|
||||
password?: string;
|
||||
firstName?: string | null;
|
||||
lastName?: string | null;
|
||||
workspaceInviteHash?: string | null;
|
||||
picture?: string | null;
|
||||
}) {
|
||||
if (!firstName) firstName = '';
|
||||
if (!lastName) lastName = '';
|
||||
|
||||
const existingUser = await this.userRepository.findOneBy({
|
||||
email: email,
|
||||
});
|
||||
assert(!existingUser, 'This user already exists', ForbiddenException);
|
||||
|
||||
const isPasswordValid = PASSWORD_REGEX.test(signUpInput.password);
|
||||
assert(isPasswordValid, 'Password too weak', BadRequestException);
|
||||
if (password) {
|
||||
const isPasswordValid = PASSWORD_REGEX.test(password);
|
||||
assert(isPasswordValid, 'Password too weak', BadRequestException);
|
||||
}
|
||||
|
||||
const passwordHash = await hashPassword(signUpInput.password);
|
||||
const passwordHash = password ? await hashPassword(password) : undefined;
|
||||
let workspace: Workspace | null;
|
||||
|
||||
if (signUpInput.workspaceInviteHash) {
|
||||
const workspace = await this.workspaceService.findFirst({
|
||||
where: {
|
||||
inviteHash: signUpInput.workspaceInviteHash,
|
||||
},
|
||||
if (workspaceInviteHash) {
|
||||
workspace = await this.workspaceRepository.findOneBy({
|
||||
inviteHash: workspaceInviteHash,
|
||||
});
|
||||
|
||||
assert(
|
||||
@ -82,44 +109,59 @@ export class AuthService {
|
||||
'This workspace inviteHash is invalid',
|
||||
ForbiddenException,
|
||||
);
|
||||
|
||||
return await this.userService.createUser(
|
||||
{
|
||||
data: {
|
||||
email: signUpInput.email,
|
||||
passwordHash,
|
||||
},
|
||||
} as Prisma.UserCreateArgs,
|
||||
workspace.id,
|
||||
);
|
||||
} else {
|
||||
const workspaceToCreate = this.workspaceRepository.create({
|
||||
displayName: '',
|
||||
domainName: '',
|
||||
inviteHash: v4(),
|
||||
});
|
||||
workspace = await this.workspaceRepository.save(workspaceToCreate);
|
||||
await this.workspaceManagerService.init(workspace.id);
|
||||
}
|
||||
|
||||
return await this.userService.createUser({
|
||||
data: {
|
||||
email: signUpInput.email,
|
||||
passwordHash,
|
||||
locale: 'en',
|
||||
},
|
||||
} as Prisma.UserCreateArgs);
|
||||
const userToCreate = this.userRepository.create({
|
||||
email: email,
|
||||
firstName: firstName,
|
||||
lastName: lastName,
|
||||
canImpersonate: false,
|
||||
passwordHash,
|
||||
defaultWorkspace: workspace,
|
||||
});
|
||||
const user = await this.userRepository.save(userToCreate);
|
||||
let imagePath: string | undefined = undefined;
|
||||
|
||||
if (picture) {
|
||||
const buffer = await getImageBufferFromUrl(picture);
|
||||
|
||||
const type = await FileType.fromBuffer(buffer);
|
||||
|
||||
const { paths } = await this.fileUploadService.uploadImage({
|
||||
file: buffer,
|
||||
filename: `${v4()}.${type?.ext}`,
|
||||
mimeType: type?.mime,
|
||||
fileFolder: FileFolder.ProfilePicture,
|
||||
});
|
||||
|
||||
imagePath = paths[0];
|
||||
}
|
||||
await this.userService.createWorkspaceMember(user, imagePath);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
async verify(
|
||||
email: string,
|
||||
select: Prisma.UserSelect & {
|
||||
id: true;
|
||||
},
|
||||
): Promise<Verify> {
|
||||
const user = await this.userService.findUnique({
|
||||
async verify(email: string): Promise<Verify> {
|
||||
const user = await this.userRepository.findOne({
|
||||
where: {
|
||||
email,
|
||||
},
|
||||
select,
|
||||
relations: ['defaultWorkspace'],
|
||||
});
|
||||
|
||||
assert(user, "This user doesn't exist", NotFoundException);
|
||||
|
||||
// passwordHash is hidden for security reasons
|
||||
user.passwordHash = '';
|
||||
user.workspaceMember = await this.userService.loadWorkspaceMember(user);
|
||||
|
||||
const accessToken = await this.tokenService.generateAccessToken(user.id);
|
||||
const refreshToken = await this.tokenService.generateRefreshToken(user.id);
|
||||
@ -134,10 +176,8 @@ export class AuthService {
|
||||
}
|
||||
|
||||
async checkUserExists(email: string): Promise<UserExists> {
|
||||
const user = await this.userService.findUnique({
|
||||
where: {
|
||||
email,
|
||||
},
|
||||
const user = await this.userRepository.findOneBy({
|
||||
email,
|
||||
});
|
||||
|
||||
return { exists: !!user };
|
||||
@ -146,26 +186,16 @@ export class AuthService {
|
||||
async checkWorkspaceInviteHashIsValid(
|
||||
inviteHash: string,
|
||||
): Promise<WorkspaceInviteHashValid> {
|
||||
const workspace = await this.workspaceService.findFirst({
|
||||
where: {
|
||||
inviteHash,
|
||||
},
|
||||
const workspace = await this.workspaceRepository.findOneBy({
|
||||
inviteHash,
|
||||
});
|
||||
|
||||
return { isValid: !!workspace };
|
||||
}
|
||||
|
||||
async impersonate(
|
||||
userId: string,
|
||||
select: Prisma.UserSelect & {
|
||||
id: true;
|
||||
},
|
||||
) {
|
||||
const user = await this.userService.findUnique({
|
||||
where: {
|
||||
id: userId,
|
||||
},
|
||||
select,
|
||||
async impersonate(userId: string) {
|
||||
const user = await this.userRepository.findOneBy({
|
||||
id: userId,
|
||||
});
|
||||
|
||||
assert(user, "This user doesn't exist", NotFoundException);
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { prismaMock } from 'src/database/client-mock/jest-prisma-singleton';
|
||||
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
||||
|
||||
import { TokenService } from './token.service';
|
||||
@ -22,10 +20,6 @@ describe('TokenService', () => {
|
||||
provide: EnvironmentService,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: PrismaService,
|
||||
useValue: prismaMock,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
|
||||
@ -7,23 +7,29 @@ import {
|
||||
UnprocessableEntityException,
|
||||
} from '@nestjs/common';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { addMilliseconds } from 'date-fns';
|
||||
import ms from 'ms';
|
||||
import { TokenExpiredError } from 'jsonwebtoken';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { JwtPayload } from 'src/core/auth/strategies/jwt.auth.strategy';
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { assert } from 'src/utils/assert';
|
||||
import { AuthToken } from 'src/core/auth/dto/token.entity';
|
||||
import { ApiKeyToken, AuthToken } from 'src/core/auth/dto/token.entity';
|
||||
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
||||
import { User } from 'src/core/user/user.entity';
|
||||
import { RefreshToken } from 'src/core/refresh-token/refresh-token.entity';
|
||||
|
||||
@Injectable()
|
||||
export class TokenService {
|
||||
constructor(
|
||||
private readonly jwtService: JwtService,
|
||||
private readonly environmentService: EnvironmentService,
|
||||
private readonly prismaService: PrismaService,
|
||||
@InjectRepository(User)
|
||||
private readonly userRepository: Repository<User>,
|
||||
@InjectRepository(RefreshToken)
|
||||
private readonly refreshTokenRepository: Repository<RefreshToken>,
|
||||
) {}
|
||||
|
||||
async generateAccessToken(userId: string): Promise<AuthToken> {
|
||||
@ -31,23 +37,26 @@ export class TokenService {
|
||||
assert(expiresIn, '', InternalServerErrorException);
|
||||
const expiresAt = addMilliseconds(new Date().getTime(), ms(expiresIn));
|
||||
|
||||
const user = await this.prismaService.client.user.findUnique({
|
||||
const user = await this.userRepository.findOne({
|
||||
where: { id: userId },
|
||||
relations: ['defaultWorkspace'],
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new NotFoundException('User is not found');
|
||||
}
|
||||
|
||||
if (!user.defaultWorkspaceId) {
|
||||
if (!user.defaultWorkspace) {
|
||||
throw new NotFoundException('User does not have a default workspace');
|
||||
}
|
||||
|
||||
const jwtPayload: JwtPayload = {
|
||||
sub: user.id,
|
||||
workspaceId: user.defaultWorkspaceId,
|
||||
workspaceId: user.defaultWorkspace.id,
|
||||
};
|
||||
|
||||
console.log(jwtPayload);
|
||||
|
||||
return {
|
||||
token: this.jwtService.sign(jwtPayload),
|
||||
expiresAt,
|
||||
@ -68,9 +77,13 @@ export class TokenService {
|
||||
sub: userId,
|
||||
};
|
||||
|
||||
const refreshToken = await this.prismaService.client.refreshToken.create({
|
||||
data: refreshTokenPayload,
|
||||
});
|
||||
const refreshToken =
|
||||
this.refreshTokenRepository.create(refreshTokenPayload);
|
||||
console.log(refreshToken);
|
||||
|
||||
await this.refreshTokenRepository.save(refreshToken);
|
||||
|
||||
console.log('toto');
|
||||
|
||||
return {
|
||||
token: this.jwtService.sign(jwtPayload, {
|
||||
@ -101,6 +114,34 @@ export class TokenService {
|
||||
};
|
||||
}
|
||||
|
||||
async generateApiKeyToken(
|
||||
workspaceId: string,
|
||||
apiKeyId?: string,
|
||||
expiresAt?: Date | string,
|
||||
): Promise<Pick<ApiKeyToken, 'token'> | undefined> {
|
||||
if (!apiKeyId) {
|
||||
return;
|
||||
}
|
||||
const jwtPayload = {
|
||||
sub: workspaceId,
|
||||
};
|
||||
const secret = this.environmentService.getAccessTokenSecret();
|
||||
let expiresIn: string | number;
|
||||
if (expiresAt) {
|
||||
expiresIn = Math.floor(
|
||||
(new Date(expiresAt).getTime() - new Date().getTime()) / 1000,
|
||||
);
|
||||
} else {
|
||||
expiresIn = this.environmentService.getApiTokenExpiresIn();
|
||||
}
|
||||
const token = this.jwtService.sign(jwtPayload, {
|
||||
secret,
|
||||
expiresIn,
|
||||
jwtid: apiKeyId,
|
||||
});
|
||||
return { token };
|
||||
}
|
||||
|
||||
async verifyLoginToken(loginToken: string): Promise<string> {
|
||||
const loginTokenSecret = this.environmentService.getLoginTokenSecret();
|
||||
|
||||
@ -120,19 +161,14 @@ export class TokenService {
|
||||
UnprocessableEntityException,
|
||||
);
|
||||
|
||||
const token = await this.prismaService.client.refreshToken.findUnique({
|
||||
where: { id: jwtPayload.jti },
|
||||
const token = await this.refreshTokenRepository.findOneBy({
|
||||
id: jwtPayload.jti,
|
||||
});
|
||||
|
||||
assert(token, "This refresh token doesn't exist", NotFoundException);
|
||||
|
||||
const user = await this.prismaService.client.user.findUnique({
|
||||
where: {
|
||||
id: jwtPayload.sub,
|
||||
},
|
||||
include: {
|
||||
refreshTokens: true,
|
||||
},
|
||||
const user = await this.userRepository.findOneBy({
|
||||
id: jwtPayload.sub,
|
||||
});
|
||||
|
||||
assert(user, 'User not found', NotFoundException);
|
||||
@ -143,16 +179,17 @@ export class TokenService {
|
||||
token.revokedAt.getTime() <= Date.now() - ms(coolDown)
|
||||
) {
|
||||
// Revoke all user refresh tokens
|
||||
await this.prismaService.client.refreshToken.updateMany({
|
||||
where: {
|
||||
id: {
|
||||
in: user.refreshTokens.map(({ id }) => id),
|
||||
},
|
||||
},
|
||||
data: {
|
||||
revokedAt: new Date(),
|
||||
},
|
||||
});
|
||||
await Promise.all(
|
||||
user.refreshTokens.map(
|
||||
async ({ id }) =>
|
||||
await this.refreshTokenRepository.update(
|
||||
{ id },
|
||||
{
|
||||
revokedAt: new Date(),
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
throw new ForbiddenException(
|
||||
'Suspicious activity detected, this refresh token has been revoked. All tokens has been revoked.',
|
||||
@ -172,14 +209,14 @@ export class TokenService {
|
||||
} = await this.verifyRefreshToken(token);
|
||||
|
||||
// Revoke old refresh token
|
||||
await this.prismaService.client.refreshToken.update({
|
||||
where: {
|
||||
await this.refreshTokenRepository.update(
|
||||
{
|
||||
id,
|
||||
},
|
||||
data: {
|
||||
{
|
||||
revokedAt: new Date(),
|
||||
},
|
||||
});
|
||||
);
|
||||
|
||||
const accessToken = await this.generateAccessToken(user.id);
|
||||
const refreshToken = await this.generateRefreshToken(user.id);
|
||||
|
||||
@ -1,16 +1,13 @@
|
||||
import { PassportStrategy } from '@nestjs/passport';
|
||||
import {
|
||||
ForbiddenException,
|
||||
Injectable,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Strategy, ExtractJwt } from 'passport-jwt';
|
||||
import { User, Workspace } from '@prisma/client';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
||||
import { assert } from 'src/utils/assert';
|
||||
import { Workspace } from 'src/core/workspace/workspace.entity';
|
||||
import { User } from 'src/core/user/user.entity';
|
||||
|
||||
export type JwtPayload = { sub: string; workspaceId: string; jti?: string };
|
||||
export type PassportUser = { user?: User; workspace: Workspace };
|
||||
@ -19,7 +16,10 @@ export type PassportUser = { user?: User; workspace: Workspace };
|
||||
export class JwtAuthStrategy extends PassportStrategy(Strategy, 'jwt') {
|
||||
constructor(
|
||||
private readonly environmentService: EnvironmentService,
|
||||
private readonly prismaService: PrismaService,
|
||||
@InjectRepository(Workspace)
|
||||
private readonly workspaceRepository: Repository<Workspace>,
|
||||
@InjectRepository(User)
|
||||
private readonly userRepository: Repository<User>,
|
||||
) {
|
||||
super({
|
||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||
@ -29,26 +29,30 @@ export class JwtAuthStrategy extends PassportStrategy(Strategy, 'jwt') {
|
||||
}
|
||||
|
||||
async validate(payload: JwtPayload): Promise<PassportUser> {
|
||||
const workspace = await this.prismaService.client.workspace.findUnique({
|
||||
where: { id: payload.workspaceId ?? payload.sub },
|
||||
const workspace = await this.workspaceRepository.findOneBy({
|
||||
id: payload.workspaceId ?? payload.sub,
|
||||
});
|
||||
if (!workspace) {
|
||||
throw new UnauthorizedException();
|
||||
}
|
||||
if (payload.jti) {
|
||||
// If apiKey has been deleted or revoked, we throw an error
|
||||
const apiKey = await this.prismaService.client.apiKey.findUniqueOrThrow({
|
||||
where: { id: payload.jti },
|
||||
});
|
||||
assert(!apiKey.revokedAt, 'This API Key is revoked', ForbiddenException);
|
||||
// const apiKey = await this.prismaService.client.apiKey.findUniqueOrThrow({
|
||||
// where: { id: payload.jti },
|
||||
// });
|
||||
// assert(!apiKey.revokedAt, 'This API Key is revoked', ForbiddenException);
|
||||
}
|
||||
|
||||
const user = payload.workspaceId
|
||||
? await this.prismaService.client.user.findUniqueOrThrow({
|
||||
where: { id: payload.sub },
|
||||
? await this.userRepository.findOneBy({
|
||||
id: payload.sub,
|
||||
})
|
||||
: undefined;
|
||||
|
||||
if (!user) {
|
||||
throw new UnauthorizedException();
|
||||
}
|
||||
|
||||
return { user, workspace };
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { AbilityModule } from 'src/ability/ability.module';
|
||||
import { PrismaModule } from 'src/database/prisma.module';
|
||||
|
||||
import { CommentService } from './comment.service';
|
||||
import { CommentResolver } from './comment.resolver';
|
||||
|
||||
@Module({
|
||||
imports: [AbilityModule, PrismaModule],
|
||||
providers: [CommentService, CommentResolver],
|
||||
exports: [CommentService],
|
||||
})
|
||||
export class CommentModule {}
|
||||
@ -1,32 +0,0 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { CommentService } from 'src/core/comment/comment.service';
|
||||
import { AbilityFactory } from 'src/ability/ability.factory';
|
||||
|
||||
import { CommentResolver } from './comment.resolver';
|
||||
|
||||
describe('CommentResolver', () => {
|
||||
let resolver: CommentResolver;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
CommentResolver,
|
||||
{
|
||||
provide: CommentService,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: AbilityFactory,
|
||||
useValue: {},
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
resolver = module.get<CommentResolver>(CommentResolver);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(resolver).toBeDefined();
|
||||
});
|
||||
});
|
||||
@ -1,47 +0,0 @@
|
||||
import { Resolver, Args, Mutation } from '@nestjs/graphql';
|
||||
import { UseGuards } from '@nestjs/common';
|
||||
|
||||
import { Prisma } from '@prisma/client';
|
||||
|
||||
import { JwtAuthGuard } from 'src/guards/jwt.auth.guard';
|
||||
import { Workspace } from 'src/core/@generated/workspace/workspace.model';
|
||||
import { AuthWorkspace } from 'src/decorators/auth-workspace.decorator';
|
||||
import { CreateOneCommentArgs } from 'src/core/@generated/comment/create-one-comment.args';
|
||||
import { Comment } from 'src/core/@generated/comment/comment.model';
|
||||
import { CommentService } from 'src/core/comment/comment.service';
|
||||
import {
|
||||
PrismaSelector,
|
||||
PrismaSelect,
|
||||
} from 'src/decorators/prisma-select.decorator';
|
||||
import { AbilityGuard } from 'src/guards/ability.guard';
|
||||
import { CheckAbilities } from 'src/decorators/check-abilities.decorator';
|
||||
import { CreateCommentAbilityHandler } from 'src/ability/handlers/comment.ability-handler';
|
||||
import { AuthUser } from 'src/decorators/auth-user.decorator';
|
||||
import { User } from 'src/core/@generated/user/user.model';
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Resolver(() => Comment)
|
||||
export class CommentResolver {
|
||||
constructor(private readonly commentService: CommentService) {}
|
||||
|
||||
@Mutation(() => Comment, {
|
||||
nullable: false,
|
||||
})
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(CreateCommentAbilityHandler)
|
||||
async createOneComment(
|
||||
@Args() args: CreateOneCommentArgs,
|
||||
@AuthUser() user: User,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
@PrismaSelector({ modelName: 'Comment' })
|
||||
prismaSelect: PrismaSelect<'Comment'>,
|
||||
): Promise<Partial<Comment>> {
|
||||
return this.commentService.create({
|
||||
data: {
|
||||
...args.data,
|
||||
...{ workspace: { connect: { id: workspace.id } } },
|
||||
},
|
||||
select: prismaSelect.value,
|
||||
} as Prisma.CommentCreateArgs);
|
||||
}
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { prismaMock } from 'src/database/client-mock/jest-prisma-singleton';
|
||||
|
||||
import { CommentService } from './comment.service';
|
||||
|
||||
describe('CommentService', () => {
|
||||
let service: CommentService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
CommentService,
|
||||
{
|
||||
provide: PrismaService,
|
||||
useValue: prismaMock,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get<CommentService>(CommentService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
@ -1,39 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class CommentService {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
// Find
|
||||
findFirst = this.prismaService.client.comment.findFirst;
|
||||
findFirstOrThrow = this.prismaService.client.comment.findFirstOrThrow;
|
||||
|
||||
findUnique = this.prismaService.client.comment.findUnique;
|
||||
findUniqueOrThrow = this.prismaService.client.comment.findUniqueOrThrow;
|
||||
|
||||
findMany = this.prismaService.client.comment.findMany;
|
||||
|
||||
// Create
|
||||
create = this.prismaService.client.comment.create;
|
||||
createMany = this.prismaService.client.comment.createMany;
|
||||
|
||||
// Update
|
||||
update = this.prismaService.client.comment.update;
|
||||
upsert = this.prismaService.client.comment.upsert;
|
||||
updateMany = this.prismaService.client.comment.updateMany;
|
||||
|
||||
// Delete
|
||||
delete = this.prismaService.client.comment.delete;
|
||||
deleteMany = this.prismaService.client.comment.deleteMany;
|
||||
|
||||
// Aggregate
|
||||
aggregate = this.prismaService.client.comment.aggregate;
|
||||
|
||||
// Count
|
||||
count = this.prismaService.client.comment.count;
|
||||
|
||||
// GroupBy
|
||||
groupBy = this.prismaService.client.comment.groupBy;
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { CommentService } from 'src/core/comment/comment.service';
|
||||
import { ActivityService } from 'src/core/activity/services/activity.service';
|
||||
|
||||
import { CompanyRelationsResolver } from './company-relations.resolver';
|
||||
import { CompanyService } from './company.service';
|
||||
|
||||
describe('CompanyRelationsResolver', () => {
|
||||
let resolver: CompanyRelationsResolver;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
CompanyRelationsResolver,
|
||||
{
|
||||
provide: CompanyService,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: ActivityService,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: CommentService,
|
||||
useValue: {},
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
resolver = module.get<CompanyRelationsResolver>(CompanyRelationsResolver);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(resolver).toBeDefined();
|
||||
});
|
||||
});
|
||||
@ -1,76 +0,0 @@
|
||||
import { Resolver, ResolveField, Root, Int } from '@nestjs/graphql';
|
||||
|
||||
import { Comment } from 'src/core/@generated/comment/comment.model';
|
||||
import { Company } from 'src/core/@generated/company/company.model';
|
||||
import { CommentService } from 'src/core/comment/comment.service';
|
||||
import {
|
||||
PrismaSelect,
|
||||
PrismaSelector,
|
||||
} from 'src/decorators/prisma-select.decorator';
|
||||
import { ActivityService } from 'src/core/activity/services/activity.service';
|
||||
import { Activity } from 'src/core/@generated/activity/activity.model';
|
||||
|
||||
@Resolver(() => Company)
|
||||
export class CompanyRelationsResolver {
|
||||
constructor(
|
||||
private readonly activityService: ActivityService,
|
||||
private readonly commentService: CommentService,
|
||||
) {}
|
||||
|
||||
@ResolveField(() => [Activity], {
|
||||
nullable: false,
|
||||
})
|
||||
async activities(
|
||||
@Root() company: Company,
|
||||
@PrismaSelector({ modelName: 'Activity' })
|
||||
prismaSelect: PrismaSelect<'Activity'>,
|
||||
): Promise<Partial<Activity>[]> {
|
||||
return this.activityService.findMany({
|
||||
where: {
|
||||
activityTargets: {
|
||||
some: {
|
||||
companyId: company.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
select: prismaSelect.value,
|
||||
});
|
||||
}
|
||||
|
||||
@ResolveField(() => [Comment], {
|
||||
nullable: false,
|
||||
})
|
||||
async comments(
|
||||
@Root() company: Company,
|
||||
@PrismaSelector({ modelName: 'Comment' })
|
||||
prismaSelect: PrismaSelect<'Comment'>,
|
||||
): Promise<Partial<Comment>[]> {
|
||||
return this.commentService.findMany({
|
||||
where: {
|
||||
activity: {
|
||||
activityTargets: {
|
||||
some: {
|
||||
companyId: company.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
select: prismaSelect.value,
|
||||
});
|
||||
}
|
||||
|
||||
@ResolveField(() => Int, {
|
||||
nullable: false,
|
||||
})
|
||||
async _activityCount(@Root() company: Company): Promise<number> {
|
||||
return this.activityService.count({
|
||||
where: {
|
||||
activityTargets: {
|
||||
some: {
|
||||
companyId: company.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { CommentModule } from 'src/core/comment/comment.module';
|
||||
import { ActivityModule } from 'src/core/activity/activity.module';
|
||||
import { AbilityModule } from 'src/ability/ability.module';
|
||||
import { PrismaModule } from 'src/database/prisma.module';
|
||||
|
||||
import { CompanyService } from './company.service';
|
||||
import { CompanyResolver } from './company.resolver';
|
||||
import { CompanyRelationsResolver } from './company-relations.resolver';
|
||||
|
||||
@Module({
|
||||
imports: [CommentModule, ActivityModule, AbilityModule, PrismaModule],
|
||||
providers: [CompanyService, CompanyResolver, CompanyRelationsResolver],
|
||||
exports: [CompanyService],
|
||||
})
|
||||
export class CompanyModule {}
|
||||
@ -1,32 +0,0 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { AbilityFactory } from 'src/ability/ability.factory';
|
||||
|
||||
import { CompanyService } from './company.service';
|
||||
import { CompanyResolver } from './company.resolver';
|
||||
|
||||
describe('CompanyResolver', () => {
|
||||
let resolver: CompanyResolver;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
CompanyResolver,
|
||||
{
|
||||
provide: CompanyService,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: AbilityFactory,
|
||||
useValue: {},
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
resolver = module.get<CompanyResolver>(CompanyResolver);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(resolver).toBeDefined();
|
||||
});
|
||||
});
|
||||
@ -1,145 +0,0 @@
|
||||
import { Resolver, Query, Args, Mutation } from '@nestjs/graphql';
|
||||
import { UseGuards } from '@nestjs/common';
|
||||
|
||||
import { Prisma, Workspace } from '@prisma/client';
|
||||
import { accessibleBy } from '@casl/prisma';
|
||||
|
||||
import { JwtAuthGuard } from 'src/guards/jwt.auth.guard';
|
||||
import { AuthWorkspace } from 'src/decorators/auth-workspace.decorator';
|
||||
import { Company } from 'src/core/@generated/company/company.model';
|
||||
import { FindManyCompanyArgs } from 'src/core/@generated/company/find-many-company.args';
|
||||
import { UpdateOneCompanyArgs } from 'src/core/@generated/company/update-one-company.args';
|
||||
import { CreateOneCompanyArgs } from 'src/core/@generated/company/create-one-company.args';
|
||||
import { AffectedRows } from 'src/core/@generated/prisma/affected-rows.output';
|
||||
import { DeleteManyCompanyArgs } from 'src/core/@generated/company/delete-many-company.args';
|
||||
import {
|
||||
PrismaSelect,
|
||||
PrismaSelector,
|
||||
} from 'src/decorators/prisma-select.decorator';
|
||||
import { AbilityGuard } from 'src/guards/ability.guard';
|
||||
import { CheckAbilities } from 'src/decorators/check-abilities.decorator';
|
||||
import {
|
||||
CreateCompanyAbilityHandler,
|
||||
DeleteCompanyAbilityHandler,
|
||||
ReadOneCompanyAbilityHandler,
|
||||
UpdateCompanyAbilityHandler,
|
||||
} from 'src/ability/handlers/company.ability-handler';
|
||||
import { UserAbility } from 'src/decorators/user-ability.decorator';
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
import { FindUniqueCompanyArgs } from 'src/core/@generated/company/find-unique-company.args';
|
||||
import { CreateManyCompanyArgs } from 'src/core/@generated/company/create-many-company.args';
|
||||
|
||||
import { CompanyService } from './company.service';
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Resolver(() => Company)
|
||||
export class CompanyResolver {
|
||||
constructor(private readonly companyService: CompanyService) {}
|
||||
|
||||
@Query(() => [Company])
|
||||
@UseGuards(AbilityGuard)
|
||||
async findManyCompany(
|
||||
@Args() args: FindManyCompanyArgs,
|
||||
@UserAbility() ability: AppAbility,
|
||||
@PrismaSelector({ modelName: 'Company' })
|
||||
prismaSelect: PrismaSelect<'Company'>,
|
||||
): Promise<Partial<Company>[]> {
|
||||
return this.companyService.findMany({
|
||||
where: args.where
|
||||
? {
|
||||
AND: [args.where, accessibleBy(ability).Company],
|
||||
}
|
||||
: accessibleBy(ability).Company,
|
||||
orderBy: args.orderBy,
|
||||
cursor: args.cursor,
|
||||
take: args.take,
|
||||
skip: args.skip,
|
||||
distinct: args.distinct,
|
||||
select: prismaSelect.value,
|
||||
});
|
||||
}
|
||||
|
||||
@Query(() => Company)
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(ReadOneCompanyAbilityHandler)
|
||||
async findUniqueCompany(
|
||||
@Args() args: FindUniqueCompanyArgs,
|
||||
@PrismaSelector({ modelName: 'Company' })
|
||||
prismaSelect: PrismaSelect<'Company'>,
|
||||
): Promise<Partial<Company>> {
|
||||
const company = this.companyService.findUniqueOrThrow({
|
||||
where: args.where,
|
||||
select: prismaSelect.value,
|
||||
});
|
||||
|
||||
return company;
|
||||
}
|
||||
|
||||
@Mutation(() => Company, {
|
||||
nullable: true,
|
||||
})
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(UpdateCompanyAbilityHandler)
|
||||
async updateOneCompany(
|
||||
@Args() args: UpdateOneCompanyArgs,
|
||||
@PrismaSelector({ modelName: 'Company' })
|
||||
prismaSelect: PrismaSelect<'Company'>,
|
||||
): Promise<Partial<Company> | null> {
|
||||
return this.companyService.update({
|
||||
where: args.where,
|
||||
data: args.data,
|
||||
select: prismaSelect.value,
|
||||
} as Prisma.CompanyUpdateArgs);
|
||||
}
|
||||
|
||||
@Mutation(() => AffectedRows, {
|
||||
nullable: false,
|
||||
})
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(DeleteCompanyAbilityHandler)
|
||||
async deleteManyCompany(
|
||||
@Args() args: DeleteManyCompanyArgs,
|
||||
): Promise<AffectedRows> {
|
||||
return this.companyService.deleteMany({
|
||||
where: args.where,
|
||||
});
|
||||
}
|
||||
|
||||
@Mutation(() => Company, {
|
||||
nullable: false,
|
||||
})
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(CreateCompanyAbilityHandler)
|
||||
async createOneCompany(
|
||||
@Args() args: CreateOneCompanyArgs,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
@PrismaSelector({ modelName: 'Company' })
|
||||
prismaSelect: PrismaSelect<'Company'>,
|
||||
): Promise<Partial<Company>> {
|
||||
return this.companyService.create({
|
||||
data: {
|
||||
...args.data,
|
||||
...{ workspace: { connect: { id: workspace.id } } },
|
||||
},
|
||||
select: prismaSelect.value,
|
||||
} as Prisma.CompanyCreateArgs);
|
||||
}
|
||||
|
||||
@Mutation(() => AffectedRows, {
|
||||
nullable: false,
|
||||
})
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(CreateCompanyAbilityHandler)
|
||||
async createManyCompany(
|
||||
@Args() args: CreateManyCompanyArgs,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
): Promise<Prisma.BatchPayload> {
|
||||
return this.companyService.createMany({
|
||||
data: args.data.map((company) => ({
|
||||
...company,
|
||||
workspaceId: workspace.id,
|
||||
})),
|
||||
skipDuplicates: args.skipDuplicates,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { prismaMock } from 'src/database/client-mock/jest-prisma-singleton';
|
||||
|
||||
import { CompanyService } from './company.service';
|
||||
|
||||
describe('CompanyService', () => {
|
||||
let service: CompanyService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
CompanyService,
|
||||
{
|
||||
provide: PrismaService,
|
||||
useValue: prismaMock,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get<CompanyService>(CompanyService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
@ -1,51 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import companiesSeed from 'src/core/company/seed-data/companies.json';
|
||||
|
||||
@Injectable()
|
||||
export class CompanyService {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
// Find
|
||||
findFirst = this.prismaService.client.company.findFirst;
|
||||
findFirstOrThrow = this.prismaService.client.company.findFirstOrThrow;
|
||||
|
||||
findUnique = this.prismaService.client.company.findUnique;
|
||||
findUniqueOrThrow = this.prismaService.client.company.findUniqueOrThrow;
|
||||
|
||||
findMany = this.prismaService.client.company.findMany;
|
||||
|
||||
// Create
|
||||
create = this.prismaService.client.company.create;
|
||||
createMany = this.prismaService.client.company.createMany;
|
||||
|
||||
// Update
|
||||
update = this.prismaService.client.company.update;
|
||||
upsert = this.prismaService.client.company.upsert;
|
||||
updateMany = this.prismaService.client.company.updateMany;
|
||||
|
||||
// Delete
|
||||
delete = this.prismaService.client.company.delete;
|
||||
deleteMany = this.prismaService.client.company.deleteMany;
|
||||
|
||||
// Aggregate
|
||||
aggregate = this.prismaService.client.company.aggregate;
|
||||
|
||||
// Count
|
||||
count = this.prismaService.client.company.count;
|
||||
|
||||
// GroupBy
|
||||
groupBy = this.prismaService.client.company.groupBy;
|
||||
async createDefaultCompanies({ workspaceId }: { workspaceId: string }) {
|
||||
const companies = companiesSeed.map((company) => ({
|
||||
...company,
|
||||
workspaceId,
|
||||
}));
|
||||
await this.createMany({
|
||||
data: companies,
|
||||
});
|
||||
|
||||
return this.findMany({ where: { workspaceId } });
|
||||
}
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
[
|
||||
{
|
||||
"name": "Airbnb",
|
||||
"domainName": "airbnb.com",
|
||||
"address": "San Francisco",
|
||||
"employees": 5000
|
||||
},
|
||||
{
|
||||
"name": "Qonto",
|
||||
"domainName": "qonto.com",
|
||||
"address": "San Francisco",
|
||||
"employees": 800
|
||||
},
|
||||
{
|
||||
"name": "Stripe",
|
||||
"domainName": "stripe.com",
|
||||
"address": "San Francisco",
|
||||
"employees": 8000
|
||||
},
|
||||
{
|
||||
"name": "Figma",
|
||||
"domainName": "figma.com",
|
||||
"address": "San Francisco",
|
||||
"employees": 800
|
||||
},
|
||||
{
|
||||
"name": "Notion",
|
||||
"domainName": "notion.com",
|
||||
"address": "San Francisco",
|
||||
"employees": 400
|
||||
}
|
||||
]
|
||||
|
||||
@ -1,62 +1,24 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { WebHookModule } from 'src/core/web-hook/web-hook.module';
|
||||
import { UserModule as UserV2Module } from 'src/coreV2/user/user.module';
|
||||
import { RefreshTokenModule as RefreshTokenV2Module } from 'src/coreV2/refresh-token/refresh-token.module';
|
||||
import { WorkspaceModule as WorkspaceV2Module } from 'src/coreV2/workspace/workspace.module';
|
||||
import { WorkspaceModule } from 'src/core/workspace/workspace.module';
|
||||
import { UserModule } from 'src/core/user/user.module';
|
||||
import { RefreshTokenModule } from 'src/core/refresh-token/refresh-token.module';
|
||||
import { AuthModule } from 'src/core/auth/auth.module';
|
||||
|
||||
import { UserModule } from './user/user.module';
|
||||
import { CommentModule } from './comment/comment.module';
|
||||
import { CompanyModule } from './company/company.module';
|
||||
import { PersonModule } from './person/person.module';
|
||||
import { PipelineModule } from './pipeline/pipeline.module';
|
||||
import { AuthModule } from './auth/auth.module';
|
||||
import { WorkspaceModule } from './workspace/workspace.module';
|
||||
import { AnalyticsModule } from './analytics/analytics.module';
|
||||
import { FileModule } from './file/file.module';
|
||||
import { ClientConfigModule } from './client-config/client-config.module';
|
||||
import { AttachmentModule } from './attachment/attachment.module';
|
||||
import { ActivityModule } from './activity/activity.module';
|
||||
import { FavoriteModule } from './favorite/favorite.module';
|
||||
import { ApiKeyModule } from './api-key/api-key.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
AuthModule,
|
||||
UserModule,
|
||||
CommentModule,
|
||||
CompanyModule,
|
||||
PersonModule,
|
||||
PipelineModule,
|
||||
WorkspaceModule,
|
||||
UserModule,
|
||||
RefreshTokenModule,
|
||||
AnalyticsModule,
|
||||
FileModule,
|
||||
ClientConfigModule,
|
||||
AttachmentModule,
|
||||
ActivityModule,
|
||||
FavoriteModule,
|
||||
ApiKeyModule,
|
||||
WebHookModule,
|
||||
UserV2Module,
|
||||
RefreshTokenV2Module,
|
||||
WorkspaceV2Module,
|
||||
],
|
||||
exports: [
|
||||
AuthModule,
|
||||
UserModule,
|
||||
CommentModule,
|
||||
CompanyModule,
|
||||
PersonModule,
|
||||
PipelineModule,
|
||||
WorkspaceModule,
|
||||
AnalyticsModule,
|
||||
AttachmentModule,
|
||||
FavoriteModule,
|
||||
ApiKeyModule,
|
||||
WebHookModule,
|
||||
UserV2Module,
|
||||
RefreshTokenV2Module,
|
||||
WorkspaceV2Module,
|
||||
],
|
||||
exports: [AuthModule, WorkspaceModule, UserModule, AnalyticsModule],
|
||||
})
|
||||
export class CoreModule {}
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { AbilityModule } from 'src/ability/ability.module';
|
||||
import { PrismaModule } from 'src/database/prisma.module';
|
||||
|
||||
import { FavoriteResolver } from './resolvers/favorite.resolver';
|
||||
import { FavoriteService } from './services/favorite.service';
|
||||
|
||||
@Module({
|
||||
imports: [AbilityModule, PrismaModule],
|
||||
providers: [FavoriteService, FavoriteResolver],
|
||||
exports: [FavoriteService],
|
||||
})
|
||||
export class FavoriteModule {}
|
||||
@ -1,175 +0,0 @@
|
||||
import {
|
||||
Resolver,
|
||||
Query,
|
||||
Args,
|
||||
Mutation,
|
||||
InputType,
|
||||
Field,
|
||||
} from '@nestjs/graphql';
|
||||
import { UseGuards } from '@nestjs/common';
|
||||
|
||||
import { Prisma, Workspace } from '@prisma/client';
|
||||
|
||||
import {
|
||||
PrismaSelect,
|
||||
PrismaSelector,
|
||||
} from 'src/decorators/prisma-select.decorator';
|
||||
import { JwtAuthGuard } from 'src/guards/jwt.auth.guard';
|
||||
import { Favorite } from 'src/core/@generated/favorite/favorite.model';
|
||||
import { AbilityGuard } from 'src/guards/ability.guard';
|
||||
import { CheckAbilities } from 'src/decorators/check-abilities.decorator';
|
||||
import {
|
||||
CreateFavoriteAbilityHandler,
|
||||
DeleteFavoriteAbilityHandler,
|
||||
ReadFavoriteAbilityHandler,
|
||||
UpdateFavoriteAbilityHandler,
|
||||
} from 'src/ability/handlers/favorite.ability-handler';
|
||||
import { AuthWorkspace } from 'src/decorators/auth-workspace.decorator';
|
||||
import { FavoriteService } from 'src/core/favorite/services/favorite.service';
|
||||
import { FavoriteWhereInput } from 'src/core/@generated/favorite/favorite-where.input';
|
||||
import { SortOrder } from 'src/core/@generated/prisma/sort-order.enum';
|
||||
import { UpdateOneFavoriteArgs } from 'src/core/@generated/favorite/update-one-favorite.args';
|
||||
|
||||
@InputType()
|
||||
class FavoriteMutationForPersonArgs {
|
||||
@Field(() => String)
|
||||
personId: string;
|
||||
@Field(() => Number)
|
||||
position: number;
|
||||
}
|
||||
|
||||
@InputType()
|
||||
class FavoriteMutationForCompanyArgs {
|
||||
@Field(() => String)
|
||||
companyId: string;
|
||||
@Field(() => Number)
|
||||
position: number;
|
||||
}
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Resolver(() => Favorite)
|
||||
export class FavoriteResolver {
|
||||
constructor(private readonly favoriteService: FavoriteService) {}
|
||||
|
||||
@Query(() => [Favorite])
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(ReadFavoriteAbilityHandler)
|
||||
async findFavorites(
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
): Promise<Partial<Favorite>[]> {
|
||||
const favorites = await this.favoriteService.findMany({
|
||||
where: {
|
||||
workspaceId: workspace.id,
|
||||
},
|
||||
orderBy: [{ position: SortOrder.asc }],
|
||||
include: {
|
||||
person: true,
|
||||
company: {
|
||||
include: {
|
||||
accountOwner: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return favorites;
|
||||
}
|
||||
|
||||
@Mutation(() => Favorite, {
|
||||
nullable: false,
|
||||
})
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(CreateFavoriteAbilityHandler)
|
||||
async createFavoriteForPerson(
|
||||
@Args('data') args: FavoriteMutationForPersonArgs,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
@PrismaSelector({ modelName: 'Favorite' })
|
||||
prismaSelect: PrismaSelect<'Favorite'>,
|
||||
): Promise<Partial<Favorite>> {
|
||||
//To avoid duplicates we first fetch all favorites assinged by workspace
|
||||
const favorite = await this.favoriteService.findFirst({
|
||||
where: { workspaceId: workspace.id, personId: args.personId },
|
||||
});
|
||||
|
||||
if (favorite) return favorite;
|
||||
|
||||
return this.favoriteService.create({
|
||||
data: {
|
||||
person: {
|
||||
connect: { id: args.personId },
|
||||
},
|
||||
workspaceId: workspace.id,
|
||||
position: args.position,
|
||||
},
|
||||
select: prismaSelect.value,
|
||||
});
|
||||
}
|
||||
|
||||
@Mutation(() => Favorite, {
|
||||
nullable: false,
|
||||
})
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(CreateFavoriteAbilityHandler)
|
||||
async createFavoriteForCompany(
|
||||
@Args('data') args: FavoriteMutationForCompanyArgs,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
@PrismaSelector({ modelName: 'Favorite' })
|
||||
prismaSelect: PrismaSelect<'Favorite'>,
|
||||
): Promise<Partial<Favorite>> {
|
||||
//To avoid duplicates we first fetch all favorites assinged by workspace
|
||||
const favorite = await this.favoriteService.findFirst({
|
||||
where: { workspaceId: workspace.id, companyId: args.companyId },
|
||||
});
|
||||
|
||||
if (favorite) return favorite;
|
||||
|
||||
return this.favoriteService.create({
|
||||
data: {
|
||||
company: {
|
||||
connect: { id: args.companyId },
|
||||
},
|
||||
workspaceId: workspace.id,
|
||||
position: args.position,
|
||||
},
|
||||
select: prismaSelect.value,
|
||||
});
|
||||
}
|
||||
|
||||
@Mutation(() => Favorite, {
|
||||
nullable: false,
|
||||
})
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(UpdateFavoriteAbilityHandler)
|
||||
async updateOneFavorites(
|
||||
@Args() args: UpdateOneFavoriteArgs,
|
||||
@PrismaSelector({ modelName: 'Favorite' })
|
||||
prismaSelect: PrismaSelect<'Favorite'>,
|
||||
): Promise<Partial<Favorite>> {
|
||||
return this.favoriteService.update({
|
||||
data: args.data,
|
||||
where: args.where,
|
||||
select: prismaSelect.value,
|
||||
} as Prisma.FavoriteUpdateArgs);
|
||||
}
|
||||
|
||||
@Mutation(() => Favorite, {
|
||||
nullable: false,
|
||||
})
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(DeleteFavoriteAbilityHandler)
|
||||
async deleteFavorite(
|
||||
@Args('where') args: FavoriteWhereInput,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
@PrismaSelector({ modelName: 'Favorite' })
|
||||
prismaSelect: PrismaSelect<'Favorite'>,
|
||||
): Promise<Partial<Favorite>> {
|
||||
const favorite = await this.favoriteService.findFirst({
|
||||
where: { ...args, workspaceId: workspace.id },
|
||||
});
|
||||
|
||||
return this.favoriteService.delete({
|
||||
where: { id: favorite?.id },
|
||||
select: prismaSelect.value,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,39 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class FavoriteService {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
// Find
|
||||
findFirst = this.prismaService.client.favorite.findFirst;
|
||||
findFirstOrThrow = this.prismaService.client.favorite.findFirstOrThrow;
|
||||
|
||||
findUnique = this.prismaService.client.favorite.findUnique;
|
||||
findUniqueOrThrow = this.prismaService.client.favorite.findUniqueOrThrow;
|
||||
|
||||
findMany = this.prismaService.client.favorite.findMany;
|
||||
|
||||
// Create
|
||||
create = this.prismaService.client.favorite.create;
|
||||
createMany = this.prismaService.client.favorite.createMany;
|
||||
|
||||
// Update
|
||||
update = this.prismaService.client.favorite.update;
|
||||
upsert = this.prismaService.client.favorite.upsert;
|
||||
updateMany = this.prismaService.client.favorite.updateMany;
|
||||
|
||||
// Delete
|
||||
delete = this.prismaService.client.favorite.delete;
|
||||
deleteMany = this.prismaService.client.favorite.deleteMany;
|
||||
|
||||
// Aggregate
|
||||
aggregate = this.prismaService.client.favorite.aggregate;
|
||||
|
||||
// Count
|
||||
count = this.prismaService.client.favorite.count;
|
||||
|
||||
// GroupBy
|
||||
groupBy = this.prismaService.client.favorite.groupBy;
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { CommentService } from 'src/core/comment/comment.service';
|
||||
import { ActivityService } from 'src/core/activity/services/activity.service';
|
||||
|
||||
import { PersonRelationsResolver } from './person-relations.resolver';
|
||||
import { PersonService } from './person.service';
|
||||
|
||||
describe('PersonRelationsResolver', () => {
|
||||
let resolver: PersonRelationsResolver;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
PersonRelationsResolver,
|
||||
{
|
||||
provide: PersonService,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: ActivityService,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: CommentService,
|
||||
useValue: {},
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
resolver = module.get<PersonRelationsResolver>(PersonRelationsResolver);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(resolver).toBeDefined();
|
||||
});
|
||||
});
|
||||
@ -1,76 +0,0 @@
|
||||
import { Resolver, Root, ResolveField, Int } from '@nestjs/graphql';
|
||||
|
||||
import { Comment } from 'src/core/@generated/comment/comment.model';
|
||||
import { Person } from 'src/core/@generated/person/person.model';
|
||||
import { CommentService } from 'src/core/comment/comment.service';
|
||||
import {
|
||||
PrismaSelect,
|
||||
PrismaSelector,
|
||||
} from 'src/decorators/prisma-select.decorator';
|
||||
import { Activity } from 'src/core/@generated/activity/activity.model';
|
||||
import { ActivityService } from 'src/core/activity/services/activity.service';
|
||||
|
||||
@Resolver(() => Person)
|
||||
export class PersonRelationsResolver {
|
||||
constructor(
|
||||
private readonly activityService: ActivityService,
|
||||
private readonly commentService: CommentService,
|
||||
) {}
|
||||
|
||||
@ResolveField(() => [Activity], {
|
||||
nullable: false,
|
||||
})
|
||||
async activities(
|
||||
@Root() person: Person,
|
||||
@PrismaSelector({ modelName: 'Activity' })
|
||||
prismaSelect: PrismaSelect<'Activity'>,
|
||||
): Promise<Partial<Activity>[]> {
|
||||
return await this.activityService.findMany({
|
||||
where: {
|
||||
activityTargets: {
|
||||
some: {
|
||||
personId: person.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
select: prismaSelect.value,
|
||||
});
|
||||
}
|
||||
|
||||
@ResolveField(() => [Comment], {
|
||||
nullable: false,
|
||||
})
|
||||
async comments(
|
||||
@Root() person: Person,
|
||||
@PrismaSelector({ modelName: 'Comment' })
|
||||
prismaSelect: PrismaSelect<'Comment'>,
|
||||
): Promise<Partial<Comment>[]> {
|
||||
return this.commentService.findMany({
|
||||
where: {
|
||||
activity: {
|
||||
activityTargets: {
|
||||
some: {
|
||||
personId: person.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
select: prismaSelect.value,
|
||||
});
|
||||
}
|
||||
|
||||
@ResolveField(() => Int, {
|
||||
nullable: false,
|
||||
})
|
||||
async _activityCount(@Root() person: Person): Promise<number> {
|
||||
return this.activityService.count({
|
||||
where: {
|
||||
activityTargets: {
|
||||
some: {
|
||||
personId: person.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { CommentModule } from 'src/core/comment/comment.module';
|
||||
import { ActivityModule } from 'src/core/activity/activity.module';
|
||||
import { FileModule } from 'src/core/file/file.module';
|
||||
import { AbilityModule } from 'src/ability/ability.module';
|
||||
import { PrismaModule } from 'src/database/prisma.module';
|
||||
|
||||
import { PersonService } from './person.service';
|
||||
import { PersonResolver } from './person.resolver';
|
||||
import { PersonRelationsResolver } from './person-relations.resolver';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
CommentModule,
|
||||
ActivityModule,
|
||||
FileModule,
|
||||
AbilityModule,
|
||||
PrismaModule,
|
||||
],
|
||||
providers: [PersonService, PersonResolver, PersonRelationsResolver],
|
||||
exports: [PersonService],
|
||||
})
|
||||
export class PersonModule {}
|
||||
@ -1,37 +0,0 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { AbilityFactory } from 'src/ability/ability.factory';
|
||||
import { FileUploadService } from 'src/core/file/services/file-upload.service';
|
||||
|
||||
import { PersonService } from './person.service';
|
||||
import { PersonResolver } from './person.resolver';
|
||||
|
||||
describe('PersonResolver', () => {
|
||||
let resolver: PersonResolver;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
PersonResolver,
|
||||
{
|
||||
provide: PersonService,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: AbilityFactory,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: FileUploadService,
|
||||
useValue: {},
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
resolver = module.get<PersonResolver>(PersonResolver);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(resolver).toBeDefined();
|
||||
});
|
||||
});
|
||||
@ -1,214 +0,0 @@
|
||||
import {
|
||||
Resolver,
|
||||
Query,
|
||||
Args,
|
||||
Mutation,
|
||||
ResolveField,
|
||||
Parent,
|
||||
} from '@nestjs/graphql';
|
||||
import { UseGuards } from '@nestjs/common';
|
||||
|
||||
import { accessibleBy } from '@casl/prisma';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { FileUpload, GraphQLUpload } from 'graphql-upload';
|
||||
|
||||
import { FileFolder } from 'src/core/file/interfaces/file-folder.interface';
|
||||
|
||||
import { JwtAuthGuard } from 'src/guards/jwt.auth.guard';
|
||||
import { Person } from 'src/core/@generated/person/person.model';
|
||||
import { FindManyPersonArgs } from 'src/core/@generated/person/find-many-person.args';
|
||||
import { UpdateOnePersonArgs } from 'src/core/@generated/person/update-one-person.args';
|
||||
import { CreateOnePersonArgs } from 'src/core/@generated/person/create-one-person.args';
|
||||
import { AffectedRows } from 'src/core/@generated/prisma/affected-rows.output';
|
||||
import { DeleteManyPersonArgs } from 'src/core/@generated/person/delete-many-person.args';
|
||||
import { AuthWorkspace } from 'src/decorators/auth-workspace.decorator';
|
||||
import {
|
||||
PrismaSelect,
|
||||
PrismaSelector,
|
||||
} from 'src/decorators/prisma-select.decorator';
|
||||
import { AbilityGuard } from 'src/guards/ability.guard';
|
||||
import { CheckAbilities } from 'src/decorators/check-abilities.decorator';
|
||||
import {
|
||||
CreatePersonAbilityHandler,
|
||||
DeletePersonAbilityHandler,
|
||||
ReadPersonAbilityHandler,
|
||||
UpdatePersonAbilityHandler,
|
||||
} from 'src/ability/handlers/person.ability-handler';
|
||||
import { UserAbility } from 'src/decorators/user-ability.decorator';
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
import { Workspace } from 'src/core/@generated/workspace/workspace.model';
|
||||
import { streamToBuffer } from 'src/utils/stream-to-buffer';
|
||||
import { FileUploadService } from 'src/core/file/services/file-upload.service';
|
||||
import { CreateManyPersonArgs } from 'src/core/@generated/person/create-many-person.args';
|
||||
|
||||
import { PersonService } from './person.service';
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Resolver(() => Person)
|
||||
export class PersonResolver {
|
||||
constructor(
|
||||
private readonly personService: PersonService,
|
||||
private readonly fileUploadService: FileUploadService,
|
||||
) {}
|
||||
|
||||
@Query(() => [Person], {
|
||||
nullable: false,
|
||||
})
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(ReadPersonAbilityHandler)
|
||||
async findManyPerson(
|
||||
@Args() args: FindManyPersonArgs,
|
||||
@UserAbility() ability: AppAbility,
|
||||
@PrismaSelector({ modelName: 'Person' })
|
||||
prismaSelect: PrismaSelect<'Person'>,
|
||||
): Promise<Partial<Person>[]> {
|
||||
return this.personService.findMany({
|
||||
where: args.where
|
||||
? {
|
||||
AND: [args.where, accessibleBy(ability).Person],
|
||||
}
|
||||
: accessibleBy(ability).Person,
|
||||
orderBy: args.orderBy,
|
||||
cursor: args.cursor,
|
||||
take: args.take,
|
||||
skip: args.skip,
|
||||
distinct: args.distinct,
|
||||
select: prismaSelect.value,
|
||||
});
|
||||
}
|
||||
|
||||
@Query(() => Person)
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(ReadPersonAbilityHandler)
|
||||
async findUniquePerson(
|
||||
@Args('id') id: string,
|
||||
@UserAbility() ability: AppAbility,
|
||||
@PrismaSelector({ modelName: 'Person' })
|
||||
prismaSelect: PrismaSelect<'Person'>,
|
||||
): Promise<Partial<Person>> {
|
||||
return this.personService.findUniqueOrThrow({
|
||||
where: {
|
||||
id: id,
|
||||
},
|
||||
select: prismaSelect.value,
|
||||
});
|
||||
}
|
||||
|
||||
@ResolveField(() => String, {
|
||||
nullable: false,
|
||||
})
|
||||
displayName(@Parent() parent: Person): string {
|
||||
return `${parent.firstName ?? ''} ${parent.lastName ?? ''}`;
|
||||
}
|
||||
|
||||
@Mutation(() => Person, {
|
||||
nullable: true,
|
||||
})
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(UpdatePersonAbilityHandler)
|
||||
async updateOnePerson(
|
||||
@Args() args: UpdateOnePersonArgs,
|
||||
@PrismaSelector({ modelName: 'Person' })
|
||||
prismaSelect: PrismaSelect<'Person'>,
|
||||
): Promise<Partial<Person> | null> {
|
||||
// TODO: Do a proper check with recursion testing on args in a more generic place
|
||||
for (const key in args.data) {
|
||||
if (args.data[key]) {
|
||||
for (const subKey in args.data[key]) {
|
||||
if (JSON.stringify(args.data[key][subKey]) === '{}') {
|
||||
delete args.data[key][subKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (JSON.stringify(args.data[key]) === '{}') {
|
||||
delete args.data[key];
|
||||
}
|
||||
}
|
||||
|
||||
return this.personService.update({
|
||||
where: args.where,
|
||||
data: args.data,
|
||||
select: prismaSelect.value,
|
||||
} as Prisma.PersonUpdateArgs);
|
||||
}
|
||||
|
||||
@Mutation(() => AffectedRows, {
|
||||
nullable: false,
|
||||
})
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(DeletePersonAbilityHandler)
|
||||
async deleteManyPerson(
|
||||
@Args() args: DeleteManyPersonArgs,
|
||||
): Promise<AffectedRows> {
|
||||
return this.personService.deleteMany({
|
||||
where: args.where,
|
||||
});
|
||||
}
|
||||
|
||||
@Mutation(() => Person, {
|
||||
nullable: false,
|
||||
})
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(CreatePersonAbilityHandler)
|
||||
async createOnePerson(
|
||||
@Args() args: CreateOnePersonArgs,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
@PrismaSelector({ modelName: 'Person' })
|
||||
prismaSelect: PrismaSelect<'Person'>,
|
||||
): Promise<Partial<Person>> {
|
||||
return this.personService.create({
|
||||
data: {
|
||||
...args.data,
|
||||
...{ workspace: { connect: { id: workspace.id } } },
|
||||
},
|
||||
select: prismaSelect.value,
|
||||
} as Prisma.PersonCreateArgs);
|
||||
}
|
||||
|
||||
@Mutation(() => AffectedRows, {
|
||||
nullable: false,
|
||||
})
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(CreatePersonAbilityHandler)
|
||||
async createManyPerson(
|
||||
@Args() args: CreateManyPersonArgs,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
): Promise<Prisma.BatchPayload> {
|
||||
return this.personService.createMany({
|
||||
data: args.data.map((person) => ({
|
||||
...person,
|
||||
workspaceId: workspace.id,
|
||||
})),
|
||||
skipDuplicates: args.skipDuplicates,
|
||||
});
|
||||
}
|
||||
|
||||
@Mutation(() => String)
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(UpdatePersonAbilityHandler)
|
||||
async uploadPersonPicture(
|
||||
@Args('id') id: string,
|
||||
@Args({ name: 'file', type: () => GraphQLUpload })
|
||||
{ createReadStream, filename, mimetype }: FileUpload,
|
||||
): Promise<string> {
|
||||
const stream = createReadStream();
|
||||
const buffer = await streamToBuffer(stream);
|
||||
|
||||
const { paths } = await this.fileUploadService.uploadImage({
|
||||
file: buffer,
|
||||
filename,
|
||||
mimeType: mimetype,
|
||||
fileFolder: FileFolder.PersonPicture,
|
||||
});
|
||||
|
||||
await this.personService.update({
|
||||
where: { id },
|
||||
data: {
|
||||
avatarUrl: paths[0],
|
||||
},
|
||||
});
|
||||
|
||||
return paths[0];
|
||||
}
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { prismaMock } from 'src/database/client-mock/jest-prisma-singleton';
|
||||
|
||||
import { PersonService } from './person.service';
|
||||
|
||||
describe('PersonService', () => {
|
||||
let service: PersonService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
PersonService,
|
||||
{
|
||||
provide: PrismaService,
|
||||
useValue: prismaMock,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get<PersonService>(PersonService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
@ -1,58 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { Company } from '@prisma/client';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import peopleSeed from 'src/core/person/seed-data/people.json';
|
||||
|
||||
@Injectable()
|
||||
export class PersonService {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
// Find
|
||||
findFirst = this.prismaService.client.person.findFirst;
|
||||
findFirstOrThrow = this.prismaService.client.person.findFirstOrThrow;
|
||||
|
||||
findUnique = this.prismaService.client.person.findUnique;
|
||||
findUniqueOrThrow = this.prismaService.client.person.findUniqueOrThrow;
|
||||
|
||||
findMany = this.prismaService.client.person.findMany;
|
||||
|
||||
// Create
|
||||
create = this.prismaService.client.person.create;
|
||||
createMany = this.prismaService.client.person.createMany;
|
||||
|
||||
// Update
|
||||
update = this.prismaService.client.person.update;
|
||||
upsert = this.prismaService.client.person.upsert;
|
||||
updateMany = this.prismaService.client.person.updateMany;
|
||||
|
||||
// Delete
|
||||
delete = this.prismaService.client.person.delete;
|
||||
deleteMany = this.prismaService.client.person.deleteMany;
|
||||
|
||||
// Aggregate
|
||||
aggregate = this.prismaService.client.person.aggregate;
|
||||
|
||||
// Count
|
||||
count = this.prismaService.client.person.count;
|
||||
|
||||
// GroupBy
|
||||
groupBy = this.prismaService.client.person.groupBy;
|
||||
async createDefaultPeople({
|
||||
workspaceId,
|
||||
companies,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
companies: Company[];
|
||||
}) {
|
||||
const people = peopleSeed.map((person, i) => ({
|
||||
...person,
|
||||
companyId: companies[i].id || null,
|
||||
workspaceId,
|
||||
}));
|
||||
return this.createMany({
|
||||
data: people,
|
||||
});
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@ -1,25 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { AbilityModule } from 'src/ability/ability.module';
|
||||
import { PrismaModule } from 'src/database/prisma.module';
|
||||
|
||||
import { PipelineService } from './services/pipeline.service';
|
||||
import { PipelineResolver } from './resolvers/pipeline.resolver';
|
||||
import { PipelineStageResolver } from './resolvers/pipeline-stage.resolver';
|
||||
import { PipelineProgressResolver } from './resolvers/pipeline-progress.resolver';
|
||||
import { PipelineStageService } from './services/pipeline-stage.service';
|
||||
import { PipelineProgressService } from './services/pipeline-progress.service';
|
||||
|
||||
@Module({
|
||||
imports: [AbilityModule, PrismaModule],
|
||||
providers: [
|
||||
PipelineService,
|
||||
PipelineStageService,
|
||||
PipelineProgressService,
|
||||
PipelineResolver,
|
||||
PipelineStageResolver,
|
||||
PipelineProgressResolver,
|
||||
],
|
||||
exports: [PipelineService, PipelineStageService, PipelineProgressService],
|
||||
})
|
||||
export class PipelineModule {}
|
||||
@ -1,32 +0,0 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { PipelineProgressService } from 'src/core/pipeline/services/pipeline-progress.service';
|
||||
import { AbilityFactory } from 'src/ability/ability.factory';
|
||||
|
||||
import { PipelineProgressResolver } from './pipeline-progress.resolver';
|
||||
|
||||
describe('PipelineProgressResolver', () => {
|
||||
let resolver: PipelineProgressResolver;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
PipelineProgressResolver,
|
||||
{
|
||||
provide: PipelineProgressService,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: AbilityFactory,
|
||||
useValue: {},
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
resolver = module.get<PipelineProgressResolver>(PipelineProgressResolver);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(resolver).toBeDefined();
|
||||
});
|
||||
});
|
||||
@ -1,126 +0,0 @@
|
||||
import { Resolver, Args, Query, Mutation } from '@nestjs/graphql';
|
||||
import { UseGuards } from '@nestjs/common';
|
||||
|
||||
import { accessibleBy } from '@casl/prisma';
|
||||
import { Prisma } from '@prisma/client';
|
||||
|
||||
import { JwtAuthGuard } from 'src/guards/jwt.auth.guard';
|
||||
import { Workspace } from 'src/core/@generated/workspace/workspace.model';
|
||||
import { AuthWorkspace } from 'src/decorators/auth-workspace.decorator';
|
||||
import { FindManyPipelineProgressArgs } from 'src/core/@generated/pipeline-progress/find-many-pipeline-progress.args';
|
||||
import { PipelineProgress } from 'src/core/@generated/pipeline-progress/pipeline-progress.model';
|
||||
import { UpdateOnePipelineProgressArgs } from 'src/core/@generated/pipeline-progress/update-one-pipeline-progress.args';
|
||||
import { AffectedRows } from 'src/core/@generated/prisma/affected-rows.output';
|
||||
import { DeleteManyPipelineProgressArgs } from 'src/core/@generated/pipeline-progress/delete-many-pipeline-progress.args';
|
||||
import { CreateOnePipelineProgressArgs } from 'src/core/@generated/pipeline-progress/create-one-pipeline-progress.args';
|
||||
import { PipelineProgressService } from 'src/core/pipeline/services/pipeline-progress.service';
|
||||
import { AbilityGuard } from 'src/guards/ability.guard';
|
||||
import { CheckAbilities } from 'src/decorators/check-abilities.decorator';
|
||||
import {
|
||||
CreatePipelineProgressAbilityHandler,
|
||||
ReadPipelineProgressAbilityHandler,
|
||||
UpdatePipelineProgressAbilityHandler,
|
||||
DeletePipelineProgressAbilityHandler,
|
||||
} from 'src/ability/handlers/pipeline-progress.ability-handler';
|
||||
import { UserAbility } from 'src/decorators/user-ability.decorator';
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
import {
|
||||
PrismaSelector,
|
||||
PrismaSelect,
|
||||
} from 'src/decorators/prisma-select.decorator';
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Resolver(() => PipelineProgress)
|
||||
export class PipelineProgressResolver {
|
||||
constructor(
|
||||
private readonly pipelineProgressService: PipelineProgressService,
|
||||
) {}
|
||||
|
||||
@Query(() => [PipelineProgress])
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(ReadPipelineProgressAbilityHandler)
|
||||
async findManyPipelineProgress(
|
||||
@Args() args: FindManyPipelineProgressArgs,
|
||||
@UserAbility() ability: AppAbility,
|
||||
@PrismaSelector({ modelName: 'PipelineProgress' })
|
||||
prismaSelect: PrismaSelect<'PipelineProgress'>,
|
||||
): Promise<Partial<PipelineProgress>[]> {
|
||||
return this.pipelineProgressService.findMany({
|
||||
where: args.where
|
||||
? {
|
||||
AND: [args.where, accessibleBy(ability).PipelineProgress],
|
||||
}
|
||||
: accessibleBy(ability).PipelineProgress,
|
||||
orderBy: args.orderBy,
|
||||
cursor: args.cursor,
|
||||
take: args.take,
|
||||
skip: args.skip,
|
||||
distinct: args.distinct,
|
||||
select: prismaSelect.value,
|
||||
});
|
||||
}
|
||||
|
||||
@Mutation(() => PipelineProgress, {
|
||||
nullable: true,
|
||||
})
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(UpdatePipelineProgressAbilityHandler)
|
||||
async updateOnePipelineProgress(
|
||||
@Args() args: UpdateOnePipelineProgressArgs,
|
||||
@PrismaSelector({ modelName: 'PipelineProgress' })
|
||||
prismaSelect: PrismaSelect<'PipelineProgress'>,
|
||||
): Promise<Partial<PipelineProgress> | null> {
|
||||
// TODO: Do a proper check with recursion testing on args in a more generic place
|
||||
for (const key in args.data) {
|
||||
if (args.data[key]) {
|
||||
for (const subKey in args.data[key]) {
|
||||
if (JSON.stringify(args.data[key][subKey]) === '{}') {
|
||||
delete args.data[key][subKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (JSON.stringify(args.data[key]) === '{}') {
|
||||
delete args.data[key];
|
||||
}
|
||||
}
|
||||
return this.pipelineProgressService.update({
|
||||
where: args.where,
|
||||
data: args.data,
|
||||
select: prismaSelect.value,
|
||||
} as Prisma.PipelineProgressUpdateArgs);
|
||||
}
|
||||
|
||||
@Mutation(() => AffectedRows, {
|
||||
nullable: false,
|
||||
})
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(DeletePipelineProgressAbilityHandler)
|
||||
async deleteManyPipelineProgress(
|
||||
@Args() args: DeleteManyPipelineProgressArgs,
|
||||
): Promise<AffectedRows> {
|
||||
return this.pipelineProgressService.deleteMany({
|
||||
where: args.where,
|
||||
});
|
||||
}
|
||||
|
||||
@Mutation(() => PipelineProgress, {
|
||||
nullable: false,
|
||||
})
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(CreatePipelineProgressAbilityHandler)
|
||||
async createOnePipelineProgress(
|
||||
@Args() args: CreateOnePipelineProgressArgs,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
@PrismaSelector({ modelName: 'PipelineProgress' })
|
||||
prismaSelect: PrismaSelect<'PipelineProgress'>,
|
||||
): Promise<Partial<PipelineProgress>> {
|
||||
return this.pipelineProgressService.create({
|
||||
data: {
|
||||
...args.data,
|
||||
...{ workspace: { connect: { id: workspace.id } } },
|
||||
},
|
||||
select: prismaSelect.value,
|
||||
} as Prisma.PipelineProgressCreateArgs);
|
||||
}
|
||||
}
|
||||
@ -1,32 +0,0 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { PipelineStageService } from 'src/core/pipeline/services/pipeline-stage.service';
|
||||
import { AbilityFactory } from 'src/ability/ability.factory';
|
||||
|
||||
import { PipelineStageResolver } from './pipeline-stage.resolver';
|
||||
|
||||
describe('PipelineStageResolver', () => {
|
||||
let resolver: PipelineStageResolver;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
PipelineStageResolver,
|
||||
{
|
||||
provide: PipelineStageService,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: AbilityFactory,
|
||||
useValue: {},
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
resolver = module.get<PipelineStageResolver>(PipelineStageResolver);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(resolver).toBeDefined();
|
||||
});
|
||||
});
|
||||
@ -1,149 +0,0 @@
|
||||
import { Resolver, Args, Query, Mutation } from '@nestjs/graphql';
|
||||
import {
|
||||
ForbiddenException,
|
||||
NotFoundException,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
|
||||
import { accessibleBy } from '@casl/prisma';
|
||||
import { Prisma, Workspace } from '@prisma/client';
|
||||
|
||||
import { JwtAuthGuard } from 'src/guards/jwt.auth.guard';
|
||||
import { PipelineStage } from 'src/core/@generated/pipeline-stage/pipeline-stage.model';
|
||||
import { FindManyPipelineStageArgs } from 'src/core/@generated/pipeline-stage/find-many-pipeline-stage.args';
|
||||
import { PipelineStageService } from 'src/core/pipeline/services/pipeline-stage.service';
|
||||
import { AbilityGuard } from 'src/guards/ability.guard';
|
||||
import { CheckAbilities } from 'src/decorators/check-abilities.decorator';
|
||||
import {
|
||||
CreatePipelineStageAbilityHandler,
|
||||
DeletePipelineStageAbilityHandler,
|
||||
ReadPipelineStageAbilityHandler,
|
||||
UpdatePipelineStageAbilityHandler,
|
||||
} from 'src/ability/handlers/pipeline-stage.ability-handler';
|
||||
import { UserAbility } from 'src/decorators/user-ability.decorator';
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
import {
|
||||
PrismaSelector,
|
||||
PrismaSelect,
|
||||
} from 'src/decorators/prisma-select.decorator';
|
||||
import { UpdateOnePipelineStageArgs } from 'src/core/@generated/pipeline-stage/update-one-pipeline-stage.args';
|
||||
import { CreateOnePipelineStageArgs } from 'src/core/@generated/pipeline-stage/create-one-pipeline-stage.args';
|
||||
import { AuthWorkspace } from 'src/decorators/auth-workspace.decorator';
|
||||
import { DeleteOnePipelineStageArgs } from 'src/core/@generated/pipeline-stage/delete-one-pipeline-stage.args';
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Resolver(() => PipelineStage)
|
||||
export class PipelineStageResolver {
|
||||
constructor(private readonly pipelineStageService: PipelineStageService) {}
|
||||
|
||||
@Mutation(() => PipelineStage, {
|
||||
nullable: false,
|
||||
})
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(CreatePipelineStageAbilityHandler)
|
||||
async createOnePipelineStage(
|
||||
@Args() args: CreateOnePipelineStageArgs,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
@PrismaSelector({ modelName: 'PipelineStage' })
|
||||
prismaSelect: PrismaSelect<'PipelineStage'>,
|
||||
): Promise<Partial<PipelineStage>> {
|
||||
return this.pipelineStageService.create({
|
||||
data: {
|
||||
...args.data,
|
||||
workspace: { connect: { id: workspace.id } },
|
||||
},
|
||||
select: prismaSelect.value,
|
||||
} as Prisma.PipelineStageCreateArgs);
|
||||
}
|
||||
|
||||
@Query(() => [PipelineStage])
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(ReadPipelineStageAbilityHandler)
|
||||
async findManyPipelineStage(
|
||||
@Args() args: FindManyPipelineStageArgs,
|
||||
@UserAbility() ability: AppAbility,
|
||||
@PrismaSelector({ modelName: 'PipelineStage' })
|
||||
prismaSelect: PrismaSelect<'PipelineStage'>,
|
||||
): Promise<Partial<PipelineStage>[]> {
|
||||
return this.pipelineStageService.findMany({
|
||||
where: args.where
|
||||
? {
|
||||
AND: [args.where, accessibleBy(ability).PipelineStage],
|
||||
}
|
||||
: accessibleBy(ability).PipelineStage,
|
||||
orderBy: args.orderBy,
|
||||
cursor: args.cursor,
|
||||
take: args.take,
|
||||
skip: args.skip,
|
||||
distinct: args.distinct,
|
||||
select: prismaSelect.value,
|
||||
});
|
||||
}
|
||||
|
||||
@Mutation(() => PipelineStage, {
|
||||
nullable: true,
|
||||
})
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(UpdatePipelineStageAbilityHandler)
|
||||
async updateOnePipelineStage(
|
||||
@Args() args: UpdateOnePipelineStageArgs,
|
||||
@PrismaSelector({ modelName: 'PipelineProgress' })
|
||||
prismaSelect: PrismaSelect<'PipelineProgress'>,
|
||||
): Promise<Partial<PipelineStage> | null> {
|
||||
return this.pipelineStageService.update({
|
||||
where: args.where,
|
||||
data: args.data,
|
||||
select: prismaSelect.value,
|
||||
} as Prisma.PipelineProgressUpdateArgs);
|
||||
}
|
||||
|
||||
@Mutation(() => PipelineStage, {
|
||||
nullable: false,
|
||||
})
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(DeletePipelineStageAbilityHandler)
|
||||
async deleteOnePipelineStage(
|
||||
@Args() args: DeleteOnePipelineStageArgs,
|
||||
): Promise<PipelineStage> {
|
||||
const pipelineStageToDelete = await this.pipelineStageService.findUnique({
|
||||
where: args.where,
|
||||
});
|
||||
|
||||
if (!pipelineStageToDelete) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
const { pipelineId } = pipelineStageToDelete;
|
||||
|
||||
const remainingPipelineStages = await this.pipelineStageService.findMany({
|
||||
orderBy: { position: 'asc' },
|
||||
where: {
|
||||
pipelineId,
|
||||
NOT: { id: pipelineStageToDelete.id },
|
||||
},
|
||||
});
|
||||
|
||||
if (!remainingPipelineStages.length) {
|
||||
throw new ForbiddenException(
|
||||
`Deleting last pipeline stage is not allowed`,
|
||||
);
|
||||
}
|
||||
|
||||
const deletedPipelineStage = await this.pipelineStageService.delete({
|
||||
where: args.where,
|
||||
});
|
||||
|
||||
await Promise.all(
|
||||
remainingPipelineStages.map((pipelineStage, index) => {
|
||||
if (pipelineStage.position === index) return;
|
||||
|
||||
return this.pipelineStageService.update({
|
||||
data: { position: index },
|
||||
where: { id: pipelineStage.id },
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
return deletedPipelineStage;
|
||||
}
|
||||
}
|
||||
@ -1,32 +0,0 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { PipelineService } from 'src/core/pipeline/services/pipeline.service';
|
||||
import { AbilityFactory } from 'src/ability/ability.factory';
|
||||
|
||||
import { PipelineResolver } from './pipeline.resolver';
|
||||
|
||||
describe('PipelineResolver', () => {
|
||||
let resolver: PipelineResolver;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
PipelineResolver,
|
||||
{
|
||||
provide: PipelineService,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: AbilityFactory,
|
||||
useValue: {},
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
resolver = module.get<PipelineResolver>(PipelineResolver);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(resolver).toBeDefined();
|
||||
});
|
||||
});
|
||||
@ -1,44 +0,0 @@
|
||||
import { Resolver, Args, Query } from '@nestjs/graphql';
|
||||
import { UseGuards } from '@nestjs/common';
|
||||
|
||||
import { accessibleBy } from '@casl/prisma';
|
||||
|
||||
import { JwtAuthGuard } from 'src/guards/jwt.auth.guard';
|
||||
import { Pipeline } from 'src/core/@generated/pipeline/pipeline.model';
|
||||
import { FindManyPipelineArgs } from 'src/core/@generated/pipeline/find-many-pipeline.args';
|
||||
import { PipelineService } from 'src/core/pipeline/services/pipeline.service';
|
||||
import { AbilityGuard } from 'src/guards/ability.guard';
|
||||
import { CheckAbilities } from 'src/decorators/check-abilities.decorator';
|
||||
import { ReadPipelineAbilityHandler } from 'src/ability/handlers/pipeline.ability-handler';
|
||||
import { UserAbility } from 'src/decorators/user-ability.decorator';
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
import {
|
||||
PrismaSelector,
|
||||
PrismaSelect,
|
||||
} from 'src/decorators/prisma-select.decorator';
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Resolver(() => Pipeline)
|
||||
export class PipelineResolver {
|
||||
constructor(private readonly pipelineService: PipelineService) {}
|
||||
|
||||
@Query(() => [Pipeline])
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(ReadPipelineAbilityHandler)
|
||||
async findManyPipeline(
|
||||
@Args() args: FindManyPipelineArgs,
|
||||
@UserAbility() ability: AppAbility,
|
||||
@PrismaSelector({ modelName: 'Pipeline' })
|
||||
prismaSelect: PrismaSelect<'Pipeline'>,
|
||||
): Promise<Partial<Pipeline>[]> {
|
||||
return this.pipelineService.findMany({
|
||||
...args,
|
||||
where: args.where
|
||||
? {
|
||||
AND: [args.where, accessibleBy(ability).Pipeline],
|
||||
}
|
||||
: accessibleBy(ability).Pipeline,
|
||||
select: prismaSelect.value,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
[
|
||||
{
|
||||
"name": "New",
|
||||
"color": "red",
|
||||
"position": 0,
|
||||
"type": "open"
|
||||
},
|
||||
{
|
||||
"name": "Screening",
|
||||
"color": "purple",
|
||||
"position": 1,
|
||||
"type": "ongoing"
|
||||
},
|
||||
{
|
||||
"name": "Meeting",
|
||||
"color": "sky",
|
||||
"position": 2,
|
||||
"type": "ongoing"
|
||||
},
|
||||
{
|
||||
"name": "Proposal",
|
||||
"color": "turquoise",
|
||||
"position": 3,
|
||||
"type": "ongoing"
|
||||
},
|
||||
{
|
||||
"name": "Customer",
|
||||
"color": "yellow",
|
||||
"position": 4,
|
||||
"type": "won"
|
||||
}
|
||||
]
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
{
|
||||
"name": "Sales pipeline",
|
||||
"icon": "💰",
|
||||
"pipelineProgressableType": "Company"
|
||||
}
|
||||
|
||||
@ -1,28 +0,0 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { prismaMock } from 'src/database/client-mock/jest-prisma-singleton';
|
||||
|
||||
import { PipelineProgressService } from './pipeline-progress.service';
|
||||
|
||||
describe('PipelineProgressService', () => {
|
||||
let service: PipelineProgressService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
PipelineProgressService,
|
||||
{
|
||||
provide: PrismaService,
|
||||
useValue: prismaMock,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get<PipelineProgressService>(PipelineProgressService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
@ -1,41 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class PipelineProgressService {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
// Find
|
||||
findFirst = this.prismaService.client.pipelineProgress.findFirst;
|
||||
findFirstOrThrow =
|
||||
this.prismaService.client.pipelineProgress.findFirstOrThrow;
|
||||
|
||||
findUnique = this.prismaService.client.pipelineProgress.findUnique;
|
||||
findUniqueOrThrow =
|
||||
this.prismaService.client.pipelineProgress.findUniqueOrThrow;
|
||||
|
||||
findMany = this.prismaService.client.pipelineProgress.findMany;
|
||||
|
||||
// Create
|
||||
create = this.prismaService.client.pipelineProgress.create;
|
||||
createMany = this.prismaService.client.pipelineProgress.createMany;
|
||||
|
||||
// Update
|
||||
update = this.prismaService.client.pipelineProgress.update;
|
||||
upsert = this.prismaService.client.pipelineProgress.upsert;
|
||||
updateMany = this.prismaService.client.pipelineProgress.updateMany;
|
||||
|
||||
// Delete
|
||||
delete = this.prismaService.client.pipelineProgress.delete;
|
||||
deleteMany = this.prismaService.client.pipelineProgress.deleteMany;
|
||||
|
||||
// Aggregate
|
||||
aggregate = this.prismaService.client.pipelineProgress.aggregate;
|
||||
|
||||
// Count
|
||||
count = this.prismaService.client.pipelineProgress.count;
|
||||
|
||||
// GroupBy
|
||||
groupBy = this.prismaService.client.pipelineProgress.groupBy;
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { prismaMock } from 'src/database/client-mock/jest-prisma-singleton';
|
||||
|
||||
import { PipelineStageService } from './pipeline-stage.service';
|
||||
|
||||
describe('PipelineStageService', () => {
|
||||
let service: PipelineStageService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
PipelineStageService,
|
||||
{
|
||||
provide: PrismaService,
|
||||
useValue: prismaMock,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get<PipelineStageService>(PipelineStageService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
@ -1,58 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import seedPipelineStages from 'src/core/pipeline/seed-data/pipeline-stages.json';
|
||||
|
||||
@Injectable()
|
||||
export class PipelineStageService {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
// Find
|
||||
findFirst = this.prismaService.client.pipelineStage.findFirst;
|
||||
findFirstOrThrow = this.prismaService.client.pipelineStage.findFirstOrThrow;
|
||||
|
||||
findUnique = this.prismaService.client.pipelineStage.findUnique;
|
||||
findUniqueOrThrow = this.prismaService.client.pipelineStage.findUniqueOrThrow;
|
||||
|
||||
findMany = this.prismaService.client.pipelineStage.findMany;
|
||||
|
||||
// Create
|
||||
create = this.prismaService.client.pipelineStage.create;
|
||||
createMany = this.prismaService.client.pipelineStage.createMany;
|
||||
|
||||
// Update
|
||||
update = this.prismaService.client.pipelineStage.update;
|
||||
upsert = this.prismaService.client.pipelineStage.upsert;
|
||||
updateMany = this.prismaService.client.pipelineStage.updateMany;
|
||||
|
||||
// Delete
|
||||
delete = this.prismaService.client.pipelineStage.delete;
|
||||
deleteMany = this.prismaService.client.pipelineStage.deleteMany;
|
||||
|
||||
// Aggregate
|
||||
aggregate = this.prismaService.client.pipelineStage.aggregate;
|
||||
|
||||
// Count
|
||||
count = this.prismaService.client.pipelineStage.count;
|
||||
|
||||
// GroupBy
|
||||
groupBy = this.prismaService.client.pipelineStage.groupBy;
|
||||
|
||||
// Customs
|
||||
async createDefaultPipelineStages({
|
||||
workspaceId,
|
||||
pipelineId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
pipelineId: string;
|
||||
}) {
|
||||
const pipelineStages = seedPipelineStages.map((pipelineStage) => ({
|
||||
...pipelineStage,
|
||||
workspaceId,
|
||||
pipelineId,
|
||||
}));
|
||||
return this.createMany({
|
||||
data: pipelineStages,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { prismaMock } from 'src/database/client-mock/jest-prisma-singleton';
|
||||
|
||||
import { PipelineService } from './pipeline.service';
|
||||
|
||||
describe('PipelineService', () => {
|
||||
let service: PipelineService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
PipelineService,
|
||||
{
|
||||
provide: PrismaService,
|
||||
useValue: prismaMock,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get<PipelineService>(PipelineService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
@ -1,56 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { PipelineProgressableType } from '@prisma/client';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import seedSalesPipeline from 'src/core/pipeline/seed-data/sales-pipeline.json';
|
||||
|
||||
@Injectable()
|
||||
export class PipelineService {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
// Find
|
||||
findFirst = this.prismaService.client.pipeline.findFirst;
|
||||
findFirstOrThrow = this.prismaService.client.pipeline.findFirstOrThrow;
|
||||
|
||||
findUnique = this.prismaService.client.pipeline.findUnique;
|
||||
findUniqueOrThrow = this.prismaService.client.pipeline.findUniqueOrThrow;
|
||||
|
||||
findMany = this.prismaService.client.pipeline.findMany;
|
||||
|
||||
// Create
|
||||
create = this.prismaService.client.pipeline.create;
|
||||
createMany = this.prismaService.client.pipeline.createMany;
|
||||
|
||||
// Update
|
||||
update = this.prismaService.client.pipeline.update;
|
||||
upsert = this.prismaService.client.pipeline.upsert;
|
||||
updateMany = this.prismaService.client.pipeline.updateMany;
|
||||
|
||||
// Delete
|
||||
delete = this.prismaService.client.pipeline.delete;
|
||||
deleteMany = this.prismaService.client.pipeline.deleteMany;
|
||||
|
||||
// Aggregate
|
||||
aggregate = this.prismaService.client.pipeline.aggregate;
|
||||
|
||||
// Count
|
||||
count = this.prismaService.client.pipeline.count;
|
||||
|
||||
// GroupBy
|
||||
groupBy = this.prismaService.client.pipeline.groupBy;
|
||||
|
||||
// Customs
|
||||
async createDefaultPipeline({ workspaceId }: { workspaceId: string }) {
|
||||
const pipeline = {
|
||||
...seedSalesPipeline,
|
||||
pipelineProgressableType:
|
||||
seedSalesPipeline.pipelineProgressableType as PipelineProgressableType,
|
||||
workspaceId,
|
||||
};
|
||||
|
||||
return this.create({
|
||||
data: pipeline,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -4,7 +4,7 @@ import {
|
||||
} from '@ptc-org/nestjs-query-graphql';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { RefreshToken } from 'src/coreV2/refresh-token/refresh-token.entity';
|
||||
import { RefreshToken } from 'src/core/refresh-token/refresh-token.entity';
|
||||
|
||||
export class BeforeCreateOneRefreshToken<T extends RefreshToken>
|
||||
implements BeforeCreateOneHook<T, any>
|
||||
@ -9,38 +9,29 @@ import {
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
import {
|
||||
Authorize,
|
||||
BeforeCreateOne,
|
||||
IDField,
|
||||
} from '@ptc-org/nestjs-query-graphql';
|
||||
import { BeforeCreateOne, IDField } from '@ptc-org/nestjs-query-graphql';
|
||||
|
||||
import { UserV2 } from 'src/coreV2/user/user.entity';
|
||||
import { User } from 'src/core/user/user.entity';
|
||||
|
||||
import { BeforeCreateOneRefreshToken } from './hooks/before-create-one-refresh-token.hook';
|
||||
|
||||
@Entity('refresh_tokens')
|
||||
@ObjectType('refreshTokenV2')
|
||||
@Entity({ name: 'refreshToken', schema: 'core' })
|
||||
@ObjectType('RefreshToken')
|
||||
@BeforeCreateOne(BeforeCreateOneRefreshToken)
|
||||
@Authorize({
|
||||
authorize: (context: any) => ({
|
||||
userId: { eq: context?.req?.user?.user?.id },
|
||||
}),
|
||||
})
|
||||
export class RefreshToken {
|
||||
@IDField(() => ID)
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@ManyToOne(() => UserV2, (user) => user.refreshTokens)
|
||||
@ManyToOne(() => User, (user) => user.refreshTokens)
|
||||
@JoinColumn({ name: 'userId' })
|
||||
user: UserV2;
|
||||
user: User;
|
||||
|
||||
@Column()
|
||||
userId: string;
|
||||
|
||||
@Field()
|
||||
@Column('time with time zone')
|
||||
@Column('timestamp with time zone')
|
||||
expiresAt: Date;
|
||||
|
||||
@Column('timestamp with time zone', { nullable: true })
|
||||
@ -1,7 +1,7 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { getRepositoryToken } from '@nestjs/typeorm';
|
||||
|
||||
import { RefreshToken } from 'src/coreV2/refresh-token/refresh-token.entity';
|
||||
import { RefreshToken } from 'src/core/refresh-token/refresh-token.entity';
|
||||
|
||||
import { RefreshTokenService } from './refresh-token.service';
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
|
||||
|
||||
import { RefreshToken } from 'src/coreV2/refresh-token/refresh-token.entity';
|
||||
import { RefreshToken } from 'src/core/refresh-token/refresh-token.entity';
|
||||
|
||||
export class RefreshTokenService extends TypeOrmQueryService<RefreshToken> {}
|
||||
33
server/src/core/user/dtos/workspace-member.dto.ts
Normal file
33
server/src/core/user/dtos/workspace-member.dto.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { Field, ID, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import { IDField } from '@ptc-org/nestjs-query-graphql';
|
||||
|
||||
@ObjectType('UserWorkspaceMemberName')
|
||||
export class UserWorkspaceMemberName {
|
||||
@Field({ nullable: false })
|
||||
firstName: string;
|
||||
|
||||
@Field({ nullable: false })
|
||||
lastName: string;
|
||||
}
|
||||
|
||||
@ObjectType('UserWorkspaceMember')
|
||||
export class UserWorkspaceMember {
|
||||
@IDField(() => ID)
|
||||
id: string;
|
||||
|
||||
@Field(() => UserWorkspaceMemberName)
|
||||
name: UserWorkspaceMemberName;
|
||||
|
||||
@Field({ nullable: false })
|
||||
colorScheme: string;
|
||||
|
||||
@Field({ nullable: true })
|
||||
avatarUrl: string;
|
||||
|
||||
@Field({ nullable: false })
|
||||
locale: string;
|
||||
|
||||
@Field({ nullable: false })
|
||||
allowImpersonation: boolean;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user