Node.js azure-storage TableService has no methods - javascript

I've been trying to connect to my Azure storage account, but I'm having some problems with the azure-storage module. Specifically, once I create a TableService object, the object only has a filter method on it. Two methods I've tried have been queryTables and createTableIfNotExist. For example, createTableIfNotExistreturns "TypeError: aztd.createTableIfNotExistis not a function". Source code is below.
var azure = require('azure-storage');
var aztd = azure.createTableService();
var azseg = azure.TableUtilities.entityGenerator;
console.log("AZSEG " + Object.getOwnPropertyNames(azseg).filter(function (p) { return typeof azseg[p] === 'function'; }));
console.log("AZTD " + Object.getOwnPropertyNames(aztd).filter(function (p) { return typeof aztd[p] === 'function'; }));
aztd.createTableIfNotExist('table1', function (e, result, res) {
if (result) console.log('Table created');
});
I'm not getting any additional errors aside from the function not found. The console log returns the functions for both variables:
AZSEG Entity,Int32,Int64,Binary,Boolean,String,Guid,Double,DateTime
AZTD filter
I can see the entityGenerator is created fine, but am I missing anything for the TableService?

Actually, the function name should be createTableIfNotExists, and it seems you have typed an invalid function name.
Also you can refer to source code of azure-storage-node on github to get all functions's info.

Related

MongoDB - Mongoose - TypeError: save is not a function

I am attempting to perform an update to a MongoDB document (using mongoose) by first using .findById to get the document, then updating the fields in that document with new values. I am still a bit new to this so I used a tutorial to figure out how to get it working, then I have been updating my code for my needs. Here is the tutorial: MEAN App Tutorial with Angular 4. The original code had a schema defined, but my requirement is for a generic MongoDB interface that will simply take whatever payload is sent to it and send it along to MongoDB. The original tutorial had something like this:
exports.updateTodo = async function(todo){
var id = todo.id
try{
//Find the old Todo Object by the Id
var oldTodo = await ToDo.findById(id);
}catch(e){
throw Error("Error occured while Finding the Todo")
}
// If no old Todo Object exists return false
if(!oldTodo){
return false;
}
console.log(oldTodo)
//Edit the Todo Object
oldTodo.title = todo.title
oldTodo.description = todo.description
oldTodo.status = todo.status
console.log(oldTodo)
try{
var savedTodo = await oldTodo.save()
return savedTodo;
}catch(e){
throw Error("And Error occured while updating the Todo");
}
}
However, since I don't want a schema and want to allow anything through, I don't want to assign static values to specific field names like, title, description, status, etc. So, I came up with this:
exports.updateData = async function(update){
var id = update.id
// Check the existence of the query parameters, If they don't exist then assign a default value
var dbName = update.dbName ? update.dbName : 'test'
var collection = update.collection ? update.collection : 'testing';
const Test = mongoose.model(dbName, TestSchema, collection);
try{
//Find the existing Test object by the Id
var existingData = await Test.findById(id);
}catch(e){
throw Error("Error occurred while finding the Test document - " + e)
}
// If no existing Test object exists return false
if(!existingData){
return false;
}
console.log("Existing document is " + existingData)
//Edit the Test object
existingData = JSON.parse(JSON.stringify(update))
//This was another way to overwrite existing field values, but
//performs a "shallow copy" so it's not desireable
//existingData = Object.assign({}, existingData, update)
//existingData.title = update.title
//existingData.description = update.description
//existingData.status = update.status
console.log("New data is " + existingData)
try{
var savedOutput = await existingData.save()
return savedOutput;
}catch(e){
throw Error("An error occurred while updating the Test document - " + e);
}
}
My original problem with this was that I had a lot of issues getting the new values to overwrite the old ones. Now that that's been solved, I am getting the error of "TypeError: existingData.save is not a function". I am thinking the data type changed or something, and now it is not being accepted. When I uncomment the static values that were in the old tutorial code, it works. This is further supported by my console logging before and after I join the objects, because the first one prints the actual data and the second one prints [object Object]. However, I can't seem to figure out what it's expecting. Any help would be greatly appreciated.
EDIT: I figured it out. Apparently Mongoose has its own data type of "Model" which gets changed if you do anything crazy to the underlying data by using things like JSON.stringify. I used Object.prototype.constructor to figure out the actual object type like so:
console.log("THIS IS BEFORE: " + existingData.constructor);
existingData = JSON.parse(JSON.stringify(update));
console.log("THIS IS AFTER: " + existingData.constructor);
And I got this:
THIS IS BEFORE: function model(doc, fields, skipId) {
model.hooks.execPreSync('createModel', doc);
if (!(this instanceof model)) {
return new model(doc, fields, skipId);
}
Model.call(this, doc, fields, skipId);
}
THIS IS AFTER: function Object() { [native code] }
Which showed me what was actually going on. I added this to fix it:
existingData = new Test(JSON.parse(JSON.stringify(update)));
On a related note, I should probably just use the native MongoDB driver at this point, but it's working, so I'll just put it on my to do list for now.
You've now found a solution but I would suggest using the MongoDB driver which would make your code look something along the lines of this and would make the origional issue disappear:
// MongoDB Settings
const MongoClient = require(`mongodb`).MongoClient;
const mongodb_uri = `mongodb+srv://${REPLACE_mongodb_username}:${REPLACE_mongodb_password}#url-here.gcp.mongodb.net/test`;
const db_name = `test`;
let db; // allows us to reuse the database connection once it is opened
// Open MongoDB Connection
const open_database_connection = async () => {
try {
client = await MongoClient.connect(mongodb_uri);
} catch (err) { throw new Error(err); }
db = client.db(db_name);
};
exports.updateData = async update => {
// open database connection if it isn't already open
try {
if (!db) await open_database_connection();
} catch (err) { throw new Error(err); }
// update document
let savedOutput;
try {
savedOutput = await db.collection(`testing`).updateOne( // .save() is being depreciated
{ // filter
_id: update.id // the '_id' might need to be 'id' depending on how you have set your collection up, usually it is '_id'
},
$set: { // I've assumed that you are overwriting the fields you are updating hence the '$set' operator
update // update here - this is assuming that the update object only contains fields that should be updated
}
// If you want to add a new document if the id isn't found add the below line
// ,{ upsert: true }
);
} catch (err) { throw new Error(`An error occurred while updating the Test document - ${err}`); }
if (savedOutput.matchedCount !== 1) return false; // if you add in '{ upsert: true }' above, then remove this line as it will create a new document
return savedOutput;
}
The collection testing would need to be created before this code but this is only a one-time thing and is very easy - if you are using MongoDB Atlas then you can use MongoDB Compass / go in your online admin to create the collection without a single line of code...
As far as I can see you should need to duplicate the update object. The above reduces the database calls from 2 to one and allows you to reuse the database connection, potentially anywhere else in the application which would help to speed things up. Also don't store your MongoDB credentials directly in the code.

