summaryrefslogtreecommitdiff
path: root/src/services/api/graphql/README.graphql
blob: 8414e795a7b1c96c4850ce370a1407a8754af37a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112

Example using GraphQL mutations to configure a DHCP server:

This assumes that the http-api is running:

'set service https api'

One can configure an address on an interface, and configure the DHCP server
to run with that address as default router by requesting these 'mutations':

mutation {
  createInterfaceEthernet (data: {interface: "eth1",
                                  address: "192.168.0.1/24",
                                  description: "BOB"}) {
    success
    errors
    data {
      address
    }
  }
}

mutation {
  createDhcpServer(data: {sharedNetworkName: "BOB",
                          subnet: "192.168.0.0/24",
                          defaultRouter: "192.168.0.1",
                          dnsServer: "192.168.0.1",
                          domainName: "vyos.net",
                          lease: 86400,
                          range: 0,
                          start: "192.168.0.9",
                          stop: "192.168.0.254",
                          dnsForwardingAllowFrom: "192.168.0.0/24",
                          dnsForwardingCacheSize: 0,
                          dnsForwardingListenAddress: "192.168.0.1"}) {
    success
    errors
    data {
      defaultRouter
    }
  }
}

These requests can be set in the GraphQL playground, or made with curl:

curl -k -H 'Content-Type: application/json' -d '{"query": "{hello}"}' https://{{ host_address }}/graphql

replacing the query with the above mutations as JSON.

What's here:

services
├── api
│   └── graphql
│       ├── graphql
│       │   ├── directives.py
│       │   ├── __init__.py
│       │   ├── mutations.py
│       │   └── schema
│       │       ├── dhcp_server.graphql
│       │       ├── interface_ethernet.graphql
│       │       └── schema.graphql
│       ├── recipes
│       │   ├── dhcp_server.py
│       │   ├── __init__.py
│       │   ├── interface_ethernet.py
│       │   ├── recipe.py
│       │   └── templates
│       │       ├── dhcp_server.tmpl
│       │       └── interface_ethernet.tmpl
│       └── state.py
├── vyos-configd
├── vyos-hostsd
└── vyos-http-api-server

The GraphQL library that we are using, Ariadne, advertises itself as a
'schema-first' implementation: define the schema; define resolvers
(handlers) for declared Query and Mutation types (Subscription types are not
currently used).

In the current approach to a high-level API, we consider the
Jinja2-templated collection of configuration mode 'set'/'delete' commands as
the Ur-data; the GraphQL schema is produced from those files, located in
'api/recipes/templates'.

Resolvers for the schema Mutation fields are dynamically generated using a
'directive' added to the respective schema field. The directive,
'@generate', is handled by the class 'DataDirective' in
'api/graphql/directives.py', which calls the 'make_resolver' function in
'api/graphql/mutations.py'; the produced resolver calls the appropriate
wrapper in 'api/recipes', with base class doing the (overridable)
configuration steps of calling all defined 'set'/'delete' commands.

Integrating the above with vyos-http-api-server is ~10 lines of code.

What needs to be done:

• automate generation of schema and wrappers from templated configuration
commands

• investigate whether the inversion of control provided by the named
wrappers in 'api/recipes' is sufficient for use cases which need to modify
data

• encapsulate the manipulation of 'canonical names' which transforms the
prefixed camel-case schema names to various snake-case file/function names

• consider mechanism for migration of templates: offline vs. on-the-fly

• define the naming convention for those schema fields that refer to
configuration mode parameters: e.g. how much of the path is needed as prefix
to uniquely define the term