mirror of https://github.com/ghostfolio/ghostfolio
Browse Source
* Fix portfolio snapshot cache write and initialization retry limit * Update changelogpull/7501/head
committed by
GitHub
28 changed files with 146 additions and 11 deletions
@ -0,0 +1,7 @@ |
|||||
|
export class PortfolioSnapshotComputationError extends Error { |
||||
|
public constructor(message: string) { |
||||
|
super(message); |
||||
|
|
||||
|
this.name = 'PortfolioSnapshotComputationError'; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,26 @@ |
|||||
|
import { PortfolioSnapshotComputationError } from '@ghostfolio/api/app/portfolio/errors/portfolio-snapshot-computation.error'; |
||||
|
|
||||
|
import { ArgumentsHost, Catch, ExceptionFilter, Logger } from '@nestjs/common'; |
||||
|
import { Response } from 'express'; |
||||
|
import { getReasonPhrase, StatusCodes } from 'http-status-codes'; |
||||
|
|
||||
|
@Catch(PortfolioSnapshotComputationError) |
||||
|
export class PortfolioSnapshotComputationExceptionFilter implements ExceptionFilter { |
||||
|
private readonly logger = new Logger( |
||||
|
PortfolioSnapshotComputationExceptionFilter.name |
||||
|
); |
||||
|
|
||||
|
public catch( |
||||
|
exception: PortfolioSnapshotComputationError, |
||||
|
host: ArgumentsHost |
||||
|
) { |
||||
|
this.logger.error(exception.message); |
||||
|
|
||||
|
const response = host.switchToHttp().getResponse<Response>(); |
||||
|
|
||||
|
response.status(StatusCodes.SERVICE_UNAVAILABLE).json({ |
||||
|
message: getReasonPhrase(StatusCodes.SERVICE_UNAVAILABLE), |
||||
|
statusCode: StatusCodes.SERVICE_UNAVAILABLE |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -1,32 +1,47 @@ |
|||||
|
import { PortfolioSnapshotValue } from '@ghostfolio/api/app/portfolio/interfaces/snapshot-value.interface'; |
||||
|
import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; |
||||
|
|
||||
import type { Job, JobId, JobOptions } from 'bull'; |
import type { Job, JobId, JobOptions } from 'bull'; |
||||
|
import ms from 'ms'; |
||||
import { setTimeout } from 'timers/promises'; |
import { setTimeout } from 'timers/promises'; |
||||
|
|
||||
import { PortfolioSnapshotQueueJob } from './interfaces/portfolio-snapshot-queue-job.interface'; |
import { PortfolioSnapshotQueueJob } from './interfaces/portfolio-snapshot-queue-job.interface'; |
||||
|
|
||||
export const PortfolioSnapshotServiceMock = { |
export const PortfolioSnapshotServiceMock = { |
||||
addJobToQueue({ |
addJobToQueue: ({ |
||||
opts |
opts |
||||
}: { |
}: { |
||||
data: PortfolioSnapshotQueueJob; |
data: PortfolioSnapshotQueueJob; |
||||
name: string; |
name: string; |
||||
opts?: JobOptions; |
opts?: JobOptions; |
||||
}): Promise<Job> { |
}): Promise<Job> => { |
||||
const mockJob: Partial<Job> = { |
const mockJob: Partial<Job> = { |
||||
finished: async () => { |
finished: async () => { |
||||
await setTimeout(100); |
await setTimeout(100); |
||||
|
|
||||
return Promise.resolve(); |
// Mimic the processor which caches the computed portfolio snapshot
|
||||
|
// under the job id
|
||||
|
await RedisCacheServiceMock.set( |
||||
|
opts?.jobId as string, |
||||
|
JSON.stringify({ |
||||
|
expiration: Date.now() + ms('1 minute'), |
||||
|
portfolioSnapshot: {} |
||||
|
} as unknown as PortfolioSnapshotValue) |
||||
|
); |
||||
} |
} |
||||
}; |
}; |
||||
|
|
||||
this.jobsStore.set(opts?.jobId, mockJob); |
PortfolioSnapshotServiceMock.jobsStore.set(opts?.jobId, mockJob); |
||||
|
|
||||
return Promise.resolve(mockJob as Job); |
return Promise.resolve(mockJob as Job); |
||||
}, |
}, |
||||
getJob(jobId: JobId): Promise<Job> { |
getJob: (jobId: JobId): Promise<Job> => { |
||||
const job = this.jobsStore.get(jobId); |
const job = PortfolioSnapshotServiceMock.jobsStore.get(jobId); |
||||
|
|
||||
return Promise.resolve(job as Job); |
return Promise.resolve(job as Job); |
||||
}, |
}, |
||||
jobsStore: new Map<JobId, Partial<Job>>() |
jobsStore: new Map<JobId, Partial<Job>>(), |
||||
|
reset: () => { |
||||
|
PortfolioSnapshotServiceMock.jobsStore.clear(); |
||||
|
} |
||||
}; |
}; |
||||
|
|||||
Loading…
Reference in new issue