Skip to content

Commit c262403

Browse files
Merge pull request #33 from constructive-io/lucas/node-sql-template-v2
feat(node-sql): add SQL-based function template with example
2 parents 8f4d30e + 975b635 commit c262403

13 files changed

Lines changed: 589 additions & 46 deletions

File tree

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
const mockQuery = jest.fn();
2+
const mockRelease = jest.fn();
3+
4+
const createMockContext = () => {
5+
const mockClient = {
6+
query: mockQuery,
7+
release: mockRelease,
8+
};
9+
10+
return {
11+
job: {
12+
jobId: 'test-job-id',
13+
workerId: 'test-worker',
14+
databaseId: 'test-db',
15+
},
16+
pool: {
17+
connect: jest.fn().mockResolvedValue(mockClient),
18+
},
19+
withUserContext: jest.fn(async (_actorId: string | undefined, fn: (client: typeof mockClient) => Promise<unknown>) => {
20+
return fn(mockClient);
21+
}),
22+
log: {
23+
info: jest.fn(),
24+
error: jest.fn(),
25+
warn: jest.fn(),
26+
},
27+
env: {},
28+
};
29+
};
30+
31+
const loadHandler = () => {
32+
const mod = require('../handler');
33+
return mod.default ?? mod;
34+
};
35+
36+
describe('sql-example handler', () => {
37+
beforeEach(() => {
38+
jest.clearAllMocks();
39+
mockQuery.mockReset();
40+
});
41+
42+
it('should execute default query (SELECT version()) when no query provided', async () => {
43+
const handler = loadHandler();
44+
const context = createMockContext();
45+
mockQuery.mockResolvedValueOnce({ rows: [{ version: 'PostgreSQL 15.0' }] });
46+
47+
const result = await handler({}, context);
48+
49+
expect(result.success).toBe(true);
50+
expect(result.message).toBe('Query executed successfully');
51+
expect(context.withUserContext).toHaveBeenCalledWith(undefined, expect.any(Function));
52+
});
53+
54+
it('should execute custom query', async () => {
55+
const handler = loadHandler();
56+
const context = createMockContext();
57+
mockQuery.mockResolvedValueOnce({ rows: [{ count: 5 }] });
58+
59+
const result = await handler({ query: 'SELECT count(*) FROM users' }, context);
60+
61+
expect(result.success).toBe(true);
62+
expect(result.data).toEqual([{ count: 5 }]);
63+
});
64+
65+
it('should pass actor_id to withUserContext', async () => {
66+
const handler = loadHandler();
67+
const context = createMockContext();
68+
mockQuery.mockResolvedValueOnce({ rows: [] });
69+
70+
await handler({ actor_id: 'user-123' }, context);
71+
72+
expect(context.withUserContext).toHaveBeenCalledWith('user-123', expect.any(Function));
73+
});
74+
});

functions/sql-example/handler.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"name": "sql-example",
3+
"version": "1.0.0",
4+
"type": "node-sql",
5+
"description": "Example function using node-sql template for direct PostgreSQL access"
6+
}

functions/sql-example/handler.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import type { FunctionHandler } from './types';
2+
3+
type Params = {
4+
query?: string;
5+
actor_id?: string;
6+
};
7+
8+
type Result = {
9+
success: boolean;
10+
message: string;
11+
data?: unknown;
12+
};
13+
14+
const handler: FunctionHandler<Params, Result> = async (params, context) => {
15+
const { log, withUserContext } = context;
16+
const { query = 'SELECT version()', actor_id } = params;
17+
18+
log.info('[sql-example] Executing query', { query, actor_id });
19+
20+
const result = await withUserContext(actor_id, async (client) => {
21+
const res = await client.query(query);
22+
return res.rows;
23+
});
24+
25+
log.info('[sql-example] Query complete', { rowCount: result.length });
26+
27+
return {
28+
success: true,
29+
message: 'Query executed successfully',
30+
data: result,
31+
};
32+
};
33+
34+
export default handler;

0 commit comments

Comments
 (0)