@toa.io/extensions.exposition 0.20.0-alpha.0 → 0.20.0-alpha.2

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 (225) hide show
  1. package/components/context.toa.yaml +2 -2
  2. package/documentation/notes/sse.md +71 -0
  3. package/documentation/protocol.md +4 -1
  4. package/features/access.feature +48 -42
  5. package/features/annotation.feature +6 -4
  6. package/features/body.feature +24 -0
  7. package/features/directives.feature +2 -2
  8. package/features/dynamic.feature +11 -1
  9. package/features/errors.feature +14 -29
  10. package/features/identity.basic.feature +47 -6
  11. package/features/identity.tokens.feature +19 -16
  12. package/features/steps/Gateway.ts +26 -18
  13. package/features/steps/components/echo/manifest.toa.yaml +9 -0
  14. package/features/steps/components/echo/operations/affect.js +7 -0
  15. package/features/steps/components/echo/operations/compute.js +7 -0
  16. package/features/steps/components/greeter/manifest.toa.yaml +0 -4
  17. package/features/steps/components/greeter/operations/greet.js +1 -1
  18. package/features/steps/components/sequences/manifest.toa.yaml +10 -0
  19. package/features/steps/components/sequences/operations/numbers.js +7 -0
  20. package/features/steps/components/sequences/operations/tokens.js +16 -0
  21. package/features/streams.feature +26 -0
  22. package/package.json +7 -6
  23. package/readme.md +2 -0
  24. package/schemas/annotation.cos.yaml +5 -4
  25. package/schemas/method.cos.yaml +0 -1
  26. package/schemas/query.cos.yaml +1 -0
  27. package/source/Annotation.ts +1 -0
  28. package/source/Composition.ts +1 -2
  29. package/source/Endpoint.ts +5 -3
  30. package/source/Factory.ts +8 -10
  31. package/source/HTTP/Server.ts +1 -1
  32. package/source/HTTP/messages.ts +18 -6
  33. package/source/Mapping.ts +12 -9
  34. package/source/RTD/Node.ts +5 -5
  35. package/source/RTD/Route.ts +5 -4
  36. package/source/RTD/Tree.ts +2 -5
  37. package/source/RTD/syntax/parse.ts +1 -1
  38. package/source/Remotes.test.ts +2 -1
  39. package/source/Remotes.ts +2 -0
  40. package/source/deployment.ts +9 -2
  41. package/source/directives/dev/Family.ts +3 -1
  42. package/source/directives/dev/Throw.ts +14 -0
  43. package/source/manifest.test.ts +3 -1
  44. package/source/manifest.ts +22 -9
  45. package/transpiled/Annotation.d.ts +0 -6
  46. package/transpiled/Annotation.js +0 -3
  47. package/transpiled/Annotation.js.map +0 -1
  48. package/transpiled/Branch.d.ts +0 -7
  49. package/transpiled/Branch.js +0 -3
  50. package/transpiled/Branch.js.map +0 -1
  51. package/transpiled/Composition.d.ts +0 -14
  52. package/transpiled/Composition.js +0 -43
  53. package/transpiled/Composition.js.map +0 -1
  54. package/transpiled/Context.d.ts +0 -5
  55. package/transpiled/Context.js +0 -3
  56. package/transpiled/Context.js.map +0 -1
  57. package/transpiled/Directive.d.ts +0 -32
  58. package/transpiled/Directive.js +0 -76
  59. package/transpiled/Directive.js.map +0 -1
  60. package/transpiled/Endpoint.d.ts +0 -20
  61. package/transpiled/Endpoint.js +0 -44
  62. package/transpiled/Endpoint.js.map +0 -1
  63. package/transpiled/Factory.d.ts +0 -11
  64. package/transpiled/Factory.js +0 -67
  65. package/transpiled/Factory.js.map +0 -1
  66. package/transpiled/Gateway.d.ts +0 -19
  67. package/transpiled/Gateway.js +0 -90
  68. package/transpiled/Gateway.js.map +0 -1
  69. package/transpiled/HTTP/Server.d.ts +0 -22
  70. package/transpiled/HTTP/Server.fixtures.d.ts +0 -12
  71. package/transpiled/HTTP/Server.fixtures.js +0 -36
  72. package/transpiled/HTTP/Server.fixtures.js.map +0 -1
  73. package/transpiled/HTTP/Server.js +0 -111
  74. package/transpiled/HTTP/Server.js.map +0 -1
  75. package/transpiled/HTTP/exceptions.d.ts +0 -39
  76. package/transpiled/HTTP/exceptions.js +0 -78
  77. package/transpiled/HTTP/exceptions.js.map +0 -1
  78. package/transpiled/HTTP/formats/index.d.ts +0 -8
  79. package/transpiled/HTTP/formats/index.js +0 -38
  80. package/transpiled/HTTP/formats/index.js.map +0 -1
  81. package/transpiled/HTTP/formats/json.d.ts +0 -4
  82. package/transpiled/HTTP/formats/json.js +0 -15
  83. package/transpiled/HTTP/formats/json.js.map +0 -1
  84. package/transpiled/HTTP/formats/msgpack.d.ts +0 -4
  85. package/transpiled/HTTP/formats/msgpack.js +0 -36
  86. package/transpiled/HTTP/formats/msgpack.js.map +0 -1
  87. package/transpiled/HTTP/formats/text.d.ts +0 -4
  88. package/transpiled/HTTP/formats/text.js +0 -13
  89. package/transpiled/HTTP/formats/text.js.map +0 -1
  90. package/transpiled/HTTP/formats/yaml.d.ts +0 -4
  91. package/transpiled/HTTP/formats/yaml.js +0 -39
  92. package/transpiled/HTTP/formats/yaml.js.map +0 -1
  93. package/transpiled/HTTP/index.d.ts +0 -3
  94. package/transpiled/HTTP/index.js +0 -20
  95. package/transpiled/HTTP/index.js.map +0 -1
  96. package/transpiled/HTTP/messages.d.ts +0 -27
  97. package/transpiled/HTTP/messages.js +0 -49
  98. package/transpiled/HTTP/messages.js.map +0 -1
  99. package/transpiled/Mapping.d.ts +0 -8
  100. package/transpiled/Mapping.js +0 -35
  101. package/transpiled/Mapping.js.map +0 -1
  102. package/transpiled/Query.d.ts +0 -13
  103. package/transpiled/Query.js +0 -107
  104. package/transpiled/Query.js.map +0 -1
  105. package/transpiled/RTD/Context.d.ts +0 -11
  106. package/transpiled/RTD/Context.js +0 -3
  107. package/transpiled/RTD/Context.js.map +0 -1
  108. package/transpiled/RTD/Directives.d.ts +0 -7
  109. package/transpiled/RTD/Directives.js +0 -3
  110. package/transpiled/RTD/Directives.js.map +0 -1
  111. package/transpiled/RTD/Endpoint.d.ts +0 -9
  112. package/transpiled/RTD/Endpoint.js +0 -3
  113. package/transpiled/RTD/Endpoint.js.map +0 -1
  114. package/transpiled/RTD/Match.d.ts +0 -11
  115. package/transpiled/RTD/Match.js +0 -3
  116. package/transpiled/RTD/Match.js.map +0 -1
  117. package/transpiled/RTD/Method.d.ts +0 -9
  118. package/transpiled/RTD/Method.js +0 -16
  119. package/transpiled/RTD/Method.js.map +0 -1
  120. package/transpiled/RTD/Node.d.ts +0 -21
  121. package/transpiled/RTD/Node.js +0 -61
  122. package/transpiled/RTD/Node.js.map +0 -1
  123. package/transpiled/RTD/Route.d.ts +0 -14
  124. package/transpiled/RTD/Route.js +0 -48
  125. package/transpiled/RTD/Route.js.map +0 -1
  126. package/transpiled/RTD/Tree.d.ts +0 -14
  127. package/transpiled/RTD/Tree.js +0 -45
  128. package/transpiled/RTD/Tree.js.map +0 -1
  129. package/transpiled/RTD/factory.d.ts +0 -6
  130. package/transpiled/RTD/factory.js +0 -36
  131. package/transpiled/RTD/factory.js.map +0 -1
  132. package/transpiled/RTD/index.d.ts +0 -8
  133. package/transpiled/RTD/index.js +0 -38
  134. package/transpiled/RTD/index.js.map +0 -1
  135. package/transpiled/RTD/segment.d.ts +0 -8
  136. package/transpiled/RTD/segment.js +0 -23
  137. package/transpiled/RTD/segment.js.map +0 -1
  138. package/transpiled/RTD/syntax/index.d.ts +0 -2
  139. package/transpiled/RTD/syntax/index.js +0 -19
  140. package/transpiled/RTD/syntax/index.js.map +0 -1
  141. package/transpiled/RTD/syntax/parse.d.ts +0 -4
  142. package/transpiled/RTD/syntax/parse.js +0 -128
  143. package/transpiled/RTD/syntax/parse.js.map +0 -1
  144. package/transpiled/RTD/syntax/types.d.ts +0 -41
  145. package/transpiled/RTD/syntax/types.js +0 -5
  146. package/transpiled/RTD/syntax/types.js.map +0 -1
  147. package/transpiled/Remotes.d.ts +0 -7
  148. package/transpiled/Remotes.js +0 -19
  149. package/transpiled/Remotes.js.map +0 -1
  150. package/transpiled/Tenant.d.ts +0 -12
  151. package/transpiled/Tenant.js +0 -30
  152. package/transpiled/Tenant.js.map +0 -1
  153. package/transpiled/deployment.d.ts +0 -3
  154. package/transpiled/deployment.js +0 -61
  155. package/transpiled/deployment.js.map +0 -1
  156. package/transpiled/directives/auth/Anonymous.d.ts +0 -6
  157. package/transpiled/directives/auth/Anonymous.js +0 -17
  158. package/transpiled/directives/auth/Anonymous.js.map +0 -1
  159. package/transpiled/directives/auth/Echo.d.ts +0 -6
  160. package/transpiled/directives/auth/Echo.js +0 -13
  161. package/transpiled/directives/auth/Echo.js.map +0 -1
  162. package/transpiled/directives/auth/Family.d.ts +0 -20
  163. package/transpiled/directives/auth/Family.js +0 -125
  164. package/transpiled/directives/auth/Family.js.map +0 -1
  165. package/transpiled/directives/auth/Id.d.ts +0 -7
  166. package/transpiled/directives/auth/Id.js +0 -17
  167. package/transpiled/directives/auth/Id.js.map +0 -1
  168. package/transpiled/directives/auth/Incept.d.ts +0 -10
  169. package/transpiled/directives/auth/Incept.js +0 -59
  170. package/transpiled/directives/auth/Incept.js.map +0 -1
  171. package/transpiled/directives/auth/Role.d.ts +0 -11
  172. package/transpiled/directives/auth/Role.js +0 -44
  173. package/transpiled/directives/auth/Role.js.map +0 -1
  174. package/transpiled/directives/auth/Rule.d.ts +0 -9
  175. package/transpiled/directives/auth/Rule.js +0 -22
  176. package/transpiled/directives/auth/Rule.js.map +0 -1
  177. package/transpiled/directives/auth/Scheme.d.ts +0 -7
  178. package/transpiled/directives/auth/Scheme.js +0 -47
  179. package/transpiled/directives/auth/Scheme.js.map +0 -1
  180. package/transpiled/directives/auth/index.d.ts +0 -2
  181. package/transpiled/directives/auth/index.js +0 -7
  182. package/transpiled/directives/auth/index.js.map +0 -1
  183. package/transpiled/directives/auth/schemes.d.ts +0 -3
  184. package/transpiled/directives/auth/schemes.js +0 -9
  185. package/transpiled/directives/auth/schemes.js.map +0 -1
  186. package/transpiled/directives/auth/split.d.ts +0 -2
  187. package/transpiled/directives/auth/split.js +0 -38
  188. package/transpiled/directives/auth/split.js.map +0 -1
  189. package/transpiled/directives/auth/types.d.ts +0 -31
  190. package/transpiled/directives/auth/types.js +0 -3
  191. package/transpiled/directives/auth/types.js.map +0 -1
  192. package/transpiled/directives/dev/Family.d.ts +0 -10
  193. package/transpiled/directives/dev/Family.js +0 -25
  194. package/transpiled/directives/dev/Family.js.map +0 -1
  195. package/transpiled/directives/dev/Stub.d.ts +0 -7
  196. package/transpiled/directives/dev/Stub.js +0 -14
  197. package/transpiled/directives/dev/Stub.js.map +0 -1
  198. package/transpiled/directives/dev/index.d.ts +0 -2
  199. package/transpiled/directives/dev/index.js +0 -7
  200. package/transpiled/directives/dev/index.js.map +0 -1
  201. package/transpiled/directives/dev/types.d.ts +0 -4
  202. package/transpiled/directives/dev/types.js +0 -3
  203. package/transpiled/directives/dev/types.js.map +0 -1
  204. package/transpiled/directives/index.d.ts +0 -2
  205. package/transpiled/directives/index.js +0 -10
  206. package/transpiled/directives/index.js.map +0 -1
  207. package/transpiled/discovery.d.ts +0 -1
  208. package/transpiled/discovery.js +0 -3
  209. package/transpiled/discovery.js.map +0 -1
  210. package/transpiled/exceptions.d.ts +0 -2
  211. package/transpiled/exceptions.js +0 -39
  212. package/transpiled/exceptions.js.map +0 -1
  213. package/transpiled/index.d.ts +0 -5
  214. package/transpiled/index.js +0 -12
  215. package/transpiled/index.js.map +0 -1
  216. package/transpiled/manifest.d.ts +0 -3
  217. package/transpiled/manifest.js +0 -30
  218. package/transpiled/manifest.js.map +0 -1
  219. package/transpiled/root.d.ts +0 -2
  220. package/transpiled/root.js +0 -39
  221. package/transpiled/root.js.map +0 -1
  222. package/transpiled/schemas.d.ts +0 -3
  223. package/transpiled/schemas.js +0 -14
  224. package/transpiled/schemas.js.map +0 -1
  225. package/transpiled/tsconfig.tsbuildinfo +0 -1
