Possible to download JPA repository in Vaadin as CSV file? - database

Assume that we have defined a entity and it's connected to a database. Now we can access the database by using a repository.
#Autowired
private DataLoggRepository dataLoggRepository;
If I want to get all the rows from the database and download it. Then I can write this code:
List<DataLogg> dataLoggers = dataLoggRepository.findAll();
Now, how can I donwload the object dataLoggers as a CSV file in Vaadin in a proper way?

Here you can see how to create a link to download a file:
Anchor csvLink = new Anchor(new StreamResource("file.csv",
() -> {
String csvString = ...// create the csv
return new ByteArrayInputStream(csvString.getBytes());
}), "Download CSV");
csvLink.getElement().setAttribute("download", true);
To create the CSV you have various options like OpenCSV or directly create the CSV from the SQL query.

Here is a working example
// Download all data
Anchor download = new Anchor(); // Add this to the layout
loggerId.addValueChangeListener(e-> {
String fileName = String.valueOf(loggerId.getValue()) + ".csv";
List<DataLogg> selectedLogger = dataLoggRepository.findByLoggerId(loggerId.getValue());
download.setHref(getStreamResource(fileName, selectedLogger));
});
download.getElement().setAttribute("download",true);
download.add(new Button("Download", new Icon(VaadinIcon.DOWNLOAD_ALT)));
Function
public StreamResource getStreamResource(String filename, List<DataLogg> selectedLogger) {
// Create a large CSV file in a form of StringBuilder and then convert it all to bytes
StringWriter stringWriter = new StringWriter();
stringWriter.write("id, dateTime, DO0, DO1, DO2, DO3, AI0, AI1, AI2, AI3, loggerId, samplingTime\n");
for (int i = 0; i < selectedLogger.size(); ++ i) {
DataLogg dataLogg = selectedLogger.get(i);
String row = dataLogg.getId() + "," +
dataLogg.getDateTime() + "," +
dataLogg.getDO0() + "," +
dataLogg.getDO1() + "," +
dataLogg.getDO2() + "," +
dataLogg.getDO3() + "," +
dataLogg.getAI0() + "," +
dataLogg.getAI1() + "," +
dataLogg.getAI2() + "," +
dataLogg.getAI3() + "," +
dataLogg.getLoggerId() + "," +
dataLogg.getSamplingTime() + "\n";
stringWriter.write(row);
}
// Try to download
try {
byte[] buffer = stringWriter.toString().getBytes("UTF-8");
return new StreamResource(filename, () -> new ByteArrayInputStream(buffer));
} catch (UnsupportedEncodingException e) {
byte[] buffer = new byte[] {0};
return new StreamResource(filename, () -> new ByteArrayInputStream(buffer));
}
}

Related

Spring boot - I want to write a POST endpoint to consume multipart/form-data WITHOUT any file uplload, I need to post json data as key-value pair pair

