@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,41 @@
1
+ import { IncomingMessage } from 'http'
2
+ import './incoming-message-extensions'
3
+ import { Socket } from 'net'
4
+
5
+ describe('IncomingMessage extensions', () => {
6
+ describe('readPostBody', () => {
7
+ it('Should be extended', () => {
8
+ const socket = new Socket()
9
+ const msg = new IncomingMessage(socket)
10
+ expect(typeof msg.readPostBody).toBe('function')
11
+ })
12
+
13
+ it('Should read the raw post body', async () => {
14
+ const exampleValue = { value: Math.random().toString() }
15
+ const socket = new Socket()
16
+ const msg = new IncomingMessage(socket)
17
+ setTimeout(() => {
18
+ msg.read = () => JSON.stringify(exampleValue)
19
+ msg.emit('readable')
20
+ msg.emit('end')
21
+ }, 10)
22
+
23
+ const result = await msg.readPostBodyRaw()
24
+ expect(result).toEqual(JSON.stringify(exampleValue))
25
+ })
26
+
27
+ it('Should read the post body', async () => {
28
+ const exampleValue = { value: Math.random().toString() }
29
+ const socket = new Socket()
30
+ const msg = new IncomingMessage(socket)
31
+ setTimeout(() => {
32
+ msg.read = () => JSON.stringify(exampleValue)
33
+ msg.emit('readable')
34
+ msg.emit('end')
35
+ }, 10)
36
+
37
+ const result = await msg.readPostBody()
38
+ expect(result).toEqual(exampleValue)
39
+ })
40
+ })
41
+ })
@@ -0,0 +1,19 @@
1
+ import http from 'http'
2
+ import { Utils } from './utils'
3
+
4
+ declare module 'http' {
5
+ export interface IncomingMessage {
6
+ readPostBodyRaw: () => Promise<string>
7
+ readPostBody: <T>() => Promise<T>
8
+ }
9
+ }
10
+
11
+ http.IncomingMessage.prototype.readPostBody = async function <T>() {
12
+ const utils = new Utils()
13
+ return await utils.readPostBody<T>(this)
14
+ }
15
+
16
+ http.IncomingMessage.prototype.readPostBodyRaw = async function () {
17
+ const utils = new Utils()
18
+ return await utils.readPostBodyRaw(this)
19
+ }
package/src/index.ts ADDED
@@ -0,0 +1,16 @@
1
+ import './injector-extensions'
2
+ export * from './api-manager'
3
+ export * from './actions'
4
+ export * from './authenticate'
5
+ export * from './authorize'
6
+ export * from './http-authentication-settings'
7
+ export * from './http-user-context'
8
+ export * from './incoming-message-extensions'
9
+ export * from './server-manager'
10
+ export * from './server-response-extensions'
11
+ export * from './utils'
12
+ export * from './models/default-session'
13
+ export * from './endpoint-generators'
14
+ export * from './schema-validator'
15
+ export * from './request-action-implementation'
16
+ export * from './validate'
@@ -0,0 +1,19 @@
1
+ import { Injector } from '@furystack/inject'
2
+ import { usingAsync } from '@furystack/utils'
3
+ import './injector-extensions'
4
+
5
+ // tslint:disable: no-string-literal
6
+
7
+ describe('Injector extensions', () => {
8
+ it('useRestService() Should be added to the injector prototype', async () => {
9
+ await usingAsync(new Injector(), async (i) => {
10
+ i.useHttpAuthentication().useRestService({ api: {}, port: 19999, root: '/api' })
11
+ })
12
+ })
13
+
14
+ it('useHttpAuthentication() Should be added to the injector prototype', async () => {
15
+ await usingAsync(new Injector(), async (i) => {
16
+ i.useHttpAuthentication().useHttpAuthentication()
17
+ })
18
+ })
19
+ })
@@ -0,0 +1,35 @@
1
+ import { User } from '@furystack/core'
2
+ import { Injector } from '@furystack/inject/dist/injector'
3
+ import { HttpAuthenticationSettings } from './http-authentication-settings'
4
+ import { RestApi } from '@furystack/rest'
5
+ import { ApiManager, ImplementApiOptions } from './api-manager'
6
+ import { DefaultSession } from 'models/default-session'
7
+
8
+ declare module '@furystack/inject/dist/injector' {
9
+ /**
10
+ * Extended Injector with Http API related methods
11
+ */
12
+ export interface Injector {
13
+ /**
14
+ * Sets up the @furystack/rest-service with the provided settings
15
+ */
16
+ useRestService: <T extends RestApi>(api: Omit<ImplementApiOptions<T>, 'injector'>) => Promise<this>
17
+
18
+ /**
19
+ * Sets up the HTTP Authentication
20
+ */
21
+ useHttpAuthentication: <TUser extends User, TSession extends DefaultSession>(
22
+ settings?: Partial<HttpAuthenticationSettings<TUser, TSession>>,
23
+ ) => this
24
+ }
25
+ }
26
+
27
+ Injector.prototype.useRestService = async function (api) {
28
+ await this.getInstance(ApiManager).addApi({ ...api, injector: this })
29
+ return this
30
+ }
31
+
32
+ Injector.prototype.useHttpAuthentication = function (s) {
33
+ this.setExplicitInstance({ ...new HttpAuthenticationSettings(), ...s }, HttpAuthenticationSettings)
34
+ return this
35
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * CORS settings
3
+ */
4
+ export interface CorsOptions {
5
+ /**
6
+ * List of allowed origins
7
+ */
8
+ origins: string[]
9
+ /**
10
+ * List of the allowed actions
11
+ */
12
+ methods?: Array<'POST' | 'GET' | 'PUT' | 'DELETE' | 'PATCH'>
13
+ /**
14
+ * List of the allowed headers
15
+ */
16
+ headers?: string[]
17
+ /**
18
+ * Enable cookies
19
+ */
20
+ credentials?: boolean
21
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Model class that defines the default session data
3
+ */
4
+ export class DefaultSession {
5
+ /**
6
+ * The generated session identifier
7
+ */
8
+ public sessionId!: string
9
+
10
+ /**
11
+ * The user's login name for the session
12
+ */
13
+ public username!: string
14
+ }
@@ -0,0 +1,70 @@
1
+ import { Injector } from '@furystack/inject'
2
+ import { ServerResponse } from 'http'
3
+ import { IncomingMessage } from 'http'
4
+
5
+ export interface ActionResult<T> {
6
+ statusCode: number
7
+ headers: { [K: string]: string }
8
+ chunk: T
9
+ }
10
+
11
+ export const JsonResult = <T extends object>(chunk: T, statusCode = 200, headers?: { [K: string]: string }) =>
12
+ ({
13
+ statusCode,
14
+ chunk,
15
+ headers: {
16
+ ...headers,
17
+ 'Content-Type': 'application/json',
18
+ },
19
+ } as ActionResult<T>)
20
+
21
+ export const PlainTextResult = (text: string, statusCode = 200, headers?: { [K: string]: string }) =>
22
+ ({
23
+ statusCode,
24
+ chunk: text,
25
+ headers: {
26
+ ...headers,
27
+ 'Content-Type': 'plain/text',
28
+ },
29
+ } as ActionResult<string>)
30
+
31
+ export const XmlResult = (text: string, statusCode = 200, headers?: { [K: string]: string }) =>
32
+ ({
33
+ statusCode,
34
+ chunk: text,
35
+ headers: {
36
+ ...headers,
37
+ 'Content-Type': 'application/xml;charset=utf-8',
38
+ },
39
+ } as ActionResult<string>)
40
+
41
+ export const EmptyResult = (statusCode = 200, headers?: { [K: string]: string }) =>
42
+ ({
43
+ statusCode,
44
+ headers: {
45
+ ...headers,
46
+ },
47
+ } as ActionResult<undefined>)
48
+
49
+ export const BypassResult = () =>
50
+ ({
51
+ chunk: 'BypassResult',
52
+ } as ActionResult<'BypassResult'>)
53
+
54
+ export type RequestActionOptions<T extends { result: unknown }> = {
55
+ request: IncomingMessage
56
+ response: ServerResponse
57
+ injector: Injector
58
+ } & (T extends {
59
+ result: unknown
60
+ body: infer U
61
+ }
62
+ ? { getBody: () => Promise<U> }
63
+ : unknown) &
64
+ (T extends { result: unknown; url: infer U } ? { getUrlParams: () => U } : unknown) &
65
+ (T extends { result: unknown; query: infer U } ? { getQuery: () => U } : unknown) &
66
+ (T extends { result: unknown; headers: infer U } ? { headers: U } : unknown)
67
+
68
+ export type RequestAction<T extends { result: unknown }> = (
69
+ options: RequestActionOptions<T>,
70
+ ) => Promise<ActionResult<T['result']>>
@@ -0,0 +1,166 @@
1
+ import { Injector } from '@furystack/inject'
2
+ import './injector-extensions'
3
+ import { usingAsync, PathHelper } from '@furystack/utils'
4
+ import { GetCurrentUser, IsAuthenticated, LoginAction, LogoutAction } from './actions'
5
+ import { RestApi } from '@furystack/rest'
6
+ import { User, InMemoryStore } from '@furystack/core'
7
+ import { DefaultSession } from './models/default-session'
8
+ import got from 'got'
9
+ import { JsonResult } from './request-action-implementation'
10
+
11
+ class UserWithPassword extends User {
12
+ password!: string
13
+ }
14
+
15
+ interface IntegrationTestApi extends RestApi {
16
+ GET: {
17
+ '/isAuthenticated': { result: { isAuthenticated: boolean } }
18
+ '/currentUser': { result: User }
19
+ '/testQuery': { query: { param1: string }; result: { param1Value: string } }
20
+ '/testUrlParams/:urlParam': { url: { urlParam: string }; result: { urlParamValue: string } }
21
+ }
22
+ POST: {
23
+ '/login': { result: User; body: { username: string; password: string } }
24
+ '/logout': { result: unknown }
25
+ '/testPostBody': { body: { value: string }; result: { bodyValue: string } }
26
+ }
27
+ }
28
+
29
+ const port = 19090
30
+ const hostName = 'localhost'
31
+ const root = 'test-api'
32
+
33
+ const prepareInjector = (i: Injector) =>
34
+ i
35
+ .setupStores((sm) =>
36
+ sm
37
+ .addStore(new InMemoryStore({ model: User, primaryKey: 'username' }))
38
+ .addStore(new InMemoryStore({ model: DefaultSession, primaryKey: 'sessionId' })),
39
+ )
40
+ .useHttpAuthentication({
41
+ getUserStore: (sm) => sm.getStoreFor(UserWithPassword, 'username'),
42
+ getSessionStore: (sm) => sm.getStoreFor(DefaultSession, 'sessionId'),
43
+ })
44
+ .useRestService<IntegrationTestApi>({
45
+ root,
46
+ port,
47
+ hostName,
48
+ cors: {
49
+ credentials: true,
50
+ origins: ['http://localhost:8080'],
51
+ headers: ['cache', 'content-type'],
52
+ },
53
+ api: {
54
+ GET: {
55
+ '/currentUser': GetCurrentUser,
56
+ '/isAuthenticated': IsAuthenticated,
57
+ '/testQuery': async (options) => JsonResult({ param1Value: options.getQuery().param1 }),
58
+ '/testUrlParams/:urlParam': async (options) => JsonResult({ urlParamValue: options.getUrlParams().urlParam }),
59
+ },
60
+ POST: {
61
+ '/login': LoginAction,
62
+ '/logout': LogoutAction,
63
+ '/testPostBody': async (options) => {
64
+ const body = await options.getBody()
65
+ return JsonResult({ bodyValue: body.value })
66
+ },
67
+ },
68
+ },
69
+ })
70
+
71
+ const apiUrl = PathHelper.joinPaths(`http://${hostName}:${port}`, root)
72
+
73
+ describe('@furystack/rest-service inregration tests', () => {
74
+ it('Should be started and disposed', async () => {
75
+ await usingAsync(new Injector(), async (i) => {
76
+ await prepareInjector(i)
77
+ })
78
+ })
79
+
80
+ it('Should respond with 404 when a route is not found', async () => {
81
+ await usingAsync(new Injector(), async (i) => {
82
+ await prepareInjector(i)
83
+ await expect(got(PathHelper.joinPaths(apiUrl, 'some-route-that-does-not-exists'))).rejects.toThrow(
84
+ 'Response code 404 (Not Found)',
85
+ )
86
+ })
87
+ })
88
+
89
+ it('Should respond with 401 for unauthorized request errors', async () => {
90
+ await usingAsync(new Injector(), async (i) => {
91
+ await prepareInjector(i)
92
+ await expect(got(PathHelper.joinPaths(apiUrl, 'currentUser'), { retry: 0 })).rejects.toThrow(
93
+ 'Response code 401 (Unauthorized)',
94
+ )
95
+ })
96
+ })
97
+
98
+ it('Should respond with 401 for unauthorized request errors', async () => {
99
+ await usingAsync(new Injector(), async (i) => {
100
+ await prepareInjector(i)
101
+ await expect(got(PathHelper.joinPaths(apiUrl, 'currentUser'), { retry: 0 })).rejects.toThrow(
102
+ 'Response code 401 (Unauthorized)',
103
+ )
104
+ })
105
+ })
106
+
107
+ it('Should respond with the correct result body', async () => {
108
+ await usingAsync(new Injector(), async (i) => {
109
+ await prepareInjector(i)
110
+ const response = await got(PathHelper.joinPaths(apiUrl, 'isAuthenticated'), { retry: 0 })
111
+ expect(response.statusCode).toBe(200)
112
+ expect(JSON.parse(response.body)).toStrictEqual({ isAuthenticated: false })
113
+ })
114
+ })
115
+
116
+ it('Should be able to read query parameters', async () => {
117
+ await usingAsync(new Injector(), async (i) => {
118
+ await prepareInjector(i)
119
+ const response = await got(PathHelper.joinPaths(apiUrl, 'testQuery?param1=foo'), { retry: 0 })
120
+ expect(response.statusCode).toBe(200)
121
+ expect(JSON.parse(response.body)).toStrictEqual({ param1Value: 'foo' })
122
+ })
123
+ })
124
+
125
+ it('Should be able to read url parameters', async () => {
126
+ await usingAsync(new Injector(), async (i) => {
127
+ await prepareInjector(i)
128
+ const response = await got(PathHelper.joinPaths(apiUrl, 'testUrlParams/bar'), { retry: 0 })
129
+ expect(response.statusCode).toBe(200)
130
+ expect(JSON.parse(response.body)).toStrictEqual({ urlParamValue: 'bar' })
131
+ })
132
+ })
133
+
134
+ it('Should be able to read post body', async () => {
135
+ await usingAsync(new Injector(), async (i) => {
136
+ await prepareInjector(i)
137
+ const response = await got(PathHelper.joinPaths(apiUrl, 'testPostBody'), {
138
+ method: 'POST',
139
+ body: JSON.stringify({ value: 'baz' }),
140
+ retry: 0,
141
+ })
142
+ expect(response.statusCode).toBe(200)
143
+ expect(JSON.parse(response.body)).toStrictEqual({ bodyValue: 'baz' })
144
+ })
145
+ })
146
+
147
+ it('Should respond with OK to OPTIONS requests', async () => {
148
+ await usingAsync(new Injector(), async (i) => {
149
+ await prepareInjector(i)
150
+ const response = await got(PathHelper.joinPaths(apiUrl, 'testPostBody'), {
151
+ method: 'OPTIONS',
152
+ retry: 0,
153
+ })
154
+ expect(response.statusCode).toBe(200)
155
+ })
156
+ })
157
+
158
+ it('Should reject requests outside of the API Root', async () => {
159
+ await usingAsync(new Injector(), async (i) => {
160
+ await prepareInjector(i)
161
+ await expect(
162
+ got(PathHelper.joinPaths(`http://${hostName}:${port}`, 'not-my-api-root'), { retry: 0 }),
163
+ ).rejects.toThrowError('socket hang up')
164
+ })
165
+ })
166
+ })
@@ -0,0 +1,112 @@
1
+ import { Injector } from '@furystack/inject'
2
+ import { RestApi } from '@furystack/rest'
3
+ import { createClient } from '@furystack/rest-client-got'
4
+ import { usingAsync } from '@furystack/utils'
5
+ import { JsonResult } from './request-action-implementation'
6
+ import { v4 } from 'uuid'
7
+ import './injector-extensions'
8
+
9
+ export interface EchoApi extends RestApi {
10
+ GET: {
11
+ '/plain': { result: unknown }
12
+ '/headers': { headers: { value?: string }; result: { headers: { value?: string } } }
13
+ '/urlParams/:id': { url: { id: string }; result: { url: { id: string } } }
14
+ '/query': {
15
+ query: { someObject: { foo: string } }
16
+ result: { query: { someObject: { foo: string } } }
17
+ }
18
+ }
19
+ POST: {
20
+ '/body': { body: { foo: string; bar: number }; result: { body: { foo: string; bar: number } } }
21
+ }
22
+ }
23
+
24
+ const createEchoApiServer = async () => {
25
+ const port = Math.round(Math.random() * 1000) + 10000
26
+ const root = '/api'
27
+ const injector = new Injector()
28
+ await injector.useRestService<EchoApi>({
29
+ port,
30
+ root,
31
+ api: {
32
+ GET: {
33
+ '/plain': async () => JsonResult({}),
34
+ '/headers': async ({ headers }) => JsonResult({ headers }),
35
+ '/query': async ({ getQuery }) => JsonResult({ query: getQuery() }),
36
+ '/urlParams/:id': async ({ getUrlParams }) => JsonResult({ url: getUrlParams() }),
37
+ },
38
+ POST: {
39
+ '/body': async ({ getBody }) => JsonResult({ body: await getBody() }),
40
+ },
41
+ },
42
+ })
43
+ const client = createClient<EchoApi>({
44
+ endpointUrl: `http://localhost:${port}/api`,
45
+ })
46
+ return {
47
+ dispose: injector.dispose.bind(injector),
48
+ root,
49
+ port,
50
+ client,
51
+ }
52
+ }
53
+
54
+ describe('REST Integration tests with GOT client', () => {
55
+ it('Should execute a single parameterless GET query', async () => {
56
+ await usingAsync(await createEchoApiServer(), async ({ client }) => {
57
+ const result = await client({
58
+ method: 'GET',
59
+ action: '/plain',
60
+ })
61
+ expect(result.response.statusCode).toBe(200)
62
+ expect(result.getJson()).toStrictEqual({})
63
+ })
64
+ })
65
+
66
+ it('Should execute a request with headers', async () => {
67
+ await usingAsync(await createEchoApiServer(), async ({ client }) => {
68
+ const value = v4()
69
+ const result = await client({
70
+ method: 'GET',
71
+ action: '/headers',
72
+ headers: {
73
+ value,
74
+ },
75
+ })
76
+ expect(result.response.statusCode).toBe(200)
77
+ expect(result.getJson().headers.value).toStrictEqual(value)
78
+ })
79
+ })
80
+
81
+ it('Should execute a request with query', async () => {
82
+ await usingAsync(await createEchoApiServer(), async ({ client }) => {
83
+ const value = v4()
84
+ const result = await client({
85
+ method: 'GET',
86
+ action: '/query',
87
+ query: {
88
+ someObject: {
89
+ foo: value,
90
+ },
91
+ },
92
+ })
93
+ expect(result.response.statusCode).toBe(200)
94
+ expect(result.getJson().query.someObject.foo).toStrictEqual(value)
95
+ })
96
+ })
97
+
98
+ it('Should execute a request with URL parameters', async () => {
99
+ await usingAsync(await createEchoApiServer(), async ({ client }) => {
100
+ const value = v4()
101
+ const result = await client({
102
+ method: 'GET',
103
+ action: '/urlParams/:id',
104
+ url: {
105
+ id: value,
106
+ },
107
+ })
108
+ expect(result.response.statusCode).toBe(200)
109
+ expect(result.getJson().url.id).toEqual(value)
110
+ })
111
+ })
112
+ })