feat(backend): add the ability to disable tracking request history (#4594)
HSB-505
This commit is contained in:
parent
12f4849061
commit
e30a6c9db5
18 changed files with 301 additions and 20 deletions
|
|
@ -12,6 +12,7 @@ import { TeamRequestModule } from '../team-request/team-request.module';
|
|||
import { InfraResolver } from './infra.resolver';
|
||||
import { ShortcodeModule } from 'src/shortcode/shortcode.module';
|
||||
import { InfraConfigModule } from 'src/infra-config/infra-config.module';
|
||||
import { UserHistoryModule } from 'src/user-history/user-history.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
|
|
@ -25,6 +26,7 @@ import { InfraConfigModule } from 'src/infra-config/infra-config.module';
|
|||
TeamRequestModule,
|
||||
ShortcodeModule,
|
||||
InfraConfigModule,
|
||||
UserHistoryModule,
|
||||
],
|
||||
providers: [InfraResolver, AdminResolver, AdminService],
|
||||
exports: [AdminService],
|
||||
|
|
|
|||
|
|
@ -117,9 +117,8 @@ export class AdminResolver {
|
|||
})
|
||||
userUIDs: string[],
|
||||
): Promise<UserDeletionResult[]> {
|
||||
const deletionResults = await this.adminService.removeUserAccounts(
|
||||
userUIDs,
|
||||
);
|
||||
const deletionResults =
|
||||
await this.adminService.removeUserAccounts(userUIDs);
|
||||
if (E.isLeft(deletionResults)) throwErr(deletionResults.left);
|
||||
return deletionResults.right;
|
||||
}
|
||||
|
|
@ -360,6 +359,16 @@ export class AdminResolver {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Mutation(() => Boolean, {
|
||||
description: 'Revoke all User History',
|
||||
})
|
||||
@UseGuards(GqlAuthGuard, GqlAdminGuard)
|
||||
async revokeAllUserHistoryByAdmin(): Promise<boolean> {
|
||||
const isDeleted = await this.adminService.deleteAllUserHistory();
|
||||
if (E.isLeft(isDeleted)) throwErr(isDeleted.left);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Subscriptions */
|
||||
|
||||
@Subscription(() => InvitedUser, {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import { ShortcodeService } from 'src/shortcode/shortcode.service';
|
|||
import { ConfigService } from '@nestjs/config';
|
||||
import { OffsetPaginationArgs } from 'src/types/input-types.args';
|
||||
import * as E from 'fp-ts/Either';
|
||||
import { UserHistoryService } from 'src/user-history/user-history.service';
|
||||
|
||||
const mockPrisma = mockDeep<PrismaService>();
|
||||
const mockPubSub = mockDeep<PubSubService>();
|
||||
|
|
@ -34,6 +35,7 @@ const mockTeamCollectionService = mockDeep<TeamCollectionService>();
|
|||
const mockMailerService = mockDeep<MailerService>();
|
||||
const mockShortcodeService = mockDeep<ShortcodeService>();
|
||||
const mockConfigService = mockDeep<ConfigService>();
|
||||
const mockUserHistoryService = mockDeep<UserHistoryService>();
|
||||
|
||||
const adminService = new AdminService(
|
||||
mockUserService,
|
||||
|
|
@ -47,6 +49,7 @@ const adminService = new AdminService(
|
|||
mockMailerService,
|
||||
mockShortcodeService,
|
||||
mockConfigService,
|
||||
mockUserHistoryService,
|
||||
);
|
||||
|
||||
const invitedUsers: InvitedUsers[] = [
|
||||
|
|
@ -292,4 +295,15 @@ describe('AdminService', () => {
|
|||
expect(result).toEqual(10);
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteAllUserHistory', () => {
|
||||
test('should resolve right and delete all user history', async () => {
|
||||
mockUserHistoryService.deleteAllHistories.mockResolvedValueOnce(
|
||||
E.right(true),
|
||||
);
|
||||
|
||||
const result = await adminService.deleteAllUserHistory();
|
||||
expect(result).toEqualRight(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import { ShortcodeService } from 'src/shortcode/shortcode.service';
|
|||
import { ConfigService } from '@nestjs/config';
|
||||
import { OffsetPaginationArgs } from 'src/types/input-types.args';
|
||||
import { UserDeletionResult } from 'src/user/user.model';
|
||||
import { UserHistoryService } from 'src/user-history/user-history.service';
|
||||
|
||||
@Injectable()
|
||||
export class AdminService {
|
||||
|
|
@ -46,6 +47,7 @@ export class AdminService {
|
|||
private readonly mailerService: MailerService,
|
||||
private readonly shortcodeService: ShortcodeService,
|
||||
private readonly configService: ConfigService,
|
||||
private readonly userHistoryService: UserHistoryService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
|
|
@ -650,4 +652,15 @@ export class AdminService {
|
|||
if (E.isLeft(result)) return E.left(result.left);
|
||||
return E.right(result.right);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all user history
|
||||
* @returns Boolean on successful deletion
|
||||
*/
|
||||
async deleteAllUserHistory() {
|
||||
const result = await this.userHistoryService.deleteAllHistories();
|
||||
|
||||
if (E.isLeft(result)) return E.left(result.left);
|
||||
return E.right(result.right);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -214,9 +214,8 @@ export class InfraResolver {
|
|||
})
|
||||
teamID: string,
|
||||
) {
|
||||
const invitations = await this.adminService.pendingInvitationCountInTeam(
|
||||
teamID,
|
||||
);
|
||||
const invitations =
|
||||
await this.adminService.pendingInvitationCountInTeam(teamID);
|
||||
return invitations;
|
||||
}
|
||||
|
||||
|
|
@ -352,9 +351,8 @@ export class InfraResolver {
|
|||
})
|
||||
providerInfo: EnableAndDisableSSOArgs[],
|
||||
) {
|
||||
const isUpdated = await this.infraConfigService.enableAndDisableSSO(
|
||||
providerInfo,
|
||||
);
|
||||
const isUpdated =
|
||||
await this.infraConfigService.enableAndDisableSSO(providerInfo);
|
||||
if (E.isLeft(isUpdated)) throwErr(isUpdated.left);
|
||||
|
||||
return true;
|
||||
|
|
@ -372,7 +370,26 @@ export class InfraResolver {
|
|||
})
|
||||
status: ServiceStatus,
|
||||
) {
|
||||
const isUpdated = await this.infraConfigService.enableAndDisableSMTP(
|
||||
const isUpdated =
|
||||
await this.infraConfigService.enableAndDisableSMTP(status);
|
||||
if (E.isLeft(isUpdated)) throwErr(isUpdated.left);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Mutation(() => Boolean, {
|
||||
description: 'Enable or Disable User History Storing in DB',
|
||||
})
|
||||
@UseGuards(GqlAuthGuard, GqlAdminGuard)
|
||||
async toggleUserHistoryStore(
|
||||
@Args({
|
||||
name: 'status',
|
||||
type: () => ServiceStatus,
|
||||
description: 'Toggle User History Store',
|
||||
})
|
||||
status: ServiceStatus,
|
||||
) {
|
||||
const isUpdated = await this.infraConfigService.toggleServiceStatus(
|
||||
InfraConfigEnum.USER_HISTORY_STORE_ENABLED,
|
||||
status,
|
||||
);
|
||||
if (E.isLeft(isUpdated)) throwErr(isUpdated.left);
|
||||
|
|
|
|||
|
|
@ -492,7 +492,19 @@ export const USER_ENVIRONMENT_INVALID_ENVIRONMENT_NAME =
|
|||
*/
|
||||
export const USER_HISTORY_NOT_FOUND = 'user_history/history_not_found' as const;
|
||||
|
||||
/*
|
||||
/**
|
||||
* User history deletion failed
|
||||
* (UserHistoryService)
|
||||
*/
|
||||
export const USER_HISTORY_DELETION_FAILED =
|
||||
'user_history/deletion_failed' as const;
|
||||
|
||||
/**
|
||||
* User history feature flag is disabled
|
||||
* (UserHistoryService)
|
||||
*/
|
||||
export const USER_HISTORY_FEATURE_FLAG_DISABLED =
|
||||
'user_history/feature_flag_disabled';
|
||||
|
||||
/**
|
||||
* Invalid Request Type in History
|
||||
|
|
|
|||
|
|
@ -262,6 +262,12 @@ export async function getDefaultInfraConfigs(): Promise<DefaultInfraConfig[]> {
|
|||
lastSyncedEnvFileValue: null,
|
||||
isEncrypted: false,
|
||||
},
|
||||
{
|
||||
name: InfraConfigEnum.USER_HISTORY_STORE_ENABLED,
|
||||
value: 'true',
|
||||
lastSyncedEnvFileValue: null,
|
||||
isEncrypted: false,
|
||||
},
|
||||
];
|
||||
|
||||
return infraConfigDefaultObjs;
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@ import { InfraConfigService } from './infra-config.service';
|
|||
import { PrismaModule } from 'src/prisma/prisma.module';
|
||||
import { SiteController } from './infra-config.controller';
|
||||
import { InfraConfigResolver } from './infra-config.resolver';
|
||||
import { PubSubModule } from 'src/pubsub/pubsub.module';
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
imports: [PrismaModule, PubSubModule],
|
||||
providers: [InfraConfigResolver, InfraConfigService],
|
||||
exports: [InfraConfigService],
|
||||
controllers: [SiteController],
|
||||
|
|
|
|||
|
|
@ -1,14 +1,24 @@
|
|||
import { UseGuards } from '@nestjs/common';
|
||||
import { Query, Resolver } from '@nestjs/graphql';
|
||||
import { Args, Query, Resolver, Subscription } from '@nestjs/graphql';
|
||||
import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard';
|
||||
import { InfraConfig } from './infra-config.model';
|
||||
import { InfraConfigService } from './infra-config.service';
|
||||
import { GqlAuthGuard } from 'src/guards/gql-auth.guard';
|
||||
import { SkipThrottle } from '@nestjs/throttler';
|
||||
import { PubSubService } from 'src/pubsub/pubsub.service';
|
||||
import { InfraConfigEnum } from 'src/types/InfraConfig';
|
||||
import * as E from 'fp-ts/Either';
|
||||
import { throwErr } from 'src/utils';
|
||||
|
||||
@UseGuards(GqlThrottlerGuard)
|
||||
@Resolver(() => InfraConfig)
|
||||
export class InfraConfigResolver {
|
||||
constructor(private infraConfigService: InfraConfigService) {}
|
||||
constructor(
|
||||
private infraConfigService: InfraConfigService,
|
||||
private pubsub: PubSubService,
|
||||
) {}
|
||||
|
||||
/* Query */
|
||||
|
||||
@Query(() => Boolean, {
|
||||
description: 'Check if the SMTP is enabled or not',
|
||||
|
|
@ -17,4 +27,33 @@ export class InfraConfigResolver {
|
|||
isSMTPEnabled() {
|
||||
return this.infraConfigService.isSMTPEnabled();
|
||||
}
|
||||
|
||||
@Query(() => InfraConfig, {
|
||||
description: 'Check if user history is enabled or not',
|
||||
})
|
||||
@UseGuards(GqlAuthGuard)
|
||||
async isUserHistoryEnabled() {
|
||||
const isEnabled = await this.infraConfigService.isUserHistoryEnabled();
|
||||
if (E.isLeft(isEnabled)) throwErr(isEnabled.left);
|
||||
return isEnabled.right;
|
||||
}
|
||||
|
||||
/* Subscriptions */
|
||||
|
||||
@Subscription(() => String, {
|
||||
description: 'Subscription for infra config update',
|
||||
resolve: (value) => value,
|
||||
})
|
||||
@SkipThrottle()
|
||||
@UseGuards(GqlAuthGuard)
|
||||
infraConfigUpdate(
|
||||
@Args({
|
||||
name: 'configName',
|
||||
description: 'Infra config key',
|
||||
type: () => InfraConfigEnum,
|
||||
})
|
||||
configName: string,
|
||||
) {
|
||||
return this.pubsub.asyncIterator(`infra_config/${configName}/updated`);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,15 +11,20 @@ import { ConfigService } from '@nestjs/config';
|
|||
import * as helper from './helper';
|
||||
import { InfraConfig as dbInfraConfig } from '@prisma/client';
|
||||
import { InfraConfig } from './infra-config.model';
|
||||
import { PubSubService } from 'src/pubsub/pubsub.service';
|
||||
import { ServiceStatus } from './helper';
|
||||
import * as E from 'fp-ts/Either';
|
||||
|
||||
const mockPrisma = mockDeep<PrismaService>();
|
||||
const mockConfigService = mockDeep<ConfigService>();
|
||||
const mockPubsub = mockDeep<PubSubService>();
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
const infraConfigService = new InfraConfigService(
|
||||
mockPrisma,
|
||||
mockConfigService,
|
||||
mockPubsub,
|
||||
);
|
||||
|
||||
const INITIALIZED_DATE_CONST = new Date();
|
||||
|
|
@ -243,4 +248,59 @@ describe('InfraConfigService', () => {
|
|||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toggleServiceStatus', () => {
|
||||
it('should toggle the service status', async () => {
|
||||
const configName = infraConfigs[0].name;
|
||||
const configStatus = ServiceStatus.DISABLE;
|
||||
|
||||
jest
|
||||
.spyOn(infraConfigService, 'update')
|
||||
.mockResolvedValueOnce(
|
||||
E.right({ name: configName, value: configStatus }),
|
||||
);
|
||||
|
||||
expect(
|
||||
await infraConfigService.toggleServiceStatus(configName, configStatus),
|
||||
).toEqualRight(true);
|
||||
});
|
||||
it('should publish the updated config value', async () => {
|
||||
const configName = infraConfigs[0].name;
|
||||
const configStatus = ServiceStatus.DISABLE;
|
||||
|
||||
jest
|
||||
.spyOn(infraConfigService, 'update')
|
||||
.mockResolvedValueOnce(
|
||||
E.right({ name: configName, value: configStatus }),
|
||||
);
|
||||
|
||||
await infraConfigService.toggleServiceStatus(configName, configStatus);
|
||||
|
||||
expect(mockPubsub.publish).toHaveBeenCalledTimes(1);
|
||||
expect(mockPubsub.publish).toHaveBeenCalledWith(
|
||||
'infra_config/GOOGLE_CLIENT_ID/updated',
|
||||
configStatus,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isUserHistoryEnabled', () => {
|
||||
it('should return true if the user history is enabled', async () => {
|
||||
const response = {
|
||||
name: InfraConfigEnum.USER_HISTORY_STORE_ENABLED,
|
||||
value: ServiceStatus.ENABLE,
|
||||
};
|
||||
|
||||
jest.spyOn(infraConfigService, 'get').mockResolvedValueOnce(
|
||||
E.right({
|
||||
name: InfraConfigEnum.USER_HISTORY_STORE_ENABLED,
|
||||
value: ServiceStatus.ENABLE,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(await infraConfigService.isUserHistoryEnabled()).toEqualRight(
|
||||
response,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -33,12 +33,14 @@ import {
|
|||
} from './helper';
|
||||
import { EnableAndDisableSSOArgs, InfraConfigArgs } from './input-args';
|
||||
import { AuthProvider } from 'src/auth/helper';
|
||||
import { PubSubService } from 'src/pubsub/pubsub.service';
|
||||
|
||||
@Injectable()
|
||||
export class InfraConfigService implements OnModuleInit {
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly configService: ConfigService,
|
||||
private readonly pubsub: PubSubService,
|
||||
) {}
|
||||
|
||||
// Following fields are not updatable by `infraConfigs` Mutation. Use dedicated mutations for these fields instead.
|
||||
|
|
@ -48,6 +50,7 @@ export class InfraConfigService implements OnModuleInit {
|
|||
InfraConfigEnum.ANALYTICS_USER_ID,
|
||||
InfraConfigEnum.IS_FIRST_TIME_INFRA_SETUP,
|
||||
InfraConfigEnum.MAILER_SMTP_ENABLE,
|
||||
InfraConfigEnum.USER_HISTORY_STORE_ENABLED,
|
||||
];
|
||||
// Following fields can not be fetched by `infraConfigs` Query. Use dedicated queries for these fields instead.
|
||||
EXCLUDE_FROM_FETCH_CONFIGS = [
|
||||
|
|
@ -132,6 +135,17 @@ export class InfraConfigService implements OnModuleInit {
|
|||
* @returns InfraConfig model
|
||||
*/
|
||||
cast(dbInfraConfig: DBInfraConfig) {
|
||||
switch (dbInfraConfig.name) {
|
||||
case InfraConfigEnum.USER_HISTORY_STORE_ENABLED:
|
||||
dbInfraConfig.value =
|
||||
dbInfraConfig.value === 'true'
|
||||
? ServiceStatus.ENABLE
|
||||
: ServiceStatus.DISABLE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
const plainValue = dbInfraConfig.isEncrypted
|
||||
? decrypt(dbInfraConfig.value)
|
||||
: dbInfraConfig.value;
|
||||
|
|
@ -342,6 +356,11 @@ export class InfraConfigService implements OnModuleInit {
|
|||
);
|
||||
if (E.isLeft(isUpdated)) return E.left(isUpdated.left);
|
||||
|
||||
this.pubsub.publish(
|
||||
`infra_config/${configName}/updated`,
|
||||
isUpdated.right.value,
|
||||
);
|
||||
|
||||
return E.right(true);
|
||||
}
|
||||
|
||||
|
|
@ -453,6 +472,19 @@ export class InfraConfigService implements OnModuleInit {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user history is enabled or not
|
||||
* @returns InfraConfig model
|
||||
*/
|
||||
async isUserHistoryEnabled() {
|
||||
const infraConfig = await this.get(
|
||||
InfraConfigEnum.USER_HISTORY_STORE_ENABLED,
|
||||
);
|
||||
|
||||
if (E.isLeft(infraConfig)) return E.left(infraConfig.left);
|
||||
return E.right(infraConfig.right);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all the InfraConfigs to their default values (from .env)
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -43,9 +43,10 @@ export type TopicDef = {
|
|||
topic: `user_request/${string}/${'created' | 'updated' | 'deleted'}`
|
||||
]: UserRequest;
|
||||
[topic: `user_request/${string}/${'moved'}`]: UserRequestReorderData;
|
||||
[
|
||||
topic: `user_history/${string}/${'created' | 'updated' | 'deleted'}`
|
||||
]: UserHistory;
|
||||
[topic: `user_history/${string}/${'created' | 'updated' | 'deleted'}`]:
|
||||
| UserHistory
|
||||
| boolean;
|
||||
[topic: `user_history/${string}/deleted_many`]: UserHistoryDeletedManyData;
|
||||
[
|
||||
topic: `user_coll/${string}/${'created' | 'updated' | 'moved'}`
|
||||
]: UserCollection;
|
||||
|
|
@ -63,7 +64,6 @@ export type TopicDef = {
|
|||
[topic: `team_coll/${string}/${'coll_removed'}`]: string;
|
||||
[topic: `team_coll/${string}/${'coll_moved'}`]: TeamCollection;
|
||||
[topic: `team_coll/${string}/${'coll_order_updated'}`]: CollectionReorderData;
|
||||
[topic: `user_history/${string}/deleted_many`]: UserHistoryDeletedManyData;
|
||||
[
|
||||
topic: `team_req/${string}/${'req_created' | 'req_updated' | 'req_moved'}`
|
||||
]: TeamRequest;
|
||||
|
|
@ -74,4 +74,5 @@ export type TopicDef = {
|
|||
[
|
||||
topic: `shortcode/${string}/${'created' | 'revoked' | 'updated'}`
|
||||
]: Shortcode;
|
||||
[topic: `infra_config/${string}/${'updated'}`]: string;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -32,4 +32,6 @@ export enum InfraConfigEnum {
|
|||
ALLOW_ANALYTICS_COLLECTION = 'ALLOW_ANALYTICS_COLLECTION',
|
||||
ANALYTICS_USER_ID = 'ANALYTICS_USER_ID',
|
||||
IS_FIRST_TIME_INFRA_SETUP = 'IS_FIRST_TIME_INFRA_SETUP',
|
||||
|
||||
USER_HISTORY_STORE_ENABLED = 'USER_HISTORY_STORE_ENABLED',
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
|
||||
import { USER_HISTORY_FEATURE_FLAG_DISABLED } from 'src/errors';
|
||||
import { InfraConfigService } from 'src/infra-config/infra-config.service';
|
||||
import { throwErr } from 'src/utils';
|
||||
import * as E from 'fp-ts/Either';
|
||||
import { ServiceStatus } from 'src/infra-config/helper';
|
||||
|
||||
@Injectable()
|
||||
export class UserHistoryFeatureFlagGuard implements CanActivate {
|
||||
constructor(private readonly infraConfigService: InfraConfigService) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const isEnabled = await this.infraConfigService.isUserHistoryEnabled();
|
||||
if (E.isLeft(isEnabled)) throwErr(isEnabled.left);
|
||||
|
||||
if (isEnabled.right.value !== ServiceStatus.ENABLE)
|
||||
throwErr(USER_HISTORY_FEATURE_FLAG_DISABLED);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -5,9 +5,10 @@ import { UserModule } from '../user/user.module';
|
|||
import { UserHistoryUserResolver } from './user.resolver';
|
||||
import { UserHistoryResolver } from './user-history.resolver';
|
||||
import { UserHistoryService } from './user-history.service';
|
||||
import { InfraConfigModule } from 'src/infra-config/infra-config.module';
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule, PubSubModule, UserModule],
|
||||
imports: [PrismaModule, PubSubModule, UserModule, InfraConfigModule],
|
||||
providers: [UserHistoryResolver, UserHistoryService, UserHistoryUserResolver],
|
||||
exports: [UserHistoryService],
|
||||
})
|
||||
|
|
|
|||
|
|
@ -11,8 +11,9 @@ import { throwErr } from '../utils';
|
|||
import * as E from 'fp-ts/Either';
|
||||
import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard';
|
||||
import { SkipThrottle } from '@nestjs/throttler';
|
||||
import { UserHistoryFeatureFlagGuard } from './user-history-feature-flag.guard';
|
||||
|
||||
@UseGuards(GqlThrottlerGuard)
|
||||
@UseGuards(GqlThrottlerGuard, UserHistoryFeatureFlagGuard)
|
||||
@Resolver()
|
||||
export class UserHistoryResolver {
|
||||
constructor(
|
||||
|
|
@ -156,4 +157,14 @@ export class UserHistoryResolver {
|
|||
userHistoryDeletedMany(@GqlUser() user: User) {
|
||||
return this.pubsub.asyncIterator(`user_history/${user.uid}/deleted_many`);
|
||||
}
|
||||
|
||||
@Subscription(() => Boolean, {
|
||||
description: 'Listen for All User History deleted',
|
||||
resolve: (value) => value,
|
||||
})
|
||||
@SkipThrottle()
|
||||
@UseGuards(GqlAuthGuard)
|
||||
userHistoryAllDeleted() {
|
||||
return this.pubsub.asyncIterator(`user_history/all/deleted`);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -493,6 +493,30 @@ describe('UserHistoryService', () => {
|
|||
);
|
||||
});
|
||||
});
|
||||
describe('deleteAllHistories', () => {
|
||||
test('Should resolve right and delete all user history', async () => {
|
||||
mockPrisma.userHistory.deleteMany.mockResolvedValueOnce({
|
||||
count: 2,
|
||||
});
|
||||
|
||||
return expect(await userHistoryService.deleteAllHistories()).toEqualRight(
|
||||
true,
|
||||
);
|
||||
});
|
||||
test('Should publish all user history delete event', async () => {
|
||||
mockPrisma.userHistory.deleteMany.mockResolvedValueOnce({
|
||||
count: 2,
|
||||
});
|
||||
|
||||
await userHistoryService.deleteAllHistories();
|
||||
|
||||
expect(mockPubSub.publish).toHaveBeenCalledTimes(1);
|
||||
return expect(mockPubSub.publish).toHaveBeenCalledWith(
|
||||
`user_history/all/deleted`,
|
||||
true,
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('validateReqType', () => {
|
||||
test('Should resolve right when a valid REST ReqType is provided', async () => {
|
||||
return expect(userHistoryService.validateReqType('REST')).toEqualRight(
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { ReqType } from 'src/types/RequestTypes';
|
|||
import * as E from 'fp-ts/Either';
|
||||
import * as O from 'fp-ts/Option';
|
||||
import {
|
||||
USER_HISTORY_DELETION_FAILED,
|
||||
USER_HISTORY_INVALID_REQ_TYPE,
|
||||
USER_HISTORY_NOT_FOUND,
|
||||
} from '../errors';
|
||||
|
|
@ -188,6 +189,21 @@ export class UserHistoryService {
|
|||
return E.right(deletionInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all user history from DB
|
||||
* @returns a boolean
|
||||
*/
|
||||
async deleteAllHistories() {
|
||||
try {
|
||||
await this.prisma.userHistory.deleteMany();
|
||||
} catch (error) {
|
||||
return E.left(USER_HISTORY_DELETION_FAILED);
|
||||
}
|
||||
|
||||
this.pubsub.publish('user_history/all/deleted', true);
|
||||
return E.right(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a user history based on history ID.
|
||||
* @param id User History ID
|
||||
|
|
|
|||
Loading…
Reference in a new issue