module Neo4j module Server # The CypherTransaction object lifecycle is as follows: # * It is initialized with the transactional endpoint URL and the connection object to use for communication. It does not communicate with the server to create this. # * The first query within the transaction sets the commit and execution addresses, :commit_url and :exec_url. # * At any time, `failure` can be called to mark a transaction failed and trigger a rollback upon closure. # * `close` is called to end the transaction. It calls `_commit_tx` or `_delete_tx`. # # If a transaction is created and then closed without performing any queries, an OpenStruct is returned that behaves like a successfully closed query. class CypherTransaction include Neo4j::Transaction::Instance include Neo4j::Core::CypherTranslator include Resource attr_reader :commit_url, :exec_url, :base_url, :connection def initialize(url, session_connection) @base_url = url @connection = session_connection register_instance end ROW_REST = %w(row REST) def _query(cypher_query, params = nil) fail 'Transaction expired, unable to perform query' if expired? statement = {statement: cypher_query, parameters: params, resultDataContents: ROW_REST} body = {statements: [statement]} response = exec_url && commit_url ? connection.post(exec_url, body) : register_urls(body) _create_cypher_response(response) end def _delete_tx _tx_query(:delete, exec_url, headers: resource_headers) end def _commit_tx _tx_query(:post, commit_url, nil) end private def _tx_query(action, endpoint, headers = {}) return empty_response if !commit_url || expired? response = connection.send(action, endpoint, headers) expect_response_code(response, 200) response end def register_urls(body) response = connection.post(base_url, body) @commit_url = response.body['commit'] @exec_url = response.headers['Location'] fail "NO ENDPOINT URL #{connection} : HEAD: #{response.headers.inspect}" if !exec_url || exec_url.empty? init_resource_data(response.body, base_url) expect_response_code(response, 201) response end def _create_cypher_response(response) first_result = response.body['results'][0] cr = CypherResponse.new(response, true) if response.body['errors'].empty? cr.set_data(first_result['data'], first_result['columns']) else first_error = response.body['errors'].first expired if first_error['message'].match(/Unrecognized transaction id/) cr.set_error(first_error['message'], first_error['code'], first_error['code']) end cr end def empty_response OpenStruct.new(status: 200, body: '') end end end end