json-rest-api 1.0.22 → 1.0.23

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.
Files changed (260) hide show
  1. package/CLAUDE.md +1 -2
  2. package/JSKIT_BACKEND_MIGRATION_TRACKER.txt +180 -0
  3. package/SINGLE_VALIDATION_CONTRACT_TODO.txt +172 -0
  4. package/docs/API.md +51 -14
  5. package/docs/COMPARISON.md +2 -2
  6. package/docs/GUIDE/GUIDE_1_Initial_Setup.md +28 -1
  7. package/docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md +5 -1
  8. package/docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md +3 -1
  9. package/docs/GUIDE/GUIDE_2_8_Effects_of_PUT_and_PATCH.md +12 -60
  10. package/docs/GUIDE/GUIDE_2_Data_And_Relations.md +2 -2
  11. package/docs/GUIDE/GUIDE_3_Field_Transformations.md +5 -1
  12. package/docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md +2 -0
  13. package/docs/GUIDE/GUIDE_X_Autofiltering.md +355 -0
  14. package/docs/GUIDE/GUIDE_X_Fastify.md +78 -0
  15. package/docs/GUIDE/GUIDE_X_Knex_Schema_And_Migrations.md +250 -0
  16. package/docs/GUIDE/GUIDE_X_Query_Projections.md +159 -0
  17. package/docs/GUIDE/GUIDE_X_Writing_Plugins.md +199 -0
  18. package/docs/GUIDE/index.md +18 -9
  19. package/docs/QUICKSTART.md +5 -1
  20. package/docs/index.md +1 -1
  21. package/index.js +3 -2
  22. package/package.json +2 -2
  23. package/plugins/core/connectors/express-plugin.js +97 -184
  24. package/plugins/core/connectors/fastify-plugin.js +206 -0
  25. package/plugins/core/connectors/lib/connector-core.js +195 -0
  26. package/plugins/core/connectors/lib/transport-http-helpers.js +122 -0
  27. package/plugins/core/connectors/lib/transport-route-schemas.js +92 -0
  28. package/plugins/core/lib/dbIntrospection.js +1078 -0
  29. package/plugins/core/lib/dbTablesOperations.js +1104 -160
  30. package/plugins/core/lib/querying/knex-json-api-transformers-querying.js +5 -2
  31. package/plugins/core/lib/querying/knex-process-includes.js +3 -4
  32. package/plugins/core/lib/querying/knex-query-helpers.js +17 -71
  33. package/plugins/core/lib/querying/knex-relationship-includes.js +466 -206
  34. package/plugins/core/lib/querying/query-builder-utils.js +19 -0
  35. package/plugins/core/lib/querying/query-field-sort-helpers.js +135 -0
  36. package/plugins/core/lib/querying/sort-helpers.js +33 -0
  37. package/plugins/core/lib/querying/storage-adapter-utils.js +51 -0
  38. package/plugins/core/lib/querying/url-helpers.js +8 -2
  39. package/plugins/core/lib/querying-writing/compile-schemas.js +12 -1
  40. package/plugins/core/lib/querying-writing/knex-field-helpers.js +83 -13
  41. package/plugins/core/lib/querying-writing/knex-json-api-transformers.js +13 -12
  42. package/plugins/core/lib/querying-writing/query-field-helpers.js +88 -0
  43. package/plugins/core/lib/querying-writing/request-contracts.js +590 -0
  44. package/plugins/core/lib/storage/canonical-storage-mapping.js +113 -0
  45. package/plugins/core/lib/storage/storage-adapter.js +79 -2
  46. package/plugins/core/lib/storage/storage-mapping.js +204 -0
  47. package/plugins/core/rest-api-anyapi-knex-plugin.js +355 -228
  48. package/plugins/core/rest-api-autofilter-plugin.js +414 -0
  49. package/plugins/core/rest-api-knex-plugin.js +249 -211
  50. package/plugins/core/rest-api-plugin-hooks/register-relationship-routes.js +10 -5
  51. package/plugins/core/rest-api-plugin-hooks/register-scope-routes.js +12 -6
  52. package/plugins/core/rest-api-plugin-methods/apply-query-filters.js +41 -0
  53. package/plugins/core/rest-api-plugin-methods/common.js +121 -83
  54. package/plugins/core/rest-api-plugin-methods/enrich-attributes.js +6 -1
  55. package/plugins/core/rest-api-plugin-methods/get.js +23 -14
  56. package/plugins/core/rest-api-plugin-methods/patch.js +28 -12
  57. package/plugins/core/rest-api-plugin-methods/post.js +13 -8
  58. package/plugins/core/rest-api-plugin-methods/put.js +45 -21
  59. package/plugins/core/rest-api-plugin-methods/query.js +15 -53
  60. package/plugins/core/rest-api-plugin.js +2 -0
  61. package/plugins/core/rest-api-positioning-plugin.js +1 -1
  62. package/plugins/core/rest-api-query-projections-plugin.js +130 -0
  63. package/plugins/core/socketio-plugin.js +1 -3
  64. package/tests/autofilter.test.js +463 -0
  65. package/tests/custom-id-simplified-post.test.js +75 -0
  66. package/tests/custom-idproperty-relationships.test.js +33 -0
  67. package/tests/db-introspection.test.js +518 -0
  68. package/tests/db-table-operations.test.js +781 -0
  69. package/tests/fastify-plugin.test.js +141 -0
  70. package/tests/field-getters.test.js +1 -2
  71. package/tests/field-setters.test.js +0 -4
  72. package/tests/fixtures/api-configs.js +188 -219
  73. package/tests/helpers/fake-fastify.js +99 -0
  74. package/tests/http-connectors-parity.test.js +235 -0
  75. package/tests/projected-fields.test.js +319 -0
  76. package/tests/relationships.test.js +3 -3
  77. package/tests/searchschema-knex-behavior.test.js +187 -0
  78. package/tests/searchschema-merge.test.js +21 -69
  79. package/tests/storage-mapping.test.js +210 -0
  80. package/audit/00-charter.md +0 -103
  81. package/audit/01-scope-map.md +0 -84
  82. package/audit/02-coverage-matrix.csv +0 -174
  83. package/audit/03-invariants.md +0 -123
  84. package/audit/04-standards-rubric.md +0 -56
  85. package/audit/05-competitor-rubric.md +0 -77
  86. package/audit/06-findings.md +0 -160
  87. package/audit/07-risk-register.csv +0 -10
  88. package/audit/08-remediation-roadmap.md +0 -177
  89. package/audit/09-signoff-check.md +0 -23
  90. package/audit/10-final-synthesis.md +0 -29
  91. package/audit/evidence/code/all-files-json-rest-api.txt +0 -168
  92. package/audit/evidence/code/all-files-json-rest-schema.txt +0 -12
  93. package/audit/evidence/code/boundary-leak-checks.txt +0 -72
  94. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__bulk-operations-plugin.js.txt +0 -480
  95. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__express-plugin.js.txt +0 -532
  96. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__lib__busboy-detector.js.txt +0 -175
  97. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__lib__formidable-detector.js.txt +0 -138
  98. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__lib__request-helpers.js.txt +0 -222
  99. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__file-handling-plugin.js.txt +0 -342
  100. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__dbTablesOperations.js.txt +0 -304
  101. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__fractional-positioning.js.txt +0 -188
  102. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__compile-schemas.js.txt +0 -336
  103. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__connectors-query-parser.js.txt +0 -165
  104. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__database-capabilities.js.txt +0 -265
  105. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__database-value-normalizers.js.txt +0 -369
  106. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__default-data-helpers.js.txt +0 -72
  107. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__field-utils.js.txt +0 -139
  108. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__knex-constants.js.txt +0 -45
  109. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__knex-field-helpers.js.txt +0 -396
  110. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__knex-json-api-transformers.js.txt +0 -162
  111. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__payload-validators.js.txt +0 -1314
  112. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__schema-helpers.js.txt +0 -488
  113. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__scope-validations.js.txt +0 -484
  114. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__simplified-helpers.js.txt +0 -857
  115. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-cross-table-search.js.txt +0 -565
  116. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-json-api-transformers-querying.js.txt +0 -485
  117. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-pagination-helpers.js.txt +0 -560
  118. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-process-includes.js.txt +0 -167
  119. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-query-helpers-base.js.txt +0 -109
  120. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-query-helpers.js.txt +0 -1064
  121. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-relationship-includes.js.txt +0 -1983
  122. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-window-queries.js.txt +0 -328
  123. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__url-helpers.js.txt +0 -119
  124. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__storage__storage-adapter.js.txt +0 -160
  125. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__writing__many-to-many-manipulations.js.txt +0 -300
  126. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__writing__relationship-processor.js.txt +0 -245
  127. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-access.js.txt +0 -459
  128. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-cors-plugin.js.txt +0 -237
  129. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-knex-plugin.js.txt +0 -1135
  130. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-multihome-plugin.js.txt +0 -314
  131. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__compile-resource-schemas.js.txt +0 -7
  132. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__register-relationship-routes.js.txt +0 -71
  133. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__register-scope-routes.js.txt +0 -102
  134. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__turn-scope-init-into-vars.js.txt +0 -49
  135. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__validate-include-configurations.js.txt +0 -39
  136. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__add-route.js.txt +0 -23
  137. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__check-permissions.js.txt +0 -13
  138. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__common.js.txt +0 -639
  139. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__delete-relationship.js.txt +0 -117
  140. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__delete.js.txt +0 -104
  141. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__enrich-attributes.js.txt +0 -199
  142. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__get-related.js.txt +0 -353
  143. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__get-relationship.js.txt +0 -61
  144. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__get.js.txt +0 -184
  145. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__patch-relationship.js.txt +0 -55
  146. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__patch.js.txt +0 -191
  147. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__post-relationship.js.txt +0 -110
  148. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__post.js.txt +0 -168
  149. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__put.js.txt +0 -253
  150. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__query.js.txt +0 -207
  151. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__release.js.txt +0 -12
  152. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin.js.txt +0 -165
  153. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-positioning-plugin.js.txt +0 -668
  154. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__socketio-plugin.js.txt +0 -621
  155. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__storage__local-storage.js.txt +0 -218
  156. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__storage__s3-storage.js.txt +0 -124
  157. package/audit/evidence/code/critical-pass1/json-rest-schema__src__CorePlugin.js.txt +0 -269
  158. package/audit/evidence/code/critical-pass1/json-rest-schema__src__Schema.js.txt +0 -242
  159. package/audit/evidence/code/critical-pass1/json-rest-schema__src__index.js.txt +0 -77
  160. package/audit/evidence/code/critical-pass1-risk-keywords.txt +0 -238
  161. package/audit/evidence/code/critical-pass1-summary.csv +0 -67
  162. package/audit/evidence/code/critical-pass2-summary.csv +0 -67
  163. package/audit/evidence/code/early-silent-zones-no-test-token.txt +0 -57
  164. package/audit/evidence/code/error-model-signals.txt +0 -98
  165. package/audit/evidence/code/git-files-json-rest-api.txt +0 -171
  166. package/audit/evidence/code/git-files-json-rest-schema.txt +0 -27
  167. package/audit/evidence/code/hook-flow-signals.txt +0 -282
  168. package/audit/evidence/code/idproperty-signals.txt +0 -157
  169. package/audit/evidence/code/import-cycles-non-anyapi.txt +0 -3
  170. package/audit/evidence/code/import-hotspots-non-anyapi.txt +0 -96
  171. package/audit/evidence/code/imports-json-rest-api.txt +0 -354
  172. package/audit/evidence/code/imports-json-rest-schema.txt +0 -15
  173. package/audit/evidence/code/in-scope-files-json-rest-api.txt +0 -160
  174. package/audit/evidence/code/in-scope-files-json-rest-schema.txt +0 -13
  175. package/audit/evidence/code/non-anyapi-core-files.txt +0 -101
  176. package/audit/evidence/code/permission-signals.txt +0 -193
  177. package/audit/evidence/code/query-include-signals.txt +0 -687
  178. package/audit/evidence/code/transaction-signals.txt +0 -28
  179. package/audit/evidence/code/validation-signals.txt +0 -650
  180. package/audit/evidence/code/write-path-signals.txt +0 -61
  181. package/audit/evidence/docs/docs-claims-signals.txt +0 -1075
  182. package/audit/evidence/docs/schema-docs-claims-signals.txt +0 -179
  183. package/audit/evidence/external/corpus-freeze.md +0 -21
  184. package/audit/evidence/external/npm-competitor-metadata.txt +0 -70
  185. package/audit/evidence/external/npm-download-signals.txt +0 -23
  186. package/audit/evidence/external/npm-jsonapi-server.txt +0 -8
  187. package/audit/evidence/external/npm-modern-jsonapi-metadata.txt +0 -38
  188. package/audit/evidence/external/readme-fortune-signals.txt +0 -26
  189. package/audit/evidence/external/readme-fortune.md +0 -165
  190. package/audit/evidence/external/readme-json-api-serializer-signals.txt +0 -80
  191. package/audit/evidence/external/readme-json-api-serializer.md +0 -690
  192. package/audit/evidence/external/readme-jsonapi-serde-server-signals.txt +0 -0
  193. package/audit/evidence/external/readme-jsonapi-serde-server.md +0 -22
  194. package/audit/evidence/external/readme-jsonapi-serializer-signals.txt +0 -63
  195. package/audit/evidence/external/readme-jsonapi-serializer.md +0 -315
  196. package/audit/evidence/external/readme-jsonapi-server-signals.txt +0 -19
  197. package/audit/evidence/external/readme-jsonapi-server.md +0 -104
  198. package/audit/evidence/external/readme-nestjs-json-api-signals.txt +0 -4
  199. package/audit/evidence/external/readme-nestjs-json-api.md +0 -94
  200. package/audit/evidence/external/readme-yaser-json-api-nestjs-signals.txt +0 -47
  201. package/audit/evidence/external/readme-yaser-json-api-nestjs.md +0 -447
  202. package/audit/evidence/external/standard-jsonapi-snippets.txt +0 -84
  203. package/audit/evidence/external/standard-rfc-snippets.txt +0 -168
  204. package/audit/evidence/external/standards-sources.txt +0 -6
  205. package/audit/evidence/runtime/baseline-commits.txt +0 -2
  206. package/audit/evidence/runtime/db-adapter-assumptions.txt +0 -3
  207. package/audit/evidence/runtime/environment.txt +0 -4
  208. package/audit/evidence/runtime/invariant-runtime-mapping.md +0 -38
  209. package/audit/evidence/runtime/probe-patch-relationship-error.exitcode +0 -1
  210. package/audit/evidence/runtime/probe-patch-relationship-error.log +0 -76
  211. package/audit/evidence/runtime/probe-patch-relationship-error.mjs +0 -67
  212. package/audit/evidence/runtime/probe-patch-relationship-referenceerror.exitcode +0 -1
  213. package/audit/evidence/runtime/probe-patch-relationship-referenceerror.log +0 -62
  214. package/audit/evidence/runtime/probe-patch-relationship-referenceerror.mjs +0 -47
  215. package/audit/evidence/runtime/test-exitcodes.txt +0 -4
  216. package/audit/evidence/runtime/test-summary.md +0 -107
  217. package/audit/evidence/runtime/trace-anchors.txt +0 -61
  218. package/audit/evidence/tests/access-multitenancy-test-signals.txt +0 -125
  219. package/audit/evidence/tests/failing-tests-summary.txt +0 -25
  220. package/audit/evidence/tests/npm-test-json-rest-api.exitcode +0 -1
  221. package/audit/evidence/tests/npm-test-json-rest-api.log +0 -5885
  222. package/audit/evidence/tests/npm-test-json-rest-schema.exitcode +0 -1
  223. package/audit/evidence/tests/npm-test-json-rest-schema.log +0 -356
  224. package/audit/evidence/tests/query-include-test-signals.txt +0 -948
  225. package/audit/evidence/tests/relationship-test-signals.txt +0 -223
  226. package/audit/evidence/tests/skipped-tests.txt +0 -10
  227. package/audit/evidence/tests/test-suite-list-non-anyapi.txt +0 -30
  228. package/audit/evidence/tests/test-suite-list.txt +0 -32
  229. package/audit/subsystems/docs-drift.md +0 -49
  230. package/audit/subsystems/pass2-cross-review.md +0 -131
  231. package/audit/subsystems/plugins-access.md +0 -51
  232. package/audit/subsystems/plugins-bulk.md +0 -50
  233. package/audit/subsystems/plugins-cors.md +0 -48
  234. package/audit/subsystems/plugins-file.md +0 -53
  235. package/audit/subsystems/plugins-multihome.md +0 -50
  236. package/audit/subsystems/plugins-positioning.md +0 -54
  237. package/audit/subsystems/plugins-socketio.md +0 -50
  238. package/audit/subsystems/query-engine.md +0 -61
  239. package/audit/subsystems/relationships.md +0 -55
  240. package/audit/subsystems/rest-orchestration.md +0 -66
  241. package/audit/subsystems/schema-core.md +0 -64
  242. package/audit/subsystems/storage-knex.md +0 -57
  243. package/audit/subsystems/tests.md +0 -48
  244. package/audit/subsystems/transport-express.md +0 -55
  245. package/audit/traces/trace-read-get.md +0 -22
  246. package/audit/traces/trace-read-query.md +0 -25
  247. package/audit/traces/trace-relationship-endpoints.md +0 -31
  248. package/audit/traces/trace-write-delete.md +0 -17
  249. package/audit/traces/trace-write-patch.md +0 -26
  250. package/audit/traces/trace-write-post.md +0 -22
  251. package/audit/traces/trace-write-put.md +0 -23
  252. package/docs/GUIDE/GUIDE_4_Authentication_And_Permissions.md +0 -707
  253. package/docs/GUIDE/GUIDE_X_Multihome.md +0 -666
  254. package/plugins/core/lib/querying-writing/payload-validators.js +0 -1314
  255. package/plugins/core/rest-api-access.js +0 -459
  256. package/plugins/core/rest-api-multihome-plugin.js +0 -314
  257. package/tests/multihome.test.js +0 -553
  258. package/tests/multihome.test.js.bak +0 -550
  259. package/tests/ownership-optout.test.js +0 -76
  260. package/tests/rest-api-access.test.js +0 -206