@@ -11,5 +11,5 @@ configuration:
11
11
  identity.tokens:
12
12
  key0: k3.local.pIZT8-9Fa6U_QtfQHOSStfGtmyzPINyKQq2Xk-hd7vA
13
13
 
14
- realtime:
15
- messages.created: [senderId, recipientId]
14
+ exposition:
15
+ debug: true
@@ -0,0 +1,71 @@
1
+ # Note: Server Sent Events
2
+
3
+ ## TL;DR
4
+
5
+ 1. Doesn't exist.
6
+ 2. Ridiculous.
7
+ 3. Redundant.
8
+
9
+ ## There is no SSE
10
+
11
+ The initial premise of SSE is to deliver real-time events specifically targeted at a particular user. It's evident that
12
+ to access these events, user authentication is essential, typically implemented through
13
+ an [HTTP Authentication framework](https://datatracker.ietf.org/doc/html/rfc2617).
14
+
15
+ However, despite this being the most straightforward
16
+ scenario, [it does not work this way](https://github.com/whatwg/html/issues/2177). The only viable way to use SSE is
17
+ to avoid the standard `EventSource` implementation.
18
+
19
+ ## Peculiar event format
20
+
21
+ ### SLAP
22
+
23
+ [Event stream format](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#event_stream_format)
24
+ defines four fields: `event`, `data`, `id`, `retry`.
25
+
26
+ In practice, code capable of producing such an object is forced to break the Single Level of Abstraction Principle. This
27
+ requires knowledge of both event structures and connection properties. In real-world systems, these may be handled by
28
+ different processes, such as an Events microservice and an API Gateway.
29
+
30
+ ### Content negotiation
31
+
32
+ The `data` property is expected to be an arbitrary string, although real-world event data is often structured as an
33
+ object.
34
+
35
+ Furthermore, since the Event stream format defines its own content type, there is no built-in way to negotiate the
36
+ format of the events.
37
+
38
+ Additionally, the `data` value is limited to a single line, which hinders the use of
39
+ more [human-friendly formats](https://yaml.org) in a straightforward manner.
40
+
41
+ ## HTTP is enough
42
+
43
+ Rather than resorting to a non-standard client and a predefined content format, a basic HTTP request can provide a
44
+ solution:
45
+
46
+ ```http
47
+ GET /events/ HTTP/1.1
48
+ accept: application/yaml
49
+ ```
50
+
51
+ ```
52
+ 200 OK
53
+ content-type: application/yaml
54
+ transfer-encoding: chunked
55
+
56
+ id: 1
57
+ event: created
58
+ data:
59
+ foo: bar
60
+
61
+ id: 2
62
+ event: deleted
63
+ data:
64
+ bar: baz
65
+ ```
66
+
67
+ Server-side implementation with flow control and stream termination handling is trivial:
68
+
69
+ ```javascript
70
+ eventStream.pipe(response)
71
+ ```
@@ -12,4 +12,7 @@ The following media types are supported for both requests and responses:
12
12
  The response format is determined by content negotiation
13
13
  using [negotiator](https://github.com/jshttp/negotiator).
14
14
 
15
- > Errors are always sent as `text/plain`. See [debug mode](../readme.md#context-annotation).
15
+ ## Streams
16
+
17
+ Reply streams are transmitted
18
+ using [chunked transfer encoding](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding#directives).
@@ -69,11 +69,12 @@ Feature: Access authorization
69
69
  Scenario: Using `auth:id` directive
70
70
  Given the annotation:
71
71
  """yaml
72
- /:id:
73
- auth:id: id
74
- GET:
75
- dev:stub:
76
- access: granted!
72
+ /:
73
+ /:id:
74
+ auth:id: id
75
+ GET:
76
+ dev:stub:
77
+ access: granted!
77
78
  """
78
79
  When the following request is received:
79
80
  """
@@ -144,14 +145,15 @@ Feature: Access authorization
144
145
  And the annotation:
145
146
  """yaml
146
147
  /:
147
- auth:role: developer:rust:junior # role scope matches
148
- /nested:
148
+ /:
149
+ auth:role: developer:rust:junior # role scope matches
150
+ /nested:
151
+ GET:
152
+ dev:stub: good!
153
+ /javascript:
154
+ auth:role: developer:javascript # role scope does not match
149
155
  GET:
150
- dev:stub: good!
151
- /javascript:
152
- auth:role: developer:javascript # role scope does not match
153
- GET:
154
- dev:stub: no good!
156
+ dev:stub: no good!
155
157
  """
156
158
  When the following request is received:
157
159
  """
@@ -211,20 +213,21 @@ Feature: Access authorization
211
213
  | 775a648d054e4ce1a65f8f17e5b51803 | efe3a65ebbee47ed95a73edd911ea328 | developer:rust |
212
214
  And the annotation:
213
215
  """yaml
214
- /rust/:id:
215
- auth:rule:
216
- id: id
217
- role: developer:rust
218
- GET:
219
- dev:stub:
220
- access: granted!
221
- /javascript/:id:
222
- rule:
223
- id: id
224
- role: developer:javascript
225
- GET:
226
- dev:stub:
227
- access: granted!
216
+ /:
217
+ /rust/:id:
218
+ auth:rule:
219
+ id: id
220
+ role: developer:rust
221
+ GET:
222
+ dev:stub:
223
+ access: granted!
224
+ /javascript/:id:
225
+ rule:
226
+ id: id
227
+ role: developer:javascript
228
+ GET:
229
+ dev:stub:
230
+ access: granted!
228
231
  """
229
232
  When the following request is received:
230
233
  """
@@ -252,11 +255,12 @@ Feature: Access authorization
252
255
  Scenario: Token authentication scheme
253
256
  Given the annotation:
254
257
  """yaml
255
- /:id:
256
- auth:id: id
257
- GET:
258
- dev:stub:
259
- access: granted!
258
+ /:
259
+ /:id:
260
+ auth:id: id
261
+ GET:
262
+ dev:stub:
263
+ access: granted!
260
264
  """
261
265
  When the following request is received:
262
266
  """
@@ -329,12 +333,13 @@ Feature: Access authorization
329
333
  Scenario: Using `auth:scheme` directive
330
334
  Given the annotation:
331
335
  """yaml
332
- /:id:
333
- auth:scheme: basic
334
- auth:id: id
335
- GET:
336
- dev:stub:
337
- access: granted!
336
+ /:
337
+ /:id:
338
+ auth:scheme: basic
339
+ auth:id: id
340
+ GET:
341
+ dev:stub:
342
+ access: granted!
338
343
  """
339
344
  When the following request is received:
340
345
  """
@@ -388,11 +393,12 @@ Feature: Access authorization
388
393
  | 775a648d054e4ce1a65f8f17e5b51803 | efe3a65ebbee47ed95a73edd911ea328 | system |
389
394
  And the annotation:
390
395
  """yaml
391
- /:id:
392
- auth:id: id
393
- GET:
394
- dev:stub:
395
- access: granted!
396
+ /:
397
+ /:id:
398
+ auth:id: id
399
+ GET:
400
+ dev:stub:
401
+ access: granted!
396
402
  """
397
403
  And the `identity.tokens` configuration:
398
404
  """yaml
@@ -3,10 +3,12 @@ Feature: Annotation
3
3
  Scenario: Simple annotation
4
4
  Given the annotation:
5
5
  """yaml
6
- anonymous: true
7
- /foo:
8
- GET:
9
- endpoint: pots.enumerate
6
+ /:
7
+ anonymous: true
8
+ /foo:
9
+ GET:
10
+ query: {}
11
+ endpoint: pots.enumerate
10
12
  """
11
13
  And the `pots` is running
12
14
  And the `pots` database contains:
@@ -19,3 +19,27 @@ Feature: Request body
19
19
  """
20
20
  201 Created
21
21
  """
22
+
23
+ Scenario Outline: Path segment as input for <operation>
24
+ Given the `echo` is running with the following manifest:
25
+ """yaml
26
+ exposition:
27
+ /:name:
28
+ GET: <operation>
29
+ """
30
+ When the following request is received:
31
+ """
32
+ GET /echo/world/ HTTP/1.1
33
+ accept: text/plain
34
+ """
35
+ Then the following reply is sent:
36
+ """
37
+ 200 OK
38
+ content-type: text/plain
39
+
40
+ Hello world
41
+ """
42
+ Examples:
43
+ | operation |
44
+ | compute |
45
+ | affect |
@@ -3,8 +3,8 @@ Feature: Directives
3
3
  Scenario: Basic directive
4
4
  Given the annotation:
5
5
  """yaml
6
- anonymous: true
7
6
  /:
7
+ anonymous: true
8
8
  GET:
9
9
  dev:stub:
10
10
  hello: world
@@ -25,8 +25,8 @@ Feature: Directives
25
25
  Scenario: Nested routes
26
26
  Given the annotation:
27
27
  """yaml
28
- anonymous: true
29
28
  /:
29
+ anonymous: true
30
30
  dev:stub:
31
31
  hello: again
32
32
  GET: {}
@@ -6,11 +6,21 @@ Feature: Dynamic tree updates
6
6
  | 4c4759e6f9c74da989d64511df42d6f4 | First pot | 100 | 80 |
7
7
 
8
8
  Scenario: Updating routes
9
+ Given the Gateway is running
9
10
  And the `pots` is running with the following manifest:
10
11
  """yaml
11
12
  exposition:
12
13
  /:
13
- POST: transit
14
+ isolated: true
15
+ GET: enumerate
16
+ """
17
+ When the following request is received:
18
+ """
19
+ GET /pots/ HTTP/1.1
20
+ """
21
+ Then the following reply is sent:
22
+ """
23
+ 401 Unauthorized
14
24
  """
15
25
  Then the `pots` is stopped
16
26
  Then the `pots` is running with the following manifest:
@@ -168,41 +168,26 @@ Feature: Errors
168
168
  Malformed authorization header.
169
169
  """
170
170
 
171
- Scenario: Creating an Identity using inception with existing credentials
172
- Given the `identity.basic` database is empty
173
- And the `users` is running with the following manifest:
171
+ Scenario Outline: Exception is thrown (debug: <debug>)
172
+ Given the annotation:
174
173
  """yaml
175
- exposition:
176
- /:
174
+ debug: <debug>
175
+ /:
176
+ GET:
177
177
  anonymous: true
178
- POST:
179
- incept: id
180
- endpoint: transit
178
+ dev:throw: Broken!
181
179
  """
182
180
  When the following request is received:
183
- # identity inception
184
181
  """
185
- POST /users/ HTTP/1.1
186
- authorization: Basic dXNlcjpwYXNzMTIzNA==
187
- accept: application/yaml
188
- content-type: application/yaml
189
-
190
- name: Bill Smith
191
- """
192
- Then the following reply is sent:
193
- """
194
- 201 Created
195
- """
196
- And the following request is received:
197
- # same credentials
198
- """
199
- POST /users/ HTTP/1.1
200
- authorization: Basic dXNlcjpwYXNzMTIzNA==
201
- content-type: text/plain
202
-
203
- name: Mary Louis
182
+ GET / HTTP/1.1
183
+ accept: text/plain
204
184
  """
205
185
  Then the following reply is sent:
206
186
  """
207
- 403 Forbidden
187
+ 500 Internal Server Error
188
+ <response>
208
189
  """
190
+ Examples:
191
+ | debug | response |
192
+ | false | content-length: 0 |
193
+ | true | Error: Broken! |
@@ -26,6 +26,7 @@ Feature: Basic authentication
26
26
  POST:
27
27
  incept: id
28
28
  endpoint: transit
29
+ query: ~
29
30
  /:id: # credential testing route
30
31
  id: id
31
32
  GET: observe
@@ -69,12 +70,13 @@ Feature: Basic authentication
69
70
 
70
71
  Scenario: Changing the password
71
72
  Given the annotation:
72
- """
73
- /:id:
74
- id: id
75
- GET:
76
- dev:stub:
77
- access: granted!
73
+ """yaml
74
+ /:
75
+ /:id:
76
+ id: id
77
+ GET:
78
+ dev:stub:
79
+ access: granted!
78
80
  """
79
81
  And the `identity.basic` database contains:
80
82
  | _id | _version | username | password |
@@ -233,3 +235,42 @@ Feature: Basic authentication
233
235
  code: PRINCIPAL_LOCKED
234
236
  message: Principal username cannot be changed.
235
237
  """
238
+
239
+ Scenario: Creating an Identity using inception with existing credentials
240
+ Given the `identity.basic` database is empty
241
+ And the `users` is running with the following manifest:
242
+ """yaml
243
+ exposition:
244
+ /:
245
+ anonymous: true
246
+ POST:
247
+ incept: id
248
+ endpoint: transit
249
+ """
250
+ When the following request is received:
251
+ # identity inception
252
+ """
253
+ POST /users/ HTTP/1.1
254
+ authorization: Basic dXNlcjpwYXNzMTIzNA==
255
+ accept: application/yaml
256
+ content-type: application/yaml
257
+
258
+ name: Bill Smith
259
+ """
260
+ Then the following reply is sent:
261
+ """
262
+ 201 Created
263
+ """
264
+ And the following request is received:
265
+ # same credentials
266
+ """
267
+ POST /users/ HTTP/1.1
268
+ authorization: Basic dXNlcjpwYXNzMTIzNA==
269
+ content-type: text/plain
270
+
271
+ name: Mary Louis
272
+ """
273
+ Then the following reply is sent:
274
+ """
275
+ 403 Forbidden
276
+ """
@@ -4,16 +4,17 @@ Feature: Tokens lifecycle
4
4
  Given the `identity.basic` database contains:
5
5
  | _id | username | password |
6
6
  | efe3a65ebbee47ed95a73edd911ea328 | developer | $2b$10$ZRSKkgZoGnrcTNA5w5eCcu3pxDzdTduhteVYXcp56AaNcilNkwJ.O |
7
- And the `greeter` is running with the following manifest:
7
+ Given the annotation:
8
8
  """yaml
9
- exposition:
10
- /:id:
9
+ /:
10
+ /hello/:id:
11
11
  auth:id: id
12
- GET: greet
12
+ GET:
13
+ dev:stub: Hello
13
14
  """
14
15
  When the following request is received:
15
16
  """
16
- GET /greeter/efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
17
+ GET /hello/efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
17
18
  authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
18
19
  accept: text/plain
19
20
  """
@@ -31,16 +32,17 @@ Feature: Tokens lifecycle
31
32
  """yaml
32
33
  refresh: 1
33
34
  """
34
- And the `greeter` is running with the following manifest:
35
+ And the annotation:
35
36
  """yaml
36
- exposition:
37
- /:id:
37
+ /:
38
+ /hello/:id:
38
39
  auth:id: id
39
- GET: greet
40
+ GET:
41
+ dev:stub: Hello
40
42
  """
41
43
  When the following request is received:
42
44
  """
43
- GET /greeter/efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
45
+ GET /hello/efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
44
46
  authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
45
47
  accept: text/plain
46
48
  """
@@ -54,7 +56,7 @@ Feature: Tokens lifecycle
54
56
  Then after 1 second
55
57
  When the following request is received:
56
58
  """
57
- GET /greeter/efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
59
+ GET /hello/efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
58
60
  authorization: Token ${{ token }}
59
61
  accept: text/plain
60
62
  """
@@ -69,11 +71,12 @@ Feature: Tokens lifecycle
69
71
  Scenario: Token revocation on password change
70
72
  Given the annotation:
71
73
  """yaml
72
- /:id:
73
- id: id
74
- GET:
75
- dev:stub:
76
- access: granted!
74
+ /:
75
+ /:id:
76
+ id: id
77
+ GET:
78
+ dev:stub:
79
+ access: granted!
77
80
  """
78
81
  And the `identity.tokens` configuration:
79
82
  """yaml
@@ -16,9 +16,16 @@ export class Gateway {
16
16
  @given('the annotation:')
17
17
  public async annotate (yaml: string): Promise<void> {
18
18
  const annotation = parse(yaml)
19
- const node = syntax.parse(annotation, shortcuts)
20
19
 
21
- process.env.TOA_EXPOSITION = encode(node)
20
+ if ('/' in annotation) {
21
+ const node = { '/': annotation['/'] }
22
+ const tree = syntax.parse(node, shortcuts)
23
+
24
+ process.env.TOA_EXPOSITION = encode(tree)
25
+ }
26
+
27
+ if (annotation.debug === true)
28
+ process.env.TOA_EXPOSITION_DEBUG = '1'
22
29
 
23
30
  await Gateway.stop()
24
31
 
@@ -40,22 +47,7 @@ export class Gateway {
40
47
  this.default = false
41
48
  }
42
49
 
43
- @after()
44
- public async cleanup (): Promise<void> {
45
- if (this.default)
46
- return
47
-
48
- delete process.env.TOA_EXPOSITION
49
-
50
- await Gateway.stop()
51
- }
52
-
53
- @afterAll()
54
- public static async stop (): Promise<void> {
55
- await instance?.disconnect()
56
- instance = null
57
- }
58
-
50
+ @given('the Gateway is running')
59
51
  public async start (): Promise<void> {
60
52
  if (instance !== null)
61
53
  return
@@ -76,6 +68,22 @@ export class Gateway {
76
68
  await timeout(50) // resource discovery
77
69
  }
78
70
 
71
+ @after()
72
+ public async cleanup (): Promise<void> {
73
+ if (this.default)
74
+ return
75
+
76
+ delete process.env.TOA_EXPOSITION
77
+
78
+ await Gateway.stop()
79
+ }
80
+
81
+ @afterAll()
82
+ public static async stop (): Promise<void> {
83
+ await instance?.disconnect()
84
+ instance = null
85
+ }
86
+
79
87
  private writeConfiguration (): void {
80
88
  for (const [id, configuration] of Object.entries(DEFAULT_CONFIGURATION)) {
81
89
  const [name, namespace = 'default'] = id.split('.').reverse()
@@ -0,0 +1,9 @@
1
+ name: echo
2
+
3
+ operations:
4
+ compute:
5
+ input:
6
+ name*: string
7
+ affect:
8
+ input:
9
+ name*: string
@@ -0,0 +1,7 @@
1
+ 'use strict'
2
+
3
+ function effect (input) {
4
+ return `Hello ${input.name}`
5
+ }
6
+
7
+ exports.effect = effect
@@ -0,0 +1,7 @@
1
+ 'use strict'
2
+
3
+ function computation (input) {
4
+ return `Hello ${input.name}`
5
+ }
6
+
7
+ exports.computation = computation
@@ -1,9 +1,5 @@
1
1
  name: greeter
2
2
 
3
- operations:
4
- greet:
5
- output: string
6
-
7
3
  exposition:
8
4
  /:
9
5
  GET: greet
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- async function computation () {
3
+ function computation () {
4
4
  return 'Hello'
5
5
  }
6
6
 
@@ -0,0 +1,10 @@
1
+ name: sequences
2
+
3
+ operations:
4
+ numbers:
5
+ input: 5
6
+
7
+ exposition:
8
+ /tokens:
9
+ anonymous: true
10
+ GET: tokens
@@ -0,0 +1,7 @@
1
+ 'use strict'
2
+
3
+ function * effect (amount) {
4
+ for (let i = 0; i < amount; i++) yield i
5
+ }
6
+
7
+ exports.effect = effect
@@ -0,0 +1,16 @@
1
+ 'use strict'
2
+
3
+ const { randomBytes } = require('node:crypto')
4
+
5
+ async function * computation () {
6
+ while (true) {
7
+ await timeout(Math.floor(Math.random() * 100) + 10)
8
+ yield randomBytes(4).toString('hex')
9
+ }
10
+ }
11
+
12
+ function timeout (ms) {
13
+ return new Promise(resolve => setTimeout(resolve, ms))
14
+ }
15
+
16
+ exports.computation = computation