neo4j-core 3.0.8 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -34,7 +34,7 @@ module Neo4j::Server
34
34
 
35
35
  def load_resource
36
36
  if resource_data.nil? || resource_data.empty?
37
- @resource_data = @session._query_or_fail("START n=relationship(#{id}) RETURN n", true) # r.first_data
37
+ @resource_data = @session._query_or_fail("#{match_start} RETURN n", true) # r.first_data
38
38
  end
39
39
  end
40
40
 
@@ -69,15 +69,15 @@ module Neo4j::Server
69
69
  end
70
70
 
71
71
  def get_property(key)
72
- @session._query_or_fail("START n=relationship(#{id}) RETURN n.`#{key}`", true)
72
+ @session._query_or_fail("#{match_start} RETURN n.`#{key}`", true)
73
73
  end
74
74
 
75
75
  def set_property(key,value)
76
- @session._query_or_fail("START n=relationship(#{id}) SET n.`#{key}` = {value}", false, {value: value})
76
+ @session._query_or_fail("#{match_start} SET n.`#{key}` = {value}", false, {value: value})
77
77
  end
78
78
 
79
79
  def remove_property(key)
80
- @session._query_or_fail("START n=relationship(#{id}) REMOVE n.`#{key}`")
80
+ @session._query_or_fail("#{match_start} REMOVE n.`#{key}`")
81
81
  end
82
82
 
83
83
  # (see Neo4j::Relationship#props)
@@ -85,21 +85,21 @@ module Neo4j::Server
85
85
  if @props
86
86
  @props
87
87
  else
88
- hash = @session._query_entity_data("START n=relationship(#{neo_id}) RETURN n")
88
+ hash = @session._query_entity_data("#{match_start} RETURN n")
89
89
  @props = Hash[hash['data'].map{ |k, v| [k.to_sym, v] }]
90
90
  end
91
91
  end
92
92
 
93
93
  # (see Neo4j::Relationship#props=)
94
94
  def props=(properties)
95
- @session._query_or_fail("START n=relationship(#{neo_id}) SET n = { props }", false, {props: properties})
95
+ @session._query_or_fail("#{match_start} SET n = { props }", false, {props: properties})
96
96
  properties
97
97
  end
98
98
 
99
99
  # (see Neo4j::Relationship#update_props)
100
100
  def update_props(properties)
101
101
  return if properties.empty?
102
- q = "START n=relationship(#{neo_id}) SET " + properties.keys.map do |k|
102
+ q = "#{match_start} SET " + properties.keys.map do |k|
103
103
  "n.`#{k}`= #{escape_value(properties[k])}"
104
104
  end.join(',')
105
105
  @session._query_or_fail(q)
@@ -111,24 +111,21 @@ module Neo4j::Server
111
111
  end
112
112
 
113
113
  def del
114
- id = neo_id
115
- @session._query("START n=relationship(#{id}) DELETE n").raise_unless_response_code(200)
114
+ @session._query("#{match_start} DELETE n").raise_unless_response_code(200)
116
115
  end
117
116
  alias_method :delete, :del
118
117
  alias_method :destroy, :del
119
118
 
120
119
  def exist?
121
- id = neo_id
122
- response = @session._query("START n=relationship(#{id}) RETURN n")
123
-
124
- if (!response.error?)
125
- return true
126
- elsif (response.error_status == 'BadInputException') # TODO see github issue neo4j/1061
127
- return false
128
- else
129
- response.raise_error
130
- end
120
+ response = @session._query("#{match_start} RETURN n")
121
+ # binding.pry
122
+ (response.data.nil? || response.data.empty?) ? false : true
131
123
  end
132
124
 
125
+ private
126
+
127
+ def match_start(identifier = 'n')
128
+ "MATCH (node)-[#{identifier}]-() WHERE ID(#{identifier}) = #{neo_id}"
129
+ end
133
130
  end
134
131
  end
@@ -10,7 +10,15 @@ module Neo4j::Server
10
10
  include Neo4j::Core::CypherTranslator
11
11
 
12
12
  alias_method :super_query, :query
