Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create transactions resource #92

Merged
merged 61 commits into from
Jan 30, 2022
Merged
Show file tree
Hide file tree
Changes from 58 commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
d86d3c4
adds empty files/folders for transaction dto, create-transaction, con…
DXRovang Dec 1, 2021
bbadaff
imports TypeOrmModule and TransactionsRepository into transactions.mo…
DXRovang Dec 1, 2021
f01163b
begins filling in transactions.repository
DXRovang Dec 1, 2021
99105ed
adds basic columns to transaction.entity
DXRovang Dec 1, 2021
4cd9bf3
recreates transactions.repository
DXRovang Dec 1, 2021
ed96735
adds basic create-transaction.dto info and installs class-validator/c…
DXRovang Dec 1, 2021
97bd9ac
adds TransactionStatus
DXRovang Dec 1, 2021
9a21a28
establishes a create-transactions function
DXRovang Dec 4, 2021
ee66825
establishes a get-transactions function
DXRovang Dec 4, 2021
c2fd00e
establishes a get-transaction-byID function
DXRovang Dec 4, 2021
d4482ad
establishes a delete-transaction function
DXRovang Dec 4, 2021
19641b6
establishes an update-transaction function
DXRovang Dec 4, 2021
441161e
adds status to filterDto
DXRovang Dec 4, 2021
4f79002
provides a single filter (status) for getting transactions
DXRovang Dec 4, 2021
2fc3690
adds search function, commented out for later development
DXRovang Dec 4, 2021
41f020e
adds filter methods for all params
DXRovang Dec 5, 2021
42acf1d
adds GlobalPipes & ValidationPipe to main
DXRovang Dec 5, 2021
c628594
temp disables @IsEnum in get-transactions, adds custom mssg to create…
DXRovang Dec 5, 2021
09b06a3
changes all id types to number
DXRovang Dec 5, 2021
2435700
changes validation type for numbers, moves enum validation to create
DXRovang Dec 5, 2021
6ab802a
changes created_at to type Date
DXRovang Dec 5, 2021
9d9b551
cleans up repository
DXRovang Dec 5, 2021
54d42c4
imports User, Asset, and Organization into the Transaction Entity
DXRovang Dec 5, 2021
8835a93
adds int to @column
DXRovang Dec 5, 2021
0d2e15e
attempts to connect transaction.entity with user.entity
DXRovang Dec 5, 2021
e902ace
cleans up extraneous code
DXRovang Dec 5, 2021
00f977f
adds eager to relationships
DXRovang Dec 5, 2021
199059d
last commit before draft
DXRovang Dec 6, 2021
b2de999
adds custom get-user decorator
DXRovang Dec 7, 2021
9ded60e
adds user to transaction controller, service, and repo create methods
DXRovang Dec 7, 2021
83750f8
connects donater_user to transaction
DXRovang Dec 8, 2021
764645c
connects donater_organization to transactions
DXRovang Dec 8, 2021
3b21155
corrects name/type of donater_user & donater_organization, comments o…
DXRovang Dec 8, 2021
96cc2f0
attempts to connect assets to transactions
DXRovang Dec 8, 2021
8aad7e4
connects transactions with recipient
DXRovang Dec 9, 2021
edc2d1a
first attempt at updating asset from the createTransaction service
DXRovang Dec 9, 2021
1105b26
connects assets to transactions BECAUSE OF KEVIN!!!
DXRovang Dec 9, 2021
ccb6ac3
restores structure
DXRovang Dec 10, 2021
a48879f
restructures transaction resource to mirror other modules (#95)
DXRovang Dec 10, 2021
2ec09bc
reinitialized db after merge
DXRovang Dec 10, 2021
b971e75
gets rid of class-transformer dependency and unnecessary await in ser…
DXRovang Dec 17, 2021
2d23ab0
adds @JoinColumn to message and asset entities
DXRovang Dec 17, 2021
a537868
deletes eager from organiztion entity
DXRovang Dec 17, 2021
f179828
reinstalls class-transformer
DXRovang Jan 8, 2022
2d7ce19
deletes eager:true for now
DXRovang Jan 8, 2022
8de7f2b
adds @IsOptional to donater_organization
DXRovang Jan 8, 2022
355f91f
changes all @IsOptional to ?
DXRovang Jan 8, 2022
2613bff
cleans up some unused code, adds type to imports
DXRovang Jan 9, 2022
165390f
adds class-transformer to server
DXRovang Jan 9, 2022
84af5df
eliminates validator from get-dto
DXRovang Jan 12, 2022
cd14334
adds @IsOptional to create-dto
DXRovang Jan 12, 2022
83b9fd0
merge origin main
jd2rogers2 Jan 23, 2022
a87d72c
format
jd2rogers2 Jan 23, 2022
20224e2
fix tests (imports) and fix lints (unused filterDto)
jd2rogers2 Jan 23, 2022
f6dc030
clean up entity decorators, clean up dtos
jd2rogers2 Jan 23, 2022
26036ad
auto date in entity, make status optional so default is used
jd2rogers2 Jan 23, 2022
826dddf
remove unused import
jd2rogers2 Jan 23, 2022
3970102
use typeorm decorator for date col
jd2rogers2 Jan 26, 2022
68d1d25
use filtering correctly (with where) in service
jd2rogers2 Jan 29, 2022
fe39084
Merge branch 'main' of https://github.com/Nonprofit-Exchange-Hub/web-…
jd2rogers2 Jan 29, 2022
4e049f7
remove created date from dto
jd2rogers2 Jan 30, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4,871 changes: 2,504 additions & 2,367 deletions server/package-lock.json

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,22 @@
"@nestjs/typeorm": "^7.1.5",
"@types/cookie-parser": "^1.4.2",
"bcrypt": "^5.0.1",
"class-transformer": "^0.5.1",
"class-validator": "^0.13.2",
"config": "^3.3.6",
"cookie-parser": "^1.4.6",
"dotenv": "^10.0.0",
"hbs": "^4.1.2",
"passport": "^0.4.1",
"passport-jwt": "^4.0.0",
"passport-local": "^1.0.0",
"pg": "^8.5.1",
"pg": "^8.7.1",
"pg-promise": "^10.9.5",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^6.6.6",
"save": "^2.4.0",
"typeorm": "^0.2.32"
"typeorm": "^0.2.41"
},
"devDependencies": {
"@nestjs/cli": "^7.5.6",
Expand Down
2 changes: 2 additions & 0 deletions server/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { AssetsModule } from './assets/assets.module';
import { MessagesModule } from './messages/messages.module';
import { UsersModule } from './users/users.module';
import { OrganizationsModule } from './organizations/organizations.module';
import { TransactionsModule } from './transactions/transactions.module';
import { CategoriesModule } from './categories/categories.module';

@Module({
Expand All @@ -28,6 +29,7 @@ import { CategoriesModule } from './categories/categories.module';
OrganizationsModule,
UsersModule,
CategoriesModule,
TransactionsModule,
],
controllers: [AppController],
providers: [],
Expand Down
7 changes: 6 additions & 1 deletion server/src/assets/entities/asset.entity.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne, JoinColumn, OneToMany } from 'typeorm';
import { User } from '../../users/entities/user.entity';

import { AssetType, Condition } from '../constants';
import { Transaction } from '../../transactions/entities/transaction.entity';

@Entity('assets')
export class Asset {
Expand Down Expand Up @@ -32,5 +33,9 @@ export class Asset {
quantity: number;

@ManyToOne(() => User, (user) => user.assets)
@JoinColumn({ name: 'poster_id' })
poster: User;

@OneToMany(() => Transaction, (transaction) => transaction.asset)
transactions: Transaction[];
DXRovang marked this conversation as resolved.
Show resolved Hide resolved
}
7 changes: 7 additions & 0 deletions server/src/auth/get-user.decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import { User } from 'src/users/entities/user.entity';

export const GetUser = createParamDecorator((_data, ctx: ExecutionContext): User => {
const req = ctx.switchToHttp().getRequest();
return req.user;
});
4 changes: 2 additions & 2 deletions server/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { resolve } from 'path';
import * as cookieParser from 'cookie-parser';

import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';

dotenv.config({ path: __dirname + '/.env' });

Expand All @@ -16,11 +17,10 @@ async function bootstrap() {
// TODO get env related base url
origin: 'http://localhost:3000',
});

app.useGlobalPipes(new ValidationPipe());
esteban-gs marked this conversation as resolved.
Show resolved Hide resolved
app.useStaticAssets(resolve('./src/public'));
app.setBaseViewsDir(resolve('./src/views'));
app.setViewEngine('hbs');

app.use(cookieParser('secret_placeholder'));

await app.listen(process.env.PORT || 3001);
Expand Down
10 changes: 9 additions & 1 deletion server/src/messages/entities/message.entity.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne, CreateDateColumn } from 'typeorm';
import {
Entity,
Column,
PrimaryGeneratedColumn,
ManyToOne,
CreateDateColumn,
JoinColumn,
} from 'typeorm';
import { User } from '../../users/entities/user.entity';
// import { Transaction } from '../../transactions/entities/transaction.entity'

