Creating a unique Id and saving it in DB - spring

I am new to Spring REST. I have to achieve the below mentioned requirement using Spring REST. I have to use JPA Repository for DB interaction
I have 2 tables, Application and App_Config. Application table has the following rows:
id(Primary Key), ApplicationId, Status, Source_System. App_config table has the following rows: ApplicationId (foreign key), HeaderText, FooterText. I need to use java UUID to generate a unique id for the application every time a new application sends an HTTP POST request. Based on the ApplicationId generated I need to save the data in App_Config table. There is a possibility that the same application appears twice. In that case I have to retrieve the already generated ApplicationId and load the Header and Footer from App_Config table.
Please advise how to achieve this via POST method. I need to send back only the generated ApplicationId to the user

Partial solution of your problem (the other part is not understood) regarding sending a rest with UUID and the rest api will be server+/generator/uuid
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
#RestController
#RequestMapping("/generator")
public class UuidGeneratorRestController {
#RequestMapping(value = "/uuid", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<UUID> getUUID() {
UUID generated = UUID.randomUUID();
return new ResponseEntity(generated, HttpStatus.OK);
}
}

Related

REST API return entire queried JSON

I do maintain an small REST API (SpringBoot) offering services to obtain data about ticketing from a small sized retailer. i.e.: you can send a request in order to get some information from a certain ticket (which has unique ID); the JSON response consists of a selection of fields from the unique ticket (which is stored as an unique document in Mongo DB).
Let say the API receives a request, then it would execute a query to Mongo DB, and then apply a projection to parse the queried data into a data model class, which in turn is finally parsed to a response JSON like, i.e.:
{
"ticketData": {
"retailerId": "023",
"ticketId": "09834723469324",
"ticketDate": "2021-06-20"
},
"buyerData": {
"buyerId": "LN4382"
}
}
Well, I am now required to return the entire queried JSON (that is, a JSON containing the whole ticket information, that has a lot of fields). ¿Is there any way to achieve this without creating a data model class with tens or hundreds of properties to match the stored ticket JSON? Even if I specify the API response using YAML and then use a codegen tool, is a lot of tedious work, and whenever the ticket JSON format evolves, I would need to change my DAO and response.
I just would like to send the original Mongo stored JSON and hand it back to the API client. Is there any way to achieve that?
You can utilize Jackson ObjectMapper which Spring already uses to serialize and deserialize JSON.
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.ArrayList;
import java.util.List;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
#RestController
public class HelloWorldController {
private final ObjectMapper objectMapper;
public HelloWorldController(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
#GetMapping("/jsonList")
public ResponseEntity<List<JsonNode>> getJsonList() {
List<String> data = List.of("{\"number\": 1}",
"{\"number\": 2}",
"{\"number\": 3}");
List<JsonNode> nodes = toJsonNodeList(data);
return ResponseEntity.ok(nodes);
}
private List<JsonNode> toJsonNodeList(List<String> strings) {
List<JsonNode> nodes = new ArrayList<>();
for (String s : strings) {
try {
JsonNode node = this.objectMapper.readTree(s);
nodes.add(node);
} catch (IOException ioe) {
throw new UncheckedIOException(ioe);
}
}
return nodes;
}
}

#GetMapping method not calles

I am a beginner so please don't be mean.
I have got an html page index.html
And I want the method MainController::getListEmployee to be called.
In this method, I put a System.err to see if the method is called. And I see nothing.
Controller code
package com.cgi.listeemployes.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.cgi.listeemployes.model.User;
import com.cgi.listeemployes.repository.UserRepository;
#Controller // This means that this class is a Controller
#RequestMapping(path="/") // This means URL's start with /demo (after Application path)
public class MainController {
#Autowired // This means to get the bean called userRepository
// Which is auto-generated by Spring, we will use it to handle the data
private UserRepository userRepository;
#GetMapping(path="/index.html")
public #ResponseBody Iterable<User> getListEmployee() {
// This returns a JSON or XML with the users
System.err.println("getting ");
return userRepository.findAll();
}
#PostMapping(path="/add") // Map ONLY POST Requests
public #ResponseBody String addNewUser (#RequestParam String name
, #RequestParam String email) {
// #ResponseBody means the returned String is the response, not a view name
// #RequestParam means it is a parameter from the GET or POST request
User n = new User();
n.setName(name);
n.setEmail(email);
userRepository.save(n);
return "Saved";
}
#GetMapping(path="/all")
public #ResponseBody Iterable<User> getAllUsers() {
// This returns a JSON or XML with the users
return userRepository.findAll();
}
}
thanks for your help
When you want to return a html, just return a string with the name of the html file, it could be "Index" (without the .html).
In your #GetMapping(path="/index.html"), you are returning an object instead a html.
If you want to load data from database and render it at your html, then add the attribute "Model model" in your parameters, like this:
#GetMapping(path="/index.html")
public String getListEmployee(Model model) {
List<User> users = userRepository.findAll();
model.addAttribute("yourUsers", users); // this gonna inject the list of users in your html
System.err.println("getting ");
return "Index"
}
Then in your html, you can get the users with ${yourUsers} and do whatever you want.
I saw your project, it is missing the template engine. Template engine is what gonna get the data of your backend and show in your front/html. I added the Thymeleaf template engine into your pom.xml, and it worked. Here is the thymeleaf dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
To work with thymeleaf, you have to put all your html into a new folder called "templates" in the "resources", same level of "static". You cannot use html in the static folder, this folder should have only css, javascripts and assets.

