json-rest-api 1.0.29 → 2.0.0

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 (292) hide show
  1. package/LICENSE +10 -670
  2. package/LICENSE-GPL-3.0 +674 -0
  3. package/LICENSE-MIT +21 -0
  4. package/README.md +39 -30
  5. package/docs/API.md +744 -1273
  6. package/docs/GUIDE/01-getting-started.md +338 -0
  7. package/docs/GUIDE/02-resources-and-relationships.md +33 -0
  8. package/docs/GUIDE/03-running-example.md +87 -0
  9. package/docs/GUIDE/04-creating-and-querying.md +181 -0
  10. package/docs/GUIDE/05-belongs-to.md +177 -0
  11. package/docs/GUIDE/06-has-many.md +184 -0
  12. package/docs/GUIDE/07-polymorphic-relationships.md +191 -0
  13. package/docs/GUIDE/08-many-to-many.md +184 -0
  14. package/docs/GUIDE/09-pagination-and-sorting.md +149 -0
  15. package/docs/GUIDE/10-put-and-patch.md +174 -0
  16. package/docs/GUIDE/11-relationship-endpoints.md +174 -0
  17. package/docs/GUIDE/12-field-transformations.md +287 -0
  18. package/docs/GUIDE/13-hooks-and-lifecycle.md +479 -0
  19. package/docs/GUIDE/14-custom-resource-methods.md +125 -0
  20. package/docs/GUIDE/15-query-projections.md +266 -0
  21. package/docs/GUIDE/{GUIDE_X_Autofiltering.md → 16-autofiltering.md} +77 -15
  22. package/docs/GUIDE/{GUIDE_X_Row_Policies.md → 17-row-policies.md} +137 -44
  23. package/docs/GUIDE/18-labels.md +86 -0
  24. package/docs/GUIDE/19-managed-transactions.md +179 -0
  25. package/docs/GUIDE/20-transaction-outcomes.md +207 -0
  26. package/docs/GUIDE/21-schema-and-migrations.md +634 -0
  27. package/docs/GUIDE/22-version-field-migrations.md +208 -0
  28. package/docs/GUIDE/23-fastify.md +179 -0
  29. package/docs/GUIDE/24-cors.md +172 -0
  30. package/docs/GUIDE/{GUIDE_X_URL_Management.md → 25-url-management.md} +7 -1
  31. package/docs/GUIDE/26-file-uploads.md +278 -0
  32. package/docs/GUIDE/27-bulk-operations.md +277 -0
  33. package/docs/GUIDE/28-socketio.md +325 -0
  34. package/docs/GUIDE/29-writing-plugins.md +278 -0
  35. package/docs/GUIDE/30-backend-capabilities.md +100 -0
  36. package/docs/GUIDE/31-positioning.md +181 -0
  37. package/docs/GUIDE/{GUIDE_Y_Appendices.md → 32-date-and-time.md} +74 -30
  38. package/docs/GUIDE/33-migrating-to-v2.md +3827 -0
  39. package/docs/GUIDE/index.md +51 -59
  40. package/docs/QUICKSTART.md +120 -287
  41. package/docs/architecture.md +75 -0
  42. package/docs/contributing.md +178 -0
  43. package/index.d.ts +36 -0
  44. package/index.js +1 -22
  45. package/lib/enhanced-logger.js +91 -85
  46. package/lib/error-context.js +445 -5
  47. package/lib/error-formatter.js +219 -59
  48. package/lib/knex-transaction.js +64 -0
  49. package/lib/logger-types.d.ts +22 -0
  50. package/lib/missing-package.js +4 -0
  51. package/lib/rest-api-errors.js +103 -8
  52. package/lib/runtime/json-rest-api.js +172 -0
  53. package/lib/transaction-types.d.ts +59 -0
  54. package/package.json +97 -27
  55. package/plugins/core/bulk-operations-plugin.js +191 -184
  56. package/plugins/core/connectors/express-plugin.js +148 -219
  57. package/plugins/core/connectors/fastify-plugin.js +125 -82
  58. package/plugins/core/connectors/lib/busboy-detector.js +74 -156
  59. package/plugins/core/connectors/lib/connector-core.js +144 -20
  60. package/plugins/core/connectors/lib/formidable-detector.js +63 -120
  61. package/plugins/core/connectors/lib/http-validators.js +77 -0
  62. package/plugins/core/connectors/lib/multipart-helpers.js +18 -0
  63. package/plugins/core/connectors/lib/request-helpers.js +10 -1
  64. package/plugins/core/connectors/lib/transport-http-helpers.js +89 -8
  65. package/plugins/core/connectors/lib/transport-route-schemas.js +7 -4
  66. package/plugins/core/file-handling-plugin.js +87 -177
  67. package/plugins/core/lib/anyapi/anyapi-registry.js +213 -164
  68. package/plugins/core/lib/anyapi/canonical-link-store.js +507 -0
  69. package/plugins/core/lib/anyapi/canonical-relationship-reader.js +598 -0
  70. package/plugins/core/lib/anyapi/schema-utils.js +25 -16
  71. package/plugins/core/lib/anyapi/utils/descriptor-helpers.js +0 -64
  72. package/plugins/core/lib/dbIntrospection.js +228 -21
  73. package/plugins/core/lib/dbTablesOperations.js +409 -92
  74. package/plugins/core/lib/fractional-positioning.js +12 -48
  75. package/plugins/core/lib/querying/identifier-query.js +19 -0
  76. package/plugins/core/lib/querying/include-query-helpers.js +364 -0
  77. package/plugins/core/lib/querying/include-to-many.js +428 -0
  78. package/plugins/core/lib/querying/include-to-one.js +579 -0
  79. package/plugins/core/lib/querying/knex-cross-table-search.js +31 -307
  80. package/plugins/core/lib/querying/knex-json-api-transformers-querying.js +99 -401
  81. package/plugins/core/lib/querying/knex-pagination-helpers.js +48 -317
  82. package/plugins/core/lib/querying/knex-process-includes.js +5 -110
  83. package/plugins/core/lib/querying/knex-query-helpers-base.js +21 -56
  84. package/plugins/core/lib/querying/knex-query-helpers.js +298 -448
  85. package/plugins/core/lib/querying/knex-relationship-includes.js +120 -2138
  86. package/plugins/core/lib/querying/knex-window-queries.js +62 -253
  87. package/plugins/core/lib/querying/query-builder-utils.js +44 -4
  88. package/plugins/core/lib/querying/query-constraint.d.ts +12 -0
  89. package/plugins/core/lib/querying/query-constraint.js +27 -0
  90. package/plugins/core/lib/querying/query-field-sort-helpers.js +134 -37
  91. package/plugins/core/lib/querying/relationship-identifiers.js +208 -0
  92. package/plugins/core/lib/querying/sort-helpers.js +13 -1
  93. package/plugins/core/lib/querying/storage-adapter-utils.js +13 -5
  94. package/plugins/core/lib/querying/url-helpers.js +11 -4
  95. package/plugins/core/lib/querying-writing/compile-schemas.js +148 -249
  96. package/plugins/core/lib/querying-writing/connectors-query-parser.js +33 -118
  97. package/plugins/core/lib/querying-writing/database-capabilities.js +149 -223
  98. package/plugins/core/lib/querying-writing/database-value-normalizers.js +179 -194
  99. package/plugins/core/lib/querying-writing/default-data-helpers.js +15 -45
  100. package/plugins/core/lib/querying-writing/field-selection-types.d.ts +43 -0
  101. package/plugins/core/lib/querying-writing/field-utils.js +113 -92
  102. package/plugins/core/lib/querying-writing/knex-constants.d.ts +18 -0
  103. package/plugins/core/lib/querying-writing/knex-constants.js +2 -26
  104. package/plugins/core/lib/querying-writing/knex-field-helpers.js +100 -290
  105. package/plugins/core/lib/querying-writing/knex-json-api-transformers.js +31 -120
  106. package/plugins/core/lib/querying-writing/query-field-helpers.js +108 -32
  107. package/plugins/core/lib/querying-writing/query-field-types.d.ts +60 -0
  108. package/plugins/core/lib/querying-writing/relationship-contracts.js +33 -13
  109. package/plugins/core/lib/querying-writing/request-contracts.js +93 -38
  110. package/plugins/core/lib/querying-writing/resource-id-normalization.js +23 -5
  111. package/plugins/core/lib/querying-writing/response-options.js +35 -0
  112. package/plugins/core/lib/querying-writing/schema-helpers.js +203 -324
  113. package/plugins/core/lib/querying-writing/scope-validations.js +92 -290
  114. package/plugins/core/lib/querying-writing/simplified-helpers.js +113 -712
  115. package/plugins/core/lib/storage/canonical-data-helpers.js +683 -0
  116. package/plugins/core/lib/storage/canonical-storage-mapping.js +51 -20
  117. package/plugins/core/lib/storage/ordinary-data-helpers.js +710 -0
  118. package/plugins/core/lib/storage/storage-adapter.js +128 -62
  119. package/plugins/core/lib/storage/storage-mapping.js +78 -28
  120. package/plugins/core/lib/storage/storage-types.d.ts +502 -0
  121. package/plugins/core/lib/writing/many-to-many-manipulations.js +114 -284
  122. package/plugins/core/lib/writing/relationship-processor.js +94 -158
  123. package/plugins/core/lib/writing/resource-version.js +172 -0
  124. package/plugins/core/lib/writing/reverse-relationship-manipulations.js +110 -0
  125. package/plugins/core/rest-api-anyapi-knex-plugin.js +116 -2485
  126. package/plugins/core/rest-api-autofilter-plugin.js +17 -19
  127. package/plugins/core/rest-api-cors-plugin.js +52 -205
  128. package/plugins/core/rest-api-knex-plugin.js +45 -1077
  129. package/plugins/core/rest-api-label-plugin.js +25 -38
  130. package/plugins/core/rest-api-plugin-hooks/register-relationship-routes.js +5 -5
  131. package/plugins/core/rest-api-plugin-hooks/register-scope-routes.js +9 -6
  132. package/plugins/core/rest-api-plugin-hooks/turn-scope-init-into-vars.js +7 -26
  133. package/plugins/core/rest-api-plugin-hooks/validate-include-configurations.js +20 -26
  134. package/plugins/core/rest-api-plugin-methods/add-route.js +4 -6
  135. package/plugins/core/rest-api-plugin-methods/apply-query-filters.js +42 -13
  136. package/plugins/core/rest-api-plugin-methods/check-permissions.js +11 -5
  137. package/plugins/core/rest-api-plugin-methods/common.js +291 -395
  138. package/plugins/core/rest-api-plugin-methods/delete-relationship.js +49 -47
  139. package/plugins/core/rest-api-plugin-methods/delete.js +31 -25
  140. package/plugins/core/rest-api-plugin-methods/enrich-attributes.js +108 -148
  141. package/plugins/core/rest-api-plugin-methods/get-related.js +113 -305
  142. package/plugins/core/rest-api-plugin-methods/get-relationship.js +25 -18
  143. package/plugins/core/rest-api-plugin-methods/get.js +36 -68
  144. package/plugins/core/rest-api-plugin-methods/lifecycle-types.d.ts +225 -0
  145. package/plugins/core/rest-api-plugin-methods/patch-relationship.js +28 -25
  146. package/plugins/core/rest-api-plugin-methods/patch.js +46 -99
  147. package/plugins/core/rest-api-plugin-methods/post-relationship.js +49 -53
  148. package/plugins/core/rest-api-plugin-methods/post.js +51 -51
  149. package/plugins/core/rest-api-plugin-methods/put.js +69 -148
  150. package/plugins/core/rest-api-plugin-methods/query.js +37 -81
  151. package/plugins/core/rest-api-plugin-methods/release.js +2 -1
  152. package/plugins/core/rest-api-plugin.js +32 -46
  153. package/plugins/core/rest-api-positioning-plugin.js +183 -628
  154. package/plugins/core/rest-api-query-projections-plugin.js +19 -102
  155. package/plugins/core/rest-api-row-policy-plugin.js +3 -3
  156. package/plugins/core/socketio-plugin.js +364 -344
  157. package/plugins/storage/local-storage.js +40 -36
  158. package/types/autofilter.d.ts +57 -0
  159. package/types/bulk-methods.d.ts +63 -0
  160. package/types/errors.d.ts +118 -0
  161. package/types/fastify-options.d.ts +19 -0
  162. package/types/file-detectors.d.ts +33 -0
  163. package/types/file-storage.d.ts +48 -0
  164. package/types/hook-context.d.ts +180 -0
  165. package/types/plugin-options.d.ts +53 -0
  166. package/types/relationship-methods.d.ts +74 -0
  167. package/types/representations.d.ts +50 -0
  168. package/types/resource-methods.d.ts +95 -0
  169. package/types/resource-schema.d.ts +157 -0
  170. package/types/row-policy.d.ts +31 -0
  171. package/types/runtime.d.ts +132 -0
  172. package/types/transactions.d.ts +21 -0
  173. package/.claude/agents/library-guide-expert.md +0 -30
  174. package/.claude/agents/rest-api-internals-expert.md +0 -54
  175. package/.claude/settings.local.json +0 -70
  176. package/AGENTS.md +0 -34
  177. package/CLAUDE.md +0 -160
  178. package/CRUSH.md +0 -55
  179. package/JSKIT_BACKEND_MIGRATION_TRACKER.txt +0 -180
  180. package/SINGLE_VALIDATION_CONTRACT_TODO.txt +0 -172
  181. package/TODO.todo +0 -13
  182. package/connectors_ideas/fastify.js +0 -217
  183. package/connectors_ideas/koa-plugin.js +0 -112
  184. package/docs/.bundle/config +0 -2
  185. package/docs/COMPARISON.md +0 -433
  186. package/docs/GUIDE/GUIDE_1_Initial_Setup.md +0 -1057
  187. package/docs/GUIDE/GUIDE_2_1_The_Starting_Point.md +0 -88
  188. package/docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md +0 -808
  189. package/docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md +0 -491
  190. package/docs/GUIDE/GUIDE_2_4_HasMany_Records.md +0 -398
  191. package/docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md +0 -898
  192. package/docs/GUIDE/GUIDE_2_6_Many_To_Many.md +0 -712
  193. package/docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md +0 -1247
  194. package/docs/GUIDE/GUIDE_2_8_Effects_of_PUT_and_PATCH.md +0 -748
  195. package/docs/GUIDE/GUIDE_2_9_Relationships_Urls.md +0 -517
  196. package/docs/GUIDE/GUIDE_2_Data_And_Relations.md +0 -66
  197. package/docs/GUIDE/GUIDE_3_Field_Transformations.md +0 -1252
  198. package/docs/GUIDE/GUIDE_5_Non-Db_Resources.md +0 -1107
  199. package/docs/GUIDE/GUIDE_6_Positioning.md +0 -692
  200. package/docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md +0 -1491
  201. package/docs/GUIDE/GUIDE_X_Bulk_Operations.md +0 -732
  202. package/docs/GUIDE/GUIDE_X_Cors.md +0 -1957
  203. package/docs/GUIDE/GUIDE_X_Fastify.md +0 -78
  204. package/docs/GUIDE/GUIDE_X_File_Uploads.md +0 -729
  205. package/docs/GUIDE/GUIDE_X_Knex_Schema_And_Migrations.md +0 -370
  206. package/docs/GUIDE/GUIDE_X_Query_Projections.md +0 -159
  207. package/docs/GUIDE/GUIDE_X_SocketIO.md +0 -1040
  208. package/docs/GUIDE/GUIDE_X_Writing_Plugins.md +0 -201
  209. package/docs/Gemfile +0 -6
  210. package/docs/Gemfile.lock +0 -308
  211. package/docs/ONBOARDING.md +0 -315
  212. package/docs/_config.yml +0 -36
  213. package/docs/_layouts/default.html +0 -123
  214. package/docs/apple-touch-icon.svg +0 -4
  215. package/docs/assets/css/style.scss +0 -392
  216. package/docs/favicon.svg +0 -4
  217. package/docs/index.md +0 -164
  218. package/docs/mini-guides/file-uploads-complete.md +0 -161
  219. package/eslint.config.js +0 -13
  220. package/examples/url-override-example.js +0 -137
  221. package/fixing_plan.md +0 -112
  222. package/plugins/core/lib/anyapi/query/anyapi-query-adapter.js +0 -657
  223. package/plugins/core/lib/querying-writing/return-record-settings.js +0 -47
  224. package/plugins/core/lib/writing/knex-json-api-transformers-writing.js +0 -110
  225. package/plugins/storage/s3-storage.js +0 -93
  226. package/quickTest.js +0 -136
  227. package/scripts/debug-anyapi-review-filter.mjs +0 -63
  228. package/scripts/inspect-anyapi-descriptor-country.mjs +0 -19
  229. package/scripts/inspect-anyapi-descriptor.mjs +0 -19
  230. package/scripts/run-docs.js +0 -133
  231. package/test-debug.js +0 -59
  232. package/tests/README.md +0 -202
  233. package/tests/TEST_TEMPLATE.test.js +0 -178
  234. package/tests/anyapi-basic.test.js +0 -105
  235. package/tests/anyapi-cursor-pagination.test.js +0 -148
  236. package/tests/anyapi-custom-idproperty.test.js +0 -163
  237. package/tests/autofilter.test.js +0 -463
  238. package/tests/bulk-operations.test.js +0 -657
  239. package/tests/computed-fields.test.js +0 -586
  240. package/tests/cors.test.js +0 -834
  241. package/tests/curl.test.js +0 -467
  242. package/tests/custom-id-mysql-returning-fallback.test.js +0 -128
  243. package/tests/custom-id-simplified-post.test.js +0 -75
  244. package/tests/custom-idproperty-relationships.test.js +0 -932
  245. package/tests/db-introspection.test.js +0 -521
  246. package/tests/db-table-operations.test.js +0 -798
  247. package/tests/fastify-plugin.test.js +0 -184
  248. package/tests/field-getters.test.js +0 -370
  249. package/tests/field-setters.test.js +0 -306
  250. package/tests/file-handling.test.js +0 -155
  251. package/tests/fixtures/api-configs.js +0 -2566
  252. package/tests/full-jsonapi-belongsto-linkage.test.js +0 -150
  253. package/tests/helpers/fake-fastify.js +0 -99
  254. package/tests/helpers/storage-mode.js +0 -65
  255. package/tests/helpers/test-utils.js +0 -203
  256. package/tests/http-connectors-parity.test.js +0 -395
  257. package/tests/id-normalization.test.js +0 -444
  258. package/tests/include-depth-validation.test.js +0 -331
  259. package/tests/includes.test.js +0 -427
  260. package/tests/local-storage.test.js +0 -96
  261. package/tests/logical-id-write-validation.test.js +0 -151
  262. package/tests/nested-includes.test.js +0 -617
  263. package/tests/pagination-boundaries.test.js +0 -154
  264. package/tests/pagination-contract.test.js +0 -122
  265. package/tests/pagination-cursor-multifield.test.js +0 -641
  266. package/tests/pagination-enhanced.test.js +0 -563
  267. package/tests/polymorphic-relationships.test.js +0 -347
  268. package/tests/polymorphic-simple.test.js +0 -148
  269. package/tests/positioning-advanced.test.js +0 -692
  270. package/tests/positioning-extended.test.js +0 -915
  271. package/tests/positioning-multifilter.test.js +0 -699
  272. package/tests/positioning.test.js +0 -560
  273. package/tests/post-commit-hooks.test.js +0 -120
  274. package/tests/projected-fields.test.js +0 -319
  275. package/tests/public-exports.test.js +0 -15
  276. package/tests/put-create-parity.test.js +0 -216
  277. package/tests/queries.test.js +0 -521
  278. package/tests/query-limits.test.js +0 -584
  279. package/tests/relationship-endpoints.test.js +0 -620
  280. package/tests/relationship-include-storage-adapter.test.js +0 -56
  281. package/tests/relationships.test.js +0 -681
  282. package/tests/rest-api.test.js +0 -337
  283. package/tests/return-record-settings.test.js +0 -184
  284. package/tests/row-policy.test.js +0 -489
  285. package/tests/s3-storage.test.js +0 -38
  286. package/tests/searchschema-knex-behavior.test.js +0 -187
  287. package/tests/searchschema-merge.test.js +0 -151
  288. package/tests/socketio.test.js +0 -860
  289. package/tests/storage-mapping.test.js +0 -362
  290. package/tests/temporal-boundaries.test.js +0 -208
  291. package/tests/test-development-guide.md +0 -370
  292. package/tests/virtual-fields.test.js +0 -404
