How to make a custom controller in Java Spring? - java

I want to make a telegram bot using Java and Spring. I want to create my own controller, similar to RestController, which allows me to make my own mapping (like GetMapping), only for the chat command, respectively. As an argument, I want to be able to receive a message from an update (which has already been parsed, removing the command, because it is already specified in the mapping), userID, chatID, and so on. That is, I need some kind of middle layer that will convert an update to these arguments that I want.
Does Spring allow you to do this? If not, is it possible to at least get the update as an argument?
I know there is a HandleInterceptor, but it uses HttpRequest which is not exactly what I need. Maybe I could extend it make a wrapper somehow?
Possible implementation of what I want is something like this:
#TelegramController
public class ListController {
private final ListService listService;
private final SendMessageService sendMessageService;
public ListController(ListService listService, SendMessageService sendMessageService) {
this.listService = listService;
this.sendMessageService = sendMessageService;
}
#CommandMapping("/addlist")
public TalkState addList(String message, User user) {
listService.addList(message, user.getId());
return TalkState.CONTINUE;
}
#CallbackQueryMapping
public TalkState addList(#RequestParam("inline_message_id") String inlineMessageId, #RequestParam("data") String data) {
listService.addList(message, userId);
sendMessageService.send("Your data: " + data);
return TalkState.STOP;
}
}

Related

What is the correct way to filter response by query parameter in Spring Boot?

If I have a Get request that returns orders of clients, how can I filter the response to give me the objects that have a specific value for example that are made by those specific clients in Spring Boot?
I have tried with #PathVariable and #RequestParams but every attempt failed.
Thank you in advance.
If you want to show a specific order which has an identifier of some sort, use #PathVariable. In the following example, the identifier is a String, but in many case it will rather be long or an Integer.
#RestController
#RequestMapping("/orders")
public class OrdersController {
#GetMapping("/{id}")
public Order getOrder(#PathVariable("id") String id) {
// get the order with a specified id from the backend
}
}
The web request in this case will look like http:/<host>:<port>/orders/123
If you want to filter the order by some name, like 'madeBy John', use Request parameter:
#RestController
#RequestMapping("/orders")
public class OrdersController {
#GetMapping("/")
public List<Order> getOrdersFilteredByName(#RequestParam("madeBy") madeBy) {
// get the order filtered by the person who made the order
// note, this one returns the list
}
}
In this case the web request will look like this: http:/<host>:<port>/orders?madeBy=John
Note that technically you can implement whatever you want at the backend, so you can pass, say, John in the first example as a path variable, on server its a String after all, however what I've described is a straightforward and kind-of-standard way of doing these things - so can expect to see this convention in many projects at least.
#RestController
#RequestMapping("/order")
public class OrderController {
// http://<host>:<port>/order/1
#GetMapping("/{id}")
public Order getOrder(#PathVariable Long id) {
// Return your order
}
// http://<host>:<port>/order?madeBy=John
#GetMapping("/)
public List<Order> getOrdersMadeBy(#RequestParam("madeBy") String madeBy) {
// Return your order list
}
}

How to structure email request class for different types of emails requiring different rendering params?

