diff --git a/apps/api/src/auth/__mocks__.ts b/apps/api/src/auth/__mocks__.ts new file mode 100644 index 00000000..f0c7b23b --- /dev/null +++ b/apps/api/src/auth/__mocks__.ts @@ -0,0 +1,49 @@ +import { KeycloakTokenParsed } from './keycloak' + +export const mockUser = { + exp: 1719866987, + iat: 1719866687, + jti: '607c488f-6e18-4455-8384-161cec4f1940', + iss: 'http://localhost:8180/auth/realms/webapp', + aud: 'account', + sub: '5795ea9e-ac11-436b-b97d-7b03dbd863f2', + typ: 'Bearer', + azp: 'jwt-headless', + session_state: 'def317ff-0043-4509-ade3-926dd155085e', + 'allowed-origins': ['*'], + realm_access: { roles: ['default-roles-webapp', 'offline_access', 'uma_authorization'] }, + resource_access: { + account: { roles: ['manage-account', 'manage-account-links', 'view-profile'] }, + }, + scope: 'openid profile email', + sid: 'def317ff-0043-4509-ade3-926dd155085e', + email_verified: 'true', + name: 'asdasd sdfsdfsdfs', + preferred_username: 'martbul01@gmail.com', + given_name: 'asdasd', + family_name: 'sdfsdfsdfs', + email: 'martbul01@gmail.com', +} as KeycloakTokenParsed + +export const mockUserAdmin = { + exp: 1719866987, + iat: 1719866687, + jti: '607c488f-6e18-4455-8384-161cec4f1940', + iss: 'http://localhost:8180/auth/realms/webapp', + aud: 'account', + sub: '5795ea9e-ac11-436b-b97d-7b03dbd863f2', + typ: 'Bearer', + azp: 'jwt-headless', + session_state: 'def317ff-0043-4509-ade3-926dd155085e', + 'allowed-origins': ['*'], + realm_access: { roles: ['default-roles-webapp', 'offline_access', 'uma_authorization'] }, + resource_access: { account: { roles: ['manage-account', 'account-view-supporters'] } }, + scope: 'openid profile email', + sid: 'def317ff-0043-4509-ade3-926dd155085e', + email_verified: 'true', + name: 'asdasd sdfsdfsdfs', + preferred_username: 'martbul01@gmail.com', + given_name: 'asdasd', + family_name: 'sdfsdfsdfs', + email: 'martbul01@gmail.com', +} as KeycloakTokenParsed diff --git a/apps/api/src/campaign-application/__mocks__/campaign-application-mocks.ts b/apps/api/src/campaign-application/__mocks__/campaign-application-mocks.ts new file mode 100644 index 00000000..cacc49f7 --- /dev/null +++ b/apps/api/src/campaign-application/__mocks__/campaign-application-mocks.ts @@ -0,0 +1,87 @@ +import { CampaignApplicationState, CampaignTypeCategory } from '@prisma/client' +import { CreateCampaignApplicationDto } from '../dto/create-campaign-application.dto' + +export const mockNewCampaignApplication = { + campaignName: 'Test Campaign', + organizerName: 'Test Organizer', + organizerEmail: 'testemail@gmail.com', + organizerPhone: '123456789', + beneficiary: 'Test beneficary', + organizerBeneficiaryRel: 'Test organizerBeneficiaryRel', + goal: 'Test goal', + history: 'Test history', + amount: '1000', + description: 'Test description', + campaignGuarantee: 'Test guarantee', + otherFinanceSources: 'Test otherFinanceSources', + otherNotes: 'Test otherNotes', + category: CampaignTypeCategory.medical, +} + +const dto: CreateCampaignApplicationDto = { + ...mockNewCampaignApplication, + acceptTermsAndConditions: true, + transparencyTermsAccepted: true, + personalInformationProcessingAccepted: true, + toEntity: new CreateCampaignApplicationDto().toEntity, +} + +export const mockCampaigns = [ + { + id: '1', + createdAt: new Date('2022-04-08T06:36:33.661Z'), + updatedAt: new Date('2022-04-08T06:36:33.662Z'), + description: 'Test description1', + organizerId: 'testOrganizerId1', + organizerName: 'Test Organizer1', + organizerEmail: 'organizer1@example.com', + beneficiary: 'test beneficary1', + organizerPhone: '123456789', + organizerBeneficiaryRel: 'Test Relation1', + campaignName: 'Test Campaign1', + goal: 'Test Goal1', + history: 'test history1', + amount: '1000', + campaignGuarantee: 'test campaignGuarantee1', + otherFinanceSources: 'test otherFinanceSources1', + otherNotes: 'test otherNotes1', + state: CampaignApplicationState.review, + category: CampaignTypeCategory.medical, + ticketURL: 'testsodifhso1', + archived: false, + }, + { + id: '2', + createdAt: new Date('2022-04-08T06:36:33.661Z'), + updatedAt: new Date('2022-04-08T06:36:33.662Z'), + description: 'Test description2', + organizerId: 'testOrganizerId2', + organizerName: 'Test Organizer2', + organizerEmail: 'organizer2@example.com', + beneficiary: 'test beneficary2', + organizerPhone: '123456789', + organizerBeneficiaryRel: 'Test Relation2', + campaignName: 'Test Campaign2', + goal: 'Test Goal2', + history: 'test history2', + amount: '1000', + campaignGuarantee: 'test campaignGuarantee2', + otherFinanceSources: 'test otherFinanceSources2', + otherNotes: 'test otherNotes2', + state: CampaignApplicationState.review, + category: CampaignTypeCategory.medical, + ticketURL: 'testsodifhso2', + archived: false, + }, +] + +export const mockCreatedCampaignApplication = { + id: 'mockCampaignApplicationId', + createdAt: new Date('2022-04-08T06:36:33.661Z'), + updatedAt: new Date('2022-04-08T06:36:33.662Z'), + ...mockNewCampaignApplication, + organizerId: 'mockOrganizerId', + state: CampaignApplicationState.review, + ticketURL: null, + archived: false, +} diff --git a/apps/api/src/campaign-application/campaign-application.controller.spec.ts b/apps/api/src/campaign-application/campaign-application.controller.spec.ts index 21244b12..6fa91b70 100644 --- a/apps/api/src/campaign-application/campaign-application.controller.spec.ts +++ b/apps/api/src/campaign-application/campaign-application.controller.spec.ts @@ -1,22 +1,37 @@ import { Test, TestingModule } from '@nestjs/testing' import { CampaignApplicationController } from './campaign-application.controller' import { CampaignApplicationService } from './campaign-application.service' -import { SpyOf, autoSpy } from '@podkrepi-bg/testing' -import { ForbiddenException } from '@nestjs/common' -import { KeycloakTokenParsed, isAdmin } from '../auth/keycloak' - -jest.mock('../auth/keycloak') +import { autoSpy } from '@podkrepi-bg/testing' +import { CreateCampaignApplicationDto } from './dto/create-campaign-application.dto' +import { KeycloakTokenParsed } from '../auth/keycloak' +import { ForbiddenException, NotFoundException } from '@nestjs/common' +import { PersonService } from '../person/person.service' +import { mockUser, mockUserAdmin } from './../auth/__mocks__' +import { mockNewCampaignApplication } from './__mocks__/campaign-application-mocks' describe('CampaignApplicationController', () => { let controller: CampaignApplicationController - let service: SpyOf + let service: CampaignApplicationService + let personService: PersonService + + const mockCreateNewCampaignApplication = { + ...mockNewCampaignApplication, + acceptTermsAndConditions: true, + transparencyTermsAccepted: true, + personalInformationProcessingAccepted: true, + toEntity: new CreateCampaignApplicationDto().toEntity, + } as CreateCampaignApplicationDto beforeEach(async () => { service = autoSpy(CampaignApplicationService) + personService = autoSpy(PersonService) const module: TestingModule = await Test.createTestingModule({ controllers: [CampaignApplicationController], - providers: [{ provide: CampaignApplicationService, useValue: service }], + providers: [ + { provide: CampaignApplicationService, useValue: service }, + { provide: PersonService, useValue: personService }, + ], }).compile() controller = module.get(CampaignApplicationController) @@ -26,63 +41,63 @@ describe('CampaignApplicationController', () => { expect(controller).toBeDefined() }) - it('when create called it should delegate to the service create', () => { - // arrange - // act - controller.create({ - acceptTermsAndConditions: true, - personalInformationProcessingAccepted: true, - transparencyTermsAccepted: true, - title: 'new ', - toEntity: jest.fn(), - }) - - // assert - expect(service.create).toHaveBeenCalledWith({ - acceptTermsAndConditions: true, - personalInformationProcessingAccepted: true, - transparencyTermsAccepted: true, - title: 'new ', - toEntity: expect.any(Function), - }) + it('when create called it should delegate to the service create', async () => { + // Arrange + jest.spyOn(personService, 'findOneByKeycloakId').mockResolvedValue(mockUser) + + // Act + await controller.create(mockCreateNewCampaignApplication, mockUser) + + // Assert + expect(service.create).toHaveBeenCalledWith(mockCreateNewCampaignApplication, mockUser) + }) + + it('when create called with wrong user it should throw NotFoundException', async () => { + jest.spyOn(personService, 'findOneByKeycloakId').mockResolvedValue(null) + + // Act & Assert + await expect(controller.create(mockCreateNewCampaignApplication, mockUser)).rejects.toThrow( + NotFoundException, + ) }) it('when findAll called by a non-admin user it should throw a ForbiddenException', () => { - // arrange + jest.mock('../auth/keycloak', () => ({ + isAdmin: jest.fn().mockReturnValue(false), + })) + + // Arrange const user = { sub: 'non-admin', 'allowed-origins': ['test'] } as KeycloakTokenParsed - ;(isAdmin as jest.Mock).mockReturnValue(false) - // act & assert + // Act & Assert expect(() => controller.findAll(user)).toThrow(ForbiddenException) }) - it('when findAll called by an admin user it should delegate to the service findAll', () => { - // arrange - const user = { sub: 'admin', 'allowed-origins': ['test'] } as KeycloakTokenParsed - ;(isAdmin as jest.Mock).mockReturnValue(true) - - // act - controller.findAll(user) - - // assert - expect(service.findAll).toHaveBeenCalledWith() + jest.mock('../auth/keycloak', () => ({ + isAdmin: jest.fn().mockImplementation((user: KeycloakTokenParsed) => { + return user.resource_access?.account?.roles.includes('account-view-supporters') + }), + })) + + // Act & Assert + expect(() => controller.findAll(mockUserAdmin)).not.toThrow(ForbiddenException) + controller.findAll(mockUserAdmin) + expect(service.findAll).toHaveBeenCalled() }) it('when findOne called it should delegate to the service findOne', () => { - // arrange - // act + // Act controller.findOne('id') - // assert + // Assert expect(service.findOne).toHaveBeenCalledWith('id') }) it('when update called it should delegate to the service update', () => { - // arrange - // act + // Act controller.update('1', {}, { sub: 'test', 'allowed-origins': ['test'] }) - // assert + // Assert expect(service.update).toHaveBeenCalledWith('1', {}) }) }) diff --git a/apps/api/src/campaign-application/campaign-application.controller.ts b/apps/api/src/campaign-application/campaign-application.controller.ts index 8a215172..3505bdf0 100644 --- a/apps/api/src/campaign-application/campaign-application.controller.ts +++ b/apps/api/src/campaign-application/campaign-application.controller.ts @@ -1,21 +1,44 @@ -import { Controller, Get, Post, Body, Patch, Param, ForbiddenException } from '@nestjs/common' +import { + Controller, + Get, + Post, + Body, + Patch, + Param, + ForbiddenException, + NotFoundException, + Logger, +} from '@nestjs/common' import { CampaignApplicationService } from './campaign-application.service' import { CreateCampaignApplicationDto } from './dto/create-campaign-application.dto' import { UpdateCampaignApplicationDto } from './dto/update-campaign-application.dto' import { ApiTags } from '@nestjs/swagger' -import { AuthenticatedUser, Public, RoleMatchingMode, Roles } from 'nest-keycloak-connect' +import { AuthenticatedUser, RoleMatchingMode, Roles } from 'nest-keycloak-connect' import { RealmViewSupporters, ViewSupporters } from '@podkrepi-bg/podkrepi-types' import { KeycloakTokenParsed, isAdmin } from '../auth/keycloak' +import { PersonService } from '../person/person.service' @ApiTags('campaign-application') @Controller('campaign-application') export class CampaignApplicationController { - constructor(private readonly campaignApplicationService: CampaignApplicationService) {} + constructor( + private readonly campaignApplicationService: CampaignApplicationService, + private readonly personService: PersonService, + ) {} @Post('create') - @Public() - create(@Body() createCampaignApplicationDto: CreateCampaignApplicationDto) { - return this.campaignApplicationService.create(createCampaignApplicationDto) + async create( + @Body() createCampaignApplicationDto: CreateCampaignApplicationDto, + @AuthenticatedUser() user: KeycloakTokenParsed, + ) { + const person = await this.personService.findOneByKeycloakId(user.sub) + + if (!person) { + Logger.error('No person found in database') + throw new NotFoundException('No person found in database') + } + + return this.campaignApplicationService.create(createCampaignApplicationDto, person) } @Get('list') diff --git a/apps/api/src/campaign-application/campaign-application.module.ts b/apps/api/src/campaign-application/campaign-application.module.ts index 0c421d19..92c20c27 100644 --- a/apps/api/src/campaign-application/campaign-application.module.ts +++ b/apps/api/src/campaign-application/campaign-application.module.ts @@ -2,8 +2,10 @@ import { Module } from '@nestjs/common' import { CampaignApplicationService } from './campaign-application.service' import { CampaignApplicationController } from './campaign-application.controller' import { PrismaModule } from '../prisma/prisma.module' +import { PersonModule } from '../person/person.module' +import { OrganizerModule } from '../organizer/organizer.module' @Module({ - imports: [PrismaModule], + imports: [PrismaModule, PersonModule, OrganizerModule], controllers: [CampaignApplicationController], providers: [CampaignApplicationService], }) diff --git a/apps/api/src/campaign-application/campaign-application.service.spec.ts b/apps/api/src/campaign-application/campaign-application.service.spec.ts index 11948680..9c5123ea 100644 --- a/apps/api/src/campaign-application/campaign-application.service.spec.ts +++ b/apps/api/src/campaign-application/campaign-application.service.spec.ts @@ -2,13 +2,34 @@ import { Test, TestingModule } from '@nestjs/testing' import { CampaignApplicationService } from './campaign-application.service' import { CreateCampaignApplicationDto } from './dto/create-campaign-application.dto' import { BadRequestException } from '@nestjs/common' -import { CampaignApplicationState, CampaignTypeCategory } from '@prisma/client' +import { CampaignApplicationState, CampaignTypeCategory, Person } from '@prisma/client' import { prismaMock, MockPrismaService } from '../prisma/prisma-client.mock' import { EmailService } from '../email/email.service' +import { OrganizerService } from '../organizer/organizer.service' +import { personMock } from '../person/__mock__/personMock' +import { + mockCampaigns, + mockCreatedCampaignApplication, + mockNewCampaignApplication, +} from './__mocks__/campaign-application-mocks' describe('CampaignApplicationService', () => { let service: CampaignApplicationService + const mockPerson = { + ...personMock, + company: null, + beneficiaries: [], + organizer: { id: 'ffdbcc41-85ec-476c-9e59-0662f3b433af' }, + } as Person + + const mockOrganizerService = { + create: jest.fn().mockResolvedValue({ + id: 'mockOrganizerId', + personId: mockPerson.id, + }), + } + beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ @@ -20,131 +41,117 @@ describe('CampaignApplicationService', () => { sendFromTemplate: jest.fn(() => true), }, }, + MockPrismaService, + { + provide: OrganizerService, + useValue: mockOrganizerService, + }, ], }).compile() service = module.get(CampaignApplicationService) }) - it('should be defined', () => { expect(service).toBeDefined() }) describe('createNewApplication', () => { - const baseDto = { - campaignName: 'Test Campaign', - organizerName: 'Test Organizer', - organizerEmail: 'organizer@example.com', - organizerPhone: '123456789', - beneficiary: 'Test Beneficiary', - organizerBeneficiaryRel: 'Test Relation', - goal: 'Test Goal', - amount: '1000', - toEntity: jest.fn(), - } - - it('should throw an error if acceptTermsAndConditions is false', () => { + it('should throw an error if acceptTermsAndConditions is false', async () => { const dto: CreateCampaignApplicationDto = { - ...baseDto, + ...mockNewCampaignApplication, acceptTermsAndConditions: false, transparencyTermsAccepted: true, personalInformationProcessingAccepted: true, + toEntity: new CreateCampaignApplicationDto().toEntity, } - expect(() => service.create(dto)).toThrow( + await expect(service.create(dto, mockPerson)).rejects.toThrow( new BadRequestException('All agreements must be checked'), ) }) - it('should throw an error if transparencyTermsAccepted is false', () => { + it('should throw an error if transparencyTermsAccepted is false', async () => { const dto: CreateCampaignApplicationDto = { - ...baseDto, + ...mockNewCampaignApplication, acceptTermsAndConditions: true, transparencyTermsAccepted: false, personalInformationProcessingAccepted: true, + toEntity: new CreateCampaignApplicationDto().toEntity, } - expect(() => service.create(dto)).toThrow( + await expect(service.create(dto, mockPerson)).rejects.toThrow( new BadRequestException('All agreements must be checked'), ) }) - it('should throw an error if personalInformationProcessingAccepted is false', () => { + it('should throw an error if personalInformationProcessingAccepted is false', async () => { const dto: CreateCampaignApplicationDto = { - ...baseDto, + ...mockNewCampaignApplication, acceptTermsAndConditions: true, transparencyTermsAccepted: true, personalInformationProcessingAccepted: false, + toEntity: new CreateCampaignApplicationDto().toEntity, } - expect(() => service.create(dto)).toThrow( + await expect(service.create(dto, mockPerson)).rejects.toThrow( new BadRequestException('All agreements must be checked'), ) }) - it('should add a new campaign-application if all agreements are true', () => { + it('should add a new campaign-application if all agreements are true', async () => { const dto: CreateCampaignApplicationDto = { - ...baseDto, + ...mockNewCampaignApplication, acceptTermsAndConditions: true, transparencyTermsAccepted: true, personalInformationProcessingAccepted: true, + toEntity: new CreateCampaignApplicationDto().toEntity, } - expect(service.create(dto)).toBe('This action adds a new campaignApplication') - }) - }) + const mockOrganizerId = 'mockOrganizerId' + jest.spyOn(mockOrganizerService, 'create').mockResolvedValue({ + id: mockOrganizerId, + personId: mockPerson.id, + }) - describe('findAll', () => { - it('should return an array of campaign-applications', async () => { - const mockCampaigns = [ - { - id: 'testId', - createdAt: new Date('2022-04-08T06:36:33.661Z'), - updatedAt: new Date('2022-04-08T06:36:33.662Z'), - description: 'Test description', - organizerId: 'testOrganizerId1', - organizerName: 'Test Organizer1', - organizerEmail: 'organizer@example.com', - beneficiary: 'test beneficary', - organizerPhone: '123456789', - organizerBeneficiaryRel: 'Test Relation', + jest + .spyOn(prismaMock.campaignApplication, 'create') + .mockResolvedValue(mockCreatedCampaignApplication) + + const result = await service.create(dto, mockPerson) + + expect(result).toEqual(mockCreatedCampaignApplication) + + expect(mockOrganizerService.create).toHaveBeenCalledWith({ + personId: mockPerson.id, + }) + + expect(prismaMock.campaignApplication.create).toHaveBeenCalledWith({ + data: { campaignName: 'Test Campaign', - goal: 'Test Goal', - history: 'test history', + organizerName: 'Test Organizer', + organizerEmail: 'testemail@gmail.com', + organizerPhone: '123456789', + beneficiary: 'Test beneficary', + organizerBeneficiaryRel: 'Test organizerBeneficiaryRel', + goal: 'Test goal', + history: 'Test history', amount: '1000', - campaignGuarantee: 'test campaignGuarantee', - otherFinanceSources: 'test otherFinanceSources', - otherNotes: 'test otherNotes', - state: CampaignApplicationState.review, - category: CampaignTypeCategory.medical, - ticketURL: 'testsodifhso', - archived: false, - }, - { - id: 'testId2', - createdAt: new Date('2022-04-08T06:36:33.661Z'), - updatedAt: new Date('2022-04-08T06:36:33.662Z'), description: 'Test description', - organizerId: 'testOrganizerId2', - organizerName: 'Test Organizer2', - organizerEmail: 'organizer2@example.com', - beneficiary: 'test beneficary2', - organizerPhone: '123456789', - organizerBeneficiaryRel: 'Test Relation2', - campaignName: 'Test Campaign2', - goal: 'Test Goal2', - history: 'test history2', - amount: '2000', - campaignGuarantee: 'test campaignGuarantee2', - otherFinanceSources: 'test otherFinanceSources2', - otherNotes: 'test otherNotes2', - state: CampaignApplicationState.review, + campaignGuarantee: 'Test guarantee', + otherFinanceSources: 'Test otherFinanceSources', + otherNotes: 'Test otherNotes', category: CampaignTypeCategory.medical, - ticketURL: 'testsodifhso2', - archived: false, + organizerId: mockOrganizerId, }, - ] + }) + + expect(mockOrganizerService.create).toHaveBeenCalledTimes(1) + expect(prismaMock.campaignApplication.create).toHaveBeenCalledTimes(1) + }) + }) + describe('findAll', () => { + it('should return an array of campaign-applications', async () => { prismaMock.campaignApplication.findMany.mockResolvedValue(mockCampaigns) const result = await service.findAll() @@ -170,4 +177,4 @@ describe('CampaignApplicationService', () => { expect(prismaMock.campaignApplication.findMany).toHaveBeenCalledTimes(1) }) }) -}) \ No newline at end of file +}) diff --git a/apps/api/src/campaign-application/campaign-application.service.ts b/apps/api/src/campaign-application/campaign-application.service.ts index 9b4a0bcc..2952e469 100644 --- a/apps/api/src/campaign-application/campaign-application.service.ts +++ b/apps/api/src/campaign-application/campaign-application.service.ts @@ -1,24 +1,64 @@ -import { BadRequestException, HttpException, HttpStatus, Injectable } from '@nestjs/common' +import { BadRequestException, Injectable, Logger } from '@nestjs/common' import { CreateCampaignApplicationDto } from './dto/create-campaign-application.dto' import { UpdateCampaignApplicationDto } from './dto/update-campaign-application.dto' import { PrismaService } from '../prisma/prisma.service' +import { OrganizerService } from '../organizer/organizer.service' +import { Person } from '@prisma/client' @Injectable() export class CampaignApplicationService { - constructor(private prisma: PrismaService) {} + constructor(private prisma: PrismaService, private organizerService: OrganizerService) {} async getCampaignByIdWithPersonIds(id: string): Promise { throw new Error('Method not implemented.') } - create(createCampaignApplicationDto: CreateCampaignApplicationDto) { - if ( - !createCampaignApplicationDto.acceptTermsAndConditions || - !createCampaignApplicationDto.transparencyTermsAccepted || - !createCampaignApplicationDto.personalInformationProcessingAccepted - ) { - throw new BadRequestException('All agreements must be checked') + async create(createCampaignApplicationDto: CreateCampaignApplicationDto, person: Person) { + try { + if ( + createCampaignApplicationDto.acceptTermsAndConditions === false || + createCampaignApplicationDto.transparencyTermsAccepted === false || + createCampaignApplicationDto.personalInformationProcessingAccepted === false + ) { + throw new BadRequestException('All agreements must be checked') + } + + let organizer = await this.prisma.organizer.findUnique({ + where: { personId: person.id }, + }) + + if (!organizer) { + organizer = await this.organizerService.create({ + personId: person.id, + }) + } + + const campaingApplicationData = { + campaignName: createCampaignApplicationDto.campaignName, + organizerName: createCampaignApplicationDto.organizerName, + organizerEmail: createCampaignApplicationDto.organizerEmail, + organizerPhone: createCampaignApplicationDto.organizerPhone, + beneficiary: createCampaignApplicationDto.beneficiary, + organizerBeneficiaryRel: createCampaignApplicationDto.organizerBeneficiaryRel, + goal: createCampaignApplicationDto.goal, + history: createCampaignApplicationDto.history, + amount: createCampaignApplicationDto.amount, + description: createCampaignApplicationDto.description, + campaignGuarantee: createCampaignApplicationDto.campaignGuarantee, + otherFinanceSources: createCampaignApplicationDto.otherFinanceSources, + otherNotes: createCampaignApplicationDto.otherNotes, + category: createCampaignApplicationDto.category, + organizerId: organizer.id, + } + + const newCampaignApplication = await this.prisma.campaignApplication.create({ + data: campaingApplicationData, + }) + + return newCampaignApplication + } catch (error) { + Logger.error('Error in create():', error) + throw error } - return 'This action adds a new campaignApplication' } findAll() { diff --git a/apps/api/src/campaign-application/dto/create-campaign-application.dto.ts b/apps/api/src/campaign-application/dto/create-campaign-application.dto.ts index 43ad8f02..20a84bce 100644 --- a/apps/api/src/campaign-application/dto/create-campaign-application.dto.ts +++ b/apps/api/src/campaign-application/dto/create-campaign-application.dto.ts @@ -1,13 +1,14 @@ import { ApiProperty } from '@nestjs/swagger' import { CampaignTypeCategory, Prisma } from '@prisma/client' import { Expose } from 'class-transformer' -import { IsBoolean, IsOptional, IsString } from 'class-validator' +import { IsBoolean, IsNotEmpty, IsOptional, IsString } from 'class-validator' @Expose() export class CreateCampaignApplicationDto { /** * What would the campaign be called. ('Help Vesko' or 'Castrate Plovdiv Cats') */ + @IsNotEmpty() @ApiProperty() @Expose() @IsString() @@ -32,36 +33,42 @@ export class CreateCampaignApplicationDto { personalInformationProcessingAccepted: boolean /** Who is organizing this campaign */ + @IsNotEmpty() @ApiProperty() @Expose() @IsString() organizerName: string /** Contact Email to use for the Campaign Application process i.e. if more documents or other info are requested */ + @IsNotEmpty() @ApiProperty() @Expose() @IsString() organizerEmail: string /** Contact Email to use for the Campaign Application process i.e. if more documents or other info are requested */ + @IsNotEmpty() @ApiProperty() @Expose() @IsString() organizerPhone: string /** Who will benefit and use the collected donations */ + @IsNotEmpty() @ApiProperty() @Expose() @IsString() beneficiary: string /** What is the relationship between the Organizer and the Beneficiary ('They're my elderly relative and I'm helping with the internet-computer stuff') */ + @IsNotEmpty() @ApiProperty() @Expose() @IsString() organizerBeneficiaryRel: string /** What is the result that the collected donations will help achieve */ + @IsNotEmpty() @ApiProperty() @Expose() @IsString() @@ -75,6 +82,7 @@ export class CreateCampaignApplicationDto { history?: string /** How much would the campaign be looking for i.e '10000lv or 5000 Eur or $5000' */ + @IsNotEmpty() @ApiProperty() @Expose() @IsString() diff --git a/apps/api/src/domain/generated/campaignApplication/dto/connect-campaignApplication.dto.ts b/apps/api/src/domain/generated/campaignApplication/dto/connect-campaignApplication.dto.ts index 076c452d..e938b528 100644 --- a/apps/api/src/domain/generated/campaignApplication/dto/connect-campaignApplication.dto.ts +++ b/apps/api/src/domain/generated/campaignApplication/dto/connect-campaignApplication.dto.ts @@ -1,4 +1,3 @@ export class ConnectCampaignApplicationDto { - id?: string - organizerEmail?: string + id: string } diff --git a/migrations/20240701155306_remove_organizer_email_unique_constraint/migration.sql b/migrations/20240701155306_remove_organizer_email_unique_constraint/migration.sql new file mode 100644 index 00000000..0525b6cc --- /dev/null +++ b/migrations/20240701155306_remove_organizer_email_unique_constraint/migration.sql @@ -0,0 +1,2 @@ +-- DropIndex +DROP INDEX "campaign_applications_organizer_email_key"; diff --git a/podkrepi.dbml b/podkrepi.dbml index 570b5b70..f1b8bcdb 100644 --- a/podkrepi.dbml +++ b/podkrepi.dbml @@ -583,7 +583,7 @@ Table campaign_applications { organizerId String [not null] organizer organizers organizerName String [not null] - organizerEmail String [unique] + organizerEmail String organizerPhone String beneficiary String [not null] organizerBeneficiaryRel String [not null] diff --git a/schema.prisma b/schema.prisma index 131271ca..26a50c97 100644 --- a/schema.prisma +++ b/schema.prisma @@ -1017,7 +1017,7 @@ model CampaignApplication { organizerId String @map("organizer_id") @db.Uuid organizer Organizer? @relation(fields: [organizerId], references: [id]) organizerName String @map("organizer_name") @db.VarChar(200) - organizerEmail String? @unique @map("organizer_email") @db.Citext + organizerEmail String? @map("organizer_email") @db.Citext organizerPhone String? @map("organizer_phone") @db.VarChar(50) // will create a beneficiary after approve of campaign application beneficiary String @db.VarChar(1500)