I am trying to insert some data in a collection after an asynchronous api call in the Accounts.onCreateUser callback
(API: https://github.com/Mangopay/mangopay2-nodejs-sdk).
However I am getting the error
throw new Error("Meteor code must always run within a Fiber. " +
Error: Meteor code must always run within a Fiber. Try wrapping
callbacks that you pass to non-Meteor libraries with
Meteor.bindEnvironment.
Here's my first try:
Accounts.onCreateUser(function(options, user) {
mangoPayApi.Users.create({
"Email": options.email,
"FirstName": options.profile.firstName,
"LastName": options.profile.lastName,
"CountryOfResidence": "FR",
"Nationality": "FR",
"Birthday": new Date(aBirthDateTimestamp).getTime() / 1000,
"PersonType": "NATURAL",
"Tag": user._id,
}, function(mpUser) {
// User created - using callback
console.log('User created ');
console.log(mpUser);
aMPData.user = mpUser;
MPData.insert(aMPData); // Insert data into collection
Second shot:
I tried to make the api call aynchronous
let synCreateUser = Meteor.wrapAsync(mangoPayApi.Users.create, mangoPayApi.Users );
user = synCreateUser.create({
"Email": post.emails[0],
"FirstName": post.profile.firstName,
"LastName": post.profile.lastName,
"CountryOfResidence": "FR",
"Nationality": "FR",
"Birthday": new Date(aBirthDateTimestamp).getTime() / 1000,
"PersonType": "NATURAL",
"Tag": post._id,
});
But now I get the following error
Exception in queued task: TypeError: Object function (/* arguments */) {
var self = context || this;
var newArgs = _.toArray(arguments);
var callback;
for (var i = newArgs.length - 1; i >= 0; --i) {
var arg = newArgs[i];
var type = typeof arg;
if (type !== "undefined") {
if (type === "function") {
callback = arg;
}
break;
}
}
if (! callback) {
if (Meteor.isClient) {
callback = logErr;
} else {
var fut = new Future();
callback = fut.resolver();
}
++i; // Insert the callback just after arg.
}
newArgs[i] = Meteor.bindEnvironment(callback);
var result = fn.apply(self, newArgs);
return fut ? fut.wait() : result;
} has no method 'create'
at Object.added (server/main.js:102:30)
at [object Object].observeChangesCallbacks.added (packages/minimongo/observe.js:153:1)
at self.applyChange.added (packages/minimongo/observe.js:53:1)
How can I insert the data I get from an api call into a collection ?
Thanks !
I am not sure what is handleCharge, but basically you should call synCreateUser if you'd like to use synchronous function you created at previous line:
let synCreateUser = Meteor.wrapAsync(mangoPayApi.Users.create, mangoPayApi.Users);
user = synCreateUser({
"Email": post.emails[0],
"FirstName": post.profile.firstName,
"LastName": post.profile.lastName,
"CountryOfResidence": "FR",
"Nationality": "FR",
"Birthday": new Date(aBirthDateTimestamp).getTime() / 1000,
"PersonType": "NATURAL",
"Tag": post._id,
});
// now you can process result from user variable
/* Create MangoPay user */
mangoPayApi.Users.create({
"Email": document.emails[0].address,
"FirstName": document.profile.firstName,
"LastName": document.profile.lastName,
"CountryOfResidence": "FR",
"Nationality": "FR",
"Birthday": new Date(aBirthDateTimestamp).getTime() / 1000,
"PersonType": "NATURAL",
"Tag": document._id,
}, Meteor.bindEnvironment( function(mpUser) {
// User created - using callback
console.log('User created ');
console.log(mpUser);
/* Mangopay Id */
aMPData.Id = mpUser.Id;
if(mpUser)
{
mangoPayApi.Wallets.create({
"Owners": [mpUser.Id],
"Description": "Client Wallet",
"Currency": "EUR"
}, Meteor.bindEnvironment( function(clientWallet) {
console.log('wallet created');
console.log(clientWallet);
aMPData.clientWallet.Id = clientWallet.Id;
aMPData.clientWallet.Owner = clientWallet.Owners[0];
/* MangoPay clientWallet wallet created */
if(clientWallet)
{
mangoPayApi.Wallets.create({
"Owners": [clientWallet.Owners[0]],
"Description": "mw Wallet",
"Currency": "EUR"
}, Meteor.bindEnvironment(function(mw) {
if(mw)
{
console.log(mw);
aMPData.mw.Id = mw.Id;
aMPData.mw.Owner = mw.Owners[0];
// Mangopay.insert(aMPData);
Meteor.users.update(document._id, { $set: { mangopay: aMPData } });
}
}));
}
})); // callback mangoPayApi.Wallets // Meteor.bindEnvironment callback angoPayApi.Wallets // mangoPayApi.Wallets.create
} // end if mpUser
})); // callback Users.create // Meteor.bindEnvironment callback Users.create// mangoPayApi.Users.create;
}
Related
I would like to send a POST request to a certain app through their API. What I am trying to do is to process the input data (called data) and send a POST request on one record by one record in the loop. Then, I delete the corresponding object in data for optimization purpose. I know that because of the asynchronous feature of JavaScript, the loop finishes before the function gets called. However, even though I wrap the api function in IIFE or wrap it in an async function with await(the code is below), the compiler still gives me function calls with the same parameter which is the last object. So, when I see created records on the app, David's information was generated three times. The screenshot below is each record object after being processed. If you could tell me ways of triggering the api call before the next iteration in the loop, that would be greatly appreciated.
const obj = [];
var record = {};
var data = [
{
"userId": "123",
"name": "John",
"phoneNumber": "123-456-6789"
},
{
"userId": "345",
"name": "Summer",
"phoneNumber": "535-631-9742"
},
{
"userId" : "789",
"name": "David",
"phoneNumber": "633-753-1352"
}
]
var dataLen = data.length;
var people = data;
createKeyValue = ((key, value) => {
var temp = {};
temp["value"] = value;
obj[key] = temp;
});
apiCall = ((record) => {
clientInformation.record.addRecord.then((resp) => {
console.log(resp);
}).catch((err) => {
console.log(err);
});
});
async function asyncFunction(record) {
let promise = new Promise((resolve, reject) => {
setTimeout(() => apiCall(record), 1000)
});
let result = await promise;
console.log(result);
}
while (dataLen > 0) {
for (let [key, value] of Object.entries(data[0])) {
switch(key) {
case 'userId':
createKeyValue(key, value);
break;
case 'name':
createKeyValue(key, value);
break;
default:
}
}
record["record"] = obj;
asyncFunction(record);
data.shift();
dataLen -= 1;
}
Here is the screenshot of how each processed data looks like.
I think you haven't understand how the for loop inside the while works. The data should be incremented each time to get the next array inside data.
The data[0] => { userId: 123 ... }, data[1] => { userId: 345 ... } and so on .
At each for loop iteration checks the 3 elements of each sub array, so each time temp stores the key values for userId and name. So when the loop finishes, the temp contains as key => userId, name and the corresponding values.
var data = [
{
"userId": "123",
"name": "John",
"phoneNumber": "123-456-6789"
},
{
"userId": "345",
"name": "Summer",
"phoneNumber": "535-631-9742"
},
{
"userId" : "789",
"name": "David",
"phoneNumber": "633-753-1352"
}
]
var dataLen = data.length;
let i = 0 ;
while ( i < dataLen) {
let temp = [];
for (let [key, value] of Object.entries(data[i])) {
if(key == 'userId' || key == 'name'){
temp[key] = value;
}
}
//Just to print the values and understand
for(let k in temp){
console.log(k+" -> "+temp[k]);
}
//here you will pass the temp values to functions
console.log(" At each iteration execute the required functions ");
//asyncFunction(temp);
i += 1;
}
const { screen } = data;
// screen = 'News', string
const { params } = data;
// params = object
// Object {
"body": "test",
"created_at": "2020-05-25 21:25:37",
"status": 1,
"title": "test",
"updated_at": "2020-05-25 21:25:37",
}
if (notification.origin === 'selected') {
if (screen) {
NavigationService.navigate(screen, { params });
}
}
function navigate(routeName, object) {
console.log('routeName =' + routeName);
// routeName =News
console.log(object);
// Object {
"params": null,
}
}
problem
I can't get object in function navigate.
I don't still understand it..
I would appreciate it if you could give me any advice.
You should not add the brackets around param when calling navigate. Call it like so:
NavigationService.navigate(screen, params);
Instead of:
NavigationService.navigate(screen, { params });
I have an API that returns data in this format -
{
"data": [
{
"id": 121,
"id_type": "some string",
"id_value": "test",
"attribute1": {
"attr_id": 140,
"attribute_client_id": null,
},
"attribute2": {
"attr2_id": 143,
"attribute2_client_id": null,
},
"status": "some string",
"person_name": "James Allen",
"friends": [
{
"friend_id": 1,
"data_id": null,
},
{
"friend_id": 2,
"data_id":null
}
],
"text_description": "Some string",
"text_format": [
"something",
"else"
],
"job_description": "new string",
"is_member": false,
"is_external": false
},
....
]
}
I want to have a function that calculates if of array with is_member is true.
I can do this in the code itself using the filter function with something like this - I am using Chakram library to hit the API end points.
describe('Check if is member is true',()=>{
it('get data',()=>{
let length_of_arr
return response.then((resp)=>{
let length_of_arr = resp.body.data;
length_of_arr.length= Object.keys(length_of_arr).length;
console.log(length_of_arr);
let new_arr = Array.from(length_of_arr);
let r = new_arr.filter(({is_member})=>is_member === true);
console.log(r.length);
expect(r.length).to.be.greater.than(0);
}) ;
});
This works perfectly fine and I am able to get the correct results. However, I need to use this same test for the same API at other places too. So I wanted to have a function which can do it.
In the root directory, I created a file custom_functions.js, which has code like
module.exports = {
get_member_details(resp,data,attr){
let length_of_arr;
let length_of_arr = resp.body.data;
length_of_arr.length= Object.keys(length_of_arr).length;
console.log(length_of_arr);
let new_arr = Array.from(length_of_arr);
let r = new_arr.filter(({attr})=>attr === true);
console.log(r.length);
}
}
However, this is not correct and it gives error that data is not defined. How can I achieve this kind of modularisation when using Javascript. I would also welcome if there are suggestions to improve how to approach this problem as well.
As i understand you want to define a function that you can call it in many tests:
//custom_functions.js
function has_member(data){
return data.filter(res => res.is_member).length > 0;
}
module.exports = {
has_member,
}
// in your test you can call this function like this :
const { has_member } require ('./custom_functions');
describe('Check if is member is true',()=>{
it('get data',() => {
return response.then((resp)=>{
const data = resp.body.data;
const has_member = has_member(data);
expect(has_member).to.be.true;
});
});
here is example of my data that I get after ajax call response is successful obj.DATA looks like this:
{
"A43D": {
"FIRSTNAME": "Mike",
"EMAIL": "mjohns#gmail.com",
"LASTNAME": "Johns"
},
"4E83": {
"FIRSTNAME": "Steve",
"EMAIL": "scook#gmail.com",
"LASTNAME": "Cook"
}
}
$.ajax({
type: 'POST',
url: 'AjaxFunctions.cfc?method=getCustomers',
data: formData,
dataType: 'json'
}).done(function(obj){
console.log(obj.DATA);
sessionStorage.setItem("customersData", JSON.stringify(obj.DATA));
}).fail(function(jqXHR, textStatus, errorThrown){
alert('Error: '+errorThrown);
});
Then I dump sessionStorage in console.log(sessionStorage) and I see this:
{
"customersData": "{\"A43D\":{\"FIRSTNAME\":\"Mike\",\"EMAIL\":\"mjohnes#gmail.com\",\"LASTNAME\":\"Johnes\"},\"4E83\":{\"FIRSTNAME\":\"Steve\",\"EMAIL\":\"scook#gmail.com\",\"LASTNAME\":\"Cook\"}}"
}
So I was trying next:
sessionData = sessionStorage.hasOwnProperty(customersData[recordID]) ? JSON.parse(sessionStorage.getItem(customersData[recordID])) : null;
This is in the function where I just pass record id and then try to access that record in session storage. If I try to console.log(sessionData) all I see is null. I'm wondering how I can access specific key in customersData object inside of the session storage? Also how I would insert/edit record in sessionStorage customersData object?
each time you try to save/load something for the localStorage/sessionStorage and you know it is a json object, always stringify/parse it depending on the case.
here you have your code fixed to work.
NOTE: I tried to create a snippet but it didn't work because we can not access to the sessionStorage of a sandbox.
NOTE2: always check what data are you going to parse, if the record doesnt exist on the storage, it will return null.
var data = {
"A43D": {
"FIRSTNAME": "Mike",
"EMAIL": "mjohns#gmail.com",
"LASTNAME": "Johns"
},
"4E83": {
"FIRSTNAME": "Steve",
"EMAIL": "scook#gmail.com",
"LASTNAME": "Cook"
}
}
//here we save the item in the sessionStorage.
sessionStorage.setItem("customersData", JSON.stringify(data));
//now we retrieve the object again, but in a string form.
var customersDataString = sessionStorage.getItem("customersData");
console.log(customersDataString);
//to get the object we have to parse it.
var customersData = JSON.parse(customersDataString);
console.log(customersData);
Here's how I'd approach this,
by creating some customersStorage Object with methods like get, set and add
jsFiddle demo
var customersStorage = {
// Get all or single customer by passing an ID
get: function( id ) {
var parsed = JSON.parse(sessionStorage.customersData || "{}");
return id ? parsed[id] : parsed;
},
// Set all
set: function( obj ) {
return sessionStorage.customersData = JSON.stringify(obj || {});
},
// Add single customer and store
add: function( id, obj ) {
var all = this.get();
// Make sure customer does not exists already;
if(all.hasOwnProperty(id)) return console.warn(id+ " exists!");
all[id] = obj;
return this.set(all);
}
};
// Let's play!
// Store All
customersStorage.set({
"A43D": {
"FIRSTNAME": "Mike",
"EMAIL": "mjohns#gmail.com",
"LASTNAME": "Johns"
},
"4E83": {
"FIRSTNAME": "Steve",
"EMAIL": "scook#gmail.com",
"LASTNAME": "Cook"
}
});
// Get single
console.log( customersStorage.get("4E83") );
// Add new
customersStorage.add("AAA0", {
"FIRSTNAME": "Espresso",
"LASTNAME": "Coffee",
"EMAIL": "espresso#coffee.com"
});
// Get all
console.log( customersStorage.get() );
Furthermore, to make your session handling object more "client" agnostic I'd expand it to handle even more data by namespace:
var ObjectStorage = function(nsp, useSession) {
var stg = (useSession ? sessionStorage : localStorage);
return {
// Get all or single customer by passing an ID
get: function(id) {
var parsed = JSON.parse(stg[nsp] || "{}");
return id ? parsed[id] : parsed;
},
// Set all
set: function(obj) {
return stg[nsp] = JSON.stringify(obj || {});
},
// Add one to all
add: function(prop, val) {
var all = this.get();
// Make sure property does not exists already;
if (all.hasOwnProperty(prop)) return console.warn(prop + " exists!");
all[prop] = val;
return this.set(all);
}
}
};
// Let's play!
// Create instance. Use true to use sessionStorage (instead of localStorage)
var Customers = new ObjectStorage("customersData", true);
// now you can use .add(), .get(), .set() on your Customers Object
This is my stored procedure:
CREATE PROCEDURE PROC()
BEGIN
SELECT * FROM TABLENAME;
END//
This is my unction to call stored procedure using SQL adapter:
function callStored() {
return WL.Server.invokeSQLStoredProcedure({
procedure : "proc",
parameters : []
});
}
This is the invocationResult:
{
"isSuccessful": true,
"resultSet": [
{
"name": "a",
"pass": "123",
"time_stamp": "2014-04-07T10:13:17.000Z"
},
{
"name": "chetan",
"pass": "123456",
"time_stamp": "2014-04-07T10:13:34.000Z"
},
{
"name": "dileep",
"pass": "456321",
"time_stamp": "2014-04-07T10:13:54.000Z"
},
{
"name": "bnc",
"pass": "654321",
"time_stamp": "2014-04-07T10:19:37.000Z"
}
]
}
I need to parse this and display or alert the values of name, pass and time_stamp.
How do I accomplish this?
In your application JavaScript (common\js\main.js), you can have something like the following. In the below code an alert will be displayed with the values of the name key from the resultSet.
You can also take a look here: use resultset returned by WL.Server.invokeSQLStatement within sql adapter procedure
function wlCommonInit() {
invokeAdapter();
}
function invokeAdapter() {
var invocationData = {
adapter: "your-adapter-name",
procedure: "callStored",
parameters: []
};
WL.Client.invokeProcedure (invocationData, {
onSuccess: invocationSuccess,
onFailure: invocationFailure}
);
}
function invocationSuccess(response) {
var i,
resultSet = response.invocationResult.resultSet;
namesArray = [];
for (i = 0; i < resultSet.length; i++) {
namesArray[i] = resultSet[i].name;
}
alert (JSON.stringify(namesArray));
}
function invocationFailure(response) {
alert (response);
}
You have already asked this here: ibm worklight stored procedure
Why did you not follow through the documentation and learned how to write the above?
Please read the documentation!
Please read the "Invoking adapter procedures from client applications" and its Exercise and code sample in the Getting started with IBM Worklight page.
function wlCommonInit(){
getUsersInfo();
}
function getUsersInfo(){
var invocationData = {
adapter : 'YOUR_ADAPTER',
procedure : 'YOUR_PROCEDURE',
parameters : []
};
WL.Client.invokeProcedure(invocationData,{
onSuccess : getUsersInfoSuccess,
onFailure : getUsersInfoFailure
});
}
function getUsersInfoSuccess(result){
if (result.invocationResult.Items.length > 0) {
displayUsersInfo(result.invocationResult.Items);
} else {
getUsersInfoFailure();
}
}
function getUsersInfoFailure(result){
alert("Cannot retrieve users info");
}
function displayUsersInfo(items){
var i = 0, usersInfo = '';
for (i = 0; i < items.length; i++) {
usersInfo += ' name: ' + items[i].name;
usersInfo += ' pass: ' + items[i].pass;
usersInfo += ' time_stamp: ' + items[i].time_stamp;
}
alert(usersInfo);
}