diff --git a/.vale/styles/Infrahub/spelling.yml b/.vale/styles/Infrahub/spelling.yml
index 252b2269f8..5fbbbcddb9 100644
--- a/.vale/styles/Infrahub/spelling.yml
+++ b/.vale/styles/Infrahub/spelling.yml
@@ -4,5 +4,6 @@ message: "Did you really mean '%s'?"
level: error
filters:
- '[pP]y.*\b'
- - '\bimport_.*\b' # New filter to ignore variables starting with 'import_'
+ - '\bimport_.*\b' # New filter to ignore variables starting with 'import_'
+ - '\w+__value' # New filter to skip Infrahub filters in documentation (name__value)
ignore: spelling-exceptions.txt
diff --git a/docs/docs/guides/generator.mdx b/docs/docs/guides/generator.mdx
index 6e8b6ddae0..cc8c7342d8 100644
--- a/docs/docs/guides/generator.mdx
+++ b/docs/docs/guides/generator.mdx
@@ -10,12 +10,12 @@ Within Infrahub a generator is defined in an [external repository](/topics/repos
The goal of this guide is to develop a Generator and add it to Infrahub, we will achieve this by following these steps.
-1. Identify the relevant data you want to extract from the database using a [GraphQL query](/topics/graphql), that can take an input parameter to filter the data
-2. Write a Python script that uses the GraphQL query to read information from the system and generates new data based on the response
-3. Create an entry for the generator within an .infrahub.yml file.
-4. Create a Git repository
+1. Create a Git repository
+2. Create an entry for the generator within an .infrahub.yml file.
+3. Identify the relevant data you want to extract from the database using a [GraphQL query](/topics/graphql), that can take an input parameter to filter the data
+4. Write a Python script that uses the GraphQL query to read information from the system and generates new data based on the response
5. Test the generator with infrahubctl
-6. Add the repository to Infrahub as an external repository
+6. Push changes to GitHub and add the repository to Infrahub as an external repository
7. Validate that the generator works by triggering it through a proposed change
## Preparations
@@ -24,9 +24,11 @@ What your generator will look like will depend on what your schema looks like an
As the default Infrahub schema doesn't have a lot of object types to use as a test, we will illustrate how this could work by adding two nodes to the schema.
-Load the following schema using the [infrahubctl schema](/infrahubctl/infrahubctl-schema) command.
+### Create and load example schema
+
+Create a **widgets.yml** file with the following:
-```yaml
+```yaml title="widgets.yml"
# yaml-language-server: $schema=https://schema.infrahub.app/infrahub/schema/latest.json
---
version: '1.0'
@@ -56,90 +58,99 @@ nodes:
unique: true
```
-Perform these steps in the frontend.
-
-1. Create two new widget objects
- - One with the name `widget1` and count 1
- - One with the name `widget2` and count 2
-2. Create a Standard group called "widgets"
-3. Add both of the created objects to the new group
-
-## 1. Identify the relevant data
-
-Here we define a GraphQL query that we will use to gather information.
+Load the following schema using the [infrahubctl schema](/infrahubctl/infrahubctl-schema) command.
-```graphql
-query Widgets($name: String!) {
- TestWidget(name__value: $name) {
- edges {
- node {
- name {
- value
- }
- count {
- value
- }
- }
- }
- }
-}
+```shell
+infrahubctl schema load widgets.yml
```
-Create a local directory on your computer where we will store the generator files.
-
```shell
-mkdir example_generator
+schema 'widgets.yml' loaded successfully
+1 schema processed in 8.453 seconds.
```
-Within that directory store the above GraphQL query as widget_query.gql.
-
-## 2. Create a Python Generator
+### Add two new nodes
-The Generator class needs to implement a `generate` function that receives a `data` parameter that contains the response from the GraphQL query.
+Perform these steps in the [Infrahub UI](http://localhost:8000).
-The goal of this generator will be to create a number of resources that depends on the set count of the widgets.
+1. Create two new widget objects
+ - One with the name `widget1` and count 1
+ - One with the name `widget2` and count 2
+2. Create a [Standard group](http://localhost:8000/objects/CoreGroup) called "**widgets**
+3. Add both of the created objects to the new group
-```python
-from infrahub_sdk.generator import InfrahubGenerator
+:::info
+Any widget object must be added to or be part of the **widgets** group to be tied to the generator.
+:::
+## 1. Create GitHub repository
-class WidgetGenerator(InfrahubGenerator):
- async def generate(self, data: dict) -> None:
- widget = data["TestWidget"]["edges"][0]["node"]
- widget_name: str = widget["name"]["value"]
- widget_count: str = widget["count"]["value"]
+Follow GitHub's instructions on [creating a new repository](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-new-repository).
- for count in range(1, widget_count + 1):
+:::info Initialize with a README
+For ease, it is suggested to **Initialize this repository with: Add a README file**.
- payload = {
- "name": f"{widget_name.lower()}-{count}",
- }
- obj = await self.client.create(kind="TestResource", data=payload)
- await obj.save(allow_upsert=True)
-```
+This allows you to clone the repository down without any extra commands.
+:::
-Store this class within a new file called widget_generator.py.
+Once the repository is created, [clone the repository](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository) down and change directory into the cloned folder.
-## 3. Create an .infrahub.yml file
+## 2. Create an .infrahub.yml file
The [.infrahub.yml](/topics/infrahub-yml) file allows you to tie the different [components of a generator](/topics/generator) together into a working unit.
-```yaml
-# yaml-language-server: $schema=https://schema.infrahub.app/python-sdk/repository-config/latest.json
----
-generator_definitions:
- - name: widget_generator
- file_path: "widget_generator.py"
- targets: widgets
- query: widget_query
- class_name: WidgetGenerator
- parameters:
- name: "name__value"
-
-queries:
- - name: widget_query
- file_path: "widget_query.gql"
-```
+:::info Convert query to Infrahub SDK objects
+We provide `convert_query_response` option to be toggled to be able to access objects from the GraphQL query as Infrahub SDK objects rather than the raw dictionary response.
+
+This allows you to manage the returned data with helper methods on the SDK objects such as `save`, `fetch`, etc. on the returned data rather than having to build a payload to send back to Infrahub to manage the objects.
+
+Read more on the [Infrahub Python SDK](/python-sdk).
+:::
+
+
+
+
+
+
+ ```yaml title=".infrahub.yml"
+ # yaml-language-server: $schema=https://schema.infrahub.app/python-sdk/repository-config/latest.json
+ ---
+ generator_definitions:
+ - name: widget_generator
+ file_path: "example_generator/widget_generator.py"
+ targets: widgets
+ query: widget_query
+ class_name: WidgetGenerator
+ parameters:
+ name: "name__value"
+
+ queries:
+ - name: widget_query
+ file_path: "example_generator/widget_query.gql"
+ ```
+
+
+
+
+ ```yaml title=".infrahub.yml"
+ # yaml-language-server: $schema=https://schema.infrahub.app/python-sdk/repository-config/latest.json
+ ---
+ generator_definitions:
+ - name: widget_generator
+ file_path: "example_generator/widget_generator.py"
+ targets: widgets
+ query: widget_query
+ convert_query_response: true
+ class_name: WidgetGenerator
+ parameters:
+ name: "name__value"
+
+ queries:
+ - name: widget_query
+ file_path: "example_generator/widget_query.gql"
+ ```
+
+
@@ -148,8 +159,9 @@ queries:
- **file_path**: the relative file path to the file containing the generator as seen from within a Git repository
- **targets**: the name of a group of which the members will be a target for this generator
- **query**: the name of the GraphQL query used within this generator
+ - **convert_query_response**: convert the result of the GraphQL query to SDK InfrahubNode objects
+ - **class_name**: the name of the Python class in the generator file
- **parameters**: the parameter to pass to the generator GraphQL query, in this case this we will pass the name of the object (widget) as the name parameter
- - **query**: the name of the GraphQL query used within this generator
Here the `name` refers to the query's name and `file_path` should point to the GraphQL file within the repository.
@@ -158,32 +170,126 @@ queries:
See [this topic](/topics/infrahub-yml) for a full explanation of everything that can be defined in the `.infrahub.yml` file.
-## 4. Create a Git repository
+## 3. Identify the relevant data
-Within the `example_generator` folder you should now have 3 files:
+Here we define a GraphQL query that we will use to gather the information that will be passed into the generator.
-- `widget_query.gql`: Contains the GraphQL query
-- `generator.py`: Contains the Python code for the generator
-- `.infrahub.yml`: Contains the definition for the generator
+
+
+ ```graphql title="example_generator/widget_query.gql"
+ query Widgets($name: String!) {
+ TestWidget(name__value: $name) {
+ edges {
+ node {
+ name {
+ value
+ }
+ count {
+ value
+ }
+ }
+ }
+ }
+ }
+ ```
+
+
+ Here we must provide `__typename` and `id` within the query so the SDK can convert the query to the correct type and properly store within the SDK.
+
+ ```graphql title="example_generator/widget_query.gql"
+ query Widgets($name: String!) {
+ TestWidget(name__value: $name) {
+ edges {
+ node {
+ __typename
+ id
+ name {
+ value
+ }
+ count {
+ value
+ }
+ }
+ }
+ }
+ }
+ ```
-Before we can test our generator we must add the files to a local Git repository.
+
+
+
+Create a local directory on your computer where we will store the generator files.
```shell
-git init --initial-branch=main
-git add .
-git commit -m "First commit"
+mkdir example_generator
```
+## 4. Create a Python Generator
+
+The Generator class needs to implement a `generate` function that receives a `data` parameter that contains the response from the GraphQL query.
+
+The goal of this generator will be to create a number of resources that depends on the set count of the widgets.
+
+
+
+
+ ```python title="example_generator/widget_generator.py"
+ from infrahub_sdk.generator import InfrahubGenerator
+
+ class WidgetGenerator(InfrahubGenerator):
+ async def generate(self, data: dict) -> None:
+ # We can now access the nodes as typical SDK objects
+ widget = data["TestWidget"]["edges"][0]["node"]
+ widget_name: str = widget["name"]["value"]
+ widget_count: str = widget["count"]["value"]
+ for count in range(1, widget_count + 1):
+ payload = {
+ "name": f"{widget_name.lower()}-{count}",
+ }
+ obj = await self.client.create(kind="TestResource", data=payload)
+ await obj.save(allow_upsert=True)
+ ```
+
+
+
+ ```python title="example_generator/widget_generator.py"
+ from infrahub_sdk.generator import InfrahubGenerator
+
+ class WidgetGenerator(InfrahubGenerator):
+ async def generate(self, data: dict) -> None:
+ # We can now access the nodes as typical SDK objects
+ widget = self.nodes[0] # or self.store.get(data["TestWidget"]["edges"][0]["node"]["id"])
+ widget_name: str = widget.name.value
+ widget_count: str = widget.count.value
+
+ for count in range(1, widget_count + 1):
+
+ payload = {
+ "name": f"{widget_name.lower()}-{count}",
+ }
+ obj = await self.client.create(kind="TestResource", data=payload)
+ await obj.save(allow_upsert=True)
+ ```
+
+
+
+
+Store this class within a new file called **widget_generator.py** within the **example_generator** directory.
+
## 5. Test the generator using infrahubctl
-Using infrahubctl you can first verify that the `.infrahub.yml` file is formatted correctly by listing available generators.
+Using infrahubctl you can first verify that the **.infrahub.yml** file is formatted correctly by listing available generators.
-```shell title="❯ infrahubctl generator --list"
+```shell
+infrahubctl generator --list
+```
+
+```shell
Generators defined in repository: 1
widget_generator (widget_generator.py::Generator) Target: widgets
```
-:::note
+:::warning
When running a generator with `infrahubctl` the [SDK tracking](/python-sdk/topics/tracking) feature isn't used. The reason for this is that internally Infrahub uses the ID of the generator_definition to control the tracking, this isn't available from the outside. For this reason it is recommended to create test branches when developing generators and validating the results.
@@ -200,6 +306,46 @@ infrahubctl generator widget_generator --branch=test-branch1 name=widget1
infrahubctl generator widget_generator --branch=test-branch1 name=widget2
```
-Now you should see the tree TestResource objects within `test-branch1` one for the first widget and two for the second one.
+Now you should see the tree [TestResource](http://localhost:8000/objects/TestResource?branch=test-branch1) objects within `test-branch1` one for the first widget and two for the second one.
+
+![resources](../media/guides/generator_pc_3.png)
+
+Merge the changes into the **main** branch using [infrahubctl branch](/infrahubctl/infrahubctl-branch) command.
+
+```shell
+infrahubctl branch merge 'test-branch1'
+```
+
+## 6. Commit and add repository to Infrahub
+
+The root directory should include the **.infrahub.yml** file and **example_generator** folder.
+
+- `.infrahub.yml`: Contains the definition for the generator
+
+Within the **example_generator** folder you should now have 2 files:
+
+- `widget_query.gql`: Contains the GraphQL query
+- `widget_generator.py`: Contains the Python code for the generator
+
+Before we can run our generator in an Proposed Change (PC) pipeline we must push the files up to the remote Git repository we created in **Step 1**.
+
+```shell
+git add .
+git commit -m "Add generator"
+git push
+```
+
+Now that we have the latest changes, [add your repository to Infrahub](/guides/repository) and then the generators will be executed as part of the proposed change CI pipeline.
+
+## 7. Generator in the CI pipeline
-With this step completed you can [add your repository to Infrahub](/guides/repository) and then the generators will be executed as part of the proposed change pipeline.
+1. Navigate to [widgets](http://localhost:8000/objects/TestWidget) in the Infrahub UI
+2. Create a new branch named **widget-3**
+3. Create a new widget with the name of **widget3** and count of **3**
+4. Add **widget3** to the **widgets** group
+5. Create a [Proposed Change](http://localhost:8000/proposed-changes?branch=widget-3)
+6. Navigate to the **Checks** tab
+7. Once the **generator** CI check has completed, navigate to the **Data** tab
+![generator ci/cd](../media/guides/generator_pc_1.png)
+8. Click the **Refresh diff** button to see the **widget3** resources created by the generator
+![data refresh](../media/guides/generator_pc_2.png)
diff --git a/docs/docs/media/guides/generator_pc_1.png b/docs/docs/media/guides/generator_pc_1.png
new file mode 100644
index 0000000000..4d80597c57
Binary files /dev/null and b/docs/docs/media/guides/generator_pc_1.png differ
diff --git a/docs/docs/media/guides/generator_pc_2.png b/docs/docs/media/guides/generator_pc_2.png
new file mode 100644
index 0000000000..dbc0c62320
Binary files /dev/null and b/docs/docs/media/guides/generator_pc_2.png differ
diff --git a/docs/docs/media/guides/generator_pc_3.png b/docs/docs/media/guides/generator_pc_3.png
new file mode 100644
index 0000000000..539da49cb2
Binary files /dev/null and b/docs/docs/media/guides/generator_pc_3.png differ