def test_field_args_is_union_of_original_field_args(self):
     self._assert_merge(
         [
             GraphQLObjectType("Object", fields={
                 "id": GraphQLField(
                     type=GraphQLInt,
                     args={"id": GraphQLArgument(type=GraphQLInt)},
                 ),
             }),
             GraphQLObjectType("Object", fields={
                 "id": GraphQLField(
                     type=GraphQLInt,
                     args={
                         "id": GraphQLArgument(type=GraphQLInt),
                         "name": GraphQLArgument(type=GraphQLString),
                     },
                 ),
             }),
         ],
         is_object_type(fields=has_entries({
             "id": is_field(args=has_entries({
                 "id": is_arg(type=is_int),
                 "name": is_arg(type=is_string),
             })),
         })),
     )
Пример #2
0
def build_schema(base: DeclarativeMeta,
                 enable_subscription: bool = False) -> GraphQLSchema:
    """

    Args:
        base:
        enable_subscription:

    Returns: :class:`graphql:graphql.type.GraphQLSchema`

    """
    queries: GraphQLFieldMap = {}
    mutations: GraphQLFieldMap = {}

    objects: Objects = {}
    inputs: Inputs = {}

    for model in base.__subclasses__():
        build_queries(model, objects, queries, inputs)
        build_mutations(model, objects, mutations, inputs)

    return GraphQLSchema(
        GraphQLObjectType("Query", queries),
        GraphQLObjectType("Mutation", mutations),
        GraphQLObjectType("Subscription", {}) if enable_subscription else None,
    )
    def test_non_nullable_list(self):

        PersonType = GraphQLObjectType(
            "Person", lambda: {"name": GraphQLField(GraphQLString)}
        )

        schema = GraphQLSchema(
            query=GraphQLObjectType(
                name="RootQueryType",
                fields={
                    "people": GraphQLField(
                        GraphQLList(GraphQLNonNull(PersonType)),
                        resolve=lambda obj, info: {"name": "eran"},
                    )
                },
            )
        )

        query = """
                query GetPeople {
                  people {
                    name
                  }
                }
            """

        parser = QueryParser(schema)
        dataclass_renderer = DataclassesRenderer(schema)

        parsed = parser.parse(query)
        rendered = dataclass_renderer.render(parsed)

        m = self.load_module(rendered)

        mock_client = MagicMock()
        mock_client.call = MagicMock(
            return_value="""
           {
               "data": {
                   "people": [
                      {
                        "name": "eran"
                      },
                      {
                        "name": "eran1"
                      }
                   ]
               }
           }
        """
        )

        result = m.GetPeople.execute(mock_client)
        assert result
        assert isinstance(result, m.GetPeople.GetPeopleData)

        assert len(result.people) == 2
        assert result.people[0].name == "eran"
        assert result.people[1].name == "eran1"
Пример #4
0
 def generate_schema(self) -> GraphQLSchema:
     """
     Finalizes the GraphQL schema by generating the GraphQLSchema
     :return: The finalized GraphQL schema
     """
     query = GraphQLObjectType("Query", self.gql_queries)
     mutation = GraphQLObjectType("Mutation", self.gql_mutations)
     return GraphQLSchema(query, mutation)
Пример #5
0
def test_batches_correctly(executor):

    Business = GraphQLObjectType(
        'Business', lambda: {
            'id':
            GraphQLField(GraphQLID, resolver=lambda root, info, **args: root),
        })

    Query = GraphQLObjectType(
        'Query', lambda: {
            'getBusiness':
            GraphQLField(Business,
                         args={
                             'id': GraphQLArgument(GraphQLNonNull(GraphQLID)),
                         },
                         resolver=lambda root, info, **args: info.context.
                         business_data_loader.load(args.get('id'))),
        })

    schema = GraphQLSchema(query=Query)

    doc = '''
{
    business1: getBusiness(id: "1") {
        id
    }
    business2: getBusiness(id: "2") {
        id
    }
}
    '''
    doc_ast = parse(doc)

    load_calls = []

    class BusinessDataLoader(DataLoader):
        def batch_load_fn(self, keys):
            load_calls.append(keys)
            return Promise.resolve(keys)

    class Context(object):
        business_data_loader = BusinessDataLoader()

    result = execute(schema,
                     doc_ast,
                     None,
                     context_value=Context(),
                     executor=executor)
    assert not result.errors
    assert result.data == {
        'business1': {
            'id': '1'
        },
        'business2': {
            'id': '2'
        },
    }
    assert load_calls == [['1', '2']]
