Skip to content

Commit

Permalink
Allow define projection fields in read model queries
Browse files Browse the repository at this point in the history
  • Loading branch information
gonzalojaubert committed Oct 25, 2023
1 parent eee152c commit f759db3
Show file tree
Hide file tree
Showing 17 changed files with 608 additions and 32 deletions.
15 changes: 12 additions & 3 deletions packages/framework-core/src/booster-read-models-reader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
ReadOnlyNonEmptyArray,
SortFor,
SubscriptionEnvelope,
ProjectionFor,
} from '@boostercloud/framework-types'
import { createInstance, createInstances, getLogger } from '@boostercloud/framework-common-helpers'
import { Booster } from './booster'
Expand Down Expand Up @@ -71,7 +72,9 @@ export class BoosterReadModelsReader {
readModelTransformedRequest.sortBy,
readModelTransformedRequest.limit,
readModelTransformedRequest.afterCursor,
readModelTransformedRequest.paginatedVersion
readModelTransformedRequest.paginatedVersion,
readModelTransformedRequest.select,
readModelTransformedRequest.skipInstance
)
}

Expand All @@ -82,7 +85,9 @@ export class BoosterReadModelsReader {
sort?: SortFor<unknown>,
limit?: number,
afterCursor?: any,
paginatedVersion?: boolean
paginatedVersion?: boolean,
select?: ProjectionFor<TReadModel>,
skipInstance?: boolean
): Promise<Array<TReadModel> | ReadModelListResult<TReadModel>> {
const readModelName = readModelClass.name
const searchResult = await this.config.provider.readModels.search<TReadModel>(
Expand All @@ -92,9 +97,13 @@ export class BoosterReadModelsReader {
sort ?? {},
limit,
afterCursor,
paginatedVersion ?? false
paginatedVersion ?? false,
select
)

if (skipInstance) {
return searchResult
}
const readModels = this.createReadModelInstances(searchResult, readModelClass)
return this.migrateReadModels(readModels, readModelName)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,8 @@ describe('BoosterReadModelReader', () => {
{},
undefined,
undefined,
false
false,
undefined
)
expect(result).to.be.deep.equal(expectedReadModels)
})
Expand All @@ -408,7 +409,8 @@ describe('BoosterReadModelReader', () => {
{},
undefined,
undefined,
false
false,
undefined
)
expect(result).to.be.deep.equal(expectedReadModels)
expect(migratorStub).to.have.been.calledTwice
Expand Down Expand Up @@ -438,12 +440,86 @@ describe('BoosterReadModelReader', () => {
{},
undefined,
undefined,
true
true,
undefined
)
expect(result).to.be.deep.equal(searchResult)
expect(migratorStub).to.have.been.calledTwice
})

context('when there are projections fields', () => {
it('calls the provider search function with the right parameters', async () => {
const readModelWithProjectionRequestEnvelope: ReadModelRequestEnvelope<TestReadModel> = {
class: TestReadModel,
className: TestReadModel.name,
requestID: random.uuid(),
version: 1,
filters,
currentUser,
select: ['id'],
skipInstance: false,
} as any

const expectedReadModels = [new TestReadModel(), new TestReadModel()]
const providerSearcherFunctionFake = fake.returns(expectedReadModels)
replace(config.provider.readModels, 'search', providerSearcherFunctionFake)

replace(Booster, 'config', config) // Needed because the function `Booster.readModel` references `this.config` from `searchFunction`

migratorStub.callsFake(async (readModel, readModelName) => readModel)

const result = await readModelReader.search(readModelWithProjectionRequestEnvelope)

expect(providerSearcherFunctionFake).to.have.been.calledOnceWithExactly(
match.any,
TestReadModel.name,
filters,
{},
undefined,
undefined,
false,
['id']
)
expect(result).to.be.deep.equal(expectedReadModels)
})

it('do not call migrates if skipInstance is true', async () => {
const readModelWithProjectionRequestEnvelope: ReadModelRequestEnvelope<TestReadModel> = {
class: TestReadModel,
className: TestReadModel.name,
requestID: random.uuid(),
version: 1,
filters,
currentUser,
select: ['id'],
skipInstance: true,
} as any

const expectedResult = [new TestReadModel(), new TestReadModel()]
const providerSearcherFunctionFake = fake.returns(expectedResult)
replace(config.provider.readModels, 'search', providerSearcherFunctionFake)

replace(Booster, 'config', config) // Needed because the function `Booster.readModel` references `this.config` from `searchFunction`

migratorStub.callsFake(async (readModel, readModelName) => readModel)

const result = await readModelReader.search(readModelWithProjectionRequestEnvelope)

expect(providerSearcherFunctionFake).to.have.been.calledOnceWithExactly(
match.any,
TestReadModel.name,
filters,
{},
undefined,
undefined,
false,
['id']
)
expect(result).to.be.deep.equal(expectedResult)
expect(migratorStub).to.have.not.been.called
})
})

context('when there is only one before hook function', () => {
const fakeBeforeFn = fake(beforeFn)

Expand Down
3 changes: 2 additions & 1 deletion packages/framework-core/test/booster.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ describe('the `Booster` class', () => {
{},
undefined,
undefined,
false
false,
undefined
)
})
it('has an instance method', async () => {
Expand Down
Loading

0 comments on commit f759db3

Please sign in to comment.