I've created two lambda functions (one Java, one Python) and added both of them to an ALB.
The code of the functions just converts the event to JSON and returns it:
public APIGatewayV2HTTPResponse handleRequest(APIGatewayV2HTTPEvent event, Context context) {
logger.info("path="+event.getRawPath());
APIGatewayV2HTTPResponse response = new APIGatewayV2HTTPResponse();
response.setStatusCode(200);
response.setBody(new ObjectMapper().writeValueAsString(event));
When I call the ALB for the Python lambda, I get
{
"requestContext": {
"elb": {
"targetGroupArn": "arn:aws:elasticloadbalancing:us-west-2:821844782278:targetgroup/lambda-fu7bq1bfvaetnsi28l5g/3440f9a769be4e61"
}
},
"httpMethod": "GET",
"path": "/test",
"queryStringParameters": {},
"headers": {
"accept": "*/*",
"host": "core-external-dev-135473791.us-west-2.elb.amazonaws.com",
"user-agent": "curl/7.85.0",
"x-amzn-trace-id": "Root=1-63a5d7ad-1aadf14e6ecfb0e733ba868d",
"x-forwarded-for": "98.167.119.9",
"x-forwarded-port": "443",
"x-forwarded-proto": "https"
},
"body": "",
"isBase64Encoded": false
}
However, for the Java lambda I get
{
"version": null,
"routeKey": null,
"rawPath": null,
"rawQueryString": null,
"cookies": null,
"headers": {
"accept": "*/*",
"host": "core-external-dev-135473791.us-west-2.elb.amazonaws.com",
"user-agent": "curl/7.85.0",
"x-amzn-trace-id": "Root=1-63a5d77d-457cc4773e94430d0d64d668",
"x-forwarded-for": "98.167.119.9",
"x-forwarded-port": "443",
"x-forwarded-proto": "https"
},
"queryStringParameters": {},
"pathParameters": null,
"stageVariables": null,
"body": "",
"isBase64Encoded": false,
"requestContext": {
"routeKey": null,
"accountId": null,
"stage": null,
"apiId": null,
"domainName": null,
"domainPrefix": null,
"time": null,
"timeEpoch": 0,
"http": null,
"authorizer": null,
"requestId": null
}
}
So while the headers are there, there's nothing else related to the request.
Ah found the problem, i need to change the class of the event when i switched the code from function URL to ALB ( to ApplicationLoadBalancerRequestEvent )
Related
I am returning Response in my controller using Springboot. I am throwing an Exception in my service class and returning a particular response code when the exception is caught. However, when ever I hit the endpoint on Postman I still gett 200 ok even if an exception was caught.
Here is the service
public Response signup(RegisterDto register) throws CrossPointException{
try{
if (!register.getEmail().matches(EMAIL_PATTERN)){
throw new CrossPointException("Email is invalid");
}
if(!register.getPhoneNumber().matches(PHONE_NUMBER_PATTERN)){
throw new CrossPointException("Phone number is invalid");
}
Optional<User> foundUser = userRepository.findByUsername(register.getEmail());
if(foundUser.isPresent()){
throw new CrossPointException("Email already exist");
}
User user = new User();
user.setFirstName(register.getFirstName());
user.setLastName(register.getLastName());
user.setPhoneNumber(register.getPhoneNumber());
user.setEmail(register.getEmail());
user.setPassword(passwordEncoder.encode(register.getPassword()));
user.setUsername(register.getEmail());
user.setCreatedAt(new Date());
user.setEnabled(false);
user.setRole(Types.Role.BUSINESS);
user.setRcNumber(register.getRcNumber());
user.setUserType(register.getUserType());
user.setRole(Types.Role.INDIVIDUAL);
user.setPartnerName(register.getPartnerName());
user.setBusinessRegistered(register.isBusinessRegistered());
user.setGender(register.getGender());
User savedUser = userRepository.save(user);
basketService.createBasket(savedUser.getId());
GenerateOtpResponseDto response = otpService.generateOtp();
mailService.sendMail(new NotificationEmail("Please Activate your account",
user.getEmail(), "this is your verification cade " + response.getOtp()));
return Response.status(200, "testing this out").entity(BaseResponse.builder().status(true).
responseCode(HttpStatus.OK.toString()).message("Thanks for signing up. Kindly check your " +
"email to activate your account").data(user).build()).build();
}catch (CrossPointException ex){
return Response.status(400, HttpStatus.BAD_REQUEST.toString()).entity(
BaseResponse.builder()
.responseCode(HttpStatus.BAD_REQUEST.toString())
.status(false)
.message(ex.getMessage())
.build()
).build();
}
}
This is what my custom BaseResponse looks like
public class BaseResponse<T> {
private boolean status;
private String message;
private String responseCode;
#JsonInclude(JsonInclude.Include.NON_NULL)
private T data;
}
When I hit the endpoint I am getting a number of things that are needless as part of the response.
{
"context": {
"headers": {},
"configuration": null,
"entity": {
"context": {
"headers": {},
"configuration": null,
"entity": {
"status": false,
"message": "Email already exist",
"responseCode": "400 BAD_REQUEST"
},
"entityType": "com.crosspoint.crosspointfinance.data.model.BaseResponse",
"entityAnnotations": [],
"entityStream": {
"committed": false,
"closed": false
},
"length": -1,
"language": null,
"location": null,
"mediaType": null,
"committed": false,
"date": null,
"lastModified": null,
"allowedMethods": [],
"acceptableMediaTypes": [
{
"type": "*",
"subtype": "*",
"parameters": {},
"quality": 1000,
"wildcardType": true,
"wildcardSubtype": true
}
],
"links": [],
"entityTag": null,
"stringHeaders": {},
"entityClass": "com.crosspoint.crosspointfinance.data.model.BaseResponse",
"responseCookies": {},
"acceptableLanguages": [
"*"
],
"requestCookies": {},
"lengthLong": -1
},
"status": 400,
"length": -1,
"language": null,
"location": null,
"mediaType": null,
"cookies": {},
"metadata": {},
"date": null,
"lastModified": null,
"allowedMethods": [],
"entity": {
"status": false,
"message": "Email already exist",
"responseCode": "400 BAD_REQUEST"
},
"statusInfo": {
"reasonPhrase": "400 BAD_REQUEST",
"statusCode": 400,
"family": "CLIENT_ERROR"
},
"links": [],
"entityTag": null,
"stringHeaders": {},
"headers": {}
},
"entityType": "org.glassfish.jersey.message.internal.OutboundJaxrsResponse",
"entityAnnotations": [],
"entityStream": {
"committed": false,
"closed": false
},
"length": -1,
"language": null,
"location": null,
"mediaType": null,
"committed": false,
"date": null,
"lastModified": null,
"allowedMethods": [],
"acceptableMediaTypes": [
{
"type": "*",
"subtype": "*",
"parameters": {},
"quality": 1000,
"wildcardType": true,
"wildcardSubtype": true
}
],
"links": [],
"entityTag": null,
"stringHeaders": {},
"entityClass": "org.glassfish.jersey.message.internal.OutboundJaxrsResponse",
"responseCookies": {},
"acceptableLanguages": [
"*"
],
"requestCookies": {},
"lengthLong": -1
},
"status": 200,
"length": -1,
"language": null,
"location": null,
"mediaType": null,
"cookies": {},
"metadata": {},
"date": null,
"lastModified": null,
"allowedMethods": [],
"entity": {
"context": {
"headers": {},
"configuration": null,
"entity": {
"status": false,
"message": "Email already exist",
"responseCode": "400 BAD_REQUEST"
},
"entityType": "com.crosspoint.crosspointfinance.data.model.BaseResponse",
"entityAnnotations": [],
"entityStream": {
"committed": false,
"closed": false
},
"length": -1,
"language": null,
"location": null,
"mediaType": null,
"committed": false,
"date": null,
"lastModified": null,
"allowedMethods": [],
"acceptableMediaTypes": [
{
"type": "*",
"subtype": "*",
"parameters": {},
"quality": 1000,
"wildcardType": true,
"wildcardSubtype": true
}
],
"links": [],
"entityTag": null,
"stringHeaders": {},
"entityClass": "com.crosspoint.crosspointfinance.data.model.BaseResponse",
"responseCookies": {},
"acceptableLanguages": [
"*"
],
"requestCookies": {},
"lengthLong": -1
},
"status": 400,
"length": -1,
"language": null,
"location": null,
"mediaType": null,
"cookies": {},
"metadata": {},
"date": null,
"lastModified": null,
"allowedMethods": [],
"entity": {
"status": false,
"message": "Email already exist",
"responseCode": "400 BAD_REQUEST"
},
"statusInfo": {
"reasonPhrase": "400 BAD_REQUEST",
"statusCode": 400,
"family": "CLIENT_ERROR"
},
"links": [],
"entityTag": null,
"stringHeaders": {},
"headers": {}
},
"statusInfo": "OK",
"links": [],
"entityTag": null,
"stringHeaders": {},
"headers": {}
}
This is response is too lengthy and contains a lot of useless info.
All I want is something like this:
{
"status": true,
"message": "Successfully Joined wait-list",
"responseCode": "200 OK",
"data": {
"id": "6349a3be93a8523e3f40ee00",
"email": "ojoi#gmail.com",
"firstName": "Ojo"
}
}
This is what my controller looks like
#PostMapping("/signup")
#PermitAll
public Response signUp(#RequestBody RegisterDto register) throws CrossPointException{
return Response.ok(auth.signup(register)).build();
}
In Quarkus, it seem that the entity returned by an exception mapper get wrapped in another entity.
Give an JAX-RS exception mapper like:
#Provider
public class WebhookExceptionMapper implements ExceptionMapper<WebhookException> {
#Override
public Response toResponse(final WebhookException e) {
return Response.status(e.getError().getCode().getStatus())
.entity(Entity.entity(e.getError(), MediaType.APPLICATION_JSON))
.build();
}
}
I get the following error response:
{
"entity": {
"code": "SOME_ERROR_CODE",
"msg": "Error message"
},
"variant": {
"language": null,
"mediaType": {
"type": "application",
"subtype": "json",
"parameters": {},
"wildcardType": false,
"wildcardSubtype": false
},
"encoding": null,
"languageString": null
},
"annotations": [],
"mediaType": {
"type": "application",
"subtype": "json",
"parameters": {},
"wildcardType": false,
"wildcardSubtype": false
},
"language": null,
"encoding": null
}
I would like the following to be returned:
{
"code": "SOME_ERROR_CODE",
"msg": "Error message"
}
Is that possible?
As is seen by looking at the package name, the javax.ws.rs.client.Entity class is only meant to be used on the client side. On the server side, you don't need to use it. What you are actually seeing is the Entity object being serialized, not the error.
If you want to set the type, just use the type() method on the Response.ResponseBuilder (that you get back from calling Response.status()). And to set the body just use the entity() method.
return Response.status(e.getError().getCode().getStatus())
.entity(e.getError())
.type(MediaType.APPLICATION_JSON)
.build();
I implemented org.hibernate.event.spi.PreInsertEventListener interface to populate common fields such as createdTime, modifiedTime, etc. in my entities. I also use Spring Boot Data Repository interfaces to handle REST methods.
POST method to create an entity (E.g., User) is working fine and the User details are getting persisted in the tables. The request body is:
{
"login": "nagu2",
"password": "123456",
"displayName": "Nagu 2",
"preferences": "Test",
"userProfile": {
"firstName": "Nagarajan"
},
"userStatus": "USERSTATUS_ACTIVE"
}
However, the Body of the 201 response contains a null value for the fields populated using PreInsertEventListener. The response body is:\
{
"createdTime": null,
"updatedTime": null,
"createdBy": null,
"updatedBy": null,
"tenant": 1,
"login": "nagu2",
"password": "123456",
"displayName": "Nagu 2",
"preferences": "Test",
"userRoles": null,
"userProfile": {
"createdTime": null,
"updatedTime": null,
"createdBy": null,
"updatedBy": null,
"tenant": 1,
"firstName": "Nagarajan",
"middleName": null,
"lastName": null,
"dateOfBirth": null,
"timeZone": null,
"primaryPhone": null,
"secondaryPhone": null,
"primaryEmail": null,
"secondaryEmail": null
},
"lastLoginTime": null,
"userStatus": "USERSTATUS_ACTIVE",
"_links": {
"self": {
"href": "http://localhost:8080/api/user/6"
},
"user": {
"href": "http://localhost:8080/api/user/6"
}
}
}
If I GET the Location URL of the 201 response, I do get all the fields populated with non-null values. The GET response of the Location URL is:
{
"createdTime": "2019-12-07T11:29:50.000+0000",
"updatedTime": "2019-12-07T11:29:50.000+0000",
"createdBy": 1,
"updatedBy": 1,
"tenant": 1,
"login": "nagu2",
"password": "123456",
"displayName": "Nagu 2",
"preferences": "Test",
"userRoles": [],
"userProfile": {
"createdTime": "2019-12-07T11:29:50.000+0000",
"updatedTime": "2019-12-07T11:29:50.000+0000",
"createdBy": 1,
"updatedBy": 1,
"tenant": 1,
"firstName": "Nagarajan",
"middleName": null,
"lastName": null,
"dateOfBirth": null,
"timeZone": null,
"primaryPhone": null,
"secondaryPhone": null,
"primaryEmail": null,
"secondaryEmail": null
},
"lastLoginTime": null,
"userStatus": "USERSTATUS_ACTIVE",
"_links": {
"self": {
"href": "http://localhost:8080/api/user/5"
},
"user": {
"href": "http://localhost:8080/api/user/5"
}
}
}
Setting RepositoryRestConfiguration.setReturnBodyOnCreate to true as suggested in this SO question did not help too.
Can you please help me with a way to return non-null values for the PreInsertEventListener-modified-fields in the 201 response of the POST request?
Following point from This SO Question and this blog post
If you have insert and update actions on your entity in one single
transaction, then your preInsert listener actions will be overridden
by the update action.
helped me resolve the issue.
Point to be noted here is that:
In onPreInsert method, when I updated only the values in Object[] state, it caused both insert and update query to fire. However, when I set the values in both Object[] state and the entity (event.getEntity()), hibernate fired only the insert query and 201 response JSON contains non-null values.
Following is the modified onPreInsert implementation:
public boolean onPreInsert(PreInsertEvent event) {
if (event.getEntity() instanceof BaseEntity) {
Object obj = event.getEntity();
BaseEntity entity = (BaseEntity) obj;
boolean isMultiTenant = event.getEntity() instanceof BaseMultiTenantEntity;
Object[] state = event.getState();
String[] propertyNames = event.getPersister().getEntityMetamodel().getPropertyNames();
Date createdTime = new Date();
entity.setCreatedTime(createdTime);
setValue(state, propertyNames, CREATED_TIME_PROPERTY, createdTime);
Integer createdBy = getUserId();
entity.setCreatedBy(createdBy);
setValue(state, propertyNames, CREATED_BY_PROPERTY, createdBy);
entity.setUpdatedTime(createdTime);
setValue(state, propertyNames, UPDATED_TIME_PROPERTY, createdTime);
entity.setUpdatedBy(createdBy);
setValue(state, propertyNames, UPDATED_BY_PROPERTY, createdBy);
if (isMultiTenant) {
BaseMultiTenantEntity multiTenantEntity = (BaseMultiTenantEntity) obj;
multiTenantEntity.setTenant(getTenant());
}
}
return false;
}
I have a simple REST API for which I am just returning Resonse.ok().build(), since the body of the function is asynchronous
I was expecting an empty response with a 200 http status code, but instead I got a full description of what seems to be the Response calls as entity.
What did I do wrong?
Here is the json response that I received from my API call
{
"context": {
"headers": {},
"entity": null,
"entityType": null,
"entityAnnotations": [],
"entityStream": {
"committed": false,
"closed": false
},
"length": -1,
"language": null,
"location": null,
"committed": false,
"mediaType": null,
"allowedMethods": [],
"links": [],
"entityTag": null,
"stringHeaders": {},
"lastModified": null,
"date": null,
"acceptableMediaTypes": [
{
"type": "*",
"subtype": "*",
"parameters": {},
"quality": 1000,
"wildcardType": true,
"wildcardSubtype": true
}
],
"acceptableLanguages": [
"*"
],
"entityClass": null,
"responseCookies": {},
"requestCookies": {},
"lengthLong": -1
},
"status": 200,
"length": -1,
"language": null,
"location": null,
"metadata": {},
"cookies": {},
"mediaType": null,
"allowedMethods": [],
"links": [],
"statusInfo": "OK",
"entityTag": null,
"stringHeaders": {},
"entity": null,
"lastModified": null,
"date": null,
"headers": {}
}
the REST api looks like this
#RestController
#RequestMapping("/accountworkers")
#Api(value = "/updater")
public class AccountUpdater {
private static Logger LOGGER = LoggerFactory.getLogger(AccountUpdater.class);
#Autowired
private AccountUpdaterController auController;
#RequestMapping(value = "/updater", method = RequestMethod.GET)
#Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Response runUpdater() {
LOGGER.info("Running account updater");
auController.doAccountUpdateAsync();
return Response.ok().build();
}
}
You are trying to mix JAX-RS and Spring MVC. These are not the same thing and are not compatible. By returning Response, which Spring MVC doesn't recognize, it it serializing it like it would any other entity. If you are using Spring MVC, then you want to be using ResponseEntity.
return ResponseEntity.ok().build()
If you are using Spring MVC, you should remove any dependencies for Jersey/JAX-RS so you don't get confused as to what you can and cannot use.
Aside, #Produces is also for JAX-RS. For Spring MVC, you are supposed to add the produces inside the #RequestMapping annotation.
#RequestMapping(value="/", produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
I need to send a HTTP Post to a REST API with the following complex type as parameters. I looked at the documentation of jersey and it helps only to send a key value pair. How can i send a HTTP Post request with the below parameters using jersey.
{
"key": "example key",
"message": {
"html": "<p>Example HTML content</p>",
"text": "Example text content",
"subject": "example subject",
"from_email": "message.from_email#example.com",
"from_name": "Example Name",
"to": [
{
"email": "recipient.email#example.com",
"name": "Recipient Name"
}
],
"headers": {
"Reply-To": "message.reply#example.com"
},
"important": false,
"track_opens": null,
"track_clicks": null,
"auto_text": null,
"auto_html": null,
"inline_css": null,
"url_strip_qs": null,
"preserve_recipients": null,
"view_content_link": null,
"bcc_address": "message.bcc_address#example.com",
"tracking_domain": null,
"signing_domain": null,
"return_path_domain": null,
"merge": true,
"global_merge_vars": [
{
"name": "merge1",
"content": "merge1 content"
}
],
"merge_vars": [
{
"rcpt": "recipient.email#example.com",
"vars": [
{
"name": "merge2",
"content": "merge2 content"
}
]
}
],
"tags": [
"password-resets"
],
"subaccount": "customer-123",
"google_analytics_domains": [
"example.com"
],
"google_analytics_campaign": "message.from_email#example.com",
"metadata": {
"website": "www.example.com"
},
"recipient_metadata": [
{
"rcpt": "recipient.email#example.com",
"values": {
"user_id": 123456
}
}
],
"attachments": [
{
"type": "text/plain",
"name": "myfile.txt",
"content": "ZXhhbXBsZSBmaWxl"
}
],
"images": [
{
"type": "image/png",
"name": "IMAGECID",
"content": "ZXhhbXBsZSBmaWxl"
}
]
},
"async": false,
"ip_pool": "Main Pool",
"send_at": "example send_at"
}
I looked at the other questions of sending HTTP Post using Jersey and all I could find was a way to only send a key\value pairs as parameters and not complex string types like above.
You should look at JAXB, it allows you to "automatically" build "complex parameters" out of "objects". Basically the procedure would be to define a class that represents the data structure you present as request message the REST API resource accepts, then populate it with the data your want to POST and send it. In this question you can find more details on how to exactly do that: Can jersey clients POST a JAXB object to the server using JSON?