Skip to content

Commit

Permalink
Validate and only return first portfolio
Browse files Browse the repository at this point in the history
  • Loading branch information
iamacook committed Jan 13, 2025
1 parent 2de07b4 commit 2925044
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 17 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { ZodError } from 'zod';
import { OctavGetPortfolioSchema } from '@/datasources/portfolio-api/entities/octav-get-portfolio.entity';
import type { OctavGetPortfolio } from '@/datasources/portfolio-api/entities/octav-get-portfolio.entity';

describe('OctavGetPortfolioSchema', () => {
it('should validate a getPortfolio response', () => {
const portfolio = { example: 'payload' };
const getPortfolio: OctavGetPortfolio = { getPortfolio: [portfolio] };

const result = OctavGetPortfolioSchema.safeParse(getPortfolio);

expect(result.success).toBe(true);
});

it('should not validate an invalid getPortfolio response', () => {
const getPortfolio = { invalid: 'getPortfolio' };

const result = OctavGetPortfolioSchema.safeParse(getPortfolio);

expect(result.success).toBe(false);
expect(!result.success && result.error).toStrictEqual(
new ZodError([
{
code: 'invalid_type',
expected: 'array',
received: 'undefined',
path: ['getPortfolio'],
message: 'Required',
},
]),
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { z } from 'zod';
import { PortfolioSchema } from '@/domain/portfolio/entities/portfolio.entity';

export const OctavGetPortfolioSchema = z.object({
getPortfolio: z.array(PortfolioSchema),
});

export type OctavGetPortfolio = z.infer<typeof OctavGetPortfolioSchema>;
7 changes: 4 additions & 3 deletions src/datasources/portfolio-api/octav-api.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { NetworkResponseError } from '@/datasources/network/entities/network.err
import { OctavApi } from '@/datasources/portfolio-api/octav-api.service';
import { rawify } from '@/validation/entities/raw.entity';
import type { INetworkService } from '@/datasources/network/network.service.interface';
import type { Portfolio } from '@/domain/portfolio/entities/portfolio.entity';
import type { OctavGetPortfolio } from '@/datasources/portfolio-api/entities/octav-get-portfolio.entity';

const mockNetworkService = jest.mocked({
get: jest.fn(),
Expand Down Expand Up @@ -70,10 +70,11 @@ describe('OctavApiService', () => {
describe('getPortfolio', () => {
it('should get portfolio', async () => {
const safeAddress = getAddress(faker.finance.ethereumAddress());
const portfolio: Portfolio = {};
const portfolio = { example: 'payload' };
const getPortfolio: OctavGetPortfolio = { getPortfolio: [portfolio] };
mockNetworkService.get.mockResolvedValueOnce({
status: 200,
data: rawify(portfolio),
data: rawify(getPortfolio),
});

await target.getPortfolio(safeAddress);
Expand Down
36 changes: 22 additions & 14 deletions src/datasources/portfolio-api/octav-api.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import {
NetworkService,
INetworkService,
} from '@/datasources/network/network.service.interface';
import { OctavGetPortfolioSchema } from '@/datasources/portfolio-api/entities/octav-get-portfolio.entity';
import { IPortfolioApi } from '@/domain/interfaces/portfolio-api.interface';
import { Portfolio } from '@/domain/portfolio/entities/portfolio.entity';
import { Raw } from '@/validation/entities/raw.entity';
import { rawify } from '@/validation/entities/raw.entity';
import type { Raw } from '@/validation/entities/raw.entity';
import type { Portfolio } from '@/domain/portfolio/entities/portfolio.entity';

@Injectable()
export class OctavApi implements IPortfolioApi {
Expand All @@ -30,19 +32,25 @@ export class OctavApi implements IPortfolioApi {
async getPortfolio(safeAddress: `0x${string}`): Promise<Raw<Portfolio>> {
try {
const url = `${this.baseUri}/api/rest/portfolio`;
const { data: portfolio } = await this.networkService.get<Portfolio>({
url,
networkRequest: {
headers: {
Authorization: `Bearer ${this.apiKey}`,
const portfolios = await this.networkService
.get<Array<Portfolio>>({
url,
networkRequest: {
headers: {
Authorization: `Bearer ${this.apiKey}`,
},
params: {
addresses: safeAddress,
includeImages: true,
},
},
params: {
addresses: safeAddress,
includeImages: true,
},
},
});
return portfolio;
})
.then((res) => {
return OctavGetPortfolioSchema.parse(res.data).getPortfolio;
});

// As we are only fetching the portfolio of one Safe, there will only be one element
return rawify(portfolios[0]);
} catch (error) {
throw this.httpErrorFactory.from(error);
}
Expand Down

0 comments on commit 2925044

Please sign in to comment.