-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(api): add dataloader to avoid refetching db
- Loading branch information
Showing
7 changed files
with
184 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import DataLoader from 'dataloader' | ||
import { Repositories, ResultRequestDbObjectNormalized } from '../types' | ||
|
||
export class Loaders { | ||
repositories: Repositories | ||
|
||
constructor (repositories: Repositories) { | ||
this.repositories = repositories | ||
} | ||
// returns a loader that fetches data using the given function | ||
private genericLoader<T> (load: (filter) => T) { | ||
return new DataLoader(async (filters: Array<string>) => { | ||
console.log('here') | ||
const data = await Promise.all( | ||
await filters.map(async (filter, index) => ({ | ||
data: await load(filter), | ||
index | ||
})) | ||
) | ||
// ensure they are sorted | ||
const fetchedDataByIndex = data.reduce((acc, val) => { | ||
acc[val.index] = val.data | ||
|
||
return acc | ||
}, {}) | ||
return filters.map((_, index) => fetchedDataByIndex[index] || null) | ||
}) | ||
} | ||
|
||
getLoaders (): { | ||
lastResult: DataLoader<string, ResultRequestDbObjectNormalized, string> | ||
requests: DataLoader<string, ResultRequestDbObjectNormalized, string> | ||
} { | ||
return { | ||
lastResult: this.genericLoader<Promise<ResultRequestDbObjectNormalized>>( | ||
async (feedFullName: string) => | ||
await this.repositories.resultRequestRepository.getLastResult( | ||
feedFullName | ||
) | ||
), | ||
|
||
requests: this.genericLoader< | ||
Promise<Array<ResultRequestDbObjectNormalized>> | ||
>( | ||
async (filter: { feedFullName: string; timestamp: number }) => | ||
await this.repositories.resultRequestRepository.getFeedRequests( | ||
filter.feedFullName, | ||
filter.timestamp | ||
) | ||
) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import { Loaders } from '../../src/loaders' | ||
|
||
describe('loaders', () => { | ||
describe('lastResult', () => { | ||
it('lastResult loader should call getLastResult', async () => { | ||
const getLastResultMock = jest.fn(() => ({ feedFullName: 'name' })) | ||
const loaders = new Loaders({ | ||
resultRequestRepository: { | ||
getLastResult: getLastResultMock | ||
} | ||
} as any) | ||
|
||
await loaders.getLoaders().lastResult.load('feedName') | ||
|
||
expect(getLastResultMock).toHaveBeenCalledWith('feedName') | ||
}) | ||
|
||
it('lastResult loader should call getLastResult the same amount of times than filters provided', async () => { | ||
const getLastResultMock = jest.fn(() => ({ feedFullName: 'name' })) | ||
const loaders = new Loaders({ | ||
resultRequestRepository: { | ||
getLastResult: getLastResultMock | ||
} | ||
} as any) | ||
|
||
await loaders.getLoaders().lastResult.load('feedName1') | ||
await loaders.getLoaders().lastResult.load('feedName2') | ||
|
||
expect(getLastResultMock).toHaveBeenNthCalledWith(1, 'feedName1') | ||
expect(getLastResultMock).toHaveBeenNthCalledWith(2, 'feedName2') | ||
}) | ||
|
||
it('lastResult loader should return the result of calling getLastResult', async () => { | ||
const getLastResultMock = jest.fn(() => ({ feedFullName: 'name' })) | ||
const loaders = new Loaders({ | ||
resultRequestRepository: { | ||
getLastResult: getLastResultMock | ||
} | ||
} as any) | ||
|
||
const result = await loaders.getLoaders().lastResult.load('feedName') | ||
|
||
expect(result).toStrictEqual({ feedFullName: 'name' }) | ||
}) | ||
}) | ||
|
||
describe('getRequests', () => { | ||
it('should call getFeedRequests', async () => { | ||
const getFeedRequestsMock = jest.fn(() => ({ feedFullName: 'name' })) | ||
const loaders = new Loaders({ | ||
resultRequestRepository: { | ||
getFeedRequests: getFeedRequestsMock | ||
} | ||
} as any) | ||
|
||
await loaders | ||
.getLoaders() | ||
.requests.load({ feedFullName: 'feedName', timestamp: 1 } as any) | ||
|
||
expect(getFeedRequestsMock).toHaveBeenCalledWith('feedName', 1) | ||
}) | ||
|
||
it('should call getFeedRequests the same amount of times than filters provided', async () => { | ||
const getFeedRequestsMock = jest.fn(() => ({ feedFullName: 'name' })) | ||
const loaders = new Loaders({ | ||
resultRequestRepository: { | ||
getFeedRequests: getFeedRequestsMock | ||
} | ||
} as any) | ||
|
||
await loaders | ||
.getLoaders() | ||
.requests.load({ feedFullName: 'feedName1', timestamp: 1 } as any) | ||
await loaders | ||
.getLoaders() | ||
.requests.load({ feedFullName: 'feedName2', timestamp: 2 } as any) | ||
|
||
expect(getFeedRequestsMock).toHaveBeenNthCalledWith(1, 'feedName1', 1) | ||
expect(getFeedRequestsMock).toHaveBeenNthCalledWith(2, 'feedName2', 2) | ||
}) | ||
|
||
it('should return the result of calling getFeedRequests', async () => { | ||
const getFeedRequestsMock = jest.fn(() => ({ feedFullName: 'name' })) | ||
const loaders = new Loaders({ | ||
resultRequestRepository: { | ||
getFeedRequests: getFeedRequestsMock | ||
} | ||
} as any) | ||
|
||
const result = await loaders.getLoaders().requests.load('feedName') | ||
|
||
expect(result).toStrictEqual({ feedFullName: 'name' }) | ||
}) | ||
}) | ||
}) |