Expand All @@ -17,6 +24,7 @@ export class Message {
created_date: Date;

@ManyToOne(() => User, (user) => user.messages, { eager: true })
@JoinColumn()
user: User;

// TODO: uncomment the code below when transactions are set up
Expand Down
9 changes: 8 additions & 1 deletion server/src/organizations/entities/organization.entity.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
import { Entity, Column, PrimaryGeneratedColumn, OneToMany } from 'typeorm';
import { Transaction } from '../../transactions/entities/transaction.entity';

@Entity('organizations')
export class Organization {
Expand Down Expand Up @@ -31,4 +32,10 @@ export class Organization {

@Column('int')
tax_exempt_id: number;

@OneToMany(
() => Transaction,
(transaction) => transaction.donater_organization || transaction.recipient,
)
transactions: Transaction[];
}
29 changes: 29 additions & 0 deletions server/src/transactions/dto/create-transaction.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { IsNotEmpty, IsEnum, IsOptional } from 'class-validator';
import { TransactionStatus } from '../transaction-status.enum';

import type { User } from 'src/users/entities/user.entity';
import type { Organization } from 'src/organizations/entities/organization.entity';
import type { Asset } from 'src/assets/entities/asset.entity';

export class CreateTransactionDto {
@IsNotEmpty()
donater_user: User;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix:

Should be:

 @IsNotEmpty()
  donater_user_id: number;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmmm, are you sure?
would nest know to add a .donater attr or function to the transation model then? now that i think about it, idk how to control the model in nest, like if i wanted to write a custom function like asset.close() (that updates the status to all the transactions related to that asset, how would i do that?

Copy link
Contributor

@esteban-gs esteban-gs Jan 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at your example,

This:

curl -X POST localhost:3001/api/assets \
-H "Content-Type: application/json" \
-d '{ "title": "thing 1", "description": "its a thing", "type": "donation", "quantity": "1",
"poster": { "first_name":"test", "last_name":"user", "email":"[email protected]", "id": "18" } }'

can be replaced by example below, if you change the createDto to use poster_id instead of a whole new poster object. The return type will be whatever the repository returns (transactionEntity):

curl -X POST localhost:3001/api/assets \
-H "Content-Type: application/json" \
-d '{ "title": "thing 1", "description": "its a thing", "type": "donation", "quantity": "1",
"poster_id": 18 }'

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then, you would have to implement something like: create-transactions-resource...sample/create-transactions-resource

Also, if you are going to stick with the getTransactionsDto as the get all filtering model, there is some funky formatting we have to do in the front end to send the nested object in the url query params. See this


@IsOptional()
donater_organization: Organization;
DXRovang marked this conversation as resolved.
Show resolved Hide resolved
Comment on lines +12 to +13
Copy link
Contributor

@esteban-gs esteban-gs Jan 26, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix:

Should be:

 @IsNotEmpty()
  donater_organization_id: number;


@IsOptional()
recipient: Organization;

Comment on lines +15 to +17
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, only the foreign key is required

// custom message for example, not necessary to code
DXRovang marked this conversation as resolved.
Show resolved Hide resolved
@IsNotEmpty({ message: 'asset_id is required' })
asset: Asset;
Comment on lines +19 to +20
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, only the foreign key is required


@IsOptional()
@IsEnum(TransactionStatus)
status: TransactionStatus;

@IsOptional()
@IsNotEmpty()
created_date: Date;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't these cancel each other?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

totally 😆
removed altogether because the default value should be used and so we don't need to pass anything in

}
5 changes: 5 additions & 0 deletions server/src/transactions/dto/get-transactions-filter.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { PartialType } from '@nestjs/mapped-types';

import { CreateTransactionDto } from './create-transaction.dto';

export class GetTransactionsDto extends PartialType(CreateTransactionDto) {}
esteban-gs marked this conversation as resolved.
Show resolved Hide resolved
5 changes: 5 additions & 0 deletions server/src/transactions/dto/update-transaction.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { PickType } from '@nestjs/mapped-types';

import { CreateTransactionDto } from './create-transaction.dto';

export class UpdateTransactionDto extends PickType(CreateTransactionDto, ['status'] as const) {}
44 changes: 44 additions & 0 deletions server/src/transactions/entities/transaction.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {
Column,
CreateDateColumn,
Entity,
JoinColumn,
ManyToOne,
PrimaryGeneratedColumn,
} from 'typeorm';
import { TransactionStatus } from '../transaction-status.enum';
import { User } from '../../users/entities/user.entity';
import { Asset } from '../../assets/entities/asset.entity';
import { Organization } from '../../organizations/entities/organization.entity';

@Entity('transactions')
export class Transaction {
@PrimaryGeneratedColumn()
id: number;

@Column({
type: 'enum',
enum: TransactionStatus,
default: TransactionStatus.IN_PROGRESS,
})
status: TransactionStatus;

@CreateDateColumn()
created_date: Date;

@ManyToOne((_type) => User, (user) => user.transactions)
@JoinColumn()
donater_user: User;

@ManyToOne((_type) => Organization, (organization) => organization.transactions)
@JoinColumn()
donater_organization?: Organization;

@ManyToOne((_type) => Asset, (asset) => asset.transactions, { eager: true })
@JoinColumn()
asset: Asset;
DXRovang marked this conversation as resolved.
Show resolved Hide resolved

@ManyToOne((_type) => Organization, (recipient) => recipient.transactions)
@JoinColumn()
recipient: Organization;
}
5 changes: 5 additions & 0 deletions server/src/transactions/transaction-status.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export enum TransactionStatus {
IN_PROGRESS = 'IN_PROGRESS',
COMPLETED = 'COMPLETED',
CANCELLED = 'CANCELLED',
}
43 changes: 43 additions & 0 deletions server/src/transactions/transactions.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Controller, Post, Body, Get, Query, Param, Delete, Patch } from '@nestjs/common';

