@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.
- package/components/context.toa.yaml +2 -2
- package/documentation/notes/sse.md +71 -0
- package/documentation/protocol.md +4 -1
- package/features/access.feature +48 -42
- package/features/annotation.feature +6 -4
- package/features/body.feature +24 -0
- package/features/directives.feature +2 -2
- package/features/dynamic.feature +11 -1
- package/features/errors.feature +14 -29
- package/features/identity.basic.feature +47 -6
- package/features/identity.tokens.feature +19 -16
- package/features/steps/Gateway.ts +26 -18
- package/features/steps/components/echo/manifest.toa.yaml +9 -0
- package/features/steps/components/echo/operations/affect.js +7 -0
- package/features/steps/components/echo/operations/compute.js +7 -0
- package/features/steps/components/greeter/manifest.toa.yaml +0 -4
- package/features/steps/components/greeter/operations/greet.js +1 -1
- package/features/steps/components/sequences/manifest.toa.yaml +10 -0
- package/features/steps/components/sequences/operations/numbers.js +7 -0
- package/features/steps/components/sequences/operations/tokens.js +16 -0
- package/features/streams.feature +26 -0
- package/package.json +7 -6
- package/readme.md +2 -0
- package/schemas/annotation.cos.yaml +5 -4
- package/schemas/method.cos.yaml +0 -1
- package/schemas/query.cos.yaml +1 -0
- package/source/Annotation.ts +1 -0
- package/source/Composition.ts +1 -2
- package/source/Endpoint.ts +5 -3
- package/source/Factory.ts +8 -10
- package/source/HTTP/Server.ts +1 -1
- package/source/HTTP/messages.ts +18 -6
- package/source/Mapping.ts +12 -9
- package/source/RTD/Node.ts +5 -5
- package/source/RTD/Route.ts +5 -4
- package/source/RTD/Tree.ts +2 -5
- package/source/RTD/syntax/parse.ts +1 -1
- package/source/Remotes.test.ts +2 -1
- package/source/Remotes.ts +2 -0
- package/source/deployment.ts +9 -2
- package/source/directives/dev/Family.ts +3 -1
- package/source/directives/dev/Throw.ts +14 -0
- package/source/manifest.test.ts +3 -1
- package/source/manifest.ts +22 -9
- package/transpiled/Annotation.d.ts +0 -6
- package/transpiled/Annotation.js +0 -3
- package/transpiled/Annotation.js.map +0 -1
- package/transpiled/Branch.d.ts +0 -7
- package/transpiled/Branch.js +0 -3
- package/transpiled/Branch.js.map +0 -1
- package/transpiled/Composition.d.ts +0 -14
- package/transpiled/Composition.js +0 -43
- package/transpiled/Composition.js.map +0 -1
- package/transpiled/Context.d.ts +0 -5
- package/transpiled/Context.js +0 -3
- package/transpiled/Context.js.map +0 -1
- package/transpiled/Directive.d.ts +0 -32
- package/transpiled/Directive.js +0 -76
- package/transpiled/Directive.js.map +0 -1
- package/transpiled/Endpoint.d.ts +0 -20
- package/transpiled/Endpoint.js +0 -44
- package/transpiled/Endpoint.js.map +0 -1
- package/transpiled/Factory.d.ts +0 -11
- package/transpiled/Factory.js +0 -67
- package/transpiled/Factory.js.map +0 -1
- package/transpiled/Gateway.d.ts +0 -19
- package/transpiled/Gateway.js +0 -90
- package/transpiled/Gateway.js.map +0 -1
- package/transpiled/HTTP/Server.d.ts +0 -22
- package/transpiled/HTTP/Server.fixtures.d.ts +0 -12
- package/transpiled/HTTP/Server.fixtures.js +0 -36
- package/transpiled/HTTP/Server.fixtures.js.map +0 -1
- package/transpiled/HTTP/Server.js +0 -111
- package/transpiled/HTTP/Server.js.map +0 -1
- package/transpiled/HTTP/exceptions.d.ts +0 -39
- package/transpiled/HTTP/exceptions.js +0 -78
- package/transpiled/HTTP/exceptions.js.map +0 -1
- package/transpiled/HTTP/formats/index.d.ts +0 -8
- package/transpiled/HTTP/formats/index.js +0 -38
- package/transpiled/HTTP/formats/index.js.map +0 -1
- package/transpiled/HTTP/formats/json.d.ts +0 -4
- package/transpiled/HTTP/formats/json.js +0 -15
- package/transpiled/HTTP/formats/json.js.map +0 -1
- package/transpiled/HTTP/formats/msgpack.d.ts +0 -4
- package/transpiled/HTTP/formats/msgpack.js +0 -36
- package/transpiled/HTTP/formats/msgpack.js.map +0 -1
- package/transpiled/HTTP/formats/text.d.ts +0 -4
- package/transpiled/HTTP/formats/text.js +0 -13
- package/transpiled/HTTP/formats/text.js.map +0 -1
- package/transpiled/HTTP/formats/yaml.d.ts +0 -4
- package/transpiled/HTTP/formats/yaml.js +0 -39
- package/transpiled/HTTP/formats/yaml.js.map +0 -1
- package/transpiled/HTTP/index.d.ts +0 -3
- package/transpiled/HTTP/index.js +0 -20
- package/transpiled/HTTP/index.js.map +0 -1
- package/transpiled/HTTP/messages.d.ts +0 -27
- package/transpiled/HTTP/messages.js +0 -49
- package/transpiled/HTTP/messages.js.map +0 -1
- package/transpiled/Mapping.d.ts +0 -8
- package/transpiled/Mapping.js +0 -35
- package/transpiled/Mapping.js.map +0 -1
- package/transpiled/Query.d.ts +0 -13
- package/transpiled/Query.js +0 -107
- package/transpiled/Query.js.map +0 -1
- package/transpiled/RTD/Context.d.ts +0 -11
- package/transpiled/RTD/Context.js +0 -3
- package/transpiled/RTD/Context.js.map +0 -1
- package/transpiled/RTD/Directives.d.ts +0 -7
- package/transpiled/RTD/Directives.js +0 -3
- package/transpiled/RTD/Directives.js.map +0 -1
- package/transpiled/RTD/Endpoint.d.ts +0 -9
- package/transpiled/RTD/Endpoint.js +0 -3
- package/transpiled/RTD/Endpoint.js.map +0 -1
- package/transpiled/RTD/Match.d.ts +0 -11
- package/transpiled/RTD/Match.js +0 -3
- package/transpiled/RTD/Match.js.map +0 -1
- package/transpiled/RTD/Method.d.ts +0 -9
- package/transpiled/RTD/Method.js +0 -16
- package/transpiled/RTD/Method.js.map +0 -1
- package/transpiled/RTD/Node.d.ts +0 -21
- package/transpiled/RTD/Node.js +0 -61
- package/transpiled/RTD/Node.js.map +0 -1
- package/transpiled/RTD/Route.d.ts +0 -14
- package/transpiled/RTD/Route.js +0 -48
- package/transpiled/RTD/Route.js.map +0 -1
- package/transpiled/RTD/Tree.d.ts +0 -14
- package/transpiled/RTD/Tree.js +0 -45
- package/transpiled/RTD/Tree.js.map +0 -1
- package/transpiled/RTD/factory.d.ts +0 -6
- package/transpiled/RTD/factory.js +0 -36
- package/transpiled/RTD/factory.js.map +0 -1
- package/transpiled/RTD/index.d.ts +0 -8
- package/transpiled/RTD/index.js +0 -38
- package/transpiled/RTD/index.js.map +0 -1
- package/transpiled/RTD/segment.d.ts +0 -8
- package/transpiled/RTD/segment.js +0 -23
- package/transpiled/RTD/segment.js.map +0 -1
- package/transpiled/RTD/syntax/index.d.ts +0 -2
- package/transpiled/RTD/syntax/index.js +0 -19
- package/transpiled/RTD/syntax/index.js.map +0 -1
- package/transpiled/RTD/syntax/parse.d.ts +0 -4
- package/transpiled/RTD/syntax/parse.js +0 -128
- package/transpiled/RTD/syntax/parse.js.map +0 -1
- package/transpiled/RTD/syntax/types.d.ts +0 -41
- package/transpiled/RTD/syntax/types.js +0 -5
- package/transpiled/RTD/syntax/types.js.map +0 -1
- package/transpiled/Remotes.d.ts +0 -7
- package/transpiled/Remotes.js +0 -19
- package/transpiled/Remotes.js.map +0 -1
- package/transpiled/Tenant.d.ts +0 -12
- package/transpiled/Tenant.js +0 -30
- package/transpiled/Tenant.js.map +0 -1
- package/transpiled/deployment.d.ts +0 -3
- package/transpiled/deployment.js +0 -61
- package/transpiled/deployment.js.map +0 -1
- package/transpiled/directives/auth/Anonymous.d.ts +0 -6
- package/transpiled/directives/auth/Anonymous.js +0 -17
- package/transpiled/directives/auth/Anonymous.js.map +0 -1
- package/transpiled/directives/auth/Echo.d.ts +0 -6
- package/transpiled/directives/auth/Echo.js +0 -13
- package/transpiled/directives/auth/Echo.js.map +0 -1
- package/transpiled/directives/auth/Family.d.ts +0 -20
- package/transpiled/directives/auth/Family.js +0 -125
- package/transpiled/directives/auth/Family.js.map +0 -1
- package/transpiled/directives/auth/Id.d.ts +0 -7
- package/transpiled/directives/auth/Id.js +0 -17
- package/transpiled/directives/auth/Id.js.map +0 -1
- package/transpiled/directives/auth/Incept.d.ts +0 -10
- package/transpiled/directives/auth/Incept.js +0 -59
- package/transpiled/directives/auth/Incept.js.map +0 -1
- package/transpiled/directives/auth/Role.d.ts +0 -11
- package/transpiled/directives/auth/Role.js +0 -44
- package/transpiled/directives/auth/Role.js.map +0 -1
- package/transpiled/directives/auth/Rule.d.ts +0 -9
- package/transpiled/directives/auth/Rule.js +0 -22
- package/transpiled/directives/auth/Rule.js.map +0 -1
- package/transpiled/directives/auth/Scheme.d.ts +0 -7
- package/transpiled/directives/auth/Scheme.js +0 -47
- package/transpiled/directives/auth/Scheme.js.map +0 -1
- package/transpiled/directives/auth/index.d.ts +0 -2
- package/transpiled/directives/auth/index.js +0 -7
- package/transpiled/directives/auth/index.js.map +0 -1
- package/transpiled/directives/auth/schemes.d.ts +0 -3
- package/transpiled/directives/auth/schemes.js +0 -9
- package/transpiled/directives/auth/schemes.js.map +0 -1
- package/transpiled/directives/auth/split.d.ts +0 -2
- package/transpiled/directives/auth/split.js +0 -38
- package/transpiled/directives/auth/split.js.map +0 -1
- package/transpiled/directives/auth/types.d.ts +0 -31
- package/transpiled/directives/auth/types.js +0 -3
- package/transpiled/directives/auth/types.js.map +0 -1
- package/transpiled/directives/dev/Family.d.ts +0 -10
- package/transpiled/directives/dev/Family.js +0 -25
- package/transpiled/directives/dev/Family.js.map +0 -1
- package/transpiled/directives/dev/Stub.d.ts +0 -7
- package/transpiled/directives/dev/Stub.js +0 -14
- package/transpiled/directives/dev/Stub.js.map +0 -1
- package/transpiled/directives/dev/index.d.ts +0 -2
- package/transpiled/directives/dev/index.js +0 -7
- package/transpiled/directives/dev/index.js.map +0 -1
- package/transpiled/directives/dev/types.d.ts +0 -4
- package/transpiled/directives/dev/types.js +0 -3
- package/transpiled/directives/dev/types.js.map +0 -1
- package/transpiled/directives/index.d.ts +0 -2
- package/transpiled/directives/index.js +0 -10
- package/transpiled/directives/index.js.map +0 -1
- package/transpiled/discovery.d.ts +0 -1
- package/transpiled/discovery.js +0 -3
- package/transpiled/discovery.js.map +0 -1
- package/transpiled/exceptions.d.ts +0 -2
- package/transpiled/exceptions.js +0 -39
- package/transpiled/exceptions.js.map +0 -1
- package/transpiled/index.d.ts +0 -5
- package/transpiled/index.js +0 -12
- package/transpiled/index.js.map +0 -1
- package/transpiled/manifest.d.ts +0 -3
- package/transpiled/manifest.js +0 -30
- package/transpiled/manifest.js.map +0 -1
- package/transpiled/root.d.ts +0 -2
- package/transpiled/root.js +0 -39
- package/transpiled/root.js.map +0 -1
- package/transpiled/schemas.d.ts +0 -3
- package/transpiled/schemas.js +0 -14
- package/transpiled/schemas.js.map +0 -1
- package/transpiled/tsconfig.tsbuildinfo +0 -1
|
@@ -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
|
-
|
|
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).
|
package/features/access.feature
CHANGED
|
@@ -69,11 +69,12 @@ Feature: Access authorization
|
|
|
69
69
|
Scenario: Using `auth:id` directive
|
|
70
70
|
Given the annotation:
|
|
71
71
|
"""yaml
|
|
72
|
-
/:
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
148
|
-
|
|
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
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
-
/:
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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
|
-
/:
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
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
|
-
/:
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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:
|
package/features/body.feature
CHANGED
|
@@ -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: {}
|
package/features/dynamic.feature
CHANGED
|
@@ -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
|
-
|
|
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:
|
package/features/errors.feature
CHANGED
|
@@ -168,41 +168,26 @@ Feature: Errors
|
|
|
168
168
|
Malformed authorization header.
|
|
169
169
|
"""
|
|
170
170
|
|
|
171
|
-
Scenario:
|
|
172
|
-
Given the
|
|
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
|
-
|
|
176
|
-
|
|
174
|
+
debug: <debug>
|
|
175
|
+
/:
|
|
176
|
+
GET:
|
|
177
177
|
anonymous: true
|
|
178
|
-
|
|
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
|
-
|
|
186
|
-
|
|
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
|
-
|
|
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
|
-
/:
|
|
74
|
-
id:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
7
|
+
Given the annotation:
|
|
8
8
|
"""yaml
|
|
9
|
-
|
|
10
|
-
/:id:
|
|
9
|
+
/:
|
|
10
|
+
/hello/:id:
|
|
11
11
|
auth:id: id
|
|
12
|
-
GET:
|
|
12
|
+
GET:
|
|
13
|
+
dev:stub: Hello
|
|
13
14
|
"""
|
|
14
15
|
When the following request is received:
|
|
15
16
|
"""
|
|
16
|
-
GET /
|
|
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
|
|
35
|
+
And the annotation:
|
|
35
36
|
"""yaml
|
|
36
|
-
|
|
37
|
-
/:id:
|
|
37
|
+
/:
|
|
38
|
+
/hello/:id:
|
|
38
39
|
auth:id: id
|
|
39
|
-
GET:
|
|
40
|
+
GET:
|
|
41
|
+
dev:stub: Hello
|
|
40
42
|
"""
|
|
41
43
|
When the following request is received:
|
|
42
44
|
"""
|
|
43
|
-
GET /
|
|
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 /
|
|
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
|
-
/:
|
|
73
|
-
id:
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
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
|
-
@
|
|
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,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
|