Skip to content

Commit

Permalink
Merge branch 'podkrepi-bg:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
quantum-grit authored Oct 1, 2023
2 parents 1403b52 + 15467e4 commit 6a225e9
Show file tree
Hide file tree
Showing 19 changed files with 138 additions and 178 deletions.
16 changes: 16 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "build",
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": [],
"label": "npm: build",
"detail": "yarn build"
}
]
}
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,10 @@ This will start the following services in your local docker:

- Local Postgres DB on default port 5432 for your personal development
- Local Keycloak Identity server Admin UI on <http://localhost:8180> with config coming from `./manifests/keycloak/config`:
- Keycloak Admin User: `admin` with pass: `admin`
- Podkrepi realm users: [email protected], [email protected], [email protected], all with pass: `$ecurePa33`
- Keycloak Local Admin User: `admin` with pass: `admin`
- Podkrepi Local Admin users:
- [email protected], [email protected], [email protected],
- all with pass: `$ecurePa33`

## Initialize the Database with Prisma Migration scripts

Expand Down
22 changes: 12 additions & 10 deletions apps/api/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,9 +203,9 @@ export class AuthService {
await this.marketingNotificationsService.provider.addContactsToList({
contacts: [
{
email: person.email,
first_name: person?.firstName || '',
last_name: person?.lastName || '',
email: registerDto.email,
first_name: registerDto.firstName,
last_name: registerDto.lastName,
},
],
list_ids: [mainList],
Expand Down Expand Up @@ -327,13 +327,15 @@ export class AuthService {

async sendMailForPasswordChange(forgotPasswordDto: ForgottenPasswordEmailDto) {
const stage = this.config.get<string>('APP_ENV') === 'development' ? 'APP_URL_LOCAL' : 'APP_URL'
const user = await this.prismaService.person.findFirst({
const person = await this.prismaService.person.findFirst({
where: { email: forgotPasswordDto.email },
})
if (!user) {

if (!person || !person.email) {
throw new NotFoundException('Invalid email')
}
const payload = { username: user.email, sub: user.keycloakId }

const payload = { username: person.email, sub: person.keycloakId }
const jtwSecret = process.env.JWT_SECRET_KEY
const access_token = this.jwtService.sign(payload, {
secret: jtwSecret,
Expand All @@ -342,12 +344,12 @@ export class AuthService {
const appUrl = this.config.get<string>(stage)
const link = `${appUrl}/change-password?token=${access_token}`
const profile = {
email: user.email,
firstName: user.firstName,
lastName: user.lastName,
email: person.email,
firstName: person.firstName,
lastName: person.lastName,
link: link,
}
const userEmail = { to: [user.email] }
const userEmail = { to: [person.email] }
const mail = new ForgottenPasswordMailDto(profile)
await this.sendEmail.sendFromTemplate(mail, userEmail, {
//Allow users to receive the mail, regardles of unsubscribes
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export class CreatePersonDto {
firstName: string
lastName: string
email: string
email?: string
phone?: string
company?: string
newsletter?: boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export class Person {
id: string
firstName: string
lastName: string
email: string
email: string | null
phone: string | null
company: string | null
createdAt: Date
Expand Down
16 changes: 2 additions & 14 deletions apps/api/src/donations/donations.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
Res,
Inject,
forwardRef,
NotFoundException,
} from '@nestjs/common'
import { ApiQuery, ApiTags } from '@nestjs/swagger'
import { DonationStatus } from '@prisma/client'
Expand Down Expand Up @@ -108,9 +107,7 @@ export class DonationsController {

@Get('user-donations')
async userDonations(@AuthenticatedUser() user: KeycloakTokenParsed) {
const person = await this.personService.findOneByKeycloakId(user.sub)
if (!person) throw new NotFoundException('User was not found')
return await this.donationsService.getDonationsByUser(user.sub, person.email)
return await this.donationsService.getDonationsByUser(user.sub, user.email)
}

@Get('money')
Expand Down Expand Up @@ -178,16 +175,7 @@ export class DonationsController {

@Get('user/:id')
async userDonationById(@Param('id') id: string, @AuthenticatedUser() user: KeycloakTokenParsed) {
const person = await this.personService.findOneByKeycloakId(user.sub)
if (!person) throw new NotFoundException('User was not found')
const donation = await this.donationsService.getUserDonationById(id, user.sub, person.email)
return {
...donation,
person: {
firstName: person.firstName,
lastName: person.lastName,
},
}
return await this.donationsService.getUserDonationById(id, user.sub, user.email)
}

@Post('payment-intent')
Expand Down
6 changes: 3 additions & 3 deletions apps/api/src/donations/donations.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ export class DonationsService {
async getUserDonationById(
id: string,
keycloakId: string,
email: string,
email?: string,
): Promise<(Donation & { person: Person | null }) | null> {
return await this.prisma.donation.findFirst({
where: {
Expand Down Expand Up @@ -564,7 +564,7 @@ export class DonationsService {

const status = updatePaymentDto.status || currentDonation.status
let donorId = currentDonation.personId
let billingEmail = ''
let billingEmail: string | null = ''
if (
(updatePaymentDto.targetPersonId &&
currentDonation.personId !== updatePaymentDto.targetPersonId) ||
Expand Down Expand Up @@ -636,7 +636,7 @@ export class DonationsService {
}
}

async getDonationsByUser(keycloakId: string, email: string) {
async getDonationsByUser(keycloakId: string, email?: string) {
const donations = await this.prisma.donation.findMany({
where: {
OR: [{ billingEmail: email }, { person: { keycloakId } }],
Expand Down
33 changes: 22 additions & 11 deletions apps/api/src/expenses/expenses.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import { Test, TestingModule } from '@nestjs/testing'
import { ExpensesController } from './expenses.controller'
import { ExpensesService } from './expenses.service'
import { MockPrismaService, prismaMock } from '../prisma/prisma-client.mock'
import { ExpenseStatus, ExpenseType, Currency } from '@prisma/client'
import { ExpenseStatus, ExpenseType, Currency, Person, Campaign } from '@prisma/client'
import { mockReset } from 'jest-mock-extended'
import { CreateExpenseDto } from './dto/create-expense.dto'
import { UpdateExpenseDto } from './dto/update-expense.dto'
import { S3Service } from '../s3/s3.service'
import { KeycloakTokenParsed } from '../auth/keycloak'

const mockData = [
{
Expand Down Expand Up @@ -69,16 +70,16 @@ describe('ExpensesController', () => {
const campaign = {}
const user = { sub: '00000000-0000-0000-0000-000000000013' }

prismaMock.person.findFirst.mockResolvedValue(person)
prismaMock.campaign.findFirst.mockResolvedValue(campaign)
prismaMock.person.findFirst.mockResolvedValue(person as Person)
prismaMock.campaign.findFirst.mockResolvedValue(campaign as Campaign)
prismaMock.expense.create.mockResolvedValue(expense)
prismaMock.vault.update.mockResolvedValue(vault)
prismaMock.vault.findFirst.mockResolvedValue(vault)
prismaMock.$transaction.mockResolvedValue([expense, vault])

const createDto: CreateExpenseDto = { ...expense }

const result = await controller.create(user, createDto, [])
const result = await controller.create(user as KeycloakTokenParsed, createDto)

expect(result).toEqual(expense)
expect(prismaMock.expense.create).toHaveBeenCalledWith({ data: createDto })
Expand Down Expand Up @@ -132,8 +133,8 @@ describe('ExpensesController', () => {

const campaign = {}

prismaMock.person.findFirst.mockResolvedValue(person)
prismaMock.campaign.findFirst.mockResolvedValue(campaign)
prismaMock.person.findFirst.mockResolvedValue(person as Person)
prismaMock.campaign.findFirst.mockResolvedValue(campaign as Campaign)
prismaMock.vault.findFirstOrThrow.mockResolvedValue(vault)
prismaMock.expense.findFirstOrThrow.mockResolvedValue(expense)
prismaMock.vault.update.mockResolvedValue(vault)
Expand All @@ -147,7 +148,7 @@ describe('ExpensesController', () => {
}

// act
const result = await controller.update(user, expense.id, updateDto)
const result = await controller.update(user as KeycloakTokenParsed, expense.id, updateDto)

// assert
expect(result).toEqual(expense)
Expand Down Expand Up @@ -193,7 +194,9 @@ describe('ExpensesController', () => {
vaultId: vault.id,
}

await expect(controller.update(user, expense.id, updateDto)).rejects.toThrow()
await expect(
controller.update(user as KeycloakTokenParsed, expense.id, updateDto),
).rejects.toThrow()
//expect an exception
expect(prismaMock.expense.update).not.toHaveBeenCalled()
})
Expand Down Expand Up @@ -233,15 +236,23 @@ describe('ExpensesController', () => {
}

// assert
await expect(controller.update(user, approvedExpense.id, updateDto)).rejects.toThrow()
await expect(controller.update(user, cancelledExpense.id, updateDto)).rejects.toThrow()
await expect(
controller.update(user as KeycloakTokenParsed, approvedExpense.id, updateDto),
).rejects.toThrow()
await expect(
controller.update(user as KeycloakTokenParsed, cancelledExpense.id, updateDto),
).rejects.toThrow()
expect(prismaMock.expense.update).not.toHaveBeenCalled()
expect(prismaMock.vault.update).not.toHaveBeenCalled()
})

it('should not update an expense, when its vault is being changed', async () => {
const expense = mockData[0]

const user: KeycloakTokenParsed = {
sub: '00000000-0000-0000-0000-000000000012',
} as KeycloakTokenParsed

const vault = {
id: '00000000-0000-0000-0000-000000000016',
name: 'vault1',
Expand All @@ -262,7 +273,7 @@ describe('ExpensesController', () => {
}

// assert
await expect(controller.update(expense.id, updateDto)).rejects.toThrow()
await expect(controller.update(user, expense.id, updateDto)).rejects.toThrow()
expect(prismaMock.expense.update).not.toHaveBeenCalled()
expect(prismaMock.vault.update).not.toHaveBeenCalled()
})
Expand Down
4 changes: 2 additions & 2 deletions apps/api/src/notifications/notifications.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ export class MarketingNotificationsService {
return new BadRequestException('Failed to get user')
}

if (!userInfo) return new BadRequestException('User not found')
if (!userInfo || !userInfo.email) return new BadRequestException('User not found')

// Already unsubscribed
if (!userInfo.newsletter) return { email: userInfo.email, subscribed: false }
Expand Down Expand Up @@ -306,7 +306,7 @@ export class MarketingNotificationsService {
return new BadRequestException('Failed to get user')
}

if (!userInfo) return new BadRequestException('User not found')
if (!userInfo || !userInfo.email) return new BadRequestException('User not found')

// Already subscribed
if (userInfo.newsletter) return { email: userInfo.email, subscribed: true }
Expand Down
3 changes: 2 additions & 1 deletion apps/api/src/person/dto/create-person.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ export class CreatePersonDto {
@ApiProperty()
@Expose()
@IsEmail()
email: string
@IsOptional()
email?: string

@ApiProperty()
@Expose()
Expand Down
6 changes: 3 additions & 3 deletions apps/api/src/person/person.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Injectable, Logger } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'
import client from '@sendgrid/client'
import mailClient from '@sendgrid/client'
import { Prisma } from '@prisma/client'
import { PrismaService } from '../prisma/prisma.service'
import { CreatePersonDto } from './dto/create-person.dto'
Expand All @@ -16,7 +16,7 @@ export class PersonService {
const apiKey = config.get<string>('sendgrid.apiKey')

if (apiKey && this.contactsUrl) {
client.setApiKey(apiKey)
mailClient.setApiKey(apiKey)
} else {
this.enabled = false
Logger.warn('no apiKey or contactsUrl for sendgrid, will not add user to the contact list')
Expand Down Expand Up @@ -138,7 +138,7 @@ export class PersonService {
}

try {
await client.request({
await mailClient.request({
url: this.contactsUrl,
method: 'PUT',
body: data,
Expand Down
2 changes: 1 addition & 1 deletion db/seed/campaign/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const prisma = new PrismaClient()
const SEED_ACTIVE_CAMPAIGNS = 5
const SEED_RANDOM_CAMPAIGNS = 5
const SEED_COMPLETED_CAMPAIGNS = 3
const SEED_HEAVILY_FUNDED_CAMPAIGNS = 3
const SEED_HEAVILY_FUNDED_CAMPAIGNS = 1

export async function campaignSeed() {
console.log('Campaigns seed')
Expand Down
19 changes: 14 additions & 5 deletions db/seed/donation/seed.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { PrismaClient, PaymentProvider, DonationStatus, DonationType, Person } from '@prisma/client'
import {
PrismaClient,
PaymentProvider,
DonationStatus,
DonationType,
Person,
CampaignState,
} from '@prisma/client'

import { donationFactory } from './factory'

Expand Down Expand Up @@ -78,8 +85,8 @@ async function seedRandomDonations({ person }: SeedData) {
async function seedDonationsForCompletedCampaign({ person }: SeedData) {
const completedCampaignVault = await prisma.vault.findFirst({
where: {
name: {
contains: 'completed',
campaign: {
state: CampaignState.complete,
},
},
})
Expand Down Expand Up @@ -119,8 +126,10 @@ async function seedDonationsForCompletedCampaign({ person }: SeedData) {
async function seedDonationsForHeavilyFundedCampaign({ person }: SeedData) {
const heavilyFundedCampaignVault = await prisma.vault.findFirst({
where: {
name: {
contains: 'heavily-funded',
campaign: {
title: {
contains: 'heavily-funded',
},
},
},
})
Expand Down
4 changes: 2 additions & 2 deletions db/seed/expense/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ export const expenseFactory = Factory.define<Expense>(({ associations }) => ({
vaultId: associations.vaultId || faker.datatype.uuid(),
documentId: associations.documentId || null,
approvedById: associations.approvedById || null,
amount: faker.datatype.number(),
amount: faker.datatype.number({ min: 1, max: 20000 }),
currency: faker.helpers.arrayElement(Object.values(Currency)),
status: faker.helpers.arrayElement(Object.values(ExpenseStatus)),
status: ExpenseStatus.pending,
deleted: faker.datatype.boolean(),
spentAt: faker.date.past(),
}))
Loading

0 comments on commit 6a225e9

Please sign in to comment.