@@ -1,117 +0,0 @@
1
- 1 import { RestApiResourceError, RestApiValidationError, RestApiPayloadError } from '../../../lib/rest-api-errors.js'
2
- 2 import { findRelationshipDefinition, handleWriteMethodError } from './common.js'
3
- 3
4
- 4 /**
5
- 5 * DELETE RELATIONSHIP
6
- 6 * Removes specific members from a to-many relationship
7
- 7 * DELETE /api/articles/1/relationships/tags
8
- 8 *
9
- 9 * @param {string} id - The ID of the resource
10
- 10 * @param {string} relationshipName - The name of the relationship
11
- 11 * @param {array} relationshipData - Array of resource identifiers to remove
12
- 12 * @returns {Promise<void>} 204 No Content
13
- 13 */
14
- 14 export default async function deleteRelationshipMethod ({ params, context, vars, helpers, scope, scopes, runHooks, scopeName, api, log }) {
15
- 15 context.method = 'deleteRelationship'
16
- 16 context.id = params.id
17
- 17 context.relationshipName = params.relationshipName
18
- 18 context.schemaInfo = scopes[scopeName].vars.schemaInfo
19
- 19
20
- 20 // Transaction handling
21
- 21 context.transaction = params.transaction ||
22
- 22 (helpers.newTransaction && !params.transaction ? await helpers.newTransaction() : null)
23
- 23 context.shouldCommit = !params.transaction && !!context.transaction
24
- 24 context.db = context.transaction || api.knex.instance
25
- 25
26
- 26 try {
27
- 27 // Validate
28
- 28 const relDef = findRelationshipDefinition(context.schemaInfo, context.relationshipName)
29
- 29 if (!relDef) {
30
- 30 throw new RestApiResourceError(
31
- 31 `Relationship '${context.relationshipName}' not found on resource '${scopeName}'`,
32
- 32 { subtype: 'relationship_not_found' }
33
- 33 )
34
- 34 }
35
- 35
36
- 36 if (relDef.type !== 'hasMany' && relDef.type !== 'manyToMany') {
37
- 37 throw new RestApiValidationError(
38
- 38 `Cannot DELETE from to-one relationship '${context.relationshipName}'`,
39
- 39 { fields: ['data'] }
40
- 40 )
41
- 41 }
42
- 42
43
- 43 if (!Array.isArray(params.relationshipData)) {
44
- 44 throw new RestApiPayloadError('DELETE from relationship requires array of resource identifiers')
45
- 45 }
46
- 46
47
- 47 // Check permissions
48
- 48 await runHooks('checkPermissions')
49
- 49 await runHooks('checkPermissionsDeleteRelationship')
50
- 50
51
- 51 // Verify parent exists
52
- 52 const exists = await helpers.dataExists({
53
- 53 scopeName,
54
- 54 context: { db: context.db, id: context.id, schemaInfo: context.schemaInfo }
55
- 55 })
56
- 56
57
- 57 if (!exists) {
58
- 58 throw new RestApiResourceError('Resource not found', { subtype: 'not_found' })
59
- 59 }
60
- 60
61
- 61 // Remove relationships
62
- 62 if (relDef?.through) {
63
- 63 if (api.anyapi?.links?.removeMany) {
64
- 64 await api.anyapi.links.removeMany({
65
- 65 context,
66
- 66 scopeName,
67
- 67 relName: context.relationshipName,
68
- 68 relDef,
69
- 69 relData: params.relationshipData,
70
- 70 })
71
- 71 } else {
72
- 72 const knex = api.knex?.instance || helpers.db
73
- 73 const pivotResource = relDef.through
74
- 74 const pivotScope = api.resources[pivotResource]
75
- 75 const pivotTable = pivotScope?.vars?.schemaInfo?.tableName || pivotResource
76
- 76 const localKey = relDef.foreignKey
77
- 77 const foreignKey = relDef.otherKey
78
- 78
79
- 79 for (const identifier of params.relationshipData) {
80
- 80 await knex(pivotTable)
81
- 81 .where(localKey, context.id)
82
- 82 .where(foreignKey, identifier.id)
83
- 83 .delete()
84
- 84 .transacting(context.transaction)
85
- 85 }
86
- 86 }
87
- 87 } else {
88
- 88 // Null out foreign keys for hasMany
89
- 89 const targetType = relDef.target
90
- 90 for (const identifier of params.relationshipData) {
91
- 91 await api.resources[targetType].patch({
92
- 92 id: identifier.id,
93
- 93 inputRecord: {
94
- 94 data: {
95
- 95 type: targetType,
96
- 96 id: identifier.id,
97
- 97 attributes: { [relDef.foreignKey]: null }
98
- 98 }
99
- 99 },
100
- 100 transaction: context.transaction,
101
- 101 simplified: false
102
- 102 })
103
- 103 }
104
- 104 }
105
- 105
106
- 106 await runHooks('finish')
107
- 107 await runHooks('finishDeleteRelationship')
108
- 108
109
- 109 if (context.shouldCommit) {
110
- 110 await context.transaction.commit()
111
- 111 }
112
- 112
113
- 113 // 204 No Content
114
- 114 } catch (error) {
115
- 115 await handleWriteMethodError(error, context, 'DELETE_RELATIONSHIP', scopeName, log, runHooks)
116
- 116 }
117
- 117 }
@@ -1,104 +0,0 @@
1
- 1 import { RestApiResourceError } from '../../../lib/rest-api-errors.js'
2
- 2 import { handleWriteMethodError } from './common.js'
3
- 3
4
- 4 /**
5
- 5 * DELETE
6
- 6 * Permanently deletes a resource.
7
- 7 * Returns 204 No Content on success, indicating the resource has been removed.
8
- 8 * This method does not return the deleted resource.
9
- 9 */
10
- 10 export default async function deleteMethod ({
11
- 11 params,
12
- 12 context,
13
- 13 vars,
14
- 14 helpers,
15
- 15 scope,
16
- 16 scopes,
17
- 17 runHooks,
18
- 18 apiOptions,
19
- 19 pluginOptions,
20
- 20 scopeOptions,
21
- 21 scopeName,
22
- 22 api,
23
- 23 log
24
- 24 }) {
25
- 25 // Make the method available to all hooks
26
- 26 context.method = 'delete'
27
- 27
28
- 28 // Set the ID in context
29
- 29 context.id = params.id
30
- 30
31
- 31 // Set scopeName in context (needed for broadcasting)
32
- 32 context.scopeName = scopeName
33
- 33
34
- 34 // Set schema info even for DELETE (needed by storage layer)
35
- 35 context.schemaInfo = scopes[scopeName].vars.schemaInfo
36
- 36
37
- 37 // Transaction handling
38
- 38 context.transaction = params.transaction ||
39
- 39 (helpers.newTransaction && !params.transaction ? await helpers.newTransaction() : null)
40
- 40 context.shouldCommit = !params.transaction && !!context.transaction
41
- 41 context.db = context.transaction || api.knex.instance
42
- 42
43
- 43 try {
44
- 44 // No payload validation needed for DELETE
45
- 45
46
- 46 // Fetch minimal record for authorization and logging
47
- 47 const minimalRecord = await helpers.dataGetMinimal({
48
- 48 scopeName,
49
- 49 context,
50
- 50 runHooks
51
- 51 })
52
- 52
53
- 53 if (!minimalRecord) {
54
- 54 throw new RestApiResourceError(
55
- 55 'Resource not found',
56
- 56 {
57
- 57 subtype: 'not_found',
58
- 58 resourceType: scopeName,
59
- 59 resourceId: context.id
60
- 60 }
61
- 61 )
62
- 62 }
63
- 63
64
- 64 context.originalMinimalRecord = minimalRecord
65
- 65 context.minimalRecord = minimalRecord
66
- 66
67
- 67 // Centralised checkPermissions function
68
- 68 await scope.checkPermissions({
69
- 69 method: 'delete',
70
- 70 originalContext: context,
71
- 71 })
72
- 72
73
- 73 // Before data operations
74
- 74 await runHooks('beforeDataCall')
75
- 75 await runHooks('beforeDataCallDelete')
76
- 76
77
- 77 // Initialize record context for hooks
78
- 78 context.record = {}
79
- 79
80
- 80 // Call the storage helper
81
- 81 await helpers.dataDelete({
82
- 82 scopeName,
83
- 83 context
84
- 84 })
85
- 85
86
- 86 await runHooks('afterDataCallDelete')
87
- 87 await runHooks('afterDataCall')
88
- 88
89
- 89 // No return record for DELETE (204 No Content)
90
- 90
91
- 91 await runHooks('finish')
92
- 92 await runHooks('finishDelete')
93
- 93
94
- 94 // Commit transaction if we created it
95
- 95 if (context.shouldCommit) {
96
- 96 await context.transaction.commit()
97
- 97 await runHooks('afterCommit')
98
- 98 }
99
- 99
100
- 100 // DELETE typically returns void/undefined (204 No Content)
101
- 101 } catch (error) {
102
- 102 await handleWriteMethodError(error, context, 'DELETE', scopeName, log, runHooks)
103
- 103 }
104
- 104 }
@@ -1,199 +0,0 @@
1
- 1 import { filterHiddenFields } from '../lib/querying-writing/field-utils.js'
2
- 2
3
- 3 /**
4
- 4 * enrichAttributes
5
- 5 * Runs the enrichAttributes hook for a specific scope to allow plugins to modify attributes
6
- 6 * before they are returned to the client. This is a scope method so each resource type
7
- 7 * can have its own attribute enrichment logic.
8
- 8 *
9
- 9 *
10
- 10 */
11
- 11 export default async function enrichAttributesMethod ({ context, params, runHooks, scopeName, scopes, api, helpers }) {
12
- 12 // Extract parameters passed to enrichAttributes
13
- 13 // - attributes: The raw attributes from database
14
- 14 // - parentContext: The context from the calling method (has queryParams, transaction, etc.)
15
- 15 // - requestedComputedFields: Which computed fields to calculate (from sparse fieldsets)
16
- 16 // - isMainResource: Whether this is the main resource or an included one
17
- 17 // - computedDependencies: Fields fetched only for computation (to be removed)
18
- 18 // Extract parameters passed to enrichAttributes
19
- 19 // - attributes: The raw attributes from database
20
- 20 // - parentContext: The context from the calling method (has queryParams, transaction, etc.)
21
- 21 // - requestedComputedFields: Which computed fields to calculate (from sparse fieldsets)
22
- 22 // - isMainResource: Whether this is the main resource or an included one
23
- 23 // - computedDependencies: Fields fetched only for computation (to be removed)
24
- 24 const { attributes, parentContext, requestedComputedFields, isMainResource, computedDependencies } = params || {}
25
- 25
26
- 26 // Return empty object if no attributes provided
27
- 27 if (!attributes) {
28
- 28 return {}
29
- 29 }
30
- 30
31
- 31 // Get schema, computed field definitions
32
- 32 const schemaStructure = scopes[scopeName]?.vars?.schemaInfo?.schemaStructure || {}
33
- 33 const computedFields = scopes[scopeName]?.vars?.schemaInfo?.computed || {}
34
- 34 const fieldGetters = scopes[scopeName]?.vars?.schemaInfo?.fieldGetters || {}
35
- 35 const sortedGetterFields = scopes[scopeName]?.vars?.schemaInfo?.sortedGetterFields || []
36
- 36
37
- 37 // Make a copy of attributes for transformation
38
- 38 const transformedAttributes = { ...attributes }
39
- 39
40
- 40 // STEP 1: Apply field getters in dependency order (before filtering)
41
- 41 // This ensures getters see all fields including hidden ones
42
- 42 for (const fieldName of sortedGetterFields) {
43
- 43 // Only process if field exists in attributes
44
- 44 if (fieldName in transformedAttributes) {
45
- 45 const getterInfo = fieldGetters[fieldName]
46
- 46 try {
47
- 47 const getterContext = {
48
- 48 attributes: transformedAttributes, // Current state with previous getters applied
49
- 49 fieldName,
50
- 50 originalValue: attributes[fieldName], // Original untransformed value
51
- 51 originalAttributes: attributes, // All original attributes
52
- 52 record: transformedAttributes, // Alias for compatibility
53
- 53 parentContext,
54
- 54 scopeName,
55
- 55 api,
56
- 56 helpers,
57
- 57 isMainResource: isMainResource !== false
58
- 58 }
59
- 59
60
- 60 // Apply getter (can be async)
61
- 61 transformedAttributes[fieldName] = await getterInfo.getter(
62
- 62 transformedAttributes[fieldName],
63
- 63 getterContext
64
- 64 )
65
- 65 } catch (error) {
66
- 66 console.error(`Error in getter for field '${fieldName}' in ${scopeName}:`, error)
67
- 67 // Keep current value on error (don't break the whole request)
68
- 68 }
69
- 69 }
70
- 70 }
71
- 71
72
- 72 // Filter hidden fields from attributes based on visibility rules
73
- 73 // This removes hidden:true fields and normallyHidden:true fields (unless requested)
74
- 74 const requestedFields = parentContext?.queryParams?.fields?.[scopeName]
75
- 75 const filteredAttributes = filterHiddenFields(transformedAttributes, { structure: schemaStructure }, requestedFields)
76
- 76
77
- 77 // Handle virtual fields - they need to be added from parent context if available
78
- 78 // Virtual fields come from input and need to be preserved in responses
79
- 79 if (parentContext && parentContext.inputRecord?.data?.attributes) {
80
- 80 const inputAttrs = parentContext.inputRecord.data.attributes
81
- 81 Object.entries(schemaStructure).forEach(([fieldName, fieldDef]) => {
82
- 82 if (fieldDef.virtual === true && fieldName in inputAttrs && inputAttrs[fieldName] !== undefined && inputAttrs[fieldName] !== null) {
83
- 83 // Add virtual field to filtered attributes if not already there
84
- 84 if (!(fieldName in filteredAttributes)) {
85
- 85 filteredAttributes[fieldName] = inputAttrs[fieldName]
86
- 86 }
87
- 87 }
88
- 88 })
89
- 89 }
90
- 90
91
- 91 // Now handle sparse fieldsets for virtual fields
92
- 92 if (requestedFields && requestedFields.length > 0) {
93
- 93 const requestedFieldsList = typeof requestedFields === 'string'
94
- 94 ? requestedFields.split(',').map(f => f.trim())
95
- 95 : requestedFields
96
- 96
97
- 97 // Remove virtual fields that weren't requested
98
- 98 Object.entries(schemaStructure).forEach(([fieldName, fieldDef]) => {
99
- 99 if (fieldDef.virtual === true && fieldName in filteredAttributes) {
100
- 100 if (!requestedFieldsList.includes(fieldName)) {
101
- 101 delete filteredAttributes[fieldName]
102
- 102 }
103
- 103 }
104
- 104 })
105
- 105 }
106
- 106
107
- 107 // Determine which computed fields to calculate
108
- 108 // We only compute fields that are requested to optimize performance
109
- 109 let fieldsToCompute = []
110
- 110 if (requestedComputedFields) {
111
- 111 // Explicit list provided (from sparse fieldsets)
112
- 112 // Example: ?fields[products]=name,profit_margin -> only compute profit_margin
113
- 113 fieldsToCompute = requestedComputedFields
114
- 114 } else if (isMainResource || !parentContext?.queryParams?.fields) {
115
- 115 // No sparse fieldsets or this is the main resource - compute all fields
116
- 116 // This ensures all computed fields are available when no filtering is applied
117
- 117 fieldsToCompute = Object.keys(computedFields)
118
- 118 }
119
- 119
120
- 120 // Create compute context with all available resources
121
- 121 // IMPORTANT: We pass the TRANSFORMED attributes (after getters) to compute functions
122
- 122 // This ensures computed fields see the getter-processed values
123
- 123 // Example: if price getter converts string to number, profit_margin sees the number
124
- 124 const computeContext = {
125
- 125 attributes: transformedAttributes, // Transformed attributes after getters
126
- 126 record: { ...transformedAttributes }, // Full record for convenience
127
- 127 context: parentContext,
128
- 128 helpers, // API helpers for complex operations
129
- 129 api, // Full API instance
130
- 130 }
131
- 131
132
- 132 // Auto-compute fields that have compute functions
133
- 133 for (const fieldName of fieldsToCompute) {
134
- 134 const fieldDef = computedFields[fieldName]
135
- 135 if (fieldDef && fieldDef.compute) {
136
- 136 try {
137
- 137 // Call the compute function with full context
138
- 138 // Example: profit_margin compute gets { attributes: { price: 100, cost: 60 } }
139
- 139 // and returns: ((100 - 60) / 100 * 100) = "40.00"
140
- 140 filteredAttributes[fieldName] = await fieldDef.compute(computeContext)
141
- 141 } catch (error) {
142
- 142 // Log error but don't fail the request - computed fields shouldn't break API
143
- 143 console.error(`Error computing field '${fieldName}' for ${scopeName}:`, error)
144
- 144 filteredAttributes[fieldName] = null
145
- 145 }
146
- 146 }
147
- 147 }
148
- 148
149
- 149 // Remove fields that were only fetched as dependencies
150
- 150 // This is the key to the dependency resolution feature:
151
- 151 // 1. We fetched dependencies from DB (e.g., 'cost' for profit_margin)
152
- 152 // 2. We used them in compute functions
153
- 153 // 3. Now we remove them if they weren't explicitly requested
154
- 154 // Example: User requests profit_margin, we fetch cost, compute, then remove cost
155
- 155 const finalAttributes = { ...filteredAttributes }
156
- 156 if (requestedFields && computedDependencies && computedDependencies.length > 0) {
157
- 157 // Parse requested fields if it's a string
158
- 158 const requested = typeof requestedFields === 'string'
159
- 159 ? requestedFields.split(',').map(f => f.trim()).filter(f => f)
160
- 160 : requestedFields
161
- 161
162
- 162 for (const dep of computedDependencies) {
163
- 163 // Only remove if it wasn't explicitly requested
164
- 164 // Example: 'cost' is removed unless user explicitly asked for it
165
- 165 if (!requested.includes(dep)) {
166
- 166 delete finalAttributes[dep]
167
- 167 }
168
- 168 }
169
- 169 }
170
- 170
171
- 171 if (requestedFields && requestedFields.length > 0) {
172
- 172 const requestedList = typeof requestedFields === 'string'
173
- 173 ? requestedFields.split(',').map((field) => field.trim()).filter((field) => field)
174
- 174 : requestedFields
175
- 175 const allowed = new Set(requestedList)
176
- 176 Object.keys(finalAttributes).forEach((key) => {
177
- 177 if (!allowed.has(key)) {
178
- 178 delete finalAttributes[key]
179
- 179 }
180
- 180 })
181
- 181 }
182
- 182
183
- 183 // Create context for enrichAttributes hooks
184
- 184 Object.assign(context, {
185
- 185 parentContext,
186
- 186 attributes: finalAttributes, // Use the final attributes after dependency removal
187
- 187 computedFields,
188
- 188 requestedComputedFields: fieldsToCompute,
189
- 189 scopeName,
190
- 190 helpers,
191
- 191 api
192
- 192 })
193
- 193
194
- 194 // Run enrichAttributes hooks for additional/override computations
195
- 195 await runHooks('enrichAttributes')
196
- 196
197
- 197 // Return the attributes from context, which hooks may have modified
198
- 198 return context.attributes
199
- 199 };