docs: rewrite JsonRpcBundle docs, add SDK docs, move them under docs/api
en/docs/api/json-rpc-bundle/functionality/groups.md
new file +132
ā0
1
+
# API groups
2
+
3
+
:::info
4
+
Available since version 10.2. Applications without groups work as before: everything belongs to the default `general` group.
5
+
:::
6
+
7
+
Groups let you have several RPC APIs in one application ā for example, a public one for clients, an internal one for the admin panel and separate BFFs. Each group has its own address, set of methods, versions, documentation and, if needed, its own token validation.
8
+
9
+
## Entry points
10
+
11
+
Import the bundle routes once for each API. `prefix` sets the address, and `defaults.api_group` sets the group (without it ā `general`):
12
+
13
+
```yaml
14
+
# config/routes/ufo_json_rpc.yaml
15
+
ufo_json_rpc:
16
+
resource: ../../vendor/ufo-tech/json-rpc-bundle/config/router.yaml
17
+
prefix: /rpc
18
+
trailing_slash_on_root: false
19
+
20
+
ufo_json_rpc_internal:
21
+
resource: ../../vendor/ufo-tech/json-rpc-bundle/config/router.yaml
22
+
prefix: /rpc/internal
23
+
defaults:
24
+
api_group: internal
25
+
trailing_slash_on_root: false
26
+
27
+
ufo_json_rpc_public:
28
+
resource: ../../vendor/ufo-tech/json-rpc-bundle/config/router.yaml
29
+
prefix: /rpc/public
30
+
defaults:
31
+
api_group: public
32
+
trailing_slash_on_root: false
33
+
```
34
+
35
+
`POST /rpc/internal` executes a request, `GET /rpc/internal` returns OpenRPC, `GET /rpc/internal/postman` returns Postman; versioned addresses are `/rpc/internal/v2` and so on. A group's documentation contains only its own methods and versions.
36
+
37
+
Route names get the group prefix: `general_ufo_rpc_api_server`, `internal_ufo_rpc_api_server`, ⦠The old names without a prefix remain aliases for `general`, so `router->generate('ufo_rpc_api_server')` works as before.
38
+
39
+
## `#[RPC\APIGroups]`
40
+
41
+
The attribute on a class or method defines which groups the method belongs to:
42
+
43
+
```php
44
+
public function __construct(array $groups = [], bool $withDefault = true)
45
+
```
46
+
47
+
`withDefault: true` adds the `general` group, `false` does not.
48
+
49
+
```php
50
+
use Ufo\JsonRpcBundle\ApiMethod\Interfaces\IRpcService;
51
+
use Ufo\RpcObject\RPC;
52
+
53
+
#[RPC\Info('catalog')]
54
+
#[RPC\APIGroups(['internal', 'public'], false)]
55
+
class CatalogApi implements IRpcService
56
+
{
57
+
public function list(): array { /* ... */ } // internal and public
58
+
59
+
#[RPC\APIGroups(['internal'], false)]
60
+
public function updatePrice(int $productId, int $price): true { return true; } // internal only
61
+
62
+
#[RPC\APIGroups(['public'], false)]
63
+
public function featured(): array { return [1]; } // public only
64
+
65
+
#[RPC\APIGroups()]
66
+
public function status(): string { return 'ok'; } // general only
67
+
68
+
#[RPC\APIGroups(['internal'], false)]
69
+
#[RPC\IgnoreApi]
70
+
public function rebuildIndex(): true { return true; } // not published anywhere
71
+
}
72
+
```
73
+
74
+
| Method | `/rpc` (general) | `/rpc/internal` | `/rpc/public` |
75
+
| --- | --- | --- | --- |
76
+
| `catalog.list` | ā | yes | yes |
77
+
| `catalog.updatePrice` | ā | yes | ā |
78
+
| `catalog.featured` | ā | ā | yes |
79
+
| `catalog.status` | yes | ā | ā |
80
+
| `catalog.rebuildIndex` | ā | ā | ā |
81
+
82
+
Rules:
83
+
84
+
* a method attribute fully replaces the class groups; without attributes a method belongs to `general`;
85
+
* `APIGroups([], false)` does not publish the method in any group; `#[RPC\IgnoreApi]` takes precedence over groups;
86
+
* `general` is not "all groups" but an ordinary default group;
87
+
* method maps and [versions](/docs/api/json-rpc-bundle/functionality/versions) are separated by group: a method from another group returns `Method not found`;
88
+
* for HTTP the group is determined by the route, not by a field in the JSON.
89
+
90
+
## Tokens for groups
91
+
92
+
Without additional configuration, all groups are checked by the base validator (`security.clients_tokens` or your `ITokenValidator`). Separate validation for groups is set with `security.providers`. Each provider has **exactly one** of the `service` or `clients_tokens` parameters and a non-empty `api_groups`.
93
+
94
+
```yaml
95
+
ufo_json_rpc:
96
+
security:
97
+
protected_api: true
98
+
token_name: 'Ufo-RPC-Token'
99
+
clients_tokens:
100
+
- '%env(resolve:UFO_API_TOKEN)%' # general and groups without a provider
101
+
providers:
102
+
internal:
103
+
api_groups: [internal]
104
+
clients_tokens:
105
+
- '%env(resolve:UFO_INTERNAL_API_TOKEN)%'
106
+
public:
107
+
service: App\Security\UserTokenValidator # implements ITokenValidator
108
+
api_groups: [public]
109
+
```
110
+
111
+
* A provider replaces the base validation for its groups; if it rejects a token, the base validation is not run.
112
+
* Two providers for the same group, a missing service or a class without `ITokenValidator` cause a container compilation error.
113
+
* `protected_api`, `protected_doc` and `token_name` are shared by all groups; protected documentation is checked by the provider of its group.
114
+
115
+
## Console and async
116
+
117
+
```bash
118
+
bin/console ufo:rpc:process '{"jsonrpc":"2.0","id":1,"method":"catalog.updatePrice","params":{"productId":1,"price":100}}' \
119
+
--api-group=internal --ver=v1
120
+
```
121
+
122
+
Batch child processes keep the group and version of the route. For Symfony Messenger the group is passed explicitly:
123
+
124
+
```php
125
+
$message = new \Ufo\RpcObject\RpcAsyncRequest($request, $token, apiGroup: 'internal');
126
+
```
127
+
128
+
Messages without a group are executed in `general`.
129
+
130
+
## Translations for a group
131
+
132
+
Method names and descriptions in the documentation can be overridden for a specific group with `translations/rpc/<group>.<locale>.yaml` files. See [API documentation](/docs/api/json-rpc-bundle/documentation).