How to specify specific request mapping url

sorry i am new to spring boot and trying to learn, i have this code that when i run it will open a json request of my catalog item if i enter any url that is localhost/catalog/(any userid). But i want to narrow it to 1 specific userid how can i do that? for example i dont want any url to work except localhost/catalog/friends or any other item from the list i mention.
Code:
package com.poc.moviecatalog.controller;
import com.poc.moviecatalog.models.CatalogItem;
import java.util.Collections;
import java.util.List;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/catalog")
public class MovieController {
#RequestMapping("/{userId}")
public List<CatalogItem> getCatalog(#PathVariable("userId") String userId) {
return Collections.singletonList(
new CatalogItem("friends", "test", 9)
);
}
}
I hope I've understood your question correctly, you don't want to provide a mapping for any user id, but want one specific endpoint /catalog/friends.
#RestController
#RequestMapping("/catalog")
public class FriendsController {
#GetMapping("/friends")
public List<Friends> getFriends() {
....
}
}
then the url would be: http:<HOST>:<PORT>/catalog/friends

Spring MVC: Saving values added into a form to load later

I have a ParticipantsController.java which can add/delete/edit participant names.
import java.util.ArrayList;
import java.util.List;
import javax.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import questForTheBest.domain.Participants;
#Controller
public class ParticipantsController {
#InitBinder("participant") // Validator for participant model
protected void initBinder(WebDataBinder binder) {
binder.addValidators(new ParticipantsValidator());
}
List<Participants> participantList = new ArrayList<>(); // create a list of participants
#RequestMapping("/participants") // Participants page
public String testing(Model model) {
model.addAttribute("participantList", participantList);
return "forms/ParticipantMaster";
}
#RequestMapping(value = "/participantDetail", method = RequestMethod.GET) // Shows participants
public String participantDetail(#ModelAttribute("participant") Participants participant, #RequestParam(value="participantId", required=false, defaultValue="-1") int participantId) {
if (participantId >= 1) { // Shows participants with an id greater than or equal to 1
Participants p2 = participantList.stream().filter(p -> (p.getId() == participantId)).findAny().get(); // gets participants
participant.setId(p2.getId()); // sets participants id
participant.setName(p2.getName()); // sets participants name
} else {
participant.setId(Participants.lastId); // otherwise create a new participant id
Participants.lastId++; // increment last id
}
return "forms/ParticipantDetail";
}
#RequestMapping(value = "/addParticipant", method = RequestMethod.POST) // Adding participants page
public String addParticipant(#Valid #ModelAttribute("participant") Participants participant,BindingResult result, Model model) {
if (result.hasErrors()) { // validation
return "forms/ParticipantDetail";
}
else {
participantList.removeIf(p -> (p.getId() == participant.getId()));
participantList.add(participant); // add participants
model.addAttribute("participantList", participantList);
return "forms/ParticipantMaster";
}
}
#RequestMapping(value = "/deleteParticipant", method = RequestMethod.GET) // Deleting participants
public String deleteParticipant(#RequestParam(value="participantId", required=false, defaultValue="-1") int participantId, Model model) {
participantList.removeIf(p -> (p.getId() == participantId)); // removes the participant with id
model.addAttribute("participantList", participantList);
return "forms/ParticipantMaster";
}
}
I wish to be able to: everytime a list of names are entered into the form I would like them to be saved when a button "Save current names" is pressed so that they can be later loaded again on another page by being able to be selected from a drop down box and then a "load" button is clicked.
The overall application is a leaderboard so I would like the same participants to be able to be saved for example so it can be loaded when a certain class in school will need to use the leaderboard.
Does this require the use of a database or is it possible without.
Thank you.
Ofcourse you will need the database for permanent storage of the leaderboard data on the solid state device (SSD storage) given your use case as memory(RAM) gets flushed/reset on machine reboot or crashes. You can have a layer of in-memory or a distributed cache as a layer above the database layer using an in-memory data structure like you have used a list in your implementation or redis respectively to make your application efficient for serving reads.
You got to have a Database Access Object (DAO) layer in your application for performing database related operations.
Please refer to this link to understand how DAO layer is implemented in spring boot
https://www.baeldung.com/jsf-spring-boot-controller-service-dao
Hope this helps!