I want to do something like this -
#ApiOperation("Solve for tasks in JSON file")
#PostMapping(value = "/tasks/file",
consumes = {MediaType.MULTIPART_FORM_DATA_VALUE},
produces = {MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<Plan> solveTest(#RequestBody(value = "file") InputStream filrInputStream) {}
I tried adding jersey multipart dependency in my spring boot app and tried my api method signature as below, but when I try posting my json string I get input stream as empty-
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
</dependency>
#ApiImplicitParams({
#ApiImplicitParam(
name = "file",
dataType = "java.io.InputStream",
examples = #io.swagger.annotations.Example(
value = {
#ExampleProperty(value = "[\r\n"
+ " {\r\n"
+ " \"duration\": 0,\r\n"
+ " \"xxxxxxTG\": \"string\",\r\n"
+ " \"sequence\": 0,\r\n"
+ " \"xxxxxx\": \"string\",\r\n"
+ " \"taskId\": \"string\",\r\n"
+ " \"taskType\": \"string\",\r\n"
+ " \"xcoordinate\": 0,\r\n"
+ " \"ycoordinate\": 0\r\n"
+ " }\r\n"
+ "]", mediaType = "multipart/form-data")
}))
})
#PostMapping(value = "/tasks/file",
consumes = {MediaType.MULTIPART_FORM_DATA_VALUE},
produces = {MediaType.APPLICATION_JSON_VALUE}
)
public ResponseEntity<Plan> solveTest1(#FormDataParam("file") InputStream file, #FormDataParam("file") FormDataContentDisposition fileMetaData)

java.io.File's .createNewFile() doesn't create a file

class FileClassOne {
public static void main(String args[]) {
File myDir = new File(File.separator);
System.out.println("myDir.getAbsolutePath() = " + myDir.getAbsolutePath());
System.out.println("myDir.isDirectory() = " + myDir.isDirectory());
System.out.println("myDir.isFile() = " + myDir.isFile());
System.out.println();
myDir = new File(File.separator+"Java"+File.separator+"FilePartOne");
System.out.println("myDir.getAbsolutePath() = " + myDir.getAbsolutePath());
System.out.println("myDir.isDirectory() = " + myDir.isDirectory());
System.out.println("myDir.isFile() = " + myDir.isFile());
System.out.println();
File myFile = new File(myDir, "Temp.txt");
System.out.println("myFile.getAbsolutePath() = " + myFile.getAbsolutePath());
System.out.println("myFile.isDirectory() = " + myFile.isDirectory());
System.out.println("myFile.isFile() = " + myFile.isFile());
System.out.println("myFile.exists() = " + myFile.exists());
try {
myFile.createNewFile();
} catch (IOException e) {
System.out.println(e.getMessage());
}
Output:
myDir.getAbsolutePath() = C:\
myDir.isDirectory() = true
myDir.isFile() = false
myDir.getAbsolutePath() = C:\Java\FilePartOne
myDir.isDirectory() = false
myDir.isFile() = false
myFile.getAbsolutePath() = C:\Java\FilePartOne\Temp.txt
myFile.isDirectory() = false
myFile.isFile() = false
myFile.exists() = false
The system cannot find the path specified
This code if from an online tutorial that works in the video and it's copied verbatim. IDE is eclipse.
I would say its likely because of missing directories along the path "C:\Java\FilePartOne".
The statement:
myFile.createNewFile();
Will attempt to create a file on a given path, not create any missing directories. You therefore get the error "The system cannot find the path specified" if any directories are missing when executing the statement.
A quick way to fix this would be to either create the missing folders yourself or add the code below just before myFile.createNewFile();.
myFile.getParentFile().mkdirs();

PostgresSql + Nodejs (ClaudiaJS) : How to cast array of string to array of timestamp

I am writing API which insert into a table with multiple rows, I am using UNNEST to make it work.
What I have done:
in js file:
api.post(PREFIX + '/class/insert', function (request) {
var db = pgp(dbconnect);
//Params
var data = request.body; //should be an array
var classes = [];
var starts = [];
var ends = [];
for (var i = 0; i < data.length; i++) {
classes.push(data[i].class_id);
starts.push(data[i].timestamp_start);
ends.push(data[i].timestamp_end);
}
const PQ = require('pg-promise').ParameterizedQuery;
var sql =
"INSERT INTO sa1.class(class_id, timestamp_start, timestamp_end) " +
"VALUES( "+
"UNNEST(ARRAY" + JSON.stringify(classes).replace(/"/g, "'") + "), " +
"UNNEST(ARRAY" + JSON.stringify(starts).replace(/"/g, "'") + "), " +
"UNNEST(ARRAY" + JSON.stringify(ends).replace(/"/g, "'") + ")"
const final_sql = new PQ(sql);
return db.any(final_sql)
.then(function (data) {
pgp.end();
return 'successful';
})
.catch(function (error) {
console.log("Error: " + error);
pgp.end();
});
}
Request body
[{
"class_id":"1",
"timestamp_start":"2017-11-14 14:01:23.634437+00",
"timestamp_end":"2017-11-14 15:20:23.634437+00"
}, {
"class_id":"2",
"timestamp_start":"2017-11-14 15:01:23.634437+00",
"timestamp_end": "2017-11-14 16:20:23.634437+00"
}]
When I run api in postman, I get the error is:
column "timestamp_start" is of type timestamp with time zone but
expression is of type text
Issue is obviously from ARRAY of string that I used in sql, my question is how to create ARRAY of timestamp for UNNEST, or any suggestion are appreciated.
Thanks
Never initialize the database inside the handler, see: Where should I initialize pg-promise
Never call pgp-end() inside HTTP handlers, it destroys all connection pools.
Use static ColumnSet type to generate multi-insert queries.
Do not return from db.any, there is no point in that context
You must provide an HTTP response within an HTTP handler
You are providing a confusing semantics for column class_id. Why is it called like that and yet being converted into a timestamp?
Never concatenate objects with strings directly.
Never concatenate SQL strings manually, it will break formatting and open your code to SQL injection.
Use Database methods according to the expected result, i.e. none in your case, and not any. See: https://github.com/vitaly-t/pg-promise#methods
Initialize everything needed only once:
const db = pgp(/*connection*/);
const cs = new pgp.helpers.ColumnSet([
'class_id',
{
name: 'timestamp_start',
cast: 'timestamp'
},
{
name: 'timestamp_end',
cast: 'timestamp'
}
], {table: {table: 'class', schema: 'sa1'}});
Implement the handler:
api.post(PREFIX + '/class/insert', request => {
const sql = pgp.helpers.insert(request.body, cs);
db.none(sql)
.then(data => {
// provide an HTTP response here
})
.catch(error => {
console.log('Error:', error);
// provide an HTTP response here
});
}
Many thanks to #JustMe,
It worked after casting array
var sql =
"INSERT INTO sa1.class(class_id, timestamp_start, timestamp_end) " +
"VALUES( "+
"UNNEST(ARRAY" + JSON.stringify(classes).replace(/"/g, "'") + "), " +
"UNNEST(ARRAY" + JSON.stringify(starts).replace(/"/g, "'") + "::timestamp[]), " +
"UNNEST(ARRAY" + JSON.stringify(ends).replace(/"/g, "'") + "::timestamp[])"

Get the Ip Info from Client to Web Api

fist at all sorry for my bad English.
I'm trying to get the IP in the login option to save them as a "Session" in the database and register who and where is using the app.
I try this, but it obvious that it isn't going to work.
var ip = new System.Net.WebClient().DownloadString("http://ipinfo.io/json");
It Gets the IP Client. So it logical that I need to do this get in the Client side. But the problem is that the Client can change this values before its send to the Web API
$http.get("http://ipinfo.io/json").then(function (response) {
return response.data;
}).catch(function (response) {
console.log(response.data);
});
The users can change this value to send me a false data in the login and I don't have how to validate if this information is valid or real. So, the question is ¿How can I do this without let the user manipulate this data?
Create a method in web API, and we can save all the information needed directly to database.
public static string UserIp()
{
string ip = HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
if (string.IsNullOrEmpty(ip))
{
ip = HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];
}
try
{
string url1 = "http://geoip.nekudo.com/api/" + ip.ToString(); // passing IP address will return location information.
WebClient client = new WebClient(); // Intialize the webclient
string jsonstring = client.DownloadString(url1);
dynamic dynObj = JsonConvert.DeserializeObject(jsonstring); // De-serialize the JSON string
string filePath = AppDomain.CurrentDomain.BaseDirectory + "\\App_Data\\Logs\\" + "Ip.txt";
using (System.IO.StreamWriter writer = new StreamWriter(filePath, true))
{
// you can save the information to database instead of writing to a file
writer.WriteLine("UserIp:" + ip);
writer.WriteLine("Date:" + DateTime.Now);
writer.WriteLine("JsonString:" + jsonstring);
writer.WriteLine("Country name:" + dynObj.country.code);
}
return dynObj;
}
catch (Exception ex)
{
string filePath = AppDomain.CurrentDomain.BaseDirectory + "\\App_Data\\Logs\\" + "I.txt";
string url1 = "http://geoip.nekudo.com/api/" + ip.ToString();
WebClient client = new WebClient(); // Intialize the webclient
string jsonstring = client.DownloadString(url1);
dynamic dynObj = JsonConvert.DeserializeObject(jsonstring);
// string a = dynObj.country.code;
using (System.IO.StreamWriter writer = new StreamWriter(filePath, true))
{
writer.WriteLine("Message :" + ex.Message + "<br/>" + Environment.NewLine + "StackTrace :" +
ex.StackTrace +
"" + Environment.NewLine + "Date :" + DateTime.Now.ToString());
writer.WriteLine("UserIp:" + ip);
writer.WriteLine("Dynamic obj:" + dynObj);
}
return null;
}
}

Spark 2.0: Moving from RDD to Dataset

I want to adapt my Java Spark app (which actually uses RDDs for some calculations) to use Datasets instead of RDDs. I'm new to Datasets and not sure how to map which transaction to a corresponding Dataset operation.
At the moment I map them like this:
JavaSparkContext.textFile(...) -> SQLContext.read().textFile(...)
JavaRDD.filter(Function) -> Dataset.filter(FilterFunction)
JavaRDD.map(Function) -> Dataset.map(MapFunction)
JavaRDD.mapToPair(PairFunction) -> Dataset.groupByKey(MapFunction) ???
JavaPairRDD.aggregateByKey(U, Function2, Function2) -> KeyValueGroupedDataset.???
And the corresponing questions are:
Equals JavaRDD.mapToPair the Dataset.groupByKey method?
Does JavaPairRDD map to KeyValueGroupedDataset?
Which method equals the JavaPairRDD.aggregateByKey method?
However, I want to port the following RDD code into a Dataset one:
JavaRDD<Article> goodRdd = ...
JavaPairRDD<String, Article> ArticlePairRdd = goodRdd.mapToPair(new PairFunction<Article, String, Article>() { // Build PairRDD<<Date|Store|Transaction><Article>>
public Tuple2<String, Article> call(Article article) throws Exception {
String key = article.getKeyDate() + "|" + article.getKeyStore() + "|" + article.getKeyTransaction() + "|" + article.getCounter();
return new Tuple2<String, Article>(key, article);
}
});
JavaPairRDD<String, String> transactionRdd = ArticlePairRdd.aggregateByKey("", // Aggregate distributed data -> PairRDD<String, String>
new Function2<String, Article, String>() {
public String call(String oldString, Article newArticle) throws Exception {
String articleString = newArticle.getOwg() + "_" + newArticle.getTextOwg(); // <<Date|Store|Transaction><owg_textOwg###owg_textOwg>>
return oldString + "###" + articleString;
}
},
new Function2<String, String, String>() {
public String call(String a, String b) throws Exception {
String c = a.concat(b);
...
return c;
}
}
);
My code looks this yet:
Dataset<Article> goodDS = ...
KeyValueGroupedDataset<String, Article> ArticlePairDS = goodDS.groupByKey(new MapFunction<Article, String>() {
public String call(Article article) throws Exception {
String key = article.getKeyDate() + "|" + article.getKeyStore() + "|" + article.getKeyTransaction() + "|" + article.getCounter();
return key;
}
}, Encoders.STRING());
// here I need something similar to aggregateByKey! Not reduceByKey as I need to return another data type (String) than I have before (Article)

Resources