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

feat: update bitsacco shares #52

Merged
merged 1 commit into from
Jan 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
22 changes: 12 additions & 10 deletions apps/api/src/shares/shares.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,10 @@ import {
OfferSharesDto,
SubscribeSharesDto,
TransferSharesDto,
UpdateSharesDto,
} from '@bitsacco/common';
import {
Body,
Controller,
Get,
Logger,
Param,
Post,
Query,
} from '@nestjs/common';
import { ApiOperation, ApiBody, ApiQuery, ApiParam } from '@nestjs/swagger';
import { Body, Controller, Get, Logger, Param, Post } from '@nestjs/common';
import { ApiOperation, ApiBody, ApiParam } from '@nestjs/swagger';
import { SharesService } from './shares.service';

@Controller('shares')
Expand Down Expand Up @@ -56,6 +49,15 @@ export class SharesController {
return this.sharesService.transferShares(req);
}

@Post('update')
@ApiOperation({ summary: 'Update Bitsacco shares' })
@ApiBody({
type: UpdateSharesDto,
})
updateShares(@Body() req: UpdateSharesDto) {
return this.sharesService.updateShares(req);
}

@Get('transactions')
@ApiOperation({ summary: 'List all Bitsacco share transactions' })
allSharesTransactions() {
Expand Down
5 changes: 5 additions & 0 deletions apps/api/src/shares/shares.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
SubscribeSharesDto,
TransferSharesDto,
UserSharesDto,
UpdateSharesDto,
} from '@bitsacco/common';
import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
import { type ClientGrpc } from '@nestjs/microservices';
Expand Down Expand Up @@ -37,6 +38,10 @@ export class SharesService implements OnModuleInit {
return this.client.transferShares(req);
}

updateShares(req: UpdateSharesDto) {
return this.client.updateShares(req);
}

userSharesTransactions(req: UserSharesDto) {
return this.client.userSharesTransactions(req);
}
Expand Down
6 changes: 6 additions & 0 deletions apps/shares/src/shares.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
SharesServiceControllerMethods,
SubscribeSharesDto,
TransferSharesDto,
UpdateSharesDto,
UserTxsRequestDto,
} from '@bitsacco/common';
import { SharesService } from './shares.service';
Expand Down Expand Up @@ -35,6 +36,11 @@ export class SharesController {
return this.sharesService.transferShares(request);
}

@GrpcMethod()
updateShares(request: UpdateSharesDto) {
return this.sharesService.updateShares(request);
}