Using Java to do *resumable uploads* using a *signed url* on google cloud storage

Based on the doc regarding how to create an object in google-cloud-storage (see "create" method in https://googleapis.github.io/google-cloud-java/google-cloud-clients/apidocs/index.html), we should be using the blob.writer(...) method when trying to upload large files, as it presumably somehow automatically handles resumable uploads. Is this right?
However, if we wish to do resumable uploads on SIGNED urls, how does one do so in Java? (Any sample code or pointers would be very much appreciated; thus far, my research has led me to believe that it is not possible using the nicely created java libraries, and instead one needs to custom build one's own logic using "PUT" and "POST" statements in Java after one has generated the signed url. Is this the "best" way so far?)
Regarding your first point, yes, the blob.writer(...) method does automatically handle resumable uploads. Unfortunately, this method is not callable from a signed URL, and only uploads files directly from a stream of bytes.
However, as you mentioned, it is possible to create a resumable upload from a signed URL with other methods, for instance using a PUT method seems to be a nice workaround.
What I did, was the following:
Create a signed URL with a "PUT" method. You can do so by specifying the SignUrlOption, as well I specified a service account with the required permissions in the bucket.
Use URLFetch to throw an HTTP request to this signed URL. I believe that you cannot use a curl command directly for example, and the UrlFetch API does the trick.
Add the uploadType=resumable header to the urlFetch HTTP request. See this documentation on how this works, and extra parameters and information.
I configured URLFetch to do an asynchronous call to the signed URL, as I believe it's more convenient when uploading big files.
An example code, to be used in an App Engine handler:
package com.example.storage;
import java.io.IOException;
import java.io.FileInputStream;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import java.util.HashMap;
import java.util.Map;
import java.nio.charset.StandardCharsets;
import java.net.URL;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
// Cloud Storage Imports
import com.google.cloud.storage.Bucket;
import com.google.cloud.storage.BucketInfo;
import com.google.cloud.storage.Storage;
import com.google.cloud.storage.StorageOptions;
import com.google.cloud.storage.Blob;
import com.google.cloud.storage.BlobId;
import com.google.cloud.storage.BlobInfo;
import com.google.cloud.storage.Storage.SignUrlOption;
import com.google.auth.oauth2.ServiceAccountCredentials;
import com.google.cloud.storage.HttpMethod;
// Url Fetch imports
import com.google.appengine.api.urlfetch.HTTPMethod;
import com.google.appengine.api.urlfetch.HTTPRequest;
import com.google.appengine.api.urlfetch.URLFetchService;
import com.google.appengine.api.urlfetch.URLFetchServiceFactory;
import com.google.appengine.api.urlfetch.HTTPHeader;
#WebServlet(name = "MainStorage", value = "/")
public class MainStorage extends HttpServlet {
#Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
// Bucket parameters
String bucketName = "MY-BUCKET-NAME";
String blobName = "MY-BLOB-NAME";
String keyPath = "/PATH-TO-SERVICE-ACCOUNT-KEY/key.json";
BlobId blobId = BlobId.of(bucketName, blobName);
Storage storage = StorageOptions.getDefaultInstance().getService();
// Create signed URL with SignUrlOptions
URL signedUrl = storage.signUrl(BlobInfo.newBuilder(bucketName, blobName).build(), 14, TimeUnit.DAYS,
SignUrlOption.signWith(ServiceAccountCredentials.fromStream(new FileInputStream(keyPath))),
SignUrlOption.httpMethod(HttpMethod.PUT));
// Contents to upload to the Blob
String content = "My-File-contents";
// Build UrlFetch request
HTTPRequest upload_request = new HTTPRequest(signedUrl, HTTPMethod.PUT);
upload_request.setPayload(content.getBytes(StandardCharsets.UTF_8));
// Set request to have an uploadType=resumable
HTTPHeader set_resumable = new HTTPHeader("uploadType", "resumable");
upload_request.setHeader(set_resumable);
URLFetchService fetcher = URLFetchServiceFactory.getURLFetchService();
// Do an asynchronous call to the signed URL with the contents
fetcher.fetchAsync(upload_request);
// Return response to App Engine handler call
response.setContentType("text/plain");
response.getWriter().println("Hello Storage");
}
}
This can probably be done in a better way, but I believe it gives an idea on how such application can be made.

Resources