Consider the following exerpt from this example in the InfluxDB documentation:
const fluxObserver = {
next(row, tableMeta) {
const o = tableMeta.toObject(row)
console.log(
`${o._time} ${o._measurement} in ${o.region} (${o.sensor_id}): ${o._field}=${o._value}`
)
},
error(error) {
console.error(error)
console.log('\nFinished ERROR')
},
complete() {
console.log('\nFinished SUCCESS')
}
}
/** Execute a query and receive line table metadata and rows. */
queryApi.queryRows(fluxQuery, fluxObserver)
The fluxObserver defines a sequence of operations at different stages of execution of the query, all of which are just console.log calls. I can imagine that people want to do something similar in a way that is actually useful, like for instance return an array that contains the result of the query, instead of logging something to a console. My problem is that queryApi.queryRows does not actually return anything, so it is not possible to do something like:
...
complete() {
console.log('\nFinished SUCCESS')
return data;
}
}
/** Execute a query and receive line table metadata and rows. */
var result = queryApi.queryRows(fluxQuery, fluxObserver);
return result;
So how can I return the data collected by the fluxObserver?
Related
Via a microservice, I retrieve several packages of JSON data and spit them out onto a Vue.js-driven page. The data looks something like this:
{"data":{"getcompanies":
[
{"id":6,"name":"Arena","address":"12 Baker Street","zip":"15090"},
{"id":7,"name":"McMillan","address":null,"zip":"15090"},
{"id":8,"name":"Ball","address":"342 Farm Road","zip":"15090"}
]
}}
{"data":{"getusers":
[{"id":22,"name":"Fred","address":"Parmesean Street","zip":"15090"},
{"id":24,"name":"George","address":"Loopy Lane","zip":"15090"},
{"id":25,"name":"Lucy","address":"Farm Road","zip":"15090"}]}}
{"data":{"getdevices":
[{"id":2,"name":"device type 1"},
{"id":4,"name":"device type 2"},
{"id":5,"name":"device type 3"}]}}
...and I successfully grab them individually via code like this:
getCompanies() {
this.sendMicroServiceRequest({
method: 'GET',
url: `api/authenticated/function/getcompanies`
})
.then((response) => {
if(response.data) {
this.dataCompanies = response.data.getcompanies
} else {
console.error(response)
}
}).catch(console.error)
}
...with getUsers() and getDevices() looking respectively the same. getCompanies() returns:
[{"id":6,"name":"Arena","address":"12 Baker Street","zip":"15090"},
{"id":7,"name":"McMillan","address":null,"zip":"15090"},
{"id":8,"name":"Ball","address":"342 Farm Road","zip":"15090"}]
...which I relay to the Vue template in a table, and this works just fine and dandy.
But this is obviously going to get unwieldy if I need to add more microservice calls down the road.
What I'm looking for is an elegant way to jump past the response.data.*whatever* and get to those id-records with a re-useable call, but I'm having trouble getting there. response.data[0] doesn't work, and mapping down to the stuff I need either comes back undefined, or in bits of array. And filtering for response.data[0].id to return just the rows with ids keeps coming back undefined.
My last attempt (see below) to access the data does work, but looks like it comes back as individual array elements. I'd rather not - if possible - rebuild an array into a JSON structure. I keep thinking I should be able to just step past the next level regardless of what it's called, and grab whatever is there in one chunk, as if I read response.data.getcompanies directly, but not caring what 'getcompanies' is, or needing to reference it by name:
// the call
this.dataCompanies = this.getFullData('companies')
getFullData(who) {
this.sendMicroServiceRequest({
method: 'GET',
url: 'api/authenticated/function/get' + who,
})
.then((response) => {
if(response) {
// attempt 1 to get chunk below 'getcompanies'
Object.keys(response.data).forEach(function(prop) {
console.log(response.data[prop])
})
// attempt 2
// for (const prop in response.data) {
// console.log(response.data[prop])
// }
let output = response.data[prop] // erroneously thinking this is in one object
return output
} else {
console.error(response)
}
}).catch(console.error)
}
...outputs:
(63) [{…}, {…}, {…}] <-- *there are 63 of these records, I'm just showing the first few...*
0: {"id":6,"name":"Arena","address":"12 Baker Street","zip":"15090"}
1: {"id":7,"name":"McMillan","address":null,"zip":"15090"},
2: {"id":8,"name":"Ball","address":"342 Farm Road","zip":"15090"}...
Oh, and the return above comes back 'undefined' for some reason that eludes me at 3AM. >.<
It's one of those things where I think I am close, but not quite. Any tips, hints, or pokes in the right direction are greatly appreciated.
I feel it's better to be explicit about accessing the object. Seems like the object key is consistent with the name of the microservice function? If so:
getData(functionName) {
return this.sendMicroServiceRequest({
method: 'GET',
url: "api/authenticated/function/" + functionName
})
.then( response => response.data[functionName] )
}
getCompanies(){
this.getData("getcompanies").then(companies => {
this.dataCompanies = companies
})
}
let arrResponse = {data: ['x']};
let objResponse = {data: {getcompanies: 'x'}};
console.log(arrResponse.data[0]);
console.log(Object.values(objResponse.data)[0]);
response.data[0] would work if data was an array. To get the first-and-only element of an object, use Object.values(response.data)[0] instead. Object.values converts an object to an array of its values.
Its counterparts Object.keys and Object.entries likewise return arrays of keys and key-value tuples respectively.
Note, order isn't guaranteed in objects, so this is only predictable in your situation because data has exactly a single key & value. Otherwise, you'd have to iterate the entry tuples and search for the desired entry.
firstValue
Let's begin with a generic function, firstValue. It will get the first value of an object, if present, otherwise it will throw an error -
const x = { something: "foo" }
const y = {}
const firstValue = t =>
{ const v = Object.values(t)
if (v.length)
return v[0]
else
throw Error("empty data")
}
console.log(firstValue(x)) // "foo"
console.log(firstValue(y)) // Error: empty data
getData
Now write a generic getData. We chain our firstValue function on the end, and be careful not to add a console.log or .catch here; that is a choice for the caller to decide -
getData(url) {
return this
.sendMicroServiceRequest({ method: "GET", url })
.then(response => {
if (response.data)
return response.data
else
return Promise.reject(response)
})
.then(firstValue)
}
Now we write getCompanies, getUsers, etc -
getCompanies() {
return getData("api/authenticated/function/getcompanies")
}
getUsers() {
return getData("api/authenticated/function/getusers")
}
//...
async and await
Maybe you could spruce up getData with async and await -
async getData(url) {
const response =
await this.sendMicroServiceRequest({ method: "GET", url })
return response.data
? firstValue(response.data)
: Promise.reject(response)
}
power of generics demonstrated
We might even suggest that these get* functions are no longer needed -
async getAll() {
return {
companies:
await getData("api/authenticated/function/getcompanies"),
users:
await getData("api/authenticated/function/getusers"),
devices:
await getData("api/authenticated/function/getdevices"),
// ...
}
}
Above we used three await getData(...) requests which happen in serial order. Perhaps you want all of these requests to run in parallel. Below we will show how to do that -
async getAll() {
const requests = [
getData("api/authenticated/function/getcompanies"),
getData("api/authenticated/function/getusers"),
getData("api/authenticated/function/getdevices")
]
const [companies, users, devices] = Promise.all(requests)
return { companies, users, devices }
}
error handling
Finally, error handling is reserved for the caller and should not be attempted within our generic functions -
this.getAll()
.then(data => this.render(data)) // some Vue template
.catch(console.error)
I am trying to check in a form is the user exists in a MySQL Database with angular.
I am creating a localstorage item inside a .subscribe so if the user exists it creates a localstorage like this:
localStorage.setItem("exist","true");
but if it doesn't exist makes this:
localStorage.setItem("exist","false");
After the function I check if it is true or false, but it is null, because .subscribe makes it after all the functions. So if I check a 2nd time it makes the localstorage item correctly.
this.userService.getAutor(id)
.subscribe(
(data) => { // Successins
localStorage.setItem("exist","true");
},
(error) => { // error
localStorage.setItem("exist","false");
}
);
}
This is where I am calling the function
this.usuarioExiste(this.dniAutor);
if(localStorage.getItem('existe') == 'false'){
var expReg = /^(\d{8})([A-Z])$/;
if(expReg.test(this.dniAutor)){
this.userService.crearAutor(JSONform)
.subscribe(
(data) => { // Success
console.log("¡Bien HECHO!");
}
);
alert("Autor añadido correctamente");
}
else{
alert("Dni mal introducido")
}
After the function I check if it is true or false, but it is null
Wrong way
this.userService.getAutor(id)
.subscribe(
(data) => { // Successins
localStorage.setItem('existe',"true"); // <-- called after functionCheckIfTrueOrFalse, NO DATA in storage yet
},
(error) => { // error
localStorage.setItem('existe',"false"); // <-- called after functionCheckIfTrueOrFalse, NO DATA in storage yet
}
);
}
functionCheckIfTrueOrFalse() // <- is called before any localStorage.setItem()
Correct way
this.userService.getAutor(id)
.subscribe(
(data) => { // Successins
localStorage.setItem('existe',"true");
functionCheckIfTrueOrFalse() // <- localStorage HAS the data
},
(error) => { // error
localStorage.setItem('existe',"false");
functionCheckIfTrueOrFalse() // <- localStorage HAS the data
}
);
}
Knowledge
If you want to spend time and understand how it works under the hood, you can check this article.
In nutshell JavaScript handles async treads differently:
You get null because you are reading data from the sync Queue before you get data from the Async tread.
As Kurt mentioned, my answer needs an explanation.
So I'll do my best below:
You have to check first if the data is available when you are in the subscribe method due to the asynchronous nature of the call. And only then you access the local storage as you intend.
Note: You can also use the filter operator like so filter(data => data) to skip the if condition but only if you're not interested to access the local storage if the data doesn't exist.
this.userService.getAutor(id).subscribe(data => {
if (data) {
localStorage.setItem('existe', 'true');
} else {
localStorage.setItem('existe', 'false');
}
});
It finally worked!
I used the suggestion of 0leg and it worked!
this.userService.getAutor(id).subscribe(data => {
if (data) {
localStorage.setItem('existe', 'true');
} else {
localStorage.setItem('existe', 'false');
}
});
Many thanks to all!
Have a nice day!
I am reading a JSON object and looping through each item. I am first checking to see if the item already exists in the database and if so I want to log a message. If it doesn't already exist I want to add it.
This is working correctly however, I would like to add a callback or finish the process with process.exit();
Because the mongoose calls are asynchronous I can't put it at the end of the for loop because they haven't finished.
Whats the best way I should handle this?
function storeBeer(data) {
data.forEach((beer) => {
let beerModel = new Beer({
beer_id: beer.id,
name: beer.name,
image_url: beer.image_url
});
Beer.findOne({
'name': beer.name
}).then(function (result) {
if (result) {
console.log(`Duplicate ${result.name}`)
} else {
beerModel.save(function (err, result) {
console.log(`Saved: ${result.name}`)
});
}
});
});
}
Is there anything I should read up on to help solve this?
One means of managing asynchronous resources is through Promise objects, which are first-class objects in Javascript. This means that they can be organized in Arrays and operated on like any other object. So, assuming you want to store these beers in parallel like the question implies, you can do something like the following:
function storeBeer(data) {
// This creates an array of Promise objects, which can be
// executed in parallel.
const promises = data.map((beer) => {
let beerModel = new Beer({
beer_id: beer.id,
name: beer.name,
image_url: beer.image_url
});
return Beer.findOne({
'name': beer.name
}).then(function (result) {
if (result) {
console.log(`Duplicate ${result.name}`)
} else {
beerModel.save(function (err, result) {
console.log(`Saved: ${result.name}`)
});
}
});
);
});
return Promise.all(promises);
}
Don't forget that the storeBeer function is now asynchronous, and will need to be utilized either through a Promise chain or through async/await.
For example, to add process exit afterwards:
async function main() {
const beers = [ ... ];
await storeBeer(beer);
process.exit(0);
}
You can also modify the above example to invoke the storeBeer function within a try / catch block to exit with a different error code based on any thrown errors.
I'm trying to get the Id of the new insert so I can push the Id onto another collection.
According to this post =>
Meteor collection.insert callback to return new id and this post => Meteor collection.insert callback issues, I should be able to
return Collection.insert(obj);
and it will return the ID of the newly inserted data to my client.
Instead I'm getting an Observable like this:
{_isScalar: false}
_isScalar: false
__proto__:
constructor: f Object()
hasOwnProperty: f hasOwnProperty()
//many other object properties
The documentation seems pretty clear that I should be getting the ID in return. Is this a bug? https://docs.meteor.com/api/collections.html#Mongo-Collection-insert]
My version of meteor is 1.4.4.5...
I've been working on this issue for a few days and I've tried getting the ID many different ways, nothing I've tried results in the ID.
Here's my full code for reference:
Server:
submitStuff: function(data): string{
var loggedInUser = Meteor.user();
if (!loggedInUser || !Roles.userIsInRole(loggedInUser,['user','admin'])){
throw new Meteor.Error("M504", "Access denied");
} else {
try{
let insertedData = null;
const model: MyModel[] = [{
data.stuff //bunch of data being inserted
}];
model.forEach((obj: MyModel) => {
insertedData = Collection.insert(obj);
});
return insertedData;
} catch(e) {
throw new Meteor.Error(e + e.reason, "|Throw new error|");
}
}
},
Client:
Meteor.call('submitData', data, (error, value) => {
if(error){
this.errors.push(error + error.reason + "|new Error code|");
}
else{
Meteor.call('userPushId', value, (error) => { //this pushes Id onto the second collection
if(error){
this.errors.push(error + error.reason + "|new Error code|");
}
});
}
Server
// ...
try {
const myModels = [{ ...whatever }];
// I'm using map so I return an array of id's.
//your forEach about technically should end up with only one id,
// which is the last insertion
const insertedDataIds = myModels.map(model => Collection.insert(model));
// you can also write to the secondary location here with something like:
const secondaryWrite = SecondaryCollection.insert({ something: insertedDataIds });
return insertedDataId's
}
//...
Client
also I don't know if this is just a typo on stack but your Meteor.call('submitData') should be Meteor.call('submitStuff') but that is probably not your actual issue.
Alright, so after searching around I realized I'm using angular-meteors rxjs package to create a MongoObservable when creating a collection instead of Mongo's regular collection.
Because I'm using MongoObservable and trying to invoke .insert() on that, the return is a bit different then a regular Mongo Collection and will return an Observable instead of the Id. Documentation Here
If you add a .collection after the collection name you will get the Id in return like so:
submitStuff: function(data): string{
var loggedInUser = Meteor.user();
if (!loggedInUser || !Roles.userIsInRole(loggedInUser,['user','admin'])){
throw new Meteor.Error("M504", "Access denied");
} else {
try{
let id = new Mongo.ObjectID();
const model: MyModel[] = [{
data.stuff //bunch of data being inserted
_id:
}];
model.forEach((obj: MyModel) => {
insertedData = Collection.collection.insert(obj); //added .collection
});
return insertedData;
} catch(e) {
throw new Meteor.Error(e + e.reason, "|Throw new error|");
}
}
},
I'm using Axios / promises to make AJAX requests, but the structure is a bit confusing. Basically I want this to happen:
Get masterlist.xml.
Parse the master list to get an array of category list URLs.
Get each category.xml.
Parse each category list to get an array of manifest URLs.
Get each manifest.xml and parse data.
Example structure:
masterlist.xml
<master>
<category>action</category>
<category>romance</category>
</master>
Category XMLs (e.g. action.xml and romance.xml)
<manifestUrls>
<manifest>foo/bar/manifest.xml</manifest>
<manifest>alice/bob/manifest.xml</manifest>
<manifest>hello/world/manifest.xml</manifest>
</manifestUrls>
Manifest XMLs
<manifest>
<data>Blah blah blah</data>
<manifest>
I'm a bit stuck on how to structure this as an Axios request with then. I'd like to have three functions, getMaster(), getCategory(url), and getManifest(url) that are preferably independent (e.g. getMaster doesn't have to call getCategory directly), but it seems like it might be necessary.
How would this be structured in Axios?
One of the main benefits of promises is that they allow you to easily avoid interdependence between your methods.
Here is a rough outline of how you could do this.
// put it all together
getMaster()
.then(parseMaster)
.then(function (categories) {
return Promise.all(categories.map(getAndParseCategory));
})
.then(flatten) // the previous then() resolves to an array-of-arrays
.then(function (manifestUrls) {
return Promise.all(manifestUrls.map(getManifest));
})
.then(function (manifests) {
// manifests is an array of all manifests
});
// Examples of what each of the functions used above would do
function getMaster() {
return axios.get('masterUrl')
.then(function (response) { return response.data; });
}
function parseMaster(masterContent) {
// parse and return an array of categories
}
function getCategory(name) {
var url = // ... build the URL based on the name
return axios.get(url)
.then(function (response) { return response.data; });
}
function parseCategory(categoryContent) {
// parse and return an array of URLs synchronously for one category
}
function getAndParseCategory(name) {
return getCategory(name).then(parseCategory);
}
function getManifest(url) {
return axios.get(url)
.then(function (response) { return response.data; });
}
function flatten(arrayOfArrays) {
return [].concat.apply([], arrayOfArrays);
}
If you're using Bluebird or something else that gives promises a .map() method, then you can tidy that pipeline up a bit:
// using Promise.resolve() at the beginning to ensure
// the chain is based of the desired kind of promise
Promise.resolve()
.then(getMaster)
.then(parseMaster)
.map(getCategory)
.map(parseCategory)
.then(flatten) // previous line resolves to an array-of-arrays
.map(getManifest)
.then(function (manifests) {
// manifests is an array of all manifests
});
Of course, you could also define your own .map method if you don't want to import a whole third party promise library:
if (!Promise.prototype.map) {
Promise.prototype.map = function (func) {
return this.then(function (result) {
return Promise.all(result.map(func));
});
};
}
Edit: To respond to your question in the comments below. If you wanted to pass the category text along so that it could be included in the manifest URLs, I think a clean way to do this would be to include that in the data returned from getCategory() so that parseCategory can make use of it. Everything else could stay the same.
Example:
function getCategory(name) {
var url = // ... build the URL based on the name
return axios.get(url)
.then(function (response) {
return {
name: name,
data: response.data
};
});
}
function parseCategory(categoryContent) {
var urls = // parse URLs from categoryContent.data
return urls.map(function (url) {
return categoryContent.name + '/' + url;
});
}