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

Nested Objects not supported? #10

Open
grvhi opened this issue Aug 5, 2019 · 9 comments
Open

Nested Objects not supported? #10

grvhi opened this issue Aug 5, 2019 · 9 comments
Labels
enhancement New feature or request

Comments

@grvhi
Copy link
Contributor

grvhi commented Aug 5, 2019

I'm trying to add support for nested objects in my graph per the example below. But it seems that the nested objects are not parsed/read correctly?

class CommandResponseAttributes(Object):
    field_type: str
    required: bool
    description: Optional[str]


class CommandResponseObject(Object):
    field_name: str
    attributes: CommandResponseAttributes


class CommandObject(Object):
    command_name: str
    description: Optional[str]
    response_schema: List[CommandResponseObject]

With the following Query:

def response_schema() -> dict:
        return {
            'new_handle': {
                'field_type': 'string',
                'required': True,
                'description': 'some description'
            },
            'send_to': {
                'field_type': 'string',
                'required': True,
                'description': 'some description'
            },
            'accept_email': {
                'field_type': 'string',
                'required': False,
                'description': 'some description'
            },
            'accept_domain': {
                'field_type': 'string',
                'required': False,
                'description': 'some description'
            },
            'lock_email': {
                'field_type': 'boolean',
                'required': True,
                'description': 'some description'
            },
            'lock_domain': {
                'field_type': 'boolean',
                'required': True,
                'description': 'some description'
            },
            'source_domain': {
                'field_type': 'string',
                'required': False,
                'description': 'some description'
            }
        }

class Query(BaseQuery):

    @field
    def command(self, command_name: str) -> CommandObject:
        schema = list()
        for field_name, attributes in response_schema().items():
            # noinspection PyArgumentList
            schema.append(
                CommandResponseObject(
                    field_name=field_name,
                    attributes=CommandResponseAttributes(**attributes)
                )
            )
        data = {
            'command_name': command_obj.name,
            'description': None,
            'response_schema': schema
        }
        # noinspection PyArgumentList
        return CommandObject(**data)

But sending a query for these objects results in the following exception:

ERROR:root:'NoneType' object has no attribute 'selections'
Traceback (most recent call last):
  File "lib/python3.7/site-packages/pygraphy/types/schema.py", line 185, in _execute_operation
    error_collector
  File "lib/python3.7/site-packages/pygraphy/types/object.py", line 158, in __task_receiver
    yield await self.__check_and_circular_resolve(tasks, error_collector)
  File "lib/python3.7/site-packages/pygraphy/types/object.py", line 174, in __check_and_circular_resolve
    result, node, error_collector, path
  File "lib/python3.7/site-packages/pygraphy/types/object.py", line 192, in __circular_resolve
    node.selection_set.selections, error_collector, path
  File "lib/python3.7/site-packages/pygraphy/types/object.py", line 158, in __task_receiver
    yield await self.__check_and_circular_resolve(tasks, error_collector)
  File "lib/python3.7/site-packages/pygraphy/types/object.py", line 174, in __check_and_circular_resolve
    result, node, error_collector, path
  File "lib/python3.7/site-packages/pygraphy/types/object.py", line 200, in __circular_resolve
    node.selection_set.selections,
AttributeError: 'NoneType' object has no attribute 'selections'
Traceback (most recent call last):
  File "lib/python3.7/asyncio/base_events.py", line 584, in run_until_complete
    return future.result()
  File "/lib/python3.7/site-packages/pygraphy/types/schema.py", line 159, in execute
    request
  File "lib/python3.7/site-packages/pygraphy/types/schema.py", line 199, in _execute_operation
    'data': dict(obj) if obj else None
  File "lib/python3.7/site-packages/pygraphy/types/object.py", line 75, in __iter__
    value = dict(value)
  File "lib/python3.7/site-packages/pygraphy/types/object.py", line 80, in __iter__
    serialized_value.append(dict(i))
  File "lib/python3.7/site-packages/pygraphy/types/object.py", line 73, in __iter__
    for name, value in self.resolve_results.items():
AttributeError: 'CommandResponseObject' object has no attribute 'resolve_results'
@ethe
Copy link
Owner

ethe commented Aug 5, 2019

Hi, could you please give me a complete sample for reproducing? I do not know the definition of CommandRegister.

@grvhi
Copy link
Contributor Author

grvhi commented Aug 5, 2019

@ethe - I've updated my original comment with the necessary representation being passed into the command field

@ethe
Copy link
Owner

ethe commented Aug 5, 2019

