Skip to content

Commit

Permalink
Merge pull request #7 from mindler-sasu/main
Browse files Browse the repository at this point in the history
support array accessors in attribute selections
  • Loading branch information
woltsu authored Mar 16, 2024
2 parents e63a83e + 548c3d6 commit 23c9af6
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`QueryQueryBuilder > handles a FilterExpression that takes attributes from cats array 1`] = `
[
{
"cats": [
{
"age": 42,
},
],
},
]
`;

exports[`QueryQueryBuilder > handles a FilterExpression that uses attribute_exists and attribute_not_exists 1`] = `
[
{
Expand Down
18 changes: 16 additions & 2 deletions src/queryBuilders/getItemQueryBuilder.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ describe("GetItemQueryBuilder", () => {
expect(Object.keys(data!).length).toBe(2);
});

it("handles selecting nested attributes excessively deep", async () => {
it("handles selecting deeply nested attributes", async () => {
const data = await tsynamoClient
.getItemFrom("myTable")
.keys({
Expand All @@ -76,7 +76,21 @@ describe("GetItemQueryBuilder", () => {
expect(Object.keys(data!).length).toBe(2);
});

it.todo("handles selecting attributes from arrays");
it("handles selecting attributes from arrays and tuples", async () => {
const data = await tsynamoClient
.getItemFrom("myOtherTable")
.keys({
userId: TEST_DATA[6].userId,
stringTimestamp: "123",
})
.consistentRead(true)
.attributes(["cats[1].age"])
.execute();

expect(Object.keys(data!).length).toBe(1);
expect(data?.cats?.length).toEqual(1);
expect(data?.cats?.[0].age).toEqual(TEST_DATA[6].cats[1].age);
});

it("can't await instance directly", async () => {
expect(
Expand Down
10 changes: 10 additions & 0 deletions src/queryBuilders/queryQueryBuilder.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,14 @@ describe("QueryQueryBuilder", () => {

expect(data).toMatchSnapshot();
});
it("handles a FilterExpression that takes attributes from cats array", async () => {
let data = await tsynamoClient
.query("myOtherTable")
.keyCondition("userId", "=", "123")
.filterExpression("cats", "attribute_exists")
.attributes(["cats[0].age"])
.execute();

expect(data).toMatchSnapshot();
});
});
18 changes: 15 additions & 3 deletions src/typeHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ type RecursiveSelectAttributes<Table, Properties> = Properties extends [
]
? First extends keyof Table
? { [key in First]: RecursiveSelectAttributes<Table[First], Rest> }
: // When "First" in path is a number, and Table is an Array, Recurse to the inner type of the array
[First, Table] extends [`${number}`, any[]]
? RecursiveSelectAttributes<As<Table, any[]>[number], Rest>[]
: never
: Table;

Expand Down Expand Up @@ -179,14 +182,23 @@ export type ObjectKeyPaths<T> =
never;

export type ObjectFullPaths<T> =
// if `T` is an object
// if `T` is an object or array
T extends Record<PropertyKey, unknown>
? // Assign the union `keyof T` to a variable `Key`
keyof T extends infer Key
? // Loop over union of keys
Key extends string | number
? // Check if the current key is an object, if it is, concatenate the key and rest of the path recursively
`${Key}` | `${Key}.${ObjectKeyPaths<T[Key]>}`
? // Check if the current key is an object
T[Key] extends Record<PropertyKey, unknown>
? // If it's an object, concatenate the key and rest of the path recursively
`${Key}` | `${Key}.${ObjectFullPaths<T[Key]>}`
: // If it's not an object, check if its an array
T[Key] extends (infer A)[]
? // If it's an array concatenate the key, array accessors, and the rest of the path recursively
| `${Key}`
| `${Key}[${number}]`
| `${Key}[${number}].${ObjectFullPaths<A[][number]>}`
: `${Key}`
: // unreachable branch (if key is symbol)
never
: // unreachable branch (`extends infer` is always truthy), but needs to be here for syntax
Expand Down
30 changes: 30 additions & 0 deletions test/testFixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@ export interface DDB {
stringTimestamp: SortKey<string>;
somethingElse: number;
someBoolean: boolean;
tuple: [
{
beer: string;
},
{
bar: string;
}
];
cats: {
name: string;
age: number;
}[];
};
}

Expand Down Expand Up @@ -162,6 +174,24 @@ export const TEST_DATA = [
stringTimestamp: "123",
somethingElse: -5,
someBoolean: true,
tuple: [
{
beer: "karhu",
},
{
bar: "oljenkorsi",
},
],
cats: [
{
name: "Pekka Töpöhäntä",
age: 42,
},
{
name: "Sylvester J. Pussycat Sr.",
age: 78,
},
],
},
{
userId: "123",
Expand Down

0 comments on commit 23c9af6

Please sign in to comment.