API Gateway exposing DynamoDB (Complex mapping template or Lambda Function needed?) - api

I am exposing my DDB via the AWS API Gateway. I have it working, but when I pass my "PUT" it's requiring me to include every field from my DDB in the body request. Some clients may only want to POST 5 fields of the 10 in my DDB. Would this be done as a POST or a PATCH? If so, what does my mapping template look like for these?
Second: I'd like to add an auto-generated order_number to each entry in the DDB. So now each PUT or POST will have a unique order_number. So my Primary Key will be a variable, and the secondary key would be a customer sku number, or vice versa.
Thanks for the help.

Body Template: I still need it to generate the "order_number"
automatically for each new entry.
#set($inputSep = $input.path('$'))
#set($thisLoop =
'{"CustomerNm":"1","ShipDate":"2","Shipping":"3","commentId":"4"}')
#set($myPlaces = $util.parseJson($thisLoop))
#set($allKeys = '')
#set($myVals = '"')
{
"TableName": "BRPI",
"Key": {
"CustomerID": {
"S": "$input.path('$.CustomerID')"
}
},
#foreach($type in $inputSep.keySet())
#if($type == "CustomerID")
#else
#set($thisPlace = $myPlaces.get($type))
#set($params = $inputSep.get($type))
#set($thisKey = "$type" + " = :val" + "$thisPlace")
#set($thisPath = "$params")
#if($foreach.hasNext)
#set($myComma = ",")
#set($myReturn = '"')
#else
#set($myComma = "")
#set($myReturn = "")
#end
#set($allKeys = "$allKeys" + "$thisKey" + "$myComma")
#set($myVals = "$myVals" + ":val" + "$thisPlace" + '": {"S": "' +
"$thisPath" + '"}' + "$myComma" + "$myReturn")
#end
#end
"UpdateExpression": "SET $allKeys",
"ExpressionAttributeValues": {
$myVals
},
"ReturnValues":"UPDATED_NEW"
}

Related

API call from google sheets is too long

