Example #1
0
def create_input_filter_field(column):
    from graphene_sqlalchemy.converter import convert_sqlalchemy_type

    graphene_type = convert_sqlalchemy_type(column.type, column)
    if graphene_type.__class__ == Field:  # TODO enum not supported
        return None
    name = str(graphene_type.__class__) + "Filter"

    if name in _INPUT_FIELDS_CACHE:
        return Field(_INPUT_FIELDS_CACHE[name])

    field_class = Filter
    fields = OrderedDict()
    fields["eq"] = Field(graphene_type.__class__,
                         description="Field should be equal to given value")
    fields["ne"] = Field(
        graphene_type.__class__,
        description="Field should not be equal to given value")
    fields["lt"] = Field(graphene_type.__class__,
                         description="Field should be less then given value")
    fields["gt"] = Field(graphene_type.__class__,
                         description="Field should be great then given value")
    fields["like"] = Field(
        graphene_type.__class__,
        description="Field should have a pattern of given value",
    )
    # TODO construct operators based on __class__
    # TODO complex filter support: OR

    field_class = type(name, (field_class, graphene.InputObjectType), {})
    field_class._meta.fields.update(fields)
    _INPUT_FIELDS_CACHE[name] = field_class
    return Field(field_class)
    def _generate_default_filters(
            cls, model,
            field_filters: 'Dict[str, Union[Iterable[str], Any]]') -> dict:
        """
        Generate GraphQL fields from SQLAlchemy model columns.

        Args:
            model: SQLAlchemy model.
            field_filters: Filters for fields.

        Returns:
            GraphQL fields dictionary:
            field name (key) - field instance (value).

        """
        graphql_filters = {}
        filters_map = cls.ALLOWED_FILTERS

        model_fields = {
            c.name: {
                'column': c,
                'type': c.type,
                'nullable': c.nullable
            }
            for c in model.__table__.columns if c.name in field_filters
        }

        for field_name, field_object in model_fields.items():
            column_type = field_object['type']

            expressions = field_filters[field_name]
            if expressions == cls.ALL:
                type_class = column_type.__class__
                try:
                    expressions = filters_map[type_class].copy()
                except KeyError:
                    for type_, exprs in filters_map.items():
                        if issubclass(type_class, type_):
                            expressions = exprs.copy()
                            break
                    else:
                        raise KeyError('Unsupported column type. '
                                       'Hint: use EXTRA_ALLOWED_FILTERS.')

                if field_object['nullable']:
                    expressions.append(cls.IS_NULL)

            field_type = convert_sqlalchemy_type(column_type,
                                                 field_object['column'])

            fields = cls._generate_filter_fields(expressions, field_name,
                                                 field_type,
                                                 field_object['nullable'])
            for name, field in fields.items():
                graphql_filters[name] = get_field_as(field,
                                                     graphene.InputField)

        return graphql_filters
    def _generate_default_filters(
            cls, model,
            field_filters: 'Dict[str, Union[Iterable[str], Any]]') -> dict:
        """
        Generate GraphQL fields from SQLAlchemy model columns.

        Args:
            model: SQLAlchemy model.
            field_filters: Filters for fields.

        Returns:
            GraphQL fields dictionary:
            field name (key) - field instance (value).

        """
        graphql_filters = {}
        filters_map = cls.ALLOWED_FILTERS

        model_fields = {
            c.name: {
                'column': c,
                'type': c.type,
                'nullable': c.nullable
            }
            for c in model.__table__.columns if c.name in field_filters
        }

        for field_name, field_object in model_fields.items():
            column_type = field_object['type']

            expressions = field_filters[field_name]
            if expressions == cls.ALL:
                expressions = filters_map[column_type.__class__].copy()
                if field_object['nullable']:
                    expressions.append(cls.IS_NULL)

            field_type = convert_sqlalchemy_type(column_type,
                                                 field_object['column'])

            fields = cls._generate_filter_fields(expressions, field_name,
                                                 field_type,
                                                 field_object['nullable'])
            for name, field in fields.items():
                graphql_filters[name] = get_field_as(field,
                                                     graphene.InputField)

        return graphql_filters
    def _get_gql_type_from_sqla_type(
        cls, column_type, sqla_column
    ) -> 'Union[Type[graphene.ObjectType], Type[GenericScalar]]':
        """
        Get GraphQL type from SQLAlchemy column.

        Args:
            column_type: SQLAlchemy column type.
            sqla_column: SQLAlchemy column or hybrid attribute.

        Returns:
            GraphQL type.

        """
        if column_type is None:
            return GenericScalar
        else:
            return convert_sqlalchemy_type(column_type, sqla_column)