import { TransactionsService } from './transactions.service';
import { CreateTransactionDto } from './dto/create-transaction.dto';
import { Transaction } from './entities/transaction.entity';
import { GetTransactionsDto } from './dto/get-transactions-filter.dto';
import { UpdateTransactionDto } from './dto/update-transaction.dto';

@Controller('transactions')
export class TransactionsController {
constructor(private transactionsService: TransactionsService) {}

@Post()
create(@Body() createTransactionDto: CreateTransactionDto): Promise<Transaction> {
return this.transactionsService.createTransaction(createTransactionDto);
}

@Get()
get(
@Query()
filterDto: GetTransactionsDto,
): Promise<Transaction[]> {
return this.transactionsService.getTransactions(filterDto);
}

@Get('/:id')
getTransactionById(@Param('id') id: number): Promise<Transaction> {
return this.transactionsService.getTransactionById(id);
}

@Patch('/:id')
update(
@Param('id') id: number,
@Body() updateTransactionStatusDto: UpdateTransactionDto,
): Promise<Transaction> {
return this.transactionsService.updateTransaction(id, updateTransactionStatusDto);
}

@Delete('/:id')
delete(@Param('id') id: number): Promise<void> {
return this.transactionsService.deleteTransaction(id);
}
}
13 changes: 13 additions & 0 deletions server/src/transactions/transactions.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Module } from '@nestjs/common';
import { TransactionsController } from './transactions.controller';
import { TransactionsService } from './transactions.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Transaction } from './entities/transaction.entity';