Пример #6
0
def test_batches_correctly(executor):
    # type: (SyncExecutor) -> None

    Business = GraphQLObjectType(
        "Business",
        lambda: {
            "id": GraphQLField(GraphQLID,
                               resolver=lambda root, info, **args: root)
        },
    )

    Query = GraphQLObjectType(
        "Query",
        lambda: {
            "getBusiness":
            GraphQLField(
                Business,
                args={"id": GraphQLArgument(GraphQLNonNull(GraphQLID))},
                resolver=lambda root, info, **args: info.context.
                business_data_loader.load(args.get("id")),
            )
        },
    )

    schema = GraphQLSchema(query=Query)

    doc = """
{
    business1: getBusiness(id: "1") {
        id
    }
    business2: getBusiness(id: "2") {
        id
    }
}
    """
    doc_ast = parse(doc)

    load_calls = []

    class BusinessDataLoader(DataLoader):
        def batch_load_fn(self, keys):
            # type: (List[str]) -> Promise
            load_calls.append(keys)
            return Promise.resolve(keys)

    class Context(object):
        business_data_loader = BusinessDataLoader()

    result = execute(schema,
                     doc_ast,
                     None,
                     context_value=Context(),
                     executor=executor)
    assert not result.errors
    assert result.data == {"business1": {"id": "1"}, "business2": {"id": "2"}}
    assert load_calls == [["1", "2"]]
    def test_can_handle_recursive_types(self):
        obj_1 = GraphQLObjectType("Object", fields=lambda: {"self": GraphQLField(type=obj_1)})
        obj_2 = GraphQLObjectType("Object", fields=lambda: {"self": GraphQLField(type=obj_2)})

        assert is_subtype(obj_1, obj_2)

        obj_1 = GraphQLObjectType("Object", fields=lambda: {"self": GraphQLField(type=obj_1)})
        obj_2 = GraphQLObjectType("Object", fields=lambda: {"self": GraphQLField(type=GraphQLNonNull(obj_2))})

        assert not is_subtype(obj_1, obj_2)
 def test_merged_object_type_has_name_of_original_type(self):
     self._assert_merge(
         [
             GraphQLObjectType("Object", fields={
                 "id": GraphQLField(type=GraphQLInt),
             }),
             GraphQLObjectType("Object", fields={
                 "id": GraphQLField(type=GraphQLInt),
             }),
         ],
         has_properties(name="Object"),
     )
Пример #9
0
def add_resolve_functions_to_scalar(name: str, obj: GraphQLObjectType,
                                    resolvers: dict):
    scalar_resolvers = resolvers.get(name, {})

    serialize = scalar_resolvers.get("serialize", obj.serialize)
    obj.serialize = serialize

    parse_literal = scalar_resolvers.get("parse_literal", obj.parse_literal)
    obj.parse_literal = parse_literal

    parse_value = scalar_resolvers.get("parse_value", obj.parse_value)
    obj.parse_value = parse_value
Пример #10
0
    def __init__(self, edb_schema):
        '''Create a graphql schema based on edgedb schema.'''

        self.edb_schema = edb_schema
        # extract and sort modules to have a consistent type ordering
        self.modules = {
            m.get_name(self.edb_schema)
            for m in self.edb_schema.get_objects(type=s_mod.Module)
        } - HIDDEN_MODULES
        self.modules = list(self.modules)
        self.modules.sort()

        self._gql_interfaces = {}
        self._gql_objtypes = {}
        self._gql_inobjtypes = {}
        self._gql_ordertypes = {}
        self._gql_enums = {}

        self._define_types()

        query = self._gql_objtypes['Query'] = GraphQLObjectType(
            name='Query',
            fields=self.get_fields('Query'),
        )

        # If a database only has abstract types and scalars, no
        # mutations will be possible (such as in a blank database),
        # but we would still want the reflection to work without
        # error, even if all that can be discovered through GraphQL
        # then is the schema.
        fields = self.get_fields('Mutation')
        if fields:
            mutation = self._gql_objtypes['Mutation'] = GraphQLObjectType(
                name='Mutation',
                fields=fields,
            )
        else:
            mutation = None

        # get a sorted list of types relevant for the Schema
        types = [
            objt for name, objt in itertools.chain(
                self._gql_objtypes.items(), self._gql_inobjtypes.items())
            # the Query is included separately
            if name not in TOP_LEVEL_TYPES
        ]
        types = sorted(types, key=lambda x: x.name)
        self._gql_schema = GraphQLSchema(query=query,
                                         mutation=mutation,
                                         types=types)

        # this map is used for GQL -> EQL translator needs
        self._type_map = {}
