Skip to content

Commit

Permalink
feat: アカウントの複数一括取得の実装 (#588)
Browse files Browse the repository at this point in the history
  • Loading branch information
laminne authored Jul 25, 2024
1 parent 8a80cd7 commit e436121
Show file tree
Hide file tree
Showing 8 changed files with 381 additions and 281 deletions.
55 changes: 55 additions & 0 deletions pkg/accounts/adaptor/repository/dummy.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Result } from '@mikuroxina/mini-fn';
import { describe, expect, it } from 'vitest';
import { Account, type AccountID } from '../../model/account.js';
import { InMemoryAccountRepository } from './dummy.js';

describe('InMemoryAccountRepository', () => {
const dummyInput: Account[] = [
Account.new({
id: '1' as AccountID,
name: '@john@example.com',
mail: 'johndoe@example.com',
nickname: 'John Doe',
bio: 'Hello, World!',
role: 'normal',
frozen: 'normal',
silenced: 'normal',
status: 'active',
createdAt: new Date('2023-09-10T12:00:00Z'),
}),
Account.new({
id: '2' as AccountID,
name: '@alice@example.com',
mail: 'alice@example.com',
nickname: 'Alice',
bio: 'Hello, World!',
role: 'normal',
frozen: 'normal',
silenced: 'normal',
status: 'active',
createdAt: new Date('2023-09-11T12:00:00Z'),
}),
Account.new({
id: '3' as AccountID,
name: '@bob@example.com',
mail: 'bob@example.com',
nickname: 'bob',
bio: 'Hello, World!',
role: 'normal',
frozen: 'normal',
silenced: 'normal',
status: 'active',
createdAt: new Date('2023-09-12T12:00:00Z'),
}),
];
const repository = new InMemoryAccountRepository(dummyInput);

it('should fetch many accounts by id', async () => {
const result = await repository.findManyByID([
'1' as AccountID,
'2' as AccountID,
]);
expect(Result.isOk(result)).toBe(true);
expect(Result.unwrap(result)).toStrictEqual([dummyInput[0], dummyInput[1]]);
});
});
11 changes: 10 additions & 1 deletion pkg/accounts/adaptor/repository/dummy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ export class InMemoryAccountRepository implements AccountRepository {
return Promise.resolve(Result.ok(undefined));
}

