def test_can_handle_json_error_from_delete(graph):
    node, = graph.create({})
    non_existent_property = Resource(node.properties.resource.uri.string + "/foo")
    try:
        non_existent_property.delete()
    except GraphError:
        assert True
    else:
        assert False
def test_can_handle_other_error_from_delete():
    with patch.object(_Resource, "delete") as mocked:
        mocked.side_effect = DodgyServerError
        resource = Resource("http://localhost:7474/db/data/node/spam")
        try:
            resource.delete()
        except GraphError as error:
            assert isinstance(error.__cause__, DodgyServerError)
        else:
            assert False
Exemple #3
0
def test_can_handle_other_error_from_delete():
    with patch.object(_Resource, "delete") as mocked:
        mocked.side_effect = DodgyServerError
        resource = Resource("http://localhost:7474/db/data/node/spam")
        try:
            resource.delete()
        except GraphError as error:
            assert isinstance(error.__cause__, DodgyServerError)
        else:
            assert False
Exemple #4
0
def test_can_handle_409(graph):
    node_id = get_attached_node_id(graph)
    resource = Resource("http://localhost:7474/db/data/node/%s" % node_id)
    try:
        resource.delete()
    except GraphError as error:
        assert_error(
            error, (GraphError,), "org.neo4j.server.rest.web.OperationFailureException",
            (_ClientError, _Response), 409)
    else:
        assert False
def test_can_handle_json_error_from_delete(graph):
    node, = graph.create({})
    non_existent_property = Resource(node.properties.resource.uri.string + "/foo")
    try:
        non_existent_property.delete()
    except GraphError as error:
        cause = error.__cause__
        assert isinstance(cause, _ClientError)
        assert isinstance(cause, _Response)
        assert cause.status_code == 404
    else:
        assert False
Exemple #6
0
def test_can_handle_json_error_from_delete(graph):
    node, = graph.create({})
    non_existent_property = Resource(node.properties.resource.uri.string +
                                     "/foo")
    try:
        non_existent_property.delete()
    except GraphError as error:
        cause = error.__cause__
        assert isinstance(cause, _ClientError)
        assert isinstance(cause, _Response)
        assert cause.status_code == 404
    else:
        assert False
Exemple #7
0
class CypherTransaction(object):
    """ A transaction is a transient resource that allows multiple Cypher
    statements to be executed within a single server transaction.
    """

    error_class = CypherTransactionError

    def __init__(self, uri):
        log.info("begin")
        self.statements = []
        self.__begin = Resource(uri)
        self.__begin_commit = Resource(uri + "/commit")
        self.__execute = None
        self.__commit = None
        self.__finished = False
        self.graph = self.__begin.graph

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.commit()

    def __assert_unfinished(self):
        if self.__finished:
            raise Finished(self)

    @property
    def _id(self):
        """ The internal server ID of this transaction, if available.
        """
        if self.__execute is None:
            return None
        else:
            return int(self.__execute.uri.path.segments[-1])

    @property
    def finished(self):
        """ Indicates whether or not this transaction has been completed or is
        still open.

        :return: :py:const:`True` if this transaction has finished,
                 :py:const:`False` otherwise
        """
        return self.__finished

    def append(self, statement, parameters=None):
        """ Add a statement to the current queue of statements to be
        executed.

        :param statement: the statement to append
        :param parameters: a dictionary of execution parameters
        """
        self.__assert_unfinished()
        p = {}
        if parameters:
            for key, value in parameters.items():
                if isinstance(value, (Node, Rel, Relationship)):
                    value = value._id
                p[key] = value
        # OrderedDict is used here to avoid statement/parameters ordering bug
        log.info("append %r %r", statement, p)
        self.statements.append(OrderedDict([
            ("statement", statement),
            ("parameters", p),
            ("resultDataContents", ["REST"]),
        ]))

    def post(self, resource):
        self.__assert_unfinished()
        rs = resource.post({"statements": self.statements})
        location = rs.location
        if location:
            self.__execute = Resource(location)
        j = rs.content
        rs.close()
        self.statements = []
        if "commit" in j:
            self.__commit = Resource(j["commit"])
        if "errors" in j:
            errors = j["errors"]
            if len(errors) >= 1:
                error = errors[0]
                raise self.error_class.hydrate(error)
        out = RecordListList()
        for result in j["results"]:
            columns = result["columns"]
            producer = RecordProducer(columns)
            out.append(RecordList(columns, [producer.produce(self.graph.hydrate(r["rest"]))
                                            for r in result["data"]]))
        return out

    def process(self):
        """ Send all pending statements to the server for execution, leaving
        the transaction open for further statements. Along with
        :meth:`append <.CypherTransaction.append>`, this method can be used to
        batch up a number of individual statements into a single HTTP request::

            from py2neo import Graph

            graph = Graph()
            statement = "MERGE (n:Person {name:{N}}) RETURN n"

            tx = graph.cypher.begin()

            def add_names(*names):
                for name in names:
                    tx.append(statement, {"N": name})
                tx.process()

            add_names("Homer", "Marge", "Bart", "Lisa", "Maggie")
            add_names("Peter", "Lois", "Chris", "Meg", "Stewie")

            tx.commit()

        :return: list of results from pending statements
        """
        log.info("process")
        return self.post(self.__execute or self.__begin)

    def commit(self):
        """ Send all pending statements to the server for execution and commit
        the transaction.

        :return: list of results from pending statements
        """
        log.info("commit")
        try:
            return self.post(self.__commit or self.__begin_commit)
        finally:
            self.__finished = True

    def rollback(self):
        """ Rollback the current transaction.
        """
        self.__assert_unfinished()
        log.info("rollback")
        try:
            if self.__execute:
                self.__execute.delete()
        finally:
            self.__finished = True
