Im currently working with policys in Azure API Management and Im interesting in extracting a value that gets returned from the response-body.
<send-one-way-request mode="new">
<set-url>http://requestb.in/xje199xj</set-url>
<set-method>POST</set-method>
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>#{
//JObject or string?
string response = context.Response.Body.As<string>(preserveContent: true);
//something here..
}
</set-body>
</send-one-way-request>
The response looks like this :
"getBookableResourcesResponse": {
"getBookableResourcesResult": {
"hasError": false,
"errorCode": 0,
"BookableResource": [
{
"resourceCode": "TRA",
"description": "Trailer",
"group": "F",
"subGroup": "C",
"category": "R",
"dialogType": "CARGO",
"orgCode": "DECK",
"length": 14,
"taraWeight": "7000",
"grossWeight": "25001",
"AddResource": [
{
"resourceCode": "EXPFIN",
"description": "Export Finland",
"dialogType": "UNDEFINED",
"amount": "0",
"ticketType": "",
"orgCode": "EXPFIN",
"required": "false"
}.....`
I want to get the value thats being returned from the "resourceCode" attribute, in this case "TRA" and then create a new JObject wich I can send to my Azure function.
{
"resourceCode": "valueFromResponseBody"
}
Instead of
string response = context.Response.Body.As<string>(preserveContent: true);
try:
var response = context.Response.Body.As<JObject>(preserveContent: true);
That would return Json.NET JObject (http://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_Linq_JObject.htm) which you later can use to navigate your response.
My solution:
<send-one-way-request mode="new">
<set-url></set-url>
<set-method>POST</set-method>
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>#{
string xml = context.Response.Body.As<string>(preserveContent: true);
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
XmlNodeList elemList = doc.DocumentElement.GetElementsByTagName("ResourceCode");
string allResources = string.Empty;
foreach (XmlNode item in elemList)
{
if (item.InnerText == "TRA" || item.InnerText == "CONT20" || item.InnerText == "CONT23" || item.InnerText == "CONT26" || item.InnerText == "CONT30" || item.InnerText == "CONT40" || item.InnerText == "CONT45" || item.InnerText == "TRUCK"|| item.InnerText == "VAN" || item.InnerText == "CAMP" || item.InnerText == "CAR")
{
allResources += item.InnerText + ",";
}
}
allResources = allResources.Substring(0, allResources.Length - 1);
return new JObject(new JProperty("resourceCode", allResources)).ToString();
}</set-body>
</send-one-way-request>
Ran into the same issue. The typical JsonConvert.SerializeXmlNode method isn't allowed in policy expressions.
However good ol' JsonConvert.SerializeObject did the trick.
<send-one-way-request mode="new">
<set-url>http://requestb.in/xje199xj</set-url>
<set-method>POST</set-method>
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>#{
string xml = context.Response.Body.As<string>(preserveContent: true);
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
return JsonConvert.SerializeObject(doc);
}
</set-body>
</send-one-way-request>
Related
I have the following request data
{
"id": "1115e297-12de-4189-8e11-75a8354f77d6",
"type": "transaction",
"version": "1.0.0",
"data": {
"account_id": "999",
"customer_info": {
"client_id": 999
},
"company_id": "999999",
"event": {
"id": 99999999,
"type_code": "3098"
}
}
}
I want to write a policy that ignores the request unless the type is "transaction" and the type_code is "3098" or "3099"
If this is not satisfied then it should just return the id of the request to the caller and not forward on the request
Here is what I have come up with
<choose>
<when condition="#((string)context.Request.Body?.As<JObject>(true)["type"] != "transaction")">
<return-response>
<set-status code="200" reason="OK" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>#{
var json = new JObject(
new JProperty("id", context.Request.Body?.As<JObject>(true)["id"])
);
return json.ToString();
}</set-body>
</return-response>
</when>
<when condition="#(context.Request.Body?.As<JObject>(true)["data"] == null || context.Request.Body?.As<JObject>(true)["data"]["event"] == null)">
<return-response>
<set-status code="200" reason="OK" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>#{
var json = new JObject(
new JProperty("id", context.Request.Body?.As<JObject>(true)["id"])
);
return json.ToString();
}</set-body>
</return-response>
</when>
<when condition="#((int)context.Request.Body?.As<JObject>(true)["data"]["event"]["type_code"] != 3098 && (int)context.Request.Body?.As<JObject>(true)["data"]["event"]["type_code"] != 3099)">
<return-response>
<set-status code="200" reason="OK" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>#{
var json = new JObject(
new JProperty("id", context.Request.Body?.As<JObject>(true)["id"])
);
return json.ToString();
}</set-body>
</return-response>
</when>
</choose>
I'm tipping there is a much more elegant way to do this?
Sure. Provided all you return-response policies are the same you can use multiline expression:
<set-variable name="payload" value="#((string)context.Request.Body?.As<JObject>(true))" />
<choose>
<when condition="#{
var payload = (JObject)context.Variables["payload"];
if (payload?["type"] != "transaction"
|| payload?["data"]?["event"]?["type_code"] != 3099)
{
return true;
}
}">
<return-response>
<set-status code="200" reason="OK" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>#{
var payload = (JObject)context.Variables["payload"];
var json = new JObject(
new JProperty("id", payload?["id"])
);
return json.ToString();
}</set-body>
</return-response>
</when>
</choose>
I'm simply trying to execute the standard example bulkImport sproc for documentDB API and I can't seem to pass it an array of objects. I always get 400 errors despite the documentation giving clear direction to send an array of objects
.. very frustrating.
Additional details: Even if I wrap the array in an object with the array under a property of 'items' and include it in my sproc it still errors out saying the same bad request, needs to be an object or JSON-serialized. When I try to do JSON.stringify(docs) before sending it fails to parse on the other side.
Bad Request: The document body must be an object or a string representing a JSON-serialized object.
bulkInsert.js:
https://github.com/Azure/azure-documentdb-js-server/blob/master/samples/stored-procedures/BulkImport.js
My Code (using documentdb-util for async):
execProc(docs, insertProc);
async function execProc(docs, insertProc){
let database = await dbUtil.database('test');
let collection = await dbUtil.collection(database, 'test');
let procInstance = await dbUtil.storedProcedure(collection, insertProc);
try{
let result = await dbUtil.executeStoredProcedure(procInstance, docs);
console.log(result);
} catch(e){
console.log(e.body)
}
}
Header
Object {Cache-Control: "no-cache", x-ms-version: "2017-11-15",
User-Agent: "win32/10.0.16299 Nodejs/v8.9.0 documentdb-nodejs-s…",
x-ms-date: "Mon, 11 Dec 2017 07:32:29 GMT",
Accept:"application/json"
authorization: myauth
Cache-Control:"no-cache"
Content-Type:"application/json"
User-Agent:"win32/10.0.16299 Nodejs/v8.9.0 documentdb-nodejs-sdk/1.14.1"
x-ms-date:"Mon, 11 Dec 2017 07:32:29 GMT"
x-ms-version:"2017-11-15"
Path
"/dbs/myDB/colls/myColl/sprocs/myBulkInsert"
Params
Array(3) [Object, Object, Object]
length:3
0:Object {id: "0001", type: "donut", name: "Cake", …}
1:Object {id: "0002", type: "donut", name: "Raised", …}
2:Object {id: "0003", type: "donut", name: "Old Fashioned", …}
[{
"id": "0001",
"type": "donut",
"name": "Cake",
"ppu": 0.55
},
{
"id": "0002",
"type": "donut",
"name": "Raised",
"ppu": 0.35
},
{
"id": "0003",
"type": "donut",
"name": "Old Fashioned",
"ppu": 0.25
}]
The "docs" must be an array of array of params, otherwise, the procedure executor will treat them as multiple params of the procedure, not a single-array-param.
the following code works when call storedProcedure to pass argument with array type.
JS:
var docs = [{'id':1},{'id':2}];
executeStoredProcedure(proc, [docs])
C#
var docs = new[] {new MyDoc{id=1, source="abc"}, new MyDoc{id=2, source="abc"}];
dynamic[] args = new dynamic[] {docs};
ExecuteStoredProcedureAsync<int>(
procLink,
new RequestOptions {PartitionKey = new PartitionKey("abc")},
args);
NOTE: you must ensure the 'docs' have the same partition key, and pass partion key in RequestionOptions
I had the same problem. I was able to get it to work by Stringify the Array and parse it in the stored procedure. I opened an issue on the github where that code originated as well. Below is what worked for me. Good luck.
---- Stringify Array
var testArr = []
for (var i = 0; i < 50; i++) {
testArr.push({
"id": "test" + i
})
}
var testArrStr = JSON.stringify(testArr)
//pass testArrStr to stored procedure and parse in stored procedure
---- Slightly altered original BulkImport
exports.storedProcedure = {
id: "bulkImportArray",
serverScript:function bulkImportArray(docs) {
var context = getContext();
var collection = context.getCollection();
var docsToCreate = JSON.parse(docs)
var count = 0;
var docsLength = docsToCreate.length;
if (docsLength == 0) {
getContext().getResponse().setBody(0);
}
var totals = ""
function insertDoc(){
var msg = " count=" + count+" docsLength=" +docsLength + " typeof docsToCreate[]=" + typeof docsToCreate+ " length =" + docsToCreate.length
if(typeof docsToCreate[count] != 'undefined' ) {
collection.createDocument(collection.getSelfLink(),
docsToCreate[count],
function (err, documentCreated) {
if (err){
// throw new Error('Error' + err.message);
getContext().getResponse().setBody(count + " : " + err);
}else{
if (count < docsLength -1) {
count++;
insertDoc();
getContext().getResponse().setBody(msg);
} else {
getContext().getResponse().setBody(msg);
}
}
});
}else{
getContext().getResponse().setBody(msg);
}
}
insertDoc()
}
}
If you want to test it in the portal Script Explorer I had to create an escaped string i.e.
var testArr = []
for(var i=200; i<250; i++){
testArr.push({"id":"test"+i})
}
var testArrStr = JSON.stringify(testArr)
console.log('"'+testArrStr.replace(/\"/g,'\\"') + '"')
I have created a WEB API using MySQL DB. The API is working well. The code is below
try
{
var before = dt.AddMinutes(-5);
var after = dt.AddMinutes(5);
var result = medEntitites.tj_xhqd
.Where(m =>
m.zdjh == msn &&
m.sjsj >= before &&
m.sjsj <= after).Select(m => new { MSN = m.zdjh, DateTime = m.sjsj, Signal_Strength = m.xhqd }).Distinct();
return Request.CreateResponse(HttpStatusCode.OK, new { data = result });
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.NotFound, ex);
}
and the response is below
{
"data": [
{
"MSN": "002999000076",
"DateTime": "2017-10-11T10:16:48",
"Signal_Strength": "15"
},
{
"MSN": "002999000076",
"DateTime": "2017-10-11T10:19:02",
"Signal_Strength": "15"
},
{
"MSN": "002999000076",
"DateTime": "2017-10-11T10:20:58",
"Signal_Strength": "16"
},
{
"MSN": "002999000076",
"DateTime": "2017-10-11T10:22:54",
"Signal_Strength": "15"
}
]}
How's it working?
I am passing a meter serial number and a date time. The API receives both of them. +- The minutes in time and display all the records withing that time.
Now I want is that, If at a particular time there exist any record then the API will send me YES in response and if no record is found then it will send me NO.
The sample response should be
{
"data": [
{
"Response": "YES"/"NO"
}
]
}
I have searched a solution for it but couldn't able to find one
Any help would be highly appreciated.
Create the below class
public class ResponseChecker
{
public string Response{get; set;};
public ResponseChecker(IEnumerable result)
{
if(result.ToList().Count > 0)
Response = "YES";
else
Response = "NO";
}
}
Modify your code as below
try
{
var before = dt.AddMinutes(-5);
var after = dt.AddMinutes(5);
var result = medEntitites.tj_xhqd
.Where(m =>
m.zdjh == msn &&
m.sjsj >= before &&
m.sjsj <= after).Select(m => new { MSN = m.zdjh, DateTime = m.sjsj, Signal_Strength = m.xhqd }).Distinct();
return Request.CreateResponse(HttpStatusCode.OK, new { data = new ResponseChecker(result) });
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.NotFound, ex);
}
Something like this should work:
var before = dt.AddMinutes(-5);
var after = dt.AddMinutes(5);
bool anyRecords = medEntitites.tj_xhqd.Any(m =>
m.zdjh == msn &&
m.sjsj >= before &&
m.sjsj <= after);
return Request.CreateResponse(HttpStatusCode.OK, new
{
data = new[]
{
new { Response = anyRecords ? "YES" : "NO" }
}
});
The response seems a little excessive to me. Why do you need a single-element array of objects just to return a single flag?
I'm using AWS Lambda to scan data from a DynamoDB table. This is what I get in return:
{
"videos": [
{
"file": {
"S": "file1.mp4"
},
"id": {
"S": "1"
},
"canvas": {
"S": "This is Canvas1"
}
},
{
"file": {
"S": "main.mp4"
},
"id": {
"S": "0"
},
"canvas": {
"S": "this is a canvas"
}
}
]
}
My front-end application is using Ember Data Rest Adapter which does not accepts such response. Is there any way I can get normal JSON format? There is this NPM module called dynamodb-marshaler to convert DynamoDB data to normal JSON. I'm looking for a native solution if possible.
Node.js
Use the unmarshall function from AWSJavaScriptSDK:
const AWS = require("aws-sdk");
exports.handler = function( event, context, callback ) {
const newImages = event.Records.map(
(record) => AWS.DynamoDB.Converter.unmarshall(record.dynamodb.NewImage)
);
console.log('Converted records', newImages);
callback(null, `Success`);
}
Python
Use TypeDeserializer.deserialize from boto3.dynamodb.types:
import json
from boto3.dynamodb.types import TypeDeserializer
def ddb_deserialize(r, type_deserializer = TypeDeserializer()):
return type_deserializer.deserialize({"M": r})
def lambda_handler(event, context):
new_images = [ ddb_deserialize(r["dynamodb"]["NewImage"]) for r in event['Records'] ]
print('Converted records', json.dumps(new_images, indent=2))
I know is a bit old but I had the same problem processing stream data from dynamoDB in node js lambda function. I used the proposed by #churro
import sdk and output converter
var AWS = require("aws-sdk");
var parse = AWS.DynamoDB.Converter.output;
use the parse function with a small hack
exports.handler = function( event, context, callback ) {
var docClient = new AWS.DynamoDB.DocumentClient();
event.Records.forEach((record) => {
console.log(record.eventID);
console.log(record.eventName);
console.log('DynamoDB Record:', parse({ "M": record.dynamodb.NewImage }));
});
callback(null, `Successfully processed ${event.Records.length} records.`);
}
Hope it helps
AWS JavaScript SDK was recently updated with Document Client which does exactly what you need. Check the announce and usage examples here: http://blogs.aws.amazon.com/javascript/post/Tx1OVH5LUZAFC6T/Announcing-the-Amazon-DynamoDB-Document-Client-in-the-AWS-SDK-for-JavaScript
Javascript: AWS SDK provides the unmarshall function
Python: use TypeDeserializer from boto3.dynamodb.types:
from boto3.dynamodb.types import TypeDeserializer, TypeSerializer
def from_dynamodb_to_json(item):
d = TypeDeserializer()
return {k: d.deserialize(value=v) for k, v in item.items()}
## Usage:
from_dynamodb_to_json({
"Day": {"S": "Monday"},
"mylist": {"L": [{"S": "Cookies"}, {"S": "Coffee"}, {"N": "3.14159"}]}
})
# {'Day': 'Monday', 'mylist': ['Cookies', 'Coffee', Decimal('3.14159')]}
Here you can find gist which does that:
function mapper(data) {
let S = "S";
let SS = "SS";
let NN = "NN";
let NS = "NS";
let BS = "BS";
let BB = "BB";
let N = "N";
let BOOL = "BOOL";
let NULL = "NULL";
let M = "M";
let L = "L";
if (isObject(data)) {
let keys = Object.keys(data);
while (keys.length) {
let key = keys.shift();
let types = data[key];
if (isObject(types) && types.hasOwnProperty(S)) {
data[key] = types[S];
} else if (isObject(types) && types.hasOwnProperty(N)) {
data[key] = parseFloat(types[N]);
} else if (isObject(types) && types.hasOwnProperty(BOOL)) {
data[key] = types[BOOL];
} else if (isObject(types) && types.hasOwnProperty(NULL)) {
data[key] = null;
} else if (isObject(types) && types.hasOwnProperty(M)) {
data[key] = mapper(types[M]);
} else if (isObject(types) && types.hasOwnProperty(L)) {
data[key] = mapper(types[L]);
} else if (isObject(types) && types.hasOwnProperty(SS)) {
data[key] = types[SS];
} else if (isObject(types) && types.hasOwnProperty(NN)) {
data[key] = types[NN];
} else if (isObject(types) && types.hasOwnProperty(BB)) {
data[key] = types[BB];
} else if (isObject(types) && types.hasOwnProperty(NS)) {
data[key] = types[NS];
} else if (isObject(types) && types.hasOwnProperty(BS)) {
data[key] = types[BS];
}
}
}
return data;
function isObject(value) {
return typeof value === "object" && value !== null;
}
}
https://gist.github.com/igorzg/c80c0de4ad5c4028cb26cfec415cc600
If you are using python in the lambda you can utilise the dynamodb-json library.
Install library
pip install dynamodb-json
and use the below snippet
from dynamodb_json import json_util as util
def marshall(regular_json):
dynamodb_json = util.dumps(reular_json)
def unmarshall(dynamodb_json):
regular_json = util.loads(dynamodb_json)
Reference
https://pypi.org/project/dynamodb-json/
I think it's just a custom transformation exercise for each app. A simple conversion from DynamoDB's item format to you application format might look like this:
var response = {...} // your response from DynamoDB
var formattedObjects = response.videos.map(function(video) {
return {
"file": video.file.S,
"id": video.id.S,
"canvas": video.canvas.S
};
});
If you want to build a generic system for this, you would have to handle DynamoDB's various AttributeValue types. A function like the one below would do the job, but I've left out the hard work of handling most of DynamoDB's more complex attribute value types:
function dynamoItemToPlainObj(dynamoItem) {
var plainObj = {};
for (var attributeName in dynamoItem) {
var attribute = dynamoItem[attributeName];
var attributeValue;
for (var itemType in attribute) {
switch (itemType) {
case "S":
attributeValue = attribute.S.toString();
break;
case "N":
attributeValue = Number(attribute.N);
break;
// more attribute types...
default:
attributeValue = attribute[itemType].toString();
break;
}
}
plainObj[attributeName] = attributeValue;
}
return plainObj;
}
var formattedObjects = response.videos.map(dynamoItemToPlainObj);
I tried several solutions here but none worked with multi-level data, such as if it includes a list of maps e.g.
{
"item1": {
"M": {
"sub-item1": {
"L": [
{
"M": {
"sub-item1-list-map": {
"S": "value"
Below, adapted from #igorzg's answer (which also has that drawback), fixes that.
Example usage:
dynamodb.getItem({...}, function(err, data) {
if (!err && data && data.Item) {
var converted = ddb_to_json(data.Item);
Here's the conversion function:
function ddb_to_json(data) {
function isObject(value) {
return typeof value === "object" && value !== null;
}
if(isObject(data))
return convert_ddb({M:data});
function convert_ddb(ddbData) {
if (isObject(ddbData) && ddbData.hasOwnProperty('S'))
return ddbData.S;
if (isObject(ddbData) && ddbData.hasOwnProperty('N'))
return parseFloat(ddbData.N);
if (isObject(ddbData) && ddbData.hasOwnProperty('BOOL'))
return ddbData.BOOL;
if (isObject(ddbData) && ddbData.hasOwnProperty('NULL'))
return null;
if (isObject(ddbData) && ddbData.hasOwnProperty('M')) {
var x = {};
for(var k in ddbData.M)
x[k] = convert_ddb(ddbData.M[k])
return x;
}
if (isObject(ddbData) && ddbData.hasOwnProperty('L'))
return ddbData.L.map(x => convert_ddb(x));
if (isObject(ddbData) && ddbData.hasOwnProperty('SS'))
return ddbData.SS;
if (isObject(ddbData) && ddbData.hasOwnProperty('NN'))
return ddbData.NN;
if (isObject(ddbData) && ddbData.hasOwnProperty('BB'))
return ddbData.BB;
if (isObject(ddbData) && ddbData.hasOwnProperty('NS'))
return ddbData.NS;
if (isObject(ddbData) && ddbData.hasOwnProperty('BS'))
return ddbData.BS;
return data;
}
return data;
}
If you need online editor try this
https://2json.net/dynamo
This is my application code for sending push message using PARSE
public static string ParseAuthenticate(string strUserName, string
{
var httpWebRequest = (HttpWebRequest)WebRequest.Create("https://api.parse.com/1/push");
httpWebRequest.ContentType = "application/json";
httpWebRequest.Headers.Add("X-Parse-Application-Id", "my app id");
httpWebRequest.Headers.Add("X-Parse-REST-API-KEY", "my rest api key");
httpWebRequest.Method = "POST";
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var responseText = streamReader.ReadToEnd();
return responseText;
}
}
Request body
{
"channels": [
"test"
],
"data": {
"alert": "12345"
}
}
Above code where is pass my request parameter(body)? how to frame my request as JSON format?
Thanks in advance.Please help me to solve this issue.
Bellow code is running for push notification using parse in .net.
private bool PushNotification(string pushMessage)
{
bool isPushMessageSend = false;
string postString = "";
string urlpath = "https://api.parse.com/1/push";
var httpWebRequest = (HttpWebRequest)WebRequest.Create(urlpath);
postString = "{ \"channels\": [ \"Trials\" ], " +
"\"data\" : {\"alert\":\"" + pushMessage + "\"}" +
"}";
httpWebRequest.ContentType = "application/json";
httpWebRequest.ContentLength = postString.Length;
httpWebRequest.Headers.Add("X-Parse-Application-Id", "My Parse App Id");
httpWebRequest.Headers.Add("X-Parse-REST-API-KEY", "My Rest API Key");
httpWebRequest.Method = "POST";
StreamWriter requestWriter = new StreamWriter(httpWebRequest.GetRequestStream());
requestWriter.Write(postString);
requestWriter.Close();
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var responseText = streamReader.ReadToEnd();
JObject jObjRes = JObject.Parse(responseText);
if (Convert.ToString(jObjRes).IndexOf("true") != -1)
{
isPushMessageSend = true;
}
}
return isPushMessageSend;
}
To send Notification to all app user you have to set the
Data field like so:
postString = "{\"data\": { \"alert\": \"Test Notification 2 From Parse Via Chinwag Admin\" },\"where\": { \"deviceType\": \"ios\" }}";