13
- attr_reader :connection
13
+ attr_reader :connection, :auth
14
+
15
+ def initialize(data_url, connection, auth_obj = nil)
16
+ @connection = connection
17
+ @auth = auth_obj if auth_obj
18
+ Neo4j::Session.register(self)
19
+ initialize_resource(data_url)
20
+ Neo4j::Session._notify_listeners(:session_available, self)
21
+ end
14
22
 
15
23
  # @param [Hash] params could be empty or contain basic authentication user and password
16
24
  # @return [Faraday]
@@ -26,7 +34,7 @@ module Neo4j::Server
26
34
  b.use Faraday::Adapter::NetHttpPersistent
27
35
  # b.adapter Faraday.default_adapter
28
36
  end
29
- conn.headers = {'Content-Type' => 'application/json', 'User-Agent' => ::Neo4j::Session.user_agent_string}
37
+ conn.headers = { 'Content-Type' => 'application/json', 'User-Agent' => ::Neo4j::Session.user_agent_string }
30
38
  conn
31
39
  end
32
40
 
@@ -35,18 +43,21 @@ module Neo4j::Server
35
43
  #
36
44
  # @param [String] endpoint_url - the url to the neo4j server, defaults to 'http://localhost:7474'
37
45
  # @param [Hash] params faraday params, see #create_connection or an already created faraday connection
38
- def self.open(endpoint_url=nil, params = {})
46
+ def self.open(endpoint_url = nil, params = {})
39
47
  extract_basic_auth(endpoint_url, params)
40
48
  connection = params[:connection] || create_connection(params)
41
49
  url = endpoint_url || 'http://localhost:7474'
50
+ auth_obj = CypherAuthentication.new(url, connection, params)
51
+ auth_obj.authenticate
42
52
  response = connection.get(url)
43
53
  raise "Server not available on #{url} (response code #{response.status})" unless response.status == 200
54
+ establish_session(response.body, connection, auth_obj)
55
+ end
44
56
 
45
- root_data = response.body
57
+ def self.establish_session(root_data, connection, auth_obj)
46
58
  data_url = root_data['data']
47
59
  data_url << '/' unless data_url.end_with?('/')
48
-
49
- CypherSession.new(data_url, connection)
60
+ CypherSession.new(data_url, connection, auth_obj)
50
61
  end
51
62
 
52
63
  def self.extract_basic_auth(url, params)
@@ -56,14 +67,8 @@ module Neo4j::Server
56
67
  password: URI(url).password
57
68
  }
58
69
  end
59
- private_class_method :extract_basic_auth
60
70
 
61
- def initialize(data_url, connection)
62
- @connection = connection
63
- Neo4j::Session.register(self)
64
- initialize_resource(data_url)
65
- Neo4j::Session._notify_listeners(:session_available, self)
66
- end
71
+ private_class_method :extract_basic_auth
67
72
 
68
73
  def db_type
69
74
  :server_db
@@ -105,21 +110,16 @@ module Neo4j::Server
105
110
  Neo4j::Transaction.current
106
111
  end
107
112
 
108
- def create_node(props=nil, labels=[])
109
- l = labels.empty? ? "" : ":" + labels.map{|k| "`#{k}`"}.join(':')
110
- q = "CREATE (n#{l} #{cypher_prop_list(props)}) RETURN ID(n)"
111
- cypher_response = _query_or_fail(q, true)
112
- CypherNode.new(self, cypher_response)
113
+ def create_node(props = nil, labels = [])
114
+ CypherNode.new self, _query_or_fail(cypher_string(labels, props), true, cypher_prop_list(props))
113
115
  end
114
116
 
115
117
  def load_node(neo_id)
116
- cypher_response = _query("START n=node(#{neo_id}) RETURN n")
117
- load_entity(CypherNode, cypher_response)
118
+ load_entity(CypherNode, _query("MATCH (n) WHERE ID(n) = #{neo_id} RETURN n"))
118
119
  end
119
120
 
120
121
  def load_relationship(neo_id)