Currently, my notification request is like this:
public class EmailRequest{
public enum EmailType{
TYPE_1,
TYPE_2,
...
}
EmailType emailType;
String toAddress;
EmailRenderer renderer;
}
where EmailRenderer is an interface
public interface EmailRenderer{
EmailMessage render()
}
Now, each type of email has a separate implementation of the renderer interface and each implementation contains some rendering data that has to be provided by the client. This data can be different for each implementation.
Example:
public class Type1EmailRenderer implements EmailRenderer{
String param1;
String param2;
#Override
EmailMessage render(){
//rendering logic using the params
}
}
But, it seems redundant to me for the user to set the email type and renderer as well. Choosing the renderer should automatically get me the emailType. How should I restructure the request to be free of this redundancy? Also, can I use any design pattern for providing the renderers to my users?
I'll base my answer on a claim that,
putting aside programming-related questions, at the level of human logic, it looks to me strange that if I want to send an email I should know about renderers at all.
In my understanding If I have emails of different types (you've called them TYPE_1 and TYPE_2, let's give more "business" names for better clarity, like "dailyReport" or "advertisement", you'll see later why) I should just prepare a request with my data (param1, param2) and send it. I shouldn't care about renderers at all as long as the same email type assumes that the same type of renderer will be used.
So, lets say, type "advertisement" has a mandatory parameter String topic and optional parameter String targetAudience and type "dailyReport" has Integer totalUsersCount and optional String mostActiveUserName.
In this case, I propose the somewhat hybrid approach mainly based on Builder creation pattern:
public class EmailRequestBuilder {
private String toAddress;
private EmailRequestBuilder(String to) {
this.toAddress = to;
}
public static EmailRequestBuilder newEmailRequest(String to) {
return new EmailRequestBuilder(to);
}
public AdvertisementBuilder ofAdvertisementType(String topic) {
return new AdvertisementBuilder(topic, this);
}
public DailyReportBuilder ofDailyReportType(Integer totalUsersCount) {
return new DailyReportBuilder(totalUsersCount, this);
}
// all builders in the same package, hence package private build method,
// concrete email type builders will call this method, I'll show at the end
EmailRequest build(EmailType type, EmailRenderer emailRenderer) {
return new EmailRequest (to, type, emailRenderer);
}
}
public class AdvertisementBuilder {
private String topic;
private EmailRequestBuilder emailRequestBuilder;
// package private, so that only EmailRequestBuilder will be able to create it
AdvertisementBuilder(String topic, EmailRequestBuilder emailRequestBuilder) // mandatory parameters in constructor + reference to already gathered data {
this.topic = topic;
this.emailRequestBuilder = emailRequestBuilder;
}
// for optional parameters provide an explicit method that can be called
// but its not a mandatory call
public AdvertisementBuilder withTargetAudience(String audience) {
this.audience = audience;
return this;
}
public EmailRequest buildRequest() {
EmailRenderer renderer = new AdvertisementRenderer(topic, audience);
return emailRequestBuilder.build(EmailType.ADVERTISEMENT, renderer);
}
}
// A similar builder for DailyReport (I'll omit it but assume that there is a class
class DailyReportBuilder {}
Now the good part about it that now you can't go wrong as a user. A typical interaction with such a construction will be:
EmailRequest request = EmailRequestBuilder.newEmailRequest("john.smith#gmail.com")
.ofAdvertisementType("sample topic") // its a mandatory param, you have to supply, can't go wrong
.withTargetAudience("target audience") // non-mandatory call
.buildRequest();
Couple of notes:
Once you pick a type by calling ofDailyReportType/ ofAdvertisementType the user can't really supply parameters of different email type, because it gets "routed" to the builder that doesn't have methods for wrong parameters. An immediate implication of this is that an autocomplete will work in your IDE and people who will use this method will thank you about it ;)
It's easy to add new email types this way, no existing code will change.
Maybe with this approach, an enum EmailType will be redundant. I've preserved it in my solution but probably you'll drop it if it's not required.
Since I sometimes restrict the visibility (package private build methods, constructors, and so forth) - it will be __the_only__way to create the request which means that no-one will create "internal" objects only because it's possible to do so. At least a malicious programmer will think twice before breaking encapsulation :)
For example you can use "factory method".
EmailRenderer createRenderer(EmailType type) {
switch (type) {
case: TYPE_1:
return new RendererType1();
case: TYPE_2:
return new RendererType2();
...
}
}
Also, you probably can introduce cashing of this objects in order not to create them every time. Maybe some lazy initialization (you create appropriate Renderer first time when you needed and after that always return that same instance).

Should I put the ID of my entity in the URL or into the form as a hidden field?