@GrpcMethod()
userSharesTransactions(request: UserTxsRequestDto) {
return this.sharesService.userSharesTransactions(request);
Expand Down
44 changes: 30 additions & 14 deletions apps/shares/src/shares.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
SharesTxStatus,
SubscribeSharesDto,
TransferSharesDto,
UpdateSharesDto,
UserSharesDto,
UserShareTxsResponse,
} from '@bitsacco/common';
Expand Down Expand Up @@ -90,10 +91,6 @@ export class SharesService {
sharesId,
...transfer
}: TransferSharesDto): Promise<UserShareTxsResponse> {
// const originShares = await this.userShareTransactions({
// userId: fromUserId,
// });

const originShares = await this.shares.findOne({ _id: sharesId });

if (originShares.status !== SharesTxStatus.COMPLETE) {
Expand All @@ -104,29 +101,48 @@ export class SharesService {
throw new Error('Not enough shares to transfer');
}

await this.shares.findOneAndUpdate(
{ _id: sharesId },
{
$set: {
quantity: originShares.quantity - transfer.quantity,
updatedAt: Date.now(),
transfer,
},
// Update origin shares quantity, and record transfer metadata
await this.updateShares({
sharesId,
updates: {
quantity: originShares.quantity - transfer.quantity,
transfer,
},
);
});

// Assign shares to the recipient
await this.shares.create({
userId: transfer.toUserId,
offerId: originShares.offerId,
quantity: transfer.quantity,
transfer,
status: SharesTxStatus.COMPLETE,
transfer,
});

return this.userSharesTransactions({ userId: transfer.fromUserId });
}

async updateShares({
sharesId,
updates,
}: UpdateSharesDto): Promise<UserShareTxsResponse> {
const originShares = await this.shares.findOne({ _id: sharesId });
const { quantity, status, transfer, offerId } = updates;

let { userId } = await this.shares.findOneAndUpdate(
{ _id: sharesId },
{
quantity: quantity ?? originShares.quantity,
status: status ?? originShares.status,
transfer: transfer ?? originShares.transfer,
offerId: offerId ?? originShares.offerId,
updatedAt: Date.now(),
},
);

return this.userSharesTransactions({ userId });
}

async userSharesTransactions({
userId,
}: UserSharesDto): Promise<UserShareTxsResponse> {
Expand Down
82 changes: 78 additions & 4 deletions libs/common/src/dto/shares.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,19 @@ import {
IsPositive,
IsNumber,
IsDateString,
IsEnum,
IsOptional,
ValidateNested,
IsNotEmptyObject,
} from 'class-validator';
import {
OfferSharesRequest,
SharesTxStatus,
type SharesTxTransferMeta,
SharesTxUpdates,
SubscribeSharesRequest,
TransferSharesRequest,
UpdateSharesRequest,
UserSharesTxsRequest,
} from '@bitsacco/common';
import { ApiProperty } from '@nestjs/swagger';
Expand All @@ -24,13 +32,19 @@ export class OfferSharesDto implements OfferSharesRequest {
@IsNotEmpty()
@IsDateString()
@Type(() => String)
@ApiProperty({ description: 'Start date of availability (ISO 8601 format)', example: '2024-12-30T12:19:04.077Z' })
@ApiProperty({
description: 'Start date of availability (ISO 8601 format)',
example: '2024-12-30T12:19:04.077Z',
})
availableFrom: string;

@IsNotEmpty()
@IsDateString()
@Type(() => String)
@ApiProperty({ description: 'End date of availability (ISO 8601 format)', example: '2025-12-30T12:19:04.077Z' })
@ApiProperty({
description: 'End date of availability (ISO 8601 format)',
example: '2025-12-30T12:19:04.077Z',
})
availableTo?: string;
}

Expand All @@ -50,7 +64,7 @@ export class SubscribeSharesDto implements SubscribeSharesRequest {
@IsPositive()
@IsNumber()
@Type(() => Number)
@ApiProperty()
@ApiProperty({ description: 'Amount of shares to subscribe', example: 21 })
quantity: number;
}

Expand All @@ -76,10 +90,70 @@ export class TransferSharesDto implements TransferSharesRequest {
@IsPositive()
@IsNumber()
@Type(() => Number)
@ApiProperty()
@ApiProperty({ description: 'Amount of shares to transfer', example: 10 })
quantity: number;
}

class SharesTxTransferMetaDto implements SharesTxTransferMeta {
@IsNotEmpty()
@IsString()
@Type(() => String)
@ApiProperty({ example: '7b158dfd-cb98-40b1-9ed2-a13006a9f670' })
fromUserId: string;

@IsNotEmpty()
@IsString()
@Type(() => String)
@ApiProperty({ example: '8b158dfd-cb98-40b1-9ed2-a13006a9f671' })
toUserId: string;

@IsPositive()
@IsNumber()
@Type(() => Number)
@ApiProperty({ description: 'Amount of shares transferred', example: 10 })
quantity: number;
}

class SharesTxUpdatesDto implements SharesTxUpdates {
@IsOptional()
@IsPositive()
@IsNumber()
@Type(() => Number)
@ApiProperty()
quantity?: number;

@IsOptional()
@IsEnum(SharesTxStatus)
@ApiProperty({ enum: SharesTxStatus })
status?: SharesTxStatus;

@IsOptional()
@ValidateNested()
@Type(() => SharesTxTransferMetaDto)
@ApiProperty({ type: SharesTxTransferMetaDto })
transfer?: SharesTxTransferMeta;

@IsOptional()
@IsString()
@Type(() => String)
@ApiProperty({ example: '3e158dfd-cb98-40b1-9ed2-a13006a9f430' })
offerId?: string;
}

export class UpdateSharesDto implements UpdateSharesRequest {
@IsNotEmpty()
@IsString()
@Type(() => String)
@ApiProperty({ example: '3e158dfd-cb98-40b1-9ed2-a13006a9f430' })
sharesId: string;

@IsNotEmptyObject()
@ValidateNested()
@Type(() => SharesTxUpdatesDto)
@ApiProperty({ type: SharesTxUpdatesDto })
updates: SharesTxUpdatesDto;
}

export class UserSharesDto implements UserSharesTxsRequest {
@IsNotEmpty()
@IsString()
Expand Down
22 changes: 22 additions & 0 deletions libs/common/src/types/proto/shares.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions proto/shares.proto
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ service SharesService {
rpc GetSharesOffers(lib.Empty) returns (AllSharesOffers);
rpc SubscribeShares(SubscribeSharesRequest) returns (UserShareTxsResponse);
rpc TransferShares(TransferSharesRequest) returns (UserShareTxsResponse);
rpc UpdateShares(UpdateSharesRequest) returns (UserShareTxsResponse);
rpc UserSharesTransactions(UserSharesTxsRequest) returns (UserShareTxsResponse);
rpc AllSharesTransactions(lib.Empty) returns (AllSharesTxsResponse);
}
Expand Down Expand Up @@ -101,6 +102,21 @@ message TransferSharesRequest {
int32 quantity = 4;
}

message UpdateSharesRequest {
string shares_id = 1;
SharesTxUpdates updates = 2;
}

message SharesTxUpdates {
optional int32 quantity = 1;

optional SharesTxStatus status = 2;

optional SharesTxTransferMeta transfer = 3;

optional string offer_id = 4;
}

message UserSharesTxsRequest {
string user_id = 1;
}
Expand Down
Loading