Skip to content

Commit f88898c

Browse files
authored
Merge pull request #439 from Bhenzdizma/back
implementation for savings goals persistence
2 parents daaeb04 + 15343ab commit f88898c

File tree

14 files changed

+165
-29
lines changed

14 files changed

+165
-29
lines changed

backend/src/common/filters/http-exception.filter.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,14 @@ function isDatabaseConnectionError(exception: unknown): exception is Error {
4141

4242
return (
4343
DB_CONNECTION_PATTERNS.some((pattern) => pattern.test(message)) ||
44-
['ECONNREFUSED', 'ENOTFOUND', 'ETIMEDOUT', '57P01', '08001', '08006'].includes(
45-
code,
46-
)
44+
[
45+
'ECONNREFUSED',
46+
'ENOTFOUND',
47+
'ETIMEDOUT',
48+
'57P01',
49+
'08001',
50+
'08006',
51+
].includes(code)
4752
);
4853
}
4954

backend/src/common/guards/rpc-throttle.guard.spec.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import { Test, TestingModule } from '@nestjs/testing';
22
import { ExecutionContext, HttpException } from '@nestjs/common';
3-
import { ThrottlerException, ThrottlerModuleOptions, ThrottlerStorage } from '@nestjs/throttler';
3+
import {
4+
ThrottlerException,
5+
ThrottlerModuleOptions,
6+
ThrottlerStorage,
7+
} from '@nestjs/throttler';
48
import { Reflector } from '@nestjs/core';
59
import { RpcThrottleGuard } from './rpc-throttle.guard';
610

