Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BC-5714 - Remove async and context #4696

Closed
wants to merge 30 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
3c704ad
Add fileExists property to fileRecord
bischofmax Jan 2, 2024
59972ce
Add fileExists to response
bischofmax Jan 2, 2024
4548b8f
Add isUploading
bischofmax Jan 4, 2024
2798ef1
Add tests
bischofmax Jan 5, 2024
5eec031
Add promise resolve to test mocks
bischofmax Jan 5, 2024
304624e
Use ApiPropertyOptional for isUploading prop
bischofmax Jan 5, 2024
356b013
Merge branch 'main' into BC-5714-store-upload-state
bischofmax Jan 9, 2024
7817516
Add apiheader for If-None-Match
bischofmax Jan 9, 2024
b49ed3f
Merge branch 'BC-5714-store-upload-state' of github.com:hpi-schul-clo…
bischofmax Jan 9, 2024
aec41e4
Add console logs
bischofmax Jan 9, 2024
e9d0c86
Merge branch 'main' into BC-5714-store-upload-state
bischofmax Jan 9, 2024
37ab24d
Revert "Add console logs"
bischofmax Jan 10, 2024
4e5697a
Turn on mikroorm debug
bischofmax Jan 10, 2024
1489825
BC-5714 - add req context
SevenWaysDP Jan 10, 2024
6276908
Merge branch 'main' into BC-5714-store-upload-state
bischofmax Jan 11, 2024
49aab22
Rename markAsLoaded to markAsUploaded
bischofmax Jan 11, 2024
c01c009
Remove mikororm debug mode
bischofmax Jan 11, 2024
3f75e03
Merge branch 'BC-5714-store-upload-state' of https://github.com/hpi-s…
SevenWaysDP Jan 11, 2024
b5cc099
BC-5714 - add ingress config
SevenWaysDP Jan 11, 2024
2bcff0c
Revert "BC-5714 - add req context"
SevenWaysDP Jan 11, 2024
fdc2e71
BC-5714 - remove await
SevenWaysDP Jan 11, 2024
970f94a
add error logging
SevenWaysDP Jan 11, 2024
ba492b4
try request scope
SevenWaysDP Jan 12, 2024
e9fd73f
Add scope to repo
bischofmax Jan 12, 2024
c6027ea
add scope to MikroOrm Module
SevenWaysDP Jan 12, 2024
336ba02
Create new async request context in uc
bischofmax Jan 12, 2024
e1e3a1c
Remove await from busboy on file
bischofmax Jan 15, 2024
c4eb4fe
Only starte file storage api server on debug command
bischofmax Jan 15, 2024
4df2220
Remove RequestContext.create
bischofmax Jan 15, 2024
9c78b78
Fix Imports
bischofmax Jan 15, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ metadata:
nginx.ingress.kubernetes.io/http2-max-header-size: 96k
nginx.ingress.kubernetes.io/large-client-header-buffers: 4 100k
nginx.ingress.kubernetes.io/proxy-buffer-size: 96k
nginx.ingress.kubernetes.io/proxy-request-buffering: "off"
{% if CLUSTER_ISSUER is defined %}
cert-manager.io/cluster-issuer: {{ CLUSTER_ISSUER }}
{% endif %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export class FileRecordResponse {
this.creatorId = fileRecord.creatorId;
this.mimeType = fileRecord.mimeType;
this.parentType = fileRecord.parentType;
this.isUploading = fileRecord.isUploading;
this.deletedSince = fileRecord.deletedSince;
this.previewStatus = fileRecord.getPreviewStatus();
}
Expand Down Expand Up @@ -46,6 +47,9 @@ export class FileRecordResponse {
@ApiProperty({ enum: FileRecordParentType, enumName: 'FileRecordParentType' })
parentType: FileRecordParentType;

@ApiPropertyOptional()
isUploading?: boolean;

@ApiProperty({ enum: PreviewStatus, enumName: 'PreviewStatus' })
previewStatus: PreviewStatus;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ export class FilesStorageController {
@ApiResponse({ status: 422, type: UnprocessableEntityException })
@ApiResponse({ status: 500, type: InternalServerErrorException })
@ApiHeader({ name: 'Range', required: false })
@ApiHeader({ name: 'If-None-Match', required: false })
@Get('/preview/:fileRecordId/:fileName')
@RequestTimeout(config().INCOMING_REQUEST_TIMEOUT)
async downloadPreview(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe('FileRecord Entity', () => {
};
});

it('should provide the target id as entity id', () => {
it('should provide target id', () => {
const parentId = new ObjectId().toHexString();
const fileRecord = new FileRecord({
...props,
Expand All @@ -41,7 +41,7 @@ describe('FileRecord Entity', () => {
expect(fileRecord.parentId).toEqual(parentId);
});

it('should provide the creator id as entity id', () => {
it('should provide creator id', () => {
const creatorId = new ObjectId().toHexString();
const fileRecord = new FileRecord({
...props,
Expand All @@ -50,7 +50,7 @@ describe('FileRecord Entity', () => {
expect(fileRecord.creatorId).toEqual(creatorId);
});

it('should provide the school id as entity id', () => {
it('should provide school id', () => {
const schoolId = new ObjectId().toHexString();
const fileRecord = new FileRecord({
...props,
Expand All @@ -59,14 +59,23 @@ describe('FileRecord Entity', () => {
expect(fileRecord.schoolId).toEqual(schoolId);
});

it('should provide the isCopyFrom as entity id', () => {
it('should provide isCopyFrom', () => {
const isCopyFrom = new ObjectId().toHexString();
const fileRecord = new FileRecord({
...props,
isCopyFrom,
});
expect(fileRecord.isCopyFrom).toEqual(isCopyFrom);
});

it('should provide isUploading', () => {
const isUploading = true;
const fileRecord = new FileRecord({
...props,
isUploading,
});
expect(fileRecord.isUploading).toEqual(isUploading);
});
});

describe('when embedding the security status', () => {
Expand Down Expand Up @@ -852,4 +861,23 @@ describe('FileRecord Entity', () => {
});
});
});

describe('markAsLoaded is called', () => {
describe('WHEN isUploading is true', () => {
const setup = () => {
const isUploading = true;
const fileRecord = fileRecordFactory.build({ isUploading });

return { fileRecord, isUploading };
};

it('should set it to undefined', () => {
const { fileRecord } = setup();
expect(fileRecord.isUploading).toBe(true);
const result = fileRecord.markAsUploaded();

expect(result).toBe(undefined);
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export interface FileRecordProperties {
schoolId: EntityId;
deletedSince?: Date;
isCopyFrom?: EntityId;
isUploading?: boolean;
}

interface ParentInfo {
Expand Down Expand Up @@ -120,6 +121,9 @@ export class FileRecord extends BaseEntityWithTimestamps {
@Enum()
parentType: FileRecordParentType;

@Property({ nullable: true })
isUploading?: boolean;

@Index()
@Property({ fieldName: 'parent' })
_parentId: ObjectId;
Expand Down Expand Up @@ -161,6 +165,7 @@ export class FileRecord extends BaseEntityWithTimestamps {
this.name = props.name;
this.mimeType = props.mimeType;
this.parentType = props.parentType;
this.isUploading = props.isUploading;
this._parentId = new ObjectId(props.parentId);
if (props.creatorId !== undefined) {
this._creatorId = new ObjectId(props.creatorId);
Expand Down Expand Up @@ -311,4 +316,8 @@ export class FileRecord extends BaseEntityWithTimestamps {
public removeCreatorId(): void {
this.creatorId = undefined;
}

public markAsUploaded(): void {
this.isUploading = undefined;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export function createFileRecord(
parentId: params.parentId,
creatorId: userId,
schoolId: params.schoolId,
isUploading: true,
});

return entity;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { createMock, DeepMocked } from '@golevelup/ts-jest';
import { AntivirusService } from '@infra/antivirus';
import { S3ClientAdapter } from '@infra/s3-client';
import { ObjectId } from '@mikro-orm/mongodb';
import { BadRequestException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Test, TestingModule } from '@nestjs/testing';
import { AntivirusService } from '@infra/antivirus';
import { S3ClientAdapter } from '@infra/s3-client';
import { fileRecordFactory, setupEntities } from '@shared/testing';
import { readableStreamWithFileTypeFactory } from '@shared/testing/factory/readable-stream-with-file-type.factory';
import { LegacyLogger } from '@src/core/logger';
Expand Down Expand Up @@ -157,6 +157,8 @@ describe('FilesStorageService upload methods', () => {
if (fr instanceof FileRecord && !fr._id) {
fr._id = new ObjectId();
}

return Promise.resolve();
});

return {
Expand Down Expand Up @@ -187,6 +189,37 @@ describe('FilesStorageService upload methods', () => {
expect(getFileRecordsOfParentSpy).toHaveBeenCalledWith(params.parentId);
});

it('should call fileRecordRepo.save in first call with isUploading: true', async () => {
const { params, file, userId } = setup();

// haveBeenCalledWith can't be use here because fileRecord is a reference and
// it will always compare the final state of the object
let param: FileRecord | undefined;

fileRecordRepo.save.mockReset();
fileRecordRepo.save.mockImplementationOnce(async (fr) => {
if (fr instanceof FileRecord && !fr._id) {
fr._id = new ObjectId();
}

param = JSON.parse(JSON.stringify(fr)) as FileRecord;

return Promise.resolve();
});

fileRecordRepo.save.mockImplementationOnce(async (fr) => {
if (fr instanceof FileRecord && !fr._id) {
fr._id = new ObjectId();
}

return Promise.resolve();
});

await service.uploadFile(userId, params, file);

expect(param).toMatchObject({ isUploading: true });
});

it('should call fileRecordRepo.save twice with correct params', async () => {
const { params, file, fileSize, userId, readableStreamWithFileType, expectedFileRecord } = setup();

Expand All @@ -201,6 +234,7 @@ describe('FilesStorageService upload methods', () => {
size: fileSize,
createdAt: expect.any(Date),
updatedAt: expect.any(Date),
isUploading: undefined,
})
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,12 +180,16 @@ export class FilesStorageService {
// The actual file size is set here because it is known only after the whole file is streamed.
fileRecord.size = await fileSizePromise;
this.throwErrorIfFileIsTooBig(fileRecord.size);

fileRecord.markAsUploaded();

await this.fileRecordRepo.save(fileRecord);

if (!useStreamToAntivirus || !fileRecord.isPreviewPossible()) {
await this.sendToAntivirus(fileRecord);
}
} catch (error) {
this.logger.error(`create file failed for fileRecordId ${fileRecord.id}`, error);
await this.storageClient.delete([filePath]);
await this.fileRecordRepo.delete(fileRecord);
throw error;
Expand Down
30 changes: 20 additions & 10 deletions apps/server/src/modules/files-storage/uc/files-storage.uc.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { EntityManager } from '@mikro-orm/core';
import { AuthorizationContext } from '@modules/authorization';
import { AuthorizationReferenceService } from '@modules/authorization/domain';
import { HttpService } from '@nestjs/axios';
Expand Down Expand Up @@ -36,7 +37,8 @@ export class FilesStorageUC {
private readonly authorizationReferenceService: AuthorizationReferenceService,
private readonly httpService: HttpService,
private readonly filesStorageService: FilesStorageService,
private readonly previewService: PreviewService
private readonly previewService: PreviewService,
private readonly em: EntityManager
) {
this.logger.setContext(FilesStorageUC.name);
}
Expand All @@ -63,18 +65,26 @@ export class FilesStorageUC {
private async uploadFileWithBusboy(userId: EntityId, params: FileRecordParams, req: Request): Promise<FileRecord> {
const promise = new Promise<FileRecord>((resolve, reject) => {
const bb = busboy({ headers: req.headers, defParamCharset: 'utf8' });
let promise2: Promise<FileRecord>;

// eslint-disable-next-line @typescript-eslint/no-misused-promises
bb.on('file', async (_name, file, info) => {
bb.on('file', (_name, file, info) => {
const fileDto = FileDtoBuilder.buildFromRequest(info, file);

try {
const record = await this.filesStorageService.uploadFile(userId, params, fileDto);
resolve(record);
} catch (error) {
req.unpipe(bb);
reject(error);
}
promise2 = this.filesStorageService.uploadFile(userId, params, fileDto);
});

bb.on('finish', () => {
promise2
.then((result) => {
resolve(result);
console.log('fileclose', result);
return result;
})
.catch((error) => {
this.logger.error({ message: 'could not upload file', error: error as Error });
req.unpipe(bb);
reject(error);
});
});

req.pipe(bb);
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
"nest:start:management:prod": "node dist/apps/server/apps/management.app",
"nest:start:files-storage": "nest start files-storage & nest start files-storage-amqp & nest start preview-generator-amqp",
"nest:start:files-storage:dev": "nest start files-storage --watch & nest start files-storage-amqp --watch & nest start preview-generator-amqp --watch",
"nest:start:files-storage:debug": "nest start files-storage --debug --watch & nest start files-storage-amqp --debug --watch & nest start preview-generator-amqp --debug --watch",
"nest:start:files-storage:debug": "nest start files-storage --debug --watch",
"nest:start:files-storage:prod": "node dist/apps/server/apps/files-storage.app",
"nest:start:files-storage-amqp:prod": "node dist/apps/server/apps/files-storage-consumer.app",
"nest:start:preview-generator-amqp:prod": "node dist/apps/server/apps/preview-generator-consumer.app",
Expand Down
Loading