Skip to content
This repository was archived by the owner on Apr 30, 2025. It is now read-only.

Commit e1953bb

Browse files
Jozef VolakJozefiel
Jozef Volak
authored andcommitted
[graphql-pydantic-converter] stringify mutation input
1 parent ccfdf04 commit e1953bb

File tree

7 files changed

+97
-33
lines changed

7 files changed

+97
-33
lines changed

.github/workflows/release_utils.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ jobs:
3535
run: |
3636
echo "IMAGE_NAME=frinx/${{ env.PACKAGE_NAME }}:${{ env.PACKAGE_VERSION }}" >> "$GITHUB_ENV"
3737
- name: Poetry publish
38-
run: poetry publish -u "__token__" -p "${{ secrets.PYPI_TOKEN }}" --build --dry-run
38+
run: poetry publish -u "__token__" -p "${{ secrets.PYPI_TOKEN }}" --build
3939
- name: Build docker image
4040
run: docker build . --file Dockerfile --build-arg git_commit=$(git rev-parse HEAD) --tag ${{ env.IMAGE_NAME }}
4141
- name: Log into docker hub
@@ -53,4 +53,4 @@ jobs:
5353
body: |
5454
- Release of ${{ env.PACKAGE_NAME }}:${{ env.PACKAGE_VERSION }} for image with tag ${{ env.IMAGE_NAME }}
5555
draft: false
56-
prerelease: false
56+
prerelease: false

utils/graphql-pydantic-converter/CHANGELOG.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,10 @@
1515
# 1.0.0
1616
- Migration to pydantic v2
1717

18-
# 1.1.0
19-
- Change Map type to dict (key, value)
18+
# 1.0.1
19+
- Change Map type to dict (key, value)
20+
21+
# 1.0.2
22+
- Stringify mutation input strings
23+
- Add __typename to payload
24+
- create test folder as a module

utils/graphql-pydantic-converter/graphql_pydantic_converter/graphql_types.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
from __future__ import annotations
22

3+
import json
4+
import typing
35
from enum import Enum
46
from typing import TYPE_CHECKING
57

68
from pydantic import BaseModel
79
from pydantic import ConfigDict
10+
from pydantic import Field
811

912
if TYPE_CHECKING:
1013
from typing import Any
@@ -101,6 +104,7 @@ class Interface(BaseModel):
101104

102105

103106
class Payload(BaseModel):
107+
typename: bool = Field(default=False, alias='__typename')
104108

105109
model_config = ConfigDict(
106110
extra='forbid',
@@ -153,7 +157,10 @@ def _parse_num(value: int | float) -> str:
153157

154158
@staticmethod
155159
def _parse_str(value: Any) -> str:
156-
return f'"{value}"'
160+
try:
161+
return json.dumps(json.loads(json.dumps(value)))
162+
except ValueError:
163+
return f'"{value}"'
157164

158165
@staticmethod
159166
def _parse_tuple(value: tuple[Any, ...]) -> str:
@@ -270,7 +277,10 @@ def _parse_num(value: int | float) -> str:
270277

271278
@staticmethod
272279
def _parse_str(value: Any) -> str:
273-
return f'"{value}"'
280+
try:
281+
return json.dumps(json.loads(json.dumps(value)))
282+
except ValueError:
283+
return f'"{value}"'
274284

275285
@staticmethod
276286
def _parse_tuple(value: tuple[Any, ...]) -> str:
@@ -350,6 +360,6 @@ def render(self) -> str:
350360
return f'{{ { name }{variable} {{ {payload} }} }}'
351361

352362

353-
def concatenate_queries(queries: list[Union[Query, Mutation]]) -> str:
363+
def concatenate_queries(queries: typing.Sequence[Union[Query, Mutation]]) -> str:
354364
merged_query = ''.join(query.render()[1:-1] for query in queries)
355365
return f'{{ {merged_query} }}'

utils/graphql-pydantic-converter/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ packages = [{ include = "graphql_pydantic_converter" }]
1919
name = "graphql-pydantic-converter"
2020
description = "Convert pydantic schema to pydantic datamodel and build request from it"
2121
authors = ["Jozef Volak <[email protected]>"]
22-
version = '1.0.1'
22+
version = '1.0.2'
2323
readme = ["README.md", "CHANGELOG.md"]
2424
keywords = ["graphql", "pydantic"]
2525
license = "Apache 2.0"

utils/graphql-pydantic-converter/tests/__init__.py

Whitespace-only changes.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"cli": {
3+
"cli-topology:host": "sample-topology",
4+
"cli-topology:port": "{{port}}",
5+
"cli-topology:transport-type": "ssh",
6+
"cli-topology:device-type": "{{device_type}}",
7+
"cli-topology:device-version": "{{device_version}}",
8+
"cli-topology:password": "{{password}}",
9+
"cli-topology:username": "{{username}}",
10+
"cli-topology:journal-size": 500,
11+
"cli-topology:dry-run-journal-size": 180,
12+
"cli-topology:parsing-engine": "tree-parser"
13+
}
14+
}

utils/graphql-pydantic-converter/tests/test_graphql_generator.py

Lines changed: 60 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,37 @@
11
import json
22
import typing
33