I am trying to write a script in google sheets to update my 3commas bots. The API requires a number of mandatory fields are passed even when there's only 1 item that needs to be updated.
The code I have is below and it uses the values already read from the platform updating only the base_order_volume value. This works perfectly except for when the pairs value is long (more the 2k chars) and then I get an error from the UrlFetchApp call because the URL is too long.
var sheet = SpreadsheetApp.getActiveSheet();
var key = sheet.getRange('F4').getValue();
var secret = sheet.getRange('F5').getValue();
var baseUrl = "https://3commas.io";
var editBots = "/ver1/bots/"+bots[botCounter].id+"/update";
var patchEndPoint = "/public/api"+editBots+"?";
.
.
[loop around values in sheet]
.
.
var BaseOrder=Number(sheet.getRange(rowCounter,12).getValue().toFixed(2));
var botParams = {
"name": bots[botCounter].name,
"pairs": bots[botCounter].pairs,
"max_active_deals": bots[botCounter].max_active_deals,
"base_order_volume": BaseOrder,
"take_profit": Number(bots[botCounter].take_profit),
"safety_order_volume": bots[botCounter].safety_order_volume,
"martingale_volume_coefficient": bots[botCounter].martingale_volume_coefficient,
"martingale_step_coefficient": Number(bots[botCounter].martingale_step_coefficient),
"max_safety_orders": bots[botCounter].max_safety_orders,
"active_safety_orders_count": Number(bots[botCounter].active_safety_orders_count),
"safety_order_step_percentage": Number(bots[botCounter].safety_order_step_percentage),
"take_profit_type": bots[botCounter].take_profit_type,
"strategy_list": bots[botCounter].strategy_list,
"bot_id": bots[botCounter].id
};
var keys = Object.keys(botParams);
var totalParams = keys.reduce(function(q, e, i) {
q += e + "=" + encodeURIComponent(JSON.stringify(botParams[e])) + (i != keys.length - 1 ? "&" : "");
return q;
},endPoint);
var signature = Utilities.computeHmacSha256Signature(totalParams, secret);
signature = signature.map(function(e) {return ("0" + (e < 0 ? e + 256 : e).toString(16)).slice(-2)}).join("");
var headers = {
'APIKEY': key,
'Signature': signature,
};
try {
var params = {
'method': 'PATCH',
'headers': headers,
'muteHttpExceptions': true
};
var response = JSON.parse(UrlFetchApp.fetch(baseUrl + totalParams, params).getContentText());
I have tried to set the botParams as a payload in the params but when I do the signature is incorrect.
I anyone knows how to use sheets to make a call using extensive length of parameters I'd appreciate any help at all
Some sample data for the bots array would be
{
"name": "TestBot",
"base_order_volume": 0.001,
"take_profit": 1.5,
"safety_order_volume": 0.001,
"martingale_volume_coefficient": 2,
"martingale_step_coefficient": 1,
"max_safety_orders": 1,
"active_safety_orders_count": 1,
"safety_order_step_percentage": 2.5,
"take_profit_type": "total",
"stop_loss_percentage": 0,
"cooldown": 0,
"pairs": ["BTC_ADA","BTC_TRX"],
"trailing_enabled":"true",
"trailing_deviation":0.5,
"strategy_list": [{"strategy":"cqs_telegram"}]
}
Thanks in advance
I'd consider using a Cloud Function to either do the heavy lifting, or, if you're worried about costs, use it as a proxy. You can then call the cloud function from Google Sheets. Cloud Functions can be written in whatever language you're most comfortable with, including Node.
Check the GCP pricing calculator to see what the cost would be. For many cases it would be completely free.
This should give you a sense of how to use cloud functions for CSV creation:
https://codelabs.developers.google.com/codelabs/cloud-function2sheet#0
Here is a SO question with an answer that explains how to query cloud functions with authentication.

getting "415:Media not supported" Error when passing pdf to IBM watson in Salesforce

I am planning to integrate the IBM Watson Document Conversion service
with Salesforce.
From there I am unable to send my pdf file directly to Watson and I'm getting Media Type not supported.
I am also getting this error:
{
"code" : 500 ,
"error" : "Server Error" ,
"description" : "2017-07-18T06:02:19-04:00, Error WATSNGWERR-0x0113001c occurred when accessing https://gateway.watsonplatform.net/document-conversion/api/v1/convert_document?version=2015-12-15&config="{"conversion_target":"answer_units"}", Tran-Id: gateway-dp02-1967135880 - Watson Gateway Error"
}
Here is the code I'm using:
public class Resume {
String boundary = '----------------------------741e90d31eff';
public string id{get;set;}
public string content{get;set;}
Transient public Attachment att{set;get;}
public Resume(ApexPages.StandardController controller) {
id=ApexPages.currentPage().getParameters().get('id');
att=new Attachment();
att=[Select Id,ParentId, Name,body,ContentType From Attachment where ParentId=:id limit 1];
content=String.valueOf(att.body);
System.debug('---->' + content);
String header = '--' + boundary + '\nContent-Disposition: form-data; name="att"; filename="'+att.name+'";\nContent-Type: application/pdf';
String footer = '--' + boundary + '--';
String headerEncoded =
EncodingUtil.base64Encode(Blob.valueOf(header+'\r\n\r\n'));
String bodyEncoded = EncodingUtil.base64Encode(att.body);
Blob bodyBlob = null;
String last4Bytes =
bodyEncoded.substring(bodyEncoded.length() - 4, bodyEncoded.length());
while (headerEncoded.endsWith('=')){
header+=' ';
headerEncoded = EncodingUtil.base64Encode(Blob.valueOf(header+'\r\n\r\n'));
}
if (last4Bytes.endsWith('==')) {
last4Bytes = last4Bytes.substring(0,2) + '0K';
bodyEncoded = bodyEncoded.substring(0,bodyEncoded.length()-4) + last4Bytes;
String footerEncoded = EncodingUtil.base64Encode(Blob.valueOf(footer));
bodyBlob = EncodingUtil.base64Decode(headerEncoded + bodyEncoded + footerEncoded);
} else if (last4Bytes.endsWith('=')) {
last4Bytes = last4Bytes.substring(0,3) + 'N';
bodyEncoded = bodyEncoded.substring(0,bodyEncoded.length()-4) + last4Bytes;
footer = '\n' + footer;
String footerEncoded = EncodingUtil.base64Encode(Blob.valueOf(footer));
bodyBlob = EncodingUtil.base64Decode(headerEncoded + bodyEncoded + footerEncoded);
} else {
footer = '\r\n' + footer;
String footerEncoded = EncodingUtil.base64Encode(Blob.valueOf(footer));
bodyBlob = EncodingUtil.base64Decode(headerEncoded + bodyEncoded + footerEncoded);
}
String configAsString ='\"conversion_target:answer_units\"';
Http h = new Http();
HttpRequest request = new HttpRequest();
request.setMethod('POST');
request.setHeader('Content-Type','multipart/form-data; boundary=' + boundary);
String username= 'DOCUMENT-CONVERSION-USERNAME';
String password= 'DOCUMENT-CONVERSION-PASSWORD';
request.setHeader('Authorization', 'Basic ' + EncodingUtil.base64Encode(blob.valueOf(username + ':' + password)));
request.setEndpoint('https://gateway.watsonplatform.net/document-conversion/api/v1/convert_document?version=2015-12-15&config='+configAsString);
request.setBodyAsBlob(bodyBlob);
request.setCompressed(true);
HttpResponse response = h.send(request);
System.debug(response.getBody());
}
}
You are sending the config as a query parameter, but it should be in the body.
Here is the curl command that makes what you are trying to do:
curl -X POST \
-u "{username}":"{password}" \
-F config="{\"conversion_target\":\"answer_units\"}" \
-F "file=#sample.pdf;type=application/pdf" \
"https://gateway.watsonplatform.net/document-conversion/api/v1/convert_document?version=2015-12-15"
I also think there is an error in the way you are creating the body. My team built an SDK to use the Watson APIs in the Salesforce environment. I would suggest you take a look.
If you can't deploy the SDK to your Salesforce organization (It's a lot of code), copy the code from the IBMWatsonMultipartBody.cls class. It will help you encode an Attachment as base64 so that you can end it to Watson.
UPDATE: The Document Conversion service was deprecated but the features from that service were enhanced and migrated to the Discovery service