121
- cypher_response = _query("START r=relationship(#{neo_id}) RETURN r")
122
- load_entity(CypherRelationship, cypher_response)
122
+ load_entity(CypherRelationship, _query("MATCH (n)-[r]-() WHERE ID(r) = #{neo_id} RETURN r"))
123
123
  end
124
124
 
125
125
  def load_entity(clazz, cypher_response)
@@ -144,36 +144,21 @@ module Neo4j::Server
144
144
  end
145
145
 
146
146
  def uniqueness_constraints(label)
147
- response = @connection.get("#{@resource_url}schema/constraint/#{label}/uniqueness")
148
- expect_response_code(response, 200)
149
- data_resource = response.body
150
-
151
- property_keys = data_resource.map do |row|
152
- row['property_keys'].map(&:to_sym)
153
- end
154
-
155
- {
156
- property_keys: property_keys
157
- }
147
+ schema_properties("#{@resource_url}schema/constraint/#{label}/uniqueness")
158
148
  end
159
149
 
160
150
  def indexes(label)
161
- response = @connection.get("#{@resource_url}schema/index/#{label}")
162
- expect_response_code(response, 200)
163
- data_resource = response.body
164
-
165
- property_keys = data_resource.map do |row|
166
- row['property_keys'].map(&:to_sym)
167
- end
151
+ schema_properties("#{@resource_url}schema/index/#{label}")
152
+ end
168
153
 
169
- {
170
- property_keys: property_keys
171
- }
154
+ def schema_properties(query_string)
155
+ response = @connection.get(query_string)
156
+ expect_response_code(response, 200)
157
+ { property_keys: response.body.map { |row| row['property_keys'].map(&:to_sym) } }
172
158
  end
173
159
 
174
160
  def find_all_nodes(label_name)
175
- response = _query_or_fail("MATCH (n:`#{label_name}`) RETURN ID(n)")
176
- search_result_to_enumerable_first_column(response)
161
+ search_result_to_enumerable_first_column(_query_or_fail("MATCH (n:`#{label_name}`) RETURN ID(n)"))
177
162
  end
178
163
 
179
164
  def find_nodes(label_name, key, value)
@@ -254,41 +239,16 @@ module Neo4j::Server
254
239
  yielder << CypherNode.new(self, data[0]).wrapper
255
240
  end
256
241
  end
257
- end
242
+ end
258
243
 
259
244
  def map_column(key, map, data)
260
- case map[key]
261
- when :node
262
- CypherNode.new(self, data).wrapper
263
- when :rel, :relationship
264
- CypherRelationship.new(self, data)
265
- else
266
- data
245
+ if map[key] == :node
246
+ CypherNode.new(self, data).wrapper
247
+ elsif map[key] == :rel || map[:key] || :relationship
248
+ CypherRelationship.new(self, data)
249
+ else
250
+ data
267
251
  end
268
252
  end
269
-
270
-
271
- # def search_result_to_enumerable(response, ret, map)
272
- # return [] unless response.data
273
-
274
- # if (ret.size == 1)
275
- # Enumerator.new do |yielder|
276
- # response.data.each do |data|
277
- # yielder << map_column(key, map, data[0])
278
- # end
279
- # end
280
-
281
- # else
282
- # Enumerator.new do |yielder|
283
- # response.data.each do |data|
284
- # hash = {}
285
- # ret.each_with_index do |key, i|
286
- # hash[key] = map_column(key, map, data[i])
287
- # end
288
- # yielder << hash
289
- # end
290
- # end
291
- # end
292
- # end
293
253
  end
294
254
  end
@@ -1,15 +1,31 @@
1
1
  module Neo4j
2
2
  module Tasks
3
-
4
3
  module ConfigServer
5
4
 
6
- def config(data, port)
7
- s = set_property(data, 'org.neo4j.server.webserver.https.enabled', 'false')
5
+ def config(source_text, port)
6
+ s = set_property(source_text, 'org.neo4j.server.webserver.https.enabled', 'false')
8
7
  set_property(s, 'org.neo4j.server.webserver.port', port)
9
8
  end
10
9
 