Пример #11
0
def connection_definitions(
    node_type: Union[GraphQLNamedOutputType,
                     GraphQLNonNull[GraphQLNamedOutputType]],
    name: Optional[str] = None,
    resolve_node: Optional[GraphQLFieldResolver] = None,
    resolve_cursor: Optional[GraphQLFieldResolver] = None,
    edge_fields: Optional[Thunk[GraphQLFieldMap]] = None,
    connection_fields: Optional[Thunk[GraphQLFieldMap]] = None,
) -> GraphQLConnectionDefinitions:
    """Return GraphQLObjectTypes for a connection with the given name.

    The nodes of the returned object types will be of the specified type.
    """
    name = name or get_named_type(node_type).name

    edge_type = GraphQLObjectType(
        name + "Edge",
        description="An edge in a connection.",
        fields=lambda: {
            "node":
            GraphQLField(
                node_type,
                resolve=resolve_node,
                description="The item at the end of the edge",
            ),
            "cursor":
            GraphQLField(
                GraphQLNonNull(GraphQLString),
                resolve=resolve_cursor,
                description="A cursor for use in pagination",
            ),
            **resolve_maybe_thunk(edge_fields or {}),
        },
    )

    connection_type = GraphQLObjectType(
        name + "Connection",
        description="A connection to a list of items.",
        fields=lambda: {
            "pageInfo":
            GraphQLField(
                GraphQLNonNull(page_info_type),
                description="Information to aid in pagination.",
            ),
            "edges":
            GraphQLField(GraphQLList(edge_type),
                         description="A list of edges."),
            **resolve_maybe_thunk(connection_fields or {}),
        },
    )

    return GraphQLConnectionDefinitions(edge_type, connection_type)
Пример #12
0
 def test_field_type_is_common_subtype_of_field_types(self):
     self._assert_merge(
         [
             GraphQLObjectType("Object", fields={
                 "id": GraphQLField(type=GraphQLNonNull(GraphQLInt)),
             }),
             GraphQLObjectType("Object", fields={
                 "id": GraphQLField(type=GraphQLInt),
             }),
         ],
         is_object_type(fields=has_entries({
             "id": is_field(type=is_non_null(is_int)),
         })),
     )
Пример #13
0
 def test_when_query_is_not_subtype_then_schema_is_not_subtype(self):
     assert not is_subtype(
         GraphQLSchema(
             query=GraphQLObjectType("Object", fields={
                 "id": GraphQLField(type=GraphQLInt),
             }),
         ),
         GraphQLSchema(
             query=GraphQLObjectType("Object", fields={
                 "id": GraphQLField(type=GraphQLInt),
                 "name": GraphQLField(type=GraphQLString),
             }),
         ),
     )
Пример #14
0
 def test_types_within_list_types_are_merged(self):
     self._assert_merge(
         [
             GraphQLList(GraphQLObjectType("Object", fields={
                 "id": GraphQLField(type=GraphQLInt),
             })),
             GraphQLList(GraphQLObjectType("Object", fields={
                 "name": GraphQLField(type=GraphQLString),
             })),
         ],
         is_list_type(is_object_type(fields=has_entries({
             "id": is_field(type=is_int),
             "name": is_field(type=is_string),
         }))),
     )
Пример #15
0
def relay_connection_type(node_type):
    base_name = node_type.name
    Edge = GraphQLObjectType(
        name=base_name + 'Edge',
        fields={
            'cursor': GraphQLField(type=GraphQLNonNull(GraphQLString)),
            'node': GraphQLField(type=node_type),
        })
    Connection = GraphQLObjectType(
        name=base_name + 'Connection',
        fields={
            'pageInfo': GraphQLField(type=GraphQLNonNull(PageInfoType)),
            'edges': GraphQLField(type=GraphQLList(Edge)),
        })
    return Connection
