Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add a mutation action plugin #89

Merged
merged 1 commit into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions api/plugins/action/mutation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from gql.dsl import DSLMutation
from . import LagoonActionBase

class ActionModule(LagoonActionBase):

def run(self, tmp=None, task_vars=None):

if task_vars is None:
task_vars = dict()

result = super(ActionModule, self).run(tmp, task_vars)
del tmp # tmp no longer has any effect

self.createClient(task_vars)

mutation = self._task.args.get('mutation')
input = self._task.args.get('input')
selectType = self._task.args.get('select', None)
subfields = self._task.args.get('subfields', [])

with self.client:
mutationObj = self.client.build_dynamic_mutation(
mutation, input, selectType, subfields)
res = self.client.execute_query_dynamic(DSLMutation(mutationObj))
result['result'] = res[mutation]
result['changed'] = True
return result
64 changes: 63 additions & 1 deletion api/plugins/module_utils/gql.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,13 @@ def execute_query(self, query: str, variables: Optional[Dict[str, Any]]={}) -> D
self.vvvv(f"GraphQL TransportQueryError: {e}")
return {'error': e}

def build_dynamic_query(self, query: str, mainType: str, args: Optional[Dict[str, Any]] = {}, fields: List[str] = [], subFieldsMap: Optional[Dict[str, List[str]]] = {}) -> DSLField:
def build_dynamic_query(self,
query: str,
mainType: str,
args: Optional[Dict[str, Any]] = {},
fields: List[str] = [],
subFieldsMap: Optional[Dict[str, List[str]]] = {},
) -> DSLField:
"""
Dynamically build a query against the Lagoon API.

Expand Down Expand Up @@ -132,6 +138,62 @@ def build_dynamic_query(self, query: str, mainType: str, args: Optional[Dict[str

return queryObj

def build_dynamic_mutation(self,
mutation: str,
inputArgs: Optional[Dict[str, Any]] = {},
selectType: str = '',
subfields: List[str] = [],
) -> DSLField:
"""
Dynamically build a mutation against the Lagoon API.

The mutation is built from the mutation name
(e.g, deployEnvironmentBranch) and a dict of input arguments
(e.g, {project: {name: "test"}, branchName: "master"} ).

Taking the following graphql mutation as an example:
{
addFact(
input: {
environment: 243307,
name: "test_module",
value: "2.0.0",
source: "ansible_playbook:test-mutation",
description: "The test_module module version",
category: "Drupal Module Version"
}
) { id }
}
mutation = "addFact"
inputArgs = {
environment: 243307,
name: "test_module",
value: "2.0.0",
source: "ansible_playbook:test-mutation",
description: "The test_module module version",
category: "Drupal Module Version"
}
selectType = "Fact" (since addFact returns Fact)
subfields = ["id"]
"""

if not len(inputArgs):
raise AnsibleValidationError("Input arguments are required for mutations.")

if selectType and not len(subfields):
raise AnsibleValidationError("Subfields are required if selectType is set.")

# Build the main query with top-level fields if any.
mutationObj: DSLField = getattr(self.ds.Mutation, mutation)
mutationObj.args(input=inputArgs)

if selectType:
selectTypeObj: DSLType = getattr(self.ds, selectType)
for f in subfields:
mutationObj.select(getattr(selectTypeObj, f))

return mutationObj

def execute_query_dynamic(self, *operations: DSLExecutable) -> Dict[str, Any]:
"""Executes a dynamic query with the open session.

Expand Down
81 changes: 81 additions & 0 deletions api/plugins/modules/mutation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

DOCUMENTATION = r'''
module: mutation
short_description: Run a mutation against the Lagoon GraphQL API.
options:
mutation:
description:
- The mutation to run, e.g, addFactsByName.
required: true
type: str
input:
description:
- Input to pass to the mutation.
required: true
type: dict
select:
description:
- The type to select from the mutation result.
type: str
default: null
subfields:
description:
- The subfields to select from the mutation result.
type: list
default: []
'''

EXAMPLES = r'''
- name: Delete a Fact via mutation before creating
lagoon.api.mutation:
mutation: deleteFact
input:
environment: "{{ environment_id }}"
name: lagoon_logs

- name: Add a Fact for a project
lagoon.api.mutation:
mutation: addFact
input:
name: lagoon_logs
category: Drupal Module Version
environment: "{{ environment_id }}"
value: 2.0.0
source: ansible_playbook:audit:module_version
description: The lagoon_logs module version
select: Fact
subfields:
- id

- name: Delete Facts from a source via mutation before creating
lagoon.api.mutation:
mutation: deleteFactsFromSource
input:
environment: "{{ environment_id }}"
source: ansible_playbook:audit:module_version

- name: Add multiple Facts for a project
lagoon.api.mutation:
mutation: addFactsByName
input:
project: "{{ project_name }}"
environment: "{{ environment }}"
fact:
- name: admin_toolbar
category: Drupal Module Version
environment_id: "{{ environment_id }}"
value: 2.3.0
source: ansible_playbook:audit:module_version
description: The admin_toolbar module version
- name: panelizer
category: Drupal Module Version
environment_id: "{{ environment_id }}"
value: 4.0.0
source: ansible_playbook:audit:module_version
description: The panelizer module version
select: Fact
subfields:
- id
'''
Loading