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,191 +0,0 @@
1
- 1 import { RestApiResourceError } from '../../../lib/rest-api-errors.js'
2
- 2 import { validatePatchPayload } from '../lib/querying-writing/payload-validators.js'
3
- 3 import { processRelationships } from '../lib/writing/relationship-processor.js'
4
- 4 import { updateManyToManyRelationship } from '../lib/writing/many-to-many-manipulations.js'
5
- 5 import { ERROR_SUBTYPES } from '../lib/querying-writing/knex-constants.js'
6
- 6 import {
7
- 7 setupCommonRequest,
8
- 8 validateResourceAttributesBeforeWrite,
9
- 9 validateRelationshipAccess,
10
- 10 applyFieldSetters,
11
- 11 validatePivotResource,
12
- 12 handleRecordReturnAfterWrite,
13
- 13 handleWriteMethodError
14
- 14 } from './common.js'
15
- 15
16
- 16 /**
17
- 17 * PATCH
18
- 18 * Performs a partial update on an existing resource's attributes or relationships.
19
- 19 * Unlike PUT, PATCH only updates the fields provided, leaving other fields unchanged.
20
- 20 * This method supports updating both attributes and relationships (1:1 and n:n).
21
- 21 * For relationships, only the ones explicitly provided will be updated.
22
- 22 * Just like PUT, it CANNOT have the `included` array in data.
23
- 23 */
24
- 24 export default async function patchMethod ({
25
- 25 params,
26
- 26 context,
27
- 27 vars,
28
- 28 helpers,
29
- 29 scope,
30
- 30 scopes,
31
- 31 runHooks,
32
- 32 apiOptions,
33
- 33 pluginOptions,
34
- 34 scopeOptions,
35
- 35 scopeName,
36
- 36 api,
37
- 37 log
38
- 38 }) {
39
- 39 context.method = 'patch'
40
- 40
41
- 41 try {
42
- 42 const { schema, schemaStructure, schemaRelationships } = await setupCommonRequest({
43
- 43 params,
44
- 44 context,
45
- 45 vars,
46
- 46 scopes,
47
- 47 scopeOptions,
48
- 48 scopeName,
49
- 49 api,
50
- 50 helpers
51
- 51 })
52
- 52 context.id = context.inputRecord.data.id
53
- 53
54
- 54 // Run early hooks for pre-processing (e.g., file handling)
55
- 55 await runHooks('beforeProcessing')
56
- 56 await runHooks('beforeProcessingPatch')
57
- 57
58
- 58 // Validate PATCH payload to ensure the partial update actually contains changes.
59
- 59 // PATCH requests must include either attributes to update or relationships to modify -
60
- 60 // an empty PATCH is invalid. This prevents accidental no-op requests and ensures clients
61
- 61 // are explicit about what they want to change. Unlike PUT, PATCH preserves all fields
62
- 62 // not mentioned in the request.
63
- 63 // Example: data must have either attributes: {title: 'New'} or relationships: {author: {...}}
64
- 64 validatePatchPayload(context.inputRecord, scopes)
65
- 65
66
- 66 // Validate that user has read access to all related resources
67
- 67 // This ensures users can only create relationships to resources they can access
68
- 68 await validateRelationshipAccess(context, context.inputRecord, helpers, runHooks, api)
69
- 69
70
- 70 // Extract foreign keys from JSON:API relationships and prepare many-to-many operations
71
- 71 // Example: relationships.author -> author_id: '123' for storage
72
- 72 // Example: relationships.tags -> array of pivot records to create later (only for provided relationships in PATCH)
73
- 73 const { belongsToUpdates, manyToManyRelationships } = processRelationships(
74
- 74 scope,
75
- 75 { context }
76
- 76 )
77
- 77
78
- 78 await validateResourceAttributesBeforeWrite({
79
- 79 context,
80
- 80 schema,
81
- 81 belongsToUpdates,
82
- 82 runHooks,
83
- 83 isPartialValidation: true
84
- 84 })
85
- 85
86
- 86 // Fetch minimal record for authorization checks
87
- 87 const minimalRecord = await helpers.dataGetMinimal({
88
- 88 scopeName,
89
- 89 context,
90
- 90 runHooks
91
- 91 })
92
- 92
93
- 93 if (!minimalRecord) {
94
- 94 throw new RestApiResourceError(
95
- 95 `Resource not found: ${scopeName}/${context.id}`,
96
- 96 ERROR_SUBTYPES.NOT_FOUND
97
- 97 )
98
- 98 }
99
- 99
100
- 100 context.minimalRecord = minimalRecord
101
- 101
102
- 102 // Centralised checkPermissions function
103
- 103 await scope.checkPermissions({
104
- 104 method: 'patch',
105
- 105 originalContext: context,
106
- 106 })
107
- 107
108
- 108 // Merge belongsTo updates into attributes before patching the record
109
- 109 if (Object.keys(belongsToUpdates).length > 0) {
110
- 110 context.inputRecord.data.attributes = {
111
- 111 ...context.inputRecord.data.attributes,
112
- 112 ...belongsToUpdates
113
- 113 }
114
- 114 }
115
- 115
116
- 116 await runHooks('beforeDataCall')
117
- 117 await runHooks('beforeDataCallPatch')
118
- 118
119
- 119 // Apply field setters after validation and before storage
120
- 120 if (context.inputRecord?.data?.attributes) {
121
- 121 context.inputRecord.data.attributes = await applyFieldSetters(
122
- 122 context.inputRecord.data.attributes,
123
- 123 context.schemaInfo,
124
- 124 context,
125
- 125 api,
126
- 126 helpers
127
- 127 )
128
- 128 }
129
- 129
130
- 130 // Call the storage helper - should return the patched record
131
- 131 await helpers.dataPatch({
132
- 132 scopeName,
133
- 133 context
134
- 134 })
135
- 135
136
- 136 await runHooks('afterDataCallPatch')
137
- 137 await runHooks('afterDataCall')
138
- 138
139
- 139 // Process many-to-many relationships after main record update
140
- 140 // For PATCH, we only update the relationships that were explicitly provided
141
- 141 for (const { relName, relDef, relData } of manyToManyRelationships) {
142
- 142 if (relDef?.through && api.anyapi?.links?.syncMany) {
143
- 143 await api.anyapi.links.syncMany({
144
- 144 context,
145
- 145 scopeName,
146
- 146 relName,
147
- 147 relDef,
148
- 148 relData,
149
- 149 isUpdate: true,
150
- 150 })
151
- 151 continue
152
- 152 }
153
- 153
154
- 154 validatePivotResource(scopes, relDef, relName)
155
- 155
156
- 156 await updateManyToManyRelationship(null, {
157
- 157 api,
158
- 158 context: {
159
- 159 resourceId: context.id,
160
- 160 relDef,
161
- 161 relData,
162
- 162 transaction: context.transaction
163
- 163 }
164
- 164 })
165
- 165 }
166
- 166
167
- 167 const ret = await handleRecordReturnAfterWrite({
168
- 168 context,
169
- 169 scopeName,
170
- 170 api,
171
- 171 scopes,
172
- 172 schemaStructure,
173
- 173 schemaRelationships,
174
- 174 scopeOptions,
175
- 175 vars,
176
- 176 runHooks,
177
- 177 helpers,
178
- 178 log
179
- 179 })
180
- 180
181
- 181 // Commit transaction if we created it
182
- 182 if (context.shouldCommit) {
183
- 183 await context.transaction.commit()
184
- 184 await runHooks('afterCommit')
185
- 185 }
186
- 186
187
- 187 return ret
188
- 188 } catch (error) {
189
- 189 await handleWriteMethodError(error, context, 'PATCH', scopeName, log, runHooks)
190
- 190 }
191
- 191 }
@@ -1,110 +0,0 @@
1
- 1 import { RestApiResourceError, RestApiValidationError, RestApiPayloadError } from '../../../lib/rest-api-errors.js'
2
- 2 import { findRelationshipDefinition, handleWriteMethodError } from './common.js'
3
- 3 import { createPivotRecords } from '../lib/writing/many-to-many-manipulations.js'
4
- 4
5
- 5 /**
6
- 6 * POST RELATIONSHIP
7
- 7 * Adds members to a to-many relationship
8
- 8 * POST /api/articles/1/relationships/tags
9
- 9 *
10
- 10 * @param {string} id - The ID of the resource
11
- 11 * @param {string} relationshipName - The name of the relationship
12
- 12 * @param {array} relationshipData - Array of resource identifiers to add
13
- 13 * @returns {Promise<void>} 204 No Content
14
- 14 */
15
- 15 export default async function postRelationshipMethod ({ params, context, vars, helpers, scope, scopes, runHooks, scopeName, api, log }) {
16
- 16 context.method = 'postRelationship'
17
- 17 context.id = params.id
18
- 18 context.relationshipName = params.relationshipName
19
- 19 context.schemaInfo = scopes[scopeName].vars.schemaInfo
20
- 20
21
- 21 // Transaction handling
22
- 22 context.transaction = params.transaction ||
23
- 23 (helpers.newTransaction && !params.transaction ? await helpers.newTransaction() : null)
24
- 24 context.shouldCommit = !params.transaction && !!context.transaction
25
- 25
26
- 26 context.db = context.transaction || api.knex.instance
27
- 27
28
- 28 try {
29
- 29 // Validate
30
- 30 const relDef = findRelationshipDefinition(context.schemaInfo, context.relationshipName)
31
- 31 if (!relDef) {
32
- 32 throw new RestApiResourceError(
33
- 33 `Relationship '${context.relationshipName}' not found on resource '${scopeName}'`,
34
- 34 { subtype: 'relationship_not_found' }
35
- 35 )
36
- 36 }
37
- 37
38
- 38 if (relDef.type !== 'hasMany' && relDef.type !== 'manyToMany') {
39
- 39 throw new RestApiValidationError(
40
- 40 `Cannot POST to to-one relationship '${context.relationshipName}'. Use PATCH instead.`,
41
- 41 { fields: ['data'] }
42
- 42 )
43
- 43 }
44
- 44
45
- 45 if (!Array.isArray(params.relationshipData)) {
46
- 46 throw new RestApiPayloadError('POST to relationship requires array of resource identifiers')
47
- 47 }
48
- 48
49
- 49 // Check permissions
50
- 50 await runHooks('checkPermissions')
51
- 51 await runHooks('checkPermissionsPostRelationship')
52
- 52
53
- 53 // Verify parent exists
54
- 54 const exists = await helpers.dataExists({
55
- 55 scopeName,
56
- 56 context: { db: context.db, id: context.id, schemaInfo: context.schemaInfo },
57
- 57 })
58
- 58
59
- 59 if (!exists) {
60
- 60 throw new RestApiResourceError('Resource not found', { subtype: 'not_found' })
61
- 61 }
62
- 62
63
- 63 // Add relationships
64
- 64 if (relDef?.through) {
65
- 65 if (api.anyapi?.links?.attachMany) {
66
- 66 await api.anyapi.links.attachMany({
67
- 67 context,
68
- 68 scopeName,
69
- 69 relName: context.relationshipName,
70
- 70 relDef,
71
- 71 relData: params.relationshipData,
72
- 72 })
73
- 73 } else {
74
- 74 await createPivotRecords(api, context.id, {
75
- 75 through: relDef.through,
76
- 76 foreignKey: relDef.foreignKey,
77
- 77 otherKey: relDef.otherKey
78
- 78 }, params.relationshipData, context.transaction)
79
- 79 }
80
- 80 } else if (relDef.type === 'hasMany') {
81
- 81 // Update foreign keys for hasMany
82
- 82 const targetType = relDef.target
83
- 83 for (const identifier of params.relationshipData) {
84
- 84 await api.resources[targetType].patch({
85
- 85 id: identifier.id,
86
- 86 inputRecord: {
87
- 87 data: {
88
- 88 type: targetType,
89
- 89 id: identifier.id,
90
- 90 attributes: { [relDef.foreignKey]: context.id }
91
- 91 }
92
- 92 },
93
- 93 transaction: context.transaction,
94
- 94 simplified: false
95
- 95 })
96
- 96 }
97
- 97 }
98
- 98
99
- 99 await runHooks('finish')
100
- 100 await runHooks('finishPostRelationship')
101
- 101
102
- 102 if (context.shouldCommit) {
103
- 103 await context.transaction.commit()
104
- 104 }
105
- 105
106
- 106 // 204 No Content
107
- 107 } catch (error) {
108
- 108 await handleWriteMethodError(error, context, 'POST_RELATIONSHIP', scopeName, log, runHooks)
109
- 109 }
110
- 110 };
@@ -1,168 +0,0 @@
1
- 1 import { validatePostPayload } from '../lib/querying-writing/payload-validators.js'
2
- 2 import { processRelationships } from '../lib/writing/relationship-processor.js'
3
- 3 import { createPivotRecords } from '../lib/writing/many-to-many-manipulations.js'
4
- 4 import {
5
- 5 setupCommonRequest,
6
- 6 validateResourceAttributesBeforeWrite,
7
- 7 validateRelationshipAccess,
8
- 8 applyFieldSetters,
9
- 9 validatePivotResource,
10
- 10 handleRecordReturnAfterWrite,
11
- 11 handleWriteMethodError
12
- 12 } from './common.js'
13
- 13
14
- 14 /**
15
- 15 * POST
16
- 16 * Creates a new resource. The request must include a JSON:API document with a 'data' object
17
- 17 * containing 'type' and 'attributes'. It can also establish relationships to existing resources.
18
- 18 * The returned document contains the created resource with its server-assigned ID.
19
- 19 */
20
- 20 export default async function postMethod ({
21
- 21 params,
22
- 22 context,
23
- 23 vars,
24
- 24 helpers,
25
- 25 scope,
26
- 26 scopes,
27
- 27 runHooks,
28
- 28 apiOptions,
29
- 29 pluginOptions,
30
- 30 scopeOptions,
31
- 31 scopeName,
32
- 32 api,
33
- 33 log
34
- 34 }) {
35
- 35 context.method = 'post'
36
- 36
37
- 37 try {
38
- 38 const { schema, schemaStructure, schemaRelationships } = await setupCommonRequest({
39
- 39 params,
40
- 40 context,
41
- 41 vars,
42
- 42 scopes,
43
- 43 scopeOptions,
44
- 44 scopeName,
45
- 45 api,
46
- 46 helpers
47
- 47 })
48
- 48
49
- 49 // Run early hooks for pre-processing (e.g., file handling)
50
- 50 await runHooks('beforeProcessing')
51
- 51 await runHooks('beforeProcessingPost')
52
- 52
53
- 53 // Validate POST payload to ensure it follows JSON:API format and references valid resources.
54
- 54 // This checks the payload has required 'data' object with 'type' and 'attributes', validates
55
- 55 // that data.type matches a real resource type (preventing creation of non-existent resources),
56
- 56 // and ensures any relationships reference valid resource types with proper ID format.
57
- 57 // Example: data.type: 'articles' must be a registered scope, relationships.author must reference 'users'.
58
- 58 validatePostPayload(context.inputRecord, scopes)
59
- 59
60
- 60 // Validate that user has read access to all related resources
61
- 61 // This ensures users can only create relationships to resources they can access
62
- 62 await validateRelationshipAccess(context, context.inputRecord, helpers, runHooks, api)
63
- 63
64
- 64 // Extract foreign keys from JSON:API relationships and prepare many-to-many operations
65
- 65 // Example: relationships.author -> author_id: '123' for storage
66
- 66 // Example: relationships.tags -> array of pivot records to create later
67
- 67 const { belongsToUpdates, manyToManyRelationships } = await processRelationships(
68
- 68 scope,
69
- 69 { context }
70
- 70 )
71
- 71
72
- 72 // Merge belongsTo updates into attributes before validation (like PUT/PATCH do)
73
- 73 if (Object.keys(belongsToUpdates).length > 0) {
74
- 74 context.inputRecord.data.attributes = {
75
- 75 ...context.inputRecord.data.attributes,
76
- 76 ...belongsToUpdates
77
- 77 }
78
- 78 }
79
- 79
80
- 80 await validateResourceAttributesBeforeWrite({
81
- 81 context,
82
- 82 schema,
83
- 83 belongsToUpdates,
84
- 84 runHooks
85
- 85 })
86
- 86
87
- 87 if (context.inputRecord?.data) {
88
- 88 const inputData = context.inputRecord.data
89
- 89 context.minimalRecord = {
90
- 90 type: scopeName,
91
- 91 ...(inputData.id !== undefined && inputData.id !== null ? { id: String(inputData.id) } : {}),
92
- 92 attributes: structuredClone(inputData.attributes || {}),
93
- 93 relationships: structuredClone(inputData.relationships || {})
94
- 94 }
95
- 95 }
96
- 96
97
- 97 // Centralised checkPermissions function
98
- 98 await scope.checkPermissions({
99
- 99 method: 'post',
100
- 100 originalContext: context,
101
- 101 })
102
- 102
103
- 103 await runHooks('beforeDataCall')
104
- 104 await runHooks('beforeDataCallPost')
105
- 105
106
- 106 // Apply field setters after validation and before storage
107
- 107 if (context.inputRecord?.data?.attributes) {
108
- 108 context.inputRecord.data.attributes = await applyFieldSetters(
109
- 109 context.inputRecord.data.attributes,
110
- 110 context.schemaInfo,
111
- 111 context,
112
- 112 api,
113
- 113 helpers
114
- 114 )
115
- 115 }
116
- 116
117
- 117 // Create the main record - storage helper should return the created record with its ID
118
- 118 context.id = await helpers.dataPost({
119
- 119 scopeName,
120
- 120 context
121
- 121 })
122
- 122
123
- 123 await runHooks('afterDataCallPost')
124
- 124 await runHooks('afterDataCall')
125
- 125
126
- 126 // Process many-to-many relationships after main record creation
127
- 127 for (const { relName, relDef, relData } of manyToManyRelationships) {
128
- 128 if (api.anyapi?.links?.attachMany && relDef?.through) {
129
- 129 await api.anyapi.links.attachMany({
130
- 130 context,
131
- 131 scopeName,
132
- 132 relName,
133
- 133 relDef,
134
- 134 relData,
135
- 135 })
136
- 136 continue
137
- 137 }
138
- 138
139
- 139 // Validate pivot resource exists
140
- 140 validatePivotResource(scopes, relDef, relName)
141
- 141 await createPivotRecords(api, context.id, relDef, relData, context.transaction)
142
- 142 }
143
- 143
144
- 144 const ret = await handleRecordReturnAfterWrite({
145
- 145 context,
146
- 146 scopeName,
147
- 147 api,
148
- 148 scopes,
149
- 149 schemaStructure,
150
- 150 schemaRelationships,
151
- 151 scopeOptions,
152
- 152 vars,
153
- 153 runHooks,
154
- 154 helpers,
155
- 155 log
156
- 156 })
157
- 157
158
- 158 // Commit transaction if we created it
159
- 159 if (context.shouldCommit) {
160
- 160 await context.transaction.commit()
161
- 161 await runHooks('afterCommit')
162
- 162 }
163
- 163
164
- 164 return ret
165
- 165 } catch (error) {
166
- 166 await handleWriteMethodError(error, context, 'POST', scopeName, log, runHooks)
167
- 167 }
168
- 168 }