Lua in Redis from JSON

I have a list of JSON strings stored in Redis that looks something like this:
[
{ "ID": 25, "DomainID": 23455, "Name": "Stuff", "Value": 23 },
{ "ID": 35, "DomainID": 23455, "Name": "Stuff", "Value": 10 }
]
The key would be something like "Event:23455".
Using a Lua script and ServiceStack.Redis how would I pull out an anonymous object containing only values where the value is less than 20?
So what I want to return would look like this:
[{ "ID": 35, "Value": 10}]
Thanks.
UPDATE 03/31/2013:
After trying what has been suggested I now have a new problem. A Lua script syntax error.
I'm getting a Lua syntax error about "expecting '=' near cjson". Here is the Lua script string (in C#) I am feeding to Redis:
string luaScript = "local tDecoded = cjson.decode(redis.call('GET', KEYS[1]));"
+ "local tFinal = {};"
+ "for iIndex, tValue in ipairs(tDecoded) do"
+ " if tonumber( tValue.Value ) < 20 then"
+ " table.insert(tFinal, { ID = tValue.ID, Value = tValue.Value});"
+ " end"
+ "end"
+ "return cjson.encode(tFinal);";
Are there any Lua or Redis Lua experts out there that can see what might be the problem? i.e. Does the Lua syntax look correct?
UPDATE 04/02/2013
The parsing error was resolved by adding \n newline characters to end of each line like so.
string luaScript = "local tDecoded = redis.call('GET', KEYS[1]);\n"
+ "local tFinal = {};\n"
+ "for iIndex, tValue in ipairs(tDecoded) do\n"
+ " if tonumber( tValue.Value ) < 20 then\n"
+ " table.insert(tFinal, { ID = tValue.ID, Value = tValue.Value});\n"
+ " else\n"
+ " table.insert(tFinal, { ID = 999, Value = 0});\n"
+ " end\n"
+ "end\n"
+ "return cjson.encode(tFinal);";
Unfortunately this works without errors but for some reason only returns "{}" or an empty list in the ServiceStack RedisClient. So I'm not there yet but I am one step closer.
You can use LuaJSON available on GitHub or you can also try this JSON to Lua table parser for the task. The usage will be something like this(for the GitHub link):
local json = require( "json" ) -- or JSON.lua
local tDecoded = json.decode(sJSON) -- sJSON is the original JSON string
local tFinal = {}
for iIndex, tValue in ipairs( tDecoded ) do
if tonumber( tValue.Value ) < 20 then
table.insert( tFinal, { ID = tValue.ID, Value = tValue.Value} )
end
end
print( json.encode(tFinal) )

is there any more standard way to resume function from jQuery ajax call?

I am trying to write a big project which involves of a lot of code. That's why I want to separate functionalities from different files.
the first file, dataJS, I make an AJAX call to get data from a JSON file.
the second file, showJS I want to display the data obtained from the dataJS file.
When it comes to implementation, I realise that AJAX call takes longer time and even though I include dataJS and showJS in order, showJS will still get a null data
therefore I made a function called continueFromDataJS() in showJS file
and call continueFromDataJS() at the end of the AJAX success function.
I think it's a rather makedo solution. Is there any standard way to do it?
In addition, all intellisense in my Visual Studio is gone. Despite separate files, is there any way to make visual studio get intellisense from the dataJS?
Thank you
sorry, I don't know how to add a follow up question
this is the code
for simplicity I rename some of the files and only take some part out of it. Hope that helps
code in html
code in dataJS.js
var planets = [];
var jsonData = null;
$(function () {
$.getJSON("Scripts/planetData.js", function (data) {
//planets[0] = new planet("uranus", "career", 45, 700, 400, 0.1, 5, 3);
jsonData = data;
for (var i = 0; i < data.planets.length; i++) {
var curPlanet = data.planets[i];
planets[i] = new planet(curPlanet.graphic, i, curPlanet.field, curPlanet.planetInitialAngle, curPlanet.distanceFromStar, curPlanet.planetRadius, curPlanet.planetRevolvingSpeed, curPlanet.planetRotationSpeed, curPlanet.contents.length);
$("#result").append("<p>" + curPlanet.graphic + " " + curPlanet.field + " " + curPlanet.planetInitialAngle + " " + curPlanet.distanceFromStar + " " + curPlanet.planetRadius + " " + curPlanet.planetRevolvingSpeed + " " + curPlanet.planetRotationSpeed + " " + curPlanet.contents.length + "</p>");
}
callDisplayScript(); //**continue from showJS.js file is that the way to do this?**
});
});
// more functions below in dataJS.js
showJS.js
function callDisplayScript() { **// this is the ugly part. What's the proper way to do it?**
$("#display #close").click(function () {
$("#display").fadeOut('slow');
});
$article = $("#display article");
$article.empty();
var data = jsonData.planets[pID].contents; // **this line won't get jsonData if it's out this curly brace.**
for (var i = 0; i < data.length; i++) {
$article.append(data[i].title);
$article.append(data[i].content);
}
$("#display").fadeIn('slow');
};
don't forget to answer my intellisense question. I want in datajs.js automatically hint planets and jsonData declared in datajs.js
is it possible?

