Meteor how to use Meteor.wrapAsync with facebook Graph Api - facebook-graph-api

I solved Graph Api asynchronous request by using fibers/future, which allows to give function result after predefined amount of time, downside of this solution is when facebook sends response faster than 1000ms it will wait anyway.
Is there any way to make Server Side function which returns graph api result right after response comes? I have found Meteor.wrapAsync could be helpful, but I'm not sure i get it's syntax correctly.
Here's what I have done using fibers and it's working exactly one second.
function graphGet(query){
var response = new Future(); // wait for async FB response
var waitingTime = 1000;
var graphResponse = "no result after: " + waitingTime + "ms";
FBGraph.get(query, function(error, response) {
if (response) { graphResponse = response; }
else { graphResponse = error; }
});
setTimeout(function() {
response['return'](graphResponse);
}, waitingTime);
return response.wait();
}

The same code using Meteor.wrapAsync is much shorter :
function graphGet(query){
// wrap the async func into a FBGraph bound sync version
var fbGraphGetSync = Meteor.wrapAsync(FBGraph.get, FBGraph);
// use a try / catch block to differentiate between error and success
try{
var result = fbGraphGetSync(query);
return result;
}
catch(exception){
console.log(exception);
}
}

Related

Is there a solution to asynchronously wait for C++ data on Flutter's invokeMethod?

Currently, this is how I read from C++ using Flutter:
final Uint8List result = await platform.invokeMethod(Common.MESSAGE_METHOD, {"message": buffer});
It is handled by Kotlin like this:
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
if (call.method == MESSAGE_METHOD) {
val message: ByteArray? = call.argument<ByteArray>("message")
//... //response = Read something FROM C++
result.success(response)
Since this happens in the main thread, if I take too much time to answer, I make Flutter's UI slow.
Is there a solution to get C++ data in an async way?
I know that Flutter has support for event channels to send data back from C++ to Flutter. But what about just requesting the data on the Flutter side and waiting for it to arrive in a Future, so I can have lots of widgets inside a FutureBuilder that resolves to something when ready?
If reading something from C++ is a heavy process, You can use AsysncTask to perform it in the background for android.
internal class HeavyMsgReader(var result: MethodChannel.Result) : AsyncTask<ByteArray?, Void?, String?>() {
override fun doInBackground(vararg message: ByteArray?): String {
//... //response = Read something FROM C++
return "response"
}
override fun onPostExecute(response: String?) {
result.success(response)
}
}
Calling async task:
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
if (call.method == MESSAGE_METHOD) {
val message: ByteArray? = call.argument<ByteArray>("message")
HeavyMsgReader(result).execute(message);
Hopefully this will work
import 'dart:async';
Future<Uint8List> fetchData(buffer) async {
final Uint8List result = await platform.invokeMethod(Common.MESSAGE_METHOD, {"message": buffer});
return result;
}
And just call it, like this
fetchData(buffer).then((result) => {
print(result)
}).catchError(print);
Proof that its working:
import 'dart:async';
Future<String> fetchUserOrder() async {
await Future.delayed(Duration(seconds: 5));
return 'Callback!';
}
Future<void> main() async {
fetchUserOrder().then((result) => {
print(result)
}).catchError(print);
while(true){
print('main_thread_running');
await Future.delayed(Duration(seconds: 1));
}
}
output:
main_thread_running
main_thread_running
main_thread_running
main_thread_running
main_thread_running
Callback!
main_thread_running
main_thread_running
...

nswag generated service has no return logic

I have a asp.net WebAPI service for user login that takes an email and password. The api method has the following signature. LoginDto has two fileds, Email and password.
public async Task<IActionResult> Login(LoginDto dto)
Once the user is authenticated, WebAPI returns an object that has token and Id:
return Ok(new { Token = GenerateJwtTokenFromClaims(claims), Id=user.Id });
On the client side (Blazor app), I used nswag command line tool by running nswag run and it "successfully" generated the Service and Contract files. Everything complies. nswag generated code is pasted below.
When I want to use the login nswag Service, I have the following method (I also have an overloaded method with CancellationToken but I only use this method):
public System.Threading.Tasks.Task Login2Async(LoginDto body)
{
return Login2Async(body, System.Threading.CancellationToken.None);
}
The question that I have is that how do I get the response out of the nswag-generated-code that the WebAPI login sent back to the client? When I try to assign a var to the method, I get Cannot assign void to an implicitly-typed variable which makes sense since I don't see a return type. I also don't see any logic in the nswag generated service file to return the response to the caller. How do I get the response back from the nswag generated API call? Is there an option I have to set in nswag run to get a response object back? Thanks in advance.
public async System.Threading.Tasks.Task Login2Async(LoginDto body, System.Threading.CancellationToken cancellationToken)
{
var urlBuilder_ = new System.Text.StringBuilder();
urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Account/Login");
var client_ = _httpClient;
var disposeClient_ = false;
try
{
using (var request_ = new System.Net.Http.HttpRequestMessage())
{
var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value));
content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json");
request_.Content = content_;
request_.Method = new System.Net.Http.HttpMethod("POST");
PrepareRequest(client_, request_, urlBuilder_);
var url_ = urlBuilder_.ToString();
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
PrepareRequest(client_, request_, url_);
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
var disposeResponse_ = true;
try
{
var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
if (response_.Content != null && response_.Content.Headers != null)
{
foreach (var item_ in response_.Content.Headers)
headers_[item_.Key] = item_.Value;
}
ProcessResponse(client_, response_);
var status_ = (int)response_.StatusCode;
if (status_ == 200)
{
return;
}
else
if (status_ == 400)
{
var objectResponse_ = await ReadObjectResponseAsync<ProblemDetails>(response_, headers_).ConfigureAwait(false);
throw new ApiException<ProblemDetails>("Bad Request", status_, objectResponse_.Text, headers_, objectResponse_.Object, null);
}
else
{
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
}
}
finally
{
if (disposeResponse_)
response_.Dispose();
}
}
}
finally
{
if (disposeClient_)
client_.Dispose();
}
}
Big thanks to the NSwag team, the issue is resolved. I was returning anonymous object from the WebAPI method. The correct way to do is the following. Notice that IActionResult was changed to ActionResult passing a concrete object to return to the caller.
public async Task<ActionResult<LoginDtoResponse>> Login(LoginDto dto)
then returning
return Ok(new LoginDtoResponse { Token = GenerateJwtTokenFromClaims(claims), Id=user.Id });
After that I did that, the following code was generated:
if (status_ == 200)
{
var objectResponse_ = await ReadObjectResponseAsync<LoginDtoResponse>(response_, headers_).ConfigureAwait(false);
return objectResponse_.Object;
}