Example #5
0
    def _get_gql_type_from_sqla_type(
        cls, column_type, sqla_column
    ) -> 'Union[Type[graphene.ObjectType], Type[GenericScalar]]':
        """
        Get GraphQL type from SQLAlchemy column.

        Args:
            column_type: SQLAlchemy column type.
            sqla_column: SQLAlchemy column or hybrid attribute.

        Returns:
            GraphQL type.

        """
        if column_type is None:
            return GenericScalar
        else:
            _type = convert_sqlalchemy_type(column_type, sqla_column)
            if inspect.isfunction(_type):
                return _type()  # only graphene-sqlalchemy>2.2.0
            return _type
Example #6
0
def create_auth_schema():
    def login_mutate(cls, info, model=None, **kwargs):
        query = model.get_query(info)
        model = model._meta.model
        data = kwargs.get('data', {})

        if 'email' in data and 'password' in data:
            try:
                user = query.filter(model.email == data['email']).one()
            except NoResultFound:
                raise CodeduExceptionHandler(
                    HTTPBadRequest(description="USER NOT FOUND"))

            if user.password == hmac.new(Config.SECRET_KEY.encode(),
                                         data['password'].encode(),
                                         sha256).hexdigest():
                encoded_jwt = jwt.encode(
                    {
                        'iat': datetime.datetime.utcnow(),
                        'user_id': user.id,
                        'email': data['email'],
                        'admin': user.admin,
                        # 'exp':datetime.datetime.utcnow() + datetime.timedelta(seconds=30),
                    },
                    Config.SECRET_KEY,
                    algorithm='HS256')

                return cls(**{'user': user, 'token': encoded_jwt.decode()})
            else:
                raise CodeduExceptionHandler(
                    HTTPBadRequest(description="INVALID PASSWORD"))
        else:
            raise CodeduExceptionHandler(
                HTTPBadRequest(description="INVALID PARAMETER"))

    def register_mutate(cls, info, model=None, **kwargs):
        model = model._meta.model
        data = kwargs.get('data', None)
        image_info = info.context.get('image_info', None)
        if data:
            validate_user_data(data)
            data['password'] = hmac.new(Config.SECRET_KEY.encode(),
                                        data['password'].encode(),
                                        sha256).hexdigest()
            db_session = info.context.get('session', None)
            if db_session:
                instance = model(**data)
                db_session.add(instance)
                db_session_flush(db_session)

                if image_info:
                    image_handle('user', instance, image_info[0])

            return cls(**{model.__tablename__: instance})

    def update_user_info_mutate(cls, info, model=None, **kwargs):
        if info.context['auth']['data']:
            query = model.get_query(info)
            model = model._meta.model
            data = kwargs.get('data', None)
            image_info = info.context.get('image_info', None)
            if data:
                validate_user_data(data)
                if not info.context['auth']['data']['admin']:
                    data['id'] = info.context['auth']['data']['user_id']
                    data['password'] = hmac.new(Config.SECRET_KEY.encode(),
                                                data['password'].encode(),
                                                sha256).hexdigest()
                else:
                    if not 'id' in data:
                        CodeduExceptionHandler(
                            HTTPBadRequest(description="INVALID PARAMETER"))
                    if 'password' in data: del data['password']

                instance = get_instance_by_pk(query, model, data)

                if info.context['auth']['data']['admin'] or (
                        instance.one()
                        and instance.one().password == data['password']):
                    instance.update(data)
                    if image_info:
                        image_handle('user', instance.one(), image_info[0])
                    return cls(**{model.__tablename__: instance.one()})
                else:
                    raise CodeduExceptionHandler(
                        HTTPBadRequest(description="INVALID PASSWORD"))
        else:
            raise CodeduExceptionHandler(
                HTTPUnauthorized(
                    description=info.context['auth']['description']))

    def update_password_mutate(cls, info, model=None, **kwargs):
        if info.context['auth']['data']:
            query = model.get_query(info)
            model = model._meta.model
            data = kwargs.get('data', None)
            if data:
                validate_user_data(data)
                if not info.context['auth']['data']['admin']:
                    data['id'] = info.context['auth']['data']['user_id']
                    data['password'] = hmac.new(Config.SECRET_KEY.encode(),
                                                data['password'].encode(),
                                                sha256).hexdigest()
                data["password_modified"] = datetime.datetime.utcnow()

                instance = get_instance_by_pk(query, model, data)

                if info.context['auth']['data']['admin'] or instance.one(
                ).password == data['password']:
                    data['password'] = hmac.new(Config.SECRET_KEY.encode(),
                                                data['new_password'].encode(),
                                                sha256).hexdigest()
                    del data['new_password']
                    instance.update(data)
                    return cls(**{model.__tablename__: instance.one()})
                else:
                    raise CodeduExceptionHandler(
                        HTTPBadRequest(description="INVALID PASSWORD"))
        else:
            raise CodeduExceptionHandler(
                HTTPUnauthorized(
                    description=info.context['auth']['description']))

    def delete_account_mutate(cls, info, model=None, **kwargs):
        if info.context['auth']['data']:
            query = model.get_query(info)
            model = model._meta.model
            data = kwargs.get('data', None)
            if data:
                if not info.context['auth']['data']['admin']:
                    data['id'] = info.context['auth']['data']['user_id']
                    data['password'] = hmac.new(Config.SECRET_KEY.encode(),
                                                data['password'].encode(),
                                                sha256).hexdigest()

                instance = get_instance_by_pk(query, model, data)

                if info.context['auth']['data']['admin'] or instance.one(
                ).password == data['password']:
                    tmp_instance = instance.one()
                    instance.delete()
                    image_handle("user", tmp_instance, None)
                    return cls(**{model.__tablename__: tmp_instance})
                else:
                    raise CodeduExceptionHandler(
                        HTTPBadRequest(description="INVALID PASSWORD"))
        else:
            raise CodeduExceptionHandler(
                HTTPUnauthorized(
                    description=info.context['auth']['description']))

    query_field = {}
    mutation_field = {}

    query_field["login"] = create_mutation_field(
        "Login", gql_models['user'], login_mutate,
        create_input_class(
            'LoginInput', {
                'email': graphene.String(required=True),
                'password': graphene.String(required=True),
            }), {
                'token': graphene.String(),
            })

    mutation_field["register"] = create_mutation_field(
        "Register",
        gql_models['user'],
        register_mutate,
        create_input_class(
            'RegisterInput', {
                'username': graphene.String(required=True),
                'email': graphene.String(required=True),
                'password': graphene.String(required=True),
                'admin': graphene.Boolean(),
            }),
    )

    fields = {}
    for colname, column in gql_models[
            'user']._meta.model.__table__.columns.items():
        if not colname in ['created', 'modified', 'email']:
            fields[colname] = convert_sqlalchemy_type(
                getattr(column, 'type', None), column)()

    mutation_field["update_user_info"] = create_mutation_field(
        "UpdateUserInfo",
        gql_models['user'],
        update_user_info_mutate,
        create_input_class('UpdateUserInfoInput', fields),
    )

    mutation_field["update_password"] = create_mutation_field(
        "UpdatePassword",
        gql_models['user'],
        update_password_mutate,
        create_input_class(
            'UpdatePasswordInput', {
                'password': graphene.String(),
                'new_password': graphene.String(required=True),
                'id': graphene.ID(),
            }),
    )
    mutation_field["delete_account"] = create_mutation_field(
        "DeleteAccount",
        gql_models['user'],
        delete_account_mutate,
        create_input_class('DeleteAccountInput', {
            'password': graphene.String(),
            'id': graphene.ID(),
        }),
    )

    return (query_field, mutation_field)