Get web methods dynamically for an asmx service

We have number of asmx services. I want to give an user a page with a textbox to input service url like http://abc.win.com/myservice/customerdata.asmx. When user hit "Load" button, dynamically I add all the web methods to the dropdown. I need some pointers:
1. How to dynamically get all the methods?
2. How can I get the SOAP request for the method selected? So that, we can replace the parameter values with actual values?
Appreciate your help.
Kuul13 : You need to modify your webservice URL like http://abc.win.com/myservice/customerdata.asmx?wsdl when user clicks on load button. then you can use we "ServiceDescription" class to get wsdl description and then iterate that to get method names in 'WebMethodInfoCollection' class.
To get SOAP request you need to use SOAPExtension class.This will give you SOAP Request and Response XML.Refere this link for that : http://blog.encoresystems.net/articles/how-to-capture-soap-envelopes-when-consuming-a-web-service.aspx?www.microsoft.com
For dynamically calling webservice look a this V.Good article
http://www.codeproject.com/KB/webservices/webservice_.aspx
Please reply me for any comment.
System.Net.WebClient client = new System.Net.WebClient();
System.IO.Stream stream = client.OpenRead("http://www.webservicex.net/globalweather.asmx?wsdl");
ServiceDescription description = ServiceDescription.Read(stream);
ServiceDescriptionImporter importer = new ServiceDescriptionImporter();
importer.ProtocolName = "Soap12";
importer.AddServiceDescription(description, null, null);
importer.Style = ServiceDescriptionImportStyle.Client;
importer.CodeGenerationOptions = System.Xml.Serialization.CodeGenerationOptions.GenerateProperties;
CodeNamespace nmspace = new CodeNamespace();
CodeCompileUnit unit1 = new CodeCompileUnit();
unit1.Namespaces.Add(nmspace);
ServiceDescriptionImportWarnings warning = importer.Import(nmspace, unit1);
if (warning == 0)
{
CodeDomProvider provider1 = CodeDomProvider.CreateProvider("CSharp");
string[] assemblyReferences = new string[2] { "System.Web.Services.dll", "System.Xml.dll" };
CompilerParameters parms = new CompilerParameters(assemblyReferences);
CompilerResults results = provider1.CompileAssemblyFromDom(parms, unit1);
object[] args = new object[1];
args[0] = "India";
object wsvcClass = results.CompiledAssembly.CreateInstance("GlobalWeather");
MethodInfo mi = wsvcClass.GetType().GetMethod("GetCitiesByCountry");
RegExpForCountryCity(mi.Invoke(wsvcClass, args).ToString());
}
else
{
Console.WriteLine("Warning: " + warning);
}
void RegExpForCountryCity(string strHTML)
{
Regex qariRegex = new Regex(#"<Table>\s*<Country>(?<Country>[\s\S]*?)</Country>\s*<City>(?<City>[\s\S]*?)</City>\s*</Table>", RegexOptions.IgnoreCase | RegexOptions.Multiline);
MatchCollection mc = qariRegex.Matches(strHTML);
string strCountryCity = "";
for (int i = 0; i < mc.Count; i++)
{
if (string.IsNullOrEmpty(strCountryCity))
strCountryCity = "Country: " + "<b>" + mc[i].Groups["Country"].Value + "</b>" + " " + "City: " + "<b>" + mc[i].Groups["City"].Value + "</b>" + "</br>";
else
strCountryCity += "</br>" + "Country: " + "<b>" + mc[i].Groups["Country"].Value + "</b>" + " " + "City: " + "<b>" + mc[i].Groups["City"].Value + "</b>" + "</br>";
}
Response.Write(strCountryCity);
}