How to implement Enum in SpringBoot with neo4j Rx? - spring-boot

I have an entity class with an enumeration class, while I am trying to insert data then it shows me a conversion error. What is the best and easiest way to implement it?
enum class Vehicle {
Bike,
Bicycle
}
class VehicleTemplate{
#Id
#GeneratedValue
var id:Long?=null
var model:String?=null
var type:Vehicle?=null
}
interface VehicleTemplateInf : ReactiveNeo4jRepository< VehicleTemplate,Long>
#RestController
#RequestMapping("/product/category/")
class CrtProductCategory {
#Autowired
lateinit var vehicleTemplateInf: VehicleTemplateInf
#PutMapping("add")
fun addCategory(#RequestBody vehicleTemplate: VehicleTemplate?): Mono< VehicleTemplate > {
return vehicleTemplateInf(vehicleTemplate!!)
}
}
Displayed Error
"No converter found capable of converting from type [Product.Vehicle] to type [org.neo4j.driver.Value]"
I have used this dependencies
<dependency>
<groupId>org.neo4j.springframework.data</groupId>
<artifactId>spring-data-neo4j-rx-spring-boot-starter</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
<version>2.11.0</version>
</dependency>

Related

Spring Cassandra ddl-auto

I'm using Spring with Cassandra and I'm trying to deploy the service to an existing Cassandra DB in production, I've been reading about the ddl-auto and I'm not sure if my code will override the scheme or the data there.
These are my dependencies,
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-cassandra</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
and I'm using the following to query the Repository,
import org.springframework.data.cassandra.repository.CassandraRepository;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
...
...
#Repository
public interface PostsRepo extends CrudRepository<Posts, String> {
Optional<Posts> findBypostid(String id);
}
I don't have any sql file in my project, and my application.properties file is empty.
My questions are
Do I need to define something specifically to stop/disable automatic schema creation?
Is the automatic schema creation option only applicable for embedded DB and there is nothing to worry about here?
what about spring.jpa.hibernate.ddl or spring.jpa.defer-datasource-initialization? should I set them to none and false? or not having them simply is enough?
Spring Data Cassandra can create the schema in Cassandra for you (Tables and Types). It is not enabled by default.
If you work with Spring Boot and the starter spring-boot-starter-data-cassandra you can use the flag spring.data.cassandra.schema-action in your application.yaml
spring:
data:
cassandra:
keyspace-name: sample keyspace
username: token
password: passwd
schema-action: create-if-not-exists
request:
timeout: 10s
connection:
connect-timeout: 10s
init-query-timeout: 10s
If you work with Spring data cassandra without Spring boot you may inherit from AbstractCassandraConfiguration and override the method getSchemaAction as described below:
#Configuration
#EnableCassandraRepositories
public class CassandraConfig extends AbstractCassandraConfiguration {
#Value("${cassandra.contactpoints}")
private String contactPoints;
#Value("${cassandra.port}")
private int port;
#Value("${cassandra.keyspace}")
private String keySpace;
#Value("${cassandra.basePackages}")
private String basePackages;
#Override
protected String getKeyspaceName() {
return keySpace;
}
#Override
protected String getContactPoints() {
return contactPoints;
}
#Override
protected int getPort() {
return port;
}
#Override
public SchemaAction getSchemaAction() {
return SchemaAction.CREATE_IF_NOT_EXISTS;
}
#Override
public String[] getEntityBasePackages() {
return new String[] {basePackages};
}
}
Multiple values are allowed for the field as described in the official Spring documentation quoted here:
SchemaAction.NONE: No tables or types are created or dropped. This is the default setting.
SchemaAction.CREATE: Create tables, indexes, and user-defined types from entities annotated with #Table and types annotated with #UserDefinedType. Existing tables or types cause an error if you tried to create the type.
SchemaAction.CREATE_IF_NOT_EXISTS: Like SchemaAction.CREATE but with IF NOT EXISTS applied. Existing tables or types do not cause any errors but may remain stale.
SchemaAction.RECREATE: Drops and recreates existing tables and types that are known to be used. Tables and types that are not configured in the application are not dropped.
SchemaAction.RECREATE_DROP_UNUSED: Drops all tables and types and recreates only known tables and types.
If the feature might be useful for your developments, it is not recommended to use it and specially in production here are my rational:
The way to implement an efficient data model with Cassandra is to design your queries first, and based on them your defined the needed tables. If 2 queries work with the same data it is recommended to create 2 tables with the same data changing the primary key. If you work with Object Mapping, (object=>Table) you may be tempted to reuse the same bean for different queries ..with the same table
Creating the schema in production will require fine tuning of the DDL requests (overriding the TTL, the compaction strategy, enabling NodeSync, special properties)
Human errors. If you let you schema-action to RECREATE...good luck.

