@toa.io/extensions.exposition 0.8.3-dev.9 → 0.20.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/context.toa.yaml +15 -0
- package/components/identity.bans/manifest.toa.yaml +18 -0
- package/components/identity.basic/events/principal.js +9 -0
- package/components/identity.basic/manifest.toa.yaml +50 -0
- package/components/identity.basic/source/authenticate.ts +29 -0
- package/components/identity.basic/source/create.ts +19 -0
- package/components/identity.basic/source/transit.ts +64 -0
- package/components/identity.basic/source/types.ts +42 -0
- package/components/identity.basic/tsconfig.json +9 -0
- package/components/identity.roles/manifest.toa.yaml +31 -0
- package/components/identity.roles/source/list.ts +7 -0
- package/components/identity.roles/source/principal.ts +20 -0
- package/components/identity.roles/tsconfig.json +9 -0
- package/components/identity.tokens/manifest.toa.yaml +39 -0
- package/components/identity.tokens/receivers/identity.bans.updated.js +3 -0
- package/components/identity.tokens/source/authenticate.test.ts +56 -0
- package/components/identity.tokens/source/authenticate.ts +38 -0
- package/components/identity.tokens/source/decrypt.test.ts +59 -0
- package/components/identity.tokens/source/decrypt.ts +25 -0
- package/components/identity.tokens/source/encrypt.test.ts +35 -0
- package/components/identity.tokens/source/encrypt.ts +25 -0
- package/components/identity.tokens/source/revoke.ts +5 -0
- package/components/identity.tokens/source/types.ts +48 -0
- package/components/identity.tokens/tsconfig.json +9 -0
- package/cucumber.js +9 -0
- package/documentation/.assets/ia3-dark.jpg +0 -0
- package/documentation/.assets/ia3-light.jpg +0 -0
- package/documentation/.assets/overview-dark.jpg +0 -0
- package/documentation/.assets/overview-light.jpg +0 -0
- package/documentation/.assets/role-scopes-dark.jpg +0 -0
- package/documentation/.assets/role-scopes-light.jpg +0 -0
- package/documentation/.assets/rtd-dark.jpg +0 -0
- package/documentation/.assets/rtd-light.jpg +0 -0
- package/documentation/access.md +256 -0
- package/documentation/components.md +276 -0
- package/documentation/identity.md +156 -0
- package/documentation/protocol.md +15 -0
- package/documentation/query.md +226 -0
- package/documentation/tree.md +169 -0
- package/features/access.feature +442 -0
- package/features/annotation.feature +28 -0
- package/features/body.feature +21 -0
- package/features/directives.feature +56 -0
- package/features/dynamic.feature +89 -0
- package/features/errors.feature +208 -0
- package/features/identity.basic.feature +235 -0
- package/features/identity.feature +61 -0
- package/features/identity.roles.feature +51 -0
- package/features/identity.tokens.feature +116 -0
- package/features/queries.feature +214 -0
- package/features/routes.feature +49 -0
- package/features/steps/Common.ts +10 -0
- package/features/steps/Components.ts +43 -0
- package/features/steps/Database.ts +58 -0
- package/features/steps/Gateway.ts +105 -0
- package/features/steps/HTTP.ts +71 -0
- package/features/steps/Parameters.ts +12 -0
- package/features/steps/Workspace.ts +40 -0
- package/features/steps/components/greeter/manifest.toa.yaml +9 -0
- package/features/steps/components/greeter/operations/greet.js +7 -0
- package/features/steps/components/pots/manifest.toa.yaml +20 -0
- package/features/steps/components/users/manifest.toa.yaml +11 -0
- package/features/steps/tsconfig.json +9 -0
- package/package.json +31 -17
- package/readme.md +181 -0
- package/schemas/annotation.cos.yaml +4 -0
- package/schemas/directive.cos.yaml +3 -0
- package/schemas/method.cos.yaml +9 -0
- package/schemas/node.cos.yaml +5 -0
- package/schemas/query.cos.yaml +16 -0
- package/schemas/querystring.cos.yaml +5 -0
- package/schemas/range.cos.yaml +2 -0
- package/schemas/route.cos.yaml +2 -0
- package/source/Annotation.ts +6 -0
- package/source/Branch.ts +8 -0
- package/source/Composition.ts +58 -0
- package/source/Context.ts +6 -0
- package/source/Directive.test.ts +91 -0
- package/source/Directive.ts +120 -0
- package/source/Endpoint.ts +57 -0
- package/source/Factory.ts +53 -0
- package/source/Gateway.ts +93 -0
- package/source/HTTP/Server.fixtures.ts +45 -0
- package/source/HTTP/Server.test.ts +221 -0
- package/source/HTTP/Server.ts +135 -0
- package/source/HTTP/exceptions.ts +77 -0
- package/source/HTTP/formats/index.ts +19 -0
- package/source/HTTP/formats/json.ts +13 -0
- package/source/HTTP/formats/msgpack.ts +10 -0
- package/source/HTTP/formats/text.ts +9 -0
- package/source/HTTP/formats/yaml.ts +14 -0
- package/source/HTTP/index.ts +3 -0
- package/source/HTTP/messages.test.ts +116 -0
- package/source/HTTP/messages.ts +77 -0
- package/source/Mapping.ts +48 -0
- package/source/Query.test.ts +37 -0
- package/source/Query.ts +105 -0
- package/source/RTD/Context.ts +16 -0
- package/source/RTD/Directives.ts +9 -0
- package/source/RTD/Endpoint.ts +11 -0
- package/source/RTD/Match.ts +16 -0
- package/source/RTD/Method.ts +24 -0
- package/source/RTD/Node.ts +85 -0
- package/source/RTD/Route.ts +58 -0
- package/source/RTD/Tree.ts +57 -0
- package/source/RTD/factory.ts +47 -0
- package/source/RTD/index.ts +8 -0
- package/source/RTD/segment.test.ts +32 -0
- package/source/RTD/segment.ts +25 -0
- package/source/RTD/syntax/index.ts +2 -0
- package/source/RTD/syntax/parse.test.ts +188 -0
- package/source/RTD/syntax/parse.ts +153 -0
- package/source/RTD/syntax/types.ts +48 -0
- package/source/Remotes.test.ts +41 -0
- package/source/Remotes.ts +20 -0
- package/source/Tenant.ts +38 -0
- package/source/deployment.ts +42 -0
- package/source/directives/auth/Anonymous.ts +14 -0
- package/source/directives/auth/Echo.ts +12 -0
- package/source/directives/auth/Family.ts +145 -0
- package/source/directives/auth/Id.ts +19 -0
- package/source/directives/auth/Incept.ts +42 -0
- package/source/directives/auth/Role.test.ts +62 -0
- package/source/directives/auth/Role.ts +56 -0
- package/source/directives/auth/Rule.ts +28 -0
- package/source/directives/auth/Scheme.ts +26 -0
- package/source/directives/auth/index.ts +3 -0
- package/source/directives/auth/schemes.ts +8 -0
- package/source/directives/auth/split.ts +15 -0
- package/source/directives/auth/types.ts +37 -0
- package/source/directives/dev/Family.ts +34 -0
- package/source/directives/dev/Stub.ts +14 -0
- package/source/directives/dev/index.ts +3 -0
- package/source/directives/dev/types.ts +5 -0
- package/source/directives/index.ts +5 -0
- package/source/discovery.ts +1 -0
- package/source/exceptions.ts +17 -0
- package/source/index.test.ts +9 -0
- package/source/index.ts +6 -0
- package/source/manifest.test.ts +57 -0
- package/source/manifest.ts +35 -0
- package/source/root.ts +38 -0
- package/source/schemas.ts +9 -0
- package/transpiled/Annotation.d.ts +6 -0
- package/transpiled/Annotation.js +3 -0
- package/transpiled/Annotation.js.map +1 -0
- package/transpiled/Branch.d.ts +7 -0
- package/transpiled/Branch.js +3 -0
- package/transpiled/Branch.js.map +1 -0
- package/transpiled/Composition.d.ts +14 -0
- package/transpiled/Composition.js +43 -0
- package/transpiled/Composition.js.map +1 -0
- package/transpiled/Context.d.ts +5 -0
- package/transpiled/Context.js +3 -0
- package/transpiled/Context.js.map +1 -0
- package/transpiled/Directive.d.ts +32 -0
- package/transpiled/Directive.js +76 -0
- package/transpiled/Directive.js.map +1 -0
- package/transpiled/Endpoint.d.ts +20 -0
- package/transpiled/Endpoint.js +44 -0
- package/transpiled/Endpoint.js.map +1 -0
- package/transpiled/Factory.d.ts +11 -0
- package/transpiled/Factory.js +67 -0
- package/transpiled/Factory.js.map +1 -0
- package/transpiled/Gateway.d.ts +19 -0
- package/transpiled/Gateway.js +90 -0
- package/transpiled/Gateway.js.map +1 -0
- package/transpiled/HTTP/Server.d.ts +22 -0
- package/transpiled/HTTP/Server.fixtures.d.ts +12 -0
- package/transpiled/HTTP/Server.fixtures.js +36 -0
- package/transpiled/HTTP/Server.fixtures.js.map +1 -0
- package/transpiled/HTTP/Server.js +111 -0
- package/transpiled/HTTP/Server.js.map +1 -0
- package/transpiled/HTTP/exceptions.d.ts +39 -0
- package/transpiled/HTTP/exceptions.js +78 -0
- package/transpiled/HTTP/exceptions.js.map +1 -0
- package/transpiled/HTTP/formats/index.d.ts +8 -0
- package/transpiled/HTTP/formats/index.js +38 -0
- package/transpiled/HTTP/formats/index.js.map +1 -0
- package/transpiled/HTTP/formats/json.d.ts +4 -0
- package/transpiled/HTTP/formats/json.js +15 -0
- package/transpiled/HTTP/formats/json.js.map +1 -0
- package/transpiled/HTTP/formats/msgpack.d.ts +4 -0
- package/transpiled/HTTP/formats/msgpack.js +36 -0
- package/transpiled/HTTP/formats/msgpack.js.map +1 -0
- package/transpiled/HTTP/formats/text.d.ts +4 -0
- package/transpiled/HTTP/formats/text.js +13 -0
- package/transpiled/HTTP/formats/text.js.map +1 -0
- package/transpiled/HTTP/formats/yaml.d.ts +4 -0
- package/transpiled/HTTP/formats/yaml.js +39 -0
- package/transpiled/HTTP/formats/yaml.js.map +1 -0
- package/transpiled/HTTP/index.d.ts +3 -0
- package/transpiled/HTTP/index.js +20 -0
- package/transpiled/HTTP/index.js.map +1 -0
- package/transpiled/HTTP/messages.d.ts +27 -0
- package/transpiled/HTTP/messages.js +49 -0
- package/transpiled/HTTP/messages.js.map +1 -0
- package/transpiled/Mapping.d.ts +8 -0
- package/transpiled/Mapping.js +35 -0
- package/transpiled/Mapping.js.map +1 -0
- package/transpiled/Query.d.ts +13 -0
- package/transpiled/Query.js +107 -0
- package/transpiled/Query.js.map +1 -0
- package/transpiled/RTD/Context.d.ts +11 -0
- package/transpiled/RTD/Context.js +3 -0
- package/transpiled/RTD/Context.js.map +1 -0
- package/transpiled/RTD/Directives.d.ts +7 -0
- package/transpiled/RTD/Directives.js +3 -0
- package/transpiled/RTD/Directives.js.map +1 -0
- package/transpiled/RTD/Endpoint.d.ts +9 -0
- package/transpiled/RTD/Endpoint.js +3 -0
- package/transpiled/RTD/Endpoint.js.map +1 -0
- package/transpiled/RTD/Match.d.ts +11 -0
- package/transpiled/RTD/Match.js +3 -0
- package/transpiled/RTD/Match.js.map +1 -0
- package/transpiled/RTD/Method.d.ts +9 -0
- package/transpiled/RTD/Method.js +16 -0
- package/transpiled/RTD/Method.js.map +1 -0
- package/transpiled/RTD/Node.d.ts +21 -0
- package/transpiled/RTD/Node.js +61 -0
- package/transpiled/RTD/Node.js.map +1 -0
- package/transpiled/RTD/Route.d.ts +14 -0
- package/transpiled/RTD/Route.js +48 -0
- package/transpiled/RTD/Route.js.map +1 -0
- package/transpiled/RTD/Tree.d.ts +14 -0
- package/transpiled/RTD/Tree.js +45 -0
- package/transpiled/RTD/Tree.js.map +1 -0
- package/transpiled/RTD/factory.d.ts +6 -0
- package/transpiled/RTD/factory.js +36 -0
- package/transpiled/RTD/factory.js.map +1 -0
- package/transpiled/RTD/index.d.ts +8 -0
- package/transpiled/RTD/index.js +38 -0
- package/transpiled/RTD/index.js.map +1 -0
- package/transpiled/RTD/segment.d.ts +8 -0
- package/transpiled/RTD/segment.js +23 -0
- package/transpiled/RTD/segment.js.map +1 -0
- package/transpiled/RTD/syntax/index.d.ts +2 -0
- package/transpiled/RTD/syntax/index.js +19 -0
- package/transpiled/RTD/syntax/index.js.map +1 -0
- package/transpiled/RTD/syntax/parse.d.ts +4 -0
- package/transpiled/RTD/syntax/parse.js +128 -0
- package/transpiled/RTD/syntax/parse.js.map +1 -0
- package/transpiled/RTD/syntax/types.d.ts +41 -0
- package/transpiled/RTD/syntax/types.js +5 -0
- package/transpiled/RTD/syntax/types.js.map +1 -0
- package/transpiled/Remotes.d.ts +7 -0
- package/transpiled/Remotes.js +19 -0
- package/transpiled/Remotes.js.map +1 -0
- package/transpiled/Tenant.d.ts +12 -0
- package/transpiled/Tenant.js +30 -0
- package/transpiled/Tenant.js.map +1 -0
- package/transpiled/deployment.d.ts +3 -0
- package/transpiled/deployment.js +61 -0
- package/transpiled/deployment.js.map +1 -0
- package/transpiled/directives/auth/Anonymous.d.ts +6 -0
- package/transpiled/directives/auth/Anonymous.js +17 -0
- package/transpiled/directives/auth/Anonymous.js.map +1 -0
- package/transpiled/directives/auth/Echo.d.ts +6 -0
- package/transpiled/directives/auth/Echo.js +13 -0
- package/transpiled/directives/auth/Echo.js.map +1 -0
- package/transpiled/directives/auth/Family.d.ts +20 -0
- package/transpiled/directives/auth/Family.js +125 -0
- package/transpiled/directives/auth/Family.js.map +1 -0
- package/transpiled/directives/auth/Id.d.ts +7 -0
- package/transpiled/directives/auth/Id.js +17 -0
- package/transpiled/directives/auth/Id.js.map +1 -0
- package/transpiled/directives/auth/Incept.d.ts +10 -0
- package/transpiled/directives/auth/Incept.js +59 -0
- package/transpiled/directives/auth/Incept.js.map +1 -0
- package/transpiled/directives/auth/Role.d.ts +11 -0
- package/transpiled/directives/auth/Role.js +44 -0
- package/transpiled/directives/auth/Role.js.map +1 -0
- package/transpiled/directives/auth/Rule.d.ts +9 -0
- package/transpiled/directives/auth/Rule.js +22 -0
- package/transpiled/directives/auth/Rule.js.map +1 -0
- package/transpiled/directives/auth/Scheme.d.ts +7 -0
- package/transpiled/directives/auth/Scheme.js +47 -0
- package/transpiled/directives/auth/Scheme.js.map +1 -0
- package/transpiled/directives/auth/index.d.ts +2 -0
- package/transpiled/directives/auth/index.js +7 -0
- package/transpiled/directives/auth/index.js.map +1 -0
- package/transpiled/directives/auth/schemes.d.ts +3 -0
- package/transpiled/directives/auth/schemes.js +9 -0
- package/transpiled/directives/auth/schemes.js.map +1 -0
- package/transpiled/directives/auth/split.d.ts +2 -0
- package/transpiled/directives/auth/split.js +38 -0
- package/transpiled/directives/auth/split.js.map +1 -0
- package/transpiled/directives/auth/types.d.ts +31 -0
- package/transpiled/directives/auth/types.js +3 -0
- package/transpiled/directives/auth/types.js.map +1 -0
- package/transpiled/directives/dev/Family.d.ts +10 -0
- package/transpiled/directives/dev/Family.js +25 -0
- package/transpiled/directives/dev/Family.js.map +1 -0
- package/transpiled/directives/dev/Stub.d.ts +7 -0
- package/transpiled/directives/dev/Stub.js +14 -0
- package/transpiled/directives/dev/Stub.js.map +1 -0
- package/transpiled/directives/dev/index.d.ts +2 -0
- package/transpiled/directives/dev/index.js +7 -0
- package/transpiled/directives/dev/index.js.map +1 -0
- package/transpiled/directives/dev/types.d.ts +4 -0
- package/transpiled/directives/dev/types.js +3 -0
- package/transpiled/directives/dev/types.js.map +1 -0
- package/transpiled/directives/index.d.ts +2 -0
- package/transpiled/directives/index.js +10 -0
- package/transpiled/directives/index.js.map +1 -0
- package/transpiled/discovery.d.ts +1 -0
- package/transpiled/discovery.js +3 -0
- package/transpiled/discovery.js.map +1 -0
- package/transpiled/exceptions.d.ts +2 -0
- package/transpiled/exceptions.js +39 -0
- package/transpiled/exceptions.js.map +1 -0
- package/transpiled/index.d.ts +5 -0
- package/transpiled/index.js +12 -0
- package/transpiled/index.js.map +1 -0
- package/transpiled/manifest.d.ts +3 -0
- package/transpiled/manifest.js +30 -0
- package/transpiled/manifest.js.map +1 -0
- package/transpiled/root.d.ts +2 -0
- package/transpiled/root.js +39 -0
- package/transpiled/root.js.map +1 -0
- package/transpiled/schemas.d.ts +3 -0
- package/transpiled/schemas.js +14 -0
- package/transpiled/schemas.js.map +1 -0
- package/transpiled/tsconfig.tsbuildinfo +1 -0
- package/tsconfig.json +12 -0
- package/src/.manifest/index.js +0 -7
- package/src/.manifest/normalize.js +0 -58
- package/src/.manifest/schema.yaml +0 -69
- package/src/.manifest/validate.js +0 -17
- package/src/constants.js +0 -3
- package/src/deployment.js +0 -23
- package/src/exposition.js +0 -68
- package/src/factory.js +0 -76
- package/src/index.js +0 -9
- package/src/manifest.js +0 -12
- package/src/query/criteria.js +0 -55
- package/src/query/enum.js +0 -35
- package/src/query/index.js +0 -17
- package/src/query/query.js +0 -60
- package/src/query/range.js +0 -28
- package/src/query/sort.js +0 -19
- package/src/remote.js +0 -88
- package/src/server.js +0 -83
- package/src/tenant.js +0 -29
- package/src/translate/etag.js +0 -14
- package/src/translate/index.js +0 -7
- package/src/translate/request.js +0 -68
- package/src/translate/response.js +0 -62
- package/src/tree.js +0 -109
- package/test/manifest.normalize.fixtures.js +0 -37
- package/test/manifest.normalize.test.js +0 -37
- package/test/manifest.validate.test.js +0 -40
- package/test/query.range.test.js +0 -18
- package/test/tree.fixtures.js +0 -21
- package/test/tree.test.js +0 -44
- package/types/annotations.d.ts +0 -10
- package/types/declarations.d.ts +0 -31
- package/types/exposition.d.ts +0 -13
- package/types/http.d.ts +0 -13
- package/types/query.d.ts +0 -16
- package/types/remote.d.ts +0 -19
- package/types/server.d.ts +0 -13
- package/types/tree.d.ts +0 -33
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
Feature: Queries
|
|
2
|
+
|
|
3
|
+
Background:
|
|
4
|
+
Given the `pots` database contains:
|
|
5
|
+
| _id | title | volume | temperature |
|
|
6
|
+
| 4c4759e6f9c74da989d64511df42d6f4 | First pot | 100 | 80 |
|
|
7
|
+
| 99988d785d7d445cad45dbf8531f560b | Second pot | 200 | 30 |
|
|
8
|
+
| a7edded6b2ab47a0aca9508cc4da4138 | Third pot | 300 | 50 |
|
|
9
|
+
| bc6913d317334d76acd07d9f25f73535 | Fourth pot | 400 | 90 |
|
|
10
|
+
|
|
11
|
+
Scenario: Request with `id` query parameter
|
|
12
|
+
Given the `pots` is running with the following manifest:
|
|
13
|
+
"""yaml
|
|
14
|
+
exposition:
|
|
15
|
+
/pot:
|
|
16
|
+
GET: observe
|
|
17
|
+
"""
|
|
18
|
+
When the following request is received:
|
|
19
|
+
"""
|
|
20
|
+
GET /pots/pot/?id=99988d785d7d445cad45dbf8531f560b HTTP/1.1
|
|
21
|
+
accept: application/yaml
|
|
22
|
+
"""
|
|
23
|
+
Then the following reply is sent:
|
|
24
|
+
"""
|
|
25
|
+
200 OK
|
|
26
|
+
content-type: application/yaml
|
|
27
|
+
|
|
28
|
+
id: 99988d785d7d445cad45dbf8531f560b
|
|
29
|
+
title: Second pot
|
|
30
|
+
volume: 200
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
Scenario: Request with query criteria
|
|
34
|
+
Given the `pots` is running with the following manifest:
|
|
35
|
+
"""yaml
|
|
36
|
+
exposition:
|
|
37
|
+
/:
|
|
38
|
+
GET: enumerate
|
|
39
|
+
"""
|
|
40
|
+
When the following request is received:
|
|
41
|
+
"""
|
|
42
|
+
GET /pots/?criteria=volume<300&limit=10 HTTP/1.1
|
|
43
|
+
accept: application/yaml
|
|
44
|
+
"""
|
|
45
|
+
Then the following reply is sent:
|
|
46
|
+
"""
|
|
47
|
+
200 OK
|
|
48
|
+
content-type: application/yaml
|
|
49
|
+
|
|
50
|
+
- id: 4c4759e6f9c74da989d64511df42d6f4
|
|
51
|
+
title: First pot
|
|
52
|
+
volume: 100
|
|
53
|
+
- id: 99988d785d7d445cad45dbf8531f560b
|
|
54
|
+
title: Second pot
|
|
55
|
+
volume: 200
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
Scenario: Request with `omit` and `limit`
|
|
59
|
+
Given the `pots` is running with the following manifest:
|
|
60
|
+
"""yaml
|
|
61
|
+
exposition:
|
|
62
|
+
/:
|
|
63
|
+
GET: enumerate
|
|
64
|
+
"""
|
|
65
|
+
When the following request is received:
|
|
66
|
+
"""
|
|
67
|
+
GET /pots/?omit=1&limit=2 HTTP/1.1
|
|
68
|
+
accept: application/yaml
|
|
69
|
+
"""
|
|
70
|
+
Then the following reply is sent:
|
|
71
|
+
"""
|
|
72
|
+
200 OK
|
|
73
|
+
content-type: application/yaml
|
|
74
|
+
|
|
75
|
+
- id: 99988d785d7d445cad45dbf8531f560b
|
|
76
|
+
title: Second pot
|
|
77
|
+
volume: 200
|
|
78
|
+
- id: a7edded6b2ab47a0aca9508cc4da4138
|
|
79
|
+
title: Third pot
|
|
80
|
+
volume: 300
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
Scenario: Request with sorting
|
|
84
|
+
Given the `pots` is running with the following manifest:
|
|
85
|
+
"""yaml
|
|
86
|
+
exposition:
|
|
87
|
+
/:
|
|
88
|
+
GET: enumerate
|
|
89
|
+
"""
|
|
90
|
+
When the following request is received:
|
|
91
|
+
"""
|
|
92
|
+
GET /pots/?sort=volume:desc&limit=2 HTTP/1.1
|
|
93
|
+
accept: application/yaml
|
|
94
|
+
"""
|
|
95
|
+
Then the following reply is sent:
|
|
96
|
+
"""
|
|
97
|
+
200 OK
|
|
98
|
+
content-type: application/yaml
|
|
99
|
+
|
|
100
|
+
- id: bc6913d317334d76acd07d9f25f73535
|
|
101
|
+
title: Fourth pot
|
|
102
|
+
volume: 400
|
|
103
|
+
- id: a7edded6b2ab47a0aca9508cc4da4138
|
|
104
|
+
title: Third pot
|
|
105
|
+
volume: 300
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
Scenario: Request to a route with a path variable
|
|
109
|
+
Given the `pots` is running with the following manifest:
|
|
110
|
+
"""yaml
|
|
111
|
+
exposition:
|
|
112
|
+
/:id:
|
|
113
|
+
GET: observe
|
|
114
|
+
"""
|
|
115
|
+
When the following request is received:
|
|
116
|
+
"""
|
|
117
|
+
GET /pots/99988d785d7d445cad45dbf8531f560b/ HTTP/1.1
|
|
118
|
+
accept: application/yaml
|
|
119
|
+
"""
|
|
120
|
+
Then the following reply is sent:
|
|
121
|
+
"""
|
|
122
|
+
200 OK
|
|
123
|
+
content-type: application/yaml
|
|
124
|
+
|
|
125
|
+
id: 99988d785d7d445cad45dbf8531f560b
|
|
126
|
+
title: Second pot
|
|
127
|
+
volume: 200
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
Scenario: Request to a route with predefined criretia
|
|
131
|
+
Given the `pots` is running with the following manifest:
|
|
132
|
+
"""yaml
|
|
133
|
+
exposition:
|
|
134
|
+
/big:
|
|
135
|
+
GET:
|
|
136
|
+
endpoint: enumerate
|
|
137
|
+
query:
|
|
138
|
+
criteria: volume>200
|
|
139
|
+
"""
|
|
140
|
+
When the following request is received:
|
|
141
|
+
"""
|
|
142
|
+
GET /pots/big/ HTTP/1.1
|
|
143
|
+
accept: application/yaml
|
|
144
|
+
"""
|
|
145
|
+
Then the following reply is sent:
|
|
146
|
+
"""
|
|
147
|
+
200 OK
|
|
148
|
+
content-type: application/yaml
|
|
149
|
+
|
|
150
|
+
- id: a7edded6b2ab47a0aca9508cc4da4138
|
|
151
|
+
title: Third pot
|
|
152
|
+
volume: 300
|
|
153
|
+
- id: bc6913d317334d76acd07d9f25f73535
|
|
154
|
+
title: Fourth pot
|
|
155
|
+
volume: 400
|
|
156
|
+
"""
|
|
157
|
+
|
|
158
|
+
Scenario: Request to a route with combined criteria
|
|
159
|
+
Given the `pots` is running with the following manifest:
|
|
160
|
+
"""yaml
|
|
161
|
+
exposition:
|
|
162
|
+
/big:
|
|
163
|
+
GET:
|
|
164
|
+
endpoint: enumerate
|
|
165
|
+
query:
|
|
166
|
+
criteria: volume>200; # open criteria
|
|
167
|
+
"""
|
|
168
|
+
When the following request is received:
|
|
169
|
+
"""
|
|
170
|
+
GET /pots/big/?criteria=temperature>50 HTTP/1.1
|
|
171
|
+
accept: application/yaml
|
|
172
|
+
"""
|
|
173
|
+
Then the following reply is sent:
|
|
174
|
+
"""
|
|
175
|
+
200 OK
|
|
176
|
+
content-type: application/yaml
|
|
177
|
+
|
|
178
|
+
- id: bc6913d317334d76acd07d9f25f73535
|
|
179
|
+
title: Fourth pot
|
|
180
|
+
volume: 400
|
|
181
|
+
temperature: 90
|
|
182
|
+
"""
|
|
183
|
+
|
|
184
|
+
Scenario: Request to a route with predefined query
|
|
185
|
+
Given the `pots` is running with the following manifest:
|
|
186
|
+
"""yaml
|
|
187
|
+
exposition:
|
|
188
|
+
/hottest2:
|
|
189
|
+
GET:
|
|
190
|
+
endpoint: enumerate
|
|
191
|
+
query:
|
|
192
|
+
criteria: temperature>60
|
|
193
|
+
sort: temperature:desc
|
|
194
|
+
limit: 2
|
|
195
|
+
"""
|
|
196
|
+
When the following request is received:
|
|
197
|
+
"""
|
|
198
|
+
GET /pots/hottest2/ HTTP/1.1
|
|
199
|
+
accept: application/yaml
|
|
200
|
+
"""
|
|
201
|
+
Then the following reply is sent:
|
|
202
|
+
"""
|
|
203
|
+
200 OK
|
|
204
|
+
content-type: application/yaml
|
|
205
|
+
|
|
206
|
+
- id: bc6913d317334d76acd07d9f25f73535
|
|
207
|
+
title: Fourth pot
|
|
208
|
+
volume: 400
|
|
209
|
+
temperature: 90
|
|
210
|
+
- id: 4c4759e6f9c74da989d64511df42d6f4
|
|
211
|
+
title: First pot
|
|
212
|
+
volume: 100
|
|
213
|
+
temperature: 80
|
|
214
|
+
"""
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
Feature: Routes
|
|
2
|
+
|
|
3
|
+
Scenario Outline: Basic routes
|
|
4
|
+
Given the `greeter` is running with the following manifest:
|
|
5
|
+
"""yaml
|
|
6
|
+
namespace: basic
|
|
7
|
+
|
|
8
|
+
exposition:
|
|
9
|
+
/strict:
|
|
10
|
+
GET:
|
|
11
|
+
endpoint: greet
|
|
12
|
+
/shortcuts:
|
|
13
|
+
/operation:
|
|
14
|
+
GET: greet
|
|
15
|
+
"""
|
|
16
|
+
When the following request is received:
|
|
17
|
+
"""
|
|
18
|
+
GET /basic/greeter<route> HTTP/1.1
|
|
19
|
+
accept: text/plain
|
|
20
|
+
"""
|
|
21
|
+
Then the following reply is sent:
|
|
22
|
+
"""
|
|
23
|
+
200 OK
|
|
24
|
+
|
|
25
|
+
Hello
|
|
26
|
+
"""
|
|
27
|
+
Examples:
|
|
28
|
+
| route |
|
|
29
|
+
| /strict/ |
|
|
30
|
+
| /shortcuts/operation/ |
|
|
31
|
+
|
|
32
|
+
Scenario: Basic routes within default namespace
|
|
33
|
+
Given the `greeter` is running with the following manifest:
|
|
34
|
+
"""yaml
|
|
35
|
+
exposition:
|
|
36
|
+
/:
|
|
37
|
+
GET: greet
|
|
38
|
+
"""
|
|
39
|
+
When the following request is received:
|
|
40
|
+
"""
|
|
41
|
+
GET /greeter/ HTTP/1.1
|
|
42
|
+
accept: text/plain
|
|
43
|
+
"""
|
|
44
|
+
Then the following reply is sent:
|
|
45
|
+
"""
|
|
46
|
+
200 OK
|
|
47
|
+
|
|
48
|
+
Hello
|
|
49
|
+
"""
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { binding, given } from 'cucumber-tsflow'
|
|
2
|
+
import { timeout } from '@toa.io/generic'
|
|
3
|
+
|
|
4
|
+
@binding()
|
|
5
|
+
export class Common {
|
|
6
|
+
@given('after {float} second(s)')
|
|
7
|
+
public async timeout (interval: number): Promise<void> {
|
|
8
|
+
await timeout(interval * 1000)
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { after, binding, given } from 'cucumber-tsflow'
|
|
2
|
+
import * as boot from '@toa.io/boot'
|
|
3
|
+
import { timeout } from '@toa.io/generic'
|
|
4
|
+
import { type Connector } from '@toa.io/core'
|
|
5
|
+
import { parse } from '@toa.io/yaml'
|
|
6
|
+
import { Workspace } from './Workspace'
|
|
7
|
+
|
|
8
|
+
@binding([Workspace])
|
|
9
|
+
export class Components {
|
|
10
|
+
private readonly workspace: Workspace
|
|
11
|
+
private composition: Connector | null = null
|
|
12
|
+
|
|
13
|
+
public constructor (workspace: Workspace) {
|
|
14
|
+
this.workspace = workspace
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@given('the `{word}` is running')
|
|
18
|
+
public async run (name: string): Promise<void> {
|
|
19
|
+
await this.runComponent(name)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@given('the `{word}` is running with the following manifest:')
|
|
23
|
+
public async patchAndRun (name: string, yaml: string): Promise<void> {
|
|
24
|
+
const manifest = parse(yaml)
|
|
25
|
+
|
|
26
|
+
await this.runComponent(name, manifest)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@after()
|
|
30
|
+
@given('the `{word}` is stopped')
|
|
31
|
+
public async stop (_?: string): Promise<void> {
|
|
32
|
+
await this.composition?.disconnect()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private async runComponent (name: string, manifest?: object): Promise<void> {
|
|
36
|
+
const path = await this.workspace.addComponent(name, manifest)
|
|
37
|
+
|
|
38
|
+
this.composition = await boot.composition([path])
|
|
39
|
+
|
|
40
|
+
await this.composition.connect()
|
|
41
|
+
await timeout(50) // discovery
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { afterAll, beforeAll, binding, given } from 'cucumber-tsflow'
|
|
2
|
+
import { type DataTable } from '@cucumber/cucumber'
|
|
3
|
+
import { MongoClient } from 'mongodb'
|
|
4
|
+
|
|
5
|
+
@binding()
|
|
6
|
+
export class Database {
|
|
7
|
+
private static client: MongoClient
|
|
8
|
+
|
|
9
|
+
@given('the `{word}` database contains:')
|
|
10
|
+
public async upsert (id: string, table: DataTable): Promise<void> {
|
|
11
|
+
const [name, namespace = 'default'] = id.split('.').reverse()
|
|
12
|
+
const collection = Database.client.db(namespace).collection(name)
|
|
13
|
+
|
|
14
|
+
const columns = table.raw()[0]
|
|
15
|
+
const rows = table.rows()
|
|
16
|
+
const documents: Document[] = []
|
|
17
|
+
|
|
18
|
+
for (let r = 0; r < rows.length; r++) {
|
|
19
|
+
const document: Document = {}
|
|
20
|
+
|
|
21
|
+
for (let c = 0; c < columns.length; c++) {
|
|
22
|
+
const str = rows[r][c]
|
|
23
|
+
const int = parseInt(str)
|
|
24
|
+
|
|
25
|
+
document[columns[c]] = int.toString() === str ? int : str
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
documents.push(document)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
await collection.deleteMany({})
|
|
32
|
+
|
|
33
|
+
if (documents.length > 0)
|
|
34
|
+
await collection.insertMany(documents)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@given('the `{word}` database is empty')
|
|
38
|
+
public async truncate (id: string): Promise<void> {
|
|
39
|
+
const [name, namespace = 'default'] = id.split('.').reverse()
|
|
40
|
+
const collection = Database.client.db(namespace).collection(name)
|
|
41
|
+
|
|
42
|
+
await collection.deleteMany({})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@beforeAll()
|
|
46
|
+
public static async connect (): Promise<void> {
|
|
47
|
+
this.client = new MongoClient('mongodb://developer:secret@localhost:27017')
|
|
48
|
+
|
|
49
|
+
await this.client.connect()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@afterAll()
|
|
53
|
+
public static async disconnect (): Promise<void> {
|
|
54
|
+
await this.client.close()
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
type Document = Record<string, string | number>
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { after, afterAll, binding, given } from 'cucumber-tsflow'
|
|
2
|
+
import * as boot from '@toa.io/boot'
|
|
3
|
+
import { type Connector } from '@toa.io/core'
|
|
4
|
+
import { parse } from '@toa.io/yaml'
|
|
5
|
+
import { encode, timeout } from '@toa.io/generic'
|
|
6
|
+
import { Factory } from '../../source'
|
|
7
|
+
import * as syntax from '../../source/RTD/syntax'
|
|
8
|
+
import { shortcuts } from '../../source/Directive'
|
|
9
|
+
|
|
10
|
+
let instance: Connector | null = null
|
|
11
|
+
|
|
12
|
+
@binding()
|
|
13
|
+
export class Gateway {
|
|
14
|
+
private default: boolean = true
|
|
15
|
+
|
|
16
|
+
@given('the annotation:')
|
|
17
|
+
public async annotate (yaml: string): Promise<void> {
|
|
18
|
+
const annotation = parse(yaml)
|
|
19
|
+
const node = syntax.parse(annotation, shortcuts)
|
|
20
|
+
|
|
21
|
+
process.env.TOA_EXPOSITION = encode(node)
|
|
22
|
+
|
|
23
|
+
await Gateway.stop()
|
|
24
|
+
|
|
25
|
+
this.default = false
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@given('the `{word}` configuration:')
|
|
29
|
+
public async configure (id: string, yaml: string): Promise<void> {
|
|
30
|
+
const [name, namespace = 'default'] = id.split('.').reverse()
|
|
31
|
+
const key = `TOA_CONFIGURATION_${namespace.toUpperCase()}_${name.toUpperCase()}`
|
|
32
|
+
const def = DEFAULT_CONFIGURATION[id] ?? {}
|
|
33
|
+
const patch: object = parse(yaml)
|
|
34
|
+
const configuration = Object.assign({}, def, patch)
|
|
35
|
+
|
|
36
|
+
process.env[key] = encode(configuration)
|
|
37
|
+
|
|
38
|
+
await Gateway.stop()
|
|
39
|
+
|
|
40
|
+
this.default = false
|
|
41
|
+
}
|
|
42
|
+
|
|
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
|
+
|
|
59
|
+
public async start (): Promise<void> {
|
|
60
|
+
if (instance !== null)
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
process.env.TOA_EXPOSITION ??= DEFAULT_ANNOTATION
|
|
64
|
+
|
|
65
|
+
this.writeConfiguration()
|
|
66
|
+
|
|
67
|
+
const factory = new Factory(boot)
|
|
68
|
+
const service = factory.service()
|
|
69
|
+
|
|
70
|
+
if (service === null)
|
|
71
|
+
throw new Error('?')
|
|
72
|
+
|
|
73
|
+
instance = service
|
|
74
|
+
|
|
75
|
+
await service.connect()
|
|
76
|
+
await timeout(50) // resource discovery
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private writeConfiguration (): void {
|
|
80
|
+
for (const [id, configuration] of Object.entries(DEFAULT_CONFIGURATION)) {
|
|
81
|
+
const [name, namespace = 'default'] = id.split('.').reverse()
|
|
82
|
+
const key = `TOA_CONFIGURATION_${namespace.toUpperCase()}_${name.toUpperCase()}`
|
|
83
|
+
|
|
84
|
+
process.env[key] ??= encode(configuration)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const DEFAULT_ANNOTATION = encode({
|
|
90
|
+
routes: [],
|
|
91
|
+
methods: [],
|
|
92
|
+
directives: [
|
|
93
|
+
{
|
|
94
|
+
family: 'auth',
|
|
95
|
+
name: 'anonymous',
|
|
96
|
+
value: true
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
} satisfies syntax.Node)
|
|
100
|
+
|
|
101
|
+
const DEFAULT_CONFIGURATION: Record<string, object> = {
|
|
102
|
+
'identity.tokens': {
|
|
103
|
+
key0: 'k3.local.pIZT8-9Fa6U_QtfQHOSStfGtmyzPINyKQq2Xk-hd7vA'
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import * as assert from 'assert'
|
|
2
|
+
import { AssertionError } from 'assert'
|
|
3
|
+
import { binding, when, then } from 'cucumber-tsflow'
|
|
4
|
+
import * as http from '@toa.io/http'
|
|
5
|
+
import { trim } from '@toa.io/generic'
|
|
6
|
+
import { Parameters } from './parameters'
|
|
7
|
+
import { Gateway } from './Gateway'
|
|
8
|
+
|
|
9
|
+
@binding([Gateway, Parameters])
|
|
10
|
+
export class HTTP {
|
|
11
|
+
private readonly gateway: Gateway
|
|
12
|
+
private readonly origin: string
|
|
13
|
+
private response: string = ''
|
|
14
|
+
private readonly variables: Record<string, string> = {}
|
|
15
|
+
|
|
16
|
+
public constructor (gateway: Gateway, parameters: Parameters) {
|
|
17
|
+
this.gateway = gateway
|
|
18
|
+
this.origin = parameters.origin
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@when('the following request is received:')
|
|
22
|
+
public async request (input: string): Promise<any> {
|
|
23
|
+
let [headers, body] = trim(input).split('\n\n')
|
|
24
|
+
|
|
25
|
+
if (body !== undefined)
|
|
26
|
+
headers += '\ncontent-length: ' + body?.length
|
|
27
|
+
|
|
28
|
+
const text = headers + '\n\n' + (body ?? '')
|
|
29
|
+
const request = text.replaceAll(SUBSTITUTE, (_, name) => this.variables[name])
|
|
30
|
+
|
|
31
|
+
await this.gateway.start()
|
|
32
|
+
|
|
33
|
+
this.response = await http.request(request, this.origin)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@then('the following reply is sent:')
|
|
37
|
+
public responseIncludes (expected: string): void {
|
|
38
|
+
const lines = trim(expected).split('\n')
|
|
39
|
+
|
|
40
|
+
for (const line of lines) {
|
|
41
|
+
const escaped = line.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
42
|
+
|
|
43
|
+
const expression = escaped.replace(CAPTURE,
|
|
44
|
+
(_, name) => `(?<${name}>\\S{1,2048})`)
|
|
45
|
+
|
|
46
|
+
const rx = new RegExp(expression, 'i')
|
|
47
|
+
const match = this.response.match(rx)
|
|
48
|
+
|
|
49
|
+
if (match === null)
|
|
50
|
+
throw new AssertionError({ message: `Response is missing '${line}'\n${this.response}` })
|
|
51
|
+
|
|
52
|
+
Object.assign(this.variables, match.groups)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@then('the reply does not contain:')
|
|
57
|
+
public responseExcludes (expected: string): void {
|
|
58
|
+
const lines = trim(expected).split('\n')
|
|
59
|
+
|
|
60
|
+
for (const line of lines) {
|
|
61
|
+
line.replace(SUBSTITUTE, (_, name) => this.variables[name])
|
|
62
|
+
|
|
63
|
+
const includes = this.response.includes(line)
|
|
64
|
+
|
|
65
|
+
assert.equal(includes, false, `Response contains '${line}'\n${this.response}`)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const CAPTURE = /\\\$\\{\\{ (?<name>[A-Za-z_]{0,32}) \\}\\}/g
|
|
71
|
+
const SUBSTITUTE = /\${{ (?<name>[A-Za-z_]{0,32}) }}/g
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { setDefaultTimeout } from '@cucumber/cucumber'
|
|
2
|
+
|
|
3
|
+
export class Parameters {
|
|
4
|
+
public readonly origin: string
|
|
5
|
+
|
|
6
|
+
public constructor () {
|
|
7
|
+
this.origin = 'http://localhost:8000'
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
setDefaultTimeout(10 * 1000)
|
|
12
|
+
process.env.TOA_DEV = '1'
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { join } from 'node:path'
|
|
2
|
+
import { directory } from '@toa.io/filesystem'
|
|
3
|
+
import * as yaml from '@toa.io/yaml'
|
|
4
|
+
|
|
5
|
+
export class Workspace {
|
|
6
|
+
private root: string = devnull
|
|
7
|
+
|
|
8
|
+
public static exists
|
|
9
|
+
(target: Workspace, key: string, descriptor: PropertyDescriptor): PropertyDescriptor {
|
|
10
|
+
const method = descriptor.value
|
|
11
|
+
|
|
12
|
+
descriptor.value = async function (this: Workspace, ...args: any[]): Promise<any> {
|
|
13
|
+
if (this.root === devnull) this.root = await directory.temp()
|
|
14
|
+
|
|
15
|
+
return method.apply(this, args)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return descriptor
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@Workspace.exists
|
|
22
|
+
public async addComponent (name: string, patch?: object): Promise<string> {
|
|
23
|
+
const source = join(__dirname, 'components', name)
|
|
24
|
+
const target = join(this.root, name)
|
|
25
|
+
|
|
26
|
+
await directory.copy(source, target)
|
|
27
|
+
|
|
28
|
+
if (patch !== undefined) await this.patchManifest(target, patch)
|
|
29
|
+
|
|
30
|
+
return target
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private async patchManifest (target: string, patch: object): Promise<void> {
|
|
34
|
+
const path = join(target, 'manifest.toa.yaml')
|
|
35
|
+
|
|
36
|
+
await yaml.patch(path, patch)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const devnull = '/dev/null'
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
name: pots
|
|
2
|
+
|
|
3
|
+
entity:
|
|
4
|
+
schema:
|
|
5
|
+
title: string
|
|
6
|
+
volume: number
|
|
7
|
+
temperature?: number
|
|
8
|
+
|
|
9
|
+
operations:
|
|
10
|
+
transit:
|
|
11
|
+
concurrency: none
|
|
12
|
+
input:
|
|
13
|
+
title*: ~
|
|
14
|
+
volume*: ~
|
|
15
|
+
observe:
|
|
16
|
+
output:
|
|
17
|
+
id: string
|
|
18
|
+
title: string
|
|
19
|
+
volume: number
|
|
20
|
+
temperature: number
|