@teleporthq/teleport-plugin-next-static-props 0.44.0 → 0.44.1

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/src/index.ts CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  FileType,
6
6
  TeleportError,
7
7
  } from '@teleporthq/teleport-types'
8
- import { StringUtils, GenericUtils } from '@teleporthq/teleport-shared'
8
+ import { StringUtils, GenericUtils, TableAccess } from '@teleporthq/teleport-shared'
9
9
  import { RouteUtils } from '@teleporthq/teleport-plugin-common'
10
10
  import { generateInitialPropsAST } from './utils'
11
11
 
@@ -13,7 +13,17 @@ interface StaticPropsPluginConfig {
13
13
  componentChunkName?: string
14
14
  }
15
15
 
16
- const { isDynamicRoute, pageHasSameTableMutationWorkflow, pageReadsProtectedTable } = RouteUtils
16
+ const {
17
+ isDynamicRoute,
18
+ pageDataReadArguments,
19
+ pageDataReadsAsStore,
20
+ pageHasSameTableMutationWorkflow,
21
+ pageReadsProtectedTable,
22
+ } = RouteUtils
23
+
24
+ // The data sources whose read routes guard their tables (see
25
+ // teleport-plugin-next-data-source `table-access-guard.ts`).
26
+ const GUARDED_SOURCE_TYPES = ['teleport', 'postgresql']
17
27
 