I am sorry that I can not reproduce the issue following your sample. Please make sure that you have already used the newest version of Pygraphy.

Here is my test file according to your discription.

import asyncio
from typing import Optional, List
from pygraphy import Object, Query as BaseQuery, field, Schema as BaseSchema


class CommandResponseAttributes(Object):
    field_type: str
    required: bool
    description: Optional[str]


class CommandResponseObject(Object):
    field_name: str
    attributes: CommandResponseAttributes


class CommandObject(Object):
    command_name: str
    description: Optional[str]
    response_schema: List[CommandResponseObject]


def response_schema() -> dict:
    return {
        'new_handle': {
            'field_type': 'string',
            'required': True,
            'description': 'The handle just created, without @<domain>'
        },
        'send_to': {
            'field_type': 'string',
            'required': True,
            'description': 'The actual email address to which this handle '
                            'will forward accepted emails.'
        },
        'accept_email': {
            'field_type': 'string',
            'required': False,
            'description': 'The only email address from which emails will '
                            'be accepted by this handle. If `None`, the '
                            'handle\'s other incoming rules apply.'
        },
        'accept_domain': {
            'field_type': 'string',
            'required': False,
            'description': 'The only email domain from which emails will '
                            'be accepted by this handle. If `None`, the '
                            'handle\'s other incoming rules apply.'
        },
        'lock_email': {
            'field_type': 'boolean',
            'required': True,
            'description': 'Will this handle be locked to the email '
                            'of the sender of the first email received by '
                            'this handle?'
        },
        'lock_domain': {
            'field_type': 'boolean',
            'required': True,
            'description': 'Will this handle be locked to the domain '
                            'of the sender of the first email received by '
                            'this handle?'
        },
        'source_domain': {
            'field_type': 'string',
            'required': False,
            'description': 'The domain of the website for which this '
                            'handle was created, if available.'
        }
    }

class Query(BaseQuery):

    @field
    def command(self, command_name: str) -> CommandObject:
        schema = list()
        for field_name, attributes in response_schema().items():
            # noinspection PyArgumentList
            schema.append(
                CommandResponseObject(
                    field_name=field_name,
                    attributes=CommandResponseAttributes(**attributes)
                )
            )
        data = {
            'command_name': 'test',
            'description': None,
            'response_schema': schema
        }
        # noinspection PyArgumentList
        return CommandObject(**data)


class Schema(BaseSchema):
    query: Optional[Query]


print(asyncio.run(Schema.execute('''
{
    command(commandName: "test") {
        responseSchema {
            fieldName
            attributes {
                description
                required
                fieldType
            }
        }
    }
}
''')))

@grvhi
Copy link
Contributor Author

grvhi commented Aug 5, 2019

Thanks @ethe - I see my problem was with the query I was submitting. The simplified version of the query I was using is:

{command(commandName: "test") {responseSchema}}

If you run this query, you should get the same exception messages as I did above. While the error is in the query, I think Pygraphy could/should return a more helpful error message?

The issue seems to be specifically the omission of sub-selections in the query.

@ethe
Copy link
Owner

ethe commented Aug 5, 2019

I think maybe {command(commandName: "test") {responseSchema}} is not a valid GraphQL query, as GraphQL Spec, you should describe each field you want explicitly, could you please provide more info about it?

@grvhi
Copy link
Contributor Author

grvhi commented Aug 5, 2019

I think maybe {command(commandName: "test") {responseSchema}} is not a valid GraphQL query, as GraphQL Spec, you should describe each field you want explicitly, could you please provide more info about it?

Yes, you're absolutely correct: my suggestion is that Pygraphy should require sub-selections when querying for an object.

If I'm not mistaken, in the same situation graphene returns an error stating that responseSchema requires sub-fields to be selected as part of the query. i.e. In validating the query, graphene detects that no fields under responseSchema have been selected and immediately raises an exception.

@ethe
Copy link
Owner

ethe commented Aug 5, 2019

You are right, the Pygraphy should return more helpful messages after an error query. I will fix it recently, than you!

@ethe ethe added enhancement New feature or request and removed need more info labels Aug 5, 2019
@grvhi
Copy link
Contributor Author

grvhi commented Aug 5, 2019

You are right, the Pygraphy should return more helpful messages after an error query. I will fix it recently, than you!

Thank you! It's great to be finally working with a pythonic GraphQL library!

@grvhi grvhi closed this as completed Aug 5, 2019
@grvhi grvhi reopened this Aug 5, 2019
@ethe
Copy link
Owner

ethe commented Aug 5, 2019

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants