json-rest-api 1.0.17 → 1.0.19

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/index.js CHANGED
@@ -13,6 +13,7 @@ export { RestApiPlugin } from './plugins/core/rest-api-plugin.js'
13
13
  export { FileHandlingPlugin } from './plugins/core/file-handling-plugin.js'
14
14
  export { CorsPlugin } from './plugins/core/rest-api-cors-plugin.js'
15
15
  export { AccessPlugin } from './plugins/core/rest-api-access.js'
16
+ export { LabelPlugin } from './plugins/core/rest-api-label-plugin.js'
16
17
 
17
18
  // Database plugins
18
19
  export { RestApiKnexPlugin } from './plugins/core/rest-api-knex-plugin.js'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "json-rest-api",
3
- "version": "1.0.17",
3
+ "version": "1.0.19",
4
4
  "main": "index.js",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -184,7 +184,7 @@ export const AccessPlugin = {
184
184
  }
185
185
 
186
186
  fields[field] = {
187
- type: 'number',
187
+ type: 'id',
188
188
  belongsTo: config.ownership.userResource,
189
189
  ...(relationshipName ? { as: relationshipName } : {}),
190
190
  nullable: true,
@@ -0,0 +1,74 @@
1
+ export const LabelPlugin = {
2
+ name: 'rest-api-label',
3
+ dependencies: ['rest-api'],
4
+
5
+ /**
6
+ * Install the label plugin (adds a computed 'label' attribute to all resources)
7
+ * @param {object} deps Injected dependencies from json-rest-api
8
+ * @param {function} deps.addHook - Register hooks
9
+ * @param {object} deps.pluginOptions - Plugin options
10
+ */
11
+ async install ({ addHook, pluginOptions = {} }) {
12
+ const opts = {
13
+ preferNameFields: ['name', 'title'],
14
+ disable: false,
15
+ ...pluginOptions
16
+ }
17
+
18
+ function pickLabelFieldFromFields (fields, searchSchema) {
19
+ const byGlobal = Object.keys(searchSchema || {}).find(k => searchSchema[k]?.globalSearch)
20
+ if (byGlobal) return byGlobal
21
+ for (const key of opts.preferNameFields) {
22
+ if (fields?.[key]?.type === 'string') return key
23
+ }
24
+ const firstString = Object.entries(fields || {}).find(([_, def]) => def?.type === 'string')?.[0]
25
+ if (firstString) return firstString
26
+ return null
27
+ }
28
+
29
+ // Inject as a computed field AFTER schemas are compiled,
30
+ // so it lives in schemaInfo.computed and not in schemaStructure
31
+ addHook(
32
+ 'scope:added',
33
+ 'rest-api-label:inject-computed',
34
+ { afterFunction: 'compileResourceSchemas' },
35
+ ({ context, scopes }) => {
36
+ if (opts.disable) return
37
+ const { scopeName } = context
38
+ const scope = scopes?.[scopeName]
39
+ const schemaInfo = scope?.vars?.schemaInfo
40
+ if (!schemaInfo) return
41
+
42
+ // Respect explicit label already defined by resource
43
+ if (schemaInfo.computed?.label || schemaInfo.schemaStructure?.label) {
44
+ // If label accidentally made it into schemaStructure, remove it
45
+ if (schemaInfo.schemaStructure?.label) delete schemaInfo.schemaStructure.label
46
+ return
47
+ }
48
+
49
+ const structure = schemaInfo.schemaStructure || {}
50
+ const searchStruct = schemaInfo.searchSchemaStructure || {}
51
+ const idProp = schemaInfo.idProperty || 'id'
52
+
53
+ const pick = pickLabelFieldFromFields(structure, searchStruct)
54
+
55
+ schemaInfo.computed = schemaInfo.computed || {}
56
+ schemaInfo.computed.label = {
57
+ type: 'string',
58
+ computed: true,
59
+ ...(pick ? { dependencies: [pick] } : {}),
60
+ compute: ({ attributes }) => {
61
+ if (pick) {
62
+ const v = attributes?.[pick]
63
+ if (v != null) return String(v)
64
+ }
65
+ for (const k of opts.preferNameFields) {
66
+ if (attributes?.[k] != null) return String(attributes[k])
67
+ }
68
+ return String(attributes?.[idProp] ?? '')
69
+ }
70
+ }
71
+ }
72
+ )
73
+ }
74
+ }