4-
from model import AddBlueprintInput
5-
from model import AddBlueprintMutation
6-
from model import AddBlueprintPayload
7-
from model import Blueprint
8-
from model import BlueprintConnection
9-
from model import BlueprintEdge
10-
from model import BlueprintsQuery
114
from pydantic import Field
12-
from render_models import AllocationStrategy
13-
from render_models import ClaimResourceMutation
14-
from render_models import PageInfoSchedule
15-
from render_models import Resource
16-
from render_models import ResourcePool
17-
from render_models import ResourcePoolConnection
18-
from render_models import ResourcePoolEdge
19-
from render_models import Schedule
20-
from render_models import ScheduleConnection
21-
from render_models import ScheduleEdge
22-
from render_models import SchedulesFilterInput
23-
from render_models import SchedulesQuery
24-
from render_models import SearchPoolsByTagsQuery
25-
from render_models import TagAnd
26-
from render_models import TagOr
275

286
import graphql_pydantic_converter.graphql_types
297
from graphql_pydantic_converter.graphql_types import ENUM
308
from graphql_pydantic_converter.graphql_types import Input
319
from graphql_pydantic_converter.schema_converter import GraphqlJsonParser
3210

11+
from .model import AddBlueprintInput
12+
from .model import AddBlueprintMutation
13+
from .model import AddBlueprintPayload
14+
from .model import Blueprint
15+
from .model import BlueprintConnection
16+
from .model import BlueprintEdge
17+
from .model import BlueprintsQuery
18+
from .model import BlueprintsQueryResponse
19+
from .render_models import AllocationStrategy
20+
from .render_models import ClaimResourceMutation
21+
from .render_models import PageInfoSchedule
22+
from .render_models import Resource
23+
from .render_models import ResourcePool
24+
from .render_models import ResourcePoolConnection
25+
from .render_models import ResourcePoolEdge
26+
from .render_models import Schedule
27+
from .render_models import ScheduleConnection
28+
from .render_models import ScheduleEdge
29+
from .render_models import SchedulesFilterInput
30+
from .render_models import SchedulesQuery
31+
from .render_models import SearchPoolsByTagsQuery
32+
from .render_models import TagAnd
33+
from .render_models import TagOr
34+
3335

3436
class TestTaskGenerator:
3537
def test_render_json(self) -> None:
@@ -131,6 +133,38 @@ class AddDeviceInput(Input):
131133

132134
assert reference == mutation
133135

136+
json_blueprint = 'tests/blueprint.json'
137+
138+
with open(json_blueprint) as json_file:
139+
device_import_json = json_file.read()
140+
template = device_import_json
141+
142+
mutation = AddBlueprintMutation(
143+
payload=AddBlueprintPayload(
144+
blueprint=Blueprint(
145+
id=True,
146+
name=True,
147+
template=True
148+
)
149+
),
150+
input=AddBlueprintInput(
151+
name='blueprint',
152+
template=template,
153+
)
154+
).render()
155+
156+
reference = ('mutation { addBlueprint ( input: { name: "blueprint", template: "{\n\t\"cli\": '
157+
'{\n\t\t\"cli-topology:host\": \"sample-topology\",\n\t\t\"cli-topology:port\": '
158+
'\"{{port}}\",\n\t\t\"cli-topology:transport-type\": \"ssh\",\n\t\t\"cli-topology:device-type\": '
159+
'\"{{device_type}}\",\n\t\t\"cli-topology:device-version\": '
160+
'\"{{device_version}}\",\n\t\t\"cli-topology:password\": '
161+
'\"{{password}}\",\n\t\t\"cli-topology:username\": '
162+
'\"{{username}}\",\n\t\t\"cli-topology:journal-size\": '
163+
'500,\n\t\t\"cli-topology:dry-run-journal-size\": 180,\n\t\t\"cli-topology:parsing-engine\": '
164+
'\"tree-parser\"\n\t}\n}" }) { blueprint { id name template } } }')
165+
166+
assert reference == mutation
167+
134168
def test_render_input_advanced(self) -> None:
135169
query = SearchPoolsByTagsQuery(
136170
tags=TagOr(
@@ -202,7 +236,6 @@ def test_render_input_advanced(self) -> None:
202236
assert reference == query_render
203237

204238
def test_parse_response(self) -> None:
205-
from model import BlueprintsQueryResponse
206239

207240
response: typing.Any = {
208241
'data': {
@@ -253,7 +286,9 @@ def test_multiple_query(self) -> None:
253286

254287
query_second = SchedulesQuery(
255288
payload=ScheduleConnection(
289+
__typename=True,
256290
pageInfo=PageInfoSchedule(
291+
__typename=True,
257292
hasNextPage=True,
258293
hasPreviousPage=True,
259294
startCursor=True,
@@ -285,9 +320,9 @@ def test_multiple_query(self) -> None:
285320
reference = '{ SearchPoolsByTags ( tags: { matchesAny: [ { matchesAll: [ "root_pool" ] } ] } ) ' \
286321
'{ edges { node { AllocationStrategy { Name } ' \
287322
'Name PoolProperties PoolType id } } totalCount } schedules ( after: "aaa", first: 10, filter: ' \
288-
'{ workflowName: "TEST_A" , workflowVersion: "1" } ) { edges { node { name enabled ' \
323+
'{ workflowName: "TEST_A" , workflowVersion: "1" } ) { __typename edges { node { name enabled ' \
289324
'cronString } cursor } ' \
290-
'pageInfo { hasNextPage hasPreviousPage startCursor endCursor } totalCount } }'
325+
'pageInfo { __typename hasNextPage hasPreviousPage startCursor endCursor } totalCount } }'
291326

292327
merged_query = graphql_pydantic_converter.graphql_types.concatenate_queries(queries)
293328
assert reference == merged_query

0 commit comments

Comments
 (0)