neography-down 1.6.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.project +12 -0
  4. data/.rspec +1 -0
  5. data/.travis.yml +4 -0
  6. data/CHANGELOG.md +939 -0
  7. data/CONTRIBUTORS +18 -0
  8. data/Gemfile +4 -0
  9. data/Guardfile +14 -0
  10. data/LICENSE +19 -0
  11. data/README.md +281 -0
  12. data/Rakefile +14 -0
  13. data/examples/facebook.rb +40 -0
  14. data/examples/facebook_v2.rb +25 -0
  15. data/examples/greatest.rb +43 -0
  16. data/examples/linkedin.rb +39 -0
  17. data/examples/linkedin_v2.rb +22 -0
  18. data/examples/traversal_example1.rb +65 -0
  19. data/examples/traversal_example2.rb +54 -0
  20. data/lib/neography.rb +45 -0
  21. data/lib/neography/config.rb +64 -0
  22. data/lib/neography/connection.rb +263 -0
  23. data/lib/neography/equal.rb +21 -0
  24. data/lib/neography/errors.rb +60 -0
  25. data/lib/neography/index.rb +52 -0
  26. data/lib/neography/multi_json_parser.rb +28 -0
  27. data/lib/neography/neography.rb +10 -0
  28. data/lib/neography/node.rb +63 -0
  29. data/lib/neography/node_path.rb +29 -0
  30. data/lib/neography/node_relationship.rb +37 -0
  31. data/lib/neography/node_traverser.rb +146 -0
  32. data/lib/neography/path_traverser.rb +100 -0
  33. data/lib/neography/property.rb +110 -0
  34. data/lib/neography/property_container.rb +28 -0
  35. data/lib/neography/railtie.rb +19 -0
  36. data/lib/neography/relationship.rb +78 -0
  37. data/lib/neography/relationship_traverser.rb +80 -0
  38. data/lib/neography/rest.rb +99 -0
  39. data/lib/neography/rest/batch.rb +414 -0
  40. data/lib/neography/rest/clean.rb +17 -0
  41. data/lib/neography/rest/constraints.rb +38 -0
  42. data/lib/neography/rest/cypher.rb +29 -0
  43. data/lib/neography/rest/extensions.rb +21 -0
  44. data/lib/neography/rest/gremlin.rb +20 -0
  45. data/lib/neography/rest/helpers.rb +96 -0
  46. data/lib/neography/rest/node_auto_indexes.rb +60 -0
  47. data/lib/neography/rest/node_indexes.rb +139 -0
  48. data/lib/neography/rest/node_labels.rb +49 -0
  49. data/lib/neography/rest/node_paths.rb +49 -0
  50. data/lib/neography/rest/node_properties.rb +52 -0
  51. data/lib/neography/rest/node_relationships.rb +33 -0
  52. data/lib/neography/rest/node_traversal.rb +25 -0
  53. data/lib/neography/rest/nodes.rb +94 -0
  54. data/lib/neography/rest/other_node_relationships.rb +38 -0
  55. data/lib/neography/rest/relationship_auto_indexes.rb +60 -0
  56. data/lib/neography/rest/relationship_indexes.rb +142 -0
  57. data/lib/neography/rest/relationship_properties.rb +52 -0
  58. data/lib/neography/rest/relationship_types.rb +11 -0
  59. data/lib/neography/rest/relationships.rb +16 -0
  60. data/lib/neography/rest/schema_indexes.rb +26 -0
  61. data/lib/neography/rest/spatial.rb +137 -0
  62. data/lib/neography/rest/transactions.rb +101 -0
  63. data/lib/neography/tasks.rb +207 -0
  64. data/lib/neography/version.rb +3 -0
  65. data/neography.gemspec +39 -0
  66. data/spec/integration/authorization_spec.rb +40 -0
  67. data/spec/integration/broken_spatial_spec.rb +28 -0
  68. data/spec/integration/index_spec.rb +71 -0
  69. data/spec/integration/neography_spec.rb +10 -0
  70. data/spec/integration/node_encoding_spec.rb +71 -0
  71. data/spec/integration/node_path_spec.rb +222 -0
  72. data/spec/integration/node_relationship_spec.rb +381 -0
  73. data/spec/integration/node_spec.rb +260 -0
  74. data/spec/integration/parsing_spec.rb +13 -0
  75. data/spec/integration/performance_spec.rb +17 -0
  76. data/spec/integration/relationship_spec.rb +37 -0
  77. data/spec/integration/rest_batch_no_streaming_spec.rb +41 -0
  78. data/spec/integration/rest_batch_spec.rb +604 -0
  79. data/spec/integration/rest_batch_streaming_spec.rb +51 -0
  80. data/spec/integration/rest_bulk_spec.rb +106 -0
  81. data/spec/integration/rest_constraints_spec.rb +72 -0
  82. data/spec/integration/rest_experimental_spec.rb +22 -0
  83. data/spec/integration/rest_gremlin_fail_spec.rb +46 -0
  84. data/spec/integration/rest_header_spec.rb +15 -0
  85. data/spec/integration/rest_index_spec.rb +481 -0
  86. data/spec/integration/rest_labels_spec.rb +128 -0
  87. data/spec/integration/rest_node_spec.rb +274 -0
  88. data/spec/integration/rest_other_node_relationship_spec.rb +137 -0
  89. data/spec/integration/rest_path_spec.rb +231 -0
  90. data/spec/integration/rest_plugin_spec.rb +177 -0
  91. data/spec/integration/rest_relationship_spec.rb +354 -0
  92. data/spec/integration/rest_relationship_types_spec.rb +18 -0
  93. data/spec/integration/rest_schema_index_spec.rb +32 -0
  94. data/spec/integration/rest_spatial_spec.rb +166 -0
  95. data/spec/integration/rest_transaction_spec.rb +166 -0
  96. data/spec/integration/rest_traverse_spec.rb +149 -0
  97. data/spec/integration/unmanaged_spec.rb +27 -0
  98. data/spec/matchers.rb +33 -0
  99. data/spec/neography_spec.rb +23 -0
  100. data/spec/spec_helper.rb +44 -0
  101. data/spec/unit/config_spec.rb +135 -0
  102. data/spec/unit/connection_spec.rb +284 -0
  103. data/spec/unit/node_spec.rb +100 -0
  104. data/spec/unit/properties_spec.rb +285 -0
  105. data/spec/unit/relationship_spec.rb +118 -0
  106. data/spec/unit/rest/batch_spec.rb +262 -0
  107. data/spec/unit/rest/clean_spec.rb +16 -0
  108. data/spec/unit/rest/constraints_spec.rb +45 -0
  109. data/spec/unit/rest/cypher_spec.rb +20 -0
  110. data/spec/unit/rest/extensions_spec.rb +28 -0
  111. data/spec/unit/rest/gremlin_spec.rb +25 -0
  112. data/spec/unit/rest/helpers_spec.rb +124 -0
  113. data/spec/unit/rest/labels_spec.rb +77 -0
  114. data/spec/unit/rest/node_auto_indexes_spec.rb +70 -0
  115. data/spec/unit/rest/node_indexes_spec.rb +140 -0
  116. data/spec/unit/rest/node_paths_spec.rb +77 -0
  117. data/spec/unit/rest/node_properties_spec.rb +79 -0
  118. data/spec/unit/rest/node_relationships_spec.rb +57 -0
  119. data/spec/unit/rest/node_traversal_spec.rb +35 -0
  120. data/spec/unit/rest/nodes_spec.rb +187 -0
  121. data/spec/unit/rest/relationship_auto_indexes_spec.rb +66 -0
  122. data/spec/unit/rest/relationship_indexes_spec.rb +132 -0
  123. data/spec/unit/rest/relationship_properties_spec.rb +79 -0
  124. data/spec/unit/rest/relationship_types_spec.rb +15 -0
  125. data/spec/unit/rest/relationships_spec.rb +21 -0
  126. data/spec/unit/rest/schema_index_spec.rb +30 -0
  127. data/spec/unit/rest/transactions_spec.rb +43 -0
  128. metadata +372 -0
