chore: moved shared-requests into shortcode module
This commit is contained in:
@@ -20,7 +20,6 @@ import { ShortcodeModule } from './shortcode/shortcode.module';
|
||||
import { COOKIES_NOT_FOUND } from './errors';
|
||||
import { ThrottlerModule } from '@nestjs/throttler';
|
||||
import { AppController } from './app.controller';
|
||||
import { SharedRequestModule } from './shared-request/shared-request.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -78,7 +77,6 @@ import { SharedRequestModule } from './shared-request/shared-request.module';
|
||||
TeamInvitationModule,
|
||||
UserCollectionModule,
|
||||
ShortcodeModule,
|
||||
SharedRequestModule,
|
||||
],
|
||||
providers: [GQLComplexityPlugin],
|
||||
controllers: [AppController],
|
||||
|
||||
@@ -622,29 +622,23 @@ export const MAILER_SMTP_URL_UNDEFINED = 'mailer/smtp_url_undefined' as const;
|
||||
export const MAILER_FROM_ADDRESS_UNDEFINED =
|
||||
'mailer/from_address_undefined' as const;
|
||||
|
||||
/**
|
||||
* SharedRequest not found in DB
|
||||
* (SharedRequestService)
|
||||
*/
|
||||
export const SHARED_REQUEST_NOT_FOUND = 'shared_request/not_found' as const;
|
||||
|
||||
/**
|
||||
* SharedRequest invalid request JSON format
|
||||
* (SharedRequestService)
|
||||
* (ShortcodeService)
|
||||
*/
|
||||
export const SHARED_REQUEST_INVALID_REQUEST_JSON =
|
||||
'shared_request/request_invalid_format' as const;
|
||||
export const SHORTCODE_INVALID_REQUEST_JSON =
|
||||
'shortcode/request_invalid_format' as const;
|
||||
|
||||
/**
|
||||
* SharedRequest invalid properties JSON format
|
||||
* (SharedRequestService)
|
||||
* (ShortcodeService)
|
||||
*/
|
||||
export const SHARED_REQUEST_INVALID_PROPERTIES_JSON =
|
||||
'shared_request/properties_invalid_format' as const;
|
||||
export const SHORTCODE_INVALID_PROPERTIES_JSON =
|
||||
'shortcode/properties_invalid_format' as const;
|
||||
|
||||
/**
|
||||
* SharedRequest invalid properties not found
|
||||
* (SharedRequestService)
|
||||
* (ShortcodeService)
|
||||
*/
|
||||
export const SHARED_REQUEST_PROPERTIES_NOT_FOUND =
|
||||
'shared_request/properties_not_found' as const;
|
||||
export const SHORTCODE_PROPERTIES_NOT_FOUND =
|
||||
'shortcode/properties_not_found' as const;
|
||||
|
||||
@@ -27,7 +27,6 @@ import { UserRequestUserCollectionResolver } from './user-request/resolvers/user
|
||||
import { UserEnvsUserResolver } from './user-environment/user.resolver';
|
||||
import { UserHistoryUserResolver } from './user-history/user.resolver';
|
||||
import { UserSettingsUserResolver } from './user-settings/user.resolver';
|
||||
import { SharedRequestResolver } from './shared-request/shared-request.resolver';
|
||||
|
||||
/**
|
||||
* All the resolvers present in the application.
|
||||
@@ -57,7 +56,6 @@ const RESOLVERS = [
|
||||
UserRequestUserCollectionResolver,
|
||||
UserSettingsResolver,
|
||||
UserSettingsUserResolver,
|
||||
SharedRequestResolver,
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,7 +27,6 @@ import {
|
||||
UserCollectionReorderData,
|
||||
} from 'src/user-collection/user-collections.model';
|
||||
import { Shortcode } from 'src/shortcode/shortcode.model';
|
||||
import { SharedRequest } from 'src/shared-request/shared-requests.model';
|
||||
|
||||
// A custom message type that defines the topic and the corresponding payload.
|
||||
// For every module that publishes a subscription add its type def and the possible subscription type.
|
||||
@@ -70,8 +69,7 @@ export type TopicDef = {
|
||||
[topic: `team_req/${string}/req_deleted`]: string;
|
||||
[topic: `team/${string}/invite_added`]: TeamInvitation;
|
||||
[topic: `team/${string}/invite_removed`]: string;
|
||||
[topic: `shortcode/${string}/${'created' | 'revoked'}`]: Shortcode;
|
||||
[
|
||||
topic: `shared_request/${string}/${'created' | 'revoked' | 'updated'}`
|
||||
]: SharedRequest;
|
||||
topic: `shortcode/${string}/${'created' | 'revoked' | 'updated'}`
|
||||
]: Shortcode;
|
||||
};
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { SharedRequestService } from './shared-request.service';
|
||||
import { SharedRequestResolver } from './shared-request.resolver';
|
||||
import { PubSubModule } from 'src/pubsub/pubsub.module';
|
||||
import { PrismaModule } from 'src/prisma/prisma.module';
|
||||
import { UserModule } from 'src/user/user.module';
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule, PubSubModule, UserModule],
|
||||
providers: [SharedRequestService, SharedRequestResolver],
|
||||
})
|
||||
export class SharedRequestModule {}
|
||||
@@ -1,168 +0,0 @@
|
||||
import {
|
||||
Args,
|
||||
ID,
|
||||
Resolver,
|
||||
Query,
|
||||
Mutation,
|
||||
Subscription,
|
||||
} from '@nestjs/graphql';
|
||||
import { SharedRequest } from './shared-requests.model';
|
||||
import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard';
|
||||
import { UseGuards } from '@nestjs/common';
|
||||
import { SharedRequestService } from './shared-request.service';
|
||||
import { PubSubService } from 'src/pubsub/pubsub.service';
|
||||
import * as E from 'fp-ts/Either';
|
||||
import { GqlAuthGuard } from 'src/guards/gql-auth.guard';
|
||||
import { throwErr } from 'src/utils';
|
||||
import { GqlUser } from 'src/decorators/gql-user.decorator';
|
||||
import { AuthUser } from 'src/types/AuthUser';
|
||||
import { SkipThrottle } from '@nestjs/throttler';
|
||||
import { PaginationArgs } from 'src/types/input-types.args';
|
||||
|
||||
@UseGuards(GqlThrottlerGuard)
|
||||
@Resolver(() => SharedRequest)
|
||||
export class SharedRequestResolver {
|
||||
constructor(
|
||||
private readonly sharedRequestService: SharedRequestService,
|
||||
private readonly pubsub: PubSubService,
|
||||
) {}
|
||||
|
||||
/* Queries */
|
||||
@Query(() => SharedRequest, {
|
||||
description: 'Resolves and returns a shared-request data',
|
||||
})
|
||||
async sharedRequest(
|
||||
@Args({
|
||||
name: 'code',
|
||||
type: () => ID,
|
||||
description: 'The shared-request to fetch',
|
||||
})
|
||||
code: string,
|
||||
) {
|
||||
const result = await this.sharedRequestService.getSharedRequest(code);
|
||||
|
||||
if (E.isLeft(result)) throwErr(result.left);
|
||||
return result.right;
|
||||
}
|
||||
|
||||
@Query(() => [SharedRequest], {
|
||||
description: 'List all shared-request the current user has generated',
|
||||
})
|
||||
@UseGuards(GqlAuthGuard)
|
||||
async mySharedRequests(
|
||||
@GqlUser() user: AuthUser,
|
||||
@Args() args: PaginationArgs,
|
||||
) {
|
||||
return this.sharedRequestService.fetchUserSharedRequests(user.uid, args);
|
||||
}
|
||||
|
||||
/* Mutations */
|
||||
@Mutation(() => SharedRequest, {
|
||||
description: 'Create a shared-request for the given request.',
|
||||
})
|
||||
@UseGuards(GqlAuthGuard)
|
||||
async createSharedRequest(
|
||||
@GqlUser() user: AuthUser,
|
||||
@Args({
|
||||
name: 'request',
|
||||
description: 'JSON string of the request object',
|
||||
})
|
||||
request: string,
|
||||
@Args({
|
||||
name: 'properties',
|
||||
description: 'JSON string of the properties of the embed',
|
||||
nullable: true,
|
||||
})
|
||||
properties: string,
|
||||
) {
|
||||
const result = await this.sharedRequestService.createSharedRequest(
|
||||
request,
|
||||
properties,
|
||||
user,
|
||||
);
|
||||
|
||||
if (E.isLeft(result)) throwErr(result.left);
|
||||
return result.right;
|
||||
}
|
||||
|
||||
@Mutation(() => SharedRequest, {
|
||||
description: 'Update a user generated shared-request',
|
||||
})
|
||||
@UseGuards(GqlAuthGuard)
|
||||
async updateSharedRequest(
|
||||
@GqlUser() user: AuthUser,
|
||||
@Args({
|
||||
name: 'code',
|
||||
type: () => ID,
|
||||
description: 'The shared-request to update',
|
||||
})
|
||||
code: string,
|
||||
@Args({
|
||||
name: 'properties',
|
||||
description: 'JSON string of the properties of the embed',
|
||||
})
|
||||
properties: string,
|
||||
) {
|
||||
const result = await this.sharedRequestService.updateSharedRequest(
|
||||
code,
|
||||
user.uid,
|
||||
properties,
|
||||
);
|
||||
|
||||
if (E.isLeft(result)) throwErr(result.left);
|
||||
return result.right;
|
||||
}
|
||||
|
||||
@Mutation(() => Boolean, {
|
||||
description: 'Revoke a user generated shared-request',
|
||||
})
|
||||
@UseGuards(GqlAuthGuard)
|
||||
async revokeSharedRequest(
|
||||
@GqlUser() user: AuthUser,
|
||||
@Args({
|
||||
name: 'code',
|
||||
type: () => ID,
|
||||
description: 'The shared-request to resolve',
|
||||
})
|
||||
code: string,
|
||||
) {
|
||||
const result = await this.sharedRequestService.revokeSharedRequest(
|
||||
code,
|
||||
user.uid,
|
||||
);
|
||||
|
||||
if (E.isLeft(result)) throwErr(result.left);
|
||||
return result.right;
|
||||
}
|
||||
|
||||
/* Subscriptions */
|
||||
@Subscription(() => SharedRequest, {
|
||||
description: 'Listen for shared-request creation',
|
||||
resolve: (value) => value,
|
||||
})
|
||||
@SkipThrottle()
|
||||
@UseGuards(GqlAuthGuard)
|
||||
mySharedRequestCreated(@GqlUser() user: AuthUser) {
|
||||
return this.pubsub.asyncIterator(`shared_request/${user.uid}/created`);
|
||||
}
|
||||
|
||||
@Subscription(() => SharedRequest, {
|
||||
description: 'Listen for shared-request updates',
|
||||
resolve: (value) => value,
|
||||
})
|
||||
@SkipThrottle()
|
||||
@UseGuards(GqlAuthGuard)
|
||||
mySharedRequestUpdated(@GqlUser() user: AuthUser) {
|
||||
return this.pubsub.asyncIterator(`shared_request/${user.uid}/updated`);
|
||||
}
|
||||
|
||||
@Subscription(() => SharedRequest, {
|
||||
description: 'Listen for shared-request deletion',
|
||||
resolve: (value) => value,
|
||||
})
|
||||
@SkipThrottle()
|
||||
@UseGuards(GqlAuthGuard)
|
||||
mySharedRequestRevoked(@GqlUser() user: AuthUser) {
|
||||
return this.pubsub.asyncIterator(`shared_request/${user.uid}/revoked`);
|
||||
}
|
||||
}
|
||||
@@ -1,473 +0,0 @@
|
||||
import { mockDeep, mockReset } from 'jest-mock-extended';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import {
|
||||
SHARED_REQUEST_INVALID_PROPERTIES_JSON,
|
||||
SHARED_REQUEST_INVALID_REQUEST_JSON,
|
||||
SHARED_REQUEST_NOT_FOUND,
|
||||
SHARED_REQUEST_PROPERTIES_NOT_FOUND,
|
||||
} from 'src/errors';
|
||||
import { UserService } from 'src/user/user.service';
|
||||
import { SharedRequestService } from './shared-request.service';
|
||||
import { SharedRequest } from './shared-requests.model';
|
||||
import { AuthUser } from 'src/types/AuthUser';
|
||||
|
||||
const mockPrisma = mockDeep<PrismaService>();
|
||||
|
||||
const mockPubSub = {
|
||||
publish: jest.fn().mockResolvedValue(null),
|
||||
};
|
||||
|
||||
const mockDocFunc = jest.fn();
|
||||
|
||||
const mockUserService = new UserService(mockPrisma as any, mockPubSub as any);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
const sharedRequestsService = new SharedRequestService(
|
||||
mockPrisma,
|
||||
mockPubSub as any,
|
||||
mockUserService,
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
mockReset(mockPrisma);
|
||||
mockPubSub.publish.mockClear();
|
||||
});
|
||||
const createdOn = new Date();
|
||||
|
||||
const user: AuthUser = {
|
||||
uid: '123344',
|
||||
email: 'dwight@dundermifflin.com',
|
||||
displayName: 'Dwight Schrute',
|
||||
photoURL: 'https://en.wikipedia.org/wiki/Dwight_Schrute',
|
||||
isAdmin: false,
|
||||
refreshToken: 'hbfvdkhjbvkdvdfjvbnkhjb',
|
||||
createdOn: createdOn,
|
||||
currentGQLSession: {},
|
||||
currentRESTSession: {},
|
||||
};
|
||||
|
||||
const mockEmbed = {
|
||||
id: '123',
|
||||
request: '{}',
|
||||
properties: '{}',
|
||||
createdOn: createdOn,
|
||||
creatorUid: user.uid,
|
||||
updatedOn: createdOn,
|
||||
};
|
||||
|
||||
const mockShortcode = {
|
||||
id: '123',
|
||||
request: '{}',
|
||||
properties: null,
|
||||
createdOn: createdOn,
|
||||
creatorUid: user.uid,
|
||||
updatedOn: createdOn,
|
||||
};
|
||||
|
||||
const sharedRequests = [
|
||||
{
|
||||
id: 'blablabla',
|
||||
request: {
|
||||
hello: 'there',
|
||||
},
|
||||
properties: {
|
||||
foo: 'bar',
|
||||
},
|
||||
creatorUid: user.uid,
|
||||
createdOn: new Date(),
|
||||
updatedOn: createdOn,
|
||||
},
|
||||
{
|
||||
id: 'blablabla1',
|
||||
request: {
|
||||
hello: 'there',
|
||||
},
|
||||
properties: {
|
||||
foo: 'bar',
|
||||
},
|
||||
creatorUid: user.uid,
|
||||
createdOn: new Date(),
|
||||
updatedOn: createdOn,
|
||||
},
|
||||
];
|
||||
|
||||
describe('SharedRequestService', () => {
|
||||
describe('getSharedRequest', () => {
|
||||
test('should return a valid SharedRequest with valid SharedRequest ID', async () => {
|
||||
mockPrisma.shortcode.findFirstOrThrow.mockResolvedValueOnce(mockEmbed);
|
||||
|
||||
const result = await sharedRequestsService.getSharedRequest(mockEmbed.id);
|
||||
expect(result).toEqualRight(<SharedRequest>{
|
||||
id: mockEmbed.id,
|
||||
createdOn: mockEmbed.createdOn,
|
||||
request: JSON.stringify(mockEmbed.request),
|
||||
properties: JSON.stringify(mockEmbed.properties),
|
||||
});
|
||||
});
|
||||
|
||||
test('should throw SHARED_REQUEST_NOT_FOUND error when SharedRequest ID is invalid', async () => {
|
||||
mockPrisma.shortcode.findFirstOrThrow.mockRejectedValueOnce(
|
||||
'NotFoundError',
|
||||
);
|
||||
|
||||
const result = await sharedRequestsService.getSharedRequest('invalidID');
|
||||
expect(result).toEqualLeft(SHARED_REQUEST_NOT_FOUND);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createSharedRequest', () => {
|
||||
test('should throw SHARED_REQUEST_INVALID_REQUEST_JSON error if incoming request data is invalid', async () => {
|
||||
const result = await sharedRequestsService.createSharedRequest(
|
||||
'invalidRequest',
|
||||
null,
|
||||
user,
|
||||
);
|
||||
expect(result).toEqualLeft(SHARED_REQUEST_INVALID_REQUEST_JSON);
|
||||
});
|
||||
|
||||
test('should throw SHARED_REQUEST_INVALID_PROPERTIES_JSON error if incoming properties data is invalid', async () => {
|
||||
const result = await sharedRequestsService.createSharedRequest(
|
||||
'{}',
|
||||
'invalid_data',
|
||||
user,
|
||||
);
|
||||
expect(result).toEqualLeft(SHARED_REQUEST_INVALID_PROPERTIES_JSON);
|
||||
});
|
||||
|
||||
test('should successfully create a new Embed with valid user uid', async () => {
|
||||
// generateUniqueShortCodeID --> getSharedRequest
|
||||
mockPrisma.shortcode.findFirstOrThrow.mockRejectedValueOnce(
|
||||
'NotFoundError',
|
||||
);
|
||||
mockPrisma.shortcode.create.mockResolvedValueOnce(mockEmbed);
|
||||
|
||||
const result = await sharedRequestsService.createSharedRequest(
|
||||
'{}',
|
||||
'{}',
|
||||
user,
|
||||
);
|
||||
expect(result).toEqualRight(<SharedRequest>{
|
||||
id: mockEmbed.id,
|
||||
createdOn: mockEmbed.createdOn,
|
||||
request: JSON.stringify(mockEmbed.request),
|
||||
properties: JSON.stringify(mockEmbed.properties),
|
||||
});
|
||||
});
|
||||
|
||||
test('should successfully create a new ShortCode with valid user uid', async () => {
|
||||
// generateUniqueShortCodeID --> getSharedRequest
|
||||
mockPrisma.shortcode.findFirstOrThrow.mockRejectedValueOnce(
|
||||
'NotFoundError',
|
||||
);
|
||||
mockPrisma.shortcode.create.mockResolvedValueOnce(mockShortcode);
|
||||
|
||||
const result = await sharedRequestsService.createSharedRequest(
|
||||
'{}',
|
||||
null,
|
||||
user,
|
||||
);
|
||||
expect(result).toEqualRight(<SharedRequest>{
|
||||
id: mockShortcode.id,
|
||||
createdOn: mockShortcode.createdOn,
|
||||
request: JSON.stringify(mockShortcode.request),
|
||||
properties: mockShortcode.properties,
|
||||
});
|
||||
});
|
||||
|
||||
test('should send pubsub message to `shared_request/{uid}/created` on successful creation of a Shortcode', async () => {
|
||||
// generateUniqueShortCodeID --> getSharedRequest
|
||||
mockPrisma.shortcode.findFirstOrThrow.mockRejectedValueOnce(
|
||||
'NotFoundError',
|
||||
);
|
||||
mockPrisma.shortcode.create.mockResolvedValueOnce(mockShortcode);
|
||||
|
||||
const result = await sharedRequestsService.createSharedRequest(
|
||||
'{}',
|
||||
null,
|
||||
user,
|
||||
);
|
||||
|
||||
expect(mockPubSub.publish).toHaveBeenCalledWith(
|
||||
`shared_request/${mockShortcode.creatorUid}/created`,
|
||||
<SharedRequest>{
|
||||
id: mockShortcode.id,
|
||||
createdOn: mockShortcode.createdOn,
|
||||
request: JSON.stringify(mockShortcode.request),
|
||||
properties: mockShortcode.properties,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test('should send pubsub message to `shared_request/{uid}/created` on successful creation of an Embed', async () => {
|
||||
// generateUniqueShortCodeID --> getSharedRequest
|
||||
mockPrisma.shortcode.findFirstOrThrow.mockRejectedValueOnce(
|
||||
'NotFoundError',
|
||||
);
|
||||
mockPrisma.shortcode.create.mockResolvedValueOnce(mockEmbed);
|
||||
|
||||
const result = await sharedRequestsService.createSharedRequest(
|
||||
'{}',
|
||||
'{}',
|
||||
user,
|
||||
);
|
||||
|
||||
expect(mockPubSub.publish).toHaveBeenCalledWith(
|
||||
`shared_request/${mockEmbed.creatorUid}/created`,
|
||||
<SharedRequest>{
|
||||
id: mockEmbed.id,
|
||||
createdOn: mockEmbed.createdOn,
|
||||
request: JSON.stringify(mockEmbed.request),
|
||||
properties: JSON.stringify(mockEmbed.properties),
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetchUserSharedRequests', () => {
|
||||
test('should return list of SharedRequests with valid inputs and no cursor', async () => {
|
||||
mockPrisma.shortcode.findMany.mockResolvedValueOnce(sharedRequests);
|
||||
|
||||
const result = await sharedRequestsService.fetchUserSharedRequests(
|
||||
user.uid,
|
||||
{
|
||||
cursor: null,
|
||||
take: 10,
|
||||
},
|
||||
);
|
||||
expect(result).toEqual(<SharedRequest[]>[
|
||||
{
|
||||
id: sharedRequests[0].id,
|
||||
request: JSON.stringify(sharedRequests[0].request),
|
||||
properties: JSON.stringify(sharedRequests[0].properties),
|
||||
createdOn: sharedRequests[0].createdOn,
|
||||
},
|
||||
{
|
||||
id: sharedRequests[1].id,
|
||||
request: JSON.stringify(sharedRequests[1].request),
|
||||
properties: JSON.stringify(sharedRequests[1].properties),
|
||||
createdOn: sharedRequests[1].createdOn,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('should return list of SharedRequests with valid inputs and cursor', async () => {
|
||||
mockPrisma.shortcode.findMany.mockResolvedValue([sharedRequests[1]]);
|
||||
|
||||
const result = await sharedRequestsService.fetchUserSharedRequests(
|
||||
user.uid,
|
||||
{
|
||||
cursor: 'blablabla',
|
||||
take: 10,
|
||||
},
|
||||
);
|
||||
expect(result).toEqual(<SharedRequest[]>[
|
||||
{
|
||||
id: sharedRequests[1].id,
|
||||
request: JSON.stringify(sharedRequests[1].request),
|
||||
properties: JSON.stringify(sharedRequests[1].properties),
|
||||
createdOn: sharedRequests[1].createdOn,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('should return an empty array for an invalid cursor', async () => {
|
||||
mockPrisma.shortcode.findMany.mockResolvedValue([]);
|
||||
|
||||
const result = await sharedRequestsService.fetchUserSharedRequests(
|
||||
user.uid,
|
||||
{
|
||||
cursor: 'invalidcursor',
|
||||
take: 10,
|
||||
},
|
||||
);
|
||||
|
||||
expect(result).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('should return an empty array for an invalid user id and null cursor', async () => {
|
||||
mockPrisma.shortcode.findMany.mockResolvedValue([]);
|
||||
|
||||
const result = await sharedRequestsService.fetchUserSharedRequests(
|
||||
'invalidid',
|
||||
{
|
||||
cursor: null,
|
||||
take: 10,
|
||||
},
|
||||
);
|
||||
|
||||
expect(result).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('should return an empty array for an invalid user id and an invalid cursor', async () => {
|
||||
mockPrisma.shortcode.findMany.mockResolvedValue([]);
|
||||
|
||||
const result = await sharedRequestsService.fetchUserSharedRequests(
|
||||
'invalidid',
|
||||
{
|
||||
cursor: 'invalidcursor',
|
||||
take: 10,
|
||||
},
|
||||
);
|
||||
|
||||
expect(result).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('revokeSharedRequest', () => {
|
||||
test('should return true on successful deletion of SharedRequest with valid inputs', async () => {
|
||||
mockPrisma.shortcode.delete.mockResolvedValueOnce(mockEmbed);
|
||||
|
||||
const result = await sharedRequestsService.revokeSharedRequest(
|
||||
mockEmbed.id,
|
||||
mockEmbed.creatorUid,
|
||||
);
|
||||
|
||||
expect(mockPrisma.shortcode.delete).toHaveBeenCalledWith({
|
||||
where: {
|
||||
creator_uid_shortcode_unique: {
|
||||
creatorUid: mockEmbed.creatorUid,
|
||||
id: mockEmbed.id,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toEqualRight(true);
|
||||
});
|
||||
|
||||
test('should return SHARED_REQUEST_NOT_FOUND error when SharedRequest is invalid and user uid is valid', async () => {
|
||||
mockPrisma.shortcode.delete.mockRejectedValue('RecordNotFound');
|
||||
expect(
|
||||
sharedRequestsService.revokeSharedRequest('invalid', 'testuser'),
|
||||
).resolves.toEqualLeft(SHARED_REQUEST_NOT_FOUND);
|
||||
});
|
||||
|
||||
test('should return SHARED_REQUEST_NOT_FOUND error when SharedRequest is valid and user uid is invalid', async () => {
|
||||
mockPrisma.shortcode.delete.mockRejectedValue('RecordNotFound');
|
||||
expect(
|
||||
sharedRequestsService.revokeSharedRequest(
|
||||
'blablablabla',
|
||||
'invalidUser',
|
||||
),
|
||||
).resolves.toEqualLeft(SHARED_REQUEST_NOT_FOUND);
|
||||
});
|
||||
|
||||
test('should return SHARED_REQUEST_NOT_FOUND error when both SharedRequest and user uid are invalid', async () => {
|
||||
mockPrisma.shortcode.delete.mockRejectedValue('RecordNotFound');
|
||||
expect(
|
||||
sharedRequestsService.revokeSharedRequest('invalid', 'invalid'),
|
||||
).resolves.toEqualLeft(SHARED_REQUEST_NOT_FOUND);
|
||||
});
|
||||
|
||||
test('should send pubsub message to `shared_request/{uid}/revoked` on successful deletion of SharedRequest', async () => {
|
||||
mockPrisma.shortcode.delete.mockResolvedValueOnce(mockEmbed);
|
||||
|
||||
const result = await sharedRequestsService.revokeSharedRequest(
|
||||
mockEmbed.id,
|
||||
mockEmbed.creatorUid,
|
||||
);
|
||||
|
||||
expect(mockPubSub.publish).toHaveBeenCalledWith(
|
||||
`shared_request/${mockEmbed.creatorUid}/revoked`,
|
||||
{
|
||||
id: mockEmbed.id,
|
||||
createdOn: mockEmbed.createdOn,
|
||||
request: JSON.stringify(mockEmbed.request),
|
||||
properties: JSON.stringify(mockEmbed.properties),
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteUserSharedRequests', () => {
|
||||
test('should successfully delete all users SharedRequests with valid user uid', async () => {
|
||||
mockPrisma.shortcode.deleteMany.mockResolvedValueOnce({ count: 1 });
|
||||
|
||||
const result = await sharedRequestsService.deleteUserSharedRequests(
|
||||
mockEmbed.creatorUid,
|
||||
);
|
||||
expect(result).toEqual(1);
|
||||
});
|
||||
|
||||
test('should return 0 when user uid is invalid', async () => {
|
||||
mockPrisma.shortcode.deleteMany.mockResolvedValueOnce({ count: 0 });
|
||||
|
||||
const result = await sharedRequestsService.deleteUserSharedRequests(
|
||||
mockEmbed.creatorUid,
|
||||
);
|
||||
expect(result).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateSharedRequest', () => {
|
||||
test('should return SHARED_REQUEST_PROPERTIES_NOT_FOUND error when updatedProps in invalid', async () => {
|
||||
const result = await sharedRequestsService.updateSharedRequest(
|
||||
mockEmbed.id,
|
||||
user.uid,
|
||||
'',
|
||||
);
|
||||
expect(result).toEqualLeft(SHARED_REQUEST_PROPERTIES_NOT_FOUND);
|
||||
});
|
||||
|
||||
test('should return SHARED_REQUEST_PROPERTIES_NOT_FOUND error when updatedProps in invalid JSON format', async () => {
|
||||
const result = await sharedRequestsService.updateSharedRequest(
|
||||
mockEmbed.id,
|
||||
user.uid,
|
||||
'{kk',
|
||||
);
|
||||
expect(result).toEqualLeft(SHARED_REQUEST_INVALID_PROPERTIES_JSON);
|
||||
});
|
||||
|
||||
test('should return SHARED_REQUEST_NOT_FOUND error when SharedRequest ID is invalid', async () => {
|
||||
mockPrisma.shortcode.update.mockRejectedValue('RecordNotFound');
|
||||
const result = await sharedRequestsService.updateSharedRequest(
|
||||
'invalidID',
|
||||
user.uid,
|
||||
'{}',
|
||||
);
|
||||
expect(result).toEqualLeft(SHARED_REQUEST_NOT_FOUND);
|
||||
});
|
||||
|
||||
test('should successfully update a SharedRequests with valid inputs', async () => {
|
||||
mockPrisma.shortcode.update.mockResolvedValueOnce({
|
||||
...mockEmbed,
|
||||
properties: '{"foo":"bar"}',
|
||||
});
|
||||
|
||||
const result = await sharedRequestsService.updateSharedRequest(
|
||||
mockEmbed.id,
|
||||
user.uid,
|
||||
'{"foo":"bar"}',
|
||||
);
|
||||
expect(result).toEqualRight({
|
||||
id: mockEmbed.id,
|
||||
createdOn: mockEmbed.createdOn,
|
||||
request: JSON.stringify(mockEmbed.request),
|
||||
properties: JSON.stringify('{"foo":"bar"}'),
|
||||
});
|
||||
});
|
||||
|
||||
test('should send pubsub message to `shared_request/{uid}/updated` on successful Update of SharedRequest', async () => {
|
||||
mockPrisma.shortcode.update.mockResolvedValueOnce({
|
||||
...mockEmbed,
|
||||
properties: '{"foo":"bar"}',
|
||||
});
|
||||
|
||||
const result = await sharedRequestsService.updateSharedRequest(
|
||||
mockEmbed.id,
|
||||
user.uid,
|
||||
'{"foo":"bar"}',
|
||||
);
|
||||
|
||||
expect(mockPubSub.publish).toHaveBeenCalledWith(
|
||||
`shared_request/${mockEmbed.creatorUid}/updated`,
|
||||
{
|
||||
id: mockEmbed.id,
|
||||
createdOn: mockEmbed.createdOn,
|
||||
request: JSON.stringify(mockEmbed.request),
|
||||
properties: JSON.stringify('{"foo":"bar"}'),
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,259 +0,0 @@
|
||||
import { Injectable, OnModuleInit } from '@nestjs/common';
|
||||
import { PrismaService } from 'src/prisma/prisma.service';
|
||||
import { PubSubService } from 'src/pubsub/pubsub.service';
|
||||
import { Shortcode as DBSharedRequest } from '@prisma/client';
|
||||
import * as E from 'fp-ts/Either';
|
||||
import * as TO from 'fp-ts/TaskOption';
|
||||
import * as T from 'fp-ts/Task';
|
||||
import { SharedRequest } from './shared-requests.model';
|
||||
import {
|
||||
SHARED_REQUEST_INVALID_PROPERTIES_JSON,
|
||||
SHARED_REQUEST_INVALID_REQUEST_JSON,
|
||||
SHARED_REQUEST_NOT_FOUND,
|
||||
SHARED_REQUEST_PROPERTIES_NOT_FOUND,
|
||||
} from 'src/errors';
|
||||
import { stringToJson } from 'src/utils';
|
||||
import { AuthUser } from 'src/types/AuthUser';
|
||||
import { PaginationArgs } from 'src/types/input-types.args';
|
||||
import { UserDataHandler } from 'src/user/user.data.handler';
|
||||
import { UserService } from 'src/user/user.service';
|
||||
|
||||
const SHORT_CODE_LENGTH = 12;
|
||||
const SHORT_CODE_CHARS =
|
||||
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
@Injectable()
|
||||
export class SharedRequestService implements UserDataHandler, OnModuleInit {
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly pubsub: PubSubService,
|
||||
private readonly userService: UserService,
|
||||
) {}
|
||||
|
||||
onModuleInit() {
|
||||
this.userService.registerUserDataHandler(this);
|
||||
}
|
||||
|
||||
canAllowUserDeletion(user: AuthUser): TO.TaskOption<string> {
|
||||
return TO.none;
|
||||
}
|
||||
|
||||
onUserDelete(user: AuthUser): T.Task<void> {
|
||||
return async () => {
|
||||
await this.deleteUserSharedRequests(user.uid);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all the Users SharedRequests
|
||||
* @param uid User Uid
|
||||
* @returns number of all deleted user SharedRequests
|
||||
*/
|
||||
async deleteUserSharedRequests(uid: string) {
|
||||
const deletedShortCodes = await this.prisma.shortcode.deleteMany({
|
||||
where: {
|
||||
creatorUid: uid,
|
||||
},
|
||||
});
|
||||
|
||||
return deletedShortCodes.count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a Prisma SharedRequest type into the SharedRequest model
|
||||
*
|
||||
* @param sharedRequestInfo Prisma SharedRequest type
|
||||
* @returns GQL SharedRequest
|
||||
*/
|
||||
private cast(sharedRequestInfo: DBSharedRequest): SharedRequest {
|
||||
return <SharedRequest>{
|
||||
id: sharedRequestInfo.id,
|
||||
request: JSON.stringify(sharedRequestInfo.request),
|
||||
properties:
|
||||
sharedRequestInfo.properties != null
|
||||
? JSON.stringify(sharedRequestInfo.properties)
|
||||
: null,
|
||||
createdOn: sharedRequestInfo.createdOn,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Generate a shortcode
|
||||
*
|
||||
* @returns generated shortcode
|
||||
*/
|
||||
private generateShortCodeID(): string {
|
||||
let result = '';
|
||||
for (let i = 0; i < SHORT_CODE_LENGTH; i++) {
|
||||
result +=
|
||||
SHORT_CODE_CHARS[Math.floor(Math.random() * SHORT_CODE_CHARS.length)];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if ShortCode is already present in DB
|
||||
*
|
||||
* @returns Shortcode
|
||||
*/
|
||||
private async generateUniqueShortCodeID() {
|
||||
while (true) {
|
||||
const code = this.generateShortCodeID();
|
||||
|
||||
const data = await this.getSharedRequest(code);
|
||||
if (E.isLeft(data)) return E.right(code);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch details regarding a SharedRequest
|
||||
*
|
||||
* @param sharedRequestID SharedRequest
|
||||
* @returns Either of SharedRequest details or error
|
||||
*/
|
||||
async getSharedRequest(sharedRequestID: string) {
|
||||
try {
|
||||
const sharedRequest = await this.prisma.shortcode.findFirstOrThrow({
|
||||
where: { id: sharedRequestID },
|
||||
});
|
||||
return E.right(this.cast(sharedRequest));
|
||||
} catch (error) {
|
||||
return E.left(SHARED_REQUEST_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new SharedRequest
|
||||
*
|
||||
* @param request JSON string of request details
|
||||
* @param properties JSON string of embed properties, if present
|
||||
* @returns Either of SharedRequest or error
|
||||
*/
|
||||
async createSharedRequest(
|
||||
request: string,
|
||||
properties: string | null = null,
|
||||
userInfo: AuthUser,
|
||||
) {
|
||||
const requestData = stringToJson(request);
|
||||
if (E.isLeft(requestData))
|
||||
return E.left(SHARED_REQUEST_INVALID_REQUEST_JSON);
|
||||
|
||||
const parsedProperties = stringToJson(properties);
|
||||
if (E.isLeft(parsedProperties))
|
||||
return E.left(SHARED_REQUEST_INVALID_PROPERTIES_JSON);
|
||||
|
||||
const generatedShortCode = await this.generateUniqueShortCodeID();
|
||||
if (E.isLeft(generatedShortCode)) return E.left(generatedShortCode.left);
|
||||
|
||||
const createdSharedRequest = await this.prisma.shortcode.create({
|
||||
data: {
|
||||
id: generatedShortCode.right,
|
||||
request: requestData.right,
|
||||
properties: parsedProperties.right ?? undefined,
|
||||
creatorUid: userInfo.uid,
|
||||
},
|
||||
});
|
||||
|
||||
this.pubsub.publish(
|
||||
`shared_request/${createdSharedRequest.creatorUid}/created`,
|
||||
this.cast(createdSharedRequest),
|
||||
);
|
||||
|
||||
return E.right(this.cast(createdSharedRequest));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch SharedRequest created by a User
|
||||
*
|
||||
* @param uid User Uid
|
||||
* @param args Pagination arguments
|
||||
* @returns Array of SharedRequest
|
||||
*/
|
||||
async fetchUserSharedRequests(uid: string, args: PaginationArgs) {
|
||||
const sharedRequests = await this.prisma.shortcode.findMany({
|
||||
where: {
|
||||
creatorUid: uid,
|
||||
},
|
||||
orderBy: {
|
||||
createdOn: 'desc',
|
||||
},
|
||||
skip: args.cursor ? 1 : 0,
|
||||
take: args.take,
|
||||
cursor: args.cursor ? { id: args.cursor } : undefined,
|
||||
});
|
||||
|
||||
const fetchedSharedRequests: SharedRequest[] = sharedRequests.map((code) =>
|
||||
this.cast(code),
|
||||
);
|
||||
|
||||
return fetchedSharedRequests;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a SharedRequest
|
||||
*
|
||||
* @param sharedRequestID SharedRequest ID
|
||||
* @param uid User Uid
|
||||
* @returns Boolean on successful deletion
|
||||
*/
|
||||
async revokeSharedRequest(sharedRequestID: string, uid: string) {
|
||||
try {
|
||||
const deletedSharedRequest = await this.prisma.shortcode.delete({
|
||||
where: {
|
||||
creator_uid_shortcode_unique: {
|
||||
creatorUid: uid,
|
||||
id: sharedRequestID,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
this.pubsub.publish(
|
||||
`shared_request/${deletedSharedRequest.creatorUid}/revoked`,
|
||||
this.cast(deletedSharedRequest),
|
||||
);
|
||||
|
||||
return E.right(true);
|
||||
} catch (error) {
|
||||
return E.left(SHARED_REQUEST_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a created SharedRequest
|
||||
* @param sharedRequestID SharedRequest ID
|
||||
* @param uid User Uid
|
||||
* @returns Updated SharedRequest
|
||||
*/
|
||||
async updateSharedRequest(
|
||||
sharedRequestID: string,
|
||||
uid: string,
|
||||
updatedProps: string,
|
||||
) {
|
||||
if (!updatedProps) return E.left(SHARED_REQUEST_PROPERTIES_NOT_FOUND);
|
||||
|
||||
const parsedProperties = stringToJson(updatedProps);
|
||||
if (E.isLeft(parsedProperties) || !parsedProperties.right)
|
||||
return E.left(SHARED_REQUEST_INVALID_PROPERTIES_JSON);
|
||||
|
||||
try {
|
||||
const updatedSharedRequest = await this.prisma.shortcode.update({
|
||||
where: {
|
||||
creator_uid_shortcode_unique: {
|
||||
creatorUid: uid,
|
||||
id: sharedRequestID,
|
||||
},
|
||||
},
|
||||
data: {
|
||||
properties: parsedProperties.right,
|
||||
},
|
||||
});
|
||||
|
||||
this.pubsub.publish(
|
||||
`shared_request/${updatedSharedRequest.creatorUid}/updated`,
|
||||
this.cast(updatedSharedRequest),
|
||||
);
|
||||
|
||||
return E.right(this.cast(updatedSharedRequest));
|
||||
} catch (error) {
|
||||
return E.left(SHARED_REQUEST_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import { Field, ID, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
@ObjectType()
|
||||
export class SharedRequest {
|
||||
@Field(() => ID, {
|
||||
description: 'The 12 digit alphanumeric code',
|
||||
})
|
||||
id: string;
|
||||
|
||||
@Field({
|
||||
description: 'JSON string representing the request data',
|
||||
})
|
||||
request: string;
|
||||
|
||||
@Field({
|
||||
description: 'JSON string representing the properties for an embed',
|
||||
nullable: true,
|
||||
})
|
||||
properties: string;
|
||||
|
||||
@Field({
|
||||
description: 'Timestamp of when the SharedRequest was created',
|
||||
})
|
||||
createdOn: Date;
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import { Field, ID, ObjectType } from '@nestjs/graphql';
|
||||
@ObjectType()
|
||||
export class Shortcode {
|
||||
@Field(() => ID, {
|
||||
description: 'The shortcode. 12 digit alphanumeric.',
|
||||
description: 'The 12 digit alphanumeric code',
|
||||
})
|
||||
id: string;
|
||||
|
||||
@@ -13,7 +13,13 @@ export class Shortcode {
|
||||
request: string;
|
||||
|
||||
@Field({
|
||||
description: 'Timestamp of when the Shortcode was created',
|
||||
description: 'JSON string representing the properties for an embed',
|
||||
nullable: true,
|
||||
})
|
||||
properties: string;
|
||||
|
||||
@Field({
|
||||
description: 'Timestamp of when the SharedRequest was created',
|
||||
})
|
||||
createdOn: Date;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ export class ShortcodeResolver {
|
||||
@Query(() => Shortcode, {
|
||||
description: 'Resolves and returns a shortcode data',
|
||||
nullable: true,
|
||||
deprecationReason: 'Use SharedRequests instead',
|
||||
})
|
||||
async shortcode(
|
||||
@Args({
|
||||
@@ -55,7 +54,6 @@ export class ShortcodeResolver {
|
||||
|
||||
@Query(() => [Shortcode], {
|
||||
description: 'List all shortcodes the current user has generated',
|
||||
deprecationReason: 'Use SharedRequests instead',
|
||||
})
|
||||
@UseGuards(GqlAuthGuard)
|
||||
async myShortcodes(@GqlUser() user: AuthUser, @Args() args: PaginationArgs) {
|
||||
@@ -65,22 +63,54 @@ export class ShortcodeResolver {
|
||||
/* Mutations */
|
||||
@Mutation(() => Shortcode, {
|
||||
description: 'Create a shortcode for the given request.',
|
||||
deprecationReason: 'Use SharedRequests instead',
|
||||
})
|
||||
@UseGuards(GqlAuthGuard)
|
||||
async createShortcode(
|
||||
@GqlUser() user: AuthUser,
|
||||
@Args({
|
||||
name: 'request',
|
||||
description: 'JSON string of the request object',
|
||||
})
|
||||
request: string,
|
||||
@Context() ctx: any,
|
||||
@Args({
|
||||
name: 'properties',
|
||||
description: 'JSON string of the properties of the embed',
|
||||
nullable: true,
|
||||
})
|
||||
properties: string,
|
||||
) {
|
||||
const decodedAccessToken = this.jwtService.verify(
|
||||
ctx.req.cookies['access_token'],
|
||||
);
|
||||
const result = await this.shortcodeService.createShortcode(
|
||||
request,
|
||||
decodedAccessToken?.sub,
|
||||
properties,
|
||||
user,
|
||||
);
|
||||
|
||||
if (E.isLeft(result)) throwErr(result.left);
|
||||
return result.right;
|
||||
}
|
||||
|
||||
@Mutation(() => Shortcode, {
|
||||
description: 'Update a user generated Shortcode',
|
||||
})
|
||||
@UseGuards(GqlAuthGuard)
|
||||
async updateShortcode(
|
||||
@GqlUser() user: AuthUser,
|
||||
@Args({
|
||||
name: 'code',
|
||||
type: () => ID,
|
||||
description: 'The Shortcode to update',
|
||||
})
|
||||
code: string,
|
||||
@Args({
|
||||
name: 'properties',
|
||||
description: 'JSON string of the properties of the embed',
|
||||
})
|
||||
properties: string,
|
||||
) {
|
||||
const result = await this.shortcodeService.updateShortcode(
|
||||
code,
|
||||
user.uid,
|
||||
properties,
|
||||
);
|
||||
|
||||
if (E.isLeft(result)) throwErr(result.left);
|
||||
@@ -89,7 +119,6 @@ export class ShortcodeResolver {
|
||||
|
||||
@Mutation(() => Boolean, {
|
||||
description: 'Revoke a user generated shortcode',
|
||||
deprecationReason: 'Use SharedRequests instead',
|
||||
})
|
||||
@UseGuards(GqlAuthGuard)
|
||||
async revokeShortcode(
|
||||
@@ -111,7 +140,6 @@ export class ShortcodeResolver {
|
||||
@Subscription(() => Shortcode, {
|
||||
description: 'Listen for shortcode creation',
|
||||
resolve: (value) => value,
|
||||
deprecationReason: 'Use SharedRequests instead',
|
||||
})
|
||||
@SkipThrottle()
|
||||
@UseGuards(GqlAuthGuard)
|
||||
@@ -119,10 +147,19 @@ export class ShortcodeResolver {
|
||||
return this.pubsub.asyncIterator(`shortcode/${user.uid}/created`);
|
||||
}
|
||||
|
||||
@Subscription(() => Shortcode, {
|
||||
description: 'Listen for Shortcode updates',
|
||||
resolve: (value) => value,
|
||||
})
|
||||
@SkipThrottle()
|
||||
@UseGuards(GqlAuthGuard)
|
||||
myShortcodesUpdated(@GqlUser() user: AuthUser) {
|
||||
return this.pubsub.asyncIterator(`shortcode/${user.uid}/updated`);
|
||||
}
|
||||
|
||||
@Subscription(() => Shortcode, {
|
||||
description: 'Listen for shortcode deletion',
|
||||
resolve: (value) => value,
|
||||
deprecationReason: 'Use SharedRequests instead',
|
||||
})
|
||||
@SkipThrottle()
|
||||
@UseGuards(GqlAuthGuard)
|
||||
|
||||
@@ -3,7 +3,10 @@ import { PrismaService } from '../prisma/prisma.service';
|
||||
import {
|
||||
SHORTCODE_ALREADY_EXISTS,
|
||||
SHORTCODE_INVALID_JSON,
|
||||
SHORTCODE_INVALID_PROPERTIES_JSON,
|
||||
SHORTCODE_INVALID_REQUEST_JSON,
|
||||
SHORTCODE_NOT_FOUND,
|
||||
SHORTCODE_PROPERTIES_NOT_FOUND,
|
||||
} from 'src/errors';
|
||||
import { Shortcode } from './shortcode.model';
|
||||
import { ShortcodeService } from './shortcode.service';
|
||||
@@ -22,7 +25,7 @@ const mockFB = {
|
||||
doc: mockDocFunc,
|
||||
},
|
||||
};
|
||||
const mockUserService = new UserService(mockFB as any, mockPubSub as any);
|
||||
const mockUserService = new UserService(mockPrisma as any, mockPubSub as any);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
@@ -38,20 +41,34 @@ beforeEach(() => {
|
||||
});
|
||||
const createdOn = new Date();
|
||||
|
||||
const shortCodeWithOutUser = {
|
||||
id: '123',
|
||||
request: '{}',
|
||||
properties: null,
|
||||
const user: AuthUser = {
|
||||
uid: '123344',
|
||||
email: 'dwight@dundermifflin.com',
|
||||
displayName: 'Dwight Schrute',
|
||||
photoURL: 'https://en.wikipedia.org/wiki/Dwight_Schrute',
|
||||
isAdmin: false,
|
||||
refreshToken: 'hbfvdkhjbvkdvdfjvbnkhjb',
|
||||
createdOn: createdOn,
|
||||
creatorUid: null,
|
||||
currentGQLSession: {},
|
||||
currentRESTSession: {},
|
||||
};
|
||||
|
||||
const shortCodeWithUser = {
|
||||
const mockEmbed = {
|
||||
id: '123',
|
||||
request: '{}',
|
||||
properties: '{}',
|
||||
createdOn: createdOn,
|
||||
creatorUid: user.uid,
|
||||
updatedOn: createdOn,
|
||||
};
|
||||
|
||||
const mockShortcode = {
|
||||
id: '123',
|
||||
request: '{}',
|
||||
properties: null,
|
||||
createdOn: createdOn,
|
||||
creatorUid: 'user_uid_1',
|
||||
creatorUid: user.uid,
|
||||
updatedOn: createdOn,
|
||||
};
|
||||
|
||||
const shortcodes = [
|
||||
@@ -63,8 +80,9 @@ const shortcodes = [
|
||||
properties: {
|
||||
foo: 'bar',
|
||||
},
|
||||
creatorUid: 'testuser',
|
||||
creatorUid: user.uid,
|
||||
createdOn: new Date(),
|
||||
updatedOn: createdOn,
|
||||
},
|
||||
{
|
||||
id: 'blablabla1',
|
||||
@@ -74,25 +92,23 @@ const shortcodes = [
|
||||
properties: {
|
||||
foo: 'bar',
|
||||
},
|
||||
creatorUid: 'testuser',
|
||||
creatorUid: user.uid,
|
||||
createdOn: new Date(),
|
||||
updatedOn: createdOn,
|
||||
},
|
||||
];
|
||||
|
||||
describe('ShortcodeService', () => {
|
||||
describe('getShortCode', () => {
|
||||
test('should return a valid shortcode with valid shortcode ID', async () => {
|
||||
mockPrisma.shortcode.findFirstOrThrow.mockResolvedValueOnce(
|
||||
shortCodeWithOutUser,
|
||||
);
|
||||
test('should return a valid Shortcode with valid Shortcode ID', async () => {
|
||||
mockPrisma.shortcode.findFirstOrThrow.mockResolvedValueOnce(mockEmbed);
|
||||
|
||||
const result = await shortcodeService.getShortCode(
|
||||
shortCodeWithOutUser.id,
|
||||
);
|
||||
const result = await shortcodeService.getShortCode(mockEmbed.id);
|
||||
expect(result).toEqualRight(<Shortcode>{
|
||||
id: shortCodeWithOutUser.id,
|
||||
createdOn: shortCodeWithOutUser.createdOn,
|
||||
request: JSON.stringify(shortCodeWithOutUser.request),
|
||||
id: mockEmbed.id,
|
||||
createdOn: mockEmbed.createdOn,
|
||||
request: JSON.stringify(mockEmbed.request),
|
||||
properties: JSON.stringify(mockEmbed.properties),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -107,10 +123,10 @@ describe('ShortcodeService', () => {
|
||||
});
|
||||
|
||||
describe('fetchUserShortCodes', () => {
|
||||
test('should return list of shortcodes with valid inputs and no cursor', async () => {
|
||||
test('should return list of Shortcode with valid inputs and no cursor', async () => {
|
||||
mockPrisma.shortcode.findMany.mockResolvedValueOnce(shortcodes);
|
||||
|
||||
const result = await shortcodeService.fetchUserShortCodes('testuser', {
|
||||
const result = await shortcodeService.fetchUserShortCodes(user.uid, {
|
||||
cursor: null,
|
||||
take: 10,
|
||||
});
|
||||
@@ -118,20 +134,22 @@ describe('ShortcodeService', () => {
|
||||
{
|
||||
id: shortcodes[0].id,
|
||||
request: JSON.stringify(shortcodes[0].request),
|
||||
properties: JSON.stringify(shortcodes[0].properties),
|
||||
createdOn: shortcodes[0].createdOn,
|
||||
},
|
||||
{
|
||||
id: shortcodes[1].id,
|
||||
request: JSON.stringify(shortcodes[1].request),
|
||||
properties: JSON.stringify(shortcodes[1].properties),
|
||||
createdOn: shortcodes[1].createdOn,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('should return list of shortcodes with valid inputs and cursor', async () => {
|
||||
test('should return list of Shortcode with valid inputs and cursor', async () => {
|
||||
mockPrisma.shortcode.findMany.mockResolvedValue([shortcodes[1]]);
|
||||
|
||||
const result = await shortcodeService.fetchUserShortCodes('testuser', {
|
||||
const result = await shortcodeService.fetchUserShortCodes(user.uid, {
|
||||
cursor: 'blablabla',
|
||||
take: 10,
|
||||
});
|
||||
@@ -139,6 +157,7 @@ describe('ShortcodeService', () => {
|
||||
{
|
||||
id: shortcodes[1].id,
|
||||
request: JSON.stringify(shortcodes[1].request),
|
||||
properties: JSON.stringify(shortcodes[1].properties),
|
||||
createdOn: shortcodes[1].createdOn,
|
||||
},
|
||||
]);
|
||||
@@ -147,7 +166,7 @@ describe('ShortcodeService', () => {
|
||||
test('should return an empty array for an invalid cursor', async () => {
|
||||
mockPrisma.shortcode.findMany.mockResolvedValue([]);
|
||||
|
||||
const result = await shortcodeService.fetchUserShortCodes('testuser', {
|
||||
const result = await shortcodeService.fetchUserShortCodes(user.uid, {
|
||||
cursor: 'invalidcursor',
|
||||
take: 10,
|
||||
});
|
||||
@@ -179,77 +198,111 @@ describe('ShortcodeService', () => {
|
||||
});
|
||||
|
||||
describe('createShortcode', () => {
|
||||
test('should throw SHORTCODE_INVALID_JSON error if incoming request data is invalid', async () => {
|
||||
test('should throw SHORTCODE_INVALID_REQUEST_JSON error if incoming request data is invalid', async () => {
|
||||
const result = await shortcodeService.createShortcode(
|
||||
'invalidRequest',
|
||||
'user_uid_1',
|
||||
null,
|
||||
user,
|
||||
);
|
||||
expect(result).toEqualLeft(SHORTCODE_INVALID_JSON);
|
||||
expect(result).toEqualLeft(SHORTCODE_INVALID_REQUEST_JSON);
|
||||
});
|
||||
|
||||
test('should successfully create a new shortcode with valid user uid', async () => {
|
||||
// generateUniqueShortCodeID --> getShortCode
|
||||
test('should throw SHORTCODE_INVALID_PROPERTIES_JSON error if incoming properties data is invalid', async () => {
|
||||
const result = await shortcodeService.createShortcode(
|
||||
'{}',
|
||||
'invalid_data',
|
||||
user,
|
||||
);
|
||||
expect(result).toEqualLeft(SHORTCODE_INVALID_PROPERTIES_JSON);
|
||||
});
|
||||
|
||||
test('should successfully create a new Embed with valid user uid', async () => {
|
||||
// generateUniqueShortCodeID --> getShortcode
|
||||
mockPrisma.shortcode.findFirstOrThrow.mockRejectedValueOnce(
|
||||
'NotFoundError',
|
||||
);
|
||||
mockPrisma.shortcode.create.mockResolvedValueOnce(shortCodeWithUser);
|
||||
mockPrisma.shortcode.create.mockResolvedValueOnce(mockEmbed);
|
||||
|
||||
const result = await shortcodeService.createShortcode('{}', 'user_uid_1');
|
||||
expect(result).toEqualRight({
|
||||
id: shortCodeWithUser.id,
|
||||
createdOn: shortCodeWithUser.createdOn,
|
||||
request: JSON.stringify(shortCodeWithUser.request),
|
||||
const result = await shortcodeService.createShortcode('{}', '{}', user);
|
||||
expect(result).toEqualRight(<Shortcode>{
|
||||
id: mockEmbed.id,
|
||||
createdOn: mockEmbed.createdOn,
|
||||
request: JSON.stringify(mockEmbed.request),
|
||||
properties: JSON.stringify(mockEmbed.properties),
|
||||
});
|
||||
});
|
||||
|
||||
test('should successfully create a new shortcode with null user uid', async () => {
|
||||
// generateUniqueShortCodeID --> getShortCode
|
||||
test('should successfully create a new ShortCode with valid user uid', async () => {
|
||||
// generateUniqueShortCodeID --> getShortcode
|
||||
mockPrisma.shortcode.findFirstOrThrow.mockRejectedValueOnce(
|
||||
'NotFoundError',
|
||||
);
|
||||
mockPrisma.shortcode.create.mockResolvedValueOnce(shortCodeWithUser);
|
||||
mockPrisma.shortcode.create.mockResolvedValueOnce(mockShortcode);
|
||||
|
||||
const result = await shortcodeService.createShortcode('{}', null);
|
||||
expect(result).toEqualRight({
|
||||
id: shortCodeWithUser.id,
|
||||
createdOn: shortCodeWithUser.createdOn,
|
||||
request: JSON.stringify(shortCodeWithOutUser.request),
|
||||
const result = await shortcodeService.createShortcode('{}', null, user);
|
||||
expect(result).toEqualRight(<Shortcode>{
|
||||
id: mockShortcode.id,
|
||||
createdOn: mockShortcode.createdOn,
|
||||
request: JSON.stringify(mockShortcode.request),
|
||||
properties: mockShortcode.properties,
|
||||
});
|
||||
});
|
||||
|
||||
test('should send pubsub message to `shortcode/{uid}/created` on successful creation of shortcode', async () => {
|
||||
// generateUniqueShortCodeID --> getShortCode
|
||||
test('should send pubsub message to `shortcode/{uid}/created` on successful creation of a Shortcode', async () => {
|
||||
// generateUniqueShortCodeID --> getShortcode
|
||||
mockPrisma.shortcode.findFirstOrThrow.mockRejectedValueOnce(
|
||||
'NotFoundError',
|
||||
);
|
||||
mockPrisma.shortcode.create.mockResolvedValueOnce(shortCodeWithUser);
|
||||
mockPrisma.shortcode.create.mockResolvedValueOnce(mockShortcode);
|
||||
|
||||
const result = await shortcodeService.createShortcode('{}', null, user);
|
||||
|
||||
const result = await shortcodeService.createShortcode('{}', 'user_uid_1');
|
||||
expect(mockPubSub.publish).toHaveBeenCalledWith(
|
||||
`shortcode/${shortCodeWithUser.creatorUid}/created`,
|
||||
{
|
||||
id: shortCodeWithUser.id,
|
||||
createdOn: shortCodeWithUser.createdOn,
|
||||
request: JSON.stringify(shortCodeWithUser.request),
|
||||
`shortcode/${mockShortcode.creatorUid}/created`,
|
||||
<Shortcode>{
|
||||
id: mockShortcode.id,
|
||||
createdOn: mockShortcode.createdOn,
|
||||
request: JSON.stringify(mockShortcode.request),
|
||||
properties: mockShortcode.properties,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test('should send pubsub message to `shortcode/{uid}/created` on successful creation of an Embed', async () => {
|
||||
// generateUniqueShortCodeID --> getShortcode
|
||||
mockPrisma.shortcode.findFirstOrThrow.mockRejectedValueOnce(
|
||||
'NotFoundError',
|
||||
);
|
||||
mockPrisma.shortcode.create.mockResolvedValueOnce(mockEmbed);
|
||||
|
||||
const result = await shortcodeService.createShortcode('{}', '{}', user);
|
||||
|
||||
expect(mockPubSub.publish).toHaveBeenCalledWith(
|
||||
`shortcode/${mockEmbed.creatorUid}/created`,
|
||||
<Shortcode>{
|
||||
id: mockEmbed.id,
|
||||
createdOn: mockEmbed.createdOn,
|
||||
request: JSON.stringify(mockEmbed.request),
|
||||
properties: JSON.stringify(mockEmbed.properties),
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('revokeShortCode', () => {
|
||||
test('should return true on successful deletion of shortcode with valid inputs', async () => {
|
||||
mockPrisma.shortcode.delete.mockResolvedValueOnce(shortCodeWithUser);
|
||||
test('should return true on successful deletion of Shortcode with valid inputs', async () => {
|
||||
mockPrisma.shortcode.delete.mockResolvedValueOnce(mockEmbed);
|
||||
|
||||
const result = await shortcodeService.revokeShortCode(
|
||||
shortCodeWithUser.id,
|
||||
shortCodeWithUser.creatorUid,
|
||||
mockEmbed.id,
|
||||
mockEmbed.creatorUid,
|
||||
);
|
||||
|
||||
expect(mockPrisma.shortcode.delete).toHaveBeenCalledWith({
|
||||
where: {
|
||||
creator_uid_shortcode_unique: {
|
||||
creatorUid: shortCodeWithUser.creatorUid,
|
||||
id: shortCodeWithUser.id,
|
||||
creatorUid: mockEmbed.creatorUid,
|
||||
id: mockEmbed.id,
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -257,52 +310,53 @@ describe('ShortcodeService', () => {
|
||||
expect(result).toEqualRight(true);
|
||||
});
|
||||
|
||||
test('should return SHORTCODE_NOT_FOUND error when shortcode is invalid and user uid is valid', async () => {
|
||||
test('should return SHORTCODE_NOT_FOUND error when Shortcode is invalid and user uid is valid', async () => {
|
||||
mockPrisma.shortcode.delete.mockRejectedValue('RecordNotFound');
|
||||
expect(
|
||||
shortcodeService.revokeShortCode('invalid', 'testuser'),
|
||||
).resolves.toEqualLeft(SHORTCODE_NOT_FOUND);
|
||||
});
|
||||
|
||||
test('should return SHORTCODE_NOT_FOUND error when shortcode is valid and user uid is invalid', async () => {
|
||||
test('should return SHORTCODE_NOT_FOUND error when Shortcode is valid and user uid is invalid', async () => {
|
||||
mockPrisma.shortcode.delete.mockRejectedValue('RecordNotFound');
|
||||
expect(
|
||||
shortcodeService.revokeShortCode('blablablabla', 'invalidUser'),
|
||||
).resolves.toEqualLeft(SHORTCODE_NOT_FOUND);
|
||||
});
|
||||
|
||||
test('should return SHORTCODE_NOT_FOUND error when both shortcode and user uid are invalid', async () => {
|
||||
test('should return SHORTCODE_NOT_FOUND error when both Shortcode and user uid are invalid', async () => {
|
||||
mockPrisma.shortcode.delete.mockRejectedValue('RecordNotFound');
|
||||
expect(
|
||||
shortcodeService.revokeShortCode('invalid', 'invalid'),
|
||||
).resolves.toEqualLeft(SHORTCODE_NOT_FOUND);
|
||||
});
|
||||
|
||||
test('should send pubsub message to `shortcode/{uid}/revoked` on successful deletion of shortcode', async () => {
|
||||
mockPrisma.shortcode.delete.mockResolvedValueOnce(shortCodeWithUser);
|
||||
test('should send pubsub message to `shortcode/{uid}/revoked` on successful deletion of Shortcode', async () => {
|
||||
mockPrisma.shortcode.delete.mockResolvedValueOnce(mockEmbed);
|
||||
|
||||
const result = await shortcodeService.revokeShortCode(
|
||||
shortCodeWithUser.id,
|
||||
shortCodeWithUser.creatorUid,
|
||||
mockEmbed.id,
|
||||
mockEmbed.creatorUid,
|
||||
);
|
||||
|
||||
expect(mockPubSub.publish).toHaveBeenCalledWith(
|
||||
`shortcode/${shortCodeWithUser.creatorUid}/revoked`,
|
||||
`shortcode/${mockEmbed.creatorUid}/revoked`,
|
||||
{
|
||||
id: shortCodeWithUser.id,
|
||||
createdOn: shortCodeWithUser.createdOn,
|
||||
request: JSON.stringify(shortCodeWithUser.request),
|
||||
id: mockEmbed.id,
|
||||
createdOn: mockEmbed.createdOn,
|
||||
request: JSON.stringify(mockEmbed.request),
|
||||
properties: JSON.stringify(mockEmbed.properties),
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteUserShortCodes', () => {
|
||||
test('should successfully delete all users shortcodes with valid user uid', async () => {
|
||||
test('should successfully delete all users Shortcodes with valid user uid', async () => {
|
||||
mockPrisma.shortcode.deleteMany.mockResolvedValueOnce({ count: 1 });
|
||||
|
||||
const result = await shortcodeService.deleteUserShortCodes(
|
||||
shortCodeWithUser.creatorUid,
|
||||
mockEmbed.creatorUid,
|
||||
);
|
||||
expect(result).toEqual(1);
|
||||
});
|
||||
@@ -311,9 +365,81 @@ describe('ShortcodeService', () => {
|
||||
mockPrisma.shortcode.deleteMany.mockResolvedValueOnce({ count: 0 });
|
||||
|
||||
const result = await shortcodeService.deleteUserShortCodes(
|
||||
shortCodeWithUser.creatorUid,
|
||||
mockEmbed.creatorUid,
|
||||
);
|
||||
expect(result).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateShortcode', () => {
|
||||
test('should return SHORTCODE_PROPERTIES_NOT_FOUND error when updatedProps in invalid', async () => {
|
||||
const result = await shortcodeService.updateShortcode(
|
||||
mockEmbed.id,
|
||||
user.uid,
|
||||
'',
|
||||
);
|
||||
expect(result).toEqualLeft(SHORTCODE_PROPERTIES_NOT_FOUND);
|
||||
});
|
||||
|
||||
test('should return SHORTCODE_PROPERTIES_NOT_FOUND error when updatedProps in invalid JSON format', async () => {
|
||||
const result = await shortcodeService.updateShortcode(
|
||||
mockEmbed.id,
|
||||
user.uid,
|
||||
'{kk',
|
||||
);
|
||||
expect(result).toEqualLeft(SHORTCODE_INVALID_PROPERTIES_JSON);
|
||||
});
|
||||
|
||||
test('should return SHORTCODE_NOT_FOUND error when Shortcode ID is invalid', async () => {
|
||||
mockPrisma.shortcode.update.mockRejectedValue('RecordNotFound');
|
||||
const result = await shortcodeService.updateShortcode(
|
||||
'invalidID',
|
||||
user.uid,
|
||||
'{}',
|
||||
);
|
||||
expect(result).toEqualLeft(SHORTCODE_NOT_FOUND);
|
||||
});
|
||||
|
||||
test('should successfully update a Shortcodes with valid inputs', async () => {
|
||||
mockPrisma.shortcode.update.mockResolvedValueOnce({
|
||||
...mockEmbed,
|
||||
properties: '{"foo":"bar"}',
|
||||
});
|
||||
|
||||
const result = await shortcodeService.updateShortcode(
|
||||
mockEmbed.id,
|
||||
user.uid,
|
||||
'{"foo":"bar"}',
|
||||
);
|
||||
expect(result).toEqualRight({
|
||||
id: mockEmbed.id,
|
||||
createdOn: mockEmbed.createdOn,
|
||||
request: JSON.stringify(mockEmbed.request),
|
||||
properties: JSON.stringify('{"foo":"bar"}'),
|
||||
});
|
||||
});
|
||||
|
||||
test('should send pubsub message to `shortcode/{uid}/updated` on successful Update of Shortcode', async () => {
|
||||
mockPrisma.shortcode.update.mockResolvedValueOnce({
|
||||
...mockEmbed,
|
||||
properties: '{"foo":"bar"}',
|
||||
});
|
||||
|
||||
const result = await shortcodeService.updateShortcode(
|
||||
mockEmbed.id,
|
||||
user.uid,
|
||||
'{"foo":"bar"}',
|
||||
);
|
||||
|
||||
expect(mockPubSub.publish).toHaveBeenCalledWith(
|
||||
`shortcode/${mockEmbed.creatorUid}/updated`,
|
||||
{
|
||||
id: mockEmbed.id,
|
||||
createdOn: mockEmbed.createdOn,
|
||||
request: JSON.stringify(mockEmbed.request),
|
||||
properties: JSON.stringify('{"foo":"bar"}'),
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,7 +4,13 @@ import * as O from 'fp-ts/Option';
|
||||
import * as TO from 'fp-ts/TaskOption';
|
||||
import * as E from 'fp-ts/Either';
|
||||
import { PrismaService } from 'src/prisma/prisma.service';
|
||||
import { SHORTCODE_INVALID_JSON, SHORTCODE_NOT_FOUND } from 'src/errors';
|
||||
import {
|
||||
SHORTCODE_INVALID_JSON,
|
||||
SHORTCODE_INVALID_PROPERTIES_JSON,
|
||||
SHORTCODE_INVALID_REQUEST_JSON,
|
||||
SHORTCODE_NOT_FOUND,
|
||||
SHORTCODE_PROPERTIES_NOT_FOUND,
|
||||
} from 'src/errors';
|
||||
import { UserDataHandler } from 'src/user/user.data.handler';
|
||||
import { Shortcode } from './shortcode.model';
|
||||
import { Shortcode as DBShortCode } from '@prisma/client';
|
||||
@@ -46,10 +52,14 @@ export class ShortcodeService implements UserDataHandler, OnModuleInit {
|
||||
* @param shortcodeInfo Prisma Shortcode type
|
||||
* @returns GQL Shortcode
|
||||
*/
|
||||
private returnShortCode(shortcodeInfo: DBShortCode): Shortcode {
|
||||
private cast(shortcodeInfo: DBShortCode): Shortcode {
|
||||
return <Shortcode>{
|
||||
id: shortcodeInfo.id,
|
||||
request: JSON.stringify(shortcodeInfo.request),
|
||||
properties:
|
||||
shortcodeInfo.properties != null
|
||||
? JSON.stringify(shortcodeInfo.properties)
|
||||
: null,
|
||||
createdOn: shortcodeInfo.createdOn,
|
||||
};
|
||||
}
|
||||
@@ -94,7 +104,7 @@ export class ShortcodeService implements UserDataHandler, OnModuleInit {
|
||||
const shortcodeInfo = await this.prisma.shortcode.findFirstOrThrow({
|
||||
where: { id: shortcode },
|
||||
});
|
||||
return E.right(this.returnShortCode(shortcodeInfo));
|
||||
return E.right(this.cast(shortcodeInfo));
|
||||
} catch (error) {
|
||||
return E.left(SHORTCODE_NOT_FOUND);
|
||||
}
|
||||
@@ -104,14 +114,21 @@ export class ShortcodeService implements UserDataHandler, OnModuleInit {
|
||||
* Create a new ShortCode
|
||||
*
|
||||
* @param request JSON string of request details
|
||||
* @param userUID user UID, if present
|
||||
* @param userInfo user UI
|
||||
* @param properties JSON string of embed properties, if present
|
||||
* @returns Either of ShortCode or error
|
||||
*/
|
||||
async createShortcode(request: string, userUID: string | null) {
|
||||
const shortcodeData = stringToJson(request);
|
||||
if (E.isLeft(shortcodeData)) return E.left(SHORTCODE_INVALID_JSON);
|
||||
async createShortcode(
|
||||
request: string,
|
||||
properties: string | null = null,
|
||||
userInfo: AuthUser,
|
||||
) {
|
||||
const requestData = stringToJson(request);
|
||||
if (E.isLeft(requestData)) return E.left(SHORTCODE_INVALID_REQUEST_JSON);
|
||||
|
||||
const user = await this.userService.findUserById(userUID);
|
||||
const parsedProperties = stringToJson(properties);
|
||||
if (E.isLeft(parsedProperties))
|
||||
return E.left(SHORTCODE_INVALID_PROPERTIES_JSON);
|
||||
|
||||
const generatedShortCode = await this.generateUniqueShortCodeID();
|
||||
if (E.isLeft(generatedShortCode)) return E.left(generatedShortCode.left);
|
||||
@@ -119,8 +136,9 @@ export class ShortcodeService implements UserDataHandler, OnModuleInit {
|
||||
const createdShortCode = await this.prisma.shortcode.create({
|
||||
data: {
|
||||
id: generatedShortCode.right,
|
||||
request: shortcodeData.right,
|
||||
creatorUid: O.isNone(user) ? null : user.value.uid,
|
||||
request: requestData.right,
|
||||
properties: parsedProperties.right ?? undefined,
|
||||
creatorUid: userInfo.uid,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -128,11 +146,11 @@ export class ShortcodeService implements UserDataHandler, OnModuleInit {
|
||||
if (createdShortCode.creatorUid) {
|
||||
this.pubsub.publish(
|
||||
`shortcode/${createdShortCode.creatorUid}/created`,
|
||||
this.returnShortCode(createdShortCode),
|
||||
this.cast(createdShortCode),
|
||||
);
|
||||
}
|
||||
|
||||
return E.right(this.returnShortCode(createdShortCode));
|
||||
return E.right(this.cast(createdShortCode));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,7 +174,7 @@ export class ShortcodeService implements UserDataHandler, OnModuleInit {
|
||||
});
|
||||
|
||||
const fetchedShortCodes: Shortcode[] = shortCodes.map((code) =>
|
||||
this.returnShortCode(code),
|
||||
this.cast(code),
|
||||
);
|
||||
|
||||
return fetchedShortCodes;
|
||||
@@ -182,7 +200,7 @@ export class ShortcodeService implements UserDataHandler, OnModuleInit {
|
||||
|
||||
this.pubsub.publish(
|
||||
`shortcode/${deletedShortCodes.creatorUid}/revoked`,
|
||||
this.returnShortCode(deletedShortCodes),
|
||||
this.cast(deletedShortCodes),
|
||||
);
|
||||
|
||||
return E.right(true);
|
||||
@@ -205,4 +223,45 @@ export class ShortcodeService implements UserDataHandler, OnModuleInit {
|
||||
|
||||
return deletedShortCodes.count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a created Shortcode
|
||||
* @param shortcodeID Shortcode ID
|
||||
* @param uid User Uid
|
||||
* @returns Updated Shortcode
|
||||
*/
|
||||
async updateShortcode(
|
||||
shortcodeID: string,
|
||||
uid: string,
|
||||
updatedProps: string,
|
||||
) {
|
||||
if (!updatedProps) return E.left(SHORTCODE_PROPERTIES_NOT_FOUND);
|
||||
|
||||
const parsedProperties = stringToJson(updatedProps);
|
||||
if (E.isLeft(parsedProperties) || !parsedProperties.right)
|
||||
return E.left(SHORTCODE_INVALID_PROPERTIES_JSON);
|
||||
|
||||
try {
|
||||
const updatedShortcode = await this.prisma.shortcode.update({
|
||||
where: {
|
||||
creator_uid_shortcode_unique: {
|
||||
creatorUid: uid,
|
||||
id: shortcodeID,
|
||||
},
|
||||
},
|
||||
data: {
|
||||
properties: parsedProperties.right,
|
||||
},
|
||||
});
|
||||
|
||||
this.pubsub.publish(
|
||||
`shortcode/${updatedShortcode.creatorUid}/updated`,
|
||||
this.cast(updatedShortcode),
|
||||
);
|
||||
|
||||
return E.right(this.cast(updatedShortcode));
|
||||
} catch (error) {
|
||||
return E.left(SHORTCODE_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user