I think in terms of REST, the ID should be placed into the URL, something like:
https://example.com/module/[ID]
and then I call GET, PUT, DELETE on that URL. That's kind of clear I think. In Spring MVC controllers, I'd get the ID with #PathVariable. Works.
Now, my practical problem with Spring MVC is, that if I do this, I have to NOT include the ID as part of the form (as well), Spring emits warnings of type
Skipping URI variable 'id' since the request contains a bind value with the same name.
otherwise. And it also makes kind of sense to only send it once, right? What would you do if they don't match??
That would be fine, but I do have a custom validator for my form backing bean, that needs to know the ID! (It needs to check if a certain unique name is already being used for a different entity instance, but cannot without knowing the ID of the submitted form).
I haven't found a good way to tell the validator that ID from #PathVariable, since the validation happens even before code in my controller method is executed.
How would you solve this dilemma?
This is my Controller (modified):
#Controller
#RequestMapping("/channels")
#RoleRestricted(resource = RoleResource.CHANNEL_ADMIN)
public class ChannelAdminController
{
protected ChannelService channelService;
protected ChannelEditFormValidator formValidator;
#Autowired
public ChannelAdminController(ChannelService channelService, ChannelEditFormValidator formValidator)
{
this.channelService = channelService;
this.formValidator = formValidator;
}
#RequestMapping(value = "/{channelId}/admin", method = RequestMethod.GET)
public String editChannel(#PathVariable Long channelId, #ModelAttribute("channelForm") ChannelEditForm channelEditForm, Model model)
{
if (channelId > 0)
{
// Populate from persistent entity
}
else
{
// Prepare form with default values
}
return "channel/admin/channel-edit";
}
#RequestMapping(value = "/{channelId}/admin", method = RequestMethod.PUT)
public String saveChannel(#PathVariable Long channelId, #ModelAttribute("channelForm") #Valid ChannelEditForm channelEditForm, BindingResult result, Model model, RedirectAttributes redirectAttributes)
{
try
{
// Has to validate in controller if the name is already used by another channel, since in the validator, we don't know the channelId
Long nameChannelId = channelService.getChannelIdByName(channelEditForm.getName());
if (nameChannelId != null && !nameChannelId.equals(channelId))
result.rejectValue("name", "channel:admin.f1.error.name");
}
catch (EmptyResultDataAccessException e)
{
// That's fine, new valid unique name (not so fine using an exception for this, but you know...)
}
if (result.hasErrors())
{
return "channel/admin/channel-edit";
}
// Copy properties from form to ChannelEditRequest DTO
// ...
// Save
// ...
redirectAttributes.addFlashAttribute("successMessage", new SuccessMessage.Builder("channel:admin.f1.success", "Success!").build());
// POST-REDIRECT-GET
return "redirect:/channels/" + channelId + "/admin";
}
#InitBinder("channelForm")
protected void initBinder(WebDataBinder binder)
{
binder.setValidator(formValidator);
}
}
I think I finally found the solution.
As it turns out Spring binds path variables to form beans, too! I haven't found this documented anywhere, and wouldn't have expected it, but when trying to rename the path variable, like #DavidW suggested (which I would have expected to only have a local effect in my controller method), I realized that some things got broken, because of the before-mentioned.
So, basically, the solution is to have the ID property on the form-backing object, too, BUT not including a hidden input field in the HTML form. This way Spring will use the path variable and populate it on the form. The local #PathVariable parameter in the controller method can even be skipped.
The cleanest way to solve this, I think, is to let the database handle the duplicates: Add a unique constraint to the database column. (or JPA by adding a #UniqueConstraint)
But you still have to catch the database exception and transform it to a user friendly message.
This way you can keep the spring MVC validator simple: only validate fields, without needing to query the database.
Could you not simply disambiguate the 2 (URI template variables vs. parameters) by using a different name for your URI template variable?
#RequestMapping(value = "/{chanId}/admin", method = RequestMethod.PUT)
public String saveChannel(#PathVariable Long chanId, #ModelAttribute("channelForm") #Valid ChannelEditForm channelEditForm, BindingResult result, Model model, RedirectAttributes redirectAttributes)
{
[...]
What ever you said is correct the correct way to design rest api is to mention the resource id in path variable if you look at some examples from the swagger now as open api you could find similar examples there
for you the correct solution would be to use a custom validator like this
import javax.validation.Validator;`
import org.apache.commons.lang3.StringUtils;`
import org.springframework.validation.Errors;`
importorg.springframework.validation.beanvalidation.CustomValidatorBean;`
public class MyValidator extends CustomValidatorBean {`
public void myvalidate(Object target,Errors errors,String flag,Profile profile){
super.validate(target,errors);
if(StringUtils.isEmpty(profile.name())){
errors.rejectValue("name", "NotBlank.profilereg.name", new Object[] { "name" }, "Missing Required Fields");
}
}
}
This would make sure all the fields are validated and you dont need to pass the id in the form.

CORDA: Invoke custom query on all parties involved in transaction

I have multiple questions on Corda:
Can we predefine the h2 configuration to pick in build.gradle file?
I have a transaction in my corda network where i want to validate something based on custom fields the validation has to happen based on query that needs to be fired on all 3 parties sender, receiver, notary how can i fetch the session of all 3 nodes? i am able to pull the session of sender using getServiceHub().jdbcSession()
What will be the most suggested way to query a notary for custom fields? Can it be done using creating a sub-flow if yes then how?
we have validating and non validating notaries, where do we actually validate using notary? Where do we write the validation code?
how can we enable autosuggest in intellij for java api of corda?
You can set the h2Port option in deployNodes:
node {
name "O=PartyA,L=London,C=GB"
advertisedServices = []
p2pPort 10005
rpcPort 10006
webPort 10007
h2Port 10008
cordapps = ["net.corda:corda-finance:$corda_release_version"]
rpcUsers = [[ user: "user1", "password": "test", "permissions": []]]
}
Is that the kind of configuration you needed?
Each node's database is private by design, and cannot be queried from another node. Instead, you need to communicate with the other nodes as part of your flow in a way that causes them to initiate a response flow on their end where they query their own databases and send the results back. Something like:
public class CollectDBDataFlow {
#InitiatingFlow
#StartableByRPC
public static class Initiator extends FlowLogic<List<Object>> {
Party counterparty;
public Initiator(Party counterparty) {
this.counterparty = counterparty;
}
#Suspendable
#Override public List<Object> call() {
// TODO: Implement queryMyDatabase to perform querying.
Object myDBData = queryMyDatabase();
FlowSession counterpartySession = initiateFlow(counterparty);
Object theirDBData = counterpartySession.receive(Object.class);
return ImmutableList.of(myDBData, theirDBData);
}
}
#InitiatedBy(Initiator.class)
public static class Responder extends FlowLogic<Void> {
private FlowSession counterpartySession;
public Responder(FlowSession counterpartySession) {
this.counterpartySession = counterpartySession;
}
#Suspendable
#Override
public Void call() {
// TODO: Implement queryMyDatabase to perform querying.
Object myDBData = queryMyDatabase();
counterpartySession.send(myDBData);
return null;
}
}
}
The role of the notary isn't to be queried for data, but to prevent double-spends. You could technically do it using the method described in (2) above, but it wouldn't be advised. What are you trying to achieve?
The validation logic is written into the platform. See https://github.com/corda/corda/blob/release-V1/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryFlow.kt.
The auto-complete should appear automatically, just like any other library.

How do I define a function for AWS Lambda to start in Java?

I'm using this doc as a tutorial
http://docs.aws.amazon.com/lambda/latest/dg/get-started-step4-optional.html
The entry method/function that they supply for AWS lamda looks like this:
public String myHandler(int myCount, Context context) {
LambdaLogger logger = context.getLogger();
logger.log("received : " + myCount);
return String.valueOf(myCount);
}
My issue is that I don't know what arguments I can define and how AWS lambda knows what to pass to me. It'd be great to see all potential method signatures I could come up with. Maybe I'm just not looking in the right place for this documentation, but I'd love to be able to do something like
public String myHandler(String json) {
MyJsonValidatorClass validator = new ...;
boolean isValid = validator.isValidJson(json);
return String.valueOf(isValid);
}
I'm just not sure what I'm able to do in AWS Lamdas really. When writing a java main application, I know that I have String[] args to deal with and nothing else. Am I missing something here? Or am I just thinking about this totally wrong?
The lambda runtime uses reflection to see what type your method wants as it's first parameter, then tries to parse the raw input data according to that specification. The types it supports are listed here:
Simple Java types (AWS Lambda supports the String, Integer, Boolean, Map, and List types)
POJO (Plain Old Java Object) type
Stream type (If you do not want to use POJOs or if Lambda's serialization approach does not meet your needs, you can use the byte stream implementation. [..])
Examples for how handler methods would look like are
// you could do your own json parsing in here
String handler(String input, Context context)
// lambda parses json for you
JoinResponsePojo handler(JoinRequestPojo request, Context context)
// when even String is not enough
void handler(InputStream inputStream, OutputStream outputStream, Context context)
For convenience and to help you prevent errors, there are the RequestHandler and RequestStreamHandler interfaces which capture exactly above method signatures (docs). I'd usually use those rather than freestyle-implementing handler methods.
Usually the most convenient way is to work with POJOs directly, since usually the input is json. There are also some predefined POJOs for common events in aws-lambda-java-events you can use. Or you can write your own like outlined in "Example: Using POJOs for Handler Input/Output (Java)"
js callbacks are used to return data, so your example is either
public class ExampleHandler1 implements RequestHandler<String, String> {
#Override
public String handleRequest(String input, Context context) {
// would preferably use some other way to generate json
return "{\"speech\": \"hello theres\"}";
}
}
or using a pojo like
public class ExampleHandler2 implements RequestHandler<String, Speech> {
public static class Speech {
private String speech;
public String getSpeech() {
return speech;
}
public void setSpeech(String speech) {
this.speech = speech;
}
}
#Override
public Speech handleRequest(String input, Context context) {
Speech speech = new Speech();
speech.setSpeech("hello theres");
return speech;
}
}

Categories