AWS lambda, C# async mail sending issue - MailJet API

I was originally using AWS SES for mail sending from my Lambda, but it has very slow delivery.
I then decided to switch to MailJet and use their API for sending mails. I am using the NuGet package, V3.1 of the API, and pretty much the sample code from MailJet to send the mails async.
public async Task<bool> SendEmailAsync(EmailModel model)
{
Boolean sent = false;
MailjetClient client = new MailjetClient(Environment.GetEnvironmentVariable("EmailAPIKey"), Environment.GetEnvironmentVariable("EmailAPISecret"))
{
Version = ApiVersion.V3_1,
};
MailjetRequest request = new MailjetRequest
{
Resource = Send.Resource,
}.Property(Send.Messages, new JArray {
new JObject {
{"From", new JObject {
{"Email", Environment.GetEnvironmentVariable("SenderEmail")},
{"Name", Environment.GetEnvironmentVariable("SenderEmailName")}
}
},
{"HTMLPart", model.EmailHtmlBody},
{"Subject", model.EmailSubject},
{"TextPart", model.EmailTextBody},
{"To", new JArray {
new JObject {
{"Email", model.EmailTo},
{"Name", model.EmailTo}
}
}}
}
});
try
{
MailjetResponse response = await client.PostAsync(request);
if (response.IsSuccessStatusCode)
{
sent = true;
LambdaLogger.Log(string.Format("Total: {0}, Count: {1}\n", response.GetTotal(), response.GetCount()));
LambdaLogger.Log(response.GetData().ToString());
}
else
{
sent = false;
LambdaLogger.Log(string.Format("StatusCode: {0}\n", response.StatusCode));
LambdaLogger.Log(string.Format("ErrorInfo: {0}\n", response.GetErrorInfo()));
LambdaLogger.Log(response.GetData().ToString());
LambdaLogger.Log(string.Format("ErrorMessage: {0}\n", response.GetErrorMessage()));
}
}
catch (Exception mailFail)
{
sent = false;
LambdaLogger.Log(string.Format("Failed: {0}\n", mailFail.Message.ToString() + " : " + mailFail.InnerException.Message.ToString()));
}
return sent;
}
When I test the code locally everything works just fine.
When I deploy the lambda to AWS and call the method for sending mails, it is completely random if the mail is send. I am guessing it is the async part which fails for some reason, I am hoping someone can help me to figure this out, because for now I am stuck on this issue.
Or if someone can tell me how to get Amazon SES to send without delay.
From the question:
it is completely random
And from a comment:
it seems like the lambda just keeps running and does not wait for the reply from MailJet
It is sounding like an async/await issue, but probably not where you think. Note that you are correctly awaiting the result of the MailJet operation:
MailjetResponse response = await client.PostAsync(request);
But that's only in this method. This method is of course async:
public async Task<bool> SendEmailAsync(EmailModel model)
When you call this method, do you await it? The method where you call it, that should also be async. Do you await that? Basically, are you "async all the way down"?
It sounds like somewhere in the codebase there's an async operation that's being invoked and then forgotten.

