Skip to content

Commit

Permalink
feat: Transformed image names to image url while sending imported data
Browse files Browse the repository at this point in the history
  • Loading branch information
chavda-bhavik committed Jul 2, 2024
1 parent 4731bce commit 94203a4
Show file tree
Hide file tree
Showing 12 changed files with 89 additions and 22 deletions.
9 changes: 8 additions & 1 deletion apps/api/src/app/shared/helpers/common.helper.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BadRequestException } from '@nestjs/common';
import { APIMessages } from '../constants';
import { PaginationResult, Defaults } from '@impler/shared';
import { PaginationResult, Defaults, FileMimeTypesEnum } from '@impler/shared';

export function validateNotFound(data: any, entityName: 'upload' | 'template'): boolean {
if (data) return true;
Expand Down Expand Up @@ -57,3 +57,10 @@ export function isDateString(date: string | number): boolean {
// @ts-ignore
return new Date(date) != 'Invalid Date' && !isNaN(new Date(date));
}

export function getAssetMimeType(name: string): string {
if (name.endsWith('.png')) return FileMimeTypesEnum.PNG;
else if (name.endsWith('.jpg')) return FileMimeTypesEnum.JPEG;
else if (name.endsWith('.jpeg')) return FileMimeTypesEnum.JPEG;
throw new Error('Unsupported file type');
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ColumnTypesEnum, FileMimeTypesEnum, FileNameService, ISchemaItem } from

import { ExcelFileService } from '@shared/services/file';
import { IExcelFileHeading } from '@shared/types/file.types';
import { getAssetMimeType } from '@shared/helpers/common.helper';
import { DownloadSampleDataCommand } from './download-sample.command';
import { StorageService } from '@impler/shared/dist/services/storage';

Expand Down Expand Up @@ -76,7 +77,7 @@ export class DownloadSample {
const file = zip.files[filename];
const fileData = await file.async('base64');
const storageFilename = this.fileNameService.getAssetFilePath(data.importId, filename);
await this.storageService.uploadFile(storageFilename, fileData, this.getAssetMimeType(filename));
await this.storageService.uploadFile(storageFilename, fileData, getAssetMimeType(filename));
})
);
}
Expand All @@ -87,10 +88,4 @@ export class DownloadSample {
type: hasMultiSelect ? FileMimeTypesEnum.EXCELM : FileMimeTypesEnum.EXCELX,
};
}
getAssetMimeType(name: string): string {
if (name.endsWith('.png')) return FileMimeTypesEnum.PNG;
else if (name.endsWith('.jpg')) return FileMimeTypesEnum.JPEG;
else if (name.endsWith('.jpeg')) return FileMimeTypesEnum.JPEG;
throw new Error('Unsupported file type');
}
}
20 changes: 18 additions & 2 deletions apps/api/src/app/upload/upload.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ import {
} from '@nestjs/common';
import { ApiTags, ApiSecurity, ApiConsumes, ApiOperation, ApiParam, ApiQuery, ApiOkResponse } from '@nestjs/swagger';

