I have created and table in dynamodb (EU-WEST-1) and I have created a camel route to put a json payload into the table.
My route looks like this:
String dynamoDbEndpoint = String.format("aws-ddb://tablename?accessKey=%s&secretKey=RAW(%s)&tableName=tablename&amazonDdbEndpoint=dynamodb.eu-west-1.amazonaws.com",awsAccessKey,awsSecretKey);
from("direct:ipg-queue").to(dynamoDbEndpoint)
I get the error message:
com.amazonaws.AmazonServiceException: 1 validation error detected: Value null at 'item' failed to satisfy constraint: Member must not be null (Service: AmazonDynamoDB; Status Code: 400; Error Code: ValidationException; Request ID: 0AVSR54LRTOG3U0TTC5B1QO4KBVV4KQNSO5AEMVJF66Q9ASUAAJG)
An example payload is:
{"accountNumber":"123456789","customerName":"John Smith","id":"8422b9e0-739b-4d19-9291-037b68344068"}
Represented as a String.
I'm doing something stupid, but can't figure it out...
So it turns out I was being stupid. You just need to set the CamelAwdDbItem header in the exchange. I added the following process block to my route before the dynamo db endpoint and away I went...
.unmarshal()
.json(JsonLibrary.Gson, Map.class)
.process((Exchange exchange) -> {
Map body = (Map) exchange.getIn()
.getBody();
Map<String, AttributeValue> newBody = new HashMap();
for(Object key : body.keySet()) {
newBody.put(key.toString(), new AttributeValue(body.get(key).toString()));
}
exchange.getIn().setHeader("CamelAwsDdbItem", newBody);
})
I also put &operation=PutItem onto the end of the endpoint definition.
Related
I have a logic app that is taking failed runs from an app writing to application insights, and I want to group all the errors by the operation name into a single message. Can someone explain how to do this?
my starting data looks like:
[{ "messageError": "Notification sent to AppName but not received for request: 20200213215520_hUu22w9RZlyc, user email#email.com Status: NotFound",
"transactionKey": "20200213215520_hUu22w9RZlyc"},
{ "messageError": "App to App Import Request: 20200213215520_hUu22w9RZlyc from user email#email.com was unable to insert to following line(s) into App with error(s) :\r\n Line 123: Unable to unlock this record.",
"transactionKey": "20200213215520_hUu22w9RZlyc"}]
What I am trying to get out of that would be a single row that concatenates both messageError values into one statement on a common transaction key. Something like this:
[{ "messageErrors": [{"Notification sent to AppName but not received for request: 20200213215520_hUu22w9RZlyc, user email#email.com Status: NotFound"},
{"App to App Import Request: 20200213215520_hUu22w9RZlyc from user email#email.com was unable to insert to following line(s) into App with error(s) :\r\n Line 123: Unable to unlock this record."}],
"transactionKey": "20200213215520_hUu22w9RZlyc"}]
There might be as many as 20 rows in the dataset, and the concatenation needs to be smart enough to group only if there are multiple rows with the same transactionKey. Has anyone done this, and have a suggestion on how to group them?
For this requirement, I thought that we can use liquid template to do the "group by" operation for your json data at the beginning. But according to some test, it seems azure logic app doesn't support "group by" in its liquid template. So there two solutions for us to choose:
A. One solution is do these operations in logic app by "For each" loop, "If" condition, compose the json data and so many other actions, and also we have to initialize many variables. I tried this solution first, but I gave up it after creating so many actions in logic app. It's too complicated.
B. The other solution is call a azure function in logic app, and we can do the operations for the json data in function code. It's not easy either, but I think it's better than the first solution. So I tried this solution and got success. Please refer to the steps below:
1. We need to create a azure function app with a "HTTP" trigger in it.
2. In your "HTTP" trigger, please refer to my code below:
#r "Newtonsoft.Json"
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
public static async Task<object> Run(HttpRequestMessage req, TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
string body = await req.Content.ReadAsStringAsync();
JArray array = JArray.Parse(body);
JArray resultArray = new JArray();
JObject tempObj = new JObject();
foreach (var obj in array)
{
JObject jsonObj = JObject.Parse(obj.ToString());
string transactionKey = jsonObj.GetValue("transactionKey").ToString();
string messageError = jsonObj.GetValue("messageError").ToString();
Boolean hasKey = false;
foreach (var item in tempObj)
{
JObject jsonItem = (JObject)item.Value;
string keyInItem = jsonItem.GetValue("transactionKey").ToString();
if (transactionKey.Equals(keyInItem))
{
hasKey = true;
break;
}else
{
hasKey = false;
}
}
if (hasKey.Equals(false))
{
JObject newObj = new JObject();
JArray newArr = new JArray();
newArr.Add(messageError);
newObj.Add("transactionKey", transactionKey);
newObj.Add("messageErrors", newArr);
tempObj.Add(transactionKey, newObj);
}
else
{
JObject oldObj = (JObject)tempObj.GetValue(transactionKey);
JArray oldArr = (JArray)oldObj.GetValue("messageErrors");
oldArr.Add(messageError);
oldObj.Property("messageErrors").Remove();
oldObj.Add("messageErrors", oldArr);
tempObj.Property(transactionKey).Remove();
tempObj.Add(transactionKey, oldObj);
}
}
foreach (var x in tempObj)
{
resultArray.Add(x.Value);
}
return resultArray;
}
3. Test and save the function, and then go to your logic app. In logic app, I initialize a variable named "data" with the json data below to simulate your scene.
4. Then create function in your logic app and choose the "HTTP" trigger which you created just now.
5. After running the logic app, we can get the result shown as below:
[
{
"transactionKey": "20200213215520_hUu22w9RZlyc",
"messageErrors": [
"xxxxxxxxx",
"yyyyyyyy"
]
},
{
"transactionKey": "keykey",
"messageErrors": [
"testtest11",
"testtest22",
"testtest33"
]
}
]
I am trying to connect asp.net core web api(thats already connected to my local sqlserver and working fine) to my asp.net-mvc5 web app so when the controller is called it fetches the data from database using API
Now, this is how I'm trying to connect the api in my asp.net mvc project, shown below is homeController:
namespace AptitudeTest.Controllers
{
public class HomeController : Controller
{
string Baseurl = "http://localhost:51448";
public async Task<ActionResult> Index()
{
List<Teacher> teacher = new List<Teacher>();
//Teacher t1 = new Teacher();
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(Baseurl);
client.DefaultRequestHeaders.Clear();
//Define request data format
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
//Sending request to find web api REST service resource GetAllEmployees using HttpClient
HttpResponseMessage Res = await client.GetAsync("api/Admin/Get");
//Checking the response is successful or not which is sent using HttpClient
if (Res.IsSuccessStatusCode)
{
//Storing the response details recieved from web api
var EmpResponse = Res.Content.ReadAsStringAsync().Result;
//Deserializing the response recieved from web api and storing into the Employee list
teacher = JsonConvert.DeserializeObject<List<Teacher>>(EmpResponse);
}
//returning the employee list to view
return View(teacher);
}
}
I expect it to return me teachers as json objects but this is the error im getting:
The model item passed into the dictionary is of type 'System.Collections.Generic.List1[AptitudeTest.Teacher]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable1[AptitudeTest.Models.Teacher]'.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: The model item passed into the dictionary is of type 'System.Collections.Generic.List1[AptitudeTest.Teacher]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable1[AptitudeTest.Models.Teacher]'.
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
The error you are getting is:
The model item passed into the dictionary is of type 'System.Collections.Generic.List1[AptitudeTest.Teacher]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable1[AptitudeTest.Models.Teacher]'.
which means your view expects different model object as input while the passed object is of different type. It looks like that you have in view defined model as:
IEnumerable<Teacher>
while you are returning a List<Teacher>. Either change in view it to List<Teacher> like:
#model List<Teacher>
or return an IEnumerable<Teacher> object from the controller action.
for that you can write the following:
IEnumerable<Teacher> teachers = teacher;
return View(teachers );
Either way you should be able to resolve the error and keep moving.
EDIT:
didn't noticed that there are two different classes in different namespace, one is AptitudeTest.Teacher and other is AptitudeTest.Models.Teacher. the class which we have defined the view should be same which is passed from the action method. Right now there are two classes each in different namespace.
I have an angular js application, and when trying to issue the following post request :
$resource('api/'+API_VERSION+'/projects/:projectId/users/:userId')
.save(
{
projectId:$scope.project.id,
userId:id
},
{}
,function(){
// Handle callback
});
I get a 400 bad request error.
the request is handled by a spring RestController and the method looks like the following :
#RequestMapping(value = "/projects/{projectId}/users/{userID}",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
#RolesAllowed(AuthoritiesConstants.USER)
void addUsers(#PathVariable Long projectId, #PathVariable Long userId) {
log.debug("REST request to add admin to project");
projectService.addUser(projectId, userId);
}
I Checked the request that is been sent to the server, and nothing bad strikes me.
The url is correct (all parameter are of valid type), and the content type is set to Application json.
Thanks in advance for any help.
Your API consumes JSON and returns void, so I think you should have consumes = MediaType.APPLICAT_JSON_VALUE in your #RequestMapping.
[Edit]:
Apart from the consumes annotation everything is fine with your back-end. Can you try making your post request with the following code :
$resource('api/'+API_VERSION+'/projects/:projectId/users/:userId',
{projectId: $scope.project.id, userId: id}).$save();
or again, creating an instance of the resource :
var Project = $resource('api/'+API_VERSION+'/projects/:projectId/users/:userId',
{projectId: $scope.project.id, userId: id});
var newProject = new Project();
newProject.$save();
And let me know if it worked ?
I am trying to create a RESTish service using grails. I have the following...
def delete(Question q){
def text = request.reader.text;
def slurper = new JsonSlurper();
def result = slurper.parseText(text)
println "Request body is ${text} but the parsed version has a text of ${q.text} whereas the slurper gives me ${result as JSON}"
render noteService.delete(result.key)
}
This gives me an output of...
Request body is {"text":"Test Text","desc":"Test Desc","voteCount":0,"key":0} but the parsed version has a text of null whereas the slurper gives me {"desc":"Test Desc","key":0,"text":"Test Text","voteCount":0}
Why is this not wiring properly? The command object looks as follows...
#Validateable
class Question {
Integer key
String text
String desc
Integer voteCount
}
Is the delete request a GET under the hood or something? Is it expecting some other format?
Update
The create (POST) request is wiring fine which leads me to believe it is something with the differance between the Restangular call and what grails is expecting (So I think my request type guess might be right). My restangular code is simply...
this.delete = function(index) {
var questionToUpdate = _this.questions[index];
questionToUpdate.remove();
}
Also appears to fail with update (put) as well
Grails version is 2.4.3
Problem was that request.reader.text turns of the parsing of command objects.
I have the following routes:
/projects/{projectName}
and
/projects/{projectName}/Wall/{wallName}
Now I'd like to have that all GETs be allowed but PUT, POST, DELETE should only be allowed by project members i.e. users members of that project. I have a special class that given a user id and project name I can get the status of the user's membership - something like MyEnroler.getRole(userId, projectName) - where the userId is part of the request header and the projectName is taken from the URI.
I've tried a number of things but doesn't work. Here's the idea:
public class RoleMethodAuthorizer extends Authorizer {
#Override
protected boolean authorize(Request req, Response resp) {
//If it's a get request then no need for further authorization.
if(req.getMethod().equals(Method.GET))
return true;
else
{
String authorEmail = req.getClientInfo().getUser().getIdentifier();
String projectName = req.getAttributes().get("project").toString();
Role userRole = MyEnroler.getRole(authorEmail, projectName);
//forbid updates to resources if done by non-members of project
if(userRole.equals(MyEnroler.NON_MEMBER))
return false;
//for everybody else, return true
return true;
}
}
}
Now simply doing the following completely fails when creating inbound root in the Application:
Router projectRouter = new Router(getContext());
RoleMethodAuthorizer rma = new RoleMethodAuthorizer();
//Guard declaration here. Then setNext Restlet
guard.setNext(projectRouter);
projectRouter.attach("/projects/{project}",rma);
Router wallRouter = new Router(getContext());
wallRouter.attach("/Wall/{wallName}", WallResource.class);
rma.setNext(wallRouter);
//return guard;
So a request to /projects/stackoverflow/Wall/restlet fails. The URL is never found. I'm guessing since it's trying to match it with the projectRouter. Well I tried the various modes (MODE_BEST_MATCH or MODE_FIRST/NEXT_MATCH) to no avail.
Nothing seems to work. Conceptually this should work. I'm only intercepting a call and just being transparent to the request, but don't know how things are working on the inside.
I could move the authorizer just after the guard, but I'd lose access to the request attribute of projectName - I don't wish to parse the URL myself to search for the projectName since the URL pattern could change and would break the functionality - i.e. require 2 changes instead of one.
Any ideas how to achieve this?
I would use the standard RoleAuthorizer class to supply the list of allowed roles, along with your custom enroller probably split into two I would then add a custom Filter class that does something like this to call your Enrolers.
protected int beforeHandle(final Request request, final Response response) throws ResourceException {
final String projectName = (String) request.getAttributes().get("projectName");
// Check that a projectName is supplied, should not have got this far otherwise but lets check.
if (projectName == null || projectName.isEmpty()) {
throw new ResourceException(Status.CLIENT_ERROR_NOT_FOUND);
}
if (Method.GET.equals(request.getMethod())){
new ReadEnroler(projectName).enrole(request.getClientInfo());
}else{
new MutateEnroler(projectName).enrole(request.getClientInfo());
}
return super.beforeHandle(request, response);
}
the enrolers would then set the appropriate values in the clientInfo.getRoles() Collection when enrole was called.