Skip to content

Commit 2576b48

Browse files
committed
fix(drizzle): cast jsonb selector params to eql_v2_encrypted
1 parent 6a64895 commit 2576b48

File tree

2 files changed

+88
-3
lines changed

2 files changed

+88
-3
lines changed
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import type { ProtectClient } from '@cipherstash/protect/client'
2+
import { PgDialect, pgTable } from 'drizzle-orm/pg-core'
3+
import { describe, expect, it, vi } from 'vitest'
4+
import { createProtectOperators, encryptedType } from '../src/pg'
5+
6+
const docsTable = pgTable('json_docs', {
7+
metadata: encryptedType<Record<string, unknown>>('metadata', {
8+
dataType: 'json',
9+
searchableJson: true,
10+
}),
11+
})
12+
13+
function createMockProtectClient() {
14+
const encryptedSelector = '{"v":"encrypted-selector"}'
15+
const encryptQuery = vi.fn(async (terms: unknown[]) => ({
16+
data: terms.map(() => encryptedSelector),
17+
}))
18+
19+
return {
20+
client: { encryptQuery } as unknown as ProtectClient,
21+
encryptQuery,
22+
encryptedSelector,
23+
}
24+
}
25+
26+
describe('createProtectOperators JSONB selector typing', () => {
27+
it('casts jsonbPathQueryFirst selector params to eql_v2_encrypted', async () => {
28+
const { client, encryptQuery, encryptedSelector } =
29+
createMockProtectClient()
30+
const protectOps = createProtectOperators(client)
31+
const dialect = new PgDialect()
32+
33+
const condition = await protectOps.jsonbPathQueryFirst(
34+
docsTable.metadata,
35+
'$.profile.email',
36+
)
37+
const query = dialect.sqlToQuery(condition)
38+
39+
expect(query.sql).toMatch(
40+
/eql_v2\.jsonb_path_query_first\([^,]+,\s*\$\d+::eql_v2_encrypted\)/,
41+
)
42+
expect(query.params).toHaveLength(1)
43+
expect(typeof query.params[0]).toBe('string')
44+
expect(encryptQuery).toHaveBeenCalledTimes(1)
45+
expect(encryptQuery.mock.calls[0]?.[0]).toMatchObject([
46+
{ queryType: 'steVecSelector' },
47+
])
48+
})
49+
50+
it('casts jsonbPathExists selector params to eql_v2_encrypted', async () => {
51+
const { client } = createMockProtectClient()
52+
const protectOps = createProtectOperators(client)
53+
const dialect = new PgDialect()
54+
55+
const condition = await protectOps.jsonbPathExists(
56+
docsTable.metadata,
57+
'$.profile.email',
58+
)
59+
const query = dialect.sqlToQuery(condition)
60+
61+
expect(query.sql).toMatch(
62+
/eql_v2\.jsonb_path_exists\([^,]+,\s*\$\d+::eql_v2_encrypted\)/,
63+
)
64+
expect(query.params).toHaveLength(1)
65+
expect(typeof query.params[0]).toBe('string')
66+
})
67+
68+
it('casts jsonbGet selector params to eql_v2_encrypted', async () => {
69+
const { client } = createMockProtectClient()
70+
const protectOps = createProtectOperators(client)
71+
const dialect = new PgDialect()
72+
73+
const condition = await protectOps.jsonbGet(
74+
docsTable.metadata,
75+
'$.profile.email',
76+
)
77+
const query = dialect.sqlToQuery(condition)
78+
79+
expect(query.sql).toMatch(/->\s*\$\d+::eql_v2_encrypted/)
80+
expect(query.params).toHaveLength(1)
81+
expect(typeof query.params[0]).toBe('string')
82+
})
83+
})

packages/drizzle/src/pg/operators.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,8 @@ function createJsonbOperator(
882882
protectTableCache: Map<string, ProtectTable<ProtectTableColumn>>,
883883
): Promise<SQL> {
884884
const { config } = columnInfo
885+
const encryptedSelector = (value: unknown) =>
886+
sql`${bindIfParam(value, left)}::eql_v2_encrypted`
885887

886888
if (!config?.searchableJson) {
887889
throw new ProtectOperatorError(
@@ -907,11 +909,11 @@ function createJsonbOperator(
907909
}
908910
switch (operator) {
909911
case 'jsonbPathQueryFirst':
910-
return sql`eql_v2.jsonb_path_query_first(${left}, ${bindIfParam(encrypted, left)})`
912+
return sql`eql_v2.jsonb_path_query_first(${left}, ${encryptedSelector(encrypted)})`
911913
case 'jsonbGet':
912-
return sql`${left} -> ${bindIfParam(encrypted, left)}`
914+
return sql`${left} -> ${encryptedSelector(encrypted)}`
913915
case 'jsonbPathExists':
914-
return sql`eql_v2.jsonb_path_exists(${left}, ${bindIfParam(encrypted, left)})`
916+
return sql`eql_v2.jsonb_path_exists(${left}, ${encryptedSelector(encrypted)})`
915917
}
916918
}
917919

0 commit comments

Comments
 (0)