Springboot Upload and Download file(multiple format) from S3

Im trying to expose simple rest controller to take multipart file as input and upload to S3 and download API to get file key as input and download the file from S3 and send to FE.
Here this Api should support all standard file formats.
Is there a generic implementation for this as this looks pretty standard feature . I could not find any implementation
Why don't you try Spring Content? It does exactly what you need.
Assuming maven, Spring Boot and Spring Data (let me know if you are using something else):-
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- HSQL -->
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.hateoas</groupId>
<artifactId>spring-hateoas</artifactId>
</dependency>
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>content-s3-spring-boot-starter</artifactId>
<version>${spring-content-version}</version>
</dependency>
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>content-rest-spring-boot-starter</artifactId>
<version>${spring-content-version}</version>
</dependency>
...
<dependencies>
Update your entity with the managed Spring Content annotations.
Document.java
#Entity
public class Document {
...existing fields...
#ContentId
private String contentId;
#ContentLength
private Long contentLen;
#MimeType
private String mimeType;
...getters and setters...
}
Create a connection to your S3 store. The S3 Store has been implemented to use a SimpleStorageResourceLoader so this bean will ultimately be used by your store.
StoreConfig.java
#Configuration
#EnableS3Stores
public class S3Config {
#Autowired
private Environment env;
public Region region() {
return Region.getRegion(Regions.fromName(System.getenv("AWS_REGION")));
}
#Bean
public BasicAWSCredentials basicAWSCredentials() {
return new BasicAWSCredentials(env.getProperty("AWS_ACCESS_KEY_ID"), env.getProperty("AWS_SECRET_KEY"));
}
#Bean
public AmazonS3 client(AWSCredentials awsCredentials) {
AmazonS3Client amazonS3Client = new AmazonS3Client(awsCredentials);
amazonS3Client.setRegion(region());
return amazonS3Client;
}
#Bean
public SimpleStorageResourceLoader simpleStorageResourceLoader(AmazonS3 client) {
return new SimpleStorageResourceLoader(client);
}
}
Define a Store typed to Document - as that is what you are associating content with.
DocumentContentStore.java
#StoreRestResource
public interface DocumentStore extends ContentStore<Document, String> {
}
When you run this you also need to set the bucket for your store. This can be done by specifying spring.content.s3.bucket in application.properties/yaml or by setting the AWS_BUCKET environment variable.
This is enough to create a REST-based content service for storing content in S3 and associating that content with your Document entity. Spring Content will see the Store interface and the S3 dependencies. Assume you want to store content in S3 and inject an implementation of your interface for you. Meaning you dont have to implement it yourself. You will be able to store content by POSTing a multipart-form-data request to:
POST /documents/{documentId}/content
and fetching it again with:
GET /documents/{documentId}/content
(the service supports full CRUD BTW and video streaming in case that might be important).
You'll see that Spring Content associates content with your entity by managing the content related annotations for you.
This can be used with or without Spring Data. The dependencies and Store are a little different depending. I assume you have entities that you want to associate data with as you added a spring-data tag but let me know if not and I can adapt the answer.
There is a video of this here - the demo starts about half way through. It uses the Filesystem module not S3 but they are interchangeable. Just need to pick the right dependencies for the type of store you are using. S3 in your case.
HTH

Spring Test mockMvc is ignoring validation

