diff --git a/apps/api/src/app/common/common.controller.ts b/apps/api/src/app/common/common.controller.ts index d9538852c..d2dd0be93 100644 --- a/apps/api/src/app/common/common.controller.ts +++ b/apps/api/src/app/common/common.controller.ts @@ -35,7 +35,7 @@ export class CommonController { summary: 'Check if request is valid (Checks Auth)', }) async isRequestValid(@Body() body: ValidRequestDto): Promise<{ success: boolean }> { - return this.validRequest.execute( + return await this.validRequest.execute( ValidRequestCommand.create({ projectId: body.projectId, templateId: body.templateId, diff --git a/apps/api/src/app/common/usecases/get-import-config/get-import-config.usecase.ts b/apps/api/src/app/common/usecases/get-import-config/get-import-config.usecase.ts index 1ee737506..cf33a891f 100644 --- a/apps/api/src/app/common/usecases/get-import-config/get-import-config.usecase.ts +++ b/apps/api/src/app/common/usecases/get-import-config/get-import-config.usecase.ts @@ -1,6 +1,6 @@ import { BadRequestException, Injectable } from '@nestjs/common'; import { UserRepository, TemplateRepository, TemplateEntity } from '@impler/dal'; -import { IImportConfig } from '@impler/shared'; +import { AVAILABLE_BILLABLEMETRIC_CODE_ENUM, IImportConfig } from '@impler/shared'; import { PaymentAPIService } from '@impler/services'; import { APIMessages } from '@shared/constants'; @@ -15,7 +15,10 @@ export class GetImportConfig { async execute(projectId: string, templateId?: string): Promise { const userEmail = await this.userRepository.findUserEmailFromProjectId(projectId); - const removeBrandingAvailable = await this.paymentAPIService.checkEvent(userEmail, 'REMOVE_BRANDING'); + const removeBrandingAvailable = await this.paymentAPIService.checkEvent({ + email: userEmail, + billableMetricCode: AVAILABLE_BILLABLEMETRIC_CODE_ENUM.REMOVE_BRANDING, + }); let template: TemplateEntity; if (templateId) { diff --git a/apps/api/src/app/common/usecases/valid-request/valid-request.usecase.ts b/apps/api/src/app/common/usecases/valid-request/valid-request.usecase.ts index 5b680d543..533493080 100644 --- a/apps/api/src/app/common/usecases/valid-request/valid-request.usecase.ts +++ b/apps/api/src/app/common/usecases/valid-request/valid-request.usecase.ts @@ -5,15 +5,18 @@ import { Injectable, HttpStatus, HttpException, UnauthorizedException } from '@n import { APIMessages } from '@shared/constants'; import { SchemaDto } from 'app/common/dtos/Schema.dto'; import { ValidRequestCommand } from './valid-request.command'; -import { ProjectRepository, TemplateRepository } from '@impler/dal'; +import { ProjectRepository, TemplateRepository, UserEntity } from '@impler/dal'; import { UniqueColumnException } from '@shared/exceptions/unique-column.exception'; import { DocumentNotFoundException } from '@shared/exceptions/document-not-found.exception'; +import { PaymentAPIService } from '@impler/services'; +import { AVAILABLE_BILLABLEMETRIC_CODE_ENUM } from '@impler/shared'; @Injectable() export class ValidRequest { constructor( private projectRepository: ProjectRepository, - private templateRepository: TemplateRepository + private templateRepository: TemplateRepository, + private paymentAPIService: PaymentAPIService ) {} async execute(command: ValidRequestCommand): Promise<{ success: boolean }> { @@ -38,6 +41,22 @@ export class ValidRequest { throw new DocumentNotFoundException('Template', command.templateId, APIMessages.INCORRECT_KEYS_FOUND); } } + if (command.schema) { + const project = await this.projectRepository.getUserOfProject(command.projectId); + + const isBillableMetricAvailable = await this.paymentAPIService.checkEvent({ + email: (project._userId as unknown as UserEntity).email, + billableMetricCode: AVAILABLE_BILLABLEMETRIC_CODE_ENUM.IMAGE_UPLOAD, + }); + + if (!isBillableMetricAvailable) { + throw new DocumentNotFoundException( + 'Schema', + command.schema, + APIMessages.ERROR_ACCESSING_FEATURE.IMAGE_UPLOAD + ); + } + } if (command.schema) { const parsedSchema: SchemaDto[] = diff --git a/apps/api/src/app/review/usecases/start-process/start-process.usecase.ts b/apps/api/src/app/review/usecases/start-process/start-process.usecase.ts index f1a638135..5ed497fd5 100644 --- a/apps/api/src/app/review/usecases/start-process/start-process.usecase.ts +++ b/apps/api/src/app/review/usecases/start-process/start-process.usecase.ts @@ -26,7 +26,9 @@ export class StartProcess { let importedData; const destination = (uploadInfo._templateId as unknown as TemplateEntity)?.destination; const userEmail = await this.uploadRepository.getUserEmailFromUploadId(_uploadId); - const dataProcessingAllowed = await this.paymentAPIService.checkEvent(userEmail); + const dataProcessingAllowed = await this.paymentAPIService.checkEvent({ + email: userEmail, + }); if ( dataProcessingAllowed && diff --git a/apps/api/src/app/shared/constants.ts b/apps/api/src/app/shared/constants.ts index b466c6fb7..f4a8bd86d 100644 --- a/apps/api/src/app/shared/constants.ts +++ b/apps/api/src/app/shared/constants.ts @@ -33,6 +33,9 @@ export const APIMessages = { COLUMN_KEY_DUPLICATED: 'Column with the same key already exists. Please provide a unique key.', ERROR_DURING_VALIDATION: 'Something went wrong while validating data. Data is not imported yet, but team is informed about issue. Please try again after sometime.', + ERROR_ACCESSING_FEATURE: { + IMAGE_UPLOAD: 'You do not have access to Image Upload Functionality.', + }, }; export const CONSTANTS = { diff --git a/apps/api/src/app/template/usecases/get-template-details/get-template-details.usecase.ts b/apps/api/src/app/template/usecases/get-template-details/get-template-details.usecase.ts index 7c7bf2658..c40fd838b 100644 --- a/apps/api/src/app/template/usecases/get-template-details/get-template-details.usecase.ts +++ b/apps/api/src/app/template/usecases/get-template-details/get-template-details.usecase.ts @@ -10,7 +10,7 @@ export class GetTemplateDetails { async execute(_id: string): Promise { const template = await this.templateRepository.findOne( { _id }, - '_projectId name sampleFileUrl _id totalUploads totalInvalidRecords totalRecords' + '_projectId name sampleFileUrl _id totalUploads totalInvalidRecords totalRecords mode' ); if (!template) { throw new DocumentNotFoundException('Template', _id); diff --git a/apps/api/src/migrations/update-mode/update-mode.migrations.ts b/apps/api/src/migrations/update-mode/update-mode.migrations.ts new file mode 100644 index 000000000..964a5c935 --- /dev/null +++ b/apps/api/src/migrations/update-mode/update-mode.migrations.ts @@ -0,0 +1,38 @@ +import '../../config'; +import { AppModule } from '../../app.module'; + +import { NestFactory } from '@nestjs/core'; +import { TemplateRepository } from '@impler/dal'; + +export async function run() { + console.log('start migration - registering payment users'); + + let app; + try { + // Initialize the MongoDB connection + app = await NestFactory.create(AppModule, { + logger: false, + }); + + const templateRepository = new TemplateRepository(); + + // Fetch all templates without the 'mode' field + const templatesWithoutMode = await templateRepository.find({ mode: { $exists: false } }); + console.log('Templates without mode:', templatesWithoutMode); + + const updateResult = await templateRepository.update({ mode: { $exists: false } }, { mode: 'manual', multi: true }); + + console.log('Updated templates:', updateResult); + + console.log('end migration - Adding manual mode to all templates users'); + } catch (error) { + console.error('An error occurred during the migration:', error); + } finally { + if (app) { + await app.close(); + } + process.exit(0); + } +} + +run(); diff --git a/apps/queue-manager/src/consumers/end-import.consumer.ts b/apps/queue-manager/src/consumers/end-import.consumer.ts index 99f38e89d..a28678035 100644 --- a/apps/queue-manager/src/consumers/end-import.consumer.ts +++ b/apps/queue-manager/src/consumers/end-import.consumer.ts @@ -18,7 +18,9 @@ export class EndImportConsumer extends BaseConsumer { await this.convertRecordsToJsonFile(data.uploadId); const userEmail = await this.uploadRepository.getUserEmailFromUploadId(data.uploadId); - const dataProcessingAllowed = await this.paymentAPIService.checkEvent(userEmail); + const dataProcessingAllowed = await this.paymentAPIService.checkEvent({ + email: userEmail, + }); if (dataProcessingAllowed) { if (data.destination === DestinationsEnum.WEBHOOK) { diff --git a/apps/web/components/imports/forms/ColumnForm.tsx b/apps/web/components/imports/forms/ColumnForm.tsx index b9fcd7725..6c0398ca7 100644 --- a/apps/web/components/imports/forms/ColumnForm.tsx +++ b/apps/web/components/imports/forms/ColumnForm.tsx @@ -1,3 +1,4 @@ +import { useEffect, useState } from 'react'; import { modals } from '@mantine/modals'; import { Controller, useForm } from 'react-hook-form'; import { @@ -8,20 +9,22 @@ import { SimpleGrid, Title, Group, + Flex, CloseButton, Select, useMantineColorScheme, - Flex, + SelectItem, } from '@mantine/core'; import { ColumnTypesEnum, DEFAULT_VALUES, IColumn } from '@impler/shared'; -import { colors, COLUMN_TYPES, DELIMITERS, MODAL_KEYS, MODAL_TITLES, DOCUMENTATION_REFERENCE_LINKS } from '@config'; +import { colors, DELIMITERS, MODAL_KEYS, MODAL_TITLES, DOCUMENTATION_REFERENCE_LINKS } from '@config'; import { Button } from '@ui/button'; import { Textarea } from '@ui/textarea'; import { Checkbox } from '@ui/checkbox'; import { MultiSelect } from '@ui/multi-select'; import { CustomSelect } from '@ui/custom-select'; +import { useSchema } from '@hooks/useSchema'; import TooltipLink from '@components/TooltipLink/TooltipLink'; interface ColumnFormProps { @@ -31,6 +34,8 @@ interface ColumnFormProps { } export function ColumnForm({ onSubmit, data, isLoading }: ColumnFormProps) { + const { getColumnTypes } = useSchema({ templateId: data?._templateId as string }); + const [columnTypes, setColumnType] = useState(getColumnTypes()); const { colorScheme } = useMantineColorScheme(); const { watch, @@ -48,6 +53,10 @@ export function ColumnForm({ onSubmit, data, isLoading }: ColumnFormProps) { modals.close(MODAL_KEYS.COLUMN_UPDATE); }; + useEffect(() => { + setColumnType(getColumnTypes()); + }, []); + return (
@@ -113,13 +122,8 @@ export function ColumnForm({ onSubmit, data, isLoading }: ColumnFormProps) { control={control} render={({ field: { value, onChange, onBlur } }) => (