Exemple #8
0
class CypherTransaction(object):
    """ A transaction is a transient resource that allows multiple Cypher
    statements to be executed within a single server transaction.
    """

    error_class = CypherTransactionError

    def __init__(self, uri):
        log.info("begin")
        self.statements = []
        self.__begin = Resource(uri)
        self.__begin_commit = Resource(uri + "/commit")
        self.__execute = None
        self.__commit = None
        self.__finished = False
        self.graph = self.__begin.graph

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.commit()

    def __assert_unfinished(self):
        if self.__finished:
            raise Finished(self)

    @property
    def _id(self):
        """ The internal server ID of this transaction, if available.
        """
        if self.__execute is None:
            return None
        else:
            return int(self.__execute.uri.path.segments[-1])

    @property
    def finished(self):
        """ Indicates whether or not this transaction has been completed or is
        still open.

        :return: :py:const:`True` if this transaction has finished,
                 :py:const:`False` otherwise
        """
        return self.__finished

    def append(self, statement, parameters=None, **kwparameters):
        """ Add a statement to the current queue of statements to be
        executed.

        :arg statement: the statement to append
        :arg parameters: a dictionary of execution parameters
        """
        self.__assert_unfinished()

        s = ustr(statement)
        p = {}

        def add_parameters(params):
            if params:
                for k, v in dict(params).items():
                    if isinstance(v, (Node, Rel, Relationship)):
                        v = v._id
                    p[k] = v

        try:
            add_parameters(statement.parameters)
        except AttributeError:
            pass
        add_parameters(dict(parameters or {}, **kwparameters))

        s, p = presubstitute(s, p)

        # OrderedDict is used here to avoid statement/parameters ordering bug
        log.info("append %r %r", s, p)
        self.statements.append(
            OrderedDict([
                ("statement", s),
                ("parameters", p),
                ("resultDataContents", ["REST"]),
            ]))

    def post(self, resource):
        self.__assert_unfinished()
        rs = resource.post({"statements": self.statements})
        location = rs.location
        if location:
            self.__execute = Resource(location)
        j = rs.content
        rs.close()
        self.statements = []
        if "commit" in j:
            self.__commit = Resource(j["commit"])
        if "errors" in j:
            errors = j["errors"]
            if len(errors) >= 1:
                error = errors[0]
                raise self.error_class.hydrate(error)
        out = RecordListList()
        for result in j["results"]:
            columns = result["columns"]
            producer = RecordProducer(columns)
            out.append(
                RecordList(columns, [
                    producer.produce(self.graph.hydrate(r["rest"]))
                    for r in result["data"]
                ]))
        return out

    def process(self):
        """ Send all pending statements to the server for execution, leaving
        the transaction open for further statements. Along with
        :meth:`append <.CypherTransaction.append>`, this method can be used to
        batch up a number of individual statements into a single HTTP request::

            from py2neo import Graph

            graph = Graph()
            statement = "MERGE (n:Person {name:{N}}) RETURN n"

            tx = graph.cypher.begin()

            def add_names(*names):
                for name in names:
                    tx.append(statement, {"N": name})
                tx.process()

            add_names("Homer", "Marge", "Bart", "Lisa", "Maggie")
            add_names("Peter", "Lois", "Chris", "Meg", "Stewie")

            tx.commit()

        :return: list of results from pending statements
        """
        log.info("process")
        return self.post(self.__execute or self.__begin)

    def commit(self):
        """ Send all pending statements to the server for execution and commit
        the transaction.

        :return: list of results from pending statements
        """
        log.info("commit")
        try:
            return self.post(self.__commit or self.__begin_commit)
        finally:
            self.__finished = True

    def rollback(self):
        """ Rollback the current transaction.
        """
        self.__assert_unfinished()
        log.info("rollback")
        try:
            if self.__execute:
                self.__execute.delete()
        finally:
            self.__finished = True