API Gateway -> Lambda -> DynamoDB using Cognito. HTTP POST-> Unable to read response but returns a code 200

Scenario:
I query an HTTP POST (using Authorizer as Header parameter from Cognito).
When I try to fetch/read the query response, it triggers the error event. However, in the browser, I can see how 2 HTTP POST responses with 200 code and one of them returning the valid response. For example: if I make the request via POST man I receive the data in 1 response in a good way.
Problem:
I am unable to print the result because it launches the error event with not valid response data.
Browser images:
https://i.postimg.cc/MTMsxZjw/Screenshot-1.png
https://i.postimg.cc/3RstwMgv/Screenshot-2.png
Lambda code:
'use strict';
var AWS = require('aws-sdk'),
documentClient = new AWS.DynamoDB.DocumentClient();
exports.handler = function index(event, context, callback){
var params = {
TableName : "data-table"
};
documentClient.scan(params, function(err, data){
if(err){
callback(err, null);
}else{
console.log(JSON.stringify(data.Items));
callback(null, data.Items);
}
});
}
Client side JS code:
function requestData(pickupLocation) {
$.ajax({
type: 'POST',
url: _config.api.invokeUrl,
headers: {
Authorization: authToken,
},
data: "{}",
cache: false,
success: completeRequest,
error: errorRequest
});
}
function completeRequest(response) {
alert("hello");
alert(response.d);
}
function errorRequest(response) {
alert("hello1");
alert(response.status + ' ' + response.statusText);
}
According to further clarification based on the comments, this looks like API gateway has CORS disabled or enabled with incorrect header value returns.
The solution is to re-enable CORS through API gateway and in the advanced options add Access-Control-Allow-Origin to the header response (if not already on by default).
If you're proxying the response, you need to follow a specific format as described here
'use strict';
console.log('Loading hello world function');
exports.handler = async (event) => {
let name = "you";
let city = 'World';
let time = 'day';
let day = '';
let responseCode = 200;
console.log("request: " + JSON.stringify(event));
// This is a simple illustration of app-specific logic to return the response.
// Although only 'event.queryStringParameters' are used here, other request data,
// such as 'event.headers', 'event.pathParameters', 'event.body', 'event.stageVariables',
// and 'event.requestContext' can be used to determine what response to return.
//
if (event.queryStringParameters && event.queryStringParameters.name) {
console.log("Received name: " + event.queryStringParameters.name);
name = event.queryStringParameters.name;
}
if (event.pathParameters && event.pathParameters.proxy) {
console.log("Received proxy: " + event.pathParameters.proxy);
city = event.pathParameters.proxy;
}
if (event.headers && event.headers['day']) {
console.log("Received day: " + event.headers.day);
day = event.headers.day;
}
if (event.body) {
let body = JSON.parse(event.body)
if (body.time)
time = body.time;
}
let greeting = `Good ${time}, ${name} of ${city}. `;
if (day) greeting += `Happy ${day}!`;
let responseBody = {
message: greeting,
input: event
};
// The output from a Lambda proxy integration must be
// of the following JSON object. The 'headers' property
// is for custom response headers in addition to standard
// ones. The 'body' property must be a JSON string. For
// base64-encoded payload, you must also set the 'isBase64Encoded'
// property to 'true'.
let response = {
statusCode: responseCode,
headers: {
"x-custom-header" : "my custom header value"
},
body: JSON.stringify(responseBody)
};
console.log("response: " + JSON.stringify(response))
return response;
};
If you are using chrome you probably need the cors plugin .

Connect AWS mobile backend to DynamoDB

