Skip to content

Commit ef59c3f

Browse files
committed
updates the Python transform guide
1 parent 65d6042 commit ef59c3f

File tree

1 file changed

+98
-75
lines changed

1 file changed

+98
-75
lines changed

docs/docs/guides/python-transform.mdx

Lines changed: 98 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,46 @@ The goal of this guide is to develop a Python Transform and add it to Infrahub,
1818
6. Add the repository to Infrahub as an external repository
1919
7. Validate that the transform works by using the transform API endpoint
2020

21-
In this guide we are going to work with the builtin `Tag` objects in Infrahub. It won't provide a transform that is very useful, the goal is instead to show how the transforms are created. Once you have mastered the basics you will be ready to go on to create more advanced transforms.
21+
## 1. Loading a schema
2222

23-
## 1. Creating a query to collect the desired data
23+
In this guide we are going to work with a very simplistic network device model. I won't provide a transform that is very useful, the goal is instead to show how the transforms are created. Once you have mastered the basics you will be ready to go on to create more advanced transforms.
24+
25+
```yaml
26+
---
27+
version: "1.0"
28+
nodes:
29+
- name: Device
30+
namespace: Network
31+
display_labels:
32+
- name__value
33+
attributes:
34+
- name: name
35+
kind: Text
36+
label: Name
37+
optional: false
38+
unique: true
39+
- name: description
40+
kind: Text
41+
label: Description
42+
optional: true
43+
```
44+
45+
Store the schema as a YAML file on your local disk, and load the schema into Infrahub using the following command
46+
47+
```bash
48+
infrahubctl schema load /path/to/schema.yml
49+
```
50+
51+
## 2. Creating a query to collect the desired data
2452

2553
As the first step we need to have some data in the database to actually query.
2654

27-
Create three tags, called "red", "green", "blue", either using the frontend or by submitting three GraphQL mutations as per below (just swapping out the name of the color each time).
55+
Create three devices, called "switch1", "switch2", "switch3", either using the frontend or by submitting three GraphQL mutations as per below (just swapping out the name of the color each time).
2856

