@teleporthq/teleport-plugin-next-data-source 0.43.66 → 0.43.68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__tests__/array-mapper-cache-wiring.test.ts +11 -0
- package/__tests__/array-overlap-sql.test.ts +53 -0
- package/__tests__/blog-post-page-enrichment.test.ts +214 -0
- package/__tests__/browser-row-policy.test.ts +503 -0
- package/__tests__/data-source-cache-wrapping.test.ts +18 -2
- package/__tests__/data-source-request-locale.test.ts +313 -0
- package/__tests__/ecommerce-product-options.test.ts +206 -0
- package/__tests__/ecommerce-product-pages.test.ts +82 -0
- package/__tests__/ecommerce-product-pre-options.golden.json +505 -0
- package/__tests__/ecommerce-product-storefront-contract.test.ts +293 -0
- package/__tests__/ecommerce-product-subscriptions.test.ts +121 -0
- package/__tests__/fetchers.test.ts +2 -2
- package/__tests__/gated-data-provider.test.ts +88 -0
- package/__tests__/items-path.test.ts +150 -0
- package/__tests__/localized-columns.test.ts +217 -0
- package/__tests__/numbered-pagination.test.ts +15 -0
- package/__tests__/product-filter-fields.ts +230 -0
- package/__tests__/product-filter-sql.fixture.json +7 -0
- package/__tests__/product-options-storefront.fixture.json +2510 -0
- package/__tests__/product-page-url-fixtures.json +25 -0
- package/__tests__/product-price-sort.ts +4 -2
- package/__tests__/raw-rows-mode.test.ts +111 -0
- package/__tests__/refetch-loading-state.test.ts +23 -0
- package/__tests__/rest-api-response-cache.test.ts +434 -0
- package/__tests__/search-clear-button.test.ts +122 -0
- package/__tests__/table-read-route-protected-tables.test.ts +493 -0
- package/dist/cjs/array-overlap-sql.d.ts +21 -0
- package/dist/cjs/array-overlap-sql.d.ts.map +1 -0
- package/dist/cjs/array-overlap-sql.js +25 -0
- package/dist/cjs/array-overlap-sql.js.map +1 -0
- package/dist/cjs/content-localization.d.ts +37 -0
- package/dist/cjs/content-localization.d.ts.map +1 -0
- package/dist/cjs/content-localization.js +29 -0
- package/dist/cjs/content-localization.js.map +1 -0
- package/dist/cjs/count-effect.d.ts +11 -1
- package/dist/cjs/count-effect.d.ts.map +1 -1
- package/dist/cjs/count-effect.js +30 -2
- package/dist/cjs/count-effect.js.map +1 -1
- package/dist/cjs/data-source-fetchers.d.ts.map +1 -1
- package/dist/cjs/data-source-fetchers.js +10 -4
- package/dist/cjs/data-source-fetchers.js.map +1 -1
- package/dist/cjs/fetchers/airtable.d.ts.map +1 -1
- package/dist/cjs/fetchers/airtable.js +1 -1
- package/dist/cjs/fetchers/airtable.js.map +1 -1
- package/dist/cjs/fetchers/clickhouse.d.ts.map +1 -1
- package/dist/cjs/fetchers/clickhouse.js +1 -1
- package/dist/cjs/fetchers/clickhouse.js.map +1 -1
- package/dist/cjs/fetchers/firestore.d.ts.map +1 -1
- package/dist/cjs/fetchers/firestore.js +1 -1
- package/dist/cjs/fetchers/firestore.js.map +1 -1
- package/dist/cjs/fetchers/javascript.d.ts.map +1 -1
- package/dist/cjs/fetchers/javascript.js +1 -1
- package/dist/cjs/fetchers/javascript.js.map +1 -1
- package/dist/cjs/fetchers/mariadb.d.ts.map +1 -1
- package/dist/cjs/fetchers/mariadb.js +1 -1
- package/dist/cjs/fetchers/mariadb.js.map +1 -1
- package/dist/cjs/fetchers/mongodb.d.ts.map +1 -1
- package/dist/cjs/fetchers/mongodb.js +1 -1
- package/dist/cjs/fetchers/mongodb.js.map +1 -1
- package/dist/cjs/fetchers/mysql.d.ts.map +1 -1
- package/dist/cjs/fetchers/mysql.js +1 -1
- package/dist/cjs/fetchers/mysql.js.map +1 -1
- package/dist/cjs/fetchers/postgresql.d.ts +1 -1
- package/dist/cjs/fetchers/postgresql.d.ts.map +1 -1
- package/dist/cjs/fetchers/postgresql.js +9 -4
- package/dist/cjs/fetchers/postgresql.js.map +1 -1
- package/dist/cjs/fetchers/raw-query.d.ts +8 -1
- package/dist/cjs/fetchers/raw-query.d.ts.map +1 -1
- package/dist/cjs/fetchers/raw-query.js +42 -4
- package/dist/cjs/fetchers/raw-query.js.map +1 -1
- package/dist/cjs/fetchers/redshift.d.ts.map +1 -1
- package/dist/cjs/fetchers/redshift.js +1 -1
- package/dist/cjs/fetchers/redshift.js.map +1 -1
- package/dist/cjs/fetchers/rest-api.d.ts.map +1 -1
- package/dist/cjs/fetchers/rest-api.js +7 -2
- package/dist/cjs/fetchers/rest-api.js.map +1 -1
- package/dist/cjs/fetchers/teleport.d.ts.map +1 -1
- package/dist/cjs/fetchers/teleport.js +16 -6
- package/dist/cjs/fetchers/teleport.js.map +1 -1
- package/dist/cjs/fetchers/turso.d.ts.map +1 -1
- package/dist/cjs/fetchers/turso.js +1 -1
- package/dist/cjs/fetchers/turso.js.map +1 -1
- package/dist/cjs/fetchers/utils/browser-row-policy.d.ts +43 -0
- package/dist/cjs/fetchers/utils/browser-row-policy.d.ts.map +1 -0
- package/dist/cjs/fetchers/utils/browser-row-policy.js +66 -0
- package/dist/cjs/fetchers/utils/browser-row-policy.js.map +1 -0
- package/dist/cjs/fetchers/utils/request-sql-guards.d.ts +8 -0
- package/dist/cjs/fetchers/utils/request-sql-guards.d.ts.map +1 -0
- package/dist/cjs/fetchers/utils/request-sql-guards.js +12 -0
- package/dist/cjs/fetchers/utils/request-sql-guards.js.map +1 -0
- package/dist/cjs/fetchers/utils/rest-api-response-cache.d.ts +40 -0
- package/dist/cjs/fetchers/utils/rest-api-response-cache.d.ts.map +1 -0
- package/dist/cjs/fetchers/utils/rest-api-response-cache.js +64 -0
- package/dist/cjs/fetchers/utils/rest-api-response-cache.js.map +1 -0
- package/dist/cjs/fetchers/utils/table-access-guard.d.ts +23 -0
- package/dist/cjs/fetchers/utils/table-access-guard.d.ts.map +1 -0
- package/dist/cjs/fetchers/utils/table-access-guard.js +34 -0
- package/dist/cjs/fetchers/utils/table-access-guard.js.map +1 -0
- package/dist/cjs/index.d.ts +5 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +60 -8
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/items-path.d.ts +10 -0
- package/dist/cjs/items-path.d.ts.map +1 -0
- package/dist/cjs/items-path.js +86 -0
- package/dist/cjs/items-path.js.map +1 -0
- package/dist/cjs/loading-state.d.ts +16 -1
- package/dist/cjs/loading-state.d.ts.map +1 -1
- package/dist/cjs/loading-state.js +69 -3
- package/dist/cjs/loading-state.js.map +1 -1
- package/dist/cjs/localized-columns.d.ts +56 -0
- package/dist/cjs/localized-columns.d.ts.map +1 -0
- package/dist/cjs/localized-columns.js +137 -0
- package/dist/cjs/localized-columns.js.map +1 -0
- package/dist/cjs/pagination-plugin.d.ts.map +1 -1
- package/dist/cjs/pagination-plugin.js +140 -50
- package/dist/cjs/pagination-plugin.js.map +1 -1
- package/dist/cjs/product-filter-fields.d.ts +87 -0
- package/dist/cjs/product-filter-fields.d.ts.map +1 -0
- package/dist/cjs/product-filter-fields.js +161 -0
- package/dist/cjs/product-filter-fields.js.map +1 -0
- package/dist/cjs/request-locale.d.ts +49 -0
- package/dist/cjs/request-locale.d.ts.map +1 -0
- package/dist/cjs/request-locale.js +130 -0
- package/dist/cjs/request-locale.js.map +1 -0
- package/dist/cjs/search-clear.d.ts +31 -0
- package/dist/cjs/search-clear.d.ts.map +1 -0
- package/dist/cjs/search-clear.js +103 -0
- package/dist/cjs/search-clear.js.map +1 -0
- package/dist/cjs/transformations/blog-adjacent-posts.d.ts +16 -0
- package/dist/cjs/transformations/blog-adjacent-posts.d.ts.map +1 -0
- package/dist/cjs/transformations/blog-adjacent-posts.js +25 -0
- package/dist/cjs/transformations/blog-adjacent-posts.js.map +1 -0
- package/dist/cjs/transformations/blog-comments.d.ts +30 -0
- package/dist/cjs/transformations/blog-comments.d.ts.map +1 -0
- package/dist/cjs/transformations/blog-comments.js +34 -0
- package/dist/cjs/transformations/blog-comments.js.map +1 -0
- package/dist/cjs/transformations/blog-post.d.ts +2 -0
- package/dist/cjs/transformations/blog-post.d.ts.map +1 -1
- package/dist/cjs/transformations/blog-post.js +4 -1
- package/dist/cjs/transformations/blog-post.js.map +1 -1
- package/dist/cjs/transformations/ecommerce-product.d.ts +9 -1
- package/dist/cjs/transformations/ecommerce-product.d.ts.map +1 -1
- package/dist/cjs/transformations/ecommerce-product.js +28 -1
- package/dist/cjs/transformations/ecommerce-product.js.map +1 -1
- package/dist/cjs/transformations/heading-anchors.d.ts +25 -0
- package/dist/cjs/transformations/heading-anchors.d.ts.map +1 -0
- package/dist/cjs/transformations/heading-anchors.js +29 -0
- package/dist/cjs/transformations/heading-anchors.js.map +1 -0
- package/dist/cjs/transformations/index.d.ts +61 -4
- package/dist/cjs/transformations/index.d.ts.map +1 -1
- package/dist/cjs/transformations/index.js +69 -19
- package/dist/cjs/transformations/index.js.map +1 -1
- package/dist/cjs/transformations/shared-utils.d.ts.map +1 -1
- package/dist/cjs/transformations/shared-utils.js +1 -1
- package/dist/cjs/transformations/shared-utils.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/cjs/utils.d.ts +11 -1
- package/dist/cjs/utils.d.ts.map +1 -1
- package/dist/cjs/utils.js +60 -16
- package/dist/cjs/utils.js.map +1 -1
- package/dist/esm/array-overlap-sql.d.ts +21 -0
- package/dist/esm/array-overlap-sql.d.ts.map +1 -0
- package/dist/esm/array-overlap-sql.js +21 -0
- package/dist/esm/array-overlap-sql.js.map +1 -0
- package/dist/esm/content-localization.d.ts +37 -0
- package/dist/esm/content-localization.d.ts.map +1 -0
- package/dist/esm/content-localization.js +24 -0
- package/dist/esm/content-localization.js.map +1 -0
- package/dist/esm/count-effect.d.ts +11 -1
- package/dist/esm/count-effect.d.ts.map +1 -1
- package/dist/esm/count-effect.js +28 -1
- package/dist/esm/count-effect.js.map +1 -1
- package/dist/esm/data-source-fetchers.d.ts.map +1 -1
- package/dist/esm/data-source-fetchers.js +11 -5
- package/dist/esm/data-source-fetchers.js.map +1 -1
- package/dist/esm/fetchers/airtable.d.ts.map +1 -1
- package/dist/esm/fetchers/airtable.js +1 -1
- package/dist/esm/fetchers/airtable.js.map +1 -1
- package/dist/esm/fetchers/clickhouse.d.ts.map +1 -1
- package/dist/esm/fetchers/clickhouse.js +1 -1
- package/dist/esm/fetchers/clickhouse.js.map +1 -1
- package/dist/esm/fetchers/firestore.d.ts.map +1 -1
- package/dist/esm/fetchers/firestore.js +1 -1
- package/dist/esm/fetchers/firestore.js.map +1 -1
- package/dist/esm/fetchers/javascript.d.ts.map +1 -1
- package/dist/esm/fetchers/javascript.js +1 -1
- package/dist/esm/fetchers/javascript.js.map +1 -1
- package/dist/esm/fetchers/mariadb.d.ts.map +1 -1
- package/dist/esm/fetchers/mariadb.js +1 -1
- package/dist/esm/fetchers/mariadb.js.map +1 -1
- package/dist/esm/fetchers/mongodb.d.ts.map +1 -1
- package/dist/esm/fetchers/mongodb.js +1 -1
- package/dist/esm/fetchers/mongodb.js.map +1 -1
- package/dist/esm/fetchers/mysql.d.ts.map +1 -1
- package/dist/esm/fetchers/mysql.js +1 -1
- package/dist/esm/fetchers/mysql.js.map +1 -1
- package/dist/esm/fetchers/postgresql.d.ts +1 -1
- package/dist/esm/fetchers/postgresql.d.ts.map +1 -1
- package/dist/esm/fetchers/postgresql.js +9 -4
- package/dist/esm/fetchers/postgresql.js.map +1 -1
- package/dist/esm/fetchers/raw-query.d.ts +8 -1
- package/dist/esm/fetchers/raw-query.d.ts.map +1 -1
- package/dist/esm/fetchers/raw-query.js +42 -4
- package/dist/esm/fetchers/raw-query.js.map +1 -1
- package/dist/esm/fetchers/redshift.d.ts.map +1 -1
- package/dist/esm/fetchers/redshift.js +1 -1
- package/dist/esm/fetchers/redshift.js.map +1 -1
- package/dist/esm/fetchers/rest-api.d.ts.map +1 -1
- package/dist/esm/fetchers/rest-api.js +7 -2
- package/dist/esm/fetchers/rest-api.js.map +1 -1
- package/dist/esm/fetchers/teleport.d.ts.map +1 -1
- package/dist/esm/fetchers/teleport.js +16 -6
- package/dist/esm/fetchers/teleport.js.map +1 -1
- package/dist/esm/fetchers/turso.d.ts.map +1 -1
- package/dist/esm/fetchers/turso.js +1 -1
- package/dist/esm/fetchers/turso.js.map +1 -1
- package/dist/esm/fetchers/utils/browser-row-policy.d.ts +43 -0
- package/dist/esm/fetchers/utils/browser-row-policy.d.ts.map +1 -0
- package/dist/esm/fetchers/utils/browser-row-policy.js +60 -0
- package/dist/esm/fetchers/utils/browser-row-policy.js.map +1 -0
- package/dist/esm/fetchers/utils/request-sql-guards.d.ts +8 -0
- package/dist/esm/fetchers/utils/request-sql-guards.d.ts.map +1 -0
- package/dist/esm/fetchers/utils/request-sql-guards.js +8 -0
- package/dist/esm/fetchers/utils/request-sql-guards.js.map +1 -0
- package/dist/esm/fetchers/utils/rest-api-response-cache.d.ts +40 -0
- package/dist/esm/fetchers/utils/rest-api-response-cache.d.ts.map +1 -0
- package/dist/esm/fetchers/utils/rest-api-response-cache.js +59 -0
- package/dist/esm/fetchers/utils/rest-api-response-cache.js.map +1 -0
- package/dist/esm/fetchers/utils/table-access-guard.d.ts +23 -0
- package/dist/esm/fetchers/utils/table-access-guard.d.ts.map +1 -0
- package/dist/esm/fetchers/utils/table-access-guard.js +29 -0
- package/dist/esm/fetchers/utils/table-access-guard.js.map +1 -0
- package/dist/esm/index.d.ts +5 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +55 -7
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/items-path.d.ts +10 -0
- package/dist/esm/items-path.d.ts.map +1 -0
- package/dist/esm/items-path.js +58 -0
- package/dist/esm/items-path.js.map +1 -0
- package/dist/esm/loading-state.d.ts +16 -1
- package/dist/esm/loading-state.d.ts.map +1 -1
- package/dist/esm/loading-state.js +69 -3
- package/dist/esm/loading-state.js.map +1 -1
- package/dist/esm/localized-columns.d.ts +56 -0
- package/dist/esm/localized-columns.d.ts.map +1 -0
- package/dist/esm/localized-columns.js +132 -0
- package/dist/esm/localized-columns.js.map +1 -0
- package/dist/esm/pagination-plugin.d.ts.map +1 -1
- package/dist/esm/pagination-plugin.js +141 -51
- package/dist/esm/pagination-plugin.js.map +1 -1
- package/dist/esm/product-filter-fields.d.ts +87 -0
- package/dist/esm/product-filter-fields.d.ts.map +1 -0
- package/dist/esm/product-filter-fields.js +154 -0
- package/dist/esm/product-filter-fields.js.map +1 -0
- package/dist/esm/request-locale.d.ts +49 -0
- package/dist/esm/request-locale.d.ts.map +1 -0
- package/dist/esm/request-locale.js +98 -0
- package/dist/esm/request-locale.js.map +1 -0
- package/dist/esm/search-clear.d.ts +31 -0
- package/dist/esm/search-clear.d.ts.map +1 -0
- package/dist/esm/search-clear.js +74 -0
- package/dist/esm/search-clear.js.map +1 -0
- package/dist/esm/transformations/blog-adjacent-posts.d.ts +16 -0
- package/dist/esm/transformations/blog-adjacent-posts.d.ts.map +1 -0
- package/dist/esm/transformations/blog-adjacent-posts.js +21 -0
- package/dist/esm/transformations/blog-adjacent-posts.js.map +1 -0
- package/dist/esm/transformations/blog-comments.d.ts +30 -0
- package/dist/esm/transformations/blog-comments.d.ts.map +1 -0
- package/dist/esm/transformations/blog-comments.js +30 -0
- package/dist/esm/transformations/blog-comments.js.map +1 -0
- package/dist/esm/transformations/blog-post.d.ts +2 -0
- package/dist/esm/transformations/blog-post.d.ts.map +1 -1
- package/dist/esm/transformations/blog-post.js +4 -1
- package/dist/esm/transformations/blog-post.js.map +1 -1
- package/dist/esm/transformations/ecommerce-product.d.ts +9 -1
- package/dist/esm/transformations/ecommerce-product.d.ts.map +1 -1
- package/dist/esm/transformations/ecommerce-product.js +29 -2
- package/dist/esm/transformations/ecommerce-product.js.map +1 -1
- package/dist/esm/transformations/heading-anchors.d.ts +25 -0
- package/dist/esm/transformations/heading-anchors.d.ts.map +1 -0
- package/dist/esm/transformations/heading-anchors.js +25 -0
- package/dist/esm/transformations/heading-anchors.js.map +1 -0
- package/dist/esm/transformations/index.d.ts +61 -4
- package/dist/esm/transformations/index.d.ts.map +1 -1
- package/dist/esm/transformations/index.js +69 -19
- package/dist/esm/transformations/index.js.map +1 -1
- package/dist/esm/transformations/shared-utils.d.ts.map +1 -1
- package/dist/esm/transformations/shared-utils.js +1 -1
- package/dist/esm/transformations/shared-utils.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/utils.d.ts +11 -1
- package/dist/esm/utils.d.ts.map +1 -1
- package/dist/esm/utils.js +60 -16
- package/dist/esm/utils.js.map +1 -1
- package/package.json +5 -5
- package/src/array-overlap-sql.ts +41 -0
- package/src/content-localization.ts +53 -0
- package/src/count-effect.ts +53 -2
- package/src/data-source-fetchers.ts +41 -5
- package/src/fetchers/airtable.ts +4 -2
- package/src/fetchers/clickhouse.ts +4 -2
- package/src/fetchers/firestore.ts +4 -2
- package/src/fetchers/javascript.ts +9 -1
- package/src/fetchers/mariadb.ts +4 -2
- package/src/fetchers/mongodb.ts +4 -2
- package/src/fetchers/mysql.ts +4 -2
- package/src/fetchers/postgresql.ts +53 -12
- package/src/fetchers/raw-query.ts +60 -5
- package/src/fetchers/redshift.ts +4 -2
- package/src/fetchers/rest-api.ts +27 -5
- package/src/fetchers/teleport.ts +85 -22
- package/src/fetchers/turso.ts +4 -2
- package/src/fetchers/utils/browser-row-policy.ts +212 -0
- package/src/fetchers/utils/request-sql-guards.ts +25 -0
- package/src/fetchers/utils/rest-api-response-cache.ts +188 -0
- package/src/fetchers/utils/table-access-guard.ts +39 -0
- package/src/index.ts +79 -11
- package/src/items-path.ts +74 -0
- package/src/loading-state.ts +134 -3
- package/src/localized-columns.ts +146 -0
- package/src/pagination-plugin.ts +206 -60
- package/src/product-filter-fields.ts +173 -0
- package/src/request-locale.ts +113 -0
- package/src/search-clear.ts +102 -0
- package/src/transformations/blog-adjacent-posts.ts +74 -0
- package/src/transformations/blog-comments.ts +132 -0
- package/src/transformations/blog-post.ts +38 -5
- package/src/transformations/ecommerce-product.ts +568 -5
- package/src/transformations/heading-anchors.ts +85 -0
- package/src/transformations/index.ts +191 -23
- package/src/transformations/shared-utils.ts +6 -0
- package/src/utils.ts +109 -19
|
@@ -10,6 +10,10 @@ import {
|
|
|
10
10
|
generateSortFieldSqlHelper,
|
|
11
11
|
generateSortTiebreakSql,
|
|
12
12
|
} from '../product-price-sort'
|
|
13
|
+
import { generateProductFilterClauseHelper } from '../product-filter-fields'
|
|
14
|
+
import { generateReadGuardCall, generateTableAccessPreamble } from './utils/table-access-guard'
|
|
15
|
+
import { generateRequestSqlGuardsCode } from './utils/request-sql-guards'
|
|
16
|
+
import { generateArrayOverlapSqlCode } from '../array-overlap-sql'
|
|
13
17
|
|
|
14
18
|
interface PostgreSQLConfig {
|
|
15
19
|
connectionString?: string
|
|
@@ -26,7 +30,8 @@ interface PostgreSQLConfig {
|
|
|
26
30
|
|
|
27
31
|
export const generatePostgreSQLFetcher = (
|
|
28
32
|
config: Record<string, unknown>,
|
|
29
|
-
tableName: string
|
|
33
|
+
tableName: string,
|
|
34
|
+
trustedReaderRoles: ReadonlyArray<string> = []
|
|
30
35
|
): string => {
|
|
31
36
|
const pgConfig = config as PostgreSQLConfig
|
|
32
37
|
const schema = pgConfig.options?.schema
|
|
@@ -57,6 +62,8 @@ export const generatePostgreSQLFetcher = (
|
|
|
57
62
|
|
|
58
63
|
return `import { Client } from 'pg'
|
|
59
64
|
|
|
65
|
+
${generateTableAccessPreamble(trustedReaderRoles)}
|
|
66
|
+
|
|
60
67
|
const getClient = () => {
|
|
61
68
|
return new Client(${clientConfig})
|
|
62
69
|
}
|
|
@@ -66,9 +73,11 @@ ${generateSafeJSONParseCode()}
|
|
|
66
73
|
${generateFilterTreeHelpersCode()}
|
|
67
74
|
|
|
68
75
|
${generateSearchEscapeHelpersCode()}
|
|
76
|
+
${generateArrayOverlapSqlCode()}
|
|
69
77
|
|
|
70
78
|
// Builds one SQL clause for the whole filter tree, so an OR group nested in
|
|
71
79
|
// the root AND keeps its meaning instead of being flattened into ANDs.
|
|
80
|
+
${generateRequestSqlGuardsCode()}
|
|
72
81
|
const processFilters = (filters, conditions, queryParams, paramIndex) => {
|
|
73
82
|
if (!filters) return paramIndex
|
|
74
83
|
|
|
@@ -77,13 +86,26 @@ const processFilters = (filters, conditions, queryParams, paramIndex) => {
|
|
|
77
86
|
|
|
78
87
|
const buildCondition = (condition) => {
|
|
79
88
|
const field = condition.source
|
|
80
|
-
const
|
|
81
|
-
|
|
89
|
+
const normalizedCondition = normalizeInOperand(condition.operand, condition.destination)
|
|
90
|
+
if (normalizedCondition === null) return null
|
|
91
|
+
const value = normalizedCondition.value
|
|
92
|
+
const operand = normalizedCondition.operand
|
|
93
|
+
|
|
94
|
+
// The products table's virtual fields (effective price, rating bucket,
|
|
95
|
+
// on-sale flag, variant axes) are whole clauses, not columns - so they are
|
|
96
|
+
// answered before any branch below can interpolate the field name raw.
|
|
97
|
+
const productClause = productFilterClause(field, operand, value, (param) => {
|
|
98
|
+
queryParams.push(param)
|
|
99
|
+
return '$' + paramIndex++
|
|
100
|
+
})
|
|
101
|
+
if (productClause !== null) return productClause
|
|
102
|
+
assertRequestColumn(field, 'filter field')
|
|
82
103
|
|
|
83
104
|
if (Array.isArray(value)) {
|
|
84
105
|
if (value.length === 0) return null
|
|
85
106
|
if (operand === 'array_overlap') {
|
|
86
|
-
|
|
107
|
+
// The row's list (see array-overlap-sql.ts) shares any value with the set.
|
|
108
|
+
const clause = arrayOverlapSql([field], '$' + paramIndex)
|
|
87
109
|
queryParams.push(value.map((entry) => String(entry)))
|
|
88
110
|
paramIndex++
|
|
89
111
|
return clause
|
|
@@ -101,7 +123,7 @@ const processFilters = (filters, conditions, queryParams, paramIndex) => {
|
|
|
101
123
|
// ?categoryFilter=a,b,c) expands to multiple ids; one id stays one.
|
|
102
124
|
const overlapValues = String(value).split(',').map((entry) => entry.trim()).filter(Boolean)
|
|
103
125
|
if (overlapValues.length === 0) return null
|
|
104
|
-
const clause =
|
|
126
|
+
const clause = arrayOverlapSql([field], '$' + paramIndex)
|
|
105
127
|
queryParams.push(overlapValues)
|
|
106
128
|
paramIndex++
|
|
107
129
|
return clause
|
|
@@ -133,7 +155,10 @@ ${generateSortFieldSqlHelper(tableName)}
|
|
|
133
155
|
|
|
134
156
|
${generateSortFallbackFieldHelper(tableName)}
|
|
135
157
|
|
|
158
|
+
${generateProductFilterClauseHelper(tableName)}
|
|
159
|
+
|
|
136
160
|
export default async function handler(req, res) {
|
|
161
|
+
${generateReadGuardCall(tableName, null)}
|
|
137
162
|
const client = getClient()
|
|
138
163
|
|
|
139
164
|
try {
|
|
@@ -141,6 +166,8 @@ export default async function handler(req, res) {
|
|
|
141
166
|
${schema ? `await client.query('SET search_path TO ${schema}')` : ''}
|
|
142
167
|
|
|
143
168
|
const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset } = req.query
|
|
169
|
+
// A visitor never filters, sorts or searches by a column it is not served.
|
|
170
|
+
if (__rowPolicy) __brpAssertVisibleFields(__rowPolicy, req.query)
|
|
144
171
|
|
|
145
172
|
const conditions = []
|
|
146
173
|
const queryParams = []
|
|
@@ -175,6 +202,7 @@ export default async function handler(req, res) {
|
|
|
175
202
|
// Continue without search if we can't get columns
|
|
176
203
|
}
|
|
177
204
|
}
|
|
205
|
+
if (__rowPolicy) columns = __brpVisibleColumns(__rowPolicy, columns)
|
|
178
206
|
|
|
179
207
|
if (columns.length > 0) {
|
|
180
208
|
const pattern = '%' + escapeLikePattern(query) + '%'
|
|
@@ -190,6 +218,7 @@ export default async function handler(req, res) {
|
|
|
190
218
|
|
|
191
219
|
// Apply filters using helper function
|
|
192
220
|
paramIndex = processFilters(filters, conditions, queryParams, paramIndex)
|
|
221
|
+
if (__rowPolicy) conditions.push('(' + __rowPolicy.predicate + ')')
|
|
193
222
|
|
|
194
223
|
let sql = \`SELECT * FROM "${tableName}"\`
|
|
195
224
|
|
|
@@ -205,6 +234,7 @@ export default async function handler(req, res) {
|
|
|
205
234
|
const parsedSorts = safeJSONParse(sorts)
|
|
206
235
|
if (Array.isArray(parsedSorts) && parsedSorts.length > 0) {
|
|
207
236
|
const valid = parsedSorts.filter((sort) => sort && sort.field)
|
|
237
|
+
valid.forEach((sort) => assertRequestColumn(sort.field, 'sort field'))
|
|
208
238
|
const orderOf = (sort) => (sort.order || '').toUpperCase().startsWith('DESC') ? 'DESC' : 'ASC'
|
|
209
239
|
const orderClauses = valid.map((sort) => \`\${sortFieldSql(sort.field)} \${orderOf(sort)}\`)
|
|
210
240
|
const plainClauses = valid.map((sort) => \`\${sortFallbackField(sort.field)} \${orderOf(sort)}\`)
|
|
@@ -222,16 +252,17 @@ export default async function handler(req, res) {
|
|
|
222
252
|
}
|
|
223
253
|
}
|
|
224
254
|
} else if (sortBy) {
|
|
255
|
+
assertRequestColumn(sortBy, 'sort field')
|
|
225
256
|
orderBySql = \` ORDER BY \${sortBy} \${(sortOrder || '').toUpperCase().startsWith('DESC') ? 'DESC' : 'ASC'}\`
|
|
226
257
|
plainOrderBySql = orderBySql
|
|
227
258
|
}
|
|
228
259
|
const usedDiscountAwareSort = orderBySql !== plainOrderBySql
|
|
229
260
|
|
|
230
|
-
const limitValue = limit || perPage
|
|
261
|
+
const limitValue = requestRowCount(limit || perPage)
|
|
231
262
|
const offsetValue = offset !== undefined ? parseInt(offset) : (page && perPage ? (parseInt(page) - 1) * parseInt(perPage) : undefined)
|
|
232
263
|
|
|
233
264
|
let sqlTail = ''
|
|
234
|
-
if (limitValue) {
|
|
265
|
+
if (limitValue !== undefined) {
|
|
235
266
|
sqlTail += \` LIMIT \${limitValue}\`
|
|
236
267
|
}
|
|
237
268
|
|
|
@@ -265,14 +296,20 @@ export default async function handler(req, res) {
|
|
|
265
296
|
|
|
266
297
|
return res.status(200).json({
|
|
267
298
|
success: true,
|
|
268
|
-
data:
|
|
299
|
+
data: __taWithoutCredentials(
|
|
300
|
+
__rowPolicy ? __brpStripHidden(__rowPolicy, safeData) : safeData,
|
|
301
|
+
${JSON.stringify(tableName)}
|
|
302
|
+
),
|
|
269
303
|
timestamp: Date.now()
|
|
270
304
|
})
|
|
271
305
|
} catch (error) {
|
|
272
306
|
console.error('PostgreSQL fetch error:', error)
|
|
273
|
-
|
|
307
|
+
// A database error names tables, columns and values: it stays in the server
|
|
308
|
+
// log. Only an error this route raised for the caller (it carries a status)
|
|
309
|
+
// is repeated to it.
|
|
310
|
+
return res.status(error.status || 500).json({
|
|
274
311
|
success: false,
|
|
275
|
-
error: error.message
|
|
312
|
+
error: error.status ? error.message : 'Failed to fetch data',
|
|
276
313
|
timestamp: Date.now()
|
|
277
314
|
})
|
|
278
315
|
} finally {
|
|
@@ -297,11 +334,13 @@ export const generatePostgreSQLCountFetcher = (
|
|
|
297
334
|
|
|
298
335
|
return `
|
|
299
336
|
async function getCount(req, res) {
|
|
337
|
+
${generateReadGuardCall(tableName, null)}
|
|
300
338
|
const client = getClient()
|
|
301
339
|
|
|
302
340
|
try {
|
|
303
341
|
await client.connect()
|
|
304
342
|
const { query, queryColumns, filters } = req.query
|
|
343
|
+
if (__rowPolicy) __brpAssertVisibleFields(__rowPolicy, req.query)
|
|
305
344
|
const conditions = []
|
|
306
345
|
const queryParams = []
|
|
307
346
|
let paramIndex = 1
|
|
@@ -336,6 +375,7 @@ async function getCount(req, res) {
|
|
|
336
375
|
// Continue without search if we can't get columns
|
|
337
376
|
}
|
|
338
377
|
}
|
|
378
|
+
if (__rowPolicy) columns = __brpVisibleColumns(__rowPolicy, columns)
|
|
339
379
|
|
|
340
380
|
if (columns.length > 0) {
|
|
341
381
|
const pattern = '%' + escapeLikePattern(query) + '%'
|
|
@@ -353,6 +393,7 @@ async function getCount(req, res) {
|
|
|
353
393
|
|
|
354
394
|
// Apply filters using helper function
|
|
355
395
|
paramIndex = processFilters(filters, conditions, queryParams, paramIndex)
|
|
396
|
+
if (__rowPolicy) conditions.push('(' + __rowPolicy.predicate + ')')
|
|
356
397
|
|
|
357
398
|
let countSql = \`SELECT COUNT(*) FROM "${tableName}"\`
|
|
358
399
|
if (conditions.length > 0) {
|
|
@@ -369,9 +410,9 @@ async function getCount(req, res) {
|
|
|
369
410
|
})
|
|
370
411
|
} catch (error) {
|
|
371
412
|
console.error('Error getting count:', error)
|
|
372
|
-
return res.status(500).json({
|
|
413
|
+
return res.status(error.status || 500).json({
|
|
373
414
|
success: false,
|
|
374
|
-
error: error.message
|
|
415
|
+
error: error.status ? error.message : 'Failed to get count',
|
|
375
416
|
timestamp: Date.now()
|
|
376
417
|
})
|
|
377
418
|
} finally {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SessionCookieResolver } from '@teleporthq/teleport-shared'
|
|
1
2
|
import { replaceSecretReference } from '../utils'
|
|
2
3
|
|
|
3
4
|
interface PostgreSQLConfig {
|
|
@@ -13,9 +14,28 @@ interface PostgreSQLConfig {
|
|
|
13
14
|
|
|
14
15
|
const FORBIDDEN_KEYWORDS = ['CREATE', 'ALTER', 'DROP', 'TRUNCATE', 'RENAME', 'GRANT', 'REVOKE']
|
|
15
16
|
|
|
17
|
+
/** The param a `{{Current User.<field>}}` token becomes: `currentUser` + the field, capitalised. */
|
|
18
|
+
const CURRENT_USER_PARAM_PREFIX = 'currentUser'
|
|
19
|
+
|
|
20
|
+
const isCurrentUserParam = (param: string): boolean =>
|
|
21
|
+
param.length > CURRENT_USER_PARAM_PREFIX.length && param.indexOf(CURRENT_USER_PARAM_PREFIX) === 0
|
|
22
|
+
|
|
23
|
+
/** The user field a `currentUser<Field>` param stands for (`currentUserId` → `id`). */
|
|
24
|
+
const currentUserField = (param: string): string => {
|
|
25
|
+
const rest = param.slice(CURRENT_USER_PARAM_PREFIX.length)
|
|
26
|
+
return rest.charAt(0).toLowerCase() + rest.slice(1)
|
|
27
|
+
}
|
|
28
|
+
|
|
16
29
|
/**
|
|
17
30
|
* Generates an API route handler that executes a parameterized raw SQL query.
|
|
18
|
-
* Supports {{Current User.*}} substitution
|
|
31
|
+
* Supports {{Current User.*}} substitution — bound to the SESSION's user.
|
|
32
|
+
*
|
|
33
|
+
* ⛔ A `{{Current User.<field>}}` value is read from the signed-in session on
|
|
34
|
+
* the server, never from the request: the query string is whatever the caller
|
|
35
|
+
* wrote, so a route that bound `?currentUserId=` served any user's rows (their
|
|
36
|
+
* favourites, their orders) to anyone who named that user's id. A caller with
|
|
37
|
+
* no session gets no rows, exactly as when the value is missing. Other params
|
|
38
|
+
* (`currentPageEntityId`, the details page's route param) stay request values.
|
|
19
39
|
*
|
|
20
40
|
* @param config - PostgreSQL connection config
|
|
21
41
|
* @param query - Raw SQL query string with {{Current User.*}} patterns already replaced to $N placeholders
|
|
@@ -27,9 +47,43 @@ export const generateRawQueryFetcher = (
|
|
|
27
47
|
paramFields: string[]
|
|
28
48
|
): string => {
|
|
29
49
|
const pgConfig = config as PostgreSQLConfig
|
|
50
|
+
const userParams = paramFields.filter(isCurrentUserParam)
|
|
51
|
+
const requestParams = paramFields.filter((param) => !isCurrentUserParam(param))
|
|
52
|
+
|
|
53
|
+
const paramDestructure = [
|
|
54
|
+
requestParams.length > 0 ? `const { ${requestParams.join(', ')} } = req.query` : '',
|
|
55
|
+
userParams.length > 0 ? 'const __sessionUser = await sessionUser(req)' : '',
|
|
56
|
+
...userParams.map(
|
|
57
|
+
(param) =>
|
|
58
|
+
`const ${param} = sessionUserField(__sessionUser, ${JSON.stringify(
|
|
59
|
+
currentUserField(param)
|
|
60
|
+
)})`
|
|
61
|
+
),
|
|
62
|
+
]
|
|
63
|
+
.filter(Boolean)
|
|
64
|
+
.join('\n ')
|
|
65
|
+
|
|
66
|
+
const sessionHelpers =
|
|
67
|
+
userParams.length > 0
|
|
68
|
+
? `${SessionCookieResolver.generateCommonJsSessionTokenResolverCode()}
|
|
69
|
+
// The signed-in user this request's session names, or null.
|
|
70
|
+
async function sessionUser(req) {
|
|
71
|
+
try {
|
|
72
|
+
return await __tqSessionToken(req)
|
|
73
|
+
} catch (e) {
|
|
74
|
+
return null
|
|
75
|
+
}
|
|
76
|
+
}
|
|
30
77
|
|
|
31
|
-
|
|
32
|
-
|
|
78
|
+
// One field of that user, as the session carries it (the token is seeded from
|
|
79
|
+
// the users row), or undefined. The id is the token's subject when not copied.
|
|
80
|
+
function sessionUserField(user, field) {
|
|
81
|
+
if (!user) return undefined
|
|
82
|
+
var value = field === 'id' && user.id == null ? user.sub : user[field]
|
|
83
|
+
return value == null || value === '' ? undefined : value
|
|
84
|
+
}
|
|
85
|
+
`
|
|
86
|
+
: ''
|
|
33
87
|
|
|
34
88
|
const paramValidation =
|
|
35
89
|
paramFields.length > 0
|
|
@@ -58,7 +112,7 @@ export const generateRawQueryFetcher = (
|
|
|
58
112
|
return `import { Client } from 'pg'
|
|
59
113
|
|
|
60
114
|
const FORBIDDEN_KEYWORDS = ${JSON.stringify(FORBIDDEN_KEYWORDS)}
|
|
61
|
-
|
|
115
|
+
${sessionHelpers}
|
|
62
116
|
const getClient = () => {
|
|
63
117
|
const connStr = process.env.TELEPORT_DB_CONNECTION_STRING
|
|
64
118
|
if (connStr) {
|
|
@@ -122,10 +176,11 @@ export default async function handler(req, res) {
|
|
|
122
176
|
await client.end()
|
|
123
177
|
}
|
|
124
178
|
} catch (error) {
|
|
179
|
+
// A database error names tables, columns and values: server log only.
|
|
125
180
|
console.error('Raw query fetch error:', error)
|
|
126
181
|
return res.status(500).json({
|
|
127
182
|
success: false,
|
|
128
|
-
error:
|
|
183
|
+
error: 'Failed to fetch data',
|
|
129
184
|
timestamp: Date.now()
|
|
130
185
|
})
|
|
131
186
|
}
|
package/src/fetchers/redshift.ts
CHANGED
|
@@ -127,8 +127,10 @@ export default async function handler(req, res) {
|
|
|
127
127
|
if (filterTree) {
|
|
128
128
|
const buildCondition = (condition) => {
|
|
129
129
|
const field = sanitizeIdentifier(condition.source)
|
|
130
|
-
const
|
|
131
|
-
|
|
130
|
+
const normalizedCondition = normalizeInOperand(condition.operand, condition.destination)
|
|
131
|
+
if (normalizedCondition === null) return null
|
|
132
|
+
const value = normalizedCondition.value
|
|
133
|
+
const operand = normalizedCondition.operand
|
|
132
134
|
|
|
133
135
|
if (Array.isArray(value)) {
|
|
134
136
|
if (value.length === 0) return null
|
package/src/fetchers/rest-api.ts
CHANGED
|
@@ -5,6 +5,11 @@ import {
|
|
|
5
5
|
generateSafeJSONParseCode,
|
|
6
6
|
generateFilterTreeHelpersCode,
|
|
7
7
|
} from '../utils'
|
|
8
|
+
import {
|
|
9
|
+
RESTAPICacheConfig,
|
|
10
|
+
generateRESTAPIResponseCacheCode,
|
|
11
|
+
resolveRESTAPICacheTTLSeconds,
|
|
12
|
+
} from './utils/rest-api-response-cache'
|
|
8
13
|
|
|
9
14
|
export const validateRESTAPIConfig = (
|
|
10
15
|
config: Record<string, unknown>
|
|
@@ -76,11 +81,16 @@ interface RESTAPIConfig {
|
|
|
76
81
|
authorization?: Authorization
|
|
77
82
|
bodyType?: string
|
|
78
83
|
body?: string
|
|
84
|
+
cache?: RESTAPICacheConfig
|
|
79
85
|
}
|
|
80
86
|
|
|
81
87
|
export const generateRESTAPIFetcher = (config: Record<string, unknown>): string => {
|
|
82
88
|
const restConfig = config as RESTAPIConfig
|
|
83
89
|
const authCode = generateAuthCode(restConfig.authorization || {})
|
|
90
|
+
// Every cache-only fragment below is empty when caching is off, so an
|
|
91
|
+
// uncached data source keeps generating byte-identical output.
|
|
92
|
+
const cacheTTLSeconds = resolveRESTAPICacheTTLSeconds(restConfig.cache)
|
|
93
|
+
const cached = cacheTTLSeconds !== undefined
|
|
84
94
|
|
|
85
95
|
return `import fetch from 'node-fetch'
|
|
86
96
|
|
|
@@ -92,11 +102,13 @@ ${generateSortFilterHelperCode()}
|
|
|
92
102
|
|
|
93
103
|
${generateFilterTreeHelpersCode()}
|
|
94
104
|
|
|
95
|
-
${generateCollectionPathHelperCode()}
|
|
105
|
+
${generateCollectionPathHelperCode()}${
|
|
106
|
+
cached ? `\n\n${generateRESTAPIResponseCacheCode(cacheTTLSeconds)}` : ''
|
|
107
|
+
}
|
|
96
108
|
|
|
97
109
|
export default async function handler(req, res) {
|
|
98
110
|
try {
|
|
99
|
-
const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset, collectionPath } = req.query
|
|
111
|
+
const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset, collectionPath, itemsPath } = req.query
|
|
100
112
|
|
|
101
113
|
const url = ${JSON.stringify(restConfig.url)}
|
|
102
114
|
const method = ${JSON.stringify(restConfig.method || 'GET')}
|
|
@@ -134,7 +146,7 @@ export default async function handler(req, res) {
|
|
|
134
146
|
: ''
|
|
135
147
|
}
|
|
136
148
|
|
|
137
|
-
const response = await fetch(url, options)
|
|
149
|
+
const response = await ${cached ? 'fetchRestApiWithCache' : 'fetch'}(url, options)
|
|
138
150
|
|
|
139
151
|
if (!response.ok) {
|
|
140
152
|
return res.status(response.status).json({
|
|
@@ -144,9 +156,13 @@ export default async function handler(req, res) {
|
|
|
144
156
|
})
|
|
145
157
|
}
|
|
146
158
|
|
|
147
|
-
let data = await response.json()
|
|
159
|
+
let data = ${cached ? 'JSON.parse(response.bodyText)' : 'await response.json()'}
|
|
148
160
|
|
|
149
161
|
data = resolveCollectionPath(data, collectionPath)
|
|
162
|
+
const itemsEnvelope = openItemsEnvelope(data, itemsPath)
|
|
163
|
+
if (itemsEnvelope) {
|
|
164
|
+
data = itemsEnvelope.items
|
|
165
|
+
}
|
|
150
166
|
|
|
151
167
|
if (Array.isArray(data)) {
|
|
152
168
|
if (query && query.trim()) {
|
|
@@ -278,9 +294,15 @@ export default async function handler(req, res) {
|
|
|
278
294
|
}
|
|
279
295
|
}
|
|
280
296
|
|
|
297
|
+
if (itemsEnvelope) {
|
|
298
|
+
data = itemsEnvelope.close(data)
|
|
299
|
+
}
|
|
300
|
+
|
|
281
301
|
const safeData = JSON.parse(JSON.stringify(data, dateReplacer))
|
|
282
302
|
|
|
283
|
-
|
|
303
|
+
${
|
|
304
|
+
cached ? 'applyRestApiCacheHeaders(req, res, response.fetchedAt)\n \n ' : ''
|
|
305
|
+
}return res.status(200).json({
|
|
284
306
|
success: true,
|
|
285
307
|
data: safeData,
|
|
286
308
|
timestamp: Date.now()
|