Пример #16
0
 def test_object_types_are_merged_to_object_type_with_union_of_fields(self):
     self._assert_merge(
         [
             GraphQLObjectType("Object", fields={
                 "id": GraphQLField(type=GraphQLInt),
             }),
             GraphQLObjectType("Object", fields={
                 "name": GraphQLField(type=GraphQLString),
             }),
         ],
         is_object_type(fields=has_entries({
             "id": is_field(type=is_int),
             "name": is_field(type=is_string),
         })),
     )
Пример #17
0
 def test_when_field_has_extra_non_null_args_then_is_not_subtype(self):
     assert not is_subtype(
         GraphQLObjectType("Object", {
             "value": GraphQLField(
                 type=GraphQLNonNull(GraphQLInt),
                 args={"id": GraphQLArgument(type=GraphQLNonNull(GraphQLInt))},
             )
         }),
         GraphQLObjectType("Object", {
             "value": GraphQLField(
                 type=GraphQLNonNull(GraphQLInt),
                 args={},
             )
         }),
     )
Пример #18
0
 def test_when_field_has_arg_of_supertype_then_is_subtype(self):
     assert is_subtype(
         GraphQLObjectType("Object", {
             "value": GraphQLField(
                 type=GraphQLNonNull(GraphQLInt),
                 args={"id": GraphQLArgument(type=GraphQLInt)},
             )
         }),
         GraphQLObjectType("Object", {
             "value": GraphQLField(
                 type=GraphQLNonNull(GraphQLInt),
                 args={"id": GraphQLArgument(type=GraphQLNonNull(GraphQLInt))},
             )
         }),
     )
Пример #19
0
 def test_when_arg_is_missing_then_is_not_subtype(self):
     assert not is_subtype(
         GraphQLObjectType("Object", {
             "value": GraphQLField(
                 type=GraphQLNonNull(GraphQLInt),
                 args={},
             )
         }),
         GraphQLObjectType("Object", {
             "value": GraphQLField(
                 type=GraphQLNonNull(GraphQLInt),
                 args={"id": GraphQLArgument(type=GraphQLInt)},
             )
         }),
     )
Пример #20
0
    def __init__(self):
        super().__init__()
        self.report_type = GraphQLObjectType(
            "Status",
            lambda: {
                "id":
                GraphQLField(
                    GraphQLNonNull(GraphQLString),
                    description="The id of the API instance.",
                ),
                "currentStatus":
                GraphQLField(GraphQLString, description="The API status."),
                "currentTime":
                GraphQLField(GraphQLString,
                             description="The current API time."),
            },
        )

        self.q = {
            "Status": GraphQLField(self.report_type, resolve=self.get_report)
        }

        self.s = {
            "Status":
            GraphQLField(self.report_type,
                         subscribe=self.sub_report,
                         resolve=None)
        }
Пример #21
0
def build_object_type(model: DeclarativeMeta,
                      objects: Objects) -> GraphQLObjectType:
    def get_column_field(column: Column) -> GraphQLOutputType:
        if column.nullable:
            return get_graphql_type_from_column(column.type)
        else:
            return GraphQLNonNull(get_graphql_type_from_column(column.type))

    def get_relationship_field(
            relationship: RelationshipProperty) -> GraphQLOutputType:
        if relationship.direction in (interfaces.ONETOMANY,
                                      interfaces.MANYTOMANY):
            return GraphQLList(objects[get_table_name(
                relationship.mapper.entity)])
        else:
            return objects[get_table_name(relationship.mapper.entity)]

    def get_fields() -> GraphQLFieldMap:
        fields = {
            **{
                column.name: GraphQLField(get_column_field(column),
                                          resolve=make_field_resolver(column.name))
                for column in get_table(model).columns
            },
            **{
                name: GraphQLField(get_relationship_field(relationship),
                                   resolve=make_field_resolver(name))
                for name, relationship in get_relationships(model)
            },
        }

        return fields

    return GraphQLObjectType(get_table_name(model), get_fields)
