Skip to content

Commit

Permalink
feat: rxjs (#29)
Browse files Browse the repository at this point in the history
  • Loading branch information
CodyTseng authored Aug 18, 2024
1 parent 645c7db commit 6caf3af
Show file tree
Hide file tree
Showing 14 changed files with 217 additions and 153 deletions.
16 changes: 8 additions & 8 deletions package-lock.json

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

Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { EventRepository } from '../../src';
import { EMPTY, from, Observable } from 'rxjs';
import { EventRepository, Filter, toPromise } from '../../src';

describe('EventRepository', () => {
let eventRepository: EventRepository;
Expand All @@ -17,11 +18,7 @@ describe('EventRepository', () => {
expect(await eventRepository.findOne({})).toBeNull();
expect(eventRepository.find).toHaveBeenCalledWith({ limit: 1 });

eventRepository.find = jest.fn().mockResolvedValue([]);
expect(await eventRepository.findOne({})).toBeNull();
expect(eventRepository.find).toHaveBeenCalledWith({ limit: 1 });

eventRepository.find = jest.fn().mockReturnValue([]);
eventRepository.find = jest.fn().mockReturnValue(EMPTY);
expect(await eventRepository.findOne({})).toBeNull();
expect(eventRepository.find).toHaveBeenCalledWith({ limit: 1 });
});
Expand All @@ -36,13 +33,34 @@ describe('EventRepository', () => {
expect(await eventRepository.findOne({})).toEqual(event);
expect(eventRepository.find).toHaveBeenCalledWith({ limit: 1 });

eventRepository.find = jest.fn().mockResolvedValue([event]);
eventRepository.find = jest.fn().mockReturnValue(from([event]));
expect(await eventRepository.findOne({})).toEqual(event);
expect(eventRepository.find).toHaveBeenCalledWith({ limit: 1 });
});
});

eventRepository.find = jest.fn().mockReturnValue([event]);
expect(await eventRepository.findOne({})).toEqual(event);
expect(eventRepository.find).toHaveBeenCalledWith({ limit: 1 });
describe('find$', () => {
it('should return find result', async () => {
const filter = {} as Filter;
const events = [{ id: 'a' }, { id: 'b' }];

eventRepository.find = jest.fn().mockReturnValue(events);
const obs1 = eventRepository.find$(filter);
expect(obs1 instanceof Observable).toBeTruthy();
expect(await toPromise(obs1)).toEqual(events);
expect(eventRepository.find).toHaveBeenCalledWith(filter);

eventRepository.find = jest.fn().mockResolvedValue(events);
const obs2 = eventRepository.find$(filter);
expect(obs2 instanceof Observable).toBeTruthy();
expect(await toPromise(obs2)).toEqual(events);
expect(eventRepository.find).toHaveBeenCalledWith(filter);

eventRepository.find = jest.fn().mockReturnValue(from(events));
const obs3 = eventRepository.find$(filter);
expect(obs3 instanceof Observable).toBeTruthy();
expect(await toPromise(obs3)).toEqual(events);
expect(eventRepository.find).toHaveBeenCalledWith(filter);
});
});
});
Expand Down
4 changes: 2 additions & 2 deletions packages/common/__test__/utils/event.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,15 @@ describe('EventUtils', () => {

expect(
EventUtils.validate(
createEvent({ created_at: getTimestampInSeconds() + 101 }),
createEvent({ created_at: getTimestampInSeconds() + 200 }),
{ createdAtUpperLimit: 100 },
),
).toBe(
'invalid: created_at must not be later than 100 seconds from the current time',
);
expect(
EventUtils.validate(
createEvent({ created_at: getTimestampInSeconds() - 101 }),
createEvent({ created_at: getTimestampInSeconds() - 200 }),
{ createdAtLowerLimit: 100 },
),
).toBe(
Expand Down
3 changes: 2 additions & 1 deletion packages/common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
},
"dependencies": {
"@noble/curves": "^1.2.0",
"lru-cache": "^10.1.0"
"lru-cache": "^10.1.0",
"rxjs": "^7.8.1"
}
}
31 changes: 29 additions & 2 deletions packages/common/src/interfaces/event-repository.interface.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { firstValueFrom, Observable } from 'rxjs';
import { Event } from './event.interface';
import { Filter } from './filter.interface';

Expand Down Expand Up @@ -40,7 +41,7 @@ export abstract class EventRepository {
*
* @param filter Query filter
*/
abstract find(filter: Filter): Promise<Event[]> | Event[];
abstract find(filter: Filter): Promise<Event[]> | Observable<Event> | Event[];

/**
* This method is called when the event repository should be closed. You can
Expand All @@ -55,7 +56,33 @@ export abstract class EventRepository {
* @param filter Query filter
*/
async findOne(filter: Filter): Promise<Event | null> {
const [event] = await this.find({ ...filter, limit: 1 });
const query = this.find({ ...filter, limit: 1 });
if (query instanceof Observable) {
return await firstValueFrom(query).catch(() => null);
}
const [event] = await query;
return event ?? null;
}

/**
* This method doesn't need to be implemented. It's just a helper method for
* transforming the `find` method to an observable.
*
* @param filter Query filter
*/
find$(filter: Filter): Observable<Event> {
const query = this.find(filter);
if (query instanceof Observable) {
return query;
}
return new Observable(subscriber => {
(async (): Promise<void> => {
const events = await query;
for (const event of events) {
subscriber.next(event);
}
subscriber.complete();
})();
});
}
}
3 changes: 2 additions & 1 deletion packages/common/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ export * from './crypto.util';
export * from './event.util';
export * from './filter.util';
export * from './proof-of-work.util';
export * from './time.util';
export * from './rxjs.util';
export * from './shared.util';
export * from './time.util';
12 changes: 12 additions & 0 deletions packages/common/src/utils/rxjs.util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Observable } from 'rxjs';

