-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add basic tests for assets module * Add eager to createpresignedurl dto and implement CloudinaryService * Move AppModule imports from module to dynamic resolver * Rework assets service to return timestamp and signature * Rework assets service to return timestamp and signature * Add missing ApiProperty decorator for FormMetadata properties * Add fake cloudinary environment variables to circleci config * Add fake cloudinary secrets to circleci workflows * Fix unit tests * Update changelog * Add assets migration
- Loading branch information
Showing
24 changed files
with
434 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,3 +9,5 @@ THROTTLE_LIMIT=2 | |
EMAIL_API_KEY='SOME-LONG-SECRET-KEY' | ||
EMAIL_FROM_ADDRESS='Bloom Dev Housing Portal <[email protected]>' | ||
APP_SECRET='SOME-LONG-SECRET-KEY' | ||
CLOUDINARY_SECRET=CLOUDINARY_SECRET | ||
CLOUDINARY_KEY=CLOUDINARY_KEY |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { Test, TestingModule } from "@nestjs/testing" | ||
import { AssetsController } from "./assets.controller" | ||
import { AuthModule } from "../auth/auth.module" | ||
import dbOptions = require("../../ormconfig.test") | ||
import { TypeOrmModule } from "@nestjs/typeorm" | ||
import { AssetsService } from "./services/assets.service" | ||
|
||
describe("AssetsController", () => { | ||
let controller: AssetsController | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
controllers: [AssetsController], | ||
imports: [TypeOrmModule.forRoot(dbOptions), AuthModule], | ||
providers: [{ provide: AssetsService, useValue: {} }], | ||
}).compile() | ||
|
||
controller = module.get<AssetsController>(AssetsController) | ||
}) | ||
|
||
it("should be defined", () => { | ||
expect(controller).toBeDefined() | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { Body, Controller, Post, UseGuards, UsePipes, ValidationPipe } from "@nestjs/common" | ||
import { ApiBearerAuth, ApiOperation, ApiTags } from "@nestjs/swagger" | ||
import { mapTo } from "../shared/mapTo" | ||
import { ResourceType } from "../auth/decorators/resource-type.decorator" | ||
import { AuthzGuard } from "../auth/guards/authz.guard" | ||
import { defaultValidationPipeOptions } from "../shared/default-validation-pipe-options" | ||
import { DefaultAuthGuard } from "../auth/guards/default.guard" | ||
import { AssetsService } from "./services/assets.service" | ||
import { | ||
AssetCreateDto, | ||
AssetDto, | ||
CreatePresignedUploadMetadataDto, | ||
CreatePresignedUploadMetadataResponseDto, | ||
} from "./dto/asset.dto" | ||
|
||
@Controller("assets") | ||
@ApiTags("assets") | ||
@ApiBearerAuth() | ||
@ResourceType("asset") | ||
@UseGuards(DefaultAuthGuard, AuthzGuard) | ||
@UsePipes( | ||
new ValidationPipe({ | ||
...defaultValidationPipeOptions, | ||
}) | ||
) | ||
export class AssetsController { | ||
constructor(private readonly assetsService: AssetsService) {} | ||
|
||
@Post() | ||
@ApiOperation({ summary: "Create asset", operationId: "create" }) | ||
async create(@Body() assetCreateDto: AssetCreateDto): Promise<AssetDto> { | ||
const asset = await this.assetsService.create(assetCreateDto) | ||
return mapTo(AssetDto, asset) | ||
} | ||
|
||
@Post("/presigned-upload-metadata") | ||
@ApiOperation({ | ||
summary: "Create presigned upload metadata", | ||
operationId: "createPresignedUploadMetadata", | ||
}) | ||
async createPresignedUploadMetadata( | ||
@Body() createPresignedUploadMetadataDto: CreatePresignedUploadMetadataDto | ||
): Promise<CreatePresignedUploadMetadataResponseDto> { | ||
return mapTo( | ||
CreatePresignedUploadMetadataResponseDto, | ||
await this.assetsService.createPresignedUploadMetadata(createPresignedUploadMetadataDto) | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { Module } from "@nestjs/common" | ||
import { AssetsController } from "./assets.controller" | ||
import { AssetsService } from "./services/assets.service" | ||
import { CloudinaryService, UploadService } from "./services/upload.service" | ||
import { TypeOrmModule } from "@nestjs/typeorm" | ||
import { SharedModule } from "../shared/shared.module" | ||
import { Asset } from "./entities/asset.entity" | ||
import { AuthModule } from "../auth/auth.module" | ||
|
||
@Module({ | ||
controllers: [AssetsController], | ||
providers: [AssetsService, { provide: UploadService, useClass: CloudinaryService }], | ||
imports: [TypeOrmModule.forFeature([Asset]), AuthModule, SharedModule], | ||
}) | ||
export class AssetsModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { OmitType } from "@nestjs/swagger" | ||
import { Asset } from "../entities/asset.entity" | ||
import { Expose } from "class-transformer" | ||
import { IsDefined } from "class-validator" | ||
import { ValidationsGroupsEnum } from "../../shared/types/validations-groups-enum" | ||
|
||
export class AssetDto extends OmitType(Asset, [] as const) {} | ||
|
||
export class AssetCreateDto extends OmitType(AssetDto, ["id", "createdAt", "updatedAt"] as const) {} | ||
|
||
export class CreatePresignedUploadMetadataDto { | ||
@Expose() | ||
@IsDefined({ groups: [ValidationsGroupsEnum.default] }) | ||
parametersToSign: Record<string, string> | ||
} | ||
|
||
export class CreatePresignedUploadMetadataResponseDto { | ||
@Expose() | ||
signature: string | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { AbstractEntity } from "../../shared/entities/abstract.entity" | ||
import { Expose } from "class-transformer" | ||
import { IsString, MaxLength } from "class-validator" | ||
import { ValidationsGroupsEnum } from "../../shared/types/validations-groups-enum" | ||
import { Column, Entity } from "typeorm" | ||
|
||
@Entity({ name: "assets" }) | ||
export class Asset extends AbstractEntity { | ||
@Column({ type: "text" }) | ||
@Expose() | ||
@IsString({ groups: [ValidationsGroupsEnum.default] }) | ||
@MaxLength(128, { groups: [ValidationsGroupsEnum.default] }) | ||
fileId: string | ||
|
||
@Column({ type: "text" }) | ||
@Expose() | ||
@IsString({ groups: [ValidationsGroupsEnum.default] }) | ||
@MaxLength(128, { groups: [ValidationsGroupsEnum.default] }) | ||
label: string | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { Test, TestingModule } from "@nestjs/testing" | ||
import { AssetsService } from "./assets.service" | ||
import { getRepositoryToken } from "@nestjs/typeorm" | ||
import { Asset } from "../entities/asset.entity" | ||
import { UploadService } from "./upload.service" | ||
|
||
describe("AssetsService", () => { | ||
let service: AssetsService | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
providers: [ | ||
AssetsService, | ||
{ provide: getRepositoryToken(Asset), useValue: {} }, | ||
{ provide: UploadService, useValue: {} }, | ||
], | ||
}).compile() | ||
|
||
service = module.get<AssetsService>(AssetsService) | ||
}) | ||
|
||
it("should be defined", () => { | ||
expect(service).toBeDefined() | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { Injectable } from "@nestjs/common" | ||
import { | ||
AssetCreateDto, | ||
CreatePresignedUploadMetadataDto, | ||
CreatePresignedUploadMetadataResponseDto, | ||
} from "../dto/asset.dto" | ||
import { InjectRepository } from "@nestjs/typeorm" | ||
import { Repository } from "typeorm" | ||
import { Asset } from "../entities/asset.entity" | ||
import { UploadService } from "./upload.service" | ||
|
||
@Injectable() | ||
export class AssetsService { | ||
constructor( | ||
@InjectRepository(Asset) private readonly repository: Repository<Asset>, | ||
private readonly uploadService: UploadService | ||
) {} | ||
|
||
async create(assetCreateDto: AssetCreateDto) { | ||
return await this.repository.save(assetCreateDto) | ||
} | ||
|
||
createPresignedUploadMetadata( | ||
createUploadUrlDto: CreatePresignedUploadMetadataDto | ||
): Promise<CreatePresignedUploadMetadataResponseDto> { | ||
return Promise.resolve( | ||
this.uploadService.createPresignedUploadMetadata(createUploadUrlDto.parametersToSign) | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { Injectable } from "@nestjs/common" | ||
import { ConfigService } from "@nestjs/config" | ||
import { v2 as cloudinary } from "cloudinary" | ||
|
||
export abstract class UploadService { | ||
abstract createPresignedUploadMetadata( | ||
parametersToSign: Record<string, string> | ||
): { signature: string } | ||
} | ||
|
||
@Injectable() | ||
export class CloudinaryService implements UploadService { | ||
constructor(private readonly configService: ConfigService) {} | ||
|
||
createPresignedUploadMetadata(parametersToSign: Record<string, string>): { signature: string } { | ||
// Based on https://cloudinary.com/documentation/upload_images#signed_upload_video_tutorial | ||
const signature = cloudinary.utils.api_sign_request( | ||
parametersToSign, | ||
this.configService.get<string>("CLOUDINARY_SECRET") | ||
) | ||
return { | ||
signature, | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.