@furystack/rest-service 13.0.0 → 14.1.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 (311) hide show
  1. package/CHANGELOG.md +124 -0
  2. package/README.md +47 -28
  3. package/esm/actions/error-action.d.ts +7 -2
  4. package/esm/actions/error-action.d.ts.map +1 -1
  5. package/esm/actions/error-action.js +7 -2
  6. package/esm/actions/error-action.js.map +1 -1
  7. package/esm/actions/get-current-user.d.ts +1 -4
  8. package/esm/actions/get-current-user.d.ts.map +1 -1
  9. package/esm/actions/get-current-user.js +1 -4
  10. package/esm/actions/get-current-user.js.map +1 -1
  11. package/esm/actions/get-current-user.spec.js +10 -6
  12. package/esm/actions/get-current-user.spec.js.map +1 -1
  13. package/esm/actions/is-authenticated.d.ts +3 -4
  14. package/esm/actions/is-authenticated.d.ts.map +1 -1
  15. package/esm/actions/is-authenticated.js +3 -4
  16. package/esm/actions/is-authenticated.js.map +1 -1
  17. package/esm/actions/is-authenticated.spec.js +10 -6
  18. package/esm/actions/is-authenticated.spec.js.map +1 -1
  19. package/esm/actions/logout-action.spec.js +11 -9
  20. package/esm/actions/logout-action.spec.js.map +1 -1
  21. package/esm/actions/logout.d.ts +2 -6
  22. package/esm/actions/logout.d.ts.map +1 -1
  23. package/esm/actions/logout.js +3 -7
  24. package/esm/actions/logout.js.map +1 -1
  25. package/esm/actions/not-found-action.d.ts +1 -3
  26. package/esm/actions/not-found-action.d.ts.map +1 -1
  27. package/esm/actions/not-found-action.js +1 -3
  28. package/esm/actions/not-found-action.js.map +1 -1
  29. package/esm/actions/password-login-action.d.ts +5 -12
  30. package/esm/actions/password-login-action.d.ts.map +1 -1
  31. package/esm/actions/password-login-action.js +6 -13
  32. package/esm/actions/password-login-action.js.map +1 -1
  33. package/esm/actions/password-login-action.spec.js +23 -28
  34. package/esm/actions/password-login-action.spec.js.map +1 -1
  35. package/esm/authenticate.d.ts +5 -11
  36. package/esm/authenticate.d.ts.map +1 -1
  37. package/esm/authenticate.js +6 -12
  38. package/esm/authenticate.js.map +1 -1
  39. package/esm/authenticate.spec.js +41 -33
  40. package/esm/authenticate.spec.js.map +1 -1
  41. package/esm/authentication-providers/authentication-provider.d.ts +17 -0
  42. package/esm/authentication-providers/authentication-provider.d.ts.map +1 -1
  43. package/esm/authentication-providers/basic-auth-provider.d.ts +7 -0
  44. package/esm/authentication-providers/basic-auth-provider.d.ts.map +1 -1
  45. package/esm/authentication-providers/basic-auth-provider.js +7 -0
  46. package/esm/authentication-providers/basic-auth-provider.js.map +1 -1
  47. package/esm/authentication-providers/cookie-auth-provider.d.ts.map +1 -1
  48. package/esm/authentication-providers/cookie-auth-provider.js +8 -0
  49. package/esm/authentication-providers/cookie-auth-provider.js.map +1 -1
  50. package/esm/authorize.d.ts +5 -17
  51. package/esm/authorize.d.ts.map +1 -1
  52. package/esm/authorize.js +5 -17
  53. package/esm/authorize.js.map +1 -1
  54. package/esm/authorize.spec.js +34 -29
  55. package/esm/authorize.spec.js.map +1 -1
  56. package/esm/endpoint-generators/create-delete-endpoint.d.ts +6 -11
  57. package/esm/endpoint-generators/create-delete-endpoint.d.ts.map +1 -1
  58. package/esm/endpoint-generators/create-delete-endpoint.js +7 -11
  59. package/esm/endpoint-generators/create-delete-endpoint.js.map +1 -1
  60. package/esm/endpoint-generators/create-delete-endpoint.spec.js +14 -21
  61. package/esm/endpoint-generators/create-delete-endpoint.spec.js.map +1 -1
  62. package/esm/endpoint-generators/create-get-collection-endpoint.d.ts +11 -11
  63. package/esm/endpoint-generators/create-get-collection-endpoint.d.ts.map +1 -1
  64. package/esm/endpoint-generators/create-get-collection-endpoint.js +13 -12
  65. package/esm/endpoint-generators/create-get-collection-endpoint.js.map +1 -1
  66. package/esm/endpoint-generators/create-get-collection-endpoint.spec.js +43 -70
  67. package/esm/endpoint-generators/create-get-collection-endpoint.spec.js.map +1 -1
  68. package/esm/endpoint-generators/create-get-entity-endpoint.d.ts +6 -11
  69. package/esm/endpoint-generators/create-get-entity-endpoint.d.ts.map +1 -1
  70. package/esm/endpoint-generators/create-get-entity-endpoint.js +7 -11
  71. package/esm/endpoint-generators/create-get-entity-endpoint.js.map +1 -1
  72. package/esm/endpoint-generators/create-get-entity-endpoint.spec.js +16 -33
  73. package/esm/endpoint-generators/create-get-entity-endpoint.spec.js.map +1 -1
  74. package/esm/endpoint-generators/create-get-openapi-document-action.d.ts +1 -1
  75. package/esm/endpoint-generators/create-get-openapi-document-action.d.ts.map +1 -1
  76. package/esm/endpoint-generators/create-get-openapi-document-action.js +1 -1
  77. package/esm/endpoint-generators/create-get-openapi-document-action.js.map +1 -1
  78. package/esm/endpoint-generators/create-get-schema-action.d.ts +1 -1
  79. package/esm/endpoint-generators/create-get-schema-action.d.ts.map +1 -1
  80. package/esm/endpoint-generators/create-get-schema-action.js.map +1 -1
  81. package/esm/endpoint-generators/create-patch-endpoint.d.ts +7 -13
  82. package/esm/endpoint-generators/create-patch-endpoint.d.ts.map +1 -1
  83. package/esm/endpoint-generators/create-patch-endpoint.js +8 -13
  84. package/esm/endpoint-generators/create-patch-endpoint.js.map +1 -1
  85. package/esm/endpoint-generators/create-patch-endpoint.spec.js +10 -17
  86. package/esm/endpoint-generators/create-patch-endpoint.spec.js.map +1 -1
  87. package/esm/endpoint-generators/create-post-endpoint.d.ts +7 -13
  88. package/esm/endpoint-generators/create-post-endpoint.d.ts.map +1 -1
  89. package/esm/endpoint-generators/create-post-endpoint.js +8 -13
  90. package/esm/endpoint-generators/create-post-endpoint.js.map +1 -1
  91. package/esm/endpoint-generators/create-post-endpoint.spec.js +41 -14
  92. package/esm/endpoint-generators/create-post-endpoint.spec.js.map +1 -1
  93. package/esm/endpoint-generators/utils.d.ts +17 -2
  94. package/esm/endpoint-generators/utils.d.ts.map +1 -1
  95. package/esm/endpoint-generators/utils.js +33 -20
  96. package/esm/endpoint-generators/utils.js.map +1 -1
  97. package/esm/get-schema-from-api.d.ts +1 -1
  98. package/esm/get-schema-from-api.d.ts.map +1 -1
  99. package/esm/get-schema-from-api.js +1 -0
  100. package/esm/get-schema-from-api.js.map +1 -1
  101. package/esm/get-schema-from-api.spec.js.map +1 -1
  102. package/esm/helpers.d.ts +54 -88
  103. package/esm/helpers.d.ts.map +1 -1
  104. package/esm/helpers.js +90 -92
  105. package/esm/helpers.js.map +1 -1
  106. package/esm/helpers.spec.d.ts +2 -1
  107. package/esm/helpers.spec.d.ts.map +1 -1
  108. package/esm/helpers.spec.js +41 -38
  109. package/esm/helpers.spec.js.map +1 -1
  110. package/esm/http-authentication-settings.d.ts +56 -12
  111. package/esm/http-authentication-settings.d.ts.map +1 -1
  112. package/esm/http-authentication-settings.js +27 -29
  113. package/esm/http-authentication-settings.js.map +1 -1
  114. package/esm/http-server-pool.d.ts +64 -0
  115. package/esm/http-server-pool.d.ts.map +1 -0
  116. package/esm/http-server-pool.js +95 -0
  117. package/esm/http-server-pool.js.map +1 -0
  118. package/esm/http-server-pool.spec.d.ts +2 -0
  119. package/esm/http-server-pool.spec.d.ts.map +1 -0
  120. package/esm/http-server-pool.spec.js +193 -0
  121. package/esm/http-server-pool.spec.js.map +1 -0
  122. package/esm/http-user-context.d.ts +34 -44
  123. package/esm/http-user-context.d.ts.map +1 -1
  124. package/esm/http-user-context.js +75 -95
  125. package/esm/http-user-context.js.map +1 -1
  126. package/esm/http-user-context.spec.d.ts +8 -2
  127. package/esm/http-user-context.spec.d.ts.map +1 -1
  128. package/esm/http-user-context.spec.js +226 -332
  129. package/esm/http-user-context.spec.js.map +1 -1
  130. package/esm/identity-cache-keys.d.ts +28 -0
  131. package/esm/identity-cache-keys.d.ts.map +1 -0
  132. package/esm/identity-cache-keys.js +13 -0
  133. package/esm/identity-cache-keys.js.map +1 -0
  134. package/esm/identity-cache-keys.spec.d.ts +2 -0
  135. package/esm/identity-cache-keys.spec.d.ts.map +1 -0
  136. package/esm/identity-cache-keys.spec.js +21 -0
  137. package/esm/identity-cache-keys.spec.js.map +1 -0
  138. package/esm/identity-event-bus.d.ts +102 -0
  139. package/esm/identity-event-bus.d.ts.map +1 -0
  140. package/esm/identity-event-bus.js +151 -0
  141. package/esm/identity-event-bus.js.map +1 -0
  142. package/esm/identity-event-bus.spec.d.ts +2 -0
  143. package/esm/identity-event-bus.spec.d.ts.map +1 -0
  144. package/esm/identity-event-bus.spec.js +392 -0
  145. package/esm/identity-event-bus.spec.js.map +1 -0
  146. package/esm/index.d.ts +9 -4
  147. package/esm/index.d.ts.map +1 -1
  148. package/esm/index.js +9 -4
  149. package/esm/index.js.map +1 -1
  150. package/esm/login-response-strategy.d.ts +11 -10
  151. package/esm/login-response-strategy.d.ts.map +1 -1
  152. package/esm/login-response-strategy.js +7 -6
  153. package/esm/login-response-strategy.js.map +1 -1
  154. package/esm/login-response-strategy.spec.js +25 -29
  155. package/esm/login-response-strategy.spec.js.map +1 -1
  156. package/esm/openapi/generate-openapi-document.spec.js.map +1 -1
  157. package/esm/proxy-manager.spec.js +152 -176
  158. package/esm/proxy-manager.spec.js.map +1 -1
  159. package/esm/proxy-runtime.d.ts +27 -0
  160. package/esm/proxy-runtime.d.ts.map +1 -0
  161. package/esm/proxy-runtime.js +45 -0
  162. package/esm/proxy-runtime.js.map +1 -0
  163. package/esm/proxy-runtime.spec.d.ts +2 -0
  164. package/esm/proxy-runtime.spec.d.ts.map +1 -0
  165. package/esm/proxy-runtime.spec.js +39 -0
  166. package/esm/proxy-runtime.spec.js.map +1 -0
  167. package/esm/request-action-implementation.d.ts +25 -0
  168. package/esm/request-action-implementation.d.ts.map +1 -1
  169. package/esm/request-action-implementation.js +9 -0
  170. package/esm/request-action-implementation.js.map +1 -1
  171. package/esm/rest-api-runtime.d.ts +76 -0
  172. package/esm/rest-api-runtime.d.ts.map +1 -0
  173. package/esm/rest-api-runtime.js +131 -0
  174. package/esm/rest-api-runtime.js.map +1 -0
  175. package/esm/rest-api-runtime.spec.d.ts +2 -0
  176. package/esm/rest-api-runtime.spec.d.ts.map +1 -0
  177. package/esm/rest-api-runtime.spec.js +58 -0
  178. package/esm/rest-api-runtime.spec.js.map +1 -0
  179. package/esm/rest-service.integration.spec.d.ts +1 -1
  180. package/esm/rest-service.integration.spec.d.ts.map +1 -1
  181. package/esm/rest-service.integration.spec.js +57 -80
  182. package/esm/rest-service.integration.spec.js.map +1 -1
  183. package/esm/rest.integration.test.js +26 -84
  184. package/esm/rest.integration.test.js.map +1 -1
  185. package/esm/server-telemetry.d.ts +49 -0
  186. package/esm/server-telemetry.d.ts.map +1 -0
  187. package/esm/server-telemetry.js +25 -0
  188. package/esm/server-telemetry.js.map +1 -0
  189. package/esm/server-telemetry.spec.d.ts +2 -0
  190. package/esm/server-telemetry.spec.d.ts.map +1 -0
  191. package/esm/server-telemetry.spec.js +52 -0
  192. package/esm/server-telemetry.spec.js.map +1 -0
  193. package/esm/static-server-manager.spec.js +60 -125
  194. package/esm/static-server-manager.spec.js.map +1 -1
  195. package/esm/static-site-runtime.d.ts +21 -0
  196. package/esm/static-site-runtime.d.ts.map +1 -0
  197. package/esm/static-site-runtime.js +48 -0
  198. package/esm/static-site-runtime.js.map +1 -0
  199. package/esm/static-site-runtime.spec.d.ts +2 -0
  200. package/esm/static-site-runtime.spec.d.ts.map +1 -0
  201. package/esm/static-site-runtime.spec.js +30 -0
  202. package/esm/static-site-runtime.spec.js.map +1 -0
  203. package/esm/user-resolution-cache.d.ts +65 -0
  204. package/esm/user-resolution-cache.d.ts.map +1 -0
  205. package/esm/user-resolution-cache.js +101 -0
  206. package/esm/user-resolution-cache.js.map +1 -0
  207. package/esm/user-resolution-cache.spec.d.ts +2 -0
  208. package/esm/user-resolution-cache.spec.d.ts.map +1 -0
  209. package/esm/user-resolution-cache.spec.js +220 -0
  210. package/esm/user-resolution-cache.spec.js.map +1 -0
  211. package/esm/user-store.d.ts +39 -0
  212. package/esm/user-store.d.ts.map +1 -0
  213. package/esm/user-store.js +62 -0
  214. package/esm/user-store.js.map +1 -0
  215. package/esm/user-store.spec.d.ts +2 -0
  216. package/esm/user-store.spec.d.ts.map +1 -0
  217. package/esm/user-store.spec.js +72 -0
  218. package/esm/user-store.spec.js.map +1 -0
  219. package/esm/validate.d.ts +13 -12
  220. package/esm/validate.d.ts.map +1 -1
  221. package/esm/validate.integration.spec.js +27 -28
  222. package/esm/validate.integration.spec.js.map +1 -1
  223. package/esm/validate.js +12 -0
  224. package/esm/validate.js.map +1 -1
  225. package/package.json +14 -12
  226. package/src/actions/error-action.ts +7 -3
  227. package/src/actions/get-current-user.spec.ts +11 -10
  228. package/src/actions/get-current-user.ts +1 -4
  229. package/src/actions/is-authenticated.spec.ts +12 -7
  230. package/src/actions/is-authenticated.ts +3 -4
  231. package/src/actions/logout-action.spec.ts +12 -12
  232. package/src/actions/logout.ts +3 -7
  233. package/src/actions/not-found-action.ts +1 -3
  234. package/src/actions/password-login-action.spec.ts +30 -30
  235. package/src/actions/password-login-action.ts +6 -13
  236. package/src/authenticate.spec.ts +45 -51
  237. package/src/authenticate.ts +6 -12
  238. package/src/authentication-providers/authentication-provider.ts +17 -0
  239. package/src/authentication-providers/basic-auth-provider.ts +7 -0
  240. package/src/authentication-providers/cookie-auth-provider.ts +8 -0
  241. package/src/authorize.spec.ts +37 -39
  242. package/src/authorize.ts +6 -17
  243. package/src/endpoint-generators/create-delete-endpoint.spec.ts +16 -24
  244. package/src/endpoint-generators/create-delete-endpoint.ts +11 -17
  245. package/src/endpoint-generators/create-get-collection-endpoint.spec.ts +45 -78
  246. package/src/endpoint-generators/create-get-collection-endpoint.ts +17 -19
  247. package/src/endpoint-generators/create-get-entity-endpoint.spec.ts +17 -33
  248. package/src/endpoint-generators/create-get-entity-endpoint.ts +11 -17
  249. package/src/endpoint-generators/create-get-openapi-document-action.ts +2 -2
  250. package/src/endpoint-generators/create-get-schema-action.ts +1 -1
  251. package/src/endpoint-generators/create-patch-endpoint.spec.ts +11 -18
  252. package/src/endpoint-generators/create-patch-endpoint.ts +12 -23
  253. package/src/endpoint-generators/create-post-endpoint.spec.ts +45 -14
  254. package/src/endpoint-generators/create-post-endpoint.ts +13 -23
  255. package/src/endpoint-generators/utils.ts +36 -28
  256. package/src/get-schema-from-api.spec.ts +1 -1
  257. package/src/get-schema-from-api.ts +2 -1
  258. package/src/helpers.spec.ts +51 -41
  259. package/src/helpers.ts +140 -109
  260. package/src/http-authentication-settings.ts +78 -24
  261. package/src/http-server-pool.spec.ts +220 -0
  262. package/src/http-server-pool.ts +169 -0
  263. package/src/http-user-context.spec.ts +246 -378
  264. package/src/http-user-context.ts +122 -92
  265. package/src/identity-cache-keys.spec.ts +24 -0
  266. package/src/identity-cache-keys.ts +31 -0
  267. package/src/identity-event-bus.spec.ts +285 -0
  268. package/src/identity-event-bus.ts +230 -0
  269. package/src/index.ts +9 -4
  270. package/src/login-response-strategy.spec.ts +32 -31
  271. package/src/login-response-strategy.ts +14 -12
  272. package/src/openapi/generate-openapi-document.spec.ts +2 -2
  273. package/src/proxy-manager.spec.ts +158 -188
  274. package/src/proxy-runtime.spec.ts +50 -0
  275. package/src/proxy-runtime.ts +75 -0
  276. package/src/request-action-implementation.ts +25 -0
  277. package/src/rest-api-runtime.spec.ts +77 -0
  278. package/src/rest-api-runtime.ts +204 -0
  279. package/src/rest-service.integration.spec.ts +65 -85
  280. package/src/rest.integration.test.ts +27 -87
  281. package/src/server-telemetry.spec.ts +56 -0
  282. package/src/server-telemetry.ts +43 -0
  283. package/src/static-server-manager.spec.ts +63 -151
  284. package/src/static-site-runtime.spec.ts +39 -0
  285. package/src/static-site-runtime.ts +75 -0
  286. package/src/user-resolution-cache.spec.ts +241 -0
  287. package/src/user-resolution-cache.ts +164 -0
  288. package/src/user-store.spec.ts +86 -0
  289. package/src/user-store.ts +69 -0
  290. package/src/validate.integration.spec.ts +40 -35
  291. package/src/validate.ts +14 -12
  292. package/esm/api-manager.d.ts +0 -104
  293. package/esm/api-manager.d.ts.map +0 -1
  294. package/esm/api-manager.js +0 -179
  295. package/esm/api-manager.js.map +0 -1
  296. package/esm/proxy-manager.d.ts +0 -53
  297. package/esm/proxy-manager.d.ts.map +0 -1
  298. package/esm/proxy-manager.js +0 -84
  299. package/esm/proxy-manager.js.map +0 -1
  300. package/esm/server-manager.d.ts +0 -48
  301. package/esm/server-manager.d.ts.map +0 -1
  302. package/esm/server-manager.js +0 -100
  303. package/esm/server-manager.js.map +0 -1
  304. package/esm/static-server-manager.d.ts +0 -19
  305. package/esm/static-server-manager.d.ts.map +0 -1
  306. package/esm/static-server-manager.js +0 -65
  307. package/esm/static-server-manager.js.map +0 -1
  308. package/src/api-manager.ts +0 -304
  309. package/src/proxy-manager.ts +0 -102
  310. package/src/server-manager.ts +0 -135
  311. package/src/static-server-manager.ts +0 -90
@@ -1,40 +1,32 @@
1
- import { usingAsync } from '@furystack/utils'
2
- import { Injector } from '@furystack/inject'
1
+ import { getPort } from '@furystack/core/port-generator'
2
+ import { createInjector } from '@furystack/inject'
3
3
  import type { DeleteEndpoint } from '@furystack/rest'
4
- import { createDeleteEndpoint } from './create-delete-endpoint.js'
5
- import { MockClass, setupContext } from './utils.js'
4
+ import { usingAsync } from '@furystack/utils'
5
+ import { describe, expect, it } from 'vitest'
6
6
  import { useRestService } from '../helpers.js'
7
- import { getDataSetFor } from '@furystack/repository'
8
- import { describe, it, expect } from 'vitest'
9
- import { getPort } from '@furystack/core/port-generator'
7
+ import { createDeleteEndpoint } from './create-delete-endpoint.js'
8
+ import type { MockClass } from './utils.js'
9
+ import { MockDataSet, setupContext } from './utils.js'
10
10
 
11
11
  describe('createDeleteEndpoint', () => {
12
- it('Should delete the entity and report the success', async () => {
13
- await usingAsync(new Injector(), async (i) => {
14
- const port = getPort()
12
+ it('deletes the entity and responds 204', async () => {
13
+ await usingAsync(createInjector(), async (i) => {
15
14
  setupContext(i)
15
+ const port = getPort()
16
16
  await useRestService<{ DELETE: { '/:id': DeleteEndpoint<MockClass, 'id'> } }>({
17
17
  injector: i,
18
18
  root: '/api',
19
19
  port,
20
- api: {
21
- DELETE: {
22
- '/:id': createDeleteEndpoint({ model: MockClass, primaryKey: 'id' }),
23
- },
24
- },
20
+ api: { DELETE: { '/:id': createDeleteEndpoint(MockDataSet) } },
25
21
  })
26
- await getDataSetFor(i, MockClass, 'id').add(i, { id: 'mock', value: 'mock' })
27
-
28
- const countBeforeDelete = await getDataSetFor(i, MockClass, 'id').count(i)
29
- expect(countBeforeDelete).toBe(1)
22
+ const dataSet = i.get(MockDataSet)
23
+ await dataSet.add(i, { id: 'mock', value: 'mock' })
24
+ expect(await dataSet.count(i)).toBe(1)
30
25
 
31
26
  const response = await fetch(`http://127.0.0.1:${port}/api/mock`, { method: 'DELETE' })
32
27
  expect(response.status).toBe(204)
33
- const txt = await response.text()
34
- expect(txt).toBe('')
35
-
36
- const countAfterDelete = await getDataSetFor(i, MockClass, 'id').count(i)
37
- expect(countAfterDelete).toBe(0)
28
+ expect(await response.text()).toBe('')
29
+ expect(await dataSet.count(i)).toBe(0)
38
30
  })
39
31
  })
40
32
  })
@@ -1,25 +1,19 @@
1
- import type { Constructable } from '@furystack/inject'
1
+ import type { DataSetToken } from '@furystack/repository'
2
2
  import type { DeleteEndpoint } from '@furystack/rest'
3
- import type { RequestAction } from '../request-action-implementation.js'
4
- import { JsonResult } from '../request-action-implementation.js'
5
- import { getRepository } from '@furystack/repository'
3
+ import { JsonResult, type RequestAction } from '../request-action-implementation.js'
6
4
 
7
5
  /**
8
- * Creates a DELETE endpoint for removing entities
9
- * @param options The options for endpoint creation
10
- * @param options.model The Model class
11
- * @param options.primaryKey The field used as primary key on the model
12
- * @returns a boolean that indicates the success
6
+ * Creates a DELETE endpoint that removes the entity identified by the URL's
7
+ * `:id` parameter. Returns `204 No Content` once the removal has been
8
+ * persisted to the underlying store.
13
9
  */
14
- export const createDeleteEndpoint = <T extends object, TPrimaryKey extends keyof T>(options: {
15
- model: Constructable<T>
16
- primaryKey: TPrimaryKey
17
- }) => {
18
- const endpoint: RequestAction<DeleteEndpoint<T, TPrimaryKey>> = async ({ injector, getUrlParams }) => {
10
+ export const createDeleteEndpoint = <T extends object, TPrimaryKey extends keyof T>(
11
+ dataSet: DataSetToken<T, TPrimaryKey>,
12
+ ): RequestAction<DeleteEndpoint<T, TPrimaryKey>> => {
13
+ return async ({ injector, getUrlParams }) => {
19
14
  const { id } = getUrlParams()
20
- const dataSet = getRepository(injector).getDataSetFor(options.model, options.primaryKey)
21
- await dataSet.remove(injector, id)
15
+ const ds = injector.get(dataSet)
16
+ await ds.remove(injector, id)
22
17
  return JsonResult({}, 204)
23
18
  }
24
- return endpoint
25
19
  }
@@ -1,18 +1,18 @@
1
- import { usingAsync } from '@furystack/utils'
2
- import { Injector } from '@furystack/inject'
3
- import { MockClass, setupContext } from './utils.js'
4
- import { createGetCollectionEndpoint } from './create-get-collection-endpoint.js'
1
+ import type { FindOptions } from '@furystack/core'
2
+ import { getPort } from '@furystack/core/port-generator'
3
+ import { createInjector, type Injector } from '@furystack/inject'
5
4
  import type { GetCollectionEndpoint, GetCollectionResult } from '@furystack/rest'
6
5
  import { serializeToQueryString } from '@furystack/rest'
7
- import type { FindOptions } from '@furystack/core'
8
- import { getDataSetFor, getRepository } from '@furystack/repository'
6
+ import { usingAsync } from '@furystack/utils'
7
+ import { describe, expect, it } from 'vitest'
9
8
  import { useRestService } from '../helpers.js'
10
- import { describe, it, expect } from 'vitest'
11
- import { getPort } from '@furystack/core/port-generator'
9
+ import { createGetCollectionEndpoint } from './create-get-collection-endpoint.js'
10
+ import type { MockClass } from './utils.js'
11
+ import { MockDataSet, setupContext } from './utils.js'
12
12
 
13
13
  const addMockEntities = async (i: Injector) =>
14
- await getRepository(i)
15
- .getDataSetFor(MockClass, 'id')
14
+ i
15
+ .get(MockDataSet)
16
16
  .add(
17
17
  i,
18
18
  { id: 'mock1', value: '4' },
@@ -22,24 +22,21 @@ const addMockEntities = async (i: Injector) =>
22
22
  )
23
23
 
24
24
  describe('createGetCollectionEndpoint', () => {
25
- it('Should return the collection without filter / order', async () => {
26
- await usingAsync(new Injector(), async (i) => {
25
+ it('returns the collection without filter / order', async () => {
26
+ await usingAsync(createInjector(), async (i) => {
27
27
  setupContext(i)
28
28
  const port = getPort()
29
29
  await useRestService<{ GET: { '/entities': GetCollectionEndpoint<MockClass> } }>({
30
30
  injector: i,
31
31
  root: '/api',
32
32
  port,
33
- api: {
34
- GET: {
35
- '/entities': createGetCollectionEndpoint({ model: MockClass, primaryKey: 'id' }),
36
- },
37
- },
33
+ api: { GET: { '/entities': createGetCollectionEndpoint(MockDataSet) } },
38
34
  })
39
35
  await addMockEntities(i)
40
36
 
41
- const count = await getDataSetFor(i, MockClass, 'id').count(i)
42
- const allEntities = await getDataSetFor(i, MockClass, 'id').find(i, {})
37
+ const dataSet = i.get(MockDataSet)
38
+ const count = await dataSet.count(i)
39
+ const allEntities = await dataSet.find(i, {})
43
40
 
44
41
  const response = await fetch(`http://127.0.0.1:${port}/api/entities`, { method: 'GET' })
45
42
  expect(response.ok).toBe(true)
@@ -50,24 +47,21 @@ describe('createGetCollectionEndpoint', () => {
50
47
  })
51
48
  })
52
49
 
53
- it('Should return entities in order', async () => {
54
- await usingAsync(new Injector(), async (i) => {
50
+ it('returns entities in the requested order', async () => {
51
+ await usingAsync(createInjector(), async (i) => {
55
52
  setupContext(i)
56
53
  const port = getPort()
57
54
  await useRestService<{ GET: { '/entities': GetCollectionEndpoint<MockClass> } }>({
58
55
  injector: i,
59
56
  root: '/api',
60
57
  port,
61
- api: {
62
- GET: {
63
- '/entities': createGetCollectionEndpoint({ model: MockClass, primaryKey: 'id' }),
64
- },
65
- },
58
+ api: { GET: { '/entities': createGetCollectionEndpoint(MockDataSet) } },
66
59
  })
67
60
  await addMockEntities(i)
68
61
  const findOptions: FindOptions<MockClass, Array<keyof MockClass>> = { order: { value: 'ASC' } }
69
- const count = await getDataSetFor(i, MockClass, 'id').count(i, findOptions.filter)
70
- const orderedEntities = await getDataSetFor(i, MockClass, 'id').find(i, findOptions)
62
+ const dataSet = i.get(MockDataSet)
63
+ const count = await dataSet.count(i, findOptions.filter)
64
+ const orderedEntities = await dataSet.find(i, findOptions)
71
65
  const response = await fetch(`http://127.0.0.1:${port}/api/entities?${serializeToQueryString({ findOptions })}`, {
72
66
  method: 'GET',
73
67
  })
@@ -79,29 +73,21 @@ describe('createGetCollectionEndpoint', () => {
79
73
  })
80
74
  })
81
75
 
82
- it('Should return entities with filtering', async () => {
83
- await usingAsync(new Injector(), async (i) => {
76
+ it('applies a supplied filter', async () => {
77
+ await usingAsync(createInjector(), async (i) => {
84
78
  setupContext(i)
85
79
  const port = getPort()
86
80
  await useRestService<{ GET: { '/entities': GetCollectionEndpoint<MockClass> } }>({
87
81
  injector: i,
88
82
  root: '/api',
89
83
  port,
90
- api: {
91
- GET: {
92
- '/entities': createGetCollectionEndpoint({ model: MockClass, primaryKey: 'id' }),
93
- },
94
- },
84
+ api: { GET: { '/entities': createGetCollectionEndpoint(MockDataSet) } },
95
85
  })
96
86
  await addMockEntities(i)
97
- const findOptions: FindOptions<MockClass, Array<keyof MockClass>> = {
98
- filter: { id: { $ne: 'mock2' } },
99
- }
100
-
101
- const count = await getDataSetFor(i, MockClass, 'id').count(i, findOptions.filter)
102
- const filteredEntities = await getDataSetFor(i, MockClass, 'id').find(i, findOptions)
103
-
104
- expect(filteredEntities).not.toContainEqual({ id: 'mock2', value: '3' })
87
+ const findOptions: FindOptions<MockClass, Array<keyof MockClass>> = { filter: { id: { $ne: 'mock2' } } }
88
+ const dataSet = i.get(MockDataSet)
89
+ const count = await dataSet.count(i, findOptions.filter)
90
+ const filteredEntities = await dataSet.find(i, findOptions)
105
91
 
106
92
  const response = await fetch(`http://127.0.0.1:${port}/api/entities?${serializeToQueryString({ findOptions })}`, {
107
93
  method: 'GET',
@@ -114,67 +100,48 @@ describe('createGetCollectionEndpoint', () => {
114
100
  })
115
101
  })
116
102
 
117
- it('Should return entities with selecting specific fields', async () => {
118
- await usingAsync(new Injector(), async (i) => {
103
+ it('applies select projection', async () => {
104
+ await usingAsync(createInjector(), async (i) => {
119
105
  setupContext(i)
120
106
  const port = getPort()
121
107
  await useRestService<{ GET: { '/entities': GetCollectionEndpoint<MockClass> } }>({
122
108
  injector: i,
123
109
  root: '/api',
124
110
  port,
125
- api: {
126
- GET: {
127
- '/entities': createGetCollectionEndpoint({ model: MockClass, primaryKey: 'id' }),
128
- },
129
- },
111
+ api: { GET: { '/entities': createGetCollectionEndpoint(MockDataSet) } },
130
112
  })
131
113
  await addMockEntities(i)
132
- const findOptions: FindOptions<MockClass, Array<keyof MockClass>> = {
133
- select: ['id'],
134
- }
135
-
136
- const count = await getDataSetFor(i, MockClass, 'id').count(i, findOptions.filter)
137
- const selectedEntities = await getDataSetFor(i, MockClass, 'id').find(i, findOptions)
138
-
139
- selectedEntities.forEach((e) => expect(e.value).toBeUndefined())
114
+ const findOptions: FindOptions<MockClass, Array<keyof MockClass>> = { select: ['id'] }
115
+ const dataSet = i.get(MockDataSet)
116
+ const count = await dataSet.count(i, findOptions.filter)
117
+ const selected = await dataSet.find(i, findOptions)
140
118
 
141
119
  const response = await fetch(`http://127.0.0.1:${port}/api/entities?${serializeToQueryString({ findOptions })}`, {
142
120
  method: 'GET',
143
121
  })
144
-
145
122
  expect(response.ok).toBe(true)
146
123
  const json = (await response.json()) as GetCollectionResult<MockClass>
147
124
  expect(response.status).toBe(200)
148
125
  expect(json.count).toBe(count)
149
- expect(json.entries).toEqual(selectedEntities)
126
+ expect(json.entries).toEqual(selected)
150
127
  })
151
128
  })
152
129
 
153
- it('Should return entities with top/skip', async () => {
154
- await usingAsync(new Injector(), async (i) => {
130
+ it('honours top/skip', async () => {
131
+ await usingAsync(createInjector(), async (i) => {
155
132
  setupContext(i)
156
133
  const port = getPort()
157
134
  await useRestService<{ GET: { '/entities': GetCollectionEndpoint<MockClass> } }>({
158
135
  injector: i,
159
136
  root: '/api',
160
137
  port,
161
- api: {
162
- GET: {
163
- '/entities': createGetCollectionEndpoint({ model: MockClass, primaryKey: 'id' }),
164
- },
165
- },
138
+ api: { GET: { '/entities': createGetCollectionEndpoint(MockDataSet) } },
166
139
  })
167
140
  await addMockEntities(i)
168
- const findOptions: FindOptions<MockClass, Array<keyof MockClass>> = {
169
- skip: 1,
170
- top: 2,
171
- }
172
-
173
- const count = await getDataSetFor(i, MockClass, 'id').count(i, findOptions.filter)
174
- const topSkipEntities = await getDataSetFor(i, MockClass, 'id').find(i, findOptions)
175
-
176
- expect(topSkipEntities).not.toContainEqual({ id: 'mock1', value: '4' })
177
- expect(topSkipEntities).not.toContainEqual({ id: 'mock4', value: '1' })
141
+ const findOptions: FindOptions<MockClass, Array<keyof MockClass>> = { skip: 1, top: 2 }
142
+ const dataSet = i.get(MockDataSet)
143
+ const count = await dataSet.count(i, findOptions.filter)
144
+ const page = await dataSet.find(i, findOptions)
178
145
 
179
146
  const response = await fetch(`http://127.0.0.1:${port}/api/entities?${serializeToQueryString({ findOptions })}`, {
180
147
  method: 'GET',
@@ -182,7 +149,7 @@ describe('createGetCollectionEndpoint', () => {
182
149
  expect(response.status).toBe(200)
183
150
  const json = (await response.json()) as GetCollectionResult<MockClass>
184
151
  expect(json.count).toBe(count)
185
- expect(json.entries).toEqual(topSkipEntities)
152
+ expect(json.entries).toEqual(page)
186
153
  })
187
154
  })
188
155
  })
@@ -1,28 +1,26 @@
1
- import type { Constructable } from '@furystack/inject'
1
+ import type { DataSetToken } from '@furystack/repository'
2
2
  import type { GetCollectionEndpoint } from '@furystack/rest'
3
- import type { RequestAction } from '../request-action-implementation.js'
4
- import { JsonResult } from '../request-action-implementation.js'
5
- import { getRepository } from '@furystack/repository'
3
+ import { JsonResult, type RequestAction } from '../request-action-implementation.js'
6
4
 
7
5
  /**
8
- * Creates a GetCollection endpoint for the given model. The model should have a Repository DataSet
9
- * @param options The options for endpoint creation
10
- * @param options.model The Model class
11
- * @param options.primaryKey The field used as primary key on the model
12
- * @returns The created endpoint
6
+ * Creates a GetCollection endpoint backed by the DataSet resolved from the
7
+ * provided {@link DataSetToken}. The token carries the entity model and
8
+ * primary key, so callers no longer need to pass them separately.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * const endpoint = createGetCollectionEndpoint(UserDataSet)
13
+ * ```
13
14
  */
14
- export const createGetCollectionEndpoint = <T, TPrimaryKey extends keyof T>(options: {
15
- model: Constructable<T>
16
- primaryKey: TPrimaryKey
17
- }) => {
18
- const endpoint: RequestAction<GetCollectionEndpoint<T>> = async ({ injector, getQuery }) => {
15
+ export const createGetCollectionEndpoint = <T, TPrimaryKey extends keyof T>(
16
+ dataSet: DataSetToken<T, TPrimaryKey>,
17
+ ): RequestAction<GetCollectionEndpoint<T>> => {
18
+ return async ({ injector, getQuery }) => {
19
19
  const { findOptions } = getQuery()
20
- const dataSet = getRepository(injector).getDataSetFor(options.model, options.primaryKey)
21
- const entriesPromise = dataSet.find(injector, findOptions || {})
22
- const countPromise = dataSet.count(injector, findOptions?.filter)
20
+ const ds = injector.get(dataSet)
21
+ const entriesPromise = ds.find(injector, findOptions || {})
22
+ const countPromise = ds.count(injector, findOptions?.filter)
23
23
  const [entries, count] = await Promise.all([entriesPromise, countPromise])
24
-
25
24
  return JsonResult({ entries, count })
26
25
  }
27
- return endpoint
28
26
  }
@@ -1,83 +1,67 @@
1
1
  import { getPort } from '@furystack/core/port-generator'
2
- import { Injector } from '@furystack/inject'
3
- import { getDataSetFor } from '@furystack/repository'
2
+ import { createInjector } from '@furystack/inject'
4
3
  import type { GetEntityEndpoint } from '@furystack/rest'
5
4
  import { serializeToQueryString } from '@furystack/rest'
6
5
  import { usingAsync } from '@furystack/utils'
7
6
  import { describe, expect, it } from 'vitest'
8
7
  import { useRestService } from '../helpers.js'
9
8
  import { createGetEntityEndpoint } from './create-get-entity-endpoint.js'
10
- import { MockClass, setupContext } from './utils.js'
9
+ import type { MockClass } from './utils.js'
10
+ import { MockDataSet, setupContext } from './utils.js'
11
11
 
12
12
  describe('createGetEntityEndpoint', () => {
13
- it('Should return the entity', async () => {
14
- await usingAsync(new Injector(), async (i) => {
13
+ it('returns the entity by id', async () => {
14
+ await usingAsync(createInjector(), async (i) => {
15
15
  setupContext(i)
16
16
  const port = getPort()
17
17
  await useRestService<{ GET: { '/:id': GetEntityEndpoint<MockClass, 'id'> } }>({
18
18
  injector: i,
19
19
  root: '/api',
20
20
  port,
21
- api: {
22
- GET: {
23
- '/:id': createGetEntityEndpoint({ model: MockClass, primaryKey: 'id' }),
24
- },
25
- },
21
+ api: { GET: { '/:id': createGetEntityEndpoint(MockDataSet) } },
26
22
  })
27
23
  const mockEntity: MockClass = { id: 'mock', value: 'mock' }
28
- await getDataSetFor(i, MockClass, 'id').add(i, mockEntity)
24
+ await i.get(MockDataSet).add(i, mockEntity)
29
25
 
30
26
  const response = await fetch(`http://127.0.0.1:${port}/api/mock`, { method: 'GET' })
31
27
  expect(response.status).toBe(200)
32
- const body = (await response.json()) as MockClass
33
- expect(body).toEqual(mockEntity)
28
+ expect(await response.json()).toEqual(mockEntity)
34
29
  })
35
30
  })
36
31
 
37
- it('Should return the entity with the selected fields', async () => {
38
- await usingAsync(new Injector(), async (i) => {
32
+ it('honours a select query parameter', async () => {
33
+ await usingAsync(createInjector(), async (i) => {
39
34
  setupContext(i)
40
35
  const port = getPort()
41
36
  await useRestService<{ GET: { '/:id': GetEntityEndpoint<MockClass, 'id'> } }>({
42
37
  injector: i,
43
38
  root: '/api',
44
39
  port,
45
- api: {
46
- GET: {
47
- '/:id': createGetEntityEndpoint({ model: MockClass, primaryKey: 'id' }),
48
- },
49
- },
40
+ api: { GET: { '/:id': createGetEntityEndpoint(MockDataSet) } },
50
41
  })
51
- const mockEntity: MockClass = { id: 'mock', value: 'mock' }
52
- await getDataSetFor(i, MockClass, 'id').add(i, mockEntity)
42
+ await i.get(MockDataSet).add(i, { id: 'mock', value: 'mock' })
53
43
 
54
44
  const response = await fetch(`http://127.0.0.1:${port}/api/mock?${serializeToQueryString({ select: ['id'] })}`, {
55
45
  method: 'GET',
56
46
  })
57
47
  expect(response.status).toBe(200)
58
- const body = (await response.json()) as { id: string }
59
- expect(body).toEqual({ id: mockEntity.id })
48
+ expect(await response.json()).toEqual({ id: 'mock' })
60
49
  })
61
50
  })
62
51
 
63
- it('Should return 404 if no entity has been found', async () => {
64
- await usingAsync(new Injector(), async (i) => {
52
+ it('responds with 404 when the entity does not exist', async () => {
53
+ await usingAsync(createInjector(), async (i) => {
65
54
  setupContext(i)
66
55
  const port = getPort()
67
56
  await useRestService<{ GET: { '/:id': GetEntityEndpoint<MockClass, 'id'> } }>({
68
57
  injector: i,
69
58
  root: '/api',
70
59
  port,
71
- api: {
72
- GET: {
73
- '/:id': createGetEntityEndpoint({ model: MockClass, primaryKey: 'id' }),
74
- },
75
- },
60
+ api: { GET: { '/:id': createGetEntityEndpoint(MockDataSet) } },
76
61
  })
77
62
  const result = await fetch(`http://127.0.0.1:${port}/api/mock`, { method: 'GET' })
78
63
  expect(result.status).toBe(404)
79
- const body = (await result.json()) as { message: string }
80
- expect(body).toEqual({ message: 'Entity not found' })
64
+ expect(await result.json()).toEqual({ message: 'Entity not found' })
81
65
  })
82
66
  })
83
67
  })
@@ -1,30 +1,24 @@
1
- import type { Constructable } from '@furystack/inject'
1
+ import type { DataSetToken } from '@furystack/repository'
2
2
  import type { GetEntityEndpoint } from '@furystack/rest'
3
3
  import { RequestError } from '@furystack/rest'
4
- import type { RequestAction } from '../request-action-implementation.js'
5
- import { JsonResult } from '../request-action-implementation.js'
6
- import { getRepository } from '@furystack/repository'
4
+ import { JsonResult, type RequestAction } from '../request-action-implementation.js'
7
5
 
8
6
  /**
9
- * Creates a simple Get Entity endpoint for a specified model.
10
- * @param options The options for endpoint creation
11
- * @param options.model The entity model, should have a Repository DataSet
12
- * @param options.primaryKey The field name used as primary key on the model
13
- * @returns The generated endpoint
7
+ * Creates a GET entity endpoint backed by the DataSet resolved from the
8
+ * provided {@link DataSetToken}. Throws a 404 `RequestError` when the
9
+ * entity cannot be found.
14
10
  */
15
- export const createGetEntityEndpoint = <T extends object, TPrimaryKey extends keyof T>(options: {
16
- model: Constructable<T>
17
- primaryKey: TPrimaryKey
18
- }) => {
19
- const endpoint: RequestAction<GetEntityEndpoint<T, TPrimaryKey>> = async ({ injector, getUrlParams, getQuery }) => {
11
+ export const createGetEntityEndpoint = <T extends object, TPrimaryKey extends keyof T>(
12
+ dataSet: DataSetToken<T, TPrimaryKey>,
13
+ ): RequestAction<GetEntityEndpoint<T, TPrimaryKey>> => {
14
+ return async ({ injector, getUrlParams, getQuery }) => {
20
15
  const { id } = getUrlParams()
21
16
  const { select } = getQuery()
22
- const dataSet = getRepository(injector).getDataSetFor(options.model, options.primaryKey)
23
- const entry = await dataSet.get(injector, id, select)
17
+ const ds = injector.get(dataSet)
18
+ const entry = await ds.get(injector, id, select)
24
19
  if (!entry) {
25
20
  throw new RequestError('Entity not found', 404)
26
21
  }
27
22
  return JsonResult(entry)
28
23
  }
29
- return endpoint
30
24
  }
@@ -1,16 +1,16 @@
1
1
  import type { Injector } from '@furystack/inject'
2
2
  import type { ApiDocumentMetadata, OpenApiDocument, RestApi, SecuritySchemeObject } from '@furystack/rest'
3
3
 
4
- import type { RestApiImplementation } from '../api-manager.js'
5
4
  import { getSchemaFromApi } from '../get-schema-from-api.js'
6
5
  import { HttpAuthenticationSettings } from '../http-authentication-settings.js'
7
6
  import { mapProvidersToSecuritySchemes } from '../openapi/auth-provider-to-security-scheme.js'
8
7
  import { generateOpenApiDocument } from '../openapi/generate-openapi-document.js'
9
8
  import { JsonResult, type RequestAction } from '../request-action-implementation.js'
9
+ import type { RestApiImplementation } from '../rest-api-runtime.js'
10
10
 
11
11
  const getSecuritySchemesFromInjector = (injector: Injector): Record<string, SecuritySchemeObject> | undefined => {
12
12
  try {
13
- const settings = injector.getInstance(HttpAuthenticationSettings)
13
+ const settings = injector.get(HttpAuthenticationSettings)
14
14
  if (settings.authenticationProviders.length > 0) {
15
15
  return mapProvidersToSecuritySchemes(settings.authenticationProviders)
16
16
  }
@@ -1,7 +1,7 @@
1
1
  import { RequestError } from '@furystack/rest'
2
- import type { RestApiImplementation } from '../api-manager.js'
3
2
  import { getSchemaFromApi } from '../get-schema-from-api.js'
4
3
  import { JsonResult, type RequestAction } from '../request-action-implementation.js'
4
+ import type { RestApiImplementation } from '../rest-api-runtime.js'
5
5
 
6
6
  export type GetSchemaResult = ReturnType<typeof getSchemaFromApi>
7
7
 
@@ -1,42 +1,35 @@
1
1
  import { getPort } from '@furystack/core/port-generator'
2
- import { Injector } from '@furystack/inject'
3
- import { getDataSetFor } from '@furystack/repository'
2
+ import { createInjector } from '@furystack/inject'
4
3
  import type { PatchEndpoint } from '@furystack/rest'
5
4
  import { usingAsync } from '@furystack/utils'
6
5
  import { describe, expect, it } from 'vitest'
7
6
  import { useRestService } from '../helpers.js'
8
7
  import { createPatchEndpoint } from './create-patch-endpoint.js'
9
- import { MockClass, setupContext } from './utils.js'
8
+ import type { MockClass } from './utils.js'
9
+ import { MockDataSet, setupContext } from './utils.js'
10
10
 
11
11
  describe('createPatchEndpoint', () => {
12
- it('Should update the entity and report the success', async () => {
13
- await usingAsync(new Injector(), async (i) => {
12
+ it('updates the entity and responds 200', async () => {
13
+ await usingAsync(createInjector(), async (i) => {
14
14
  setupContext(i)
15
15
  const port = getPort()
16
16
  await useRestService<{ PATCH: { '/:id': PatchEndpoint<MockClass, 'id'> } }>({
17
17
  injector: i,
18
18
  root: '/api',
19
19
  port,
20
- api: {
21
- PATCH: {
22
- '/:id': createPatchEndpoint({ model: MockClass, primaryKey: 'id' }),
23
- },
24
- },
20
+ api: { PATCH: { '/:id': createPatchEndpoint(MockDataSet) } },
25
21
  })
26
- await getDataSetFor(i, MockClass, 'id').add(i, { id: 'mock', value: 'mock' })
27
-
28
- const countBeforeDelete = await getDataSetFor(i, MockClass, 'id').count(i)
29
- expect(countBeforeDelete).toBe(1)
22
+ const dataSet = i.get(MockDataSet)
23
+ await dataSet.add(i, { id: 'mock', value: 'mock' })
24
+ expect(await dataSet.count(i)).toBe(1)
30
25
 
31
26
  const response = await fetch(`http://127.0.0.1:${port}/api/mock`, {
32
27
  method: 'PATCH',
33
28
  body: JSON.stringify({ value: 'updated' }),
34
29
  })
35
30
  expect(response.status).toBe(200)
36
- const body = (await response.json()) as { value: string }
37
- expect(body).toEqual({})
38
- const updated = await getDataSetFor(i, MockClass, 'id').get(i, 'mock')
39
- expect(updated?.value).toBe('updated')
31
+ expect(await response.json()).toEqual({})
32
+ expect((await dataSet.get(i, 'mock'))?.value).toBe('updated')
40
33
  })
41
34
  })
42
35
  })
@@ -1,37 +1,26 @@
1
- import type { Constructable } from '@furystack/inject'
2
- import type { PatchEndpoint } from '@furystack/rest'
3
- import '@furystack/repository'
4
- import type { RequestAction } from '../request-action-implementation.js'
5
- import { JsonResult } from '../request-action-implementation.js'
6
- import { getRepository } from '@furystack/repository'
7
1
  import type { WithOptionalId } from '@furystack/core'
2
+ import type { DataSetToken } from '@furystack/repository'
3
+ import type { PatchEndpoint } from '@furystack/rest'
8
4
  import { readPostBody } from '../read-post-body.js'
5
+ import { JsonResult, type RequestAction } from '../request-action-implementation.js'
9
6
 
10
7
  /**
11
- * Creates a PATCH endpoint for updating entities
12
- * @param options The options for endpoint creation
13
- * @param options.model The Model class
14
- * @param options.primaryKey The field name that is used as primary key on the model
15
- * @returns a boolean that indicates the success
8
+ * Creates a PATCH endpoint that applies a partial update to an entity
9
+ * identified by the URL's `:id` parameter. The DataSet is resolved from the
10
+ * supplied {@link DataSetToken}.
16
11
  */
17
12
  export const createPatchEndpoint = <
18
13
  T extends object,
19
14
  TPrimaryKey extends keyof T,
20
15
  TWritableData = WithOptionalId<T, TPrimaryKey>,
21
- >(options: {
22
- model: Constructable<T>
23
- primaryKey: TPrimaryKey
24
- }) => {
25
- const endpoint: RequestAction<PatchEndpoint<T, TPrimaryKey, TWritableData>> = async ({
26
- injector,
27
- request,
28
- getUrlParams,
29
- }) => {
16
+ >(
17
+ dataSet: DataSetToken<T, TPrimaryKey, TWritableData>,
18
+ ): RequestAction<PatchEndpoint<T, TPrimaryKey, TWritableData>> => {
19
+ return async ({ injector, request, getUrlParams }) => {
30
20
  const { id } = getUrlParams()
31
21
  const patchData = await readPostBody<T>(request)
32
- const dataSet = getRepository(injector).getDataSetFor(options.model, options.primaryKey)
33
- await dataSet.update(injector, id, patchData)
22
+ const ds = injector.get(dataSet)
23
+ await ds.update(injector, id, patchData)
34
24
  return JsonResult({})
35
25
  }
36
- return endpoint
37
26
  }