diff --git a/packages/core/src/lib/functional-extensions/filter.ts b/packages/core/src/lib/functional-extensions/filter.ts index b6341be5..f4ee3cdd 100644 --- a/packages/core/src/lib/functional-extensions/filter.ts +++ b/packages/core/src/lib/functional-extensions/filter.ts @@ -16,7 +16,7 @@ export interface FunctionalFilterOptions { export type FunctionalFilter = ( options: FunctionalFilterOptions -) => Promise; +) => Promise; export const createFilterExtension = ( name: string, diff --git a/packages/core/test/functional-extensions/filter.spec.ts b/packages/core/test/functional-extensions/filter.spec.ts index edca3386..3b09914a 100644 --- a/packages/core/test/functional-extensions/filter.spec.ts +++ b/packages/core/test/functional-extensions/filter.spec.ts @@ -43,3 +43,31 @@ it('The extensions created by createFilter function should work with template en expect(queries[0]).toBe('SELECT $1'); expect(bindings[0].get('$1')).toBe('QAQ3QQQQ'); }); + +it('The extensions can return arbitrary value', async () => { + // Arrange + const testFilter: FunctionalFilter = async ({ args, value }) => + ({id: args['aa'], text: `QAQ${args['aa']}${value}`}); + const [Builder, Runner] = createFilterExtension('test', testFilter); + const builder = new Builder({}, ''); + const runner = new Runner({}, ''); + const { + compiler, + loader, + executeTemplate, + getCreatedQueries, + getCreatedBinding, + } = await createTestCompiler({ additionalExtensions: [builder, runner] }); + const { compiledData } = await compiler.compile( + `SELECT {{ (context.params.id | test(aa=3)).id }}, {{ (context.params.id | test(aa=3)).text }}` + ); + loader.setSource('test', compiledData); + // Act + await executeTemplate('test', { id: 'QQQQ' }); + const queries = await getCreatedQueries(); + const bindings = await getCreatedBinding(); + // Assert + expect(queries[0]).toBe('SELECT $1, $2'); + expect(bindings[0].get('$1')).toBe(3); + expect(bindings[0].get('$2')).toBe('QAQ3QQQQ'); +}); diff --git a/packages/extension-api-caller/README.md b/packages/extension-api-caller/README.md index 7ec66959..fb281a3d 100644 --- a/packages/extension-api-caller/README.md +++ b/packages/extension-api-caller/README.md @@ -23,26 +23,26 @@ To pass the path parameters: ```sql {% set a_variable_you_can_define = { "path": { "id": 1 } } %} -SELECT {{ a_variable_you_can_define | rest_api(url='https://dummyjson.com/products/:id') }} +SELECT {{ (a_variable_you_can_define | rest_api(url='https://dummyjson.com/products/:id')).id }} ``` To pass the query parameters: ```sql {% set a_variable_you_can_define = { "query": { "q": "phone" } } %} -SELECT {{ a_variable_you_can_define | rest_api(url='https://dummyjson.com/products/search') }} +SELECT {{ a_variable_you_can_define | rest_api(url='https://dummyjson.com/products/search') | dump }} ``` To issue the POST request: ```sql {% set a_variable_you_can_define = { "body": { "title": "BMW Pencil" } } %} -SELECT {{ a_variable_you_can_define | rest_api(url='https://dummyjson.com/products/add', method='POST') }} +SELECT {{ a_variable_you_can_define | rest_api(url='https://dummyjson.com/products/add', method='POST') | dump }} ``` To pass the headers and multiple fields: ```sql {% set a_variable_you_can_define = { "headers": { "Content-Type": "application/json" }, "body": { "title": "BMW Pencil" } } %} -SELECT {{ a_variable_you_can_define | rest_api(url='https://dummyjson.com/products/add', method='POST') }} +SELECT {{ a_variable_you_can_define | rest_api(url='https://dummyjson.com/products/add', method='POST') | dump }} ``` \ No newline at end of file diff --git a/packages/extension-api-caller/src/lib/filters/restApiCaller.ts b/packages/extension-api-caller/src/lib/filters/restApiCaller.ts index 842fd74a..56f6861e 100644 --- a/packages/extension-api-caller/src/lib/filters/restApiCaller.ts +++ b/packages/extension-api-caller/src/lib/filters/restApiCaller.ts @@ -48,7 +48,7 @@ export const RestApiCallerFilter: FunctionalFilter = async ({ logger.info('API request:', options); const results = await axios(options); logger.info('API response:', results.data); - return JSON.stringify(results.data); + return results.data; } catch (error: any) { const message = error.response ? `response status: ${error.response.status}, response data: ${JSON.stringify(error.response.data)}` diff --git a/packages/extension-api-caller/test/restApiCaller.spec.ts b/packages/extension-api-caller/test/restApiCaller.spec.ts index bff23980..fcc230c5 100644 --- a/packages/extension-api-caller/test/restApiCaller.spec.ts +++ b/packages/extension-api-caller/test/restApiCaller.spec.ts @@ -50,7 +50,7 @@ describe('Test "rest_api" filter', () => { extensions: { rest_api: path.join(__dirname, '..', 'src') }, }); - const sql = `{% set value = { "path": { "id": 1 } } %}SELECT {{ value | rest_api(url='https://dummyjson.com/products/:id') }}`; + const sql = `{% set value = { "path": { "id": 1 } } %}SELECT {{ value | rest_api(url='https://dummyjson.com/products/:id') | dump }}`; // Act await compileAndLoad(sql); @@ -155,7 +155,7 @@ describe('Test "rest_api" filter', () => { extensions: { rest_api: path.join(__dirname, '..', 'src') }, }); - const sql = `{% set value = { "query": { "q": "phone" } } %}SELECT {{ value | rest_api(url='https://dummyjson.com/products/search') }}`; + const sql = `{% set value = { "query": { "q": "phone" } } %}SELECT {{ value | rest_api(url='https://dummyjson.com/products/search') | dump }}`; // Act await compileAndLoad(sql); @@ -183,7 +183,35 @@ describe('Test "rest_api" filter', () => { extensions: { rest_api: path.join(__dirname, '..', 'src') }, }); - const sql = `{% set value = { "body": { "title": "BMW Pencil" }, "headers": { "Content-Type": "application/json" } } %}SELECT {{ value | rest_api(url='https://dummyjson.com/products/add', method='POST') }}`; + const sql = `{% set value = { "body": { "title": "BMW Pencil" }, "headers": { "Content-Type": "application/json" } } %}SELECT {{ value | rest_api(url='https://dummyjson.com/products/add', method='POST') | dump }}`; + + // Act + await compileAndLoad(sql); + await execute({}); + + // Assert + const queries = await getExecutedQueries(); + const bindings = await getCreatedBinding(); + + expect(queries[0]).toBe('SELECT $1'); + expect(bindings[0].get('$1')).toEqual(expected); + }, + 50 * 1000 + ) + + it( + 'Should work with template engine with field access', + async () => { + const expected = { + id: 101, + title: 'BMW Pencil' + }.id; + + const { compileAndLoad, execute, getExecutedQueries, getCreatedBinding } = await getTestCompiler({ + extensions: { rest_api: path.join(__dirname, '..', 'src') }, + }); + + const sql = `{% set value = { "body": { "title": "BMW Pencil" }, "headers": { "Content-Type": "application/json" } } %}SELECT {{ (value | rest_api(url='https://dummyjson.com/products/add', method='POST')).id }}`; // Act await compileAndLoad(sql);