CouchDB Document Update Handlers: Javascript

I am trying to create a generic document update handler.
I am using:
function(doc, req) {var field = req.query.field; var value =
req.query.value; var message = 'set '+field+' to '+value; doc[field] =
value; return [doc, message]; }
This works ok with simple json but not with a nested object such as
"abc":{"ax":"one", "by":"two" ...}
my curl command is:
curl -X PUT 'http://127.0.0.1:5984/db/_design/updatehandler/_update/inplace/id?field=abc.ax&value=three'
The result is a new field is created and the existing abc:{ax:one} is left
untouched.
With a simpler example:
if I have: "xyz":"five"
curl -X PUT 'http://127.0.0.1:5984/db/_design/updatehandler/_update/inplace/id?field=xyz&value=ten'
... works correctly.
I have not yet tried the generic process on "pqr":[s, t, u] yet but I guess
this may require a different design modification as well.
Ideally one wants something that works in at least the abovementioned three
cases if possible, as long as it is not too complex for it not to be worth
the effort.
Could someone possibly kindly help here or refer me to some javascript examples please.
Many thanks.
John
function (doc, req) {
function merge(nDoc,oDoc ) {
for (var f in nDoc) {
var tmpNewDoc = nDoc[f],
tmpDoc = oDoc[f];
var type = typeof(tmpNewDoc);
if (type === 'object' && tmpNewDoc.length === undefined && tmpDoc !== undefined) merge(tmpNewDoc, tmpDoc);
else oDoc[f] = tmpNewDoc;
}
}
if (!doc) {
return [null, toJSON({
error: 'not_found',
reason: 'No document were found with the specified ID or an incorrect method was used.'
})];
}
try {
var newDoc = JSON.parse(req.body);
merge(newDoc, doc);
}
catch (e) {
return [null, ToJSON({
error: 'bad_request',
reason: 'Invalid json or processing error'
})];
}
return [doc, toJSON({
doc: doc,
ok: true
})];
}"
}
Simply pass the new document to this handler. It will merge the new values to it (warning, the arrays will be overwrite). If you also want to merge array, you can either use a third party library or build your own recursive merge function.