@Module({
imports: [TypeOrmModule.forFeature([Transaction])],
controllers: [TransactionsController],
providers: [TransactionsService],
exports: [TransactionsService],
})
export class TransactionsModule {}
51 changes: 51 additions & 0 deletions server/src/transactions/transactions.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';

import { CreateTransactionDto } from './dto/create-transaction.dto';
import { Transaction } from './entities/transaction.entity';
import { GetTransactionsDto } from './dto/get-transactions-filter.dto';
import { UpdateTransactionDto } from './dto/update-transaction.dto';

@Injectable()
export class TransactionsService {
constructor(
@InjectRepository(Transaction)
private transactionsRepository: Repository<Transaction>,
) {}

async createTransaction(createTransactionDto: CreateTransactionDto): Promise<Transaction> {
return this.transactionsRepository.save(createTransactionDto);
}

async getTransactions(getTransactionsDto: GetTransactionsDto): Promise<Transaction[]> {
return this.transactionsRepository.find(getTransactionsDto);
}

async getTransactionById(id: number): Promise<Transaction> {
const found = await this.transactionsRepository.findOne(id);
if (!found) {
throw new NotFoundException();
}
return found;
}

async updateTransaction(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should either be updateTransactionStatus or be able to update any property
i'm leaning towards the latter unless you see a benefit in having multiple endpoints for updating different properties

id: number,
updateTransactionDto: UpdateTransactionDto,
): Promise<Transaction> {
const transaction = await this.getTransactionById(id);
const newTransaction = await this.transactionsRepository.save({
...transaction,
...updateTransactionDto,
});
return newTransaction;
}
esteban-gs marked this conversation as resolved.
Show resolved Hide resolved

async deleteTransaction(id: number): Promise<void> {
jd2rogers2 marked this conversation as resolved.
Show resolved Hide resolved
const transactionToDelete = await this.transactionsRepository.delete(id);
if (transactionToDelete.affected === 0) {
throw new NotFoundException();
}
}
}
4 changes: 4 additions & 0 deletions server/src/users/entities/user.entity.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Entity, Column, PrimaryGeneratedColumn, OneToMany } from 'typeorm';
import { Asset } from '../../assets/entities/asset.entity';
import { Message } from '../../messages/entities/message.entity';
import { Transaction } from '../../transactions/entities/transaction.entity';

@Entity('users')
export class User {
Expand All @@ -22,6 +23,9 @@ export class User {
@OneToMany(() => Asset, (asset) => asset.poster)
assets: Asset[];

@OneToMany(() => Transaction, (transaction) => transaction.donater_user)
transactions: Transaction[];

@OneToMany(() => Message, (message) => message.user)
messages: Message[];
}