@@ -1,337 +0,0 @@
1
- import { describe, it, before, beforeEach, after } from 'node:test'
2
- import assert from 'node:assert/strict'
3
- import knexLib from 'knex'
4
- import { createBasicApi, createExtendedApi } from './fixtures/api-configs.js'
5
- import {
6
- validateJsonApiStructure,
7
- resourceIdentifier,
8
- cleanTables,
9
- countRecords,
10
- createJsonApiDocument,
11
- createRelationship,
12
- createToManyRelationship,
13
- assertResourceAttributes,
14
- assertResourceRelationship
15
- } from './helpers/test-utils.js'
16
-
17
- // Create Knex instance for tests
18
- const knex = knexLib({
19
- client: 'better-sqlite3',
20
- connection: {
21
- filename: ':memory:'
22
- },
23
- useNullAsDefault: true
24
- })
25
-
26
- // API instances that persist across tests
27
- let basicApi
28
- let extendedApi
29
-
30
- describe('REST API Tests', () => {
31
- before(async () => {
32
- // Initialize APIs once
33
- basicApi = await createBasicApi(knex)
34
- extendedApi = await createExtendedApi(knex)
35
- })
36
-
37
- after(async () => {
38
- // Close database connection to allow tests to exit
39
- await knex.destroy()
40
- })
41
-
42
- describe('Basic CRUD Operations', () => {
43
- beforeEach(async () => {
44
- // Clean all tables before each test
45
- await cleanTables(knex, [
46
- 'basic_countries', 'basic_publishers', 'basic_authors', 'basic_books', 'basic_book_authors'
47
- ])
48
- })
49
-
50
- describe('POST - Create Resources', () => {
51
- it('should create a country', async () => {
52
- const countryDoc = createJsonApiDocument('countries', {
53
- name: 'United States',
54
- code: 'US'
55
- })
56
-
57
- const result = await basicApi.resources.countries.post({
58
- inputRecord: countryDoc,
59
- simplified: false
60
- })
61
-
62
- // Verify response structure
63
- validateJsonApiStructure(result, false)
64
- assert.equal(result.data.type, 'countries')
65
- assert(result.data.id, 'Should have an ID')
66
- assertResourceAttributes(result.data, {
67
- name: 'United States',
68
- code: 'US'
69
- })
70
-
71
- // Verify record was created in database
72
- const count = await countRecords(knex, 'basic_countries')
73
- assert.equal(count, 1, 'Should have created one country')
74
-
75
- // Verify data through API GET
76
- const getResult = await basicApi.resources.countries.get({
77
- id: result.data.id,
78
- simplified: false
79
- })
80
-
81
- validateJsonApiStructure(getResult, false)
82
- assert.equal(getResult.data.type, 'countries')
83
- assert.equal(getResult.data.id, result.data.id)
84
- assertResourceAttributes(getResult.data, {
85
- name: 'United States',
86
- code: 'US'
87
- })
88
- })
89
-
90
- it('should create a publisher with country relationship', async () => {
91
- // First create a country
92
- const countryDoc = createJsonApiDocument('countries', {
93
- name: 'United Kingdom',
94
- code: 'UK'
95
- })
96
-
97
- const countryResult = await basicApi.resources.countries.post({
98
- inputRecord: countryDoc,
99
- simplified: false
100
- })
101
-
102
- // Create publisher with country relationship
103
- const publisherDoc = createJsonApiDocument('publishers',
104
- {
105
- name: 'Penguin Random House'
106
- },
107
- {
108
- country: createRelationship(resourceIdentifier('countries', countryResult.data.id))
109
- }
110
- )
111
-
112
- const publisherResult = await basicApi.resources.publishers.post({
113
- inputRecord: publisherDoc,
114
- simplified: false
115
- })
116
-
117
- // Verify response
118
- validateJsonApiStructure(publisherResult, false)
119
- assert.equal(publisherResult.data.type, 'publishers')
120
- assert(publisherResult.data.id, 'Should have an ID')
121
-
122
- // Verify records in database
123
- const publisherCount = await countRecords(knex, 'basic_publishers')
124
- assert.equal(publisherCount, 1, 'Should have created one publisher')
125
-
126
- // Verify data and relationship through API
127
- const getResult = await basicApi.resources.publishers.get({
128
- id: publisherResult.data.id,
129
- queryParams: {
130
- include: ['country']
131
- },
132
- simplified: false
133
- })
134
-
135
- validateJsonApiStructure(getResult, false)
136
- assertResourceAttributes(getResult.data, {
137
- name: 'Penguin Random House'
138
- })
139
- assertResourceRelationship(getResult.data, 'country',
140
- resourceIdentifier('countries', countryResult.data.id))
141
-
142
- // Verify included data
143
- assert(getResult.included, 'Should have included data')
144
- assert.equal(getResult.included.length, 1, 'Should include one resource')
145
- assert.equal(getResult.included[0].type, 'countries')
146
- assert.equal(getResult.included[0].attributes.name, 'United Kingdom')
147
- })
148
-
149
- it('should fail to create resource with missing required field', async () => {
150
- const invalidDoc = createJsonApiDocument('countries', {
151
- code: 'FR'
152
- // Missing required 'name' field
153
- })
154
-
155
- await assert.rejects(
156
- async () => {
157
- await basicApi.resources.countries.post({
158
- inputRecord: invalidDoc,
159
- simplified: false,
160
- returnFullRecord: false
161
- })
162
- },
163
- (err) => {
164
- return err.code === 'REST_API_VALIDATION'
165
- },
166
- 'Should throw validation error for missing required field'
167
- )
168
-
169
- // Verify no record was created
170
- const count = await countRecords(knex, 'basic_countries')
171
- assert.equal(count, 0, 'Should not have created any countries')
172
- })
173
- })
174
-
175
- describe('GET - Retrieve Resources', () => {
176
- it('should retrieve a single resource', async () => {
177
- // Create test data
178
- const countryDoc = createJsonApiDocument('countries', {
179
- name: 'Canada',
180
- code: 'CA'
181
- })
182
-
183
- const createResult = await basicApi.resources.countries.post({
184
- inputRecord: countryDoc,
185
- simplified: false
186
- })
187
-
188
- // Get the resource
189
- const getResult = await basicApi.resources.countries.get({
190
- id: createResult.data.id,
191
- simplified: false
192
- })
193
-
194
- validateJsonApiStructure(getResult, false)
195
- assert.equal(getResult.data.type, 'countries')
196
- assert.equal(getResult.data.id, createResult.data.id)
197
- assertResourceAttributes(getResult.data, {
198
- name: 'Canada',
199
- code: 'CA'
200
- })
201
- })
202
-
203
- it('should fail to retrieve non-existent resource', async () => {
204
- await assert.rejects(
205
- async () => {
206
- await basicApi.resources.countries.get({
207
- id: 99999,
208
- simplified: false
209
- })
210
- },
211
- (err) => {
212
- return err.code === 'REST_API_RESOURCE' && err.subtype === 'not_found'
213
- },
214
- 'Should throw not found error'
215
- )
216
- })
217
-
218
- it('should retrieve collection of resources', async () => {
219
- // Create multiple countries
220
- const countries = [
221
- { name: 'France', code: 'FR' },
222
- { name: 'Germany', code: 'DE' },
223
- { name: 'Italy', code: 'IT' }
224
- ]
225
-
226
- for (const country of countries) {
227
- const doc = createJsonApiDocument('countries', country)
228
- await basicApi.resources.countries.post({
229
- inputRecord: doc,
230
- simplified: false
231
- })
232
- }
233
-
234
- // Query all countries
235
- const queryResult = await basicApi.resources.countries.query({
236
- simplified: false
237
- })
238
-
239
- validateJsonApiStructure(queryResult, true)
240
- assert.equal(queryResult.data.length, 3, 'Should return 3 countries')
241
-
242
- // Verify all countries are present
243
- const names = queryResult.data.map(c => c.attributes.name).sort()
244
- assert.deepEqual(names, ['France', 'Germany', 'Italy'])
245
- })
246
- })
247
-
248
- describe('PATCH - Update Resources', () => {
249
- it('should partially update a resource', async () => {
250
- // Create a country
251
- const countryDoc = createJsonApiDocument('countries', {
252
- name: 'Spain',
253
- code: 'ES'
254
- })
255
-
256
- const createResult = await basicApi.resources.countries.post({
257
- inputRecord: countryDoc,
258
- simplified: false
259
- })
260
-
261
- // Update only the name
262
- const updateDoc = {
263
- data: {
264
- type: 'countries',
265
- id: String(createResult.data.id),
266
- attributes: {
267
- name: 'España'
268
- }
269
- }
270
- }
271
-
272
- await basicApi.resources.countries.patch({
273
- id: createResult.data.id,
274
- inputRecord: updateDoc,
275
- simplified: false
276
- })
277
-
278
- // Verify the update
279
- const getResult = await basicApi.resources.countries.get({
280
- id: createResult.data.id,
281
- simplified: false
282
- })
283
-
284
- assertResourceAttributes(getResult.data, {
285
- name: 'España',
286
- code: 'ES' // Should remain unchanged
287
- })
288
- })
289
- })
290
-
291
- describe('DELETE - Remove Resources', () => {
292
- it('should delete an existing resource', async () => {
293
- // Create a country
294
- const countryDoc = createJsonApiDocument('countries', {
295
- name: 'Portugal',
296
- code: 'PT'
297
- })
298
-
299
- const createResult = await basicApi.resources.countries.post({
300
- inputRecord: countryDoc,
301
- simplified: false
302
- })
303
-
304
- // Verify it exists
305
- let count = await countRecords(knex, 'basic_countries')
306
- assert.equal(count, 1, 'Should have one country before delete')
307
-
308
- // Delete the resource
309
- const deleteResult = await basicApi.resources.countries.delete({
310
- id: createResult.data.id,
311
- simplified: false
312
- })
313
-
314
- // DELETE returns undefined (204 No Content)
315
- assert.equal(deleteResult, undefined, 'Delete should return undefined')
316
-
317
- // Verify it's gone
318
- count = await countRecords(knex, 'basic_countries')
319
- assert.equal(count, 0, 'Should have no countries after delete')
320
-
321
- // Verify GET fails
322
- await assert.rejects(
323
- async () => {
324
- await basicApi.resources.countries.get({
325
- id: createResult.data.id,
326
- simplified: false
327
- })
328
- },
329
- (err) => {
330
- return err.code === 'REST_API_RESOURCE' && err.subtype === 'not_found'
331
- },
332
- 'Should throw not found error after delete'
333
- )
334
- })
335
- })
336
- })
337
- })
@@ -1,184 +0,0 @@
1
- import { describe, it, before, after, beforeEach } from 'node:test'
2
- import assert from 'node:assert/strict'
3
- import knexLib from 'knex'
4
- import { createReturnRecordApi } from './fixtures/api-configs.js'
5
- import { cleanTables } from './helpers/test-utils.js'
6
-
7
- const knex = knexLib({
8
- client: 'better-sqlite3',
9
- connection: {
10
- filename: ':memory:'
11
- },
12
- useNullAsDefault: true
13
- })
14
-
15
- let api
16
-
17
- function itemDocument (type, name, id) {
18
- const doc = {
19
- data: {
20
- type,
21
- attributes: { name }
22
- }
23
- }
24
-
25
- if (id !== undefined) {
26
- doc.data.id = String(id)
27
- }
28
-
29
- return doc
30
- }
31
-
32
- describe('Return record settings', () => {
33
- before(async () => {
34
- api = await createReturnRecordApi(knex)
35
- })
36
-
37
- after(async () => {
38
- await knex.destroy()
39
- })
40
-
41
- beforeEach(async () => {
42
- await cleanTables(knex, [
43
- 'return_global_items',
44
- 'return_scope_items'
45
- ])
46
- })
47
-
48
- it('normalizes plugin-level boolean return settings', async () => {
49
- const created = await api.resources.global_items.post({
50
- inputRecord: itemDocument('global_items', 'Global Created'),
51
- simplified: false
52
- })
53
-
54
- assert.equal(created.data.type, 'global_items')
55
- assert.equal(created.data.attributes.name, 'Global Created')
56
-
57
- const replaced = await api.resources.global_items.put({
58
- id: created.data.id,
59
- inputRecord: itemDocument('global_items', 'Global Replaced', created.data.id),
60
- simplified: false
61
- })
62
- assert.equal(replaced, undefined)
63
-
64
- const patched = await api.resources.global_items.patch({
65
- id: created.data.id,
66
- inputRecord: itemDocument('global_items', 'Global Patched', created.data.id),
67
- simplified: false
68
- })
69
- assert.equal(patched, undefined)
70
-
71
- const fetched = await api.resources.global_items.get({
72
- id: created.data.id,
73
- simplified: false
74
- })
75
- assert.equal(fetched.data.attributes.name, 'Global Patched')
76
- })
77
-
78
- it('normalizes per-call boolean return settings', async () => {
79
- const created = await api.resources.global_items.post({
80
- inputRecord: itemDocument('global_items', 'Per-call Created'),
81
- simplified: false,
82
- returnFullRecord: false
83
- })
84
- assert.equal(created, undefined)
85
-
86
- const queryResult = await api.resources.global_items.query({
87
- simplified: false
88
- })
89
- assert.equal(queryResult.data.length, 1)
90
- const fetched = queryResult.data[0]
91
- assert.equal(fetched.attributes.name, 'Per-call Created')
92
-
93
- const patched = await api.resources.global_items.patch({
94
- id: fetched.id,
95
- inputRecord: itemDocument('global_items', 'Per-call Patched', fetched.id),
96
- simplified: false,
97
- returnFullRecord: true
98
- })
99
- assert.equal(patched.data.type, 'global_items')
100
- assert.equal(patched.data.id, fetched.id)
101
- assert.equal(patched.data.attributes.name, 'Per-call Patched')
102
- })
103
-
104
- it('normalizes resource-level boolean return settings', async () => {
105
- const created = await api.resources.scope_items.post({
106
- inputRecord: itemDocument('scope_items', 'Scoped Created'),
107
- simplified: false
108
- })
109
- assert.equal(created, undefined)
110
-
111
- const queryResult = await api.resources.scope_items.query({
112
- simplified: false
113
- })
114
- assert.equal(queryResult.data.length, 1)
115
- const fetched = queryResult.data[0]
116
-
117
- const replaced = await api.resources.scope_items.put({
118
- id: fetched.id,
119
- inputRecord: itemDocument('scope_items', 'Scoped Replaced', fetched.id),
120
- simplified: false
121
- })
122
- assert.equal(replaced.data.type, 'scope_items')
123
- assert.equal(replaced.data.id, fetched.id)
124
- assert.equal(replaced.data.attributes.name, 'Scoped Replaced')
125
-
126
- const patched = await api.resources.scope_items.patch({
127
- id: fetched.id,
128
- inputRecord: itemDocument('scope_items', 'Scoped Patched', fetched.id),
129
- simplified: false
130
- })
131
- assert.deepEqual(patched, {
132
- data: {
133
- type: 'scope_items',
134
- id: fetched.id
135
- }
136
- })
137
- })
138
-
139
- it('normalizes transport boolean return settings', async () => {
140
- const created = await api.resources.global_items.post({
141
- inputRecord: itemDocument('global_items', 'Transport Created'),
142
- simplified: false,
143
- isTransport: true
144
- })
145
- assert.equal(created, undefined)
146
-
147
- const queryResult = await api.resources.global_items.query({
148
- simplified: false
149
- })
150
- assert.equal(queryResult.data.length, 1)
151
- const fetched = queryResult.data[0]
152
-
153
- const replaced = await api.resources.global_items.put({
154
- id: fetched.id,
155
- inputRecord: itemDocument('global_items', 'Transport Replaced', fetched.id),
156
- simplified: false,
157
- isTransport: true
158
- })
159
- assert.equal(replaced.data.type, 'global_items')
160
- assert.equal(replaced.data.id, fetched.id)
161
- assert.equal(replaced.data.attributes.name, 'Transport Replaced')
162
-
163
- const patched = await api.resources.global_items.patch({
164
- id: fetched.id,
165
- inputRecord: itemDocument('global_items', 'Transport Patched', fetched.id),
166
- simplified: false,
167
- isTransport: true
168
- })
169
- assert.deepEqual(patched, {
170
- data: {
171
- type: 'global_items',
172
- id: fetched.id
173
- }
174
- })
175
-
176
- const scoped = await api.resources.scope_items.post({
177
- inputRecord: itemDocument('scope_items', 'Scoped Transport Created'),
178
- simplified: false,
179
- isTransport: true
180
- })
181
- assert.equal(scoped.data.type, 'scope_items')
182
- assert.equal(scoped.data.attributes.name, 'Scoped Transport Created')
183
- })
184
- })