export function toPromise<T>(observable: Observable<T>): Promise<T[]> {
return new Promise((resolve, reject) => {
const values: T[] = [];
observable.subscribe({
next: value => values.push(value),
error: reject,
complete: () => resolve(values),
});
});
}
13 changes: 7 additions & 6 deletions packages/core/__test__/nostr-relay.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { from } from 'rxjs';
import {
Client,
ClientContext,
Expand Down Expand Up @@ -166,8 +167,8 @@ describe('NostrRelay', () => {
.spyOn(nostrRelay['subscriptionService'], 'subscribe')
.mockImplementation();
const mockFind = jest
.spyOn(nostrRelay['eventService'], 'find')
.mockResolvedValue(events);
.spyOn(nostrRelay['eventService'], 'find$')
.mockReturnValue(from(events));

const result = await nostrRelay.handleReqMessage(
client,
Expand Down Expand Up @@ -230,8 +231,8 @@ describe('NostrRelay', () => {
.spyOn(nostrRelay['subscriptionService'], 'subscribe')
.mockImplementation();
const mockFind = jest
.spyOn(nostrRelay['eventService'], 'find')
.mockResolvedValue(events);
.spyOn(nostrRelay['eventService'], 'find$')
.mockReturnValue(from(events));

const result = await nostrRelay.handleReqMessage(
client,
Expand Down Expand Up @@ -263,8 +264,8 @@ describe('NostrRelay', () => {
.spyOn(nostrRelayWithoutHostname['subscriptionService'], 'subscribe')
.mockImplementation();
const mockFind = jest
.spyOn(nostrRelayWithoutHostname['eventService'], 'find')
.mockResolvedValue(events);
.spyOn(nostrRelayWithoutHostname['eventService'], 'find$')
.mockReturnValue(from(events));

const result = await nostrRelayWithoutHostname.handleReqMessage(
client,
Expand Down
Loading

0 comments on commit 6caf3af

Please sign in to comment.