please help me to solve the following issue:
I have a class, where several fields are marked as #NotNull:
public class SearchCommentRequest {
#NotNull
private Date fromDate;
#NotNull
private Date toDate;
//...
}
Object if this class is passed to controller as #RequestBody annotated also with #Valid:
#PostMapping(value = "/comment/search", consumes="application/json", produces = "text/csv")
public ResponseEntity<byte[]> searchComments(#RequestBody #Valid SearchCommentRequest searchRequest) {
List<SearchCommentResult> comments = commentService.searchComments(searchRequest);
So, I expect that if either fromDate or toDate is null - exception will be thrown.
Writing my integration tests, I decided to check this validation case as well:
#Test
public void searchCommentsValidateRequest() throws Exception {
ObjectMapper mapper = new ObjectMapper();
SearchCommentRequest request = new SearchCommentRequest();
// toDate = null;
request.setFromDate(new Date());
String requestBody = mapper.writer().writeValueAsString(request);
mockMvc.perform(post(COMMENT_SEARCH_ENDPOINT)
.contentType("application/json")
.content(requestBody))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().is(400));
}
But it looks like mockMvc is ignoring validation. Searching for the same issues, I found several sources where solution was adding the following dependencies:
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>2.2.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
<version>3.0.0</version>
</dependency>
But it didn't help.
I'm using Spring 4.3.3.RELEASE and manually added to pom.xml the following dependency:
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
Actually, #Notnull is often use at entity level.
It will validate and throw exception when you persist entity automatically.
If you want to validate at controller and use #Valid.
You should declare more about BindingResult result
and check errors
if(result.hasErrors()){
//do something or throw exceptions
}

JsonView for filtering Json properties in Spring MVC not working

In our Spring MVC web application for job recruiting, I work on a RESTful service to get information about available companies for a given account, or more detailed data for a single company.
This is implemented using Spring MVC in a pretty straightforward way.
Logically, the API for a single company shows more details than for a list of companies. In particular, there are two fields (CoverPhoto and Logo) whoch are only to be shown when querying the details for a single company by its id.
For the generation of the Json output, I use Jackson to annotate the returned DTO object for specific field names because sometimes they are different from the member variable names.
One of the ways to implement this in an elegant way is using JsonViews, as described in these tutorials:
https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring
http://www.baeldung.com/jackson-json-view-annotation
The only difference between them is that the second one uses interfaces for the View classes, and the first one uses classes. But that should not make any difference and my code is not working as expected with either of them.
I have created to interfaces (ObjectList and OblectDetails) and annotated the fields in my DTO with
#JsonView(Views.ObjectList.class)
for the fields I want to see on both the lisdt and the details API, and with
#JsonView(Views.ObjectDetails.class)
for the fields only to shown in the single company API.
But unfortunately, both API's show all fields, regardless of the annotation. Also fields without a #JsonView annotation appear in the output JSON, while according to the documentation, when annotating the Controller method with a #JsonView, each fields should also be annotated with a #JsonView annotation to show up.
My simplified code looks as follows:
DTO:
package nl.xxxxxx.dto.too;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.annotation.JsonView;
#JsonAutoDetect
#JsonPropertyOrder({"id", "name", "logo", "coverPhoto", "description", "shortDescription",
"phone", "address"})
public class XxxCompanyDto {
#JsonView(Views.ObjectList.class)
private Long id;
#JsonView(Views.ObjectList.class)
private String name;
#JsonView(Views.ObjectDetails.class)
private String logo;
#JsonView(Views.ObjectDetails.class)
#JsonProperty("cover_photo")
private String coverPhoto;
#JsonView(Views.ObjectList.class)
private String description;
//more fields
//setters, no getters are used to prevent ambiguity for Json generation
//omitted for clarity
}
Views:
package nl.xxx.dto.too;
public class Views {
public interface ObjectList {}
public interface ObjectDetails extends ObjectList {}
}
Controller:
package nl.xxx.controller;
import com.fasterxml.jackson.annotation.JsonView;
import org.springframework.web.bind.annotation.*;
//more imports
/**
* Created by Klaas van Gelder on 17-Nov-16.
*/
#RestController
public class XxxCompanyController {
#Autowired
//services omitted
#JsonView(Views.ObjectDetails.class)
#RequestMapping(value = "/public-api/xxx/company/{companyId}", method = RequestMethod.GET)
public TooCompanyDto getCompanyById(
#RequestHeader(value = "X-Channel") String publicationChannelToken,
#PathVariable(value = "companyId") Long companyId) {
XxxCompany tooCompany = tooCompanyService.getCompanyById(companyId);
//some verifications omitted
TooCompanyDto tooCompanyDto = tooCompanyJsonConverter.convertToDto(tooCompany);
return tooCompanyDto;
}
#JsonView(Views.ObjectList.class)
#RequestMapping(value = "/public-api/xxx/company", method = RequestMethod.GET)
public List<TooCompanyDto> listCompaniesForChannel(
#RequestHeader(value = "X-Channel") String publicationChannelToken) {
XxxPublicationChannel channel = tooVacancyService.findPublicationChannelByToken(publicationChannelToken);
List<XxxCompany> xxxCompaniesForChannel = xxxCompanyService.findCompaniesByPublicationChannelToken(publicationChannelToken);
List<XxxCompanyDto> dtoList = new ArrayList<>();
for (XxxCompany xxxCompany : xxxCompaniesForChannel) {
XxxCompanyDto xxxCompanyDto = xxxCompanyJsonConverter.convertToDto(xxxCompany);
dtoList.add(xxxCompanyDto);
}
return dtoList;
}
}
Maven:
org.springframework
spring-core
4.2.2.BUILD-SNAPSHOT
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson-2-version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson-2-version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson-2-version}</version>
</dependency>
//more depedencies
with <jackson-2-version>2.2.2</jackson-2-version> in parent POM
It seems that the JsonView annotations are completely ignored. I can probably use another solution by using two separate DTO classes but it would be nice to get this working as it should!
Any hints are more than welcome!

