I'm using the Truffle testing framework (v4.0.1) for Ethereum. I can't figure out why the transaction fees aren't adding up to gasPrice*gasUsed for the following simple contract:
contract MinTest {
function run() public returns(bool) {
return true;
}
}
The mocha test I'm using is:
contract('Minimum Test', function (accounts) {
it("min test", function () {
var initial = web3.eth.getBalance(accounts[1]);
var final;
return MinTest.deployed().then(function(instance) {
return instance.run({from: accounts[1]});
}).then(function(result) {
final = web3.eth.getBalance(accounts[1]);
var gasPrice = new BigNumber(web3.eth.gasPrice);
var gasUsed = new BigNumber(result.receipt.gasUsed);
var gasCost = gasPrice.times(gasUsed);
console.log("gasPrice : " + gasPrice);
console.log("gasUsed : " + gasUsed);
console.log("gasCost : " + gasCost);
console.log("initial : " + initial);
console.log("initial-gasCost: " + initial.minus(gasCost));
console.log("final : " + final);
console.log("unaccounted : " + initial.minus(gasCost).minus(final));
});
});
});
The test above produces the following output:
gasPrice : 20000000000
gasUsed : 21478
gasCost : 429560000000000
initial : 100000000000000000000
initial-gasCost: 99999570440000000000
final : 99997852200000000000
unaccounted : 1718240000000000
I expected that the call to the MinTest.run function of the contract to cause accounts[1] to be debited an amount exactly equal to gasPrice*gasUsed, but that isn't the case in this example. There is a an additional 1718240000000000 wei debited that I can't account for. Why is an additional 1718240000000000 wei debited here?
web3.eth.gasPrice is not the price specified in your transaction call. From the docs:
This property is read only and returns the current gas price. The gas
price is determined by the x latest blocks median gas price.
It is used to tell you what others are paying so you can dynamically determine the "going rate". You would use this if you want to change the gas price for your transactions over time. I'm guessing testrpc just has this set at 20000000000.
On the other hand, when you don't specify a gasPrice in your transaction call, it defaults to 10000000000. Below is an updated test case with the gasPrice passed in and the output (I used 15 Gwei for my test).
contract('Minimum Test', function (accounts) {
it("min test", function () {
var initial = web3.eth.getBalance(accounts[1]);
var final;
var gasPrice = new BigNumber(15000000000);
return MinTest.deployed().then(function(instance) {
return instance.run({from: accounts[1], gasPrice: gasPrice});
}).then(function(result) {
final = web3.eth.getBalance(accounts[1]);
var gasUsed = new BigNumber(result.receipt.gasUsed);
var gasCost = gasPrice.times(gasUsed);
console.log("gasPrice : " + gasPrice);
console.log("gasUsed : " + gasUsed);
console.log("gasCost : " + gasCost);
console.log("initial : " + initial);
console.log("initial-gasCost: " + initial.minus(gasCost));
console.log("final : " + final);
console.log("unaccounted : " + initial.minus(gasCost).minus(final));
});
});
});
Contract: Minimum Test
gasPrice : 15000000000
gasUsed : 21431
gasCost : 321465000000000
initial : 100000000000000000000
initial-gasCost: 99999678535000000000
final : 99999678535000000000
unaccounted : 0
√ min test (773ms)
1 passing (922ms)
EDIT - The web3js docs DO indeed say the default value for gasPrice is supposed to be the same:
gasPrice: Number|String|BigNumber - (optional, default:
To-Be-Determined) The price of gas for this transaction in wei,
defaults to the mean network gas price.
It may be a bug in Truffle then. In any case, if you pass in your own gas price, the numbers work out.
Related
I'm using walletconnect/metamask as wallets, and web3Provider as provider for my "buy tokens" function.
I'm trying to use the populateTransaction.METHOD_NAME to make a transaction from my wallet to the contracts method (with BNB transfer).
I'm trying to transfer some BNB's to contract with calling the contracts "BuyTokensForBnb" function (Like we can do it with Remix IDE.)
But I keep running into errors :/
const purchaseTokens2 = async () => {
const HODLER = await new ethers.Contract(address, abi, web3providerState);
const tx = [{
nonce: "0x00", // ignored by MetaMask
to: HODLER.address, // Required except during contract publications. (LINK CONTRACT ADDRESS MAINNET/TESTNET)
value: "0xde0b6b3a7640000", // Only required to send ether to the recipient from the initiating external account.
data: '',
// Optional, but used for defining smart contract creation and interaction.
chainId: "0x61" // Used to prevent transaction reuse across blockchains. Auto filled by MetaMask.
}];
let resp = HODLER.populateTransaction.buyTokensForBnb(0, tx)
const signer = await web3providerState.getSigner();
console.log("Non signer", web3providerState);
console.log("Signer, ", signer);
console.log("Account:", await signer.getAddress());
let tx2 = await signer.sendTransaction(tx);
let signature = await signer.signMessage("Hello world");
console.log(tx2, signature);
}
BuyTokensForBnb function
function buyTokensForBnb() public payable {
require(msg.value > minbuy, "MINIMUM PURCHASE IS 0.01 BNB");
require(msg.value <= maxbuy, "MAX BUY IS 10 BNB");
require(getPresaleState() == 5 || getPresaleState() == 4, "PRESALE IS NOT RUNNING(COULD BE CANCELLED OR ENDED, OR FINALIZED)");
if(block.timestamp > endDate || block.timestamp < startDate) {
revert("You can't buy this token yet or Presale had already ended, tokens cannot be purchased any longer.");
} else {
if(getPresaleState() == 4) {
//WHITELISTED PRESALE
PresaleWallet storage presaleWallet = presaleWallets[msg.sender];
if(presaleWallet.whitelisted == false) {
if(block.timestamp >= whitelistedEndDate) {
presaleState = PresaleState.LIVE;
generateReceiptAndUserPurchasedTokens(msg.sender, msg.value);
} else {
revert('REFUNDED, YOUR WALLET IS NOT WHITELISTED');
}
} else {
//make purchase, update tokens.
generateReceiptAndUserPurchasedTokens(msg.sender, msg.value);
}
} else if(getPresaleState() == 5) {
generateReceiptAndUserPurchasedTokens(msg.sender, msg.value);
} else {
revert('REFUNDED, PRESALE IS NOT LIVE OR IN WHITELISTED MODE');
}
}
}
I am trying to insert array in my firebase collection from cloud function. I need to have multiple lines in one document so for each line i am inserting an array. Please check my attached screenshot where you can see line0 , same way i need to have Line1,Line2,Line3..,Line n in the same document.
for line0 i am passing array from code like below and its working fine.
admin.firestore().collection("qbContestWinners").add(
{
'cmpientryid': context.params.processId,
'qbid': '',
'qbsyncdate': '',
'qbsyncstatus': 'pending',
'Line0':
{
id: "0",
description: 'PRIZE AMOUNT',
amount: 1000,
accountrefid: contestresultData.qbcontestid,
accountrefname: contestresultData.qbcontestname,
contestresultId: context.params.processId,
},
})
when i am looping through data i am getting from another table , i am not able to generate proper JSON to insert.
below is how i am looping and creating JSON after getting data from another table.
i = 1;
admin.firestore().collection("results").where('cid', '==', 'LKRRk2XXXXXXXX')
.orderBy("rank", "asc").get().then(snapshots =>
{
snapshots.forEach(doc =>
{
const contestresultId = doc.id;
const prizeAmount = doc.data().prizeamt;
const userId = doc.data().userid;
const lineNum = "Line" + i;
console.log("new line numner is: ", lineNum);
console.log(`lineNum? ${lineNum}`);
const linetxt = "Line" + String(i);
const insertData = "{"+linetxt +
":{id:'" + i +
"', description: 'PRIZE AMOUNT'"+
", amount:" + prizeAmount + "," +
"accountrefid:"+ contestresultData.qbcontestid +","+
"accountrefname:'" +contestresultData.qbcontestname +"',"+
"contestresultId:'" + contestresultId +"'," +
"},}"
const finalInsert = JSON.stringify(insertData);
const finalJSON = JSON.parse(finalInsert);
admin.firestore().collection("qbContestWinners").doc(mainID).set(
finalInsert.toJSON(),
{
merge: true
});
i= i+1;
});
});
using this code i am getting error
finalInsert.toJSON is not a function
Actually, the Line0 field is a map and not an Array, see this doc for more details.
So, if you want to create similar fields (Line1, Line2, ...), you simply need to pass a JavaScript Object to the set() method, as follows:
snapshots.forEach(doc => {
const contestresultId = doc.id;
const prizeAmount = doc.data().prizeamt;
const userId = doc.data().userid;
const lineNum = "Line" + i;
console.log("new line numner is: ", lineNum);
console.log(`lineNum? ${lineNum}`);
const lineObj = {
id: i,
description: 'PRIZE AMOUNT',
accountrefid: contestresultData.qbcontestid, //Not sure if you have defined contestresultData somewhere...
//...
}
const dataObj = {};
dataObj["Line" + i] = lineObj // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors
admin.firestore().collection("qbContestWinners").doc(mainID).set(dataObj, {merge: true});
i= i+1;
});
HOWEVER, note that you must return a promise that resolves when all the asynchronous work in your Cloud Function is complete (i.e. call to the Firestore set() method).
This is explained in the official Firebase video series, watch in particular the three videos titled "Learn JavaScript Promises".
Since you are calling several times the set() method in a forEach loop, you need to use Promise.all() in order to return a Promise when all these parallel calls to the set() method are completed.
The following should do the trick:
let i = 1;
return admin.firestore().collection("results") // <-- See the return here
.where('cid', '==', 'LKRRk2XXXXXXXX')
.orderBy("rank", "asc").get()
.then(snapshots => {
const promises = [];
snapshots.forEach(doc => {
const contestresultId = doc.id;
const prizeAmount = doc.data().prizeamt;
const userId = doc.data().userid;
const lineNum = "Line" + i;
const lineObj = {
id: i,
description: 'PRIZE AMOUNT',
accountrefid: contestresultData.qbcontestid,
//...
}
const dataObj = {};
dataObj[lineNum] = lineObj;
promises.push(admin.firestore().collection("qbContestWinners").doc(mainID).set(dataObj, {merge: true}));
i= i+1;
});
return Promise.all(promises) // <-- See the return here
});
A last remark: if mainID keeps the same value in the snapshots.forEach loop, you may adopt a totally different approach, consisting in building a JavaScript object with several LineXX properties and call the set() method only once. Since you didn't share the entire code of your Cloud Function it is impossible to say if this approach should be used or not.
first to the error
You stringify and parse a string. The problem here seems to be the order. You have to parse a "String" and to stringify an "Object". The result won't have a toJSON Method as well, but u can just stringify the Object to get a json.
the second thing
Why do you use a string to create your object? You shouldn't. Just use an object.
the third thing
You should not use Objects as Arrays. Not even in firebase.
Just use arrays. Example:
[Line0Object, Line1Object, ...]
Hint: If your array can work as its own collection. Just use a SubCollection. This might fit your needs.
I tried the Simple Open Auction example (https://solidity.readthedocs.io/en/v0.4.24/solidity-by-example.html#simple-open-auction) from the Solidity docs with ganache-cli and truffle. In migrations/2_deploy_contracts.js I set up:
var SimpleAuction = artifacts.require("./SimpleAuction.sol");
module.exports = function(deployer) {
deployer.deploy(SimpleAuction, 300, "0xe6ebc74aa685527a83c9e0df01b21acf0a1e8286");
};
With "0xe6ebc74aa685527a83c9e0df01b21acf0a1e8286" the address of account 1 in ganache.
In truffle console, I executed the following commands to simulate an auction (and I waited 300 seconds before calling auction.auctionEnd(), which succeeded):
auction = SimpleAuction.at(SimpleAuction.address)
account1 = web3.eth.accounts[1]
account2 = web3.eth.accounts[2]
account3 = web3.eth.accounts[3]
account4 = web3.eth.accounts[4]
auction.bid({from: account2, value: web3.toWei(10, "ether")})
auction.bid({from: account3, value: web3.toWei(13, "ether")})
auction.bid({from: account4, value: web3.toWei(15, "ether")})
auction.withdraw({from: account2})
auction.withdraw({from: account3})
auction.auctionEnd()
web3.fromWei(web3.eth.getBalance(account1).toString(), "ether")
web3.fromWei(web3.eth.getBalance(account2).toString(), "ether")
web3.fromWei(web3.eth.getBalance(account3).toString(), "ether")
web3.fromWei(web3.eth.getBalance(account4).toString(), "ether")
After this, the balances are:
100
99.9936296
99.9945537
84.9945537
Account 4 has won the auction and paid 15 eth, but I expected account 1 to have a balance of 115 eth, because this account is the beneficiary. I suppose the example code, which I copied verbatim, doesn't have a bug, so what am I doing wrong here?
The contract code is:
pragma solidity ^0.4.22;
contract SimpleAuction {
// Parameters of the auction. Times are either
// absolute unix timestamps (seconds since 1970-01-01)
// or time periods in seconds.
address public beneficiary;
uint public auctionEnd;
// Current state of the auction.
address public highestBidder;
uint public highestBid;
// Allowed withdrawals of previous bids
mapping(address => uint) pendingReturns;
// Set to true at the end, disallows any change
bool ended;
// Events that will be fired on changes.
event HighestBidIncreased(address bidder, uint amount);
event AuctionEnded(address winner, uint amount);
// The following is a so-called natspec comment,
// recognizable by the three slashes.
// It will be shown when the user is asked to
// confirm a transaction.
/// Create a simple auction with `_biddingTime`
/// seconds bidding time on behalf of the
/// beneficiary address `_beneficiary`.
constructor(
uint _biddingTime,
address _beneficiary
) public {
beneficiary = _beneficiary;
auctionEnd = now + _biddingTime;
}
/// Bid on the auction with the value sent
/// together with this transaction.
/// The value will only be refunded if the
/// auction is not won.
function bid() public payable {
// No arguments are necessary, all
// information is already part of
// the transaction. The keyword payable
// is required for the function to
// be able to receive Ether.
// Revert the call if the bidding
// period is over.
require(
now <= auctionEnd,
"Auction already ended."
);
// If the bid is not higher, send the
// money back.
require(
msg.value > highestBid,
"There already is a higher bid."
);
if (highestBid != 0) {
// Sending back the money by simply using
// highestBidder.send(highestBid) is a security risk
// because it could execute an untrusted contract.
// It is always safer to let the recipients
// withdraw their money themselves.
pendingReturns[highestBidder] += highestBid;
}
highestBidder = msg.sender;
highestBid = msg.value;
emit HighestBidIncreased(msg.sender, msg.value);
}
/// Withdraw a bid that was overbid.
function withdraw() public returns (bool) {
uint amount = pendingReturns[msg.sender];
if (amount > 0) {
// It is important to set this to zero because the recipient
// can call this function again as part of the receiving call
// before `send` returns.
pendingReturns[msg.sender] = 0;
if (!msg.sender.send(amount)) {
// No need to call throw here, just reset the amount owing
pendingReturns[msg.sender] = amount;
return false;
}
}
return true;
}
/// End the auction and send the highest bid
/// to the beneficiary.
function auctionEnd() public {
// It is a good guideline to structure functions that interact
// with other contracts (i.e. they call functions or send Ether)
// into three phases:
// 1. checking conditions
// 2. performing actions (potentially changing conditions)
// 3. interacting with other contracts
// If these phases are mixed up, the other contract could call
// back into the current contract and modify the state or cause
// effects (ether payout) to be performed multiple times.
// If functions called internally include interaction with external
// contracts, they also have to be considered interaction with
// external contracts.
// 1. Conditions
require(now >= auctionEnd, "Auction not yet ended.");
require(!ended, "auctionEnd has already been called.");
// 2. Effects
ended = true;
emit AuctionEnded(highestBidder, highestBid);
// 3. Interaction
beneficiary.transfer(highestBid);
}
}
When I call auction.auctionEnd() before the 300 seconds, I get an exception "Auction not yet ended", which is what it is supposed to do:
truffle(development)> auction.auctionEnd()
Error: VM Exception while processing transaction: revert Auction not yet ended.
at XMLHttpRequest._onHttpResponseEnd (/usr/local/lib/node_modules/truffle/build/webpack:/~/xhr2/lib/xhr2.js:509:1)
at XMLHttpRequest._setReadyState (/usr/local/lib/node_modules/truffle/build/webpack:/~/xhr2/lib/xhr2.js:354:1)
at XMLHttpRequestEventTarget.dispatchEvent (/usr/local/lib/node_modules/truffle/build/webpack:/~/xhr2/lib/xhr2.js:64:1)
at XMLHttpRequest.request.onreadystatechange (/usr/local/lib/node_modules/truffle/build/webpack:/~/web3/lib/web3/httpprovider.js:128:1)
at /usr/local/lib/node_modules/truffle/build/webpack:/packages/truffle-provider/wrapper.js:134:1
at /usr/local/lib/node_modules/truffle/build/webpack:/~/web3/lib/web3/requestmanager.js:86:1
at Object.InvalidResponse (/usr/local/lib/node_modules/truffle/build/webpack:/~/web3/lib/web3/errors.js:38:1)
When I call it after 300 seconds, I get a transaction and an event AuctionEnded, so it looks like the now >= auctionEnd condition is met:
truffle(development)> auction.auctionEnd()
{ tx: '0x480208cd6c4ac3580e7dcc3aa7e64cd0e7b5e11d5bea75e4769b554767158e35',
receipt:
{ transactionHash: '0x480208cd6c4ac3580e7dcc3aa7e64cd0e7b5e11d5bea75e4769b554767158e35',
transactionIndex: 0,
blockHash: '0xfde6956d9c7d6e99235606b70e11161965ddf832063e572a87df00c14484e5a1',
blockNumber: 12,
gasUsed: 76921,
cumulativeGasUsed: 76921,
contractAddress: null,
logs: [ [Object] ],
status: '0x1',
logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000100000000040000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' },
logs:
[ { logIndex: 0,
transactionIndex: 0,
transactionHash: '0x480208cd6c4ac3580e7dcc3aa7e64cd0e7b5e11d5bea75e4769b554767158e35',
blockHash: '0xfde6956d9c7d6e99235606b70e11161965ddf832063e572a87df00c14484e5a1',
blockNumber: 12,
address: '0xef0a6e95779240516e8a05039f97968f727c4f18',
type: 'mined',
event: 'AuctionEnded',
args: [Object] } ] }
Hopefully this can help you. I think you missed time travel part.
// test/SimpleAuction.js
const sa = artifacts.require("SimpleAuction");
contract('SimpleAuction', async function(accounts) {
/**
* TimeTravel function - can move this to helper file
* https://www.reddit.com/r/ethdev/comments/6n65ar/using_testrpc_and_truffles_built_in_js_tests_how/dk7357l/
*/
const timeTravel = function (time) {
return new Promise((resolve, reject) => {
web3.currentProvider.sendAsync({
jsonrpc: "2.0",
method: "evm_increaseTime",
params: [time], // 86400 is num seconds in day
id: new Date().getTime()
}, (err, result) => {
if(err){ return reject(err) }
return resolve(result)
});
})
}
var sa_instance;
var highest_bid = 2; // ether
var bob = accounts[1];
var alice = accounts[2];
var john = accounts[3];
var beneficiary = accounts[accounts.length - 1]; // last account in Ganache
var beneficiary_initial_balance;
before(async function() {
sa_instance = await sa.new(300, beneficiary);
var bib_balance_wei = await web3.eth.getBalance(beneficiary);
var bib_balance_eth = web3.fromWei(web3.toDecimal(bib_balance_wei), "ether");
beneficiary_initial_balance = parseInt(bib_balance_eth);
})
it("Bob bids with 1 ETH", async function() {
await sa_instance.bid({from: bob, value: web3.toWei(1, "ether")})
var sa_balance_wei = await web3.eth.getBalance(sa_instance.address);
var sa_balance_eth = web3.fromWei(web3.toDecimal(sa_balance_wei), "ether");
assert.equal(sa_balance_eth, 1);
})
it("Alice bids with 2 ETH", async function() {
await sa_instance.bid({from: alice, value: web3.toWei(highest_bid, "ether")})
var sa_balance_wei = await web3.eth.getBalance(sa_instance.address);
var sa_balance_eth = web3.fromWei(web3.toDecimal(sa_balance_wei), "ether");
assert.equal(sa_balance_eth, 3);
})
it("Bob can withdraw his 1 ETH back", async function() {
await sa_instance.withdraw({from: bob})
var sa_balance_wei = await web3.eth.getBalance(sa_instance.address);
var sa_balance_eth = web3.fromWei(web3.toDecimal(sa_balance_wei), "ether");
assert.equal(sa_balance_eth, highest_bid);
})
it("Anyone can end the auction when it's due", async function() {
await timeTravel(300);
await sa_instance.auctionEnd({from: john});
})
it("Contract balance left with 0 ETH", async function() {
var sa_balance_wei = await web3.eth.getBalance(sa_instance.address);
var sa_balance_eth = web3.fromWei(web3.toDecimal(sa_balance_wei), "ether");
assert.equal(sa_balance_eth, 0);
})
it("And beneficiary is now 2 ETH richer", async function() {
var bib_balance_wei = await web3.eth.getBalance(beneficiary);
var bib_balance_eth = web3.fromWei(web3.toDecimal(bib_balance_wei), "ether");
var expected_new_beneficiary_balance = beneficiary_initial_balance + highest_bid;
assert.equal(expected_new_beneficiary_balance, bib_balance_eth);
})
})
Did auctionEnd() succeed?
Most likely require(now >= auctionEnd) is not satisfied thus auctionEnd() failed.
Update: Debugged the contract myself, the transfer is successful. Beneficiary address is: 0xe6ebc74aa685527a83c9e0df01b21acf0a1e8286 not account1. So the contract works as expected, there is no problem. Check the balance of beneficiary address, you will see it has 15 ether.
Clarification:
This is the deployment instruction:
module.exports = function(deployer) {
deployer.deploy(SimpleAuction, 300, "0xe6ebc74aa685527a83c9e0df01b21acf0a1e8286");
};
So you set beneficiary address as: 0xe6ebc74aa685527a83c9e0df01b21acf0a1e8286.
And this is the transfer instruction inside auctionEnd():
beneficiary.transfer(highestBid);
So after the execution, if you run the following command you will see that beneficiary has 15 ether.
web3.eth.getBalance("0xe6ebc74aa685527a83c9e0df01b21acf0a1e8286")
BigNumber { s: 1, e: 19, c: [ 150000 ] }
How do a format my json data and/or change my function so that it gets stored as columns in Azure table storage?
I am sending a json string to the IoT hub:
{"ts":"2017-03-31T02:14:36.426Z","timeToConnect":"78","batLevel":"83.52","vbat":"3.94"}
I run the sample function (in the Azure Function App module) to transfer the data from the IoT hub into my storage account:
'use strict';
// This function is triggered each time a message is revieved in the IoTHub.
// The message payload is persisted in an Azure Storage Table
var moment = require('moment');
module.exports = function (context, iotHubMessage) {
context.log('Message received: ' + JSON.stringify(iotHubMessage));
context.bindings.deviceData = {
"partitionKey": moment.utc().format('YYYYMMDD'),
"rowKey": moment.utc().format('hhmmss') + process.hrtime()[1] + '',
"message": JSON.stringify(iotHubMessage)
};
context.done();
};
But in my storage table, it shows up as a single string rather than getting split into columns (as seen in the storage explorer.
How do I get it into columns for ts, timeToConnect, batLevel, and vbat?
In case anyone is looking for a solution in c#:
private static async Task ProcessMessage(string message, DateTime enqueuedTime)
{
var deviceData = JsonConvert.DeserializeObject<JObject>(message);
var dynamicTableEntity = new DynamicTableEntity();
dynamicTableEntity.RowKey = enqueuedTime.ToString("yyyy-MM-dd HH:mm:ss.fff");
foreach (KeyValuePair<string, JToken> keyValuePair in deviceData)
{
if (keyValuePair.Key.Equals("MyPartitionKey"))
{
dynamicTableEntity.PartitionKey = keyValuePair.Value.ToString();
}
else if (keyValuePair.Key.Equals("Timestamp")) // if you are using a parameter "Timestamp" it has to be stored in a column named differently because the column "Timestamp" will automatically be filled when adding a line to table storage
{
dynamicTableEntity.Properties.Add("MyTimestamp", EntityProperty.CreateEntityPropertyFromObject(keyValuePair.Value));
}
else
{
dynamicTableEntity.Properties.Add(keyValuePair.Key, EntityProperty.CreateEntityPropertyFromObject(keyValuePair.Value));
}
}
CloudStorageAccount storageAccount = CloudStorageAccount.Parse("myStorageConnectionString");
CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
CloudTable table = tableClient.GetTableReference("myTableName");
table.CreateIfNotExists();
var tableOperation = TableOperation.Insert(dynamicTableEntity);
await table.ExecuteAsync(tableOperation);
}
How do I get it into columns for ts, timeToConnect, batLevel, and
vbat?
To get these attributes as separate columns in table, you would need to defalte the object and store them separately (currently you are just converting the entire object into string and storing that string).
Please try the following code:
module.exports = function (context, iotHubMessage) {
context.log('Message received: ' + JSON.stringify(iotHubMessage));
var deviceData = {
"partitionKey": moment.utc().format('YYYYMMDD'),
"rowKey": moment.utc().format('hhmmss') + process.hrtime()[1] + '',
};
Object.keys(iotHubMessage).forEach(function(key) {
deviceData[key] = iotHubMessage[key];
});
context.bindings.deviceData = deviceData;
context.done();
};
Please note that I have not tried to execute this code so it may contain some errors.
I am building a bot in Gupshup with Api.ai integration. I have an agent in Api.ai with several intents and each of them linked through contexts(input & output contexts). When I use the following code to call Api.ai, the first intent is called and I get the reply. However when the second message is given, the bot takes it as a completely new message, without identifying its relation with first.
How can I solve this issue? Kindly help
function MessageHandler(context, event) {
// var nlpToken = "xxxxxxxxxxxxxxxxxxxxxxx";//Your API.ai token
// context.sendResponse(JSON.stringify(event));
sendMessageToApiAi({
message : event.message,
sessionId : new Date().getTime() +'api',
nlpToken : "3626fe2d46b64cf8a9c0d3bee99a9sb3",
callback : function(res){
//Sample response from apiai here.
context.sendResponse(JSON.parse(res).result.fulfillment.speech);
}
},context)
}
function sendMessageToApiAi(options,botcontext) {
var message = options.message; // Mandatory
var sessionId = options.sessionId || ""; // optinal
var callback = options.callback;
if (!(callback && typeof callback == 'function')) {
return botcontext.sendResponse("ERROR : type of options.callback should be function and its Mandatory");
}
var nlpToken = options.nlpToken;
if (!nlpToken) {
if (!botcontext.simpledb.botleveldata.config || !botcontext.simpledb.botleveldata.config.nlpToken) {
return botcontext.sendResponse("ERROR : token not set. Please set Api.ai Token to options.nlpToken or context.simpledb.botleveldata.config.nlpToken");
} else {
nlpToken = botcontext.simpledb.botleveldata.config.nlpToken;
}
}
var query = '?v=20150910&query='+ encodeURIComponent(message) +'&sessionId='+sessionId+'&timezone=Asia/Calcutta&lang=en '
var apiurl = "https://api.api.ai/api/query"+query;
var headers = { "Authorization": "Bearer " + nlpToken};
botcontext.simplehttp.makeGet(apiurl, headers, function(context, event) {
if (event.getresp) {
callback(event.getresp);
} else {
callback({})
}
});
}
/** Functions declared below are required **/
function EventHandler(context, event) {
if (!context.simpledb.botleveldata.numinstance)
context.simpledb.botleveldata.numinstance = 0;
numinstances = parseInt(context.simpledb.botleveldata.numinstance) + 1;
context.simpledb.botleveldata.numinstance = numinstances;
context.sendResponse("Thanks for adding me. You are:" + numinstances);
}
function HttpResponseHandler(context, event) {
// if(event.geturl === "http://ip-api.com/json")
context.sendResponse(event.getresp);
}
function DbGetHandler(context, event) {
context.sendResponse("testdbput keyword was last get by:" + event.dbval);
}
function DbPutHandler(context, event) {
context.sendResponse("testdbput keyword was last put by:" + event.dbval);
}
The sessionId has to be fixed for a user. There are two ways you can do this in the Gupshup bot code -
Use the unique userID which is sent to the bot for every user.
To get this value you can use -
event.senderobj.channelid
But this value is dependent on how different messaging channels provides it and api.ai has a limit of 36 characters.
Sample code -
function MessageHandler(context, event) {
sendMessageToApiAi({
message : event.message,
sessionId : event.senderobj.channelid,
nlpToken : "3626fe2d46b64cf8a9c0d3bee99a9sb3",
callback : function(res){
//Sample response from apiai here.
context.sendResponse(JSON.parse(res).result.fulfillment.speech);
}
},context)
}
Generate a unique sessionId for each user and store it in the database to utilise it. In the below sample , I am storing the sessionId at roomleveldata which is the default persistance provided by Gupshup, to know more check this guide.
Sample code -
function MessageHandler(context, event) {
sendMessageToApiAi({
message : event.message,
sessionId : sessionId(context),
nlpToken : "84c813598fb34dc5b1f3e1c695e49811",
callback : function(res){
//Sample response from apiai here.
context.sendResponse(JSON.stringify(res));
}
},context)
}
function sessionId(context){
var userSession = context.simpledb.roomleveldata.sessionID;
if(!userSession){
userSession = new Date().getTime() +'api';
context.simpledb.roomleveldata.sessionID = userSession;
return userSession;
}else{
return userSession;
}
}
Remember that sessionId should not exceed 36 characters.
Suresh,
It seems you generate new session id for every request:
new Date().getTime() +'api'
But if you want to make contexts work it must be one fixed value for all requests belonging to one user. For example, you could use some global variable for it.