import { GetUpload, GetAsset } from './usecases';
import { JwtAuthGuard } from '@shared/framework/auth.gaurd';
import { validateNotFound } from '@shared/helpers/common.helper';
import { getAssetMimeType, validateNotFound } from '@shared/helpers/common.helper';
import { validateUploadStatus } from '@shared/helpers/upload.helpers';
import { PaginationResponseDto } from '@shared/dtos/pagination-response.dto';
import { GetUpload } from '@shared/usecases/get-upload/get-upload.usecase';
import { ValidateMongoId } from '@shared/validations/valid-mongo-id.validation';
import { ValidImportFile } from '@shared/validations/valid-import-file.validation';
import { GetUploadCommand } from '@shared/usecases/get-upload/get-upload.command';
Expand All @@ -45,6 +45,7 @@ import {
@UseGuards(JwtAuthGuard)
export class UploadController {
constructor(
private getAsset: GetAsset,
private getUpload: GetUpload,
private terminateUpload: TerminateUpload,
private makeUploadEntry: MakeUploadEntry,
Expand Down Expand Up @@ -138,6 +139,21 @@ export class UploadController {
content.pipe(res);
}

@Get(':uploadId/asset/:name')
@ApiOperation({
summary: 'Get uploaded asset file',
})
async getUploadedAsset(
@Param('uploadId', ValidateMongoId) uploadId: string,
@Param('name') assetName: string,
@Res() res: Response
) {
const content = await this.getAsset.execute(uploadId, assetName);
res.setHeader('Content-Type', getAssetMimeType(assetName));
res.setHeader('Content-Disposition', 'attachment; filename=' + assetName);
content.pipe(res);
}

@Get(':uploadId/rows/valid')
@ApiOperation({
summary: 'Get valid rows of the uploaded file',
Expand Down
28 changes: 28 additions & 0 deletions apps/api/src/app/upload/usecases/get-asset/get-asset.usecase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Readable } from 'stream';
import { Injectable } from '@nestjs/common';
import { UploadRepository } from '@impler/dal';
import { FileNameService } from '@impler/shared';
import { StorageService } from '@impler/shared/dist/services/storage';
import { DocumentNotFoundException } from '@shared/exceptions/document-not-found.exception';

@Injectable()
export class GetAsset {
constructor(
private uploadRepository: UploadRepository,
private storageService: StorageService,
private fileNameService: FileNameService
) {}

async execute(_uploadId: string, name: string): Promise<Readable> {
const upload = await this.uploadRepository.findById(_uploadId, '_id');
if (!upload) {
throw new DocumentNotFoundException('Upload', _uploadId);
}

try {
return await this.storageService.getFileStream(this.fileNameService.getAssetFilePath(_uploadId, name));
} catch (error) {
throw new DocumentNotFoundException('Asset', name);
}
}
}
3 changes: 3 additions & 0 deletions apps/api/src/app/upload/usecases/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { GetAsset } from './get-asset/get-asset.usecase';
import { GetUploadColumns } from './get-columns/get-columns.usecase';
import { GetUpload } from '@shared/usecases/get-upload/get-upload.usecase';
import { TerminateUpload } from './terminate-upload/terminate-upload.usecase';
Expand All @@ -7,6 +8,7 @@ import { GetOriginalFileContent } from './get-original-file-content/get-original
import { GetUploadProcessInformation } from './get-upload-process-info/get-upload-process-info.usecase';

export const USE_CASES = [
GetAsset,
GetUpload,
TerminateUpload,
MakeUploadEntry,
Expand All @@ -18,6 +20,7 @@ export const USE_CASES = [
];

export {
GetAsset,
GetUpload,
MakeUploadEntry,
TerminateUpload,
Expand Down
1 change: 1 addition & 0 deletions apps/queue-manager/src/.example.env
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
NODE_ENV=local
API_BASE_URL=http://localhost:4200
RABBITMQ_CONN_URL=amqp://guest:guest@localhost:5672
MONGO_URL=mongodb://localhost:27017/impler-db

Expand Down
21 changes: 17 additions & 4 deletions apps/queue-manager/src/consumers/send-webhook-data.consumer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export class SendWebhookDataConsumer extends BaseConsumer {
recordFormat: cachedData.recordFormat,
chunkFormat: cachedData.chunkFormat,
totalRecords: allDataJson.length,
imageHeadings: cachedData.imageHeadings,
multiSelectHeadings: cachedData.multiSelectHeadings,
});

Expand Down Expand Up @@ -115,18 +116,27 @@ export class SendWebhookDataConsumer extends BaseConsumer {
chunkFormat,
recordFormat,
extra = '',
imageHeadings,
multiSelectHeadings,
}: IBuildSendDataParameters): { sendData: Record<string, unknown>; page: number } {
const defaultValuesObj = JSON.parse(defaultValues);
let slicedData = data.slice(
Math.max((page - DEFAULT_PAGE) * chunkSize, MIN_LIMIT),
Math.min(page * chunkSize, data.length)
);
if (Array.isArray(multiSelectHeadings) && multiSelectHeadings.length > 0) {
if ((Array.isArray(multiSelectHeadings) && multiSelectHeadings.length > 0) || imageHeadings?.length > 0) {
slicedData = slicedData.map((obj) => {
multiSelectHeadings.forEach((heading) => {
obj.record[heading] = obj.record[heading] ? obj.record[heading].split(',') : [];
});
if (multiSelectHeadings.length > 0)
multiSelectHeadings.forEach((heading) => {
obj.record[heading] = obj.record[heading] ? obj.record[heading].split(',') : [];
});

if (imageHeadings?.length > 0)
imageHeadings.forEach((heading) => {
obj.record[heading] = obj.record[heading]
? `${process.env.API_BASE_URL}/v1/upload/${uploadId}/asset/${obj.record[heading]}`
: '';
});

return obj;
});
Expand Down Expand Up @@ -167,11 +177,13 @@ export class SendWebhookDataConsumer extends BaseConsumer {

const defaultValueObj = {};
const multiSelectHeadings = [];
const imageHeadings = [];
const customSchema = JSON.parse(uploadata.customSchema) as ITemplateSchemaItem[];
if (Array.isArray(customSchema)) {
customSchema.forEach((item: ITemplateSchemaItem) => {
if (item.defaultValue) defaultValueObj[item.key] = item.defaultValue;
if (item.type === ColumnTypesEnum.SELECT && item.allowMultiSelect) multiSelectHeadings.push(item.key);
if (item.type === ColumnTypesEnum.IMAGE) imageHeadings.push(item.key);
});
}

Expand All @@ -190,6 +202,7 @@ export class SendWebhookDataConsumer extends BaseConsumer {
recordFormat: uploadata.customRecordFormat,
chunkFormat: uploadata.customChunkFormat,
multiSelectHeadings,
imageHeadings,
};
}

Expand Down
3 changes: 2 additions & 1 deletion apps/queue-manager/src/types/env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ declare namespace NodeJS {
export interface ProcessEnv {
MONGO_URL: string;
RABBITMQ_CONN_URL: string;
API_BASE_URL: string;
S3_LOCAL_STACK: string;
S3_REGION: string;
S3_BUCKET_NAME: string;

PAYMENT_API_BASE_URL: string;
PAYMENT_AUTH_KEY: string;
PAYMENT_AUTH_VALUE: string;

SENTRY_DSN: string;
}
}
1 change: 1 addition & 0 deletions apps/queue-manager/src/types/file-processing.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export interface IBuildSendDataParameters extends IBaseSendDataParameters {
recordFormat?: string;
chunkFormat?: string;
multiSelectHeadings?: string[];
imageHeadings?: string[];
}
export interface ISendDataResponse {
statusCode: number;
Expand Down
14 changes: 7 additions & 7 deletions apps/widget/src/hooks/Phase0/usePhase0.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useEffect } from 'react';
import { useQuery } from '@tanstack/react-query';

import { IErrorObject } from '@impler/shared';
Expand All @@ -14,19 +15,18 @@ export function usePhase0({ goNext }: IUsePhase0Props) {
const { schema, showWidget } = useAppState();
const { projectId, templateId } = useImplerState();

const { error, isLoading } = useQuery<boolean, IErrorObject, any, (string | undefined)[]>(
const { data, error, isLoading } = useQuery<unknown, IErrorObject, { success: boolean }, (string | undefined)[]>(
['valid', projectId, templateId, schema],
() => api.checkIsRequestvalid(projectId, templateId, schema) as Promise<boolean>,
() => api.checkIsRequestvalid(projectId, templateId, schema) as Promise<{ success: boolean }>,
{
onSuccess(valid) {
if (valid) {
goNext();
}
},
refetchOnWindowFocus: true,
}
);

useEffect(() => {
if (data?.success) goNext();
}, [data]);

return {
error,
isLoading,
Expand Down
1 change: 1 addition & 0 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ services:
MONGO_URL: ${MONGO_URL}
RABBITMQ_CONN_URL: ${RABBITMQ_CONN_URL}
S3_REGION: ${S3_REGION}
API_BASE_URL: ${API_BASE_URL}
S3_LOCAL_STACK: ${S3_LOCAL_STACK}
S3_BUCKET_NAME: ${S3_BUCKET_NAME}
AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
Expand Down
1 change: 1 addition & 0 deletions libs/shared/src/types/upload/upload.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export type SendWebhookCachedData = {
chunkFormat?: string;
defaultValues: string;
multiSelectHeadings?: string[];
imageHeadings?: string[];
};

export type SendBubbleCachedData = {
Expand Down

0 comments on commit 94203a4

Please sign in to comment.