Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Filter invalid values in getIds #979

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 94 additions & 16 deletions src/__tests__/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import { deepStrictEqual, equal, ok } from 'node:assert/strict';
import { describe, test } from 'node:test';
import { deepStrictEqual, ok } from 'node:assert/strict';
import { describe, test, TestContext } from 'node:test';
import { ObjectId } from 'mongodb';
import { expectType } from 'ts-expect';
import { DefaultsOption } from '../schema';
import { NestedPaths, ProjectionType, getIds, PropertyType, getDefaultValues } from '../utils';
import {
NestedPaths,
ProjectionType,
getIds,
PropertyType,
getDefaultValues,
ObjectIdConstructorParameter,
} from '../utils';

describe('utils', () => {
interface TestDocument {
Expand Down Expand Up @@ -146,7 +153,7 @@

describe('ProjectionType', () => {
test('required fields', () => {
const foo = { foo: 1 };

Check warning on line 156 in src/__tests__/utils.test.ts

View workflow job for this annotation

GitHub Actions / lint

'foo' is assigned a value but only used as a type

const testFoo: ProjectionType<TestDocument, typeof foo> = {
_id: new ObjectId(),
Expand All @@ -163,7 +170,7 @@
// @ts-expect-error `ham` should be undefined here
testFoo.ham;

const bar = { bar: 1 };

Check warning on line 173 in src/__tests__/utils.test.ts

View workflow job for this annotation

GitHub Actions / lint

'bar' is assigned a value but only used as a type

const testBar: ProjectionType<TestDocument, typeof bar> = {
_id: new ObjectId(),
Expand All @@ -182,7 +189,7 @@
});

test('multiple mixed fields', () => {
const multiple = {

Check warning on line 192 in src/__tests__/utils.test.ts

View workflow job for this annotation

GitHub Actions / lint

'multiple' is assigned a value but only used as a type
bar: 1,
ham: 1,
};
Expand All @@ -205,7 +212,7 @@
});

test('nested fields', () => {
const nested = {

Check warning on line 215 in src/__tests__/utils.test.ts

View workflow job for this annotation

GitHub Actions / lint

'nested' is assigned a value but only used as a type
foo: 1,
'nestedList.0.direct': 1,
'nestedObject.deep.deeper': 1,
Expand Down Expand Up @@ -379,19 +386,90 @@
});

describe('getIds', () => {
for (const input of [
['123456789012345678900001', '123456789012345678900002'],
[new ObjectId('123456789012345678900001'), new ObjectId('123456789012345678900002')],
['123456789012345678900001', new ObjectId('123456789012345678900002')],
]) {
test('case', () => {
const result = getIds(input);

equal(result.length, 2);
ok(result[0] instanceof ObjectId);
equal(result[0].toHexString(), '123456789012345678900001');
ok(result[1] instanceof ObjectId);
equal(result[1].toHexString(), '123456789012345678900002');
const testCases: readonly [
string,
{
readonly input: readonly ObjectIdConstructorParameter[];
readonly expected: readonly ObjectId[];
},
][] = [
[
'strings',
{
input: ['123456789012345678900001', '123456789012345678900002'],
expected: [
new ObjectId('123456789012345678900001'),
new ObjectId('123456789012345678900002'),
],
},
],
[
'ObjectIds',
{
input: [
new ObjectId('123456789012345678900099'),
new ObjectId('123456789012345678900022'),
],
expected: [
new ObjectId('123456789012345678900099'),
new ObjectId('123456789012345678900022'),
],
},
],
[
'Uint8Arrays',
{
input: [
new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]),
new Uint8Array([13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]),
],
expected: [
new ObjectId('0102030405060708090a0b0c'),
new ObjectId('0d0e0f101112131415161718'),
],
},
],
[
'mixed',
{
input: ['123456789012345678900014', new ObjectId('123456789012345678900088')],
expected: [
new ObjectId('123456789012345678900014'),
new ObjectId('123456789012345678900088'),
],
},
],
[
'invalid values',
{
input: ['123', '123456789012345678900021'],
expected: [new ObjectId('123456789012345678900021')],
},
],
];

for (const testCase of testCases) {
const [caseName, { input, expected }] = testCase;

test(`case ${caseName}`, (t: TestContext) => {
t.plan(4);
// Given

t.assert.ok(expected.length <= input.length);

// When
const actual = getIds(input);

// Then
t.assert.deepStrictEqual(actual, expected);

const isEveryObjectId = actual.every((id) => id instanceof ObjectId);
t.assert.ok(isEveryObjectId);

const isEveryHexEquivalent = actual.every(
(id, index) => id.toHexString() === expected[index].toHexString()
);
t.assert.ok(isEveryHexEquivalent);
});
}
});
Expand Down
13 changes: 11 additions & 2 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,17 @@ export type RequireAtLeastOne<TObj, Keys extends keyof TObj = keyof TObj> = {
}[Keys] &
Pick<TObj, Exclude<keyof TObj, Keys>>;

export function getIds(ids: Set<string> | readonly (ObjectId | string)[]): ObjectId[] {
return [...ids].map((id) => new ObjectId(id));
export type ObjectIdConstructorParameter = ConstructorParameters<typeof ObjectId>[0];
export function getIds(ids: Iterable<ObjectIdConstructorParameter>): ObjectId[] {
return Array.from(ids).flatMap((id) => {
try {
return new ObjectId(id);
} catch {
// Intentionally empty
}

return [];
});
}

/**
Expand Down
Loading