11
- def set_property(data, property, value)
12
- data.gsub(/#{property}\s*=\s*(\w+)/, "#{property}=#{value}")
10
+ def set_property(source_text, property, value)
11
+ source_text.gsub(/#{property}\s*=\s*(\w+)/, "#{property}=#{value}")
12
+ end
13
+
14
+ # Toggles the status of Neo4j 2.2's basic auth
15
+ def toggle_auth(status, source_text)
16
+ status_string = status == :enable ? 'true' : 'false'
17
+ set_property(source_text, 'dbms.security.authorization_enabled', status_string)
18
+ end
19
+
20
+ # POSTs to an endpoint with the form required to change a Neo4j password
21
+ # @param [String] target_address The server address, with protocol and port, against which the form should be POSTed
22
+ # @param [String] old_password The existing password for the "neo4j" user account
23
+ # @param [String] new_password The new password you want to use. Shocking, isn't it?
24
+ # @return [Hash] The response from the server indicating success/failure.
25
+ def change_password(target_address, old_password, new_password)
26
+ uri = URI.parse("#{target_address}/user/neo4j/password")
27
+ response = Net::HTTP.post_form(uri, { 'password' => old_password, 'new_password' => new_password })
28
+ JSON.parse(response.body)
13
29
  end
14
30
 
15
31
  extend self
@@ -1,3 +1,4 @@
1
+ # :nocov:
1
2
  # borrowed from architect4r
2
3
  require 'os'
3
4
  require 'httparty'
@@ -6,24 +7,18 @@ require 'httparty'
6
7
  require File.expand_path("../config_server", __FILE__)
7
8
 
8
9
  namespace :neo4j do
9
-
10
10
  def download_neo4j(file)
11
- if OS::Underlying.windows? then
12
- file_name = "neo4j.zip"
13
- download_url = "http://dist.neo4j.org/neo4j-#{file}-windows.zip"
14
- else
15
- file_name = "neo4j-unix.tar.gz"
16
- download_url = "http://dist.neo4j.org/neo4j-#{file}-unix.tar.gz"
17
- end
11
+ file_name, download_url = if OS::Underlying.windows?
12
+ ["neo4j.zip", "http://dist.neo4j.org/neo4j-#{file}-windows.zip"]
13
+ else
14
+ ["neo4j-unix.tar.gz", "http://dist.neo4j.org/neo4j-#{file}-unix.tar.gz"]
15
+ end
18
16
 
19
17
  unless File.exist?(file_name)
20
18
  # check if file is available
21
19
  status = HTTParty.head(download_url).code
22
20
  raise "#{file} is not available to download, try a different version" if status < 200 || status >= 300
23
-
24
21
  df = File.open(file_name, 'wb')
25
-
26
-
27
22
  success = false
28
23
  begin
29
24
  df << HTTParty.get(download_url)
@@ -34,8 +29,6 @@ namespace :neo4j do
34
29
  end
35
30
  end
36
31
 
37
-
38
-
39
32
  # # http://download.neo4j.org/artifact?edition=community&version=2.1.2&distribution=tarball&dlid=3462770&_ga=1.110610309.1220184053.1399636580
40
33
  #
41
34
  # parsed_url = URI.parse(download_url)
@@ -67,25 +60,29 @@ namespace :neo4j do
67
60
  "db/neo4j/#{get_environment(args)}"
68
61
  end
69
62
 
70
- desc "Install Neo4j, example neo4j:install[community-2.1.3,development]"
63
+ def config_location(args)
64
+ "#{install_location(args)}/conf/neo4j-server.properties"
65
+ end
66
+
67
+ desc "Install Neo4j with auth disabled in v2.2+, example neo4j:install[community-2.1.3,development]"
71
68
  task :install, :edition, :environment do |_, args|
72
69
  file = args[:edition]
73
70
  environment = get_environment(args)
74
71
  puts "Installing Neo4j-#{file} environment: #{environment}"
75
72
 
76
73
  downloaded_file = download_neo4j file
77
-
74
+
78
75
  if OS::Underlying.windows?
79
76
  # Extract and move to neo4j directory
80
77
  unless File.exist?(install_location(args))
81
78
  Zip::ZipFile.open(downloaded_file) do |zip_file|
82
79
  zip_file.each do |f|
83
- f_path=File.join(".", f.name)
80
+ f_path = File.join(".", f.name)
84
81
  FileUtils.mkdir_p(File.dirname(f_path))
85
82
  begin
86
83
  zip_file.extract(f, f_path) unless File.exist?(f_path)
87
84
  rescue
88
- puts f.name + " failed to extract."
85
+ puts "#{f.name} failed to extract."
89
86
  end
90
87
  end
91
88
  end
@@ -105,19 +102,20 @@ namespace :neo4j do
105
102
  %x[rm #{downloaded_file}]
106
103
  puts "Neo4j Installed in to neo4j directory."
107
104
  end
105
+ rake_auth_toggle(args, :disable) unless /-2\.0|1\.[0-9]/.match(args[:edition])
108
106
  puts "Type 'rake neo4j:start' or 'rake neo4j:start[ENVIRONMENT]' to start it\nType 'neo4j:config[ENVIRONMENT,PORT]' for changing server port, (default 7474)"
109
107
  end
110
-
108
+
111
109
  desc "Start the Neo4j Server"
112
110
  task :start, :environment do |_, args|
113
111
  puts "Starting Neo4j #{get_environment(args)}..."
114
- if OS::Underlying.windows?
115
- if %x[reg query "HKU\\S-1-5-19"].size > 0
112
+ if OS::Underlying.windows?
113
+ if %x[reg query "HKU\\S-1-5-19"].size > 0
116
114
  %x[#{install_location(args)}/bin/Neo4j.bat start] #start service
117
115
  else
118
116
  puts "Starting Neo4j directly, not as a service."
119
117
  %x[#{install_location(args)}/bin/Neo4j.bat]
120
- end
118
+ end
121
119
  else
122
120
  %x[#{install_location(args)}/bin/neo4j start]
123
121
  end
@@ -129,7 +127,7 @@ namespace :neo4j do
129
127
  port = args[:port]
130
128
  raise "no port given" unless port
131
129
  puts "Config Neo4j #{get_environment(args)} for port #{port}"
132
- location = "#{install_location(args)}/conf/neo4j-server.properties"
130
+ location = config_location(args)
133
131
  text = File.read(location)
134
132
  replace = Neo4j::Tasks::ConfigServer.config(text, port)
135
133
  File.open(location, "w") {|file| file.puts replace}
@@ -138,13 +136,13 @@ namespace :neo4j do
138
136
  desc "Stop the Neo4j Server"
139
137
  task :stop, :environment do |_, args|
140
138
  puts "Stopping Neo4j #{get_environment(args)}..."
141
- if OS::Underlying.windows?
139
+ if OS::Underlying.windows?
142
140
  if %x[reg query "HKU\\S-1-5-19"].size > 0
143
141
  %x[#{install_location(args)}/bin/Neo4j.bat stop] #stop service
144
142
  else
145
- puts "You do not have administrative rights to stop the Neo4j Service"
143
+ puts "You do not have administrative rights to stop the Neo4j Service"
146
144
  end
147
- else
145
+ else
148
146
  %x[#{install_location(args)}/bin/neo4j stop]
149
147
  end
150
148
  end
@@ -166,13 +164,13 @@ namespace :neo4j do
166
164
  desc "Restart the Neo4j Server"
167
165
  task :restart, :environment do |_, args|
168
166
  puts "Restarting Neo4j #{get_environment(args)}..."
169
- if OS::Underlying.windows?
167
+ if OS::Underlying.windows?
170
168
  if %x[reg query "HKU\\S-1-5-19"].size > 0
171
169
  %x[#{install_location(args)}/bin/Neo4j.bat restart]
172
170
  else
173
- puts "You do not have administrative rights to restart the Neo4j Service"
171
+ puts "You do not have administrative rights to restart the Neo4j Service"
174
172
  end
175
- else
173
+ else
176
174
  %x[#{install_location(args)}/bin/neo4j restart]
177
175
  end
178
176
  end
@@ -180,36 +178,83 @@ namespace :neo4j do
180
178
  desc "Reset the Neo4j Server"
181
179
  task :reset_yes_i_am_sure, :environment do |_, args|
182
180
  # Stop the server
183
- if OS::Underlying.windows?
181
+ if OS::Underlying.windows?
184
182
  if %x[reg query "HKU\\S-1-5-19"].size > 0
185
183
  %x[#{install_location(args)}/bin/Neo4j.bat stop]
186
-
184
+
187
185
  # Reset the database
188
186
  FileUtils.rm_rf("#{install_location(args)}/data/graph.db")
189
187
  FileUtils.mkdir("#{install_location(args)}/data/graph.db")
190
-
188
+
191
189
  # Remove log files
192
190
  FileUtils.rm_rf("#{install_location(args)}/data/log")
193
191
  FileUtils.mkdir("#{install_location(args)}/data/log")
194
192
 
195
193
  %x[#{install_location(args)}/bin/Neo4j.bat start]
196
194
  else
197
- puts "You do not have administrative rights to reset the Neo4j Service"
195
+ puts "You do not have administrative rights to reset the Neo4j Service"
198
196
  end
199
- else
197
+ else
200
198
  %x[#{install_location(args)}/bin/neo4j stop]
201
-
199
+
202
200
  # Reset the database
203
201
  FileUtils.rm_rf("#{install_location(args)}/data/graph.db")
204
202
  FileUtils.mkdir("#{install_location(args)}/data/graph.db")
205
-
203
+
206
204
  # Remove log files
207
205
  FileUtils.rm_rf("#{install_location(args)}/data/log")
208
206
  FileUtils.mkdir("#{install_location(args)}/data/log")
209
-
207
+
210
208
  # Start the server
211
209
  %x[#{install_location(args)}/bin/neo4j start]
212
210
  end
213
211
  end
214
212
 
215
- end
213
+ desc "Neo4j 2.2: Change connection password"
214
+ task :change_password do |_, args|
215
+ puts "This will change the password for a Neo4j server"
216
+ puts "Enter target IP address or host name without protocal and port, press enter for http://localhost:7474"
217
+ address = STDIN.gets.chomp
218
+ target_address = address.empty? ? "http://localhost:7474" : address
219
+
220
+ puts "Input current password. Leave blank if this is a fresh installation of Neo4j."
221
+ password = STDIN.gets.chomp
222
+ old_password = password.empty? ? "neo4j" : password
223
+
224
+ puts "Input new password."
225
+ new_password = STDIN.gets.chomp
226
+ raise 'A new password is required' if new_password.empty?
227
+
228
+ body = Neo4j::Tasks::ConfigServer.change_password(target_address, old_password, new_password)
229
+ if body['errors']
230
+ puts "An error was returned: #{body['errors'][0]['message']}"
231
+ else
232
+ puts "Password changed successfully! Please update your app to use:"
233
+ puts "username: neo4j"
234
+ puts "password: #{new_password}"
235
+ end
236
+ end
237
+
238
+ def rake_auth_toggle(args, status)
239
+ location = config_location(args)
240
+ text = File.read(location)
241
+ replace = Neo4j::Tasks::ConfigServer.toggle_auth(status, text)
242
+ File.open(location, "w") {|file| file.puts replace}
243
+ end
244
+
245
+ def auth_toggle_complete(status)
246
+ puts "Neo4j basic authentication #{status}. Restart server to apply."
247
+ end
248
+
249
+ desc "Neo4j 2.2: Enable Auth"
250
+ task :enable_auth, :environment do |_, args|
251
+ rake_auth_toggle(args, :enable)
252
+ auth_toggle_complete('enabled')
253
+ end
254
+
255
+ desc "Neo4j 2.2: Disable Auth"
256
+ task :disable_auth, :environment do |_, args|
257
+ rake_auth_toggle(args, :disable)
258
+ auth_toggle_complete('disabled')
259
+ end
260
+ end