Example #7
0
def create_base_schema():
    def resolve_model(self, info, model, **kwargs):
        query = model.get_query(info)
        search = info.context.get('search', None)

        if 'password' in kwargs:
            raise CodeduExceptionHandler(
                HTTPBadRequest(
                    description="you can't find user with password"))

        for arg, value in kwargs.items():
            if search:
                print('search')
                query = query.filter(
                    getattr(model._meta.model, arg).like(f"%{value}%"))
            else:
                print('match')
                query = query.filter(getattr(model._meta.model, arg) == value)

        user = query.all()

        return user

    def create_mutate(cls, info, model=None, **kwargs):
        if info.context['auth']['data']:
            if not info.context['auth']['data']['admin']:
                raise CodeduExceptionHandler(
                    HTTPUnauthorized(description='PERMISSION DENIED'))
            model = model._meta.model
            data = kwargs.get('data', None)
            if data:
                db_session = info.context.get('session', None)
                if db_session:
                    instance = model(**data)
                    db_session.add(instance)
                    db_session_flush(db_session)

                return cls(**{model.__tablename__: instance})
        else:
            raise CodeduExceptionHandler(
                HTTPUnauthorized(
                    description=info.context['auth']['description']))

    def update_mutate(cls, info, model=None, **kwargs):
        if info.context['auth']['data']:
            if not info.context['auth']['data']['admin']:
                raise CodeduExceptionHandler(
                    HTTPUnauthorized(description='PERMISSION DENIED'))
            query = model.get_query(info)
            model = model._meta.model
            data = kwargs.get('data', None)
            if data:
                instance = get_instance_by_pk(query, model, data)

                instance.update(data)
                return cls(**{model.__tablename__: instance.one()})
        else:
            raise CodeduExceptionHandler(
                HTTPUnauthorized(
                    description=info.context['auth']['description']))

    def delete_mutate(cls, info, model=None, **kwargs):
        if info.context['auth']['data']:
            if not info.context['auth']['data']['admin']:
                raise CodeduExceptionHandler(
                    HTTPUnauthorized(description='PERMISSION DENIED'))
            query = model.get_query(info)
            model = model._meta.model
            data = kwargs.get('data', None)
            if data:
                instance = get_instance_by_pk(query, model, data)

                tmp_instance = instance.one()
                instance.delete()
                return cls(**{model.__tablename__: tmp_instance})
        else:
            raise CodeduExceptionHandler(
                HTTPUnauthorized(
                    description=info.context['auth']['description']))

    query_field = {}
    mutation_field = {}
    input_classes = {}
    filter_field = {}
    fcf_field = {}

    def random_quiz(cls, info, query, value, model):
        return query.order_by(func.rand()), None

    for tablename, model in gql_models.items():
        filter_class_fields = {}
        if tablename in ['lesson_quiz']:
            filter_class_fields['random'] = graphene.Boolean()
            filter_class_fields['random_filter'] = classmethod(
                lambda cls, info, query, value, model=model: random_quiz(
                    cls, info, query, value, model))
        filter_field[tablename] = create_filter_class(f"{tablename}Filter",
                                                      model._meta.model,
                                                      filter_class_fields)()
        fcf_field[model._meta.model] = filter_field[tablename]

    FCF = type("FCF", (FilterableConnectionField, ), {
        "filters": fcf_field,
    })
    except_table = [
        'user', 'post', 'post_comment', 'code', 'code_comment', 'question',
        'answer', 'like'
    ]
    except_table += [
        "post_like", "post_comment_like", "code_like", "code_comment_like",
        "question_like", "answer_like"
    ]
    for tablename, model in gql_models.items():
        if not tablename in except_table:
            fields = {}
            for colname, column in model._meta.model.__table__.columns.items():
                if not colname == 'created' and not colname == 'modified':
                    fields[colname] = convert_sqlalchemy_type(
                        getattr(column, 'type', None), column)()
            for colname, column in model._meta.model.__mapper__.relationships.items(
            ):
                fields[colname] = convert_sqlalchemy_type(
                    getattr(column, 'type', None), column)()

            input_classes[tablename] = create_input_class(
                f"{tablename}Input", fields)

            mutation_field[f"create_{tablename}"] = create_mutation_field(
                f"Create{tablename}", model, create_mutate,
                input_classes[tablename])
            mutation_field[f"update_{tablename}"] = create_mutation_field(
                f"Update{tablename}", model, update_mutate,
                input_classes[tablename])
            mutation_field[f"delete_{tablename}"] = create_mutation_field(
                f"Delete{tablename}", model, delete_mutate,
                input_classes[tablename])

        tmp_node = create_node_class(f"{tablename}Node", model._meta.model,
                                     FCF.factory)

        tmp_node._meta.connection.total_count = graphene.Int()
        tmp_node._meta.connection.resolve_total_count = lambda self, info, **kwargs: self.length
        tmp_node._meta.connection._meta.fields["total_count"] = graphene.Field(
            graphene.NonNull(graphene.Int))

        tmp_connection = create_connection_class(
            f"{tablename}Connection",
            tmp_node,
        )
        query_field[tablename] = FCF(tmp_connection)

    return (query_field, mutation_field)