Skip to content

Commit

Permalink
fix(datasource-mongoose): error on a nested field when requesting a c…
Browse files Browse the repository at this point in the history
…hild property on a missing value (#860)
  • Loading branch information
Guillaume Gautreau authored Oct 26, 2023
1 parent f25e4b0 commit 6a04be7
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 3 deletions.
3 changes: 2 additions & 1 deletion packages/datasource-mongoose/src/collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { Error, Model, PipelineStage } from 'mongoose';

import MongooseSchema from './mongoose/schema';
import { Stack } from './types';
import addNullValues from './utils/add-null-values';
import {
buildSubdocumentPatch,
compareIds,
Expand Down Expand Up @@ -79,7 +80,7 @@ export default class MongooseCollection extends BaseCollection {
...ProjectionGenerator.project(projection),
]);

return replaceMongoTypes(records);
return addNullValues(replaceMongoTypes(records), projection);
}

async aggregate(
Expand Down
48 changes: 48 additions & 0 deletions packages/datasource-mongoose/src/utils/add-null-values.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
function addNullValuesOnRecord(
record: Record<string, unknown>,
projection: string[],
): Record<string, unknown> {
if (!record) return record;

const result = { ...record };

for (const field of projection) {
const fieldPrefix = field.split(':')[0];

if (result[fieldPrefix] === undefined) {
result[fieldPrefix] = null;
}
}

const nestedPrefixes = new Set(
projection.filter(field => field.includes(':')).map(field => field.split(':')[0]),
);

for (const nestedPrefix of nestedPrefixes) {
const childPaths = projection
.filter(field => field.startsWith(`${nestedPrefix}:`))
.map(field => field.substring(nestedPrefix.length + 1));

if (result[nestedPrefix] !== null && result[nestedPrefix] !== undefined) {
if (Array.isArray(result[nestedPrefix])) {
result[nestedPrefix] = (result[nestedPrefix] as Record<string, unknown>[]).map(
childRecord => addNullValuesOnRecord(childRecord, childPaths),
);
} else if (typeof result[nestedPrefix] === 'object') {
result[nestedPrefix] = addNullValuesOnRecord(
result[nestedPrefix] as Record<string, unknown>,
childPaths,
);
}
}
}

return result;
}

export default function addNullValues(
records: Record<string, unknown>[],
projection: string[],
): Record<string, unknown>[] {
return records.map(record => addNullValuesOnRecord(record, projection));
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ describe('MongooseCollection', () => {
new Projection('name', 'storeId__manyToOne:name'),
);

expect(expectedOwner).toEqual([{ name: 'aOwner' }]);
expect(expectedOwner).toEqual([{ name: 'aOwner', storeId__manyToOne: null }]);
});

it('should filter by relation', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,22 @@ describe('MongooseCollection', () => {

expect(records).toEqual([{ message: 'a message' }]);
});

it('should return the given record with null as the nested projection', async () => {
connection = await setupReview('collection_review_list');
const dataSource = new MongooseDatasource(connection);
const review = dataSource.getCollection('review');
const expectedRecord = { message: 'a message', title: 'a title' };
await review.create(factories.caller.build(), [expectedRecord]);

const records = await review.list(
factories.caller.build(),
factories.filter.build(),
new Projection('nestedField:nested:level'),
);

expect(records).toEqual([{ nestedField: null }]);
});
});

describe('condition tree', () => {
Expand Down Expand Up @@ -389,7 +405,7 @@ describe('MongooseCollection', () => {
field: 'nestedField.nested.level',
}),
}),
new Projection('nestedField.nested.level'),
new Projection('nestedField:nested:level'),
);

expect(records).toEqual([{ nestedField: { nested: [{ level: 10 }] } }]);
Expand Down
68 changes: 68 additions & 0 deletions packages/datasource-mongoose/test/utils/add-null-values.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import addNullValues from '../../src/utils/add-null-values';

describe('addNullValues', () => {
it('should return a different object with null values added', () => {
const input = {
id: 1,
name: 'John',
};
const projection = ['id', 'name', 'address'];

const result = addNullValues([input], projection);

expect(result).not.toBe(input);
expect(result).toEqual([{ id: 1, name: 'John', address: null }]);
});

it('should add null values in nested objects', () => {
const input = {
id: 1,
name: 'John',
address: {
street: 'Main Street',
},
};
const projection = ['id', 'name', 'address:street', 'address:city'];

const result = addNullValues([input], projection);

expect(result).toEqual([
{
id: 1,
name: 'John',
address: {
street: 'Main Street',
city: null,
},
},
]);
});

it('should add null values in nested arrays', () => {
const input = {
id: 1,
name: 'John',
addresses: [
{
street: 'Main Street',
},
],
};
const projection = ['id', 'name', 'addresses:street', 'addresses:city'];

const result = addNullValues([input], projection);

expect(result).toEqual([
{
id: 1,
name: 'John',
addresses: [
{
street: 'Main Street',
city: null,
},
],
},
]);
});
});

0 comments on commit 6a04be7

Please sign in to comment.