29-
```graphql #1-2
30-
mutation CreateTags {
31-
BuiltinTagCreate(
32-
data: {name: {value: "red"}, description: {value: "The red tag"}}
57+
```graphql
58+
mutation CreateDevice {
59+
NetworkDeviceCreate(
60+
data: {name: {value: "switch1"}, description: {value: "This is device switch1"}}
3361
) {
3462
ok
3563
object {
@@ -41,9 +69,9 @@ mutation CreateTags {
4169

4270
The next step is to create a query that returns the data we just created. The rest of this guide assumes that the following query will return a response similar to the response below the query.
4371

44-
```graphql #1-2
45-
query TagsQuery {
46-
BuiltinTag {
72+
```graphql
73+
query DeviceQuery {
74+
NetworkDevice {
4775
edges {
4876
node {
4977
name {
@@ -63,35 +91,35 @@ Response to the tags query:
6391
```json
6492
{
6593
"data": {
66-
"BuiltinTag": {
94+
"NetworkDevice": {
6795
"edges": [
6896
{
6997
"node": {
7098
"name": {
71-
"value": "blue"
99+
"value": "switch1"
72100
},
73101
"description": {
74-
"value": "The blue tag"
102+
"value": "This is device switch1"
75103
}
76104
}
77105
},
78106
{
79107
"node": {
80108
"name": {
81-
"value": "green"
109+
"value": "switch2"
82110
},
83111
"description": {
84-
"value": "The green tag"
112+
"value": "This is device switch2"
85113
}
86114
}
87115
},
88116
{
89117
"node": {
90118
"name": {
91-
"value": "red"
119+
"value": "switch3"
92120
},
93121
"description": {
94-
"value": "The red tag"
122+
"value": "This is device switch3"
95123
}
96124
}
97125
}
@@ -101,19 +129,20 @@ Response to the tags query:
101129
}
102130
```
103131

104-
While it would be possible to create a transform that targets all of these tags, for example if you want to create a report, the goal for us is to be able to focus on one of these objects. For this reason we need to modify the query from above to take an input parameter so that we can filter the result to what we want.
132+
While it would be possible to create a transform that targets all of these devices, for example if you want to create a report, the goal for us is to be able to focus on one of these objects. For this reason we need to modify the query from above to take an input parameter so that we can filter the result to what we want.
105133

106-
Create a local directory on your computer:
134+
Create a local directory on your computer.
107135

108136
```shell
109-
mkdir tags_transform
137+
mkdir device_config_render
110138
```
111139

112-
Then save the below query as a text file named `tags_query.gql`:
140+
Then save the below query as a text file named `device_config.gql`.
113141

114-
```graphql #1-2
115-
query TagsQuery($tag: String!) {
116-
BuiltinTag(name__value: $tag) {
142+
143+
```graphql
144+
query DeviceQuery($name: String!) {
145+
NetworkDevice(name__value: $name) {
117146
edges {
118147
node {
119148
name {
@@ -128,35 +157,34 @@ query TagsQuery($tag: String!) {
128157
}
129158
```
130159

131-
Here the query will require an input parameter called `$name` what will refer to the name of each tag. When we want to query for the red tag the input variables to the query would look like this:
160+
Here the query will require an input parameter called `$name` what will refer to the name of each device. When we want to query for device switch1, the input variables to the query would look like this:
132161

133162
```json
134163
{
135-
"tag": "red"
164+
"name": "switch1"
136165
}
137166
```
138167

139168
## 2. Create the Python transform file
140169

141-
The next step is to create the actual Python transform. The transform is a Python class that inherits from InfrahubTransform from the [Python SDK]($(local_base_url_1)python-sdk). Create a file called `tags_transform.py`:
170+
The next step is to create the actual Python transform. The transform is a Python class that inherits from InfrahubTransform from the [Python SDK]($(local_base_url_1)python-sdk). Create a file called `device_config.py`:
142171

143172
```python
144173
from infrahub_sdk.transforms import InfrahubTransform
145174

146175

147-
class TagsTransform(InfrahubTransform):
176+
class DeviceConfigTransform(InfrahubTransform):
148177

149-
query = "tags_query"
150-
url = "my-tags"
178+
query = "device_config_query"
151179

152180
async def transform(self, data):
153-
tag = data["BuiltinTag"]["edges"][0]["node"]
154-
tag_name = tag["name"]["value"]
155-
tag_description = tag["description"]["value"]
181+
device = data["NetworkDevice"]["edges"][0]["node"]
182+
device_name = device["name"]["value"]
183+
device_description = device["description"]["value"]
156184

157185
return {
158-
"tag_title": tag_name.title(),
159-
"bold_description": f"*{tag_description}*".upper()
186+
"device_hostname": device_name,
187+
"device_description": f"*{device_description}*"
160188
}
161189
```
162190

@@ -171,37 +199,36 @@ from infrahub_sdk.transforms import InfrahubTransform
171199
2. We define our own class based on InfrahubTransform.
172200

173201
```python
174-
class TagsTransform(InfrahubTransform):
202+
class DeviceConfigTransform(InfrahubTransform):
175203
```
176204

177205
Here we need to note the name of the class as we will need it later, optionally we can just call it `Transform` which is the default name.
178206

179207
3. We define where data comes from and what API endpoint to use.
180208

181209
```python
182-
query = "tags_query"
183-
url = "my-tags"
210+
query = "device_config_query"
184211
```
185212

186-
The query part refers to the file tags_query.gql that we created earlier. The URL parameter controls what the endpoint will be when you run this transform by targeting the API server.
213+
The query part refers to the the query that we will define in the `.infrahub.yml` repository configuration file later in the guide.
187214

188-
With this configuration the endpoint of our transform will be [http://localhost:8000/api/transform/my-tags](http://localhost:8000/api/transform/my-tags).
215+
With this configuration the endpoint of our transform will be [http://localhost:8000/api/transform/python/device_config_transform](http://localhost:8000/api/transform/python/device_config_transform).
189216

190217
4. The transform method
191218

192219
```python
193220
async def transform(self, data):
194-
tag = data["BuiltinTag"]["edges"][0]["node"]
195-
tag_name = tag["name"]["value"]
196-
tag_description = tag["description"]["value"]
221+
device = data["BuiltinTag"]["edges"][0]["node"]
222+
device_name = device["name"]["value"]
223+
device_description = device["description"]["value"]
197224

198225
return {
199-
"tag_title": tag_name.title(),
200-
"bold_description": f"*{tag_description}*".upper()
226+
"device_hostname": device_name,
227+
"device_description": f"*{device_description}*"
201228
}
202229
```
203230

204-
When running the transform the `data` input variable will consist of the response to the query we created. In this case we return a JSON object consisting of two keys `tags_title` and `bold_description` where we have modified the data in some way. Here you would return data in the format you need.
231+
When running the transform the `data` input variable will consist of the response to the query we created. In this case we return a JSON object consisting of two keys `device_hostname` and `device_description` where we have modified the data in some way. Here you would return data in the format you need.
205232

206233
:::info
207234

@@ -210,9 +237,9 @@ If you are unsure of the format of the data you can set a debug marker when test
210237
```python
211238
async def transform(self, data):
212239
breakpoint()
213-
tag = data["BuiltinTag"]["edges"][0]["node"]
214-
tag_name = tag["name"]["value"]
215-
tag_description = tag["description"]["value"]
240+
device = data["BuiltinTag"]["edges"][0]["node"]
241+
device_name = device["name"]["value"]
242+
device_description = device["description"]["value"]
216243
```
217244

218245
:::
@@ -226,18 +253,18 @@ Create a `.infrahub.yml` file in the root of the directory.
226253
```yaml
227254
---
228255
python_transforms:
229-
- name: tags_transform
230-
class_name: TagsTransform
231-
file_path: "tags_transform.py"
256+
- name: device_config_transform
257+
class_name: DeviceConfigTransform
258+
file_path: "device_config.py"
232259

233260
queries:
234-
- name: tags_query
235-
file_path: "tags_query.gql"
261+
- name: device_config_query
262+
file_path: "device_config.gql"
236263
```
237264
238265
<Tabs>
239266
<TabItem value="Python transform" default>
240-
Two parts here are required, first the `name` of the transform which should be unique across Infrahub and also the `file_path` that should point to the Python file within the repository. In this example we have also defined `class_name`, the reason for this is that we gave our class the name `TagsTransform` instead of the default `Transform`.
267+
Two parts here are required, first the `name` of the transform which should be unique across Infrahub and also the `file_path` that should point to the Python file within the repository. In this example we have also defined `class_name`, the reason for this is that we gave our class the name `DeviceConfigTransform` instead of the default `Transform`.
241268
</TabItem>
242269
<TabItem value="Queries">
243270
Here the `name` refers to the query's name and `file_path` should point to the GraphQL file within the repository.
@@ -248,10 +275,10 @@ See [this topic](../topics/infrahub-yml) for a full explanation of everything th
248275

249276
## 4. Create a Git repository
250277

251-
Within the `tags_transform` folder you should now have 3 files:
278+
Within the `device_config_render` folder you should now have 3 files:
252279

253-
* `tags_query.gql`: Contains the GraphQL query
254-
* `tags_transform.py`: Contains the Python code for the transform
280+
* `device_config.gql`: Contains the GraphQL query
281+
* `device_config.py`: Contains the Python code for the transform
255282
* `.infrahub.yml`: Contains the definition for the transform
256283

257284
Before we can test our transform we must add the files to a local Git repository.
@@ -268,29 +295,26 @@ Using infrahubctl you can first verify that the `.infrahub.yml` file is formatte
268295

269296
```shell title="❯ infrahubctl transform --list"
270297
Python transforms defined in repository: 1
271-
tags_transform (tags_transform.py::TagsTransform)
298+
device_config_transform (device_config.py::DeviceConfigTransform)
272299
```
273300

274301
:::info
275302
Trying to run the transform with just the name will produce an error.
276303

277-
```shell title="❯ infrahubctl transform tags_transform"
278-
1 error(s) occurred while executing the query
279-
- Message: Variable '$tag' of required type 'String!' was not provided.
280-
Location: [{'line': 1, 'column': 17}]
281-
Aborted.
304+
```shell title="❯ infrahubctl transform device_config_transform"
305+
{'message': "Variable '$name' of required type 'String!' was not provided.", 'locations': [{'line': 1, 'column': 19}]}
282306
```
283307

284-
Here we can see that our query is missing the required input for `$tag` which is needed to filter the data.
308+
Here we can see that our query is missing the required input for `$name` which is needed to filter the data.
285309

286310
:::
287311

288312
Run the transform and specify the variable name along with the tag we want to target.
289313

290-
```json title="❯ infrahubctl transform tags_transform tag=red"
314+
```json title="❯ infrahubctl transform device_config_transform name=switch2"
291315
{
292-
"tag_title": "Red",
293-
"bold_description": "*THE RED TAG*"
316+
"device_description": "*This is device switch2*",
317+
"device_hostname": "switch2"
294318
}
295319
```
296320

@@ -304,17 +328,16 @@ In order to avoid having the same instructions over and over please refer to the
304328

305329
Once the repository is synced to Infrahub you can access the transform from the API:
306330

307-
```json title="❯ curl http://localhost:8000/api/transform/my-tags?tag=blue"
331+
```json title="❯ curl http://localhost:8000/api/transform/python/device_config_transform?name=switch2"
308332
{
309-
"tag_title": "Blue",
310-
"bold_description": "*THE BLUE TAG*"
333+
"device_hostname":"switch2",
334+
"device_description":"*This is device switch2*"
311335
}
312336
```
313337

314-
```json title="❯ curl http://localhost:8000/api/transform/my-tags?tag=red"
338+
```json title="❯ curl http://localhost:8000/api/transform/python/device_config_transform?name=switch3"
315339
{
316-
"tag_title": "Red",
317-
"bold_description": "*THE RED TAG*"
340+
"device_hostname":"switch3",
341+
"device_description":"*This is device switch3*"
318342
}
319-
320343
```

0 commit comments

Comments
 (0)