I'm just starting to learn how to write procedures. My simple proof of concept still isn't passing muster when Neo4j starts up. Here is the code:
import java.util.ArrayList;
import java.util.stream.Stream;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.logging.Log;
import org.neo4j.procedure.Context;
import org.neo4j.procedure.Mode;
import org.neo4j.procedure.Procedure;
public class Procedures {
#Context
public GraphDatabaseService db;
#Context
public Log log;
#Procedure( name = "create_user", mode = Mode.WRITE )
public Stream<Create_user_response> create_user() {
ArrayList<Create_user_response> myList = new ArrayList<>();
Create_user_response res1 = new Create_user_response();
res1.out = 1;
myList.add(res1);
Stream<Create_user_response> myStream = myList.stream();
return myStream;
}
}
Here's my Create_user_response class:
public class Create_user_response {
public int out;
}
When Neo4j starts up it complains that my procedure needs to return a stream of records. I'm new to streams so I must be doing something wrong but just can't figure it out.
I appreciate any help. Thanks.
Turns out I had a bad dependency,
<dependency>
<groupId>org.neo4j.test</groupId>
<artifactId>neo4j-harness</artifactId>
<version>3.2.3</version>
<scope>test</scope>
</dependency>
wasn't working. I found it in a tutorial and its in Maven, but for some reason I must not have been doing something correctly with it.
Related
How to create a query using QueryBuilders for a nested List.
Something like that:
import org.elasticsearch.index.query.QueryBuilders;
...
QueryBuilders.matchQuery(...);
I need to find a document where code equals "test" and value containing "nested" text.
Sample data in a nested class:
code = "test"
value = "my nested value"
Below are the class definitions.
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
#Data
#Document(indexName = "MY_DOCUMENT")
public class MyDocument {
#Id
#Field(type = FieldType.Keyword)
private String id;
#Field(type = FieldType.Nested, name = "SUB_DOC")
private List<SubDocument> subDoc;
....
}
import lombok.Data;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
#Data
class SubDocument {
#Field(type = FieldType.Text, name = "CODE")
private String code;
#Field(type = FieldType.Text, name = "VALUE")
private String value;
}
Dependencies from pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
<version>2.7.1</version>
</dependency>
Additionally, is it possible to create this type of query in a form acceptable to Lucene?
Something like that:
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.AbstractQueryBuilder;
...
String stringQuery = "+SUB_DOC.VALUE:... +SUB_DOC.CODE:...";
AbstractQueryBuilder<?> q = QueryBuilders.queryStringQuery(stringQuery);
As you are explicitly asking for a solution using Elasticsearch QueryBuilders this is not really related to Spring Data Elasticsearch. You are just using Spring Data Elasticsearch to pass the Elasticsearch query down to the Elasticsearch client.
If you would use the Spring Data Elasticsearch classes, you could build a query like this:
var query = new CriteriaQuery(
new Criteria("subDoc.code").is("test")
.and("subDoc.value").contains("nested"));
var searchHits = operations.search(query, MyDocument.class);
I've written a feign client and I would like to test that it works using a unit test.
For my case, integration tests is not the right approach for the current development stage.
The feign client is null, I receive a NullPointerException while running the test.
How can I autowire it?
Feign client
package com.myapp.clients;
import com.myapp.model.StatusResponse;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#FeignClient(name="myClient", url="${feign.clients.my.url}")
public interface myClient {
#RequestMapping(method= RequestMethod.GET, value="/v1/users/{userId}")
StatusResponse getStatus(
#RequestHeader(value = "Auth", required = true) String authorizationHeader,
#RequestHeader(value = "my_tid", required = true) String tid,
#PathVariable("userId") String userId);
}
Tests:
package com.myapp.clients;
import com.intuit.secfraudshared.step.broker.model.StatusResponse;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
public class MyClientTest {
#Autowired
MyClient myClient;
#Test
public void testmyClient_status200() {
StatusResponse myResponse = myClient.getStatus("", "tidTestSample", "4626745161770145");
Assert.assertNotNull(iusResponse);
}
}
How can autowire MyClient?
The method that has worked for me so far while trying to test Feign Clients is stubbing the response via wiremock. You would need to add dependency for wiremock.
testImplementation 'org.springframework.cloud:spring-cloud-contract-wiremock'
Then you would need to annotate as
#RunWith(SpringRunner.class)
#SpringBootTest(properties = "feign.clients.my.url=http://localhost:${wiremock.server.port}")
#AutoConfigureWireMock(port = 0)
And then stub using wiremock.
stubFor(post(urlPathMatching("/v1/users/([a-zA-Z0-9-]*)")).willReturn(aResponse().withStatus(200).withHeader("content-type", "application/json").withBody("{\"code\":200,\"status\":\"success\"})));
where ([a-zA-Z0-9-]*) is regex for {userId} assuming it is alphanumeric.
And then, of course, assert.
StatusResponse myResponse = myClient.getStatus("", "tidTestSample", "4626745161770145");
Assert.assertNotNull(myResponse);
I've been starting to use GraphAware timetree for neo4j, and so far its working out pretty well. Now I'm trying to work out how I can unit / integration test my code that uses neo4j timetree.
I've put together some code as below... but still I'm getting the message:
org.neo4j.ogm.exception.CypherException: Error executing Cypher "Neo.ClientError.Procedure.ProcedureNotFound"; Code: Neo.ClientError.Procedure.ProcedureNotFound; Description: There is no procedure with the name `ga.timetree.events.attach` registered for this database instance. Please ensure you've spelled the procedure name correctly and that the procedure is properly deployed.
Am I sort of on the right track?
package myproject.core;
import java.util.ArrayList;
import java.util.HashMap;
import javax.inject.Inject;
import org.junit.After;
import org.junit.runner.RunWith;
import org.neo4j.graphdb.DynamicLabel;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.ogm.session.Session;
import org.neo4j.ogm.session.SessionFactory;
import org.neo4j.ogm.testutil.MultiDriverTestClass;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.neo4j.template.Neo4jOperations;
import org.springframework.test.context.junit4.SpringRunner;
import com.graphaware.common.policy.NodeInclusionPolicy;
import com.graphaware.module.timetree.module.TimeTreeConfiguration;
import com.graphaware.module.timetree.module.TimeTreeModule;
import com.graphaware.runtime.GraphAwareRuntime;
import com.graphaware.runtime.GraphAwareRuntimeFactory;
import myproject.core.context.TestPersistenceContext;
#RunWith(SpringRunner.class)
#SpringBootTest(classes = TestPersistenceContext.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class AbstractTest extends MultiDriverTestClass {
#Inject
private Neo4jOperations neo4jOperations;
public AbstractTest() {
new SessionFactory("myproject.model.pojos").openSession();
TimeTreeConfiguration timeTreeConfiguration = TimeTreeConfiguration.defaultConfiguration();
TimeTreeModule timeTreeModule = new TimeTreeModule("TT.1", timeTreeConfiguration, super.getGraphDatabaseService());
GraphAwareRuntime runtime = GraphAwareRuntimeFactory.createRuntime(super.getGraphDatabaseService());
runtime.registerModule(timeTreeModule);
runtime.start();
}
#After
public void clearDatabase() {
neo4jOperations.query("match (n) detach delete n;", new HashMap<>());
neo4jOperations.clear();
}
}
Please change your AbstractTest() constructor to read as follows:
public AbstractTest() {
new SessionFactory("myproject.model.pojos").openSession();
TimeTreeConfiguration timeTreeConfiguration = TimeTreeConfiguration.defaultConfiguration();
TimeTreeModule timeTreeModule = new TimeTreeModule("TT.1", timeTreeConfiguration, super.getGraphDatabaseService());
TimeTreeProcedures.register(super.getGraphDatabaseService());
GraphAwareRuntime runtime = GraphAwareRuntimeFactory.createRuntime(super.getGraphDatabaseService());
runtime.registerModule(timeTreeModule);
runtime.start();
}
Note the added line: TimeTreeProcedures.register(super.getGraphDatabaseService());
I am following this tutorial but have modified the POM.xml to use version 3.1.0.RELEASE of Spring Data Neo4j. I am experiencing a problem where the graph database does not seem to be instantiated correctly. The only code modification I did from the tutorial code was to replace the deprecated EmbeddedGraphDatabase API with the GraphDatabaseService API in Application.java. Any help would be appreciated.
UPDATE
This issue with the EmbdeedGraphDatabase is fixed. Now the issue is with rollback a transaction.
Application.java
package com.me.nosql.neo4j.hello;
import java.io.File;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.factory.GraphDatabaseFactory;
import org.neo4j.kernel.impl.util.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.neo4j.config.EnableNeo4jRepositories;
import org.springframework.data.neo4j.config.Neo4jConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.Transactional;
#Configuration
#EnableNeo4jRepositories
#EnableAutoConfiguration
#ComponentScan
public class Application extends Neo4jConfiguration implements CommandLineRunner {
#Autowired
PersonRepository personRepository;
public Application() {
setBasePackage("com/me/nosql/neo4j/hello");
}
#Bean(destroyMethod = "shutdown")
public GraphDatabaseService graphDatabaseService() {
return new GraphDatabaseFactory().newEmbeddedDatabase("accessingdataneo4j.db");
}
public void run(String... args) throws Exception {
Person greg = new Person("Greg");
Person roy = new Person("Roy");
Person craig = new Person("Craig");
System.out.println("Before linking up with Neo4j...");
for (Person person : new Person[]{greg, roy, craig}) {
System.out.println(person);
}
try (Transaction tx = graphDatabaseService().beginTx()) {
personRepository.save(greg);
personRepository.save(roy);
personRepository.save(craig);
greg = personRepository.findByName(greg.name);
greg.worksWith(roy);
greg.worksWith(craig);
personRepository.save(greg);
roy = personRepository.findByName(roy.name);
roy.worksWith(craig);
// We already know that roy works with greg
personRepository.save(roy);
// We already know craig works with roy and greg
tx.success();
}
System.out.println("Lookup each person by name...");
for (String name : new String[]{greg.name, roy.name, craig.name}) {
System.out.println(personRepository.findByName(name));
}
System.out.println("Looking up who works with Greg...");
for (Person person : personRepository.findByTeammatesName("Greg")) {
System.out.println(person.name + " works with Greg.");
}
}
public static void main(String[] args) throws Exception {
FileUtils.deleteRecursively(new File("accessingdataneo4j.db"));
SpringApplication.run(Application.class, args);
}
}
Error Message
Exception in thread "main" java.lang.IllegalStateException: Failed to execute CommandLineRunner
at org.springframework.boot.SpringApplication.runCommandLineRunners(SpringApplication.java:637)
at org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:652)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:312)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:909)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:898)
at com.me.nosql.neo4j.hello.Application.main(Application.java:84)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
Caused by: org.neo4j.graphdb.TransactionFailureException: Failed to mark transaction as rollback only.
at org.neo4j.kernel.TopLevelTransaction.markAsRollbackOnly(TopLevelTransaction.java:97)
at org.neo4j.kernel.TopLevelTransaction.failure(TopLevelTransaction.java:86)
at org.neo4j.cypher.internal.spi.v2_0.TransactionBoundQueryContext.close(TransactionBoundQueryContext.scala:59)
at org.neo4j.cypher.internal.compiler.v2_0.spi.DelegatingQueryContext.close(DelegatingQueryContext.scala:33)
at org.neo4j.cypher.internal.compiler.v2_0.spi.ExceptionTranslatingQueryContext.org$neo4j$cypher$internal$compiler$v2_0$spi$ExceptionTranslatingQueryContext$$super$close(ExceptionTranslatingQueryContext.scala:34)
at org.neo4j.cypher.internal.compiler.v2_0.spi.ExceptionTranslatingQueryContext$$anonfun$close$1.apply$mcV$sp(ExceptionTranslatingQueryContext.scala:34)
at org.neo4j.cypher.internal.compiler.v2_0.spi.ExceptionTranslatingQueryContext$$anonfun$close$1.apply(ExceptionTranslatingQueryContext.scala:34)
at org.neo4j.cypher.internal.compiler.v2_0.spi.ExceptionTranslatingQueryContext$$anonfun$close$1.apply(ExceptionTranslatingQueryContext.scala:34)
at org.neo4j.cypher.internal.compiler.v2_0.spi.ExceptionTranslatingQueryContext.org$neo4j$cypher$internal$compiler$v2_0$spi$ExceptionTranslatingQueryContext$$translateException(ExceptionTranslatingQueryContext.scala:149)
at org.neo4j.cypher.internal.compiler.v2_0.spi.ExceptionTranslatingQueryContext.close(ExceptionTranslatingQueryContext.scala:34)
at org.neo4j.cypher.internal.compiler.v2_0.ClosingIterator$$anonfun$close$1.apply$mcV$sp(ClosingIterator.scala:65)
at org.neo4j.cypher.internal.compiler.v2_0.ClosingIterator$$anonfun$close$1.apply(ClosingIterator.scala:63)
at org.neo4j.cypher.internal.compiler.v2_0.ClosingIterator$$anonfun$close$1.apply(ClosingIterator.scala:63)
at org.neo4j.cypher.internal.compiler.v2_0.ClosingIterator.translateException(ClosingIterator.scala:70)
at org.neo4j.cypher.internal.compiler.v2_0.ClosingIterator.close(ClosingIterator.scala:62)
at org.neo4j.cypher.internal.compiler.v2_0.ClosingIterator.failIfThrows(ClosingIterator.scala:92)
at org.neo4j.cypher.internal.compiler.v2_0.ClosingIterator.hasNext(ClosingIterator.scala:34)
at org.neo4j.cypher.internal.compiler.v2_0.PipeExecutionResult.hasNext(PipeExecutionResult.scala:166)
at scala.collection.Iterator$$anon$11.hasNext(Iterator.scala:327)
at scala.collection.convert.Wrappers$IteratorWrapper.hasNext(Wrappers.scala:29)
at org.neo4j.cypher.internal.compiler.v2_0.PipeExecutionResult$$anon$1.hasNext(PipeExecutionResult.scala:74)
at org.neo4j.helpers.collection.IteratorWrapper.hasNext(IteratorWrapper.java:42)
at com.me.nosql.neo4j.hello.Application.run(Application.java:77)
at org.springframework.boot.SpringApplication.runCommandLineRunners(SpringApplication.java:634)
... 10 more
Caused by: java.lang.NullPointerException
at org.neo4j.kernel.TopLevelTransaction.markAsRollbackOnly(TopLevelTransaction.java:93)
... 33 more
I faced the similar problem and found the hint for the solution here (http://spring.io/guides/gs/accessing-neo4j-data-rest/).
I needed to add the constructor like this:
public Application() {
setBasePackage("name/of/the/base/package");
}
The example ran, created the db and performed queries, all except the last ("Looking up who works with Greg..."). The error was:
Caused by: org.neo4j.graphdb.TransactionFailureException: Failed to mark transaction as rollback only.
I managed to resolve this by wrapping the call in the transaction, just like that:
Transaction tx = graphDatabase.beginTx();
try {
//Do all the work
tx.success();
} finally {
tx.close();
}
After then, everything worked fine for me.
EDIT: In order to make all transactions persist in DB I changed the following lines to
try (Transaction tx = graphDatabase().beginTx()) {
System.out.println("Lookup each person by name...");
for (String name : new String[]{greg.name, roy.name, craig.name}) {
System.out.println(personRepository.findByName(name));
}
tx.success();
}
try (Transaction tx = graphDatabase().beginTx()) {
System.out.println("Looking up who works with Greg...");
for (PersonJ person : personRepository.findByTeammatesName("Greg")) {
System.out.println(person.name + " works with Greg.");
}
tx.success();
}
Few things you would like to check :
new GraphDatabaseFactory().newEmbeddedDatabase( "accessingdataneo4j.db" ); Check if the path to the db is correct . Try with an absolute address.
Check if you aren't running the GraphDb instance somewhere else.
Remove this:
#Autowired
GraphDatabase graphDatabase;
And use the bean-method instead:
graphDatabaseService()
// or
graphDatabase()
Try to use CommandlineRunner as a bean and inject the dependency there. Spring doesn't inject beans that are declared in this configuration into the same config instance.
I have service interface
public interface CompoundService<T extends Compound> {
T getById(final Long id);
//...
}
and abstract implementation
public abstract class CompoundServiceImpl<T extends Compound>
implements CompoundService<T> {
//...
private Class<T> compoundClass;
//...
}
Every implementation of Compound requires it's own service interface which extends CompoundService and it's own service class which extends CompoundServiceImpl.
I would now like to add basic security uisng annotations to my methods in CompoundService. As far as I understood I must add them in the interface not the actual implementation. Since a user can have different roles for different implementations of Compound, i must take this into account. Meaning in #PreAuthorize I would like to get the name of the Compound implementation, eg. compoundClass.getSimpleName(). So that I get something like:
public interface CompoundService<T extends Compound> {
#PreAuthorize("hasRole('read_' + #root.this.compoundClass.getSimpleName())")
T getById(final Long id);
//...
}
This is basically what is mentioned here:
https://jira.springsource.org/browse/SEC-1640
however there is no example and I did not really get the solution. So should i use this? or as above #root.this?
My second question is, since this is in an interface which will be implemented by a proxy (from spring) will the experession this.compoundClass actually evaluate properly?
And last but not least how can I actually test this?*
*
I'm not actually creating a finished application but something configurable, like a framework for s specific type of database search. Meaning most authorization and authentication stuff has to come from the implementer.
Unit Testing
see http://www.lancegleason.com/blog/2009/12/07/unit-testing-spring-security-with-annotations
Since that is an old tutorial you might need to change the referenced schema versions. But more importantly the SecurityContext.xml configuration shown there does not work with Spring Security 3. See Spring Security - multiple authentication-providers for a proper configuration.
I did not require the mentioned dependencies:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core-tiger</artifactId>
</dependency>
it worked without them (however did not create an abstract test class)
root.this
This is in fact correct approach
The problem is that you can't use getSimpleName() of a class parameter. For an in-depth discussion see http://forum.springsource.org/showthread.php?98570-Getting-Payload-Classname-in-Header-Enricher-via-SpEL
The workarounds shown there did not help me much. So I came up with this very simple solution:
Just add the string property String compoundClassSimpleName to CompoundServiceImpl and set it in the constructor (which is called by subclasses):
Public abstract class CompoundServiceImpl<T extends Compound>
implements CompoundService<T> {
private String compoundClassSimpleName;
//...
public ChemicalCompoundServiceImpl(Class<T> compoundClass) {
this.compoundClass = compoundClass;
this.compoundClassSimpleName = compoundClass.getSimpleName();
}
//...
public String getCompoundClassSimpleName(){
return compoundClassSimpleName;
}
}
and her a Service implementing above abstract service:
public class TestCompoundServiceImpl extends CompoundServiceImpl<TestCompound>
implements TestCompoundService {
//...
public TestCompoundServiceImpl() {
super(TestCompound.class);
}
//...
}
And final the #PreAuthorize annotation usage:
public interface CompoundService<T extends Compound> {
#PreAuthorize("hasRole('read_' + #root.this.getCompoundClassSimpleName())")
public T getById(final Long id);
}
For above example the expression will evaluate to a role named "read_TestCompound".
Done!
As often the solution is very simple but getting there is a PITA...
EDIT:
for completeness the test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {
"classpath:ApplicationContext.xml",
"classpath:SecurityContext.xml"
})
public class CompoundServiceSecurityTest {
#Autowired
#Qualifier("testCompoundService")
private TestCompoundService testCompoundService;
public CompoundServiceSecurityTest() {
}
#Before
public void setUp() {
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken("user_test", "pass1"));
}
#Test
public void testGetById() {
System.out.println("getById");
Long id = 1000L;
TestCompound expResult = new TestCompound(id, "Test Compound");
TestCompound result = testCompoundService.getById(id);
assertEquals(expResult, result);
}
}