Пример #22
0
def _common_subtype(left, right):
    if left == right:
        return left

    elif isinstance(left, GraphQLNonNull) and isinstance(
            right, GraphQLNonNull):
        return GraphQLNonNull(_common_subtype(left.of_type, right.of_type))

    elif isinstance(left, GraphQLNonNull):
        return GraphQLNonNull(_common_subtype(left.of_type, right))

    elif isinstance(right, GraphQLNonNull):
        return GraphQLNonNull(_common_subtype(left, right.of_type))

    elif isinstance(left, GraphQLList) and isinstance(right, GraphQLList):
        return GraphQLList(_common_subtype(left.of_type, right.of_type))

    elif isinstance(left, GraphQLObjectType) and isinstance(
            right, GraphQLObjectType):
        fields = dict((field_name,
                       _common_subfield(left.fields.get(field_name),
                                        right.fields.get(field_name)))
                      for field_name in set(left.fields.keys())
                      | set(right.fields.keys()))
        return GraphQLObjectType(left.name, fields=fields)

    elif isinstance(left, GraphQLSchema) and isinstance(right, GraphQLSchema):
        return GraphQLSchema(query=_common_subtype(left.get_query_type(),
                                                   right.get_query_type()))

    else:
        raise ValueError("Cannot find common subtype")
Пример #23
0
 def create_type():
     return GraphQLObjectType(
         name='TodoItem',
         fields=lambda: {
             'text': GraphQLField(type=GraphQLString),
         },
     )
    def __init__(self):
        super().__init__(self)
        self.nav_sat_fix_data = {"uuid": "test"}

        self.nav_sat_fix_message_type = GraphQLObjectType(
            "NavSatFix",
            lambda: {
                "uuid":
                GraphQLField(GraphQLString,
                             description="The uuid of the vehicle."),
                "seq":
                GraphQLField(GraphQLInt,
                             description="The sequence number of the message."
                             ),
                "secs":
                GraphQLField(GraphQLInt, description=""),
                "nsecs":
                GraphQLField(GraphQLInt, description=""),
                "frameId":
                GraphQLField(GraphQLString, description=""),
                "statusStatus":
                GraphQLField(GraphQLInt, description=""),
                "statusService":
                GraphQLField(GraphQLInt, description=""),
                "latitude":
                GraphQLField(GraphQLFloat, description=""),
                "longitude":
                GraphQLField(GraphQLFloat, description=""),
                "altitude":
                GraphQLField(GraphQLFloat, description=""),
                # TODO: position_covariance array
                "positionCovarianceType":
                GraphQLField(GraphQLInt, description=""),
            },
            description="MAVROS NavSatFixMessage",
        )

        self.q = {
            "NavSatFix":
            GraphQLField(self.nav_sat_fix_message_type,
                         resolve=self.get_nav_sat_fix_message)
        }

        self.m = {
            "NavSatFix":
            GraphQLField(
                self.nav_sat_fix_message_type,
                args=self.get_mutation_args(self.nav_sat_fix_message_type),
                resolve=self.set_nav_sat_fix_message,
            )
        }

        self.s = {
            "NavSatFix":
            GraphQLField(
                self.nav_sat_fix_message_type,
                subscribe=self.sub_nav_sat_fix_message,
                resolve=None,
            )
        }
Пример #25
0
def build_object_type(model: DeclarativeMeta,
                      objects: Objects) -> GraphQLObjectType:
    def get_fields() -> GraphQLFieldMap:
        fields = {}

        for column in get_table(model).columns:
            graphql_type: GraphQLOutputType = get_graphql_type_from_column(
                column.type)
            if not column.nullable:
                graphql_type = GraphQLNonNull(graphql_type)

            fields[column.name] = GraphQLField(graphql_type,
                                               resolve=make_field_resolver(
                                                   column.name))

        for name, relationship in get_relationships(model):
            object_type: GraphQLOutputType = objects[get_table_name(
                relationship.mapper.entity)]
            if relationship.direction in (interfaces.ONETOMANY,
                                          interfaces.MANYTOMANY):
                object_type = GraphQLList(object_type)

            fields[name] = GraphQLField(object_type,
                                        resolve=make_field_resolver(name))

        return fields

    return GraphQLObjectType(get_table_name(model), get_fields)