I am trying to use AWS mobile backend (using lambda function) to insert into dynamoDB (also configured at the mobile backend) but with no success so far.
The relevant code:
'use strict';
console.log("Loading function");
const AWS = require('aws-sdk');
const docClient = new AWS.DynamoDB.DocumentClient({region:process.env.MOBILE_HUB_PROJECT_REGION});
exports.handler = function(event, context, callback) {
var responseCode = 200;
var requestBody, pathParams, queryStringParams, headerParams, stage,
stageVariables, cognitoIdentityId, httpMethod, sourceIp, userAgent,
requestId, resourcePath;
console.log("request: " + JSON.stringify(event));
// Request Body
requestBody = event.body;
if (requestBody !== undefined && requestBody !== null) {
// Set 'test-status' field in the request to test sending a specific response status code (e.g., 503)
responseCode = JSON.parse(requestBody)['test-status'];
}
// Path Parameters
pathParams = event.path;
// Query String Parameters
queryStringParams = event.queryStringParameters;
// Header Parameters
headerParams = event.headers;
if (event.requestContext !== null && event.requestContext !== undefined) {
var requestContext = event.requestContext;
// API Gateway Stage
stage = requestContext.stage;
// Unique Request ID
requestId = requestContext.requestId;
// Resource Path
resourcePath = requestContext.resourcePath;
var identity = requestContext.identity;
// Amazon Cognito User Identity
cognitoIdentityId = identity.cognitoIdentityId;
// Source IP
sourceIp = identity.sourceIp;
// User-Agent
userAgent = identity.userAgent;
}
// API Gateway Stage Variables
stageVariables = event.stageVariables;
// HTTP Method (e.g., POST, GET, HEAD)
httpMethod = event.httpMethod;
// TODO: Put your application logic here...
let params = {
Item:{
"prop1":0,
"prop2":"text"
},
TableName:"testTable"
};
docClient.put(params, function(data, err){
if(err)
responseCode = 500;
else
{
responseCode = 200;
context.succeed(data);
}
});
// For demonstration purposes, we'll just echo these values back to the client
var responseBody = {
requestBody : requestBody,
pathParams : pathParams,
queryStringParams : queryStringParams,
headerParams : headerParams,
stage : stage,
stageVariables : stageVariables,
cognitoIdentityId : cognitoIdentityId,
httpMethod : httpMethod,
sourceIp : sourceIp,
userAgent : userAgent,
requestId : requestId,
resourcePath : resourcePath
};
var response = {
statusCode: responseCode,
headers: {
"x-custom-header" : "custom header value"
},
body: JSON.stringify(responseBody)
};
console.log("response: " + JSON.stringify(response))
context.succeed(response);
};
this doesn't put the item to the table for some reason.
I gave the necessary permissions using the roles part, anything I am missing?
**responseCode is only for testing purposes.
Edit:
tried AWS node.js lambda request dynamodb but no response (no err, no return data) and doesn't work either.
Edit2:
Added the full handler code. (it the default generated code when creating first AWS lambda).
I have refactored some bits of your code to look much simpler and use async/await (make sure to select Node 8.10 as the running environment for your function) instead of callbacks. I also got rid of the context and callback parameters, as they were used for older versions of NodeJS. Once you're using Node 8+, async/await should be the default option.
Also, it is possible to chain a .promise() on docClient.putItem, so you can easily await on it, making your code way simpler. I have left only the DynamoDB part (which is what is relevant to your question)
'use strict';
console.log("Loading function");
const AWS = require('aws-sdk');
const docClient = new AWS.DynamoDB.DocumentClient({region:process.env.MOBILE_HUB_PROJECT_REGION});
exports.handler = async (event) => {
let params = {
Item:{
"prop0":1,
"prop2":"text"
},
TableName:"testTable"
};
try {
await docClient.put(params).promise();
} catch (e) {
console.log(e)
return {
messsage: e.message
}
}
return { message: 'Data inserted successfully' };
};
Things to keep in mind if still it does not work:
Make sure your Lambda function has the right permissions to insert items on DynamoDB (AmazonDynamoDBFullAccess will do it)
You ALWAYS have to provide the partition key when inserting items to DynamoDB. On your example, the JSON only has two properties: prop1 and prop2. If none of them are the partition key, your code will certainly fail.
Make sure you table also exists
If you code fails, just check CloudWatch logs as any exception is now captured and printed out on the console.
The reason why no data is written in the table is because the call to DynamoDB put is asynchronous and will return by calling your callback. But during that time, the rest of the code continues to execute and your function eventually finish before the call to DynamoDB has a chance to complete.
You can use the await / async keywords to make your code sychronous :
async function writeToDynamoDB(params) {
return new Promise((resolve,reject) => {
docClient.put(params, function(data, err){
if(err)
reject(500);
else
resolve(data);
});
});
}
let params = ...
var data = await writeToDynamoDB(params)
You can find sample code I wrote (in Typescript) at https://github.com/sebsto/maxi80-alexa/blob/master/lambda/src/DDBController.ts