feat: user request module with re-ordering (HBE-78) (#11)
* feat: added user-request schema in prisma * feat: basic mutation and queries implementation * fix: enum registration in graphql * feat: user resolver added for user requests * chore: refactor codes * feat: transaction added in request reordering operation * feat: pubsub added in user request * refactor: user request service * chore: feedback added * chore: code improvement * fix: bug fix * feat: request type update in schema and JSDoc added * test: fetchUserRequests and fetchUserRequest unit test added * chore: refactor two functions * test: unit test added for more functions * chore: code readability improved * test: added unit test for reorderRequests function * feat: subscriptions added * fix: User reference to AuthUser * fix: User to AuthUser in test file * chore: update dto file extensions * feat: relation added in schema level * chore: add function for db to model type casting * feat: filter with title and collectionID add in userRequest resolver * feat: resolvers added for userCollection in request module, and move inputTypes in a single file * test: test file updated * docs: description updated * feat: createdOn, updatedOn added in user request schema * chore: (delete in future) user collection module add for testing purpose * feat: separate resolvers for create, update, delete user request based on req type * feat: used paginationArgs from common types * fix: shift InputTypes to ArgsTypes * docs: update docs * feat: avoid destructuring * test: fix test cases for create and update * docs: update JS doc * feat: separate object variables for moveRequest function * test: fix test case for moveRequest function * feat: saperate parameters for fetchUserRequest * test: fix test cases for fetchUserRequests * feat: update some query names and made review changes * test: fix test cases * feat: remove filtering with title * test: fix text cases for fetchUserRequests func * feat: update subscription key * feat: edge case handled for user request creation * test: fix test case * fix: user field resolver * fix: fetch user req issue * fix: update with type check * test: fix test cases * feat: type checked on move request * test: add test case for typeValidity check func * fix: edge condition added in if statement * fix: error message * chore: removed user collection from this branch * fix: typos
This commit is contained in:
@@ -154,6 +154,20 @@ model UserEnvironment {
|
||||
isGlobal Boolean
|
||||
}
|
||||
|
||||
model UserRequest {
|
||||
id String @id @default(cuid())
|
||||
userCollection UserCollection @relation(fields: [collectionID], references: [id])
|
||||
collectionID String
|
||||
userUid String
|
||||
user User @relation(fields: [userUid], references: [uid], onDelete: Cascade)
|
||||
title String
|
||||
request Json
|
||||
type ReqType
|
||||
orderIndex Int
|
||||
createdOn DateTime @default(now()) @db.Timestamp(3)
|
||||
updatedOn DateTime @updatedAt @db.Timestamp(3)
|
||||
}
|
||||
|
||||
model UserCollection {
|
||||
id String @id @default(cuid())
|
||||
parentID String?
|
||||
@@ -169,20 +183,6 @@ model UserCollection {
|
||||
updatedOn DateTime @updatedAt @db.Timestamp(3)
|
||||
}
|
||||
|
||||
model UserRequest {
|
||||
id String @id @default(cuid())
|
||||
collectionID String
|
||||
collection UserCollection @relation(fields: [collectionID], references: [id], onDelete: Cascade)
|
||||
userUid String
|
||||
user User @relation(fields: [userUid], references: [uid], onDelete: Cascade)
|
||||
title String
|
||||
request Json
|
||||
type ReqType
|
||||
orderIndex Int
|
||||
createdOn DateTime @default(now()) @db.Timestamp(3)
|
||||
updatedOn DateTime @updatedAt @db.Timestamp(3)
|
||||
}
|
||||
|
||||
enum TeamMemberRole {
|
||||
OWNER
|
||||
VIEWER
|
||||
|
||||
@@ -6,6 +6,7 @@ import { GQLComplexityPlugin } from './plugins/GQLComplexityPlugin';
|
||||
import { AuthModule } from './auth/auth.module';
|
||||
import { UserSettingsModule } from './user-settings/user-settings.module';
|
||||
import { UserEnvironmentsModule } from './user-environment/user-environments.module';
|
||||
import { UserRequestModule } from './user-request/user-request.module';
|
||||
import { UserHistoryModule } from './user-history/user-history.module';
|
||||
import { subscriptionContextCookieParser } from './auth/helper';
|
||||
import { TeamModule } from './team/team.module';
|
||||
@@ -60,6 +61,7 @@ import { COOKIES_NOT_FOUND } from './errors';
|
||||
UserSettingsModule,
|
||||
UserEnvironmentsModule,
|
||||
UserHistoryModule,
|
||||
UserRequestModule,
|
||||
TeamModule,
|
||||
TeamEnvironmentsModule,
|
||||
TeamCollectionModule,
|
||||
|
||||
@@ -44,6 +44,36 @@ export const USER_DELETION_FAILED = 'user/deletion_failed' as const;
|
||||
*/
|
||||
export const USER_IS_OWNER = 'user/is_owner' as const;
|
||||
|
||||
/**
|
||||
* Tried to find user collection but failed
|
||||
* (UserRequestService)
|
||||
*/
|
||||
export const USER_COLLECTION_NOT_FOUND = 'user_collection/not_found' as const;
|
||||
|
||||
/**
|
||||
* Tried to reorder user request but failed
|
||||
* (UserRequestService)
|
||||
*/
|
||||
export const USER_REQUEST_CREATION_FAILED = 'user_request/creation_failed' as const;
|
||||
|
||||
/**
|
||||
* Tried to do an action on a user request but user request is not matched with user collection
|
||||
* (UserRequestService)
|
||||
*/
|
||||
export const USER_REQUEST_INVALID_TYPE = 'user_request/type_mismatch' as const;
|
||||
|
||||
/**
|
||||
* Tried to do an action on a user request where user request is not found
|
||||
* (UserRequestService)
|
||||
*/
|
||||
export const USER_REQUEST_NOT_FOUND = 'user_request/not_found' as const;
|
||||
|
||||
/**
|
||||
* Tried to reorder user request but failed
|
||||
* (UserRequestService)
|
||||
*/
|
||||
export const USER_REQUEST_REORDERING_FAILED = 'user_request/reordering_failed' as const;
|
||||
|
||||
/**
|
||||
* Tried to perform action on a team which they are not a member of
|
||||
* (GqlTeamMemberGuard)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { UserRequest } from 'src/user-request/user-request.model';
|
||||
import { User } from 'src/user/user.model';
|
||||
import { UserSettings } from 'src/user-settings/user-settings.model';
|
||||
import { UserEnvironment } from '../user-environment/user-environments.model';
|
||||
@@ -20,6 +21,13 @@ export type TopicDef = {
|
||||
topic: `user_environment/${string}/${'created' | 'updated' | 'deleted'}`
|
||||
]: UserEnvironment;
|
||||
[topic: `user_environment/${string}/deleted_many`]: number;
|
||||
[
|
||||
topic: `user_request/${string}/${
|
||||
| 'created'
|
||||
| 'updated'
|
||||
| 'deleted'
|
||||
| 'moved'}`
|
||||
]: UserRequest;
|
||||
[
|
||||
topic: `user_history/${string}/${'created' | 'updated' | 'deleted'}`
|
||||
]: UserHistory;
|
||||
|
||||
@@ -8,5 +8,6 @@ import { PubSubModule } from 'src/pubsub/pubsub.module';
|
||||
@Module({
|
||||
imports: [PrismaModule, UserModule, PubSubModule],
|
||||
providers: [UserCollectionService, UserCollectionResolver],
|
||||
exports: [UserCollectionService],
|
||||
})
|
||||
export class UserCollectionModule {}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
import { Field, ID, ArgsType } from '@nestjs/graphql';
|
||||
import { PaginationArgs } from 'src/types/input-types.args';
|
||||
import { ReqType } from 'src/user-history/user-history.model';
|
||||
|
||||
@ArgsType()
|
||||
export class GetUserRequestArgs extends PaginationArgs {
|
||||
@Field(() => ID, {
|
||||
nullable: true,
|
||||
defaultValue: undefined,
|
||||
description: 'Collection ID of the user request',
|
||||
})
|
||||
collectionID?: string;
|
||||
|
||||
@Field({
|
||||
nullable: true,
|
||||
defaultValue: undefined,
|
||||
description: 'Title of the user request',
|
||||
})
|
||||
title?: string;
|
||||
}
|
||||
|
||||
@ArgsType()
|
||||
export class MoveUserRequestArgs {
|
||||
@Field(() => ID, {
|
||||
description: 'ID of the collection, where the request is belongs to',
|
||||
})
|
||||
sourceCollectionID: string;
|
||||
|
||||
@Field(() => ID, {
|
||||
description: 'ID of the request being moved',
|
||||
})
|
||||
requestID: string;
|
||||
|
||||
@Field(() => ID, {
|
||||
description: 'ID of the collection, where the request is moving to',
|
||||
})
|
||||
destinationCollectionID: string;
|
||||
|
||||
@Field(() => ID, {
|
||||
nullable: true,
|
||||
description:
|
||||
'ID of the request that comes after the updated request in its new position',
|
||||
})
|
||||
nextRequestID: string;
|
||||
}
|
||||
|
||||
@ArgsType()
|
||||
export class CreateUserRequestArgs {
|
||||
@Field({ nullable: false, description: 'Collection ID of the user request' })
|
||||
collectionID: string;
|
||||
|
||||
@Field({ nullable: false, description: 'Title of the user request' })
|
||||
title: string;
|
||||
|
||||
@Field({ nullable: false, description: 'content/body of the user request' })
|
||||
request: string;
|
||||
|
||||
type: ReqType;
|
||||
}
|
||||
|
||||
@ArgsType()
|
||||
export class UpdateUserRequestArgs {
|
||||
@Field({
|
||||
nullable: true,
|
||||
defaultValue: undefined,
|
||||
description: 'Title of the user request',
|
||||
})
|
||||
title: string;
|
||||
|
||||
@Field({
|
||||
nullable: true,
|
||||
defaultValue: undefined,
|
||||
description: 'content/body of the user request',
|
||||
})
|
||||
request: string;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { Args, Parent, ResolveField, Resolver } from '@nestjs/graphql';
|
||||
import * as E from 'fp-ts/Either';
|
||||
import { throwErr } from 'src/utils';
|
||||
import { UserRequestService } from '../user-request.service';
|
||||
import { UserRequest } from '../user-request.model';
|
||||
import { AuthUser } from 'src/types/AuthUser';
|
||||
import { UserCollection } from 'src/user-collection/user-collections.model';
|
||||
import { PaginationArgs } from 'src/types/input-types.args';
|
||||
|
||||
@Resolver(() => UserCollection)
|
||||
export class UserRequestUserCollectionResolver {
|
||||
constructor(private readonly userRequestService: UserRequestService) {}
|
||||
|
||||
@ResolveField(() => [UserRequest], {
|
||||
description: 'Returns user requests of a user collection',
|
||||
})
|
||||
async requests(
|
||||
@Parent() user: AuthUser,
|
||||
@Parent() collection: UserCollection,
|
||||
@Args() args: PaginationArgs,
|
||||
) {
|
||||
const requests = await this.userRequestService.fetchUserRequests(
|
||||
collection.id,
|
||||
collection.type,
|
||||
args.cursor,
|
||||
args.take,
|
||||
user,
|
||||
);
|
||||
if (E.isLeft(requests)) throwErr(requests.left);
|
||||
return requests.right;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
import { UseGuards } from '@nestjs/common';
|
||||
import {
|
||||
Args,
|
||||
Context,
|
||||
ID,
|
||||
Mutation,
|
||||
Query,
|
||||
ResolveField,
|
||||
Resolver,
|
||||
Subscription,
|
||||
} from '@nestjs/graphql';
|
||||
import { GqlUser } from 'src/decorators/gql-user.decorator';
|
||||
import { GqlAuthGuard } from 'src/guards/gql-auth.guard';
|
||||
import { PubSubService } from 'src/pubsub/pubsub.service';
|
||||
import * as E from 'fp-ts/Either';
|
||||
import { throwErr } from 'src/utils';
|
||||
import { UserRequest } from '../user-request.model';
|
||||
import { UserRequestService } from '../user-request.service';
|
||||
import {
|
||||
GetUserRequestArgs,
|
||||
CreateUserRequestArgs,
|
||||
UpdateUserRequestArgs,
|
||||
MoveUserRequestArgs,
|
||||
} from '../input-type.args';
|
||||
import { AuthUser } from 'src/types/AuthUser';
|
||||
import { User } from 'src/user/user.model';
|
||||
import { ReqType } from 'src/user-history/user-history.model';
|
||||
|
||||
@Resolver(() => UserRequest)
|
||||
export class UserRequestResolver {
|
||||
constructor(
|
||||
private readonly userRequestService: UserRequestService,
|
||||
private readonly pubSub: PubSubService,
|
||||
) {}
|
||||
|
||||
@ResolveField(() => User, {
|
||||
description: 'Returns the user of the user request',
|
||||
})
|
||||
async user(@GqlUser() user: AuthUser) {
|
||||
return user;
|
||||
}
|
||||
|
||||
/* Queries */
|
||||
|
||||
@Query(() => [UserRequest], {
|
||||
description: 'Get REST user requests',
|
||||
})
|
||||
@UseGuards(GqlAuthGuard)
|
||||
async userRESTRequests(
|
||||
@GqlUser() user: AuthUser,
|
||||
@Args() args: GetUserRequestArgs,
|
||||
): Promise<UserRequest[]> {
|
||||
const requests = await this.userRequestService.fetchUserRequests(
|
||||
args.collectionID,
|
||||
ReqType.REST,
|
||||
args.cursor,
|
||||
args.take,
|
||||
user,
|
||||
);
|
||||
if (E.isLeft(requests)) throwErr(requests.left);
|
||||
return requests.right;
|
||||
}
|
||||
|
||||
@Query(() => [UserRequest], {
|
||||
description: 'Get GraphQL user requests',
|
||||
})
|
||||
@UseGuards(GqlAuthGuard)
|
||||
async userGQLRequests(
|
||||
@GqlUser() user: AuthUser,
|
||||
@Args() args: GetUserRequestArgs,
|
||||
): Promise<UserRequest[]> {
|
||||
const requests = await this.userRequestService.fetchUserRequests(
|
||||
args.collectionID,
|
||||
ReqType.GQL,
|
||||
args.cursor,
|
||||
args.take,
|
||||
user,
|
||||
);
|
||||
if (E.isLeft(requests)) throwErr(requests.left);
|
||||
return requests.right;
|
||||
}
|
||||
|
||||
@Query(() => UserRequest, {
|
||||
description: 'Get a user request by ID',
|
||||
})
|
||||
@UseGuards(GqlAuthGuard)
|
||||
async userRequest(
|
||||
@GqlUser() user: AuthUser,
|
||||
@Args({
|
||||
name: 'id',
|
||||
type: () => ID,
|
||||
description: 'ID of the user request',
|
||||
})
|
||||
id: string,
|
||||
): Promise<UserRequest> {
|
||||
const request = await this.userRequestService.fetchUserRequest(id, user);
|
||||
if (E.isLeft(request)) throwErr(request.left);
|
||||
return request.right;
|
||||
}
|
||||
|
||||
/* Mutations */
|
||||
|
||||
@Mutation(() => UserRequest, {
|
||||
description: 'Create a new user REST request',
|
||||
})
|
||||
@UseGuards(GqlAuthGuard)
|
||||
async createRESTUserRequest(
|
||||
@GqlUser() user: AuthUser,
|
||||
@Args() args: CreateUserRequestArgs,
|
||||
) {
|
||||
const request = await this.userRequestService.createRequest(
|
||||
args.collectionID,
|
||||
args.title,
|
||||
args.request,
|
||||
ReqType.REST,
|
||||
user,
|
||||
);
|
||||
if (E.isLeft(request)) throwErr(request.left);
|
||||
return request.right;
|
||||
}
|
||||
|
||||
@Mutation(() => UserRequest, {
|
||||
description: 'Create a new user GraphQL request',
|
||||
})
|
||||
@UseGuards(GqlAuthGuard)
|
||||
async createGQLUserRequest(
|
||||
@GqlUser() user: AuthUser,
|
||||
@Args() args: CreateUserRequestArgs,
|
||||
) {
|
||||
const request = await this.userRequestService.createRequest(
|
||||
args.collectionID,
|
||||
args.title,
|
||||
args.request,
|
||||
ReqType.GQL,
|
||||
user,
|
||||
);
|
||||
if (E.isLeft(request)) throwErr(request.left);
|
||||
return request.right;
|
||||
}
|
||||
|
||||
@Mutation(() => UserRequest, {
|
||||
description: 'Update a user REST request',
|
||||
})
|
||||
@UseGuards(GqlAuthGuard)
|
||||
async updateRESTUserRequest(
|
||||
@GqlUser() user: AuthUser,
|
||||
@Args({
|
||||
name: 'id',
|
||||
description: 'ID of the user REST request',
|
||||
type: () => ID,
|
||||
})
|
||||
id: string,
|
||||
@Args() args: UpdateUserRequestArgs,
|
||||
) {
|
||||
const request = await this.userRequestService.updateRequest(
|
||||
id,
|
||||
args.title,
|
||||
ReqType.REST,
|
||||
args.request,
|
||||
user,
|
||||
);
|
||||
if (E.isLeft(request)) throwErr(request.left);
|
||||
return request.right;
|
||||
}
|
||||
|
||||
@Mutation(() => UserRequest, {
|
||||
description: 'Update a user GraphQL request',
|
||||
})
|
||||
@UseGuards(GqlAuthGuard)
|
||||
async updateGQLUserRequest(
|
||||
@GqlUser() user: AuthUser,
|
||||
@Args({
|
||||
name: 'id',
|
||||
description: 'ID of the user GraphQL request',
|
||||
type: () => ID,
|
||||
})
|
||||
id: string,
|
||||
@Args() args: UpdateUserRequestArgs,
|
||||
) {
|
||||
const request = await this.userRequestService.updateRequest(
|
||||
id,
|
||||
args.title,
|
||||
ReqType.GQL,
|
||||
args.request,
|
||||
user,
|
||||
);
|
||||
if (E.isLeft(request)) throwErr(request.left);
|
||||
return request.right;
|
||||
}
|
||||
|
||||
@Mutation(() => Boolean, {
|
||||
description: 'Delete a user request',
|
||||
})
|
||||
@UseGuards(GqlAuthGuard)
|
||||
async deleteUserRequest(
|
||||
@GqlUser() user: AuthUser,
|
||||
@Args({
|
||||
name: 'id',
|
||||
description: 'ID of the user request',
|
||||
type: () => ID,
|
||||
})
|
||||
id: string,
|
||||
): Promise<boolean> {
|
||||
const isDeleted = await this.userRequestService.deleteRequest(id, user);
|
||||
if (E.isLeft(isDeleted)) throwErr(isDeleted.left);
|
||||
return isDeleted.right;
|
||||
}
|
||||
|
||||
@Mutation(() => UserRequest, {
|
||||
description:
|
||||
'Move and re-order of a user request within same or across collection',
|
||||
})
|
||||
@UseGuards(GqlAuthGuard)
|
||||
async moveUserRequest(
|
||||
@GqlUser() user: AuthUser,
|
||||
@Args() args: MoveUserRequestArgs,
|
||||
): Promise<UserRequest> {
|
||||
const request = await this.userRequestService.moveRequest(
|
||||
args.sourceCollectionID,
|
||||
args.destinationCollectionID,
|
||||
args.requestID,
|
||||
args.nextRequestID,
|
||||
user,
|
||||
);
|
||||
if (E.isLeft(request)) throwErr(request.left);
|
||||
return request.right;
|
||||
}
|
||||
|
||||
/* Subscriptions */
|
||||
|
||||
@Subscription(() => UserRequest, {
|
||||
description: 'Listen for User Request Creation',
|
||||
resolve: (value) => value,
|
||||
})
|
||||
@UseGuards(GqlAuthGuard)
|
||||
userRequestCreated(@GqlUser() user: AuthUser) {
|
||||
return this.pubSub.asyncIterator(`user_request/${user.uid}/created`);
|
||||
}
|
||||
|
||||
@Subscription(() => UserRequest, {
|
||||
description: 'Listen for User Request Update',
|
||||
resolve: (value) => value,
|
||||
})
|
||||
@UseGuards(GqlAuthGuard)
|
||||
userRequestUpdated(@GqlUser() user: AuthUser) {
|
||||
return this.pubSub.asyncIterator(`user_request/${user.uid}/updated`);
|
||||
}
|
||||
|
||||
@Subscription(() => UserRequest, {
|
||||
description: 'Listen for User Request Deletion',
|
||||
resolve: (value) => value,
|
||||
})
|
||||
@UseGuards(GqlAuthGuard)
|
||||
userRequestDeleted(@GqlUser() user: AuthUser) {
|
||||
return this.pubSub.asyncIterator(`user_request/${user.uid}/deleted`);
|
||||
}
|
||||
|
||||
@Subscription(() => UserRequest, {
|
||||
description: 'Listen for User Request Moved',
|
||||
resolve: (value) => value,
|
||||
})
|
||||
@UseGuards(GqlAuthGuard)
|
||||
userRequestMoved(@GqlUser() user: AuthUser) {
|
||||
return this.pubSub.asyncIterator(`user_request/${user.uid}/moved`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { Field, ID, ObjectType } from '@nestjs/graphql';
|
||||
import { ReqType } from 'src/user-history/user-history.model';
|
||||
|
||||
@ObjectType()
|
||||
export class UserRequest {
|
||||
@Field(() => ID, {
|
||||
description: 'ID of the user request',
|
||||
})
|
||||
id: string;
|
||||
|
||||
@Field(() => ID, {
|
||||
description: 'ID of the parent collection ID',
|
||||
})
|
||||
collectionID: string;
|
||||
|
||||
@Field({
|
||||
description: 'Title of the user request',
|
||||
})
|
||||
title: string;
|
||||
|
||||
@Field({
|
||||
description: 'Content/Body of the user request',
|
||||
})
|
||||
request: string;
|
||||
|
||||
@Field(() => ReqType, {
|
||||
description: 'Type (GRAPHQL/REST) of the user request',
|
||||
})
|
||||
type: ReqType;
|
||||
|
||||
@Field(() => Date, {
|
||||
description: 'Date of the user request creation',
|
||||
})
|
||||
createdOn: Date;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { UserCollectionModule } from 'src/user-collection/user-collection.module';
|
||||
import { PrismaModule } from '../prisma/prisma.module';
|
||||
import { PubSubModule } from '../pubsub/pubsub.module';
|
||||
import { UserRequestUserCollectionResolver } from './resolvers/user-collection.resolver';
|
||||
import { UserRequestResolver } from './resolvers/user-request.resolver';
|
||||
import { UserRequestService } from './user-request.service';
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule, PubSubModule, UserCollectionModule],
|
||||
providers: [
|
||||
UserRequestResolver,
|
||||
UserRequestUserCollectionResolver,
|
||||
UserRequestService,
|
||||
],
|
||||
})
|
||||
export class UserRequestModule {}
|
||||
@@ -0,0 +1,848 @@
|
||||
import {
|
||||
ReqType as DbRequestType,
|
||||
UserRequest as DbUserRequest,
|
||||
} from '@prisma/client';
|
||||
import { mockDeep, mockReset } from 'jest-mock-extended';
|
||||
import {
|
||||
JSON_INVALID,
|
||||
USER_REQUEST_NOT_FOUND,
|
||||
USER_REQUEST_REORDERING_FAILED,
|
||||
} from 'src/errors';
|
||||
import { PrismaService } from 'src/prisma/prisma.service';
|
||||
import { PubSubService } from 'src/pubsub/pubsub.service';
|
||||
import * as E from 'fp-ts/Either';
|
||||
import { GetUserRequestArgs } from './input-type.args';
|
||||
import { MoveUserRequestArgs } from './input-type.args';
|
||||
import {
|
||||
CreateUserRequestArgs,
|
||||
UpdateUserRequestArgs,
|
||||
} from './input-type.args';
|
||||
import { UserRequest } from './user-request.model';
|
||||
import { UserRequestService } from './user-request.service';
|
||||
import { AuthUser } from 'src/types/AuthUser';
|
||||
import { ReqType } from 'src/user-history/user-history.model';
|
||||
import { UserCollectionService } from 'src/user-collection/user-collection.service';
|
||||
|
||||
const mockPrisma = mockDeep<PrismaService>();
|
||||
const mockPubSub = mockDeep<PubSubService>();
|
||||
const mockUserCollectionService = mockDeep<UserCollectionService>();
|
||||
|
||||
// @ts-ignore
|
||||
const userRequestService = new UserRequestService(
|
||||
mockPrisma,
|
||||
mockPubSub as any,
|
||||
mockUserCollectionService,
|
||||
);
|
||||
|
||||
const user: AuthUser = {
|
||||
uid: 'user-uid',
|
||||
email: 'test@gmail.com',
|
||||
displayName: 'Test User',
|
||||
photoURL: 'https://example.com/photo.png',
|
||||
isAdmin: false,
|
||||
refreshToken: null,
|
||||
createdOn: new Date(),
|
||||
currentGQLSession: null,
|
||||
currentRESTSession: null,
|
||||
};
|
||||
const dbUserRequests: DbUserRequest[] = [
|
||||
{
|
||||
id: 'user-request-id-11',
|
||||
collectionID: 'collection-id-1',
|
||||
orderIndex: 1,
|
||||
userUid: user.uid,
|
||||
title: 'Request 1',
|
||||
request: {},
|
||||
type: DbRequestType.REST,
|
||||
createdOn: new Date(),
|
||||
updatedOn: new Date(),
|
||||
},
|
||||
{
|
||||
id: 'user-request-id-12',
|
||||
collectionID: 'collection-id-1',
|
||||
orderIndex: 2,
|
||||
userUid: user.uid,
|
||||
title: 'Request 2',
|
||||
request: {},
|
||||
type: DbRequestType.REST,
|
||||
createdOn: new Date(),
|
||||
updatedOn: new Date(),
|
||||
},
|
||||
{
|
||||
id: 'user-request-id-13',
|
||||
collectionID: 'collection-id-1',
|
||||
orderIndex: 3,
|
||||
userUid: user.uid,
|
||||
title: 'Request 3',
|
||||
request: {},
|
||||
type: DbRequestType.REST,
|
||||
createdOn: new Date(),
|
||||
updatedOn: new Date(),
|
||||
},
|
||||
{
|
||||
id: 'user-request-id-14',
|
||||
collectionID: 'collection-id-1',
|
||||
orderIndex: 4,
|
||||
userUid: user.uid,
|
||||
title: 'Request 4',
|
||||
request: {},
|
||||
type: DbRequestType.REST,
|
||||
createdOn: new Date(),
|
||||
updatedOn: new Date(),
|
||||
},
|
||||
{
|
||||
id: 'user-request-id-21',
|
||||
collectionID: 'collection-id-2',
|
||||
orderIndex: 1,
|
||||
userUid: user.uid,
|
||||
title: 'Request 1',
|
||||
request: {},
|
||||
type: DbRequestType.REST,
|
||||
createdOn: new Date(),
|
||||
updatedOn: new Date(),
|
||||
},
|
||||
{
|
||||
id: 'user-request-id-22',
|
||||
collectionID: 'collection-id-2',
|
||||
orderIndex: 2,
|
||||
userUid: user.uid,
|
||||
title: 'Request 2',
|
||||
request: {},
|
||||
type: DbRequestType.REST,
|
||||
createdOn: new Date(),
|
||||
updatedOn: new Date(),
|
||||
},
|
||||
{
|
||||
id: 'user-request-id-23',
|
||||
collectionID: 'collection-id-2',
|
||||
orderIndex: 3,
|
||||
userUid: user.uid,
|
||||
title: 'Request 3',
|
||||
request: {},
|
||||
type: DbRequestType.REST,
|
||||
createdOn: new Date(),
|
||||
updatedOn: new Date(),
|
||||
},
|
||||
{
|
||||
id: 'user-request-id-24',
|
||||
collectionID: 'collection-id-2',
|
||||
orderIndex: 4,
|
||||
userUid: user.uid,
|
||||
title: 'Request 4',
|
||||
request: {},
|
||||
type: DbRequestType.REST,
|
||||
createdOn: new Date(),
|
||||
updatedOn: new Date(),
|
||||
},
|
||||
];
|
||||
const userRequests: UserRequest[] = dbUserRequests.map((r) => {
|
||||
return {
|
||||
...r,
|
||||
request: JSON.stringify(r.request),
|
||||
type: ReqType[r.type],
|
||||
};
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
mockReset(mockPrisma);
|
||||
mockPubSub.publish.mockClear();
|
||||
mockUserCollectionService.getUserCollection.mockClear();
|
||||
});
|
||||
|
||||
describe('UserRequestService', () => {
|
||||
describe('fetchUserRequests', () => {
|
||||
test('Should resolve right and fetch user requests (with collection ID)', () => {
|
||||
const args: GetUserRequestArgs = {
|
||||
collectionID: 'collection-id-1',
|
||||
cursor: undefined,
|
||||
take: undefined,
|
||||
};
|
||||
const expectedDbUserRequests = dbUserRequests.filter(
|
||||
(r) => r.collectionID === args.collectionID,
|
||||
);
|
||||
const expectedUserRequests = userRequests.filter(
|
||||
(r) => r.collectionID === args.collectionID,
|
||||
);
|
||||
|
||||
mockPrisma.userRequest.findMany.mockResolvedValue(expectedDbUserRequests);
|
||||
const result = userRequestService.fetchUserRequests(
|
||||
args.collectionID,
|
||||
ReqType.REST,
|
||||
args.cursor,
|
||||
args.take,
|
||||
user,
|
||||
);
|
||||
|
||||
expect(result).resolves.toEqualRight(expectedUserRequests);
|
||||
});
|
||||
|
||||
test('Should resolve right and fetch user requests (with collection ID and take)', () => {
|
||||
const args: GetUserRequestArgs = {
|
||||
collectionID: 'collection-id-1',
|
||||
cursor: undefined,
|
||||
take: 2,
|
||||
};
|
||||
const expectedDbUserRequests = dbUserRequests.filter(
|
||||
(r) => r.collectionID === args.collectionID,
|
||||
);
|
||||
const expectedUserRequests = userRequests.filter(
|
||||
(r) => r.collectionID === args.collectionID,
|
||||
);
|
||||
|
||||
mockPrisma.userRequest.findMany.mockResolvedValue(expectedDbUserRequests);
|
||||
const result = userRequestService.fetchUserRequests(
|
||||
args.collectionID,
|
||||
ReqType.REST,
|
||||
args.cursor,
|
||||
args.take,
|
||||
user,
|
||||
);
|
||||
|
||||
expect(result).resolves.toEqualRight(expectedUserRequests);
|
||||
});
|
||||
test('Should resolve right and fetch user requests (with all params)', () => {
|
||||
const args: GetUserRequestArgs = {
|
||||
collectionID: 'collection-id-1',
|
||||
cursor: 'user-request-id-12',
|
||||
take: 2,
|
||||
};
|
||||
const expectedDbUserRequests = dbUserRequests.filter(
|
||||
(r) => r.collectionID === args.collectionID,
|
||||
);
|
||||
const expectedUserRequests = userRequests.filter(
|
||||
(r) => r.collectionID === args.collectionID,
|
||||
);
|
||||
|
||||
mockPrisma.userRequest.findMany.mockResolvedValue(expectedDbUserRequests);
|
||||
const result = userRequestService.fetchUserRequests(
|
||||
args.collectionID,
|
||||
ReqType.REST,
|
||||
args.cursor,
|
||||
args.take,
|
||||
user,
|
||||
);
|
||||
|
||||
expect(result).resolves.toEqualRight(expectedUserRequests);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetchUserRequest', () => {
|
||||
test('Should resolve right and fetch user request', () => {
|
||||
const expectedDbUserRequest = dbUserRequests[0];
|
||||
const expectedUserRequest = userRequests[0];
|
||||
|
||||
mockPrisma.userRequest.findUnique.mockResolvedValue(
|
||||
expectedDbUserRequest,
|
||||
);
|
||||
const result = userRequestService.fetchUserRequest(
|
||||
expectedUserRequest.id,
|
||||
user,
|
||||
);
|
||||
|
||||
expect(result).resolves.toEqualRight(expectedUserRequest);
|
||||
});
|
||||
|
||||
test('Should resolve left if user request not exist', () => {
|
||||
mockPrisma.userRequest.findUnique.mockResolvedValue(null);
|
||||
const result = userRequestService.fetchUserRequest(
|
||||
userRequests[0].id,
|
||||
user,
|
||||
);
|
||||
|
||||
expect(result).resolves.toEqualLeft(USER_REQUEST_NOT_FOUND);
|
||||
});
|
||||
|
||||
test('Should resolve left if another users user-request asked', () => {
|
||||
mockPrisma.userRequest.findUnique.mockResolvedValue({
|
||||
...dbUserRequests[0],
|
||||
userUid: 'another-user',
|
||||
});
|
||||
const result = userRequestService.fetchUserRequest(
|
||||
userRequests[0].id,
|
||||
user,
|
||||
);
|
||||
|
||||
expect(result).resolves.toEqualLeft(USER_REQUEST_NOT_FOUND);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createRequest', () => {
|
||||
test('Should resolve right and create user request', () => {
|
||||
const args: CreateUserRequestArgs = {
|
||||
collectionID: userRequests[0].collectionID,
|
||||
title: userRequests[0].title,
|
||||
request: userRequests[0].request,
|
||||
type: userRequests[0].type,
|
||||
};
|
||||
|
||||
mockPrisma.userRequest.count.mockResolvedValue(
|
||||
dbUserRequests[0].orderIndex - 1,
|
||||
);
|
||||
mockUserCollectionService.getUserCollection.mockResolvedValue(
|
||||
E.right({ type: userRequests[0].type, userUid: user.uid } as any),
|
||||
);
|
||||
mockPrisma.userRequest.create.mockResolvedValue(dbUserRequests[0]);
|
||||
|
||||
const result = userRequestService.createRequest(
|
||||
args.collectionID,
|
||||
args.title,
|
||||
args.request,
|
||||
args.type,
|
||||
user,
|
||||
);
|
||||
|
||||
expect(result).resolves.toEqualRight(userRequests[0]);
|
||||
});
|
||||
test('Should execute prisma.create() with correct params', async () => {
|
||||
const args: CreateUserRequestArgs = {
|
||||
collectionID: userRequests[0].collectionID,
|
||||
title: userRequests[0].title,
|
||||
request: userRequests[0].request,
|
||||
type: userRequests[0].type,
|
||||
};
|
||||
|
||||
mockPrisma.userRequest.count.mockResolvedValue(
|
||||
dbUserRequests[0].orderIndex - 1,
|
||||
);
|
||||
mockPrisma.userRequest.create.mockResolvedValue(dbUserRequests[0]);
|
||||
|
||||
await userRequestService.createRequest(
|
||||
args.collectionID,
|
||||
args.title,
|
||||
args.request,
|
||||
args.type,
|
||||
user,
|
||||
);
|
||||
|
||||
expect(mockPrisma.userRequest.create).toHaveBeenCalledWith({
|
||||
data: {
|
||||
...args,
|
||||
request: JSON.parse(args.request),
|
||||
type: DbRequestType[args.type],
|
||||
orderIndex: dbUserRequests[0].orderIndex,
|
||||
userUid: user.uid,
|
||||
},
|
||||
});
|
||||
});
|
||||
test('Should publish user request created message in pubnub', async () => {
|
||||
const args: CreateUserRequestArgs = {
|
||||
collectionID: userRequests[0].collectionID,
|
||||
title: userRequests[0].title,
|
||||
request: userRequests[0].request,
|
||||
type: userRequests[0].type,
|
||||
};
|
||||
|
||||
mockPrisma.userRequest.count.mockResolvedValue(
|
||||
dbUserRequests[0].orderIndex - 1,
|
||||
);
|
||||
mockPrisma.userRequest.create.mockResolvedValue(dbUserRequests[0]);
|
||||
|
||||
await userRequestService.createRequest(
|
||||
args.collectionID,
|
||||
args.title,
|
||||
args.request,
|
||||
args.type,
|
||||
user,
|
||||
);
|
||||
|
||||
expect(mockPubSub.publish).toHaveBeenCalledWith(
|
||||
`user_request/${dbUserRequests[0].userUid}/created`,
|
||||
userRequests[0],
|
||||
);
|
||||
});
|
||||
test('Should resolve left for json-invalid request', () => {
|
||||
const args: CreateUserRequestArgs = {
|
||||
collectionID: userRequests[0].collectionID,
|
||||
title: userRequests[0].title,
|
||||
request: 'invalid json',
|
||||
type: userRequests[0].type,
|
||||
};
|
||||
|
||||
mockPrisma.userRequest.count.mockResolvedValue(
|
||||
dbUserRequests[0].orderIndex - 1,
|
||||
);
|
||||
mockPrisma.userRequest.create.mockResolvedValue(dbUserRequests[0]);
|
||||
|
||||
const result = userRequestService.createRequest(
|
||||
args.collectionID,
|
||||
args.title,
|
||||
args.request,
|
||||
args.type,
|
||||
user,
|
||||
);
|
||||
|
||||
expect(result).resolves.toEqualLeft(JSON_INVALID);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateRequest', () => {
|
||||
test('Should resolve right and update user request', () => {
|
||||
const id = userRequests[0].id;
|
||||
const type = userRequests[0].type;
|
||||
const args: UpdateUserRequestArgs = {
|
||||
title: userRequests[0].title,
|
||||
request: userRequests[0].request,
|
||||
};
|
||||
|
||||
mockPrisma.userRequest.findFirst.mockResolvedValueOnce(dbUserRequests[0]);
|
||||
mockPrisma.userCollection.findFirst.mockResolvedValueOnce({} as any);
|
||||
mockPrisma.userRequest.update.mockResolvedValue(dbUserRequests[0]);
|
||||
|
||||
const result = userRequestService.updateRequest(
|
||||
id,
|
||||
args.title,
|
||||
type,
|
||||
args.request,
|
||||
user,
|
||||
);
|
||||
|
||||
expect(result).resolves.toEqualRight(userRequests[0]);
|
||||
});
|
||||
test('Should resolve right and perform prisma.update with correct param', async () => {
|
||||
const id = userRequests[0].id;
|
||||
const type = userRequests[0].type;
|
||||
const args: UpdateUserRequestArgs = {
|
||||
title: userRequests[0].title,
|
||||
request: userRequests[0].request,
|
||||
};
|
||||
|
||||
mockPrisma.userRequest.findFirst.mockResolvedValueOnce(dbUserRequests[0]);
|
||||
mockPrisma.userCollection.findFirst.mockResolvedValueOnce({} as any);
|
||||
mockPrisma.userRequest.update.mockResolvedValue(dbUserRequests[0]);
|
||||
|
||||
await userRequestService.updateRequest(
|
||||
id,
|
||||
args.title,
|
||||
type,
|
||||
args.request,
|
||||
user,
|
||||
);
|
||||
|
||||
expect(mockPrisma.userRequest.update).toHaveBeenCalledWith({
|
||||
where: { id },
|
||||
data: {
|
||||
...args,
|
||||
request: JSON.parse(args.request),
|
||||
},
|
||||
});
|
||||
});
|
||||
test('Should resolve right and publish to pubnub with correct param', async () => {
|
||||
const id = userRequests[0].id;
|
||||
const type = userRequests[0].type;
|
||||
const args: UpdateUserRequestArgs = {
|
||||
title: userRequests[0].title,
|
||||
request: userRequests[0].request,
|
||||
};
|
||||
|
||||
mockPrisma.userRequest.findFirst.mockResolvedValueOnce(dbUserRequests[0]);
|
||||
mockPrisma.userCollection.findFirst.mockResolvedValueOnce({} as any);
|
||||
mockPrisma.userRequest.update.mockResolvedValue(dbUserRequests[0]);
|
||||
|
||||
await userRequestService.updateRequest(
|
||||
id,
|
||||
args.title,
|
||||
type,
|
||||
args.request,
|
||||
user,
|
||||
);
|
||||
|
||||
expect(mockPubSub.publish).toHaveBeenCalledWith(
|
||||
`user_request/${dbUserRequests[0].userUid}/updated`,
|
||||
userRequests[0],
|
||||
);
|
||||
});
|
||||
test('Should resolve left if user request not found', () => {
|
||||
const id = userRequests[0].id;
|
||||
const type = userRequests[0].type;
|
||||
const args: UpdateUserRequestArgs = {
|
||||
title: userRequests[0].title,
|
||||
request: userRequests[0].request,
|
||||
};
|
||||
|
||||
mockPrisma.userRequest.findFirst.mockResolvedValue(null);
|
||||
|
||||
const result = userRequestService.updateRequest(
|
||||
id,
|
||||
args.title,
|
||||
type,
|
||||
args.request,
|
||||
user,
|
||||
);
|
||||
|
||||
expect(result).resolves.toEqualLeft(USER_REQUEST_NOT_FOUND);
|
||||
});
|
||||
test('Should resolve left if stringToJson returns error', () => {
|
||||
const id = userRequests[0].id;
|
||||
const type = userRequests[0].type;
|
||||
const args: UpdateUserRequestArgs = {
|
||||
title: userRequests[0].title,
|
||||
request: 'invalid json',
|
||||
};
|
||||
|
||||
mockPrisma.userRequest.findFirst.mockResolvedValueOnce(dbUserRequests[0]);
|
||||
mockPrisma.userCollection.findFirst.mockResolvedValueOnce({} as any);
|
||||
|
||||
const result = userRequestService.updateRequest(
|
||||
id,
|
||||
args.title,
|
||||
type,
|
||||
args.request,
|
||||
user,
|
||||
);
|
||||
|
||||
expect(result).resolves.toEqualLeft(JSON_INVALID);
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteRequest', () => {
|
||||
test('Should resolve right and delete user request', () => {
|
||||
const id = userRequests[0].id;
|
||||
|
||||
mockPrisma.userRequest.findFirst.mockResolvedValue(dbUserRequests[0]);
|
||||
mockPrisma.userRequest.delete.mockResolvedValue(dbUserRequests[0]);
|
||||
|
||||
const result = userRequestService.deleteRequest(id, user);
|
||||
|
||||
expect(result).resolves.toEqualRight(true);
|
||||
});
|
||||
test('Should resolve right and perform prisma.delete with correct param', async () => {
|
||||
const id = userRequests[0].id;
|
||||
|
||||
mockPrisma.userRequest.findFirst.mockResolvedValue(dbUserRequests[0]);
|
||||
mockPrisma.userRequest.delete.mockResolvedValue(null);
|
||||
|
||||
await userRequestService.deleteRequest(id, user);
|
||||
|
||||
expect(mockPrisma.userRequest.delete).toHaveBeenCalledWith({
|
||||
where: { id },
|
||||
});
|
||||
});
|
||||
test('Should resolve right and perform prisma.updateMany with correct param', async () => {
|
||||
const id = userRequests[0].id;
|
||||
|
||||
mockPrisma.userRequest.findFirst.mockResolvedValue(dbUserRequests[0]);
|
||||
mockPrisma.userRequest.delete.mockResolvedValue(null);
|
||||
|
||||
await userRequestService.deleteRequest(id, user);
|
||||
|
||||
expect(mockPrisma.userRequest.updateMany).toHaveBeenCalledWith({
|
||||
where: {
|
||||
collectionID: dbUserRequests[0].collectionID,
|
||||
orderIndex: { gt: dbUserRequests[0].orderIndex },
|
||||
},
|
||||
data: { orderIndex: { decrement: 1 } },
|
||||
});
|
||||
});
|
||||
test('Should resolve and publish message to pubnub', async () => {
|
||||
const id = userRequests[0].id;
|
||||
|
||||
mockPrisma.userRequest.findFirst.mockResolvedValue(dbUserRequests[0]);
|
||||
mockPrisma.userRequest.delete.mockResolvedValue(null);
|
||||
|
||||
const result = await userRequestService.deleteRequest(id, user);
|
||||
|
||||
expect(mockPubSub.publish).toHaveBeenCalledWith(
|
||||
`user_request/${dbUserRequests[0].userUid}/deleted`,
|
||||
userRequests[0],
|
||||
);
|
||||
});
|
||||
test('Should resolve error if the user request is not found', () => {
|
||||
const id = userRequests[0].id;
|
||||
mockPrisma.userRequest.findFirst.mockResolvedValue(null);
|
||||
|
||||
const result = userRequestService.deleteRequest(id, user);
|
||||
|
||||
expect(result).resolves.toEqualLeft(USER_REQUEST_NOT_FOUND);
|
||||
expect(mockPrisma.userRequest.findFirst).toHaveBeenCalledWith({
|
||||
where: { id, userUid: dbUserRequests[0].userUid },
|
||||
});
|
||||
expect(mockPrisma.userRequest.updateMany).not.toHaveBeenCalled();
|
||||
expect(mockPrisma.userRequest.delete).not.toHaveBeenCalled();
|
||||
expect(mockPubSub.publish).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('reorderRequests', () => {
|
||||
test('Should resolve left if transaction throws an error', async () => {
|
||||
const srcCollID = dbUserRequests[0].collectionID;
|
||||
const request = dbUserRequests[0];
|
||||
const destCollID = dbUserRequests[4].collectionID;
|
||||
const nextRequest = dbUserRequests[4];
|
||||
|
||||
mockPrisma.$transaction.mockRejectedValueOnce(new Error());
|
||||
const result = await userRequestService.reorderRequests(
|
||||
srcCollID,
|
||||
request,
|
||||
destCollID,
|
||||
nextRequest,
|
||||
);
|
||||
expect(result).toEqual(E.left(USER_REQUEST_REORDERING_FAILED));
|
||||
});
|
||||
test('Should resolve right and call transaction with the correct data', async () => {
|
||||
const srcCollID = dbUserRequests[0].collectionID;
|
||||
const request = dbUserRequests[0];
|
||||
const destCollID = dbUserRequests[4].collectionID;
|
||||
const nextRequest = dbUserRequests[4];
|
||||
|
||||
const updatedReq: DbUserRequest = {
|
||||
...request,
|
||||
collectionID: destCollID,
|
||||
orderIndex: nextRequest.orderIndex,
|
||||
};
|
||||
|
||||
mockPrisma.$transaction.mockResolvedValueOnce(E.right(updatedReq));
|
||||
const result = await userRequestService.reorderRequests(
|
||||
srcCollID,
|
||||
request,
|
||||
destCollID,
|
||||
nextRequest,
|
||||
);
|
||||
expect(mockPrisma.$transaction).toHaveBeenCalledWith(
|
||||
expect.any(Function),
|
||||
);
|
||||
expect(result).toEqual(E.right(updatedReq));
|
||||
});
|
||||
});
|
||||
|
||||
describe('findRequestAndNextRequest', () => {
|
||||
test('Should resolve right if the request and the next request are found', async () => {
|
||||
const args: MoveUserRequestArgs = {
|
||||
sourceCollectionID: userRequests[0].collectionID,
|
||||
destinationCollectionID: userRequests[4].collectionID,
|
||||
requestID: userRequests[0].id,
|
||||
nextRequestID: userRequests[4].id,
|
||||
};
|
||||
|
||||
mockPrisma.userRequest.findFirst
|
||||
.mockResolvedValueOnce(dbUserRequests[0])
|
||||
.mockResolvedValueOnce(dbUserRequests[4]);
|
||||
|
||||
const result = await userRequestService.findRequestAndNextRequest(
|
||||
args.sourceCollectionID,
|
||||
args.destinationCollectionID,
|
||||
args.requestID,
|
||||
args.nextRequestID,
|
||||
user,
|
||||
);
|
||||
|
||||
expect(result).toEqualRight({
|
||||
request: dbUserRequests[0],
|
||||
nextRequest: dbUserRequests[4],
|
||||
});
|
||||
});
|
||||
test('Should resolve right if the request and next request null', () => {
|
||||
const args: MoveUserRequestArgs = {
|
||||
sourceCollectionID: userRequests[0].collectionID,
|
||||
destinationCollectionID: userRequests[1].collectionID,
|
||||
requestID: userRequests[0].id,
|
||||
nextRequestID: null,
|
||||
};
|
||||
|
||||
mockPrisma.userRequest.findFirst
|
||||
.mockResolvedValueOnce(dbUserRequests[0])
|
||||
.mockResolvedValueOnce(null);
|
||||
|
||||
const result = userRequestService.findRequestAndNextRequest(
|
||||
args.sourceCollectionID,
|
||||
args.destinationCollectionID,
|
||||
args.requestID,
|
||||
args.nextRequestID,
|
||||
user,
|
||||
);
|
||||
|
||||
expect(result).resolves.toEqualRight({
|
||||
request: dbUserRequests[0],
|
||||
nextRequest: null,
|
||||
});
|
||||
});
|
||||
test('Should resolve left if the request is not found', () => {
|
||||
const args: MoveUserRequestArgs = {
|
||||
sourceCollectionID: userRequests[0].collectionID,
|
||||
destinationCollectionID: userRequests[1].collectionID,
|
||||
requestID: 'invalid',
|
||||
nextRequestID: null,
|
||||
};
|
||||
|
||||
mockPrisma.userRequest.findFirst.mockResolvedValueOnce(null);
|
||||
|
||||
const result = userRequestService.findRequestAndNextRequest(
|
||||
args.sourceCollectionID,
|
||||
args.destinationCollectionID,
|
||||
args.requestID,
|
||||
args.nextRequestID,
|
||||
user,
|
||||
);
|
||||
|
||||
expect(result).resolves.toEqualLeft(USER_REQUEST_NOT_FOUND);
|
||||
});
|
||||
test('Should resolve left if the nextRequest is not found', () => {
|
||||
const args: MoveUserRequestArgs = {
|
||||
sourceCollectionID: userRequests[0].collectionID,
|
||||
destinationCollectionID: userRequests[1].collectionID,
|
||||
requestID: userRequests[0].id,
|
||||
nextRequestID: 'invalid',
|
||||
};
|
||||
|
||||
mockPrisma.userRequest.findFirst
|
||||
.mockResolvedValueOnce(dbUserRequests[0])
|
||||
.mockResolvedValueOnce(null);
|
||||
|
||||
const result = userRequestService.findRequestAndNextRequest(
|
||||
args.sourceCollectionID,
|
||||
args.destinationCollectionID,
|
||||
args.requestID,
|
||||
args.nextRequestID,
|
||||
user,
|
||||
);
|
||||
|
||||
expect(result).resolves.toEqualLeft(USER_REQUEST_NOT_FOUND);
|
||||
});
|
||||
});
|
||||
|
||||
describe('moveRequest', () => {
|
||||
test('Should resolve right and the request', () => {
|
||||
const args: MoveUserRequestArgs = {
|
||||
sourceCollectionID: userRequests[0].collectionID,
|
||||
destinationCollectionID: userRequests[0].collectionID,
|
||||
requestID: userRequests[0].id,
|
||||
nextRequestID: null,
|
||||
};
|
||||
|
||||
jest
|
||||
.spyOn(userRequestService, 'findRequestAndNextRequest')
|
||||
.mockResolvedValue(
|
||||
E.right({ request: dbUserRequests[0], nextRequest: null }),
|
||||
);
|
||||
jest
|
||||
.spyOn(userRequestService, 'reorderRequests')
|
||||
.mockResolvedValue(E.right(dbUserRequests[0]));
|
||||
jest
|
||||
.spyOn(userRequestService, 'validateTypeEqualityForMoveRequest')
|
||||
.mockResolvedValue(E.right(true));
|
||||
|
||||
const result = userRequestService.moveRequest(
|
||||
args.sourceCollectionID,
|
||||
args.destinationCollectionID,
|
||||
args.requestID,
|
||||
args.nextRequestID,
|
||||
user,
|
||||
);
|
||||
|
||||
expect(result).resolves.toEqualRight(userRequests[0]);
|
||||
});
|
||||
test('Should resolve right and publish message to pubnub', async () => {
|
||||
const args: MoveUserRequestArgs = {
|
||||
sourceCollectionID: userRequests[0].collectionID,
|
||||
destinationCollectionID: userRequests[0].collectionID,
|
||||
requestID: userRequests[0].id,
|
||||
nextRequestID: null,
|
||||
};
|
||||
|
||||
jest
|
||||
.spyOn(userRequestService, 'findRequestAndNextRequest')
|
||||
.mockResolvedValue(
|
||||
E.right({ request: dbUserRequests[0], nextRequest: null }),
|
||||
);
|
||||
jest
|
||||
.spyOn(userRequestService, 'reorderRequests')
|
||||
.mockResolvedValue(E.right(dbUserRequests[0]));
|
||||
jest
|
||||
.spyOn(userRequestService, 'validateTypeEqualityForMoveRequest')
|
||||
.mockResolvedValue(E.right(true));
|
||||
|
||||
await userRequestService.moveRequest(
|
||||
args.sourceCollectionID,
|
||||
args.destinationCollectionID,
|
||||
args.requestID,
|
||||
args.nextRequestID,
|
||||
user,
|
||||
);
|
||||
|
||||
expect(mockPubSub.publish).toHaveBeenCalledWith(
|
||||
`user_request/${dbUserRequests[0].userUid}/moved`,
|
||||
userRequests[0],
|
||||
);
|
||||
});
|
||||
test('Should resolve left if finding the requests fails', () => {
|
||||
const args: MoveUserRequestArgs = {
|
||||
sourceCollectionID: userRequests[0].collectionID,
|
||||
destinationCollectionID: userRequests[0].collectionID,
|
||||
requestID: userRequests[0].id,
|
||||
nextRequestID: null,
|
||||
};
|
||||
|
||||
jest
|
||||
.spyOn(userRequestService, 'findRequestAndNextRequest')
|
||||
.mockResolvedValue(E.left(USER_REQUEST_NOT_FOUND));
|
||||
jest
|
||||
.spyOn(userRequestService, 'validateTypeEqualityForMoveRequest')
|
||||
.mockResolvedValue(E.right(true));
|
||||
|
||||
const result = userRequestService.moveRequest(
|
||||
args.sourceCollectionID,
|
||||
args.destinationCollectionID,
|
||||
args.requestID,
|
||||
args.nextRequestID,
|
||||
user,
|
||||
);
|
||||
|
||||
expect(result).resolves.toEqualLeft(USER_REQUEST_NOT_FOUND);
|
||||
});
|
||||
test('Should resolve left if reordering the request fails', async () => {
|
||||
const args: MoveUserRequestArgs = {
|
||||
sourceCollectionID: userRequests[0].collectionID,
|
||||
destinationCollectionID: userRequests[0].collectionID,
|
||||
requestID: userRequests[0].id,
|
||||
nextRequestID: null,
|
||||
};
|
||||
|
||||
jest
|
||||
.spyOn(userRequestService, 'findRequestAndNextRequest')
|
||||
.mockResolvedValue(
|
||||
E.right({
|
||||
request: dbUserRequests[0],
|
||||
nextRequest: null,
|
||||
}),
|
||||
);
|
||||
jest
|
||||
.spyOn(userRequestService, 'reorderRequests')
|
||||
.mockResolvedValue(E.left(USER_REQUEST_REORDERING_FAILED));
|
||||
jest
|
||||
.spyOn(userRequestService, 'validateTypeEqualityForMoveRequest')
|
||||
.mockResolvedValue(E.right(true));
|
||||
|
||||
const result = await userRequestService.moveRequest(
|
||||
args.sourceCollectionID,
|
||||
args.destinationCollectionID,
|
||||
args.requestID,
|
||||
args.nextRequestID,
|
||||
user,
|
||||
);
|
||||
|
||||
expect(result).toEqualLeft(USER_REQUEST_REORDERING_FAILED);
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateTypeEqualityForMoveRequest', () => {
|
||||
test('Should resolve right if the types are equal', () => {
|
||||
const srcCollID = 'srcCollID';
|
||||
const destCollID = 'destCollID';
|
||||
|
||||
mockUserCollectionService.getUserCollection.mockResolvedValueOnce(
|
||||
E.right({ type: userRequests[0].type, userUid: user.uid } as any),
|
||||
);
|
||||
mockUserCollectionService.getUserCollection.mockResolvedValueOnce(
|
||||
E.right({ type: userRequests[1].type, userUid: user.uid } as any),
|
||||
);
|
||||
|
||||
const result = userRequestService.validateTypeEqualityForMoveRequest(
|
||||
srcCollID,
|
||||
destCollID,
|
||||
userRequests[0],
|
||||
userRequests[1],
|
||||
);
|
||||
|
||||
expect(result).resolves.toEqualRight(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,448 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { PubSubService } from '../pubsub/pubsub.service';
|
||||
import * as E from 'fp-ts/Either';
|
||||
import { UserRequest } from './user-request.model';
|
||||
import { UserRequest as DbUserRequest } from '@prisma/client';
|
||||
import {
|
||||
USER_COLLECTION_NOT_FOUND,
|
||||
USER_REQUEST_CREATION_FAILED,
|
||||
USER_REQUEST_INVALID_TYPE,
|
||||
USER_REQUEST_NOT_FOUND,
|
||||
USER_REQUEST_REORDERING_FAILED,
|
||||
} from 'src/errors';
|
||||
import { stringToJson } from 'src/utils';
|
||||
import { AuthUser } from 'src/types/AuthUser';
|
||||
import { ReqType } from 'src/user-history/user-history.model';
|
||||
import { UserCollectionService } from 'src/user-collection/user-collection.service';
|
||||
|
||||
@Injectable()
|
||||
export class UserRequestService {
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly pubsub: PubSubService,
|
||||
private readonly userCollectionService: UserCollectionService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Typecast a database user request to a user request
|
||||
* @param dbRequest Database user request
|
||||
* @returns User request
|
||||
*/
|
||||
private cast(dbRequest: DbUserRequest): UserRequest {
|
||||
return {
|
||||
...dbRequest,
|
||||
type: ReqType[dbRequest.type],
|
||||
request: JSON.stringify(dbRequest.request),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get paginated user requests
|
||||
* @param collectionID ID of the collection to which the request belongs
|
||||
* @param take Number of requests to fetch
|
||||
* @param cursor ID of the request after which to fetch requests
|
||||
* @param user User who owns the requests
|
||||
* @returns Either of an Array of user requests
|
||||
*/
|
||||
async fetchUserRequests(
|
||||
collectionID: string,
|
||||
type: ReqType,
|
||||
cursor: string,
|
||||
take: number,
|
||||
user: AuthUser,
|
||||
) {
|
||||
const dbRequests = await this.prisma.userRequest.findMany({
|
||||
where: {
|
||||
userUid: user.uid,
|
||||
collectionID: collectionID,
|
||||
type,
|
||||
},
|
||||
take: take, // default: 10
|
||||
skip: cursor ? 1 : 0,
|
||||
cursor: cursor ? { id: cursor } : undefined,
|
||||
orderBy: { orderIndex: 'asc' },
|
||||
});
|
||||
|
||||
const userRequests: UserRequest[] = dbRequests.map((r) => this.cast(r));
|
||||
|
||||
return E.right(userRequests);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a user request by ID
|
||||
* @param id ID of the request to fetch
|
||||
* @param user User who owns the request
|
||||
* @returns Either of the user request
|
||||
*/
|
||||
async fetchUserRequest(
|
||||
id: string,
|
||||
user: AuthUser,
|
||||
): Promise<E.Left<string> | E.Right<UserRequest>> {
|
||||
const dbRequest = await this.prisma.userRequest.findUnique({
|
||||
where: { id },
|
||||
});
|
||||
if (!dbRequest || dbRequest.userUid !== user.uid) {
|
||||
return E.left(USER_REQUEST_NOT_FOUND);
|
||||
}
|
||||
|
||||
return E.right(this.cast(dbRequest));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of requests in a collection
|
||||
* @param collectionID ID of the collection to which the request belongs
|
||||
* @param user User who owns the collection
|
||||
* @returns Number of requests in the collection
|
||||
*/
|
||||
getRequestsCountInCollection(collectionID: string): Promise<number> {
|
||||
return this.prisma.userRequest.count({
|
||||
where: { collectionID },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a user request
|
||||
* @param collectionID ID of the collection to which the request belongs
|
||||
* @param title title of the request
|
||||
* @param request request to create
|
||||
* @param type type of the request
|
||||
* @param user User who owns the request
|
||||
* @returns Either of the created user request
|
||||
*/
|
||||
async createRequest(
|
||||
collectionID: string,
|
||||
title: string,
|
||||
request: string,
|
||||
type: ReqType,
|
||||
user: AuthUser,
|
||||
): Promise<E.Left<string> | E.Right<UserRequest>> {
|
||||
const jsonRequest = stringToJson(request);
|
||||
if (E.isLeft(jsonRequest)) return E.left(jsonRequest.left);
|
||||
|
||||
const collection = await this.userCollectionService.getUserCollection(
|
||||
collectionID,
|
||||
);
|
||||
if (E.isLeft(collection)) return E.left(collection.left);
|
||||
|
||||
if (collection.right.userUid !== user.uid)
|
||||
return E.left(USER_COLLECTION_NOT_FOUND);
|
||||
|
||||
if (collection.right.type !== ReqType[type])
|
||||
return E.left(USER_REQUEST_INVALID_TYPE);
|
||||
|
||||
try {
|
||||
const requestCount = await this.getRequestsCountInCollection(
|
||||
collectionID,
|
||||
);
|
||||
|
||||
const request = await this.prisma.userRequest.create({
|
||||
data: {
|
||||
collectionID,
|
||||
title,
|
||||
request: jsonRequest.right,
|
||||
type: ReqType[type],
|
||||
orderIndex: requestCount + 1,
|
||||
userUid: user.uid,
|
||||
},
|
||||
});
|
||||
|
||||
const userRequest = this.cast(request);
|
||||
|
||||
await this.pubsub.publish(
|
||||
`user_request/${user.uid}/created`,
|
||||
userRequest,
|
||||
);
|
||||
|
||||
return E.right(userRequest);
|
||||
} catch (err) {
|
||||
return E.left(USER_REQUEST_CREATION_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a user request
|
||||
* @param id ID of the request to update
|
||||
* @param title title of the request to update
|
||||
* @param type type of the request to update
|
||||
* @param request request to update
|
||||
* @param user User who owns the request
|
||||
*/
|
||||
async updateRequest(
|
||||
id: string,
|
||||
title: string,
|
||||
type: ReqType,
|
||||
request: string,
|
||||
user: AuthUser,
|
||||
): Promise<E.Left<string> | E.Right<UserRequest>> {
|
||||
const existRequest = await this.prisma.userRequest.findFirst({
|
||||
where: { id, userUid: user.uid },
|
||||
});
|
||||
if (!existRequest) return E.left(USER_REQUEST_NOT_FOUND);
|
||||
|
||||
if (existRequest.type !== ReqType[type])
|
||||
return E.left(USER_REQUEST_INVALID_TYPE);
|
||||
|
||||
let jsonRequest = undefined;
|
||||
if (request) {
|
||||
const jsonRequestE = stringToJson(request);
|
||||
if (E.isLeft(jsonRequestE)) return E.left(jsonRequestE.left);
|
||||
jsonRequest = jsonRequestE.right;
|
||||
}
|
||||
|
||||
const updatedRequest = await this.prisma.userRequest.update({
|
||||
where: { id },
|
||||
data: {
|
||||
title,
|
||||
request: jsonRequest,
|
||||
},
|
||||
});
|
||||
|
||||
const userRequest: UserRequest = this.cast(updatedRequest);
|
||||
|
||||
await this.pubsub.publish(`user_request/${user.uid}/updated`, userRequest);
|
||||
|
||||
return E.right(userRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a user request
|
||||
* @param id ID of the request to delete
|
||||
* @param user User who owns the request
|
||||
* @returns Either of a boolean
|
||||
*/
|
||||
async deleteRequest(
|
||||
id: string,
|
||||
user: AuthUser,
|
||||
): Promise<E.Left<string> | E.Right<boolean>> {
|
||||
const request = await this.prisma.userRequest.findFirst({
|
||||
where: { id, userUid: user.uid },
|
||||
});
|
||||
if (!request) return E.left(USER_REQUEST_NOT_FOUND);
|
||||
|
||||
await this.prisma.userRequest.updateMany({
|
||||
where: {
|
||||
collectionID: request.collectionID,
|
||||
orderIndex: { gt: request.orderIndex },
|
||||
},
|
||||
data: { orderIndex: { decrement: 1 } },
|
||||
});
|
||||
await this.prisma.userRequest.delete({ where: { id } });
|
||||
|
||||
await this.pubsub.publish(
|
||||
`user_request/${user.uid}/deleted`,
|
||||
this.cast(request),
|
||||
);
|
||||
|
||||
return E.right(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a request for re-ordering inside/across collections
|
||||
* @param srcCollID ID of the source collection
|
||||
* @param destCollID ID of the destination collection
|
||||
* @param requestID ID of the request to move
|
||||
* @param nextRequestID ID of the request after which the request should be moved
|
||||
* @param user User who owns the request
|
||||
* @returns Either of the updated request
|
||||
*/
|
||||
async moveRequest(
|
||||
srcCollID: string,
|
||||
destCollID: string,
|
||||
requestID: string,
|
||||
nextRequestID: string,
|
||||
user: AuthUser,
|
||||
): Promise<E.Left<string> | E.Right<UserRequest>> {
|
||||
const twoRequests = await this.findRequestAndNextRequest(
|
||||
srcCollID,
|
||||
destCollID,
|
||||
requestID,
|
||||
nextRequestID,
|
||||
user,
|
||||
);
|
||||
if (E.isLeft(twoRequests)) return twoRequests;
|
||||
const { request, nextRequest } = twoRequests.right;
|
||||
|
||||
const isTypeValidate = await this.validateTypeEqualityForMoveRequest(
|
||||
srcCollID,
|
||||
destCollID,
|
||||
request,
|
||||
nextRequest,
|
||||
);
|
||||
if (E.isLeft(isTypeValidate)) return E.left(isTypeValidate.left);
|
||||
|
||||
const updatedRequest = await this.reorderRequests(
|
||||
srcCollID,
|
||||
request,
|
||||
destCollID,
|
||||
nextRequest,
|
||||
);
|
||||
if (E.isLeft(updatedRequest)) return updatedRequest;
|
||||
|
||||
const userRequest: UserRequest = this.cast(updatedRequest.right);
|
||||
|
||||
await this.pubsub.publish(`user_request/${user.uid}/moved`, userRequest);
|
||||
|
||||
return E.right(userRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function validate/ensure the same type (REST/GQL) in the source and destination collection and the request
|
||||
* @param srcCollID ID of the source collection
|
||||
* @param destCollID ID of the destination collection
|
||||
* @param request Request to move
|
||||
* @param nextRequest Request after which the request should be moved
|
||||
* @returns Either of a boolean
|
||||
*/
|
||||
async validateTypeEqualityForMoveRequest(
|
||||
srcCollID,
|
||||
destCollID,
|
||||
request,
|
||||
nextRequest,
|
||||
) {
|
||||
const collections = await Promise.all([
|
||||
this.userCollectionService.getUserCollection(srcCollID),
|
||||
this.userCollectionService.getUserCollection(destCollID),
|
||||
]);
|
||||
|
||||
const srcColl = collections[0];
|
||||
if (E.isLeft(srcColl)) return E.left(srcColl.left);
|
||||
|
||||
const destColl = collections[1];
|
||||
if (E.isLeft(destColl)) return E.left(destColl.left);
|
||||
|
||||
if (
|
||||
srcColl.right.type !== destColl.right.type ||
|
||||
(nextRequest && request.type !== nextRequest.type)
|
||||
) {
|
||||
return E.left(USER_REQUEST_INVALID_TYPE);
|
||||
}
|
||||
|
||||
return E.right(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper function.
|
||||
* Find the request and the next request(destination collection)
|
||||
* @param srcCollID Source collection ID
|
||||
* @param destCollID Destination collection ID
|
||||
* @param requestID Request ID
|
||||
* @param nextRequestID Next request ID
|
||||
* @param user User who owns the collection
|
||||
* @returns Either Left with error message or Right with the request and the next request
|
||||
*/
|
||||
async findRequestAndNextRequest(
|
||||
srcCollID: string,
|
||||
destCollID: string,
|
||||
requestID: string,
|
||||
nextRequestID: string,
|
||||
user: AuthUser,
|
||||
): Promise<
|
||||
| E.Left<string>
|
||||
| E.Right<{
|
||||
request: DbUserRequest;
|
||||
nextRequest: DbUserRequest;
|
||||
}>
|
||||
> {
|
||||
const request = await this.prisma.userRequest.findFirst({
|
||||
where: { id: requestID, collectionID: srcCollID, userUid: user.uid },
|
||||
});
|
||||
if (!request) return E.left(USER_REQUEST_NOT_FOUND);
|
||||
|
||||
let nextRequest: DbUserRequest = null;
|
||||
if (nextRequestID) {
|
||||
nextRequest = await this.prisma.userRequest.findFirst({
|
||||
where: {
|
||||
id: nextRequestID,
|
||||
collectionID: destCollID,
|
||||
userUid: user.uid,
|
||||
},
|
||||
});
|
||||
if (!nextRequest) return E.left(USER_REQUEST_NOT_FOUND);
|
||||
}
|
||||
|
||||
return E.right({ request, nextRequest });
|
||||
}
|
||||
|
||||
/**
|
||||
* Update order indexes of requests in collection
|
||||
* @param srcCollID - id of collection, where the request is moving from
|
||||
* @param request - request to be moved
|
||||
* @param destCollID - id of collection, where the request is moving to
|
||||
* @param nextRequest - request that comes after the updated request in its new position
|
||||
* @returns Promise of an Either of `DbUserRequest` object or error message
|
||||
*/
|
||||
async reorderRequests(
|
||||
srcCollID: string,
|
||||
request: DbUserRequest,
|
||||
destCollID: string,
|
||||
nextRequest: DbUserRequest,
|
||||
): Promise<E.Left<string> | E.Right<DbUserRequest>> {
|
||||
try {
|
||||
return await this.prisma.$transaction<
|
||||
E.Left<string> | E.Right<DbUserRequest>
|
||||
>(async (tx) => {
|
||||
const isSameCollection = srcCollID === destCollID;
|
||||
const isMovingUp = nextRequest?.orderIndex < request.orderIndex; // false, if nextRequest is null
|
||||
|
||||
const nextReqOrderIndex = nextRequest?.orderIndex;
|
||||
const reqCountInDestColl = nextRequest
|
||||
? undefined
|
||||
: await this.getRequestsCountInCollection(destCollID);
|
||||
|
||||
// Updating order indexes of other requests in collection(s)
|
||||
if (isSameCollection) {
|
||||
const updateFrom = isMovingUp
|
||||
? nextReqOrderIndex
|
||||
: request.orderIndex + 1;
|
||||
const updateTo = isMovingUp ? request.orderIndex : nextReqOrderIndex;
|
||||
|
||||
await tx.userRequest.updateMany({
|
||||
where: {
|
||||
collectionID: srcCollID,
|
||||
orderIndex: { gte: updateFrom, lt: updateTo },
|
||||
},
|
||||
data: {
|
||||
orderIndex: isMovingUp ? { increment: 1 } : { decrement: 1 },
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await tx.userRequest.updateMany({
|
||||
where: {
|
||||
collectionID: srcCollID,
|
||||
orderIndex: { gte: request.orderIndex },
|
||||
},
|
||||
data: { orderIndex: { decrement: 1 } },
|
||||
});
|
||||
|
||||
if (nextRequest) {
|
||||
await tx.userRequest.updateMany({
|
||||
where: {
|
||||
collectionID: destCollID,
|
||||
orderIndex: { gte: nextReqOrderIndex },
|
||||
},
|
||||
data: { orderIndex: { increment: 1 } },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Updating order index of the request
|
||||
let adjust: number;
|
||||
if (isSameCollection) adjust = nextRequest ? (isMovingUp ? 0 : -1) : 0;
|
||||
else adjust = nextRequest ? 0 : 1;
|
||||
|
||||
const newOrderIndex =
|
||||
(nextReqOrderIndex ?? reqCountInDestColl) + adjust;
|
||||
|
||||
const updatedRequest = await tx.userRequest.update({
|
||||
where: { id: request.id },
|
||||
data: { orderIndex: newOrderIndex, collectionID: destCollID },
|
||||
});
|
||||
|
||||
return E.right(updatedRequest);
|
||||
});
|
||||
} catch (err) {
|
||||
return E.left(USER_REQUEST_REORDERING_FAILED);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user