No suitable constructor found for type [simple type, class java.time.LocalDateTime]

My program is written in Java 8, when I use the type of LocalDateTime, it will give me the following error:
No suitable constructor found for type [simple type, class java.time.LocalDateTime]: can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)
at [Source: [B#5976fe6f; line: 1, column: 80] (through reference chain: com.boot.framwork.entity.UserInfo["accessTime"])
The entity is like this:
public class UserInfo implements Serializable {
private static final long serialVersionUID = 1L;
private String username;
private int age;
private LocalDateTime accessTime;
private Date time;
private List<String> list = new ArrayList<String>();
private boolean isMarried;
public UserInfo() {
}
It worked when I didn't use LocalDateTime
It seems to me, that you don't have a JSR310 support. Jackson need some additional configuration to be able to recognize Java 8 Date&Time API data types. You can add it via project dependencies, just add a dependency on this
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.7.4</version>
</dependency>
or if you use a Gradle
compile group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.7.4'
Since you use SpringBoot, this should be enough, hence not, create a Bean of type JSR310Module manually.
I had this same issue and needed MyCustomClass to be taken in and out of the Lambda Function correctly so that it can be passed through my State Machine in the Step Function without any hiccups.
I used the following post to come up with the best answer for me:
AWS Lambda json deserialization with jackson annotations
And this is my code that worked for me:
import java.io.InputStream;
import java.io.OutputStream;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.LambdaLogger;
import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
public class StaticSearchPagingLambdaFunctionHandler implements RequestStreamHandler {
LambdaLogger logger = null;
MyCustomClass myCustomClass = null;
// Register the JavaTimeModule for LocalDate conversion
ObjectMapper objectMapper = new ObjectMapper().registerModule(new JavaTimeModule());
#Override
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) {
myCustomClass = objectMapper.readValue(inputStream, MyCustomClass .class);
// ...
// Do stuff with myCustomClass
// ...
objectMapper.writeValue(outputStream, myCustomClass);
}
}
Even though the JSON string will print out differently with the ObjectMapper writing to the OutPutStream, when the next lambda function takes it in while going through the Step Function, it will still get converted to LocalDate correctly.
Make sure that in MyCustomClass your toString() method prints correctly. My toString() method looks like this:
import java.time.LocalDate;
import org.json.JSONObject;
public class SimpleSearch {
private LocalDate startDate;
private LocalDate endDate;
// ...
// Getters and Setters for the LocalDate variables
// ...
#Override
public String toString() {
return new JSONObject(this).toString();
}
public SimpleSearch() {}
}
then your JSON printouts will always look like this when it gets sent to the lambda and not that other crazy Jackson format:
{
"startDate": "2018-11-01",
"endDate": "2018-11-16"
}
Some of the Maven dependencies I used:
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20180813</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.9.7</version>
</dependency>
Hopefully AWS fixes the Jackson conversions to be reciprocal, to and from JSON, so that we wouldn't have to resort to these custom conversions anymore.

Resources