reset(): void {
reset(data: Account[] = []): void {
this.data.clear();
data.map((v) => this.data.add(v));
}

findByID(id: AccountID): Promise<Option.Option<Account>> {
Expand All @@ -47,6 +48,14 @@ export class InMemoryAccountRepository implements AccountRepository {
return Promise.resolve(Option.some(account));
}

findManyByID(
id: readonly AccountID[],
): Promise<Result.Result<Error, Account[]>> {
const set = new Set(id);
const accounts = Array.from(this.data).filter((a) => set.has(a.getID()));
return Promise.resolve(Result.ok(accounts));
}

findByMail(mail: string): Promise<Option.Option<Account>> {
const account = Array.from(this.data).find((a) => a.getMail() === mail);
if (!account) {
Expand Down
14 changes: 14 additions & 0 deletions pkg/accounts/adaptor/repository/prisma.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,20 @@ export class PrismaAccountRepository implements AccountRepository {
return Option.some(this.fromPrismaArgs(res));
}

async findManyByID(
id: readonly AccountID[],
): Promise<Result.Result<Error, Account[]>> {
const res = await this.prisma.account.findMany({
where: {
id: {
in: id as AccountID[],
},
},
});

return Result.ok(res.map((a) => this.fromPrismaArgs(a)));
}

async findByMail(mail: string): Promise<Option.Option<Account>> {
const res = await this.prisma.account.findUnique({
where: {
Expand Down
3 changes: 3 additions & 0 deletions pkg/accounts/model/repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ export interface AccountRepository {
create(account: Account): Promise<Result.Result<Error, void>>;
findByName(name: string): Promise<Option.Option<Account>>;
findByID(id: AccountID): Promise<Option.Option<Account>>;
findManyByID(
id: readonly AccountID[],
): Promise<Result.Result<Error, Account[]>>;
findByMail(mail: string): Promise<Option.Option<Account>>;
edit(account: Account): Promise<Result.Result<Error, void>>;
}
Expand Down
50 changes: 42 additions & 8 deletions pkg/accounts/service/fetch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import { InMemoryAccountRepository } from '../adaptor/repository/dummy.js';
import { Account, type AccountID } from '../model/account.js';
import { FetchService } from './fetch.js';

const repository = new InMemoryAccountRepository();
await repository.create(
const testAccounts = [
Account.new({
id: '1' as AccountID,
name: '@john@example.com',
Expand All @@ -18,13 +17,38 @@ await repository.create(
frozen: 'normal',
silenced: 'normal',
status: 'notActivated',
createdAt: new Date(),
createdAt: new Date('2023-09-10T12:00:00Z'),
}),
);
Account.new({
id: '2' as AccountID,
name: '@alice@example.com',
mail: 'alice@example.com',
nickname: 'Alice',
bio: 'Hello, World!',
role: 'normal',
frozen: 'normal',
silenced: 'normal',
status: 'active',
createdAt: new Date('2023-09-11T12:00:00Z'),
}),
Account.new({
id: '3' as AccountID,
name: '@bob@example.com',
mail: 'bob@example.com',
nickname: 'bob',
bio: 'Hello, World!',
role: 'normal',
frozen: 'normal',
silenced: 'normal',
status: 'active',
createdAt: new Date('2023-09-12T12:00:00Z'),
}),
];
const repository = new InMemoryAccountRepository(testAccounts);
const fetchService = new FetchService(repository);

describe('FetchService', () => {
afterEach(() => repository.reset());
afterEach(() => repository.reset(testAccounts));

it('fetch account info', async () => {
const account = await fetchService.fetchAccount('@john@example.com');
Expand Down Expand Up @@ -68,15 +92,25 @@ describe('FetchService', () => {
frozen: 'normal',
silenced: 'normal',
status: 'notActivated',
createdAt: new Date(),
createdAt: new Date('2023-09-10T12:00:00.000Z'),
}),
);
});

it("fetch account by ID doesn't exist", async () => {
// `2` is not registered.
const account = await fetchService.fetchAccountByID('2' as AccountID);
// `20` is not registered.
const account = await fetchService.fetchAccountByID('20' as AccountID);

expect(Result.isErr(account)).toBe(true);
});

it('should fetch many account by ID', async () => {
const accounts = await fetchService.fetchManyAccountsByID([
'2' as AccountID,
'3' as AccountID,
]);

expect(Result.isOk(accounts)).toBe(true);
expect(Result.unwrap(accounts).length).toBe(2);
});
});
6 changes: 6 additions & 0 deletions pkg/accounts/service/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ export class FetchService {
const res = await this.accountRepository.findByID(id);
return Option.okOr(new Error('AccountNotFoundError'))(res);
}

async fetchManyAccountsByID(
id: AccountID[],
): Promise<Result.Result<Error, Account[]>> {
return await this.accountRepository.findManyByID(id);
}
}

export const fetchSymbol = Ether.newEtherSymbol<FetchService>();
Expand Down
6 changes: 6 additions & 0 deletions pkg/intermodule/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ export class AccountModuleFacade {
return await this.fetchService.fetchAccountByID(id);
}

async fetchAccounts(
id: AccountID[],
): Promise<Result.Result<Error, Account[]>> {
return await this.fetchService.fetchManyAccountsByID(id);
}

async fetchFollowings(
id: AccountID,
): Promise<Result.Result<Error, PartialAccount[]>> {
Expand Down
Loading

0 comments on commit e436121

Please sign in to comment.