Calling one factory within another AngularJS Ionic

I've written an ionic app with two factory one called Api that handles API calls and one called ldb(local database) that handles interaction with a local Sqlite database using the CordovaSqlite plugin.
When I try to use either factory separately they function as intended however when I try to use ldb within Apis functions it doesn't return data.
Here is my ldb code. Please note db is defined in the root scope as the sqlite database.
.factory('ldb', function($cordovaSQLite) {
function currentSite(){
return $cordovaSQLite.execute(db,"SELECT * FROM sites WHERE id=1");
}
return{
current: function(){return currentSite()}
}
}
and here is my api code
.factory('api', function($http,config,ldb) {
function getLastEvent(){
ldb.currentSite().this(function(res){
var siteid = res.rows.item(0).siteid;
var sitepin = res.rows.item(0).sitepin;
if(siteid != null && sitepin != null){
$scope.lastEvent = $http.post(config.apiURL, {siteid: siteid, sitepin: sitepin, action: 'getLastEvent'});
return true;
}
else{ return {"AlertType":"OP","Time":"00:00","Date":"No Site"};}
return {"AlertType":"OP","Time":"00:00","Date":"No Site"};
})
}
return{
lastEvent: function(){return getLastEvent()}
}
I am calling api in my controller like this.
app.lastEvent().then(function(res){
if(res.data == "Invalid Details Provided!"){$scope.lastEvent = {"AlertType":"OP","Time":"Invalid Login","Date":""};}
else if(res.data != null){ $scope.lastEvent = res.data[0];}
else{$scope.lastEvent ={"AlertType":"OP","Time":"Error","Date":""}}
})
There are some errors in your code:
in 'api' factory you wrote ldb.currentSite().this(...
instead of ldb.current().then(....
In fact the 'ldb' factory returns a function named current().
Besides, pay attention, $cordovaSQLite.execute() is an asynchronous operation so it returns a promise which has the method then() not this().
Finally you said db is defined in the "root scope": what do you mean? It is a property of $rootScope? If yes you have to reference it with $rootScope.db.

How to use an external variable in MongoDB 'where' query via Javascript?

I have a MongoDB query that searches all properties for a value defined in the search variable. It works the following way:
db.collection.findOne({
$where: function() {
var search = 'searchstring';
for (var key in this) {
if (this[key] === search) {
return true;
}
return false;
}
}
});
However, I would like to define the search variable outside the query.
But when I do so, I get an error that it is not referenced (i.e. scoping issue):
"ReferenceError: search is not defined near '[key] === search
How can I use or pass the variable to the query filter?
You can try something like this:
var searchstring = 'whatever';
var params = {};
params.$where = 'function() {' +
'var search = "' + searchstring + '";' +
'for (var key in this) {' +
'if (this[key] === search) {' +
'return true;' +
'}' +
'return false;' +
'}' +
'}';
db.collection.findOne(params);
(Stringify your function and concat with external variable)
Worked for me via mongoose
search is a variable that you define in your client, may be the shell, or any client API.
The function that you define for the $where clause, will not be executed on the client side, but on the mongodb server.
so, when the function is being interpreted in the server side and it looks for the search variable, it was never defined on the server and hence you get the error.
In your case, you want the variable search, to be replaced with its content, by the client, before being executed on the server. And this is not possible, unless you build the function content itself in the client side.
The client never really interprets anything you write inside the anonymous function. The server does. The error you see is from the server. You can validate it by firing this query and looking onto the server logs:
2015-12-22T19:03:44.011-0800 I QUERY [conn1] assertion 16722 ReferenceError:
searc is not defined
at _funcs1 (_funcs1:1:39) near 's.key === searc){retu' ns:test.t query:{ $w
here: function (){if(this.key === searc){return true}} }
There is a better way to write what you wish to achieve using the $exists operator.
var search = "title";
var find = {};
find[search] = {$exists:true};
db.collection.findOne(find);
This works, because, you build the query parameter fully on the client side, before passing it on to the findOne() method.
You can solve this problem using mongodb map reduce and scope functionality. Scope allows you to pass variables into map reduce job.
function map() {
for (var key in this) {
if (this[key] === search) {
return emit(this._id, this);
}
}
}
function reduce(key, values) {
return values[0];
}
db.collection.mapReduce(map, reduce, {
out: {inline: 1},
scope: {
search: 'searchstring'
}
}
);

Cannot access elements in object returned by Javascript Function in AngularJS

Thanks to #asgoth, I am able to use AngularJS $http service to retrieve stock prices from Yahoo as described here: Cannot read response from AngularJS $resource JSONP get from Yahoo Finance
In the "getHistoricalPrice" function, it puts the price inside an array, which is inside an object. From inside that function, I am able to access the price and write it to console.
The function returns the object to where it is called from. From there, I can successfully write the entire object out to console. However, I cannot access the elements of this object. I tried many different ways, but still cannot access the data in the object. You can see the code at http://jsfiddle.net/curt00/LTazR/2/ or below:
angular.module('app', ['ngResource']);
function AppCtrl($scope, $http, $resource) {
var historical_price = getHistoricalPrice("AAPL", 'start date is hard coded', 'end date is hard coded');
console.log("after calling historical price: ", historical_price); // historical_price is an object and all of the correct data is outputted to console here, but I cannot access its elements directly from Javascript.
for(var key in historical_price) {
console.log("key =",key); // this outputs "key = list"
}
console.log("after calling getHistoricalPrice: ", historical_price.list[0][1]); // Cannot access this as browser console gives error: TypeError: Cannot read property '1' of undefined
console.log("after calling getHistoricalPrice: ", historical_price['list'][0][1]); // Cannot access this as browser console gives error: TypeError: Cannot read property '1' of undefined
console.log("after calling getHistoricalPrice: ", historical_price[0][1]); // Cannot access this as browser console gives error: TypeError: Cannot read property '1' of undefined
function getHistoricalPrice(symbol, start, end) {
var query = 'select * from csv where url=\'http://ichart.yahoo.com/table.csv?s=' + symbol + '&a=' + '11' + '&b=' + '19' + '&c=' + '2012' + '&d=' + '11' + '&e=' + '19' + '&f=' + '2012' + '&g=d&ignore=.csv\'';
var url = 'http://query.yahooapis.com/v1/public/yql?q=' + fixedEncodeURIComponent(query) + '&format=json&callback=JSON_CALLBACK';
var histData = {};
$http.jsonp(url, {timeout: 30000}).success(function(json) {
var list = [];
var result = json.query.results.row;
result.shift(); // remove the header (columns) row
angular.forEach(result, function(row) {
list.push([(new Date(row.col0)).getTime()/1000, parseFloat(row.col4)]);
});
list.sort(function(val1, val2) {
return val1[0] - val2[0];
});
histData.list = list;
console.log('Loaded historical data',histData.list[0][1],', for ' + symbol); // This works and gives the price
});
return histData;
}
var fixedEncodeURIComponent = function(str) {
return encodeURIComponent(str).replace(/[!'()]/g, escape).replace(/\*/g, "%2A");
};
}
​
Any help or suggestions to solve this problem is greatly appreciate!
It's a matter of timing.
In lines 12-14 you are trying to access histData.list before it has been populated. This is because this code is run before the success callback to the $http.jsonp function is executed.
Any code that depends on that callback being completed must be in the callback or in a function called in the callback.
See my answer on https://stackoverflow.com/a/13967709/1916258
A great way to debug the Yahoo api is using the YQL Console: http://developer.yahoo.com/yql/console/
Info about the different posibilities (which stock info) can be found on http://www.gummy-stuff.org/Yahoo-data.htm
Edit: there was still a problem with function fixedEncodeURIComponent. It should encode quotes (") too:
var fixedEncodeURIComponent = function(str) {
return encodeURIComponent(str).replace(/[!'()]/g, escape).replace(/\*/g, "%2A").replace(/\"/g, "%22");
};
BobS is right, you aren't timing things correctly. Also you declared fixedEncodeURIComponent after you had called it. This was resulting in an immediate error when I loaded up the jsfiddle.
While you were passing the callback through to your function correctly, you weren't actually calling it. I stripped out all the post processing of the json as you have some other errors involving the query and just implemented the callback so you can see it working.
After the request is finished and you're still in the success function you need to add
if(typeof(callback) === "function"){
callback();
}
This calls that function you passed in and runs it. Here is a working jsFiddle of it:
http://jsfiddle.net/LTazR/22/
I also updated a new variable i created call output so you can see it changing.
Thanks to everybody for providing suggestions.
I solved the problem by using AngularJS' $scope variable, such as $scope.symbol[user].price. I created this variable before calling the getHistoricalPrice function and then in that function, after the result is returned from $http.jsonp, I put the value into the $scope variable, as such:
$scope.symbol[user].price = row.col4;

Categories

Resources