Пример #26
0
    def from_object(self, object_type: TypeDefinition) -> GraphQLObjectType:
        # TODO: Use StrawberryObjectType when it's implemented in another PR

        # Don't reevaluate known types
        if object_type.name in self.type_map:
            graphql_object_type = self.type_map[
                object_type.name].implementation
            assert isinstance(graphql_object_type,
                              GraphQLObjectType)  # For mypy
            return graphql_object_type

        def get_graphql_fields() -> Dict[str, GraphQLField]:
            graphql_fields = {}

            for field in object_type.fields:
                field_name = field.get_graphql_name(
                    self.config.auto_camel_case)

                graphql_fields[field_name] = self.from_field(field)

            return graphql_fields

        graphql_object_type = GraphQLObjectType(
            name=object_type.name,
            fields=get_graphql_fields,
            interfaces=list(map(self.from_interface, object_type.interfaces)),
            description=object_type.description,
        )

        self.type_map[object_type.name] = ConcreteType(
            definition=object_type, implementation=graphql_object_type)

        return graphql_object_type
Пример #27
0
    def __init__(self, edb_schema):
        '''Create a graphql schema based on edgedb schema.'''

        self.edb_schema = edb_schema
        # extract and sort modules to have a consistent type ordering
        self.modules = {
            m.name for m in
            self.edb_schema.get_modules()
        } - {'schema', 'graphql'}
        self.modules = list(self.modules)
        self.modules.sort()

        self._gql_interfaces = {}
        self._gql_objtypes = {}
        self._gql_inobjtypes = {}
        self._gql_ordertypes = {}

        self._define_types()

        query = self._gql_objtypes['Query'] = GraphQLObjectType(
            name='Query',
            fields=self.get_fields('Query'),
        )

        # get a sorted list of types relevant for the Schema
        types = [
            objt for name, objt in
            itertools.chain(self._gql_objtypes.items(),
                            self._gql_inobjtypes.items())
            # the Query is included separately
            if name != 'Query'
        ]
        types = sorted(types, key=lambda x: x.name)
        self._gql_schema = GraphQLSchema(query=query, types=types)
 def create_type():
     return GraphQLObjectType(
         name='WorksheetInstance',
         fields=lambda: {
             'id':
             GraphQLField(
                 type=req(GraphQLID),
                 resolver=lambda obj, args, *_: obj.obj_id(*args),
             ),
             'reportRecordNumber':
             GraphQLField(
                 type=req(GraphQLInt),
                 resolver=lambda obj, args, *_: obj.report_record_number(
                     *args),
             ),
             'worksheetCode':
             GraphQLField(
                 type=req(GraphQLString),
                 resolver=lambda obj, args, *_: obj.worksheet_code(*args),
             ),
             'entries':
             GraphQLField(type=req(
                 list_of(req(GraphQLWorksheetEntry.type())))),
         },
     )
Пример #29
0
def test_accepts_type_definition_with_sync_subscribe_function():
    # type: () -> None
    SubscriptionType = GraphQLObjectType(
        name="Subscription",
        fields=OrderedDict([(
            "importantEmail",
            GraphQLField(EmailEventType,
                         resolver=lambda *_: Observable.from_([None])),
        )]),
    )
    test_schema = GraphQLSchema(query=QueryType, subscription=SubscriptionType)

    stream = Subject()
    send_important_email, subscription = create_subscription(
        stream, test_schema)

    email = Email(
        from_="*****@*****.**",
        subject="Alright",
        message="Tests are good",
        unread=True,
    )
    inbox = []
    subscription.subscribe(inbox.append)
    send_important_email(email)
    assert len(inbox) == 1
    assert inbox[0].data == {"importantEmail": None}
Пример #30
0
def test_accepts_multiple_subscription_fields_defined_in_schema():
    # type: () -> None
    SubscriptionTypeMultiple = GraphQLObjectType(
        name="Subscription",
        fields=OrderedDict([
            ("importantEmail", GraphQLField(EmailEventType)),
            ("nonImportantEmail", GraphQLField(EmailEventType)),
        ]),
    )
    test_schema = GraphQLSchema(query=QueryType,
                                subscription=SubscriptionTypeMultiple)

    stream = Subject()
    send_important_email, subscription = create_subscription(
        stream, test_schema)

    email = Email(
        from_="*****@*****.**",
        subject="Alright",
        message="Tests are good",
        unread=True,
    )
    inbox = []
    stream.subscribe(inbox.append)
    send_important_email(email)
    assert len(inbox) == 1
    assert inbox[0][0] == email