@furystack/rest-service 4.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (285) hide show
  1. package/LICENSE +339 -0
  2. package/README.md +219 -0
  3. package/dist/actions/error-action.d.ts +14 -0
  4. package/dist/actions/error-action.d.ts.map +1 -0
  5. package/dist/actions/error-action.js +29 -0
  6. package/dist/actions/error-action.js.map +1 -0
  7. package/dist/actions/error-action.spec.d.ts +2 -0
  8. package/dist/actions/error-action.spec.d.ts.map +1 -0
  9. package/dist/actions/error-action.spec.js +51 -0
  10. package/dist/actions/error-action.spec.js.map +1 -0
  11. package/dist/actions/get-current-user.d.ts +11 -0
  12. package/dist/actions/get-current-user.d.ts.map +1 -0
  13. package/dist/actions/get-current-user.js +15 -0
  14. package/dist/actions/get-current-user.js.map +1 -0
  15. package/dist/actions/get-current-user.spec.d.ts +2 -0
  16. package/dist/actions/get-current-user.spec.d.ts.map +1 -0
  17. package/dist/actions/get-current-user.spec.js +20 -0
  18. package/dist/actions/get-current-user.spec.js.map +1 -0
  19. package/dist/actions/index.d.ts +7 -0
  20. package/dist/actions/index.d.ts.map +1 -0
  21. package/dist/actions/index.js +10 -0
  22. package/dist/actions/index.js.map +1 -0
  23. package/dist/actions/is-authenticated.d.ts +14 -0
  24. package/dist/actions/is-authenticated.d.ts.map +1 -0
  25. package/dist/actions/is-authenticated.js +17 -0
  26. package/dist/actions/is-authenticated.js.map +1 -0
  27. package/dist/actions/is-authenticated.spec.d.ts +2 -0
  28. package/dist/actions/is-authenticated.spec.d.ts.map +1 -0
  29. package/dist/actions/is-authenticated.spec.js +19 -0
  30. package/dist/actions/is-authenticated.spec.js.map +1 -0
  31. package/dist/actions/login-action.spec.d.ts +2 -0
  32. package/dist/actions/login-action.spec.d.ts.map +1 -0
  33. package/dist/actions/login-action.spec.js +35 -0
  34. package/dist/actions/login-action.spec.js.map +1 -0
  35. package/dist/actions/login.d.ts +16 -0
  36. package/dist/actions/login.d.ts.map +1 -0
  37. package/dist/actions/login.js +26 -0
  38. package/dist/actions/login.js.map +1 -0
  39. package/dist/actions/logout-action.spec.d.ts +2 -0
  40. package/dist/actions/logout-action.spec.d.ts.map +1 -0
  41. package/dist/actions/logout-action.spec.js +23 -0
  42. package/dist/actions/logout-action.spec.js.map +1 -0
  43. package/dist/actions/logout.d.ts +14 -0
  44. package/dist/actions/logout.d.ts.map +1 -0
  45. package/dist/actions/logout.js +20 -0
  46. package/dist/actions/logout.js.map +1 -0
  47. package/dist/actions/not-found-action.d.ts +10 -0
  48. package/dist/actions/not-found-action.d.ts.map +1 -0
  49. package/dist/actions/not-found-action.js +14 -0
  50. package/dist/actions/not-found-action.js.map +1 -0
  51. package/dist/actions/not-found-action.spec.d.ts +2 -0
  52. package/dist/actions/not-found-action.spec.d.ts.map +1 -0
  53. package/dist/actions/not-found-action.spec.js +17 -0
  54. package/dist/actions/not-found-action.spec.js.map +1 -0
  55. package/dist/add-cors-header.spec.d.ts +2 -0
  56. package/dist/add-cors-header.spec.d.ts.map +1 -0
  57. package/dist/add-cors-header.spec.js +99 -0
  58. package/dist/add-cors-header.spec.js.map +1 -0
  59. package/dist/api-manager.d.ts +61 -0
  60. package/dist/api-manager.d.ts.map +1 -0
  61. package/dist/api-manager.js +144 -0
  62. package/dist/api-manager.js.map +1 -0
  63. package/dist/authenticate.d.ts +5 -0
  64. package/dist/authenticate.d.ts.map +1 -0
  65. package/dist/authenticate.js +20 -0
  66. package/dist/authenticate.js.map +1 -0
  67. package/dist/authenticate.spec.d.ts +2 -0
  68. package/dist/authenticate.spec.d.ts.map +1 -0
  69. package/dist/authenticate.spec.js +59 -0
  70. package/dist/authenticate.spec.js.map +1 -0
  71. package/dist/authorize.d.ts +5 -0
  72. package/dist/authorize.d.ts.map +1 -0
  73. package/dist/authorize.js +22 -0
  74. package/dist/authorize.js.map +1 -0
  75. package/dist/authorize.spec.d.ts +2 -0
  76. package/dist/authorize.spec.d.ts.map +1 -0
  77. package/dist/authorize.spec.js +55 -0
  78. package/dist/authorize.spec.js.map +1 -0
  79. package/dist/endpoint-generators/create-delete-endpoint.d.ts +17 -0
  80. package/dist/endpoint-generators/create-delete-endpoint.d.ts.map +1 -0
  81. package/dist/endpoint-generators/create-delete-endpoint.js +24 -0
  82. package/dist/endpoint-generators/create-delete-endpoint.js.map +1 -0
  83. package/dist/endpoint-generators/create-delete-endpoint.spec.d.ts +2 -0
  84. package/dist/endpoint-generators/create-delete-endpoint.spec.d.ts.map +1 -0
  85. package/dist/endpoint-generators/create-delete-endpoint.spec.js +33 -0
  86. package/dist/endpoint-generators/create-delete-endpoint.spec.js.map +1 -0
  87. package/dist/endpoint-generators/create-get-collection-endpoint.d.ts +17 -0
  88. package/dist/endpoint-generators/create-get-collection-endpoint.d.ts.map +1 -0
  89. package/dist/endpoint-generators/create-get-collection-endpoint.js +26 -0
  90. package/dist/endpoint-generators/create-get-collection-endpoint.js.map +1 -0
  91. package/dist/endpoint-generators/create-get-collection-endpoint.spec.d.ts +2 -0
  92. package/dist/endpoint-generators/create-get-collection-endpoint.spec.d.ts.map +1 -0
  93. package/dist/endpoint-generators/create-get-collection-endpoint.spec.js +143 -0
  94. package/dist/endpoint-generators/create-get-collection-endpoint.spec.js.map +1 -0
  95. package/dist/endpoint-generators/create-get-entity-endpoint.d.ts +17 -0
  96. package/dist/endpoint-generators/create-get-entity-endpoint.d.ts.map +1 -0
  97. package/dist/endpoint-generators/create-get-entity-endpoint.js +29 -0
  98. package/dist/endpoint-generators/create-get-entity-endpoint.js.map +1 -0
  99. package/dist/endpoint-generators/create-get-entity-endpoint.spec.d.ts +2 -0
  100. package/dist/endpoint-generators/create-get-entity-endpoint.spec.d.ts.map +1 -0
  101. package/dist/endpoint-generators/create-get-entity-endpoint.spec.js +74 -0
  102. package/dist/endpoint-generators/create-get-entity-endpoint.spec.js.map +1 -0
  103. package/dist/endpoint-generators/create-patch-endpoint.d.ts +18 -0
  104. package/dist/endpoint-generators/create-patch-endpoint.d.ts.map +1 -0
  105. package/dist/endpoint-generators/create-patch-endpoint.js +26 -0
  106. package/dist/endpoint-generators/create-patch-endpoint.js.map +1 -0
  107. package/dist/endpoint-generators/create-patch-endpoint.spec.d.ts +2 -0
  108. package/dist/endpoint-generators/create-patch-endpoint.spec.d.ts.map +1 -0
  109. package/dist/endpoint-generators/create-patch-endpoint.spec.js +36 -0
  110. package/dist/endpoint-generators/create-patch-endpoint.spec.js.map +1 -0
  111. package/dist/endpoint-generators/create-post-endpoint.d.ts +18 -0
  112. package/dist/endpoint-generators/create-post-endpoint.d.ts.map +1 -0
  113. package/dist/endpoint-generators/create-post-endpoint.js +29 -0
  114. package/dist/endpoint-generators/create-post-endpoint.js.map +1 -0
  115. package/dist/endpoint-generators/create-post-endpoint.spec.d.ts +2 -0
  116. package/dist/endpoint-generators/create-post-endpoint.spec.d.ts.map +1 -0
  117. package/dist/endpoint-generators/create-post-endpoint.spec.js +34 -0
  118. package/dist/endpoint-generators/create-post-endpoint.spec.js.map +1 -0
  119. package/dist/endpoint-generators/index.d.ts +6 -0
  120. package/dist/endpoint-generators/index.d.ts.map +1 -0
  121. package/dist/endpoint-generators/index.js +9 -0
  122. package/dist/endpoint-generators/index.js.map +1 -0
  123. package/dist/endpoint-generators/utils.d.ts +9 -0
  124. package/dist/endpoint-generators/utils.d.ts.map +1 -0
  125. package/dist/endpoint-generators/utils.js +27 -0
  126. package/dist/endpoint-generators/utils.js.map +1 -0
  127. package/dist/http-authentication-settings.d.ts +17 -0
  128. package/dist/http-authentication-settings.d.ts.map +1 -0
  129. package/dist/http-authentication-settings.js +26 -0
  130. package/dist/http-authentication-settings.js.map +1 -0
  131. package/dist/http-user-context.d.ts +54 -0
  132. package/dist/http-user-context.d.ts.map +1 -0
  133. package/dist/http-user-context.js +153 -0
  134. package/dist/http-user-context.js.map +1 -0
  135. package/dist/http-user-context.spec.d.ts +4 -0
  136. package/dist/http-user-context.spec.d.ts.map +1 -0
  137. package/dist/http-user-context.spec.js +267 -0
  138. package/dist/http-user-context.spec.js.map +1 -0
  139. package/dist/incoming-message-extensions.d.ts +8 -0
  140. package/dist/incoming-message-extensions.d.ts.map +1 -0
  141. package/dist/incoming-message-extensions.js +14 -0
  142. package/dist/incoming-message-extensions.js.map +1 -0
  143. package/dist/incoming-message-extensions.spec.d.ts +2 -0
  144. package/dist/incoming-message-extensions.spec.d.ts.map +1 -0
  145. package/dist/incoming-message-extensions.spec.js +39 -0
  146. package/dist/incoming-message-extensions.spec.js.map +1 -0
  147. package/dist/index.d.ts +17 -0
  148. package/dist/index.d.ts.map +1 -0
  149. package/dist/index.js +20 -0
  150. package/dist/index.js.map +1 -0
  151. package/dist/injector-extensions.d.ts +21 -0
  152. package/dist/injector-extensions.d.ts.map +1 -0
  153. package/dist/injector-extensions.js +14 -0
  154. package/dist/injector-extensions.js.map +1 -0
  155. package/dist/injector-extensions.spec.d.ts +2 -0
  156. package/dist/injector-extensions.spec.d.ts.map +1 -0
  157. package/dist/injector-extensions.spec.js +19 -0
  158. package/dist/injector-extensions.spec.js.map +1 -0
  159. package/dist/models/cors-options.d.ts +22 -0
  160. package/dist/models/cors-options.d.ts.map +1 -0
  161. package/dist/models/cors-options.js +3 -0
  162. package/dist/models/cors-options.js.map +1 -0
  163. package/dist/models/default-session.d.ts +14 -0
  164. package/dist/models/default-session.d.ts.map +1 -0
  165. package/dist/models/default-session.js +10 -0
  166. package/dist/models/default-session.js.map +1 -0
  167. package/dist/request-action-implementation.d.ts +54 -0
  168. package/dist/request-action-implementation.d.ts.map +1 -0
  169. package/dist/request-action-implementation.js +42 -0
  170. package/dist/request-action-implementation.js.map +1 -0
  171. package/dist/rest-service.integration.spec.d.ts +2 -0
  172. package/dist/rest-service.integration.spec.d.ts.map +1 -0
  173. package/dist/rest-service.integration.spec.js +129 -0
  174. package/dist/rest-service.integration.spec.js.map +1 -0
  175. package/dist/rest.integration.test.d.ts +58 -0
  176. package/dist/rest.integration.test.d.ts.map +1 -0
  177. package/dist/rest.integration.test.js +94 -0
  178. package/dist/rest.integration.test.js.map +1 -0
  179. package/dist/schema-validator/index.d.ts +3 -0
  180. package/dist/schema-validator/index.d.ts.map +1 -0
  181. package/dist/schema-validator/index.js +6 -0
  182. package/dist/schema-validator/index.js.map +1 -0
  183. package/dist/schema-validator/schema-validation-error.d.ts +10 -0
  184. package/dist/schema-validator/schema-validation-error.d.ts.map +1 -0
  185. package/dist/schema-validator/schema-validation-error.js +15 -0
  186. package/dist/schema-validator/schema-validation-error.js.map +1 -0
  187. package/dist/schema-validator/schema-validator.d.ts +20 -0
  188. package/dist/schema-validator/schema-validator.d.ts.map +1 -0
  189. package/dist/schema-validator/schema-validator.js +36 -0
  190. package/dist/schema-validator/schema-validator.js.map +1 -0
  191. package/dist/schema-validator/schema-validator.test.d.ts +2 -0
  192. package/dist/schema-validator/schema-validator.test.d.ts.map +1 -0
  193. package/dist/schema-validator/schema-validator.test.js +62 -0
  194. package/dist/schema-validator/schema-validator.test.js.map +1 -0
  195. package/dist/schema-validator/validate-examples.d.ts +37 -0
  196. package/dist/schema-validator/validate-examples.d.ts.map +1 -0
  197. package/dist/schema-validator/validate-examples.js +29 -0
  198. package/dist/schema-validator/validate-examples.js.map +1 -0
  199. package/dist/server-manager.d.ts +30 -0
  200. package/dist/server-manager.d.ts.map +1 -0
  201. package/dist/server-manager.js +71 -0
  202. package/dist/server-manager.js.map +1 -0
  203. package/dist/server-response-extensions.d.ts +21 -0
  204. package/dist/server-response-extensions.d.ts.map +1 -0
  205. package/dist/server-response-extensions.js +15 -0
  206. package/dist/server-response-extensions.js.map +1 -0
  207. package/dist/server-response-extensions.spec.d.ts +2 -0
  208. package/dist/server-response-extensions.spec.d.ts.map +1 -0
  209. package/dist/server-response-extensions.spec.js +49 -0
  210. package/dist/server-response-extensions.spec.js.map +1 -0
  211. package/dist/utils.d.ts +24 -0
  212. package/dist/utils.d.ts.map +1 -0
  213. package/dist/utils.js +66 -0
  214. package/dist/utils.js.map +1 -0
  215. package/dist/validate.d.ts +18 -0
  216. package/dist/validate.d.ts.map +1 -0
  217. package/dist/validate.integration.schema.d.ts +69 -0
  218. package/dist/validate.integration.schema.d.ts.map +1 -0
  219. package/dist/validate.integration.schema.js +3 -0
  220. package/dist/validate.integration.schema.js.map +1 -0
  221. package/dist/validate.integration.spec.d.ts +13 -0
  222. package/dist/validate.integration.spec.d.ts.map +1 -0
  223. package/dist/validate.integration.spec.js +223 -0
  224. package/dist/validate.integration.spec.js.map +1 -0
  225. package/dist/validate.integration.spec.schema.json +749 -0
  226. package/dist/validate.js +49 -0
  227. package/dist/validate.js.map +1 -0
  228. package/package.json +56 -0
  229. package/src/actions/error-action.spec.ts +54 -0
  230. package/src/actions/error-action.ts +34 -0
  231. package/src/actions/get-current-user.spec.ts +23 -0
  232. package/src/actions/get-current-user.ts +15 -0
  233. package/src/actions/index.ts +6 -0
  234. package/src/actions/is-authenticated.spec.ts +18 -0
  235. package/src/actions/is-authenticated.ts +13 -0
  236. package/src/actions/login-action.spec.ts +41 -0
  237. package/src/actions/login.ts +26 -0
  238. package/src/actions/logout-action.spec.ts +27 -0
  239. package/src/actions/logout.ts +16 -0
  240. package/src/actions/not-found-action.spec.ts +17 -0
  241. package/src/actions/not-found-action.ts +13 -0
  242. package/src/add-cors-header.spec.ts +133 -0
  243. package/src/api-manager.ts +222 -0
  244. package/src/authenticate.spec.ts +78 -0
  245. package/src/authenticate.ts +22 -0
  246. package/src/authorize.spec.ts +69 -0
  247. package/src/authorize.ts +19 -0
  248. package/src/endpoint-generators/create-delete-endpoint.spec.ts +34 -0
  249. package/src/endpoint-generators/create-delete-endpoint.ts +25 -0
  250. package/src/endpoint-generators/create-get-collection-endpoint.spec.ts +164 -0
  251. package/src/endpoint-generators/create-get-collection-endpoint.ts +28 -0
  252. package/src/endpoint-generators/create-get-entity-endpoint.spec.ts +75 -0
  253. package/src/endpoint-generators/create-get-entity-endpoint.ts +29 -0
  254. package/src/endpoint-generators/create-patch-endpoint.spec.ts +36 -0
  255. package/src/endpoint-generators/create-patch-endpoint.ts +27 -0
  256. package/src/endpoint-generators/create-post-endpoint.spec.ts +32 -0
  257. package/src/endpoint-generators/create-post-endpoint.ts +30 -0
  258. package/src/endpoint-generators/index.ts +5 -0
  259. package/src/endpoint-generators/utils.ts +34 -0
  260. package/src/http-authentication-settings.ts +23 -0
  261. package/src/http-user-context.spec.ts +299 -0
  262. package/src/http-user-context.ts +160 -0
  263. package/src/incoming-message-extensions.spec.ts +41 -0
  264. package/src/incoming-message-extensions.ts +19 -0
  265. package/src/index.ts +16 -0
  266. package/src/injector-extensions.spec.ts +19 -0
  267. package/src/injector-extensions.ts +35 -0
  268. package/src/models/cors-options.ts +21 -0
  269. package/src/models/default-session.ts +14 -0
  270. package/src/request-action-implementation.ts +70 -0
  271. package/src/rest-service.integration.spec.ts +166 -0
  272. package/src/rest.integration.test.ts +112 -0
  273. package/src/schema-validator/index.ts +2 -0
  274. package/src/schema-validator/schema-validation-error.ts +11 -0
  275. package/src/schema-validator/schema-validator.test.ts +72 -0
  276. package/src/schema-validator/schema-validator.ts +31 -0
  277. package/src/schema-validator/validate-examples.ts +38 -0
  278. package/src/server-manager.ts +88 -0
  279. package/src/server-response-extensions.spec.ts +53 -0
  280. package/src/server-response-extensions.ts +30 -0
  281. package/src/utils.ts +65 -0
  282. package/src/validate.integration.schema.ts +50 -0
  283. package/src/validate.integration.spec.schema.json +779 -0
  284. package/src/validate.integration.spec.ts +218 -0
  285. package/src/validate.ts +60 -0
@@ -0,0 +1,25 @@
1
+ import { Constructable } from '@furystack/inject'
2
+ import { DeleteEndpoint } from '@furystack/rest'
3
+ import '@furystack/repository'
4
+ import { JsonResult, RequestAction } from '../request-action-implementation'
5
+
6
+ /**
7
+ * Creates a DELETE endpoint for removing entities
8
+ *
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
13
+ */
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 }) => {
19
+ const { id } = getUrlParams()
20
+ const dataSet = injector.getDataSetFor(options.model, options.primaryKey)
21
+ await dataSet.remove(injector, id)
22
+ return JsonResult({}, 204)
23
+ }
24
+ return endpoint
25
+ }
@@ -0,0 +1,164 @@
1
+ import { usingAsync } from '@furystack/utils'
2
+ import { Injector } from '@furystack/inject'
3
+ import got from 'got'
4
+ import { MockClass, setupContext } from './utils'
5
+ import { createGetCollectionEndpoint } from './create-get-collection-endpoint'
6
+ import { GetCollectionEndpoint, GetCollectionResult, serializeToQueryString } from '@furystack/rest'
7
+ import { FindOptions } from '@furystack/core'
8
+
9
+ const addMockEntities = async (i: Injector) =>
10
+ await i
11
+ .getDataSetFor(MockClass, 'id')
12
+ .add(
13
+ i,
14
+ { id: 'mock1', value: '4' },
15
+ { id: 'mock2', value: '3' },
16
+ { id: 'mock3', value: '2' },
17
+ { id: 'mock4', value: '1' },
18
+ )
19
+
20
+ describe('createGetCollectionEndpoint', () => {
21
+ it('Should return the collection without filter / order', async () => {
22
+ await usingAsync(new Injector(), async (i) => {
23
+ setupContext(i)
24
+ await i.useRestService<{ GET: { '/entities': GetCollectionEndpoint<MockClass> } }>({
25
+ root: '/api',
26
+ port: 1112,
27
+ api: {
28
+ GET: {
29
+ '/entities': createGetCollectionEndpoint({ model: MockClass, primaryKey: 'id' }),
30
+ },
31
+ },
32
+ })
33
+ await addMockEntities(i)
34
+
35
+ const count = await i.getDataSetFor(MockClass, 'id').count(i)
36
+ const allEntities = await i.getDataSetFor(MockClass, 'id').find(i, {})
37
+
38
+ const response = await got('http://127.0.0.1:1112/api/entities', { method: 'GET' })
39
+ const json: GetCollectionResult<MockClass> = JSON.parse(response.body)
40
+ expect(json.count).toBe(count)
41
+ expect(json.entries).toEqual(allEntities)
42
+ })
43
+ })
44
+
45
+ it('Should return entities in order', async () => {
46
+ await usingAsync(new Injector(), async (i) => {
47
+ setupContext(i)
48
+ await i.useRestService<{ GET: { '/entities': GetCollectionEndpoint<MockClass> } }>({
49
+ root: '/api',
50
+ port: 1113,
51
+ api: {
52
+ GET: {
53
+ '/entities': createGetCollectionEndpoint({ model: MockClass, primaryKey: 'id' }),
54
+ },
55
+ },
56
+ })
57
+ await addMockEntities(i)
58
+ const findOptions: FindOptions<MockClass, Array<keyof MockClass>> = { order: { value: 'ASC' } }
59
+ const count = await i.getDataSetFor(MockClass, 'id').count(i, findOptions.filter)
60
+ const orderedEntities = await i.getDataSetFor(MockClass, 'id').find(i, findOptions)
61
+ const response = await got(`http://127.0.0.1:1113/api/entities?${serializeToQueryString({ findOptions })}`, {
62
+ method: 'GET',
63
+ })
64
+ const json: GetCollectionResult<MockClass> = JSON.parse(response.body)
65
+ expect(json.count).toBe(count)
66
+ expect(json.entries).toEqual(orderedEntities)
67
+ })
68
+ })
69
+
70
+ it('Should return entities with filtering', async () => {
71
+ await usingAsync(new Injector(), async (i) => {
72
+ setupContext(i)
73
+ await i.useRestService<{ GET: { '/entities': GetCollectionEndpoint<MockClass> } }>({
74
+ root: '/api',
75
+ port: 1113,
76
+ api: {
77
+ GET: {
78
+ '/entities': createGetCollectionEndpoint({ model: MockClass, primaryKey: 'id' }),
79
+ },
80
+ },
81
+ })
82
+ await addMockEntities(i)
83
+ const findOptions: FindOptions<MockClass, Array<keyof MockClass>> = {
84
+ filter: { id: { $ne: 'mock2' } },
85
+ }
86
+
87
+ const count = await i.getDataSetFor(MockClass, 'id').count(i, findOptions.filter)
88
+ const filteredEntities = await i.getDataSetFor(MockClass, 'id').find(i, findOptions)
89
+
90
+ expect(filteredEntities).not.toContainEqual({ id: 'mock2', value: '3' })
91
+
92
+ const response = await got(`http://127.0.0.1:1113/api/entities?${serializeToQueryString({ findOptions })}`, {
93
+ method: 'GET',
94
+ })
95
+ const json: GetCollectionResult<MockClass> = JSON.parse(response.body)
96
+ expect(json.count).toBe(count)
97
+ expect(json.entries).toEqual(filteredEntities)
98
+ })
99
+ })
100
+
101
+ it('Should return entities with selecting specific fields', async () => {
102
+ await usingAsync(new Injector(), async (i) => {
103
+ setupContext(i)
104
+ await i.useRestService<{ GET: { '/entities': GetCollectionEndpoint<MockClass> } }>({
105
+ root: '/api',
106
+ port: 1113,
107
+ api: {
108
+ GET: {
109
+ '/entities': createGetCollectionEndpoint({ model: MockClass, primaryKey: 'id' }),
110
+ },
111
+ },
112
+ })
113
+ await addMockEntities(i)
114
+ const findOptions: FindOptions<MockClass, Array<keyof MockClass>> = {
115
+ select: ['id'],
116
+ }
117
+
118
+ const count = await i.getDataSetFor(MockClass, 'id').count(i, findOptions.filter)
119
+ const selectedEntities = await i.getDataSetFor(MockClass, 'id').find(i, findOptions)
120
+
121
+ selectedEntities.forEach((e) => expect(e.value).toBeUndefined())
122
+
123
+ const response = await got(`http://127.0.0.1:1113/api/entities?${serializeToQueryString({ findOptions })}`, {
124
+ method: 'GET',
125
+ })
126
+ const json: GetCollectionResult<MockClass> = JSON.parse(response.body)
127
+ expect(json.count).toBe(count)
128
+ expect(json.entries).toEqual(selectedEntities)
129
+ })
130
+ })
131
+
132
+ it('Should return entities with top/skip', async () => {
133
+ await usingAsync(new Injector(), async (i) => {
134
+ setupContext(i)
135
+ await i.useRestService<{ GET: { '/entities': GetCollectionEndpoint<MockClass> } }>({
136
+ root: '/api',
137
+ port: 1113,
138
+ api: {
139
+ GET: {
140
+ '/entities': createGetCollectionEndpoint({ model: MockClass, primaryKey: 'id' }),
141
+ },
142
+ },
143
+ })
144
+ await addMockEntities(i)
145
+ const findOptions: FindOptions<MockClass, Array<keyof MockClass>> = {
146
+ skip: 1,
147
+ top: 2,
148
+ }
149
+
150
+ const count = await i.getDataSetFor(MockClass, 'id').count(i, findOptions.filter)
151
+ const topSkipEntities = await i.getDataSetFor(MockClass, 'id').find(i, findOptions)
152
+
153
+ expect(topSkipEntities).not.toContainEqual({ id: 'mock1', value: '4' })
154
+ expect(topSkipEntities).not.toContainEqual({ id: 'mock4', value: '1' })
155
+
156
+ const response = await got(`http://127.0.0.1:1113/api/entities?${serializeToQueryString({ findOptions })}`, {
157
+ method: 'GET',
158
+ })
159
+ const json: GetCollectionResult<MockClass> = JSON.parse(response.body)
160
+ expect(json.count).toBe(count)
161
+ expect(json.entries).toEqual(topSkipEntities)
162
+ })
163
+ })
164
+ })
@@ -0,0 +1,28 @@
1
+ import { Constructable } from '@furystack/inject'
2
+ import { GetCollectionEndpoint } from '@furystack/rest'
3
+ import '@furystack/repository'
4
+ import { JsonResult, RequestAction } from '../request-action-implementation'
5
+
6
+ /**
7
+ * Creates a GetCollection endpoint for the given model. The model should have a Repository DataSet
8
+ *
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
13
+ */
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 }) => {
19
+ const { findOptions } = getQuery()
20
+ const dataSet = injector.getDataSetFor(options.model, options.primaryKey)
21
+ const entriesPromise = dataSet.find<any>(injector, findOptions || {})
22
+ const countPromise = dataSet.count(injector, findOptions?.filter)
23
+ const [entries, count] = await Promise.all([entriesPromise, countPromise])
24
+
25
+ return JsonResult({ entries, count })
26
+ }
27
+ return endpoint
28
+ }
@@ -0,0 +1,75 @@
1
+ import { usingAsync } from '@furystack/utils'
2
+ import { Injector } from '@furystack/inject'
3
+ import { GetEntityEndpoint, serializeToQueryString } from '@furystack/rest'
4
+ import got, { HTTPError } from 'got'
5
+ import { MockClass, setupContext } from './utils'
6
+ import { createGetEntityEndpoint } from './create-get-entity-endpoint'
7
+
8
+ describe('createGetEntityEndpoint', () => {
9
+ it('Should return the entity', async () => {
10
+ await usingAsync(new Injector(), async (i) => {
11
+ setupContext(i)
12
+ await i.useRestService<{ GET: { '/:id': GetEntityEndpoint<MockClass, 'id'> } }>({
13
+ root: '/api',
14
+ port: 1113,
15
+ api: {
16
+ GET: {
17
+ '/:id': createGetEntityEndpoint({ model: MockClass, primaryKey: 'id' }),
18
+ },
19
+ },
20
+ })
21
+ const mockEntity: MockClass = { id: 'mock', value: 'mock' }
22
+ await i.getDataSetFor(MockClass, 'id').add(i, mockEntity)
23
+
24
+ const response = await got('http://127.0.0.1:1113/api/mock', { method: 'GET' })
25
+ expect(JSON.parse(response.body)).toStrictEqual(mockEntity)
26
+ })
27
+ })
28
+
29
+ it('Should return the entity with the selected fields', async () => {
30
+ await usingAsync(new Injector(), async (i) => {
31
+ setupContext(i)
32
+ await i.useRestService<{ GET: { '/:id': GetEntityEndpoint<MockClass, 'id'> } }>({
33
+ root: '/api',
34
+ port: 1114,
35
+ api: {
36
+ GET: {
37
+ '/:id': createGetEntityEndpoint({ model: MockClass, primaryKey: 'id' }),
38
+ },
39
+ },
40
+ })
41
+ const mockEntity: MockClass = { id: 'mock', value: 'mock' }
42
+ await i.getDataSetFor(MockClass, 'id').add(i, mockEntity)
43
+
44
+ const response = await got(`http://127.0.0.1:1114/api/mock?${serializeToQueryString({ select: ['id'] })}`, {
45
+ method: 'GET',
46
+ })
47
+ expect(JSON.parse(response.body)).toStrictEqual({ id: mockEntity.id })
48
+ })
49
+ })
50
+
51
+ it('Should return 404 if no entity has been found', async () => {
52
+ await usingAsync(new Injector(), async (i) => {
53
+ setupContext(i)
54
+ await i.useRestService<{ GET: { '/:id': GetEntityEndpoint<MockClass, 'id'> } }>({
55
+ root: '/api',
56
+ port: 1115,
57
+ api: {
58
+ GET: {
59
+ '/:id': createGetEntityEndpoint({ model: MockClass, primaryKey: 'id' }),
60
+ },
61
+ },
62
+ })
63
+ await new Promise<void>((resolve, reject) => {
64
+ got(`http://127.0.0.1:1115/api/mock`, { method: 'GET' })
65
+ .then(() => reject('Should throw'))
66
+ .catch((err) => {
67
+ const e: HTTPError = err
68
+ expect(e.response.statusCode).toBe(404)
69
+ expect(JSON.parse(e.response.body as string).message).toBe('Entity not found')
70
+ resolve()
71
+ })
72
+ })
73
+ })
74
+ })
75
+ })
@@ -0,0 +1,29 @@
1
+ import { Constructable } from '@furystack/inject'
2
+ import { RequestError, GetEntityEndpoint } from '@furystack/rest'
3
+ import '@furystack/repository'
4
+ import { JsonResult, RequestAction } from '../request-action-implementation'
5
+
6
+ /**
7
+ * Creates a simple Get Entity endpoint for a specified model.
8
+ *
9
+ * @param options The options for endpoint creation
10
+ * @param options.model The entity model, should have a Repository DataSet
11
+ * @param options.primaryKey The field name used as primary key on the model
12
+ * @returns The generated endpoint
13
+ */
14
+ export const createGetEntityEndpoint = <T extends object, TPrimaryKey extends keyof T>(options: {
15
+ model: Constructable<T>
16
+ primaryKey: TPrimaryKey
17
+ }) => {
18
+ const endpoint: RequestAction<GetEntityEndpoint<T, TPrimaryKey>> = async ({ injector, getUrlParams, getQuery }) => {
19
+ const { id } = getUrlParams()
20
+ const { select } = getQuery()
21
+ const dataSet = injector.getDataSetFor(options.model, options.primaryKey)
22
+ const entry = await dataSet.get(injector, id, select)
23
+ if (!entry) {
24
+ throw new RequestError('Entity not found', 404)
25
+ }
26
+ return JsonResult(entry)
27
+ }
28
+ return endpoint
29
+ }
@@ -0,0 +1,36 @@
1
+ import { usingAsync } from '@furystack/utils'
2
+ import { Injector } from '@furystack/inject'
3
+ import { PatchEndpoint } from '@furystack/rest'
4
+ import { createPatchEndpoint } from './create-patch-endpoint'
5
+ import got from 'got'
6
+ import { MockClass, setupContext } from './utils'
7
+
8
+ describe('createPatchEndpoint', () => {
9
+ it('Should update the entity and report the success', async () => {
10
+ await usingAsync(new Injector(), async (i) => {
11
+ setupContext(i)
12
+ await i.useRestService<{ PATCH: { '/:id': PatchEndpoint<MockClass, 'id'> } }>({
13
+ root: '/api',
14
+ port: 1116,
15
+ api: {
16
+ PATCH: {
17
+ '/:id': createPatchEndpoint({ model: MockClass, primaryKey: 'id' }),
18
+ },
19
+ },
20
+ })
21
+ await i.getDataSetFor(MockClass, 'id').add(i, { id: 'mock', value: 'mock' })
22
+
23
+ const countBeforeDelete = await i.getDataSetFor(MockClass, 'id').count(i)
24
+ expect(countBeforeDelete).toBe(1)
25
+
26
+ const response = await got('http://127.0.0.1:1116/api/mock', {
27
+ method: 'PATCH',
28
+ body: JSON.stringify({ value: 'updated' }),
29
+ })
30
+ expect(response.statusCode).toBe(200)
31
+ expect(response.body).toBe('{}')
32
+ const updated = await i.getDataSetFor(MockClass, 'id').get(i, 'mock')
33
+ expect(updated?.value).toBe('updated')
34
+ })
35
+ })
36
+ })
@@ -0,0 +1,27 @@
1
+ import { Constructable } from '@furystack/inject'
2
+ import { PatchEndpoint } from '@furystack/rest'
3
+ import '@furystack/repository'
4
+ import '../incoming-message-extensions'
5
+ import { JsonResult, RequestAction } from '../request-action-implementation'
6
+
7
+ /**
8
+ * Creates a PATCH endpoint for updating entities
9
+ *
10
+ * @param options The options for endpoint creation
11
+ * @param options.model The Model class
12
+ * @param options.primaryKey The field name that is used as primary key on the model
13
+ * @returns a boolean that indicates the success
14
+ */
15
+ export const createPatchEndpoint = <T extends object, TPrimaryKey extends keyof T>(options: {
16
+ model: Constructable<T>
17
+ primaryKey: TPrimaryKey
18
+ }) => {
19
+ const endpoint: RequestAction<PatchEndpoint<T, TPrimaryKey>> = async ({ injector, request, getUrlParams }) => {
20
+ const { id } = getUrlParams()
21
+ const patchData = await request.readPostBody<T>()
22
+ const dataSet = injector.getDataSetFor(options.model, options.primaryKey)
23
+ await dataSet.update(injector, id, patchData)
24
+ return JsonResult({})
25
+ }
26
+ return endpoint
27
+ }
@@ -0,0 +1,32 @@
1
+ import { usingAsync } from '@furystack/utils'
2
+ import { Injector } from '@furystack/inject'
3
+ import { PostEndpoint } from '@furystack/rest'
4
+ import { createPostEndpoint } from './create-post-endpoint'
5
+ import got from 'got'
6
+ import { MockClass, setupContext } from './utils'
7
+
8
+ describe('createPostEndpoint', () => {
9
+ it('Should create the entity and report the success', async () => {
10
+ await usingAsync(new Injector(), async (i) => {
11
+ setupContext(i)
12
+ await i.useRestService<{ POST: { '/:id': PostEndpoint<MockClass, 'id'> } }>({
13
+ root: '/api',
14
+ port: 1117,
15
+ api: {
16
+ POST: {
17
+ '/:id': createPostEndpoint({ model: MockClass, primaryKey: 'id' }),
18
+ },
19
+ },
20
+ })
21
+ const entityToPost = { id: 'mock', value: 'posted' }
22
+ const response = await got('http://127.0.0.1:1117/api/mock', {
23
+ method: 'POST',
24
+ body: JSON.stringify(entityToPost),
25
+ })
26
+ expect(response.statusCode).toBe(201)
27
+ expect(JSON.parse(response.body)).toStrictEqual(entityToPost)
28
+ const posted = await i.getDataSetFor(MockClass, 'id').get(i, entityToPost.id)
29
+ expect(posted?.value).toBe('posted')
30
+ })
31
+ })
32
+ })
@@ -0,0 +1,30 @@
1
+ import { Constructable } from '@furystack/inject'
2
+ import { RequestError, PostEndpoint } from '@furystack/rest'
3
+ import '@furystack/repository'
4
+ import '../incoming-message-extensions'
5
+ import { JsonResult, RequestAction } from '../request-action-implementation'
6
+ import { WithOptionalId } from '@furystack/core'
7
+ /**
8
+ * Creates a POST endpoint for updating entities
9
+ *
10
+ * @param options The options for endpoint creation
11
+ * @param options.model The Model class
12
+ * @param options.primaryKey The field name used as primary key
13
+ * @returns a boolean that indicates the success
14
+ */
15
+ export const createPostEndpoint = <T extends object, TPrimaryKey extends keyof T>(options: {
16
+ model: Constructable<T>
17
+ primaryKey: TPrimaryKey
18
+ }) => {
19
+ const endpoint: RequestAction<PostEndpoint<T, TPrimaryKey>> = async ({ injector, request }) => {
20
+ const dataSet = injector.getDataSetFor(options.model, options.primaryKey)
21
+
22
+ const entityToCreate = await request.readPostBody<WithOptionalId<T, typeof dataSet['primaryKey']>>()
23
+ const { created } = await dataSet.add(injector, entityToCreate)
24
+ if (!created || !created.length) {
25
+ throw new RequestError('Entity not found', 404)
26
+ }
27
+ return JsonResult(created[0], 201)
28
+ }
29
+ return endpoint
30
+ }