@@ -0,0 +1,39 @@
1
+ require 'rubygems'
2
+ require 'neography'
3
+
4
+ @neo = Neography::Rest.new
5
+
6
+ def create_person(name)
7
+ @neo.create_node("name" => name)
8
+ end
9
+
10
+ def make_mutual_friends(node1, node2)
11
+ @neo.create_relationship("friends", node1, node2)
12
+ @neo.create_relationship("friends", node2, node1)
13
+ end
14
+
15
+ def degrees_of_separation(start_node, destination_node)
16
+ paths = @neo.get_paths(start_node, destination_node, {"type"=> "friends", "direction" => "in"}, depth=4, algorithm="allSimplePaths")
17
+ paths.each do |p|
18
+ p["names"] = p["nodes"].collect {|node| @neo.get_node_properties(node, "name")["name"] }
19
+ end
20
+
21
+ end
22
+
23
+ johnathan = create_person('Johnathan')
24
+ mark = create_person('Mark')
25
+ phill = create_person('Phill')
26
+ mary = create_person('Mary')
27
+
28
+ make_mutual_friends(johnathan, mark)
29
+ make_mutual_friends(mark, phill)
30
+ make_mutual_friends(phill, mary)
31
+ make_mutual_friends(mark, mary)
32
+
33
+ degrees_of_separation(johnathan, mary).each do |path|
34
+ puts path["names"].join(' => friends => ')
35
+ end
36
+
37
+ # RESULT
38
+ # Johnathan => friends => Mark => friends => Phill => friends => Mary
39
+ # Johnathan => friends => Mark => friends => Mary
@@ -0,0 +1,22 @@
1
+ require 'rubygems'
2
+ require 'neography'
3
+
4
+ @neo = Neography::Rest.new
5
+
6
+ johnathan = Neography::Node.create("name" =>'Johnathan')
7
+ mark = Neography::Node.create("name" =>'Mark')
8
+ phill = Neography::Node.create("name" =>'Phill')
9
+ mary = Neography::Node.create("name" =>'Mary')
10
+
11
+ johnathan.both(:friends) << mark
12
+ mark.both(:friends) << phill
13
+ phill.both(:friends) << mary
14
+ mark.both(:friends) << mary
15
+
16
+ johnathan.all_simple_paths_to(mary).incoming(:friends).depth(4).nodes.each do |node|
17
+ puts node.map{|n| n.name }.join(' => friends => ')
18
+ end
19
+
20
+ # RESULT
21
+ # Johnathan => friends => Mark => friends => Phill => friends => Mary
22
+ # Johnathan => friends => Mark => friends => Mary
@@ -0,0 +1,65 @@
1
+ require 'rubygems'
2
+ require 'neography'
3
+
4
+ @neo = Neography::Rest.new
5
+
6
+ def create_node(level, type)
7
+ @neo.create_node("NODE_LEVEL" => level, "TYPE" => type)
8
+ end
9
+
10
+ def create_other_node(type)
11
+ @neo.create_node("TYPE" => type)
12
+ end
13
+
14
+
15
+ def get_nodes_by_level(level, node)
16
+ starting_id = node["self"].split('/').last
17
+
18
+ @neo.traverse(node,"nodes", {"order" => "breadth first",
19
+ "uniqueness" => "node global",
20
+ "relationships" => {"type"=> "linked", "direction" => "out"},
21
+ "prune evaluator" => {
22
+ "language" => "javascript",
23
+ "body" => "position.startNode().hasProperty('NODE_LEVEL')
24
+ && position.startNode().getProperty('NODE_LEVEL')==5
25
+ && position.startNode().getId()!=#{starting_id};"},
26
+ "return filter" => {
27
+ "language" => "javascript",
28
+ "body" => "position.endNode().hasProperty('NODE_LEVEL') && position.endNode().getProperty('NODE_LEVEL')==5;"}})
29
+ end
30
+
31
+ node1 = create_node(5, "N")
32
+ node2 = create_node(5, "N")
33
+ node3 = create_node(5, "N")
34
+ node4 = create_node(5, "N")
35
+ node5 = create_node(5, "N")
36
+ node6 = create_node(5, "N")
37
+ node7 = create_node(5, "N")
38
+
39
+ node8 = create_other_node("Y")
40
+ node9 = create_other_node("Y")
41
+ node10 = create_other_node("Y")
42
+
43
+ node11 = create_node(6, "N")
44
+ node12 = create_node(7, "N")
45
+ node13 = create_node(8, "N")
46
+
47
+
48
+ @neo.create_relationship("linked", node1, node2)
49
+ @neo.create_relationship("linked", node2, node3)
50
+ @neo.create_relationship("linked", node3, node4)
51
+ @neo.create_relationship("linked", node4, node5)
52
+ @neo.create_relationship("linked", node5, node6)
53
+ @neo.create_relationship("linked", node6, node7)
54
+
55
+ @neo.create_relationship("linked", node2, node8)
56
+ @neo.create_relationship("linked", node3, node9)
57
+ @neo.create_relationship("linked", node4, node10)
58
+
59
+ @neo.create_relationship("linked", node5, node11)
60
+ @neo.create_relationship("linked", node6, node12)
61
+ @neo.create_relationship("linked", node7, node13)
62
+
63
+ puts "The node levels returned are #{get_nodes_by_level(5, node1).map{|n| n["data"]["NODE_LEVEL"]}.join(', ')}"
64
+
65
+ # The node levels returned are 5, 5, 5, 5, 5, 5, 5
@@ -0,0 +1,54 @@
1
+ require 'rubygems'
2
+ require 'neography'
3
+
4
+ @neo = Neography::Rest.new
5
+
6
+ def create_node(name, mysql_id)
7
+ @neo.create_node("name" => name, "mysql_id" => mysql_id)
8
+ end
9
+
10
+ def attended(student, school, degree, graduated)
11
+ @neo.create_relationship("attended", student, school, {"degree" => degree, "graduated" => graduated})
12
+ end
13
+
14
+
15
+ def graduated_with_me(student)
16
+ student = student["self"].split('/').last
17
+ student_attended = @neo.get_node_relationships(student)[0]
18
+ graduated = student_attended["data"]["graduated"]
19
+ school = student_attended["end"].split('/').last
20
+
21
+ @neo.traverse(school,"nodes", {"order" => "breadth first",
22
+ "uniqueness" => "node global",
23
+ "relationships" => {"type"=> "attended", "direction" => "in"},
24
+ "return filter" => {
25
+ "language" => "javascript",
26
+ "body" => "position.length() == 1
27
+ && position.endNode().getId() != #{student}
28
+ && position.lastRelationship().getProperty(\"graduated\") == #{graduated};"}})
29
+ end
30
+
31
+ charlie = create_node("Charlie", 1)
32
+ max = create_node("Max", 2)
33
+ peter = create_node("Peter", 3)
34
+ carol = create_node("Carol", 3)
35
+ tom = create_node("Tom", 4)
36
+ jerry = create_node("Jerry", 5)
37
+ larry = create_node("Larry", 6)
38
+
39
+ yale = create_node("Yale", 7)
40
+ harvard = create_node("Harvard", 8)
41
+ rutgers = create_node("Rutgers", 9)
42
+
43
+ attended(charlie,yale,"engineering", 2010)
44
+ attended(max,yale,"mathematics", 2005)
45
+ attended(peter,yale,"biology", 2010)
46
+ attended(carol,yale,"engineering", 2010)
47
+ attended(tom,harvard,"biology", 2008)
48
+ attended(jerry,rutgers,"physics", 2007)
49
+ attended(larry,rutgers,"mathematics", 2010)
50
+
51
+
52
+ puts "Charlie graduated with #{graduated_with_me(charlie).map{|n| n["data"]["name"]}.join(', ')}"
53
+
54
+ # The node levels returned are Peter, Carol
data/lib/neography.rb ADDED
@@ -0,0 +1,45 @@
1
+ require 'cgi'
2
+ require 'excon'
3
+ require 'json'
4
+ require 'multi_json'
5
+ require 'logger'
6
+ require 'ostruct'
7
+ require 'os'
8
+ # require 'zip/filesystem'
9
+
10
+ require 'neography/multi_json_parser'
11
+
12
+ require 'neography/version'
13
+
14
+ require 'neography/config'
15
+
16
+ require 'neography/rest'
17
+
18
+ require 'neography/neography'
19
+
20
+ require 'neography/property_container'
21
+ require 'neography/property'
22
+ require 'neography/node_relationship'
23
+ require 'neography/node_path'
24
+ require 'neography/relationship_traverser'
25
+ require 'neography/node_traverser'
26
+ require 'neography/path_traverser'
27
+ require 'neography/equal'
28
+ require 'neography/index'
29
+
30
+ require 'neography/node'
31
+ require 'neography/relationship'
32
+
33
+ require 'neography/railtie' if defined? Rails::Railtie
34
+
35
+ module Neography
36
+
37
+ def self.configure
38
+ yield configuration
39
+ end
40
+
41
+ def self.configuration
42
+ @configuration ||= Config.new
43
+ end
44
+
45
+ end
@@ -0,0 +1,64 @@
1
+ module Neography
2
+ class Config
3
+
4
+ attr_accessor :protocol, :server, :port, :directory,
5
+ :cypher_path, :gremlin_path,
6
+ :log_file, :log_enabled, :logger, :slow_log_threshold,
7
+ :max_threads,
8
+ :authentication, :username, :password,
9
+ :parser, :max_execution_time,
10
+ :proxy, :http_send_timeout, :http_receive_timeout
11
+
12
+ def initialize
13
+ set_defaults
14
+ end
15
+
16
+ def to_hash
17
+ {
18
+ :protocol => @protocol,
19
+ :server => @server,
20
+ :port => @port,
21
+ :directory => @directory,
22
+ :cypher_path => @cypher_path,
23
+ :gremlin_path => @gremlin_path,
24
+ :log_file => @log_file,
25
+ :log_enabled => @log_enabled,
26
+ :logger => @logger,
27
+ :slow_log_threshold => @slow_log_threshold,
28
+ :max_threads => @max_threads,
29
+ :authentication => @authentication,
30
+ :username => @username,
31
+ :password => @password,
32
+ :parser => @parser,
33
+ :max_execution_time => @max_execution_time,
34
+ :proxy => @proxy,
35
+ :http_send_timeout => @http_send_timeout,
36
+ :http_receive_timeout => @http_receive_timeout
37
+ }
38
+ end
39
+
40
+ private
41
+
42
+ def set_defaults
43
+ @protocol = "http://"
44
+ @server = "localhost"
45
+ @port = 7474
46
+ @directory = ""
47
+ @cypher_path = "/cypher"
48
+ @gremlin_path = "/ext/GremlinPlugin/graphdb/execute_script"
49
+ @log_file = "neography.log"
50
+ @log_enabled = false
51
+ @slow_log_threshold = 0
52
+ @max_threads = 20
53
+ @authentication = nil
54
+ @username = nil
55
+ @password = nil
56
+ @parser = MultiJsonParser
57
+ @max_execution_time = 6000
58
+ @proxy = nil
59
+ @http_send_timeout = 1200
60
+ @http_receive_timeout = 1200
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,263 @@
1
+ module Neography
2
+ module WasCreated
3
+ end
4
+ class Connection
5
+ USER_AGENT = "Neography/#{Neography::VERSION}"
6
+ ACTIONS = ["get", "post", "put", "delete"]
7
+
8
+ attr_reader :protocol, :server, :port, :directory,
9
+ :cypher_path, :gremlin_path,
10
+ :log_file, :log_enabled, :logger, :slow_log_threshold,
11
+ :max_threads,
12
+ :authentication, :username, :password,
13
+ :parser, :client,
14
+ :proxy, :http_send_timeout, :http_receive_timeout
15
+
16
+ def initialize(options = ENV['NEO4J_URL'] || {})
17
+ config = merge_configuration(options)
18
+ save_local_configuration(config)
19
+ @client ||= Excon.new(config[:proxy] || "#{@protocol}#{@server}:#{@port}",
20
+ :read_timeout => config[:http_receive_timeout],
21
+ :write_timeout => config[:http_send_timeout],
22
+ :persistent => true,
23
+ :user => config[:username],
24
+ :password => config[:password])
25
+ #authenticate
26
+ end
27
+
28
+ def configure(protocol, server, port, directory)
29
+ @protocol = protocol
30
+ @server = server
31
+ @port = port
32
+ @directory = directory
33
+ end
34
+
35
+ def configuration
36
+ @configuration ||= "#{@protocol}#{@server}:#{@port}#{@directory}"
37
+ end
38
+
39
+ def merge_options(options)
40
+ merged_options = options.merge!(@authentication)
41
+ if merged_options[:headers]
42
+ merged_options[:headers].merge!(@user_agent)
43
+ merged_options[:headers].merge!('X-Stream' => true) unless merged_options[:headers].key?('X-Stream')
44
+ merged_options[:headers].merge!(@max_execution_time)
45
+ end
46
+ merged_options
47
+ end
48
+
49
+ ACTIONS.each do |action|
50
+ define_method(action) do |path, options = {}|
51
+ # This ugly hack is required because internal Batch paths do not start with "/db/data"
52
+ # if somebody has a cleaner solution... pull request please!
53
+ partial_path = path.split("/")
54
+ if partial_path.size > 0
55
+ partial_path = partial_path[1].split("?").first
56
+ end
57
+ reserved = ["node", "relationship", "transaction", "cypher", "propertykeys", "schema", "label", "labels", "batch", "index", "ext"]
58
+ path = "/db/data" + path if reserved.include?(partial_path)
59
+ query_body = options[:body]
60
+ stream = ""
61
+ headers = merge_options(options)[:headers]
62
+ log path, query_body do
63
+ req_params = {:method => action.to_sym, :path => path, :body => query_body, :headers => headers}
64
+ is_streaming = headers && (headers['X-Stream'] == true)
65
+ if is_streaming
66
+ streamer = lambda { |chunk, _, _| stream += chunk }
67
+ req_params.merge!({:persistent => false, :response_block => streamer,
68
+ :read_timeout => 100000000, :write_timeout => 100000000})
69
+ end
70
+ response = @client.request(req_params)
71
+ evaluate_response(response, path, query_body, is_streaming, (partial_path == "batch"), stream)
72
+ end
73
+ end
74
+ end
75
+
76
+ def log(path, body)
77
+ if @log_enabled
78
+ start_time = Time.now
79
+ response = yield
80
+ time = ((Time.now - start_time) * 1000).round(2)
81
+ @logger.info "[Neography::Query] #{path} #{body} [#{time}ms]" if time >= slow_log_threshold
82
+ response
83
+ else
84
+ yield
85
+ end
86
+ end
87
+
88
+ def authenticate(path = nil)
89
+ unless @authentication.empty?
90
+ auth_type = @authentication.keys.first
91
+ @client.set_auth(
92
+ path,
93
+ @authentication[auth_type][:username],
94
+ @authentication[auth_type][:password]
95
+ )
96
+ # Force http client to always send auth credentials without
97
+ # waiting for WWW-Authenticate headers, thus saving one request
98
+ # roundtrip for each URI. Only works for HTTP basic auth.
99
+ @client.www_auth.basic_auth.challenge(configuration) if auth_type == 'basic_auth'
100
+ end
101
+ end
102
+
103
+ private
104
+
105
+ def merge_configuration(options)
106
+ options = parse_string_options(options) unless options.is_a? Hash
107
+ config = Neography.configuration.to_hash
108
+ config.merge(options)
109
+ end
110
+
111
+ def save_local_configuration(config)
112
+ @protocol = config[:protocol]
113
+ @server = config[:server]
114
+ @port = config[:port]
115
+ @directory = config[:directory]
116
+ @cypher_path = config[:cypher_path]
117
+ @gremlin_path = config[:gremlin_path]
118
+ @log_file = config[:log_file]
119
+ @log_enabled = config[:log_enabled]
120
+ @slow_log_threshold = config[:slow_log_threshold]
121
+ @max_threads = config[:max_threads]
122
+ @parser = config[:parser]
123
+ @logger = config[:logger]
124
+ @proxy = config[:proxy]
125
+
126
+ @max_execution_time = { 'max-execution-time' => config[:max_execution_time] }
127
+ @user_agent = { "User-Agent" => USER_AGENT }
128
+
129
+ @authentication = {}
130
+ if config[:authentication]
131
+ @authentication = {
132
+ "#{config[:authentication]}_auth".to_sym => {
133
+ :username => config[:username],
134
+ :password => config[:password]
135
+ }
136
+ }
137
+ end
138
+
139
+ if @log_enabled
140
+ @logger ||= Logger.new(@log_file)
141
+ end
142
+ end
143
+
144
+ def evaluate_chunk_response(response, result)
145
+ code = response.code
146
+ return_result(code, result)
147
+ end
148
+
149
+ def evaluate_response(response, path, query_body, streaming, batching, stream = nil)
150
+ code = response.status
151
+ parsed = false
152
+ if streaming && batching
153
+ code, body, parsed = handle_batch(stream)
154
+ elsif streaming
155
+ body = (stream || '').force_encoding("UTF-8")
156
+ else
157
+ body = response.body.force_encoding("UTF-8")
158
+ end
159
+ return_result(response, code, body, parsed, path, query_body)
160
+ end
161
+
162
+ def handle_batch(stream)
163
+ code = 200
164
+ body = @parser.json(stream.force_encoding("UTF-8"))
165
+ body.each do |result|
166
+ if result["status"] >= 400
167
+ code = result["status"]
168
+ break
169
+ end
170
+ end
171
+ return code, body, true
172
+ end
173
+
174
+ def return_result(response, code, body, parsed, path, query_body)
175
+ case code
176
+ when 200
177
+ @logger.debug "OK, created #{body}" if @log_enabled
178
+ parsed ? body : @parser.json(body)
179
+ when 201
180
+ @logger.debug "OK, created #{body}" if @log_enabled
181
+ r = parsed ? body : @parser.json(body)
182
+ r.extend(WasCreated)
183
+ r
184
+ when 204
185
+ @logger.debug "OK, no content returned" if @log_enabled
186
+ nil
187
+ when 400..500
188
+ handle_4xx_500_response(response, code, body, path, query_body)
189
+ nil
190
+ end
191
+ end
192
+
193
+ def handle_4xx_500_response(response, code, body, path, query_body)
194
+ index = 0
195
+ request = {:path => path, :body => query_body}
196
+ if body.nil? or body == ""
197
+ parsed_body = {"message" => "No error message returned from server.",
198
+ "stacktrace" => "No stacktrace returned from server." }
199
+ elsif body.is_a? Hash
200
+ parsed_body = body
201
+ elsif body.is_a? Array
202
+ body.each_with_index do |result, idx|
203
+ if result["status"] >= 400
204
+ index = idx
205
+ parsed_body = result["body"] || result
206
+ break
207
+ end
208
+ end
209
+ else
210
+ parsed_body = @parser.json(body)
211
+ end
212
+
213
+ message = parsed_body["message"]
214
+ stacktrace = parsed_body["stacktrace"]
215
+
216
+ @logger.error "#{response.dump} error: #{body}" if @log_enabled
217
+ raise_errors(code, parsed_body["exception"], message, stacktrace, request, index)
218
+ end
219
+
220
+ def raise_errors(code, exception, message, stacktrace, request, index)
221
+ error = nil
222
+ case code
223
+ when 401
224
+ error = UnauthorizedError
225
+ when 409
226
+ error = OperationFailureException
227
+ end
228
+
229
+ error ||= case exception
230
+ when /SyntaxException/ ; SyntaxException
231
+ when /this is not a query/ ; SyntaxException
232
+ when /PropertyValueException/ ; PropertyValueException
233
+ when /BadInputException/ ; BadInputException
234
+ when /NodeNotFoundException/ ; NodeNotFoundException
235
+ when /NoSuchPropertyException/ ; NoSuchPropertyException
236
+ when /RelationshipNotFoundException/ ; RelationshipNotFoundException
237
+ when /ParameterNotFoundException/ ; ParameterNotFoundException
238
+ when /NotFoundException/ ; NotFoundException
239
+ when /UniquePathNotUniqueException/ ; UniquePathNotUniqueException
240
+ when /DeadlockDetectedException/ ; DeadlockDetectedException
241
+ else
242
+ NeographyError
243
+ end
244
+
245
+ raise error.new(message, code, stacktrace, request, index)
246
+ end
247
+
248
+ def parse_string_options(options)
249
+ url = URI.parse(options)
250
+ options = {
251
+ :protocol => url.scheme + "://",
252
+ :server => url.host,
253
+ :port => url.port,
254
+ :directory => url.path,
255
+ :username => url.user,
256
+ :password => url.password
257
+ }
258
+ options[:authentication] = 'basic' unless url.user.nil?
259
+ options
260
+ end
261
+
262
+ end
263
+ end