Skip to content

Commit

Permalink
Merge pull request #94 from souravbhowmik1999/fileUpload
Browse files Browse the repository at this point in the history
PS-2419 Multiple File upload on tenant
  • Loading branch information
Shubham4026 authored Nov 19, 2024
2 parents 01947e1 + f602ec2 commit b75a609
Show file tree
Hide file tree
Showing 9 changed files with 3,397 additions and 1,248 deletions.
4,397 changes: 3,193 additions & 1,204 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.688.0",
"@nestjs/axios": "^0.0.7",
"@nestjs/common": "^8.4.2",
"@nestjs/config": "^2.3.4",
Expand Down
63 changes: 63 additions & 0 deletions src/common/services/upload-file.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Injectable, BadRequestException, InternalServerErrorException } from '@nestjs/common';
import { extname } from 'path';
import { v4 as uuidv4 } from 'uuid';
import { HeadObjectCommand, PutObjectCommand, S3Client } from '@aws-sdk/client-s3';
import { ConfigService } from "@nestjs/config";


@Injectable()
export class FilesUploadService {
private readonly s3Client: S3Client;
private readonly bucketName: string = this.configService.get<string>("AWS_BUCKET_NAME");

constructor(
private configService: ConfigService,
) {
this.s3Client = new S3Client({
region: this.configService.get<string>("AWS_REGION"),
credentials: {
accessKeyId: this.configService.get<string>("AWS_ACCESS_KEY_ID"),
secretAccessKey: this.configService.get<string>("AWS_SECRET_ACCESS_KEY"),
},
});
}

async saveFile(file: Express.Multer.File): Promise<{ filePath: string; fileSize: number }> {
const allowedExtensions: string[] = ['.jpg', '.jpeg', '.png', '.gif', '.ico', '.webp'];
const fileExtension = extname(file.originalname).toLowerCase();

if (!allowedExtensions.includes(fileExtension)) {
throw new BadRequestException(`File type ${fileExtension} is not allowed.`);
}

const uniqueFileName = `${uuidv4()}${fileExtension}`;
const fileUrl = `https://${this.bucketName}.s3.${this.configService.get<string>("AWS_REGION")}.amazonaws.com/${uniqueFileName}`;

const params = {
Bucket: this.bucketName,
Key: uniqueFileName,
Body: file.buffer,
ContentType: file.mimetype,
};

try {
await this.s3Client.send(new PutObjectCommand(params));
} catch (error) {
throw new InternalServerErrorException(`Failed to upload file to S3. Error: ${error?.message || error}`);
}

const metadata = await this.s3Client.send(new HeadObjectCommand({
Bucket: this.bucketName,
Key: uniqueFileName,
}));

if (!metadata.ContentLength) {
throw new InternalServerErrorException('Failed to upload file to S3.');
}

return {
filePath: fileUrl,
fileSize: file.size,
};
}
}
8 changes: 8 additions & 0 deletions src/tenant/dto/tenant-create.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ export class TenantCreateDto {
@Expose()
params: object;

//file path
@ApiPropertyOptional({
type: String,
})
@IsString()
@Expose()
programImages: string[];

constructor(obj?: Partial<TenantCreateDto>) {
if (obj) {
Object.assign(this, obj);
Expand Down
54 changes: 54 additions & 0 deletions src/tenant/dto/tenant-update.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Expose, Type } from "class-transformer";
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { IsNotEmpty, IsString } from "class-validator";

export class TenantUpdateDto {
//tenant name
@ApiPropertyOptional({
type: String,
description: "Tenant name",
default: "",
})
@IsString()
@Expose()
name: string;

//domain
@ApiPropertyOptional({
type: String,
description: "Domain Name",
default: "",
})
@IsString()
@Expose()
domain: string;

//params
@ApiPropertyOptional({
type: Object,
description: "Params",
default: "",
})
@Expose()
params: object;

//file path
@ApiPropertyOptional({
type: String,
})
@IsString()
@Expose()
programImages: string[];


@ApiPropertyOptional({ type: String })
@IsString()
@Expose()
description: string;

constructor(obj?: Partial<TenantUpdateDto>) {
if (obj) {
Object.assign(this, obj);
}
}
}
42 changes: 21 additions & 21 deletions src/tenant/entities/tenent.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,31 @@ import {
Column,
CreateDateColumn,
UpdateDateColumn,
} from "typeorm";
} from 'typeorm';

@Entity({ name: "Tenants" })
export class Tenants {
@PrimaryGeneratedColumn("uuid")
tenantId: string;
@Entity('Tenants')
export class Tenant {
@PrimaryGeneratedColumn('uuid')
tenantId: string; // UUID field

@Column()
name: string;
@Column({ type: 'text' })
name: string; // Text field for tenant's name

@Column()
domain: string;
@Column({ type: 'text' })
domain: string; // Text field for tenant's domain

@CreateDateColumn({
type: "timestamp with time zone",
default: () => "CURRENT_TIMESTAMP",
})
createdAt: Date;
@CreateDateColumn({ type: 'timestamptz', default: () => 'CURRENT_TIMESTAMP' })
createdAt: Date; // Timestamp for creation date with timezone

@UpdateDateColumn({
type: "timestamp with time zone",
default: () => "CURRENT_TIMESTAMP",
})
updatedAt: Date;
@UpdateDateColumn({ type: 'timestamptz', default: () => 'CURRENT_TIMESTAMP' })
updatedAt: Date; // Timestamp for last updated date with timezone

@Column()
params: string;
@Column({ type: 'jsonb', nullable: true })
params: Record<string, any>; // JSONB field for additional parameters

@Column({ type: 'json', nullable: true })
programImages: string[]; // JSON field to store array of program images

@Column({ type: 'text' })
description: string; // Text field for tenant's domain
}
57 changes: 45 additions & 12 deletions src/tenant/tenant.controller.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { Body, Controller, Delete, Get, Patch, Post, Query, Req, Res, SerializeOptions } from '@nestjs/common';
import { Body, Controller, Delete, Get, Patch, Post, Query, Req, Res, SerializeOptions, UploadedFile, UploadedFiles, UseInterceptors } from '@nestjs/common';
import { TenantService } from './tenant.service';
import { ApiCreatedResponse, ApiForbiddenResponse } from '@nestjs/swagger';
import { TenantCreateDto } from './dto/tenant-create.dto';

import { FilesInterceptor } from '@nestjs/platform-express';
import { FilesUploadService } from 'src/common/services/upload-file';
import { TenantUpdateDto } from './dto/tenant-update.dto';
@Controller('tenant')
export class TenantController {
constructor(
private tenantService: TenantService,
private readonly filesUploadService: FilesUploadService
) { }
//Get tenant information
@Get("/read")
Expand All @@ -26,47 +29,77 @@ export class TenantController {
@Post("/create")
@ApiCreatedResponse({ description: "Tenant Created Successfully" })
@ApiForbiddenResponse({ description: "Forbidden" })
@UseInterceptors(FilesInterceptor('programImages', 10))
@SerializeOptions({
strategy: "excludeAll",
})
public async createTenants(
@Req() request: Request,
@Res() response: Response,
@Body() tenantCreateDto: TenantCreateDto
@Body() tenantCreateDto: TenantCreateDto,
@UploadedFiles() files: Express.Multer.File[],
) {
const uploadedFiles = [];

// Loop through each file and upload it
if (files && files.length > 0) {
for (const file of files) {
const uploadedFile = await this.filesUploadService.saveFile(file);
uploadedFiles.push(uploadedFile);
}

// Assuming tenantCreateDto needs an array of file paths
tenantCreateDto.programImages = uploadedFiles.map(file => file.filePath); // Adjust field as needed
}

return await this.tenantService.createTenants(request, tenantCreateDto, response);
}

//Delete a tenant
@Delete("/delete")
//Update a tenant
@Patch("/update")
@ApiCreatedResponse({ description: "Tenant Data Fetch" })
@ApiForbiddenResponse({ description: "Forbidden" })
@UseInterceptors(FilesInterceptor('programImages', 10))
@SerializeOptions({
strategy: "excludeAll",
})
public async deleteTenants(
public async updateTenants(
@Req() request: Request,
@Res() response: Response,
@Query("id") id: string
@Query("id") id: string,
@Body() tenantUpdateDto: TenantUpdateDto,
@UploadedFiles() files: Express.Multer.File[],
) {
const uploadedFiles = [];

// Loop through each file and upload it
if (files && files.length > 0) {
for (const file of files) {
const uploadedFile = await this.filesUploadService.saveFile(file);
uploadedFiles.push(uploadedFile);
}
// Assuming tenantCreateDto needs an array of file paths
tenantUpdateDto.programImages = uploadedFiles.map(file => file.filePath); // Adjust field as needed
}
const tenantId = id;
return await this.tenantService.deleteTenants(request, tenantId, response);
return await this.tenantService.updateTenants(request, tenantId, tenantUpdateDto, response);
}


//Update a tenant
@Patch("/update")
//Delete a tenant
@Delete("/delete")
@ApiCreatedResponse({ description: "Tenant Data Fetch" })
@ApiForbiddenResponse({ description: "Forbidden" })
@SerializeOptions({
strategy: "excludeAll",
})
public async updateTenants(
public async deleteTenants(
@Req() request: Request,
@Res() response: Response,
@Query("id") id: string
) {
const tenantId = id;
return await this.tenantService.updateTenants(request, tenantId, response);
return await this.tenantService.deleteTenants(request, tenantId, response);
}

}
7 changes: 4 additions & 3 deletions src/tenant/tenant.module.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { Module } from '@nestjs/common';
import { TenantController } from './tenant.controller';
import { TenantService } from './tenant.service';
import { Tenants } from 'src/tenant/entities/tenent.entity';
import { Tenant } from 'src/tenant/entities/tenent.entity';
import { TypeOrmModule } from '@nestjs/typeorm';
import { FilesUploadService } from 'src/common/services/upload-file';

@Module({
imports: [
TypeOrmModule.forFeature([Tenants])
TypeOrmModule.forFeature([Tenant])
],
controllers: [TenantController],
providers: [TenantService]
providers: [TenantService, FilesUploadService]
})
export class TenantModule { }
16 changes: 8 additions & 8 deletions src/tenant/tenant.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { HttpStatus, Injectable } from '@nestjs/common';
import { Tenants } from './entities/tenent.entity';
import { Tenant } from './entities/tenent.entity';
import { Repository } from 'typeorm';
import APIResponse from "src/common/responses/response";
import { InjectRepository } from '@nestjs/typeorm';
Expand All @@ -10,8 +10,8 @@ import { APIID } from '@utils/api-id.config';
@Injectable()
export class TenantService {
constructor(
@InjectRepository(Tenants)
private tenantRepository: Repository<Tenants>,
@InjectRepository(Tenant)
private tenantRepository: Repository<Tenant>,
) { }

public async getTenants(request, response) {
Expand Down Expand Up @@ -86,6 +86,7 @@ export class TenantService {
HttpStatus.CONFLICT
);
}

let result = await this.tenantRepository.save(tenantCreateDto);
return APIResponse.success(
response,
Expand Down Expand Up @@ -145,7 +146,7 @@ export class TenantService {
}
}

public async updateTenants(request, tenantId, response) {
public async updateTenants(request, tenantId, tenantUpdateDto, response) {
let apiId = APIID.TENANT_UPDATE;
try {
let checkExitTenants = await this.tenantRepository.find({
Expand All @@ -164,10 +165,9 @@ export class TenantService {
);
}

let result = await this.tenantRepository.update(
tenantId,
request.body
);
console.log(tenantUpdateDto);

let result = await this.tenantRepository.update(tenantId, tenantUpdateDto);
return APIResponse.success(
response,
apiId,
Expand Down

0 comments on commit b75a609

Please sign in to comment.