@@ -32,8 +36,8 @@ describe('RpcThrottleGuard', () => {
3236
// Initialize the guard with mocked dependencies
3337
guard = new RpcThrottleGuard(
3438
mockOptions as any,
35-
mockStorageService as any,
36-
mockReflector as any,
39+
mockStorageService,
40+
mockReflector,
3741
);
3842

3943
// Mock response object
@@ -110,9 +114,9 @@ describe('RpcThrottleGuard', () => {
110114
const limit = 10;
111115
const ttl = 60000; // 1 minute
112116

113-
await expect(
114-
guard.onLimitExceeded(context, limit, ttl),
115-
).rejects.toThrow('Too many RPC requests');
117+
await expect(guard.onLimitExceeded(context, limit, ttl)).rejects.toThrow(
118+
'Too many RPC requests',
119+
);
116120
});
117121

118122
it('should set Retry-After header', async () => {

backend/src/common/guards/rpc-throttle.guard.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,14 @@ export class RpcThrottleGuard extends ThrottlerGuard {
7878
response.setHeader('Retry-After', Math.ceil(ttl / 1000));
7979
response.setHeader('X-RateLimit-Limit', limit);
8080
response.setHeader('X-RateLimit-Remaining', 0);
81-
response.setHeader('X-RateLimit-Reset', new Date(Date.now() + ttl).toISOString());
81+
response.setHeader(
82+
'X-RateLimit-Reset',
83+
new Date(Date.now() + ttl).toISOString(),
84+
);
8285

8386
// Throw ThrottlerException which results in HTTP 429
8487
throw new ThrottlerException(
85-
`Too many RPC requests. Maximum ${limit} requests per ${Math.round(ttl / 1000)} seconds allowed.`
88+
`Too many RPC requests. Maximum ${limit} requests per ${Math.round(ttl / 1000)} seconds allowed.`,
8689
);
8790
}
8891
}

backend/src/migrations/1775000000000-AlignTransactionsEntity.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { MigrationInterface, QueryRunner } from 'typeorm';
22

3-
export class AlignTransactionsEntity1775000000000
4-
implements MigrationInterface
5-
{
3+
export class AlignTransactionsEntity1775000000000 implements MigrationInterface {
64
public async up(queryRunner: QueryRunner): Promise<void> {
75
await queryRunner.query(`
86
DO $$
@@ -133,4 +131,4 @@ export class AlignTransactionsEntity1775000000000
133131
$$;
134132
`);
135133
}
136-
}
134+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import {
2+
MigrationInterface,
3+
QueryRunner,
4+
Table,
5+
TableForeignKey,
6+
TableIndex,
7+
} from 'typeorm';
8+
9+
export class CreateSavingsGoalsTable1775100000000 implements MigrationInterface {
10+
public async up(queryRunner: QueryRunner): Promise<void> {
11+
await queryRunner.createTable(
12+
new Table({
13+
name: 'savings_goals',
14+
columns: [
15+
{
16+
name: 'id',
17+
type: 'uuid',
18+
isPrimary: true,
19+
generationStrategy: 'uuid',
20+
default: 'uuid_generate_v4()',
21+
},
22+
{
23+
name: 'userId',
24+
type: 'uuid',
25+
isNullable: false,
26+
},
27+
{
28+
name: 'goalName',
29+
type: 'varchar',
30+
length: '255',
31+
isNullable: false,
32+
},
33+
{
34+
name: 'targetAmount',
35+
type: 'decimal',
36+
precision: 14,
37+
scale: 2,
38+
isNullable: false,
39+
},
40+
{
41+
name: 'targetDate',
42+
type: 'date',
43+
isNullable: false,
44+
},
45+
{
46+
name: 'status',
47+
type: 'enum',
48+
enum: ['IN_PROGRESS', 'COMPLETED'],
49+
default: "'IN_PROGRESS'",
50+
isNullable: false,
51+
},
52+
{
53+
name: 'metadata',
54+
type: 'jsonb',
55+
isNullable: true,
56+
},
57+
{
58+
name: 'createdAt',
59+
type: 'timestamp',
60+
default: 'now()',
61+
},
62+
{
63+
name: 'updatedAt',
64+
type: 'timestamp',
65+
default: 'now()',
66+
},
67+
],
68+
}),
69+
true,
70+
);
71+
72+
// Create foreign key to users table
73+
await queryRunner.createForeignKey(
74+
'savings_goals',
75+
new TableForeignKey({
76+
columnNames: ['userId'],
77+
referencedTableName: 'users',
78+
referencedColumnNames: ['id'],
79+
onDelete: 'CASCADE',
80+
}),
81+
);
82+
83+
// Create index on userId for faster lookups
84+
await queryRunner.createIndex(
85+
'savings_goals',
86+
new TableIndex({
87+
name: 'IDX_SAVINGS_GOALS_USER_ID',
88+
columnNames: ['userId'],
89+
}),
90+
);
91+
92+
// Create index on status for filtering active/completed goals
93+
await queryRunner.createIndex(
94+
'savings_goals',
95+
new TableIndex({
96+
name: 'IDX_SAVINGS_GOALS_STATUS',
97+
columnNames: ['status'],
98+
}),
99+
);
100+
101+
// Create index on targetDate for date-based queries
102+
await queryRunner.createIndex(
103+
'savings_goals',
104+
new TableIndex({
105+
name: 'IDX_SAVINGS_GOALS_TARGET_DATE',
106+
columnNames: ['targetDate'],
107+
}),
108+
);
109+
}
110+
111+
public async down(queryRunner: QueryRunner): Promise<void> {
112+
await queryRunner.dropTable('savings_goals');
113+
}
114+
}

backend/src/modules/analytics/analytics.module.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ import { LedgerTransaction } from '../blockchain/entities/transaction.entity';
1313
providers: [AnalyticsService],
1414
exports: [AnalyticsService],
1515
})
16-
export class AnalyticsModule {}
16+
export class AnalyticsModule {}

backend/src/modules/blockchain/indexer.service.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,11 @@ export class IndexerService implements OnModuleInit {
8080

8181
for (const event of events) {
8282
const ok = await this.processEvent(event);
83-
ok ? processed++ : failed++;
83+
if (ok) {
84+
processed++;
85+
} else {
86+
failed++;
87+
}
8488
}
8589

8690
this.indexerState.totalEventsProcessed += processed;
@@ -200,4 +204,4 @@ export class IndexerService implements OnModuleInit {
200204
getMonitoredContracts(): string[] {
201205
return Array.from(this.contractIds);
202206
}
203-
}
207+
}

backend/src/modules/governance/dto/voting-power-response.dto.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import { ApiProperty } from '@nestjs/swagger';
22

33
export class VotingPowerResponseDto {
44
@ApiProperty({
5-
description: 'The user\'s voting power as a formatted string',
5+
description: "The user's voting power as a formatted string",
66
example: '12,500 NST',
77
})
88
votingPower: string;
9-
}
9+
}

backend/src/modules/governance/governance.service.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,4 @@ describe('GovernanceService', () => {
108108
);
109109
});
110110
});
111-
});
111+
});

backend/src/modules/governance/governance.service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,4 @@ export class GovernanceService {
4646
});
4747
return { votingPower: `${votingPower} NST` };
4848
}
49-
}
49+
}

0 commit comments

Comments
 (0)