18
28
  export const createStaticPropsPlugin: ComponentPluginFactory<StaticPropsPluginConfig> = (
19
29
  config
@@ -50,6 +60,10 @@ export const createStaticPropsPlugin: ComponentPluginFactory<StaticPropsPluginCo
50
60
  Name of the function that is being imported
51
61
  */
52
62
  let resourceImportName
63
+ // The page's row is what it renders, so it is read as a visitor is served
64
+ // it — unless only the store's trusted readers can open the page.
65
+ const readsAsVisitor = !pageDataReadsAsStore(uidl, options.auth)
66
+ let entityReadable = true
53
67
 
54
68
  if (isLocalResource) {
55
69
  const usedResource = resources.items?.[resource.id]
@@ -63,6 +77,20 @@ export const createStaticPropsPlugin: ComponentPluginFactory<StaticPropsPluginCo
63
77
  StringUtils.camelCaseToDashCase(usedResource.name + 'Resource')
64
78
  )
65
79
 
80
+ // A visitor is served no row of an order or a gift card, so a page that
81
+ // is not the admin's renders without one rather than carry a row read as
82
+ // the store. A member's page reads their public profile only (see
83
+ // `TableAccess.PUBLIC_PROFILE_COLUMNS`): read as the store, it put any
84
+ // member's whole account in the page data of whoever opened `/profile/<id>`.
85
+ const tableName = usedResource.params?.tableName?.content
86
+ const sourceType = usedResource.params?.dataSourceType?.content
87
+ entityReadable = !(
88
+ readsAsVisitor &&
89
+ typeof tableName === 'string' &&
90
+ GUARDED_SOURCE_TYPES.indexOf(String(sourceType)) !== -1 &&
91
+ TableAccess.pageDataReadsNoRowOf(tableName)
92
+ )
93
+
66
94
  dependencies[resourceImportName] = {
67
95
  type: 'local',
68
96
  path: `${GenericUtils.generatePageDependenciesPrefix({
@@ -83,7 +111,11 @@ export const createStaticPropsPlugin: ComponentPluginFactory<StaticPropsPluginCo
83
111
  resourceImportName,
84
112
  resources.cache,
85
113
  options.skipI18n,
86
- useServerSideProps
114
+ useServerSideProps,
115
+ // A resource the project generated forwards it to the data-source module;
116
+ // another package's function takes its own arguments.
117
+ () => (isLocalResource ? pageDataReadArguments(uidl, options.auth) : []),
118
+ entityReadable
87
119
  )
88
120
 
89
121
  // NOTE: the chunk is always registered under the 'getStaticProps' name
package/src/utils.ts CHANGED
@@ -19,7 +19,11 @@ export const generateInitialPropsAST = (
19
19
  * auth-gated per request, so there is no real CDN-caching upside being
20
20
  * traded away. See `createStaticPropsPlugin` for the selection logic.
21
21
  */
22
- useServerSideProps?: boolean
22
+ useServerSideProps?: boolean,
23
+ /** What the page's read hands the resource after its params (`{ as: 'visitor' }`). */
24
+ readArguments: () => types.Expression[] = () => [],
25
+ /** False when the page's reader is served no row of the table: it renders without one. */
26
+ entityReadable: boolean = true
23
27
  ) => {
24
28
  // Destructure params from context so expr-type resource params can reference `params` directly
25
29
  const paramsDestructureAST = types.variableDeclaration('const', [
@@ -51,7 +55,9 @@ export const generateInitialPropsAST = (
51
55
  resourceImportName,
52
56
  revalidateSeconds,
53
57
  skipI18n,
54
- useServerSideProps
58
+ useServerSideProps,
59
+ readArguments,
60
+ entityReadable
55
61
  ),
56
62
  ]),
57
63
  types.catchClause(
@@ -138,7 +144,9 @@ const computePropsAST = (
138
144
  resourceImportName: string,
139
145
  revalidateSeconds: number | null,
140
146
  skipI18n?: boolean,
141
- useServerSideProps?: boolean
147
+ useServerSideProps?: boolean,
148
+ readArguments: () => types.Expression[] = () => [],
149
+ entityReadable: boolean = true
142
150
  ) => {
143
151
  const funcParams: types.ObjectProperty[] = Object.keys(
144
152
  initialPropsData.resource?.params || {}
@@ -218,6 +226,7 @@ const computePropsAST = (
218
226
  : types.cloneNode(param, true)
219
227
  ),
220
228
  ]),
229
+ ...readArguments(),
221
230
  ])
222
231
  )
223
232
 
@@ -288,6 +297,10 @@ const computePropsAST = (
288
297
  )
289
298
  )
290
299
 
300
+ if (!entityReadable) {
301
+ return [buildUnreadableEntityReturnAST(initialPropsData, revalidateProperty)]
302
+ }
303
+
291
304
  return [
292
305
  declarationAST,
293
306
  notFoundAST,
@@ -297,6 +310,46 @@ const computePropsAST = (
297
310
  ]
298
311
  }
299
312
 
313
+ /**
314
+ * The page, without its row: `{ props: { ...context?.params, <exposeAs>: null } }`.
315
+ *
316
+ * For a page whose reader is served no row of the table it shows (an order, a
317
+ * gift card — see `TableAccess.pageDataReadsNoRowOf`). Reading it as the
318
+ * store put another member's row in the page data of whoever opened the
319
+ * address; reading it as the visitor is always refused, which would 404 the
320
+ * page whatever else it shows.
321
+ */
322
+ const buildUnreadableEntityReturnAST = (
323
+ initialPropsData: UIDLInitialPropsData,
324
+ revalidateProperty: () => types.ObjectProperty | null
325
+ ): types.ReturnStatement =>
326
+ types.returnStatement(
327
+ types.objectExpression(
328
+ [
329
+ types.objectProperty(
330
+ types.identifier('props'),
331
+ types.objectExpression([
332
+ types.spreadElement(
333
+ types.optionalMemberExpression(
334
+ types.identifier('context'),
335
+ types.identifier('params'),
336
+ false,
337
+ true
338
+ )
339
+ ),
340
+ types.objectProperty(
341
+ types.identifier(
342
+ StringUtils.createStateOrPropStoringValue(initialPropsData.exposeAs.name)
343
+ ),
344
+ types.nullLiteral()
345
+ ),
346
+ ])
347
+ ),
348
+ revalidateProperty(),
349
+ ].filter(Boolean)
350
+ )
351
+ )
352
+
300
353
  /**
301
354
  * A page that shows a SUBSET of a table's rows (`redirect.unlessFieldEquals`)
302
355
  * and